Compare commits
42
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4436ffaaab | ||
|
|
c98038b2d7 | ||
|
|
849c4bb8c5 | ||
|
|
ee64322d76 | ||
|
|
79d65a768a | ||
|
|
37610dc8f3 | ||
|
|
2e31dc20e4 | ||
|
|
ae3d98fb47 | ||
|
|
a938a790fa | ||
|
|
71d556dddf | ||
|
|
d61227b2fd | ||
|
|
612a6b00e4 | ||
|
|
b3353b518c | ||
|
|
0b44761d43 | ||
|
|
c7979e4023 | ||
|
|
e00904e100 | ||
|
|
96e2366569 | ||
|
|
e76624862d | ||
|
|
c5c83a61d3 | ||
|
|
ccf0d6205f | ||
|
|
f482c1d702 | ||
|
|
a4c056e822 | ||
|
|
f5071b2aae | ||
|
|
0fa13ee43d | ||
|
|
1d2b8a08fa | ||
|
|
0c06744450 | ||
|
|
4ada29fbc3 | ||
|
|
76182231dd | ||
|
|
8cd6915b9d | ||
|
|
f90de26bc2 | ||
|
|
cc2e661aab | ||
|
|
3a92cdab3a | ||
|
|
b367982c23 | ||
|
|
a4f4dc617e | ||
|
|
2eb0f11d05 | ||
|
|
6694df991c | ||
|
|
8b2bfa7e9d | ||
|
|
7386220325 | ||
|
|
000faeaca7 | ||
|
|
3f9783ac02 | ||
|
|
4e6dc67ae8 | ||
|
|
666140bb73 |
@@ -21,8 +21,9 @@ jobs:
|
|||||||
- name: Run update script
|
- name: Run update script
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||||
|
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||||
run: |
|
run: |
|
||||||
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 test/renderdiff/src/commit_msg.py)
|
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||||
git config --global user.email "filament.bot@gmail.com"
|
git config --global user.email "filament.bot@gmail.com"
|
||||||
|
|||||||
@@ -134,18 +134,17 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||||
run: |
|
run: |
|
||||||
ls ./gltf/Models
|
|
||||||
TEST_DIR=test/renderdiff
|
TEST_DIR=test/renderdiff
|
||||||
source ${TEST_DIR}/src/preamble.sh
|
source ${TEST_DIR}/src/preamble.sh
|
||||||
start_
|
set -eux
|
||||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||||
bash ${TEST_DIR}/generate.sh && \
|
bash ${TEST_DIR}/generate.sh
|
||||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||||
--branch=${GOLDEN_BRANCH} \
|
--branch=${GOLDEN_BRANCH} \
|
||||||
--output=${GOLDEN_OUTPUT_DIR}
|
--output=${GOLDEN_OUTPUT_DIR}
|
||||||
|
|
||||||
# Note that we need to upload the output even if comparison fails, so we undo `set -ex`
|
# Note that we need to upload the output even if comparison fails, so we undo `set -eux`
|
||||||
end_
|
set +eux
|
||||||
|
|
||||||
python3 ${TEST_DIR}/src/compare.py \
|
python3 ${TEST_DIR}/src/compare.py \
|
||||||
--src=${GOLDEN_OUTPUT_DIR} \
|
--src=${GOLDEN_OUTPUT_DIR} \
|
||||||
@@ -158,6 +157,7 @@ jobs:
|
|||||||
cat compare_output.txt >> "$GITHUB_OUTPUT"
|
cat compare_output.txt >> "$GITHUB_OUTPUT"
|
||||||
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
shell: bash
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: presubmit-renderdiff-result
|
name: presubmit-renderdiff-result
|
||||||
|
|||||||
+4
-2
@@ -872,7 +872,9 @@ add_subdirectory(${LIBRARIES}/utils)
|
|||||||
add_subdirectory(${LIBRARIES}/viewer)
|
add_subdirectory(${LIBRARIES}/viewer)
|
||||||
add_subdirectory(${FILAMENT}/shaders)
|
add_subdirectory(${FILAMENT}/shaders)
|
||||||
add_subdirectory(${EXTERNAL}/abseil/tnt)
|
add_subdirectory(${EXTERNAL}/abseil/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
# Add zstd before basisu to force it to use the external zstd target,
|
||||||
|
# preventing a duplicate symbol conflict with its bundled version.
|
||||||
|
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
||||||
@@ -886,7 +888,7 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
|||||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/getopt)
|
add_subdirectory(${EXTERNAL}/getopt)
|
||||||
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||||
|
|
||||||
|
|
||||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.67.1'
|
implementation 'com.google.android.filament:filament-android:1.68.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:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.67.1'
|
pod 'Filament', '~> 1.68.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ 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
|
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||||
|
|
||||||
|
## v1.68.1
|
||||||
|
|
||||||
|
|
||||||
## v1.68.0
|
## v1.68.0
|
||||||
|
|
||||||
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ buildscript {
|
|||||||
'kotlin': '2.0.21',
|
'kotlin': '2.0.21',
|
||||||
'kotlin_coroutines': '1.9.0',
|
'kotlin_coroutines': '1.9.0',
|
||||||
'buildTools': '35.0.0',
|
'buildTools': '35.0.0',
|
||||||
'ndk': '27.0.11718014',
|
'ndk': '29.0.14206865',
|
||||||
'androidx_core': '1.13.1',
|
'androidx_core': '1.13.1',
|
||||||
'androidx_annotations': '1.9.0'
|
'androidx_annotations': '1.9.0'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ using namespace filament;
|
|||||||
|
|
||||||
extern "C" JNIEXPORT jlong JNICALL
|
extern "C" JNIEXPORT jlong JNICALL
|
||||||
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality) {
|
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) {
|
||||||
Engine* engine = (Engine*) nativeEngine;
|
Engine* engine = (Engine*) nativeEngine;
|
||||||
AutoBuffer buffer(env, buffer_, size);
|
AutoBuffer buffer(env, buffer_, size);
|
||||||
auto builder = Material::Builder();
|
auto builder = Material::Builder();
|
||||||
@@ -33,6 +33,7 @@ Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
|||||||
builder.sphericalHarmonicsBandCount(shBandCount);
|
builder.sphericalHarmonicsBandCount(shBandCount);
|
||||||
}
|
}
|
||||||
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
|
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
|
||||||
|
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
|
||||||
Material* material = builder
|
Material* material = builder
|
||||||
.package(buffer.getData(), buffer.getSize())
|
.package(buffer.getData(), buffer.getSize())
|
||||||
.build(*engine);
|
.build(*engine);
|
||||||
|
|||||||
@@ -261,6 +261,20 @@ public class Material {
|
|||||||
LOW
|
LOW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether a material instance should use UBO batching or not.
|
||||||
|
*/
|
||||||
|
public enum UboBatchingMode {
|
||||||
|
/**
|
||||||
|
* For default, it follows the engine settings.
|
||||||
|
* If UBO batching is enabled on the engine and the material domain is SURFACE, it
|
||||||
|
* turns on the UBO batching. Otherwise, it turns off the UBO batching.
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
/** Disable the Ubo Batching for this material */
|
||||||
|
DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
public static class UserVariantFilterBit {
|
public static class UserVariantFilterBit {
|
||||||
/** Directional lighting */
|
/** Directional lighting */
|
||||||
public static int DIRECTIONAL_LIGHTING = 0x01;
|
public static int DIRECTIONAL_LIGHTING = 0x01;
|
||||||
@@ -372,6 +386,7 @@ public class Material {
|
|||||||
private int mSize;
|
private int mSize;
|
||||||
private int mShBandCount = 0;
|
private int mShBandCount = 0;
|
||||||
private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW;
|
private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW;
|
||||||
|
private UboBatchingMode mUboBatchingMode = UboBatchingMode.DEFAULT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -416,6 +431,17 @@ public class Material {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the batching mode of the instances created from this material.
|
||||||
|
* @param uboBatchingMode
|
||||||
|
* @return Reference to this Builder for chaining calls.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder uboBatching(UboBatchingMode mode) {
|
||||||
|
mUboBatchingMode = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns the Material object.
|
* Creates and returns the Material object.
|
||||||
*
|
*
|
||||||
@@ -428,7 +454,7 @@ public class Material {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public Material build(@NonNull Engine engine) {
|
public Material build(@NonNull Engine engine) {
|
||||||
long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
|
long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
|
||||||
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal());
|
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal(), mUboBatchingMode.ordinal());
|
||||||
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
|
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
|
||||||
return new Material(nativeMaterial);
|
return new Material(nativeMaterial);
|
||||||
}
|
}
|
||||||
@@ -1094,7 +1120,7 @@ public class Material {
|
|||||||
mNativeObject = 0;
|
mNativeObject = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality);
|
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality, int uboBatchingMode);
|
||||||
private static native long nCreateInstance(long nativeMaterial);
|
private static native long nCreateInstance(long nativeMaterial);
|
||||||
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
||||||
private static native long nGetDefaultInstance(long nativeMaterial);
|
private static native long nGetDefaultInstance(long nativeMaterial);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.67.1
|
VERSION_NAME=1.68.0
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ function print_fgviewer_help {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
|
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
|
||||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".")}
|
FILAMENT_NDK_VERSION=$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".")
|
||||||
|
|
||||||
# Internal variables
|
# Internal variables
|
||||||
ISSUE_CLEAN=false
|
ISSUE_CLEAN=false
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Unless explicitly specified, NDK version will be set to match exactly the required one
|
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
|
||||||
FILAMENT_NDK_VERSION=${GITHUB_NDK_VERSION:-27.0.11718014}
|
FILAMENT_NDK_VERSION=$(cat `dirname $0`/../common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g)
|
||||||
|
|
||||||
(! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) &&
|
(! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) &&
|
||||||
echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" &&
|
echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" &&
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ GITHUB_CMAKE_VERSION=3.22.1
|
|||||||
GITHUB_NINJA_VERSION=1.10.2
|
GITHUB_NINJA_VERSION=1.10.2
|
||||||
GITHUB_MESA_VERSION=24.2.1
|
GITHUB_MESA_VERSION=24.2.1
|
||||||
GITHUB_LLVM_VERSION=16
|
GITHUB_LLVM_VERSION=16
|
||||||
GITHUB_NDK_VERSION=27.0.11718014
|
GITHUB_NDK_VERSION=29.0.14206865
|
||||||
GITHUB_EMSDK_VERSION=3.1.60
|
GITHUB_EMSDK_VERSION=3.1.60
|
||||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||||
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
||||||
@@ -75,7 +75,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|||||||
# C_FLAGS += -Wl,-pie
|
# C_FLAGS += -Wl,-pie
|
||||||
# CXX_FLAGS += -lstdc++
|
# CXX_FLAGS += -lstdc++
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -mcpu=cortex-a57" CACHE STRING "Toolchain CFLAGS")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -march=armv8-a -mtune=cortex-a78" CACHE STRING "Toolchain CFLAGS")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|||||||
# for hardfp: CFLAGS must have -mhard-float
|
# for hardfp: CFLAGS must have -mhard-float
|
||||||
# LDFLAGS must have -Wl,--no-warn-mismatch
|
# LDFLAGS must have -Wl,--no-warn-mismatch
|
||||||
#
|
#
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -march=armv7-a -mcpu=cortex-a15 -mfloat-abi=softfp -mfpu=neon-vfpv4 -fPIE" CACHE STRING "Toolchain CFLAGS")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 -fPIE" CACHE STRING "Toolchain CFLAGS")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++ -fPIE -pie" CACHE STRING "Toolchain LDFLAGS")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++ -fPIE -pie" CACHE STRING "Toolchain LDFLAGS")
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
||||||
|
|||||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.67.1'
|
implementation 'com.google.android.filament:filament-android:1.68.0'
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||||
@@ -196,7 +196,7 @@ dependencies {
|
|||||||
</div>
|
</div>
|
||||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||||
<pre><code class="language-shell">pod 'Filament', '~> 1.67.1'
|
<pre><code class="language-shell">pod 'Filament', '~> 1.68.0'
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
+268
-270
File diff suppressed because one or more lines are too long
+162
-164
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -10,6 +10,6 @@ sortedcontainers==2.4.0
|
|||||||
trio==0.31.0
|
trio==0.31.0
|
||||||
trio-websocket==0.12.2
|
trio-websocket==0.12.2
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
urllib3==2.5.0
|
urllib3==2.6.0
|
||||||
websocket-client==1.9.0
|
websocket-client==1.9.0
|
||||||
wsproto==1.2.0
|
wsproto==1.2.0
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
|||||||
src/vulkan/VulkanDriverFactory.h
|
src/vulkan/VulkanDriverFactory.h
|
||||||
src/vulkan/VulkanExternalImageManager.cpp
|
src/vulkan/VulkanExternalImageManager.cpp
|
||||||
src/vulkan/VulkanExternalImageManager.h
|
src/vulkan/VulkanExternalImageManager.h
|
||||||
|
src/vulkan/VulkanStreamedImageManager.cpp
|
||||||
|
src/vulkan/VulkanStreamedImageManager.h
|
||||||
src/vulkan/VulkanFboCache.cpp
|
src/vulkan/VulkanFboCache.cpp
|
||||||
src/vulkan/VulkanFboCache.h
|
src/vulkan/VulkanFboCache.h
|
||||||
src/vulkan/VulkanHandles.cpp
|
src/vulkan/VulkanHandles.cpp
|
||||||
@@ -316,6 +318,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
|||||||
src/webgpu/WebGPURenderPrimitive.h
|
src/webgpu/WebGPURenderPrimitive.h
|
||||||
src/webgpu/WebGPURenderTarget.cpp
|
src/webgpu/WebGPURenderTarget.cpp
|
||||||
src/webgpu/WebGPURenderTarget.h
|
src/webgpu/WebGPURenderTarget.h
|
||||||
|
src/webgpu/WebGPUStagePool.cpp
|
||||||
|
src/webgpu/WebGPUStagePool.h
|
||||||
src/webgpu/WebGPUStrings.h
|
src/webgpu/WebGPUStrings.h
|
||||||
src/webgpu/WebGPUSwapChain.cpp
|
src/webgpu/WebGPUSwapChain.cpp
|
||||||
src/webgpu/WebGPUSwapChain.h
|
src/webgpu/WebGPUSwapChain.h
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
|
||||||
#define TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
|
||||||
|
|
||||||
#include <backend/Handle.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of association between a buffer object and a stream.
|
|
||||||
*/
|
|
||||||
enum class BufferObjectStreamAssociationType { TRANSFORM_MATRIX };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A descriptor for a buffer object to stream association.
|
|
||||||
*/
|
|
||||||
class UTILS_PUBLIC BufferObjectStreamDescriptor {
|
|
||||||
public:
|
|
||||||
//! creates an empty descriptor
|
|
||||||
BufferObjectStreamDescriptor() noexcept = default;
|
|
||||||
|
|
||||||
BufferObjectStreamDescriptor(const BufferObjectStreamDescriptor& rhs) = delete;
|
|
||||||
BufferObjectStreamDescriptor& operator=(const BufferObjectStreamDescriptor& rhs) = delete;
|
|
||||||
|
|
||||||
BufferObjectStreamDescriptor(BufferObjectStreamDescriptor&& rhs) = default;
|
|
||||||
BufferObjectStreamDescriptor& operator=(BufferObjectStreamDescriptor&& rhs) = default;
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct StreamDataDescriptor {
|
|
||||||
uint32_t offset;
|
|
||||||
Handle<HwStream> stream;
|
|
||||||
BufferObjectStreamAssociationType associationType;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<StreamDataDescriptor> mStreams;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace filament::backend
|
|
||||||
|
|
||||||
#if !defined(NDEBUG)
|
|
||||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
|
||||||
const filament::backend::BufferObjectStreamDescriptor& b);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
|
||||||
@@ -1713,6 +1713,8 @@ using FrameTimestamps = Platform::FrameTimestamps;
|
|||||||
|
|
||||||
using CompositorTiming = Platform::CompositorTiming;
|
using CompositorTiming = Platform::CompositorTiming;
|
||||||
|
|
||||||
|
using AsynchronousMode = Platform::AsynchronousMode;
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|
||||||
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ public:
|
|||||||
ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept;
|
ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept;
|
||||||
ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept;
|
ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept;
|
||||||
|
|
||||||
|
bool operator==(const ExternalImageHandle& rhs) const noexcept {
|
||||||
|
return mTarget == rhs.mTarget;
|
||||||
|
}
|
||||||
explicit operator bool() const noexcept { return mTarget != nullptr; }
|
explicit operator bool() const noexcept { return mTarget != nullptr; }
|
||||||
|
|
||||||
ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; }
|
ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; }
|
||||||
@@ -252,6 +255,28 @@ public:
|
|||||||
REALTIME,
|
REALTIME,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how asynchronous operations are handled by the engine.
|
||||||
|
*/
|
||||||
|
enum class AsynchronousMode : uint8_t {
|
||||||
|
/**
|
||||||
|
* Asynchronous operations are disabled. This is the default.
|
||||||
|
*/
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to use a dedicated worker thread for asynchronous tasks. If threading is not
|
||||||
|
* supported by the platform, it automatically falls back to using an amortization strategy.
|
||||||
|
*/
|
||||||
|
THREAD_PREFERRED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses an amortization strategy, processing a small number of asynchronous tasks during
|
||||||
|
* each engine update cycle.
|
||||||
|
*/
|
||||||
|
AMORTIZATION,
|
||||||
|
};
|
||||||
|
|
||||||
struct DriverConfig {
|
struct DriverConfig {
|
||||||
/**
|
/**
|
||||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||||
@@ -321,6 +346,11 @@ public:
|
|||||||
* - VulkanPlatform
|
* - VulkanPlatform
|
||||||
*/
|
*/
|
||||||
bool vulkanEnableStagingBufferBypass = false;
|
bool vulkanEnableStagingBufferBypass = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
|
||||||
|
*/
|
||||||
|
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
Platform() noexcept;
|
Platform() noexcept;
|
||||||
|
|||||||
@@ -30,8 +30,6 @@
|
|||||||
|
|
||||||
#include <math/mat3.h>
|
#include <math/mat3.h>
|
||||||
|
|
||||||
#include "AndroidNativeWindow.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "private/backend/Driver.h"
|
#include "private/backend/Driver.h"
|
||||||
|
|
||||||
#include <backend/BufferDescriptor.h>
|
#include <backend/BufferDescriptor.h>
|
||||||
#include <backend/BufferObjectStreamDescriptor.h>
|
|
||||||
#include <backend/CallbackHandler.h>
|
#include <backend/CallbackHandler.h>
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Handle.h>
|
#include <backend/Handle.h>
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class BufferDescriptor;
|
class BufferDescriptor;
|
||||||
class BufferObjectStreamDescriptor;
|
|
||||||
class CallbackHandler;
|
class CallbackHandler;
|
||||||
class PixelBufferDescriptor;
|
class PixelBufferDescriptor;
|
||||||
class Program;
|
class Program;
|
||||||
|
|||||||
@@ -392,10 +392,6 @@ DECL_DRIVER_API_N(updateBufferObject,
|
|||||||
backend::BufferDescriptor&&, data,
|
backend::BufferDescriptor&&, data,
|
||||||
uint32_t, byteOffset)
|
uint32_t, byteOffset)
|
||||||
|
|
||||||
DECL_DRIVER_API_N(registerBufferObjectStreams,
|
|
||||||
backend::BufferObjectHandle, boh,
|
|
||||||
backend::BufferObjectStreamDescriptor&&, streams)
|
|
||||||
|
|
||||||
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
|
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
|
||||||
backend::BufferObjectHandle, boh,
|
backend::BufferObjectHandle, boh,
|
||||||
backend::BufferDescriptor&&, data,
|
backend::BufferDescriptor&&, data,
|
||||||
|
|||||||
@@ -1144,8 +1144,12 @@ bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
|
|||||||
bool MetalDriver::isFrameBufferFetchSupported() {
|
bool MetalDriver::isFrameBufferFetchSupported() {
|
||||||
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
|
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
|
||||||
// Apple GPUs with readWriteTextureSupport.
|
// Apple GPUs with readWriteTextureSupport.
|
||||||
return mContext->highestSupportedGpuFamily.apple >= 1 &&
|
// On macOS, framebuffer fetch requires MSL 2.3, which is only available with macOS 11.0.
|
||||||
mContext->device.readWriteTextureSupport;
|
if (@available(macOS 11.0, *)) {
|
||||||
|
return mContext->highestSupportedGpuFamily.apple >= 1 &&
|
||||||
|
mContext->device.readWriteTextureSupport;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {
|
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {
|
||||||
@@ -1286,10 +1290,6 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
|
|||||||
scheduleDestroy(std::move(data));
|
scheduleDestroy(std::move(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
|
|
||||||
// Noop
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
||||||
uint32_t byteOffset) {
|
uint32_t byteOffset) {
|
||||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||||
|
|||||||
@@ -1684,7 +1684,7 @@ MetalMemoryMappedBuffer::~MetalMemoryMappedBuffer() = default;
|
|||||||
void MetalMemoryMappedBuffer::unmap() {
|
void MetalMemoryMappedBuffer::unmap() {
|
||||||
#if !defined(FILAMENT_IOS) && defined(__x86_64__)
|
#if !defined(FILAMENT_IOS) && defined(__x86_64__)
|
||||||
// Managed memory requires didModifyRange to synchronize changes to the GPU. This is specific to Intel Macs.
|
// Managed memory requires didModifyRange to synchronize changes to the GPU. This is specific to Intel Macs.
|
||||||
MetalBuffer* buffer = bo->getBuffer();
|
MetalBuffer* buffer = mtl.bo->getBuffer();
|
||||||
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
|
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
|
||||||
if (mtlBuffer && mtlBuffer.storageMode == MTLStorageModeManaged) {
|
if (mtlBuffer && mtlBuffer.storageMode == MTLStorageModeManaged) {
|
||||||
[mtlBuffer didModifyRange:NSMakeRange(mtl.offset, mtl.size)];
|
[mtlBuffer didModifyRange:NSMakeRange(mtl.offset, mtl.size)];
|
||||||
|
|||||||
@@ -111,9 +111,22 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
|||||||
id<MTLLibrary> library = nil;
|
id<MTLLibrary> library = nil;
|
||||||
switch (program.getShaderLanguage()) {
|
switch (program.getShaderLanguage()) {
|
||||||
case ShaderLanguage::MSL: {
|
case ShaderLanguage::MSL: {
|
||||||
// By default, Metal uses the most recent language version.
|
|
||||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||||
options.fastMathEnabled = YES;
|
options.fastMathEnabled = YES;
|
||||||
|
// The docs for MTLCompileOptions say that Metal uses the most
|
||||||
|
// recent language version if it is not specified, but this is
|
||||||
|
// not always true: it is possible for the environment to be set
|
||||||
|
// up in a way where an older version is used by default. So we
|
||||||
|
// explicitly set the minimum version we require.
|
||||||
|
#if defined(FILAMENT_IOS)
|
||||||
|
options.languageVersion = MTLLanguageVersion2_0;
|
||||||
|
#else
|
||||||
|
if (@available(macOS 11.0, *)) {
|
||||||
|
options.languageVersion = MTLLanguageVersion2_3;
|
||||||
|
} else {
|
||||||
|
options.languageVersion = MTLLanguageVersion2_2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
assert_invariant(source[source.size() - 1] == '\0');
|
assert_invariant(source[source.size() - 1] == '\0');
|
||||||
// the shader string is null terminated and the length includes the null character
|
// the shader string is null terminated and the length includes the null character
|
||||||
|
|||||||
@@ -291,8 +291,6 @@ void NoopDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor
|
|||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoopDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) { }
|
|
||||||
|
|
||||||
void NoopDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
|
void NoopDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
|
||||||
uint32_t byteOffset) {
|
uint32_t byteOffset) {
|
||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
|
|||||||
// - the nameVersion is out of date *and* we're on the protected context, in this case:
|
// - the nameVersion is out of date *and* we're on the protected context, in this case:
|
||||||
// - the name must be stale from a previous use of this context because we always
|
// - the name must be stale from a previous use of this context because we always
|
||||||
// destroy the protected context when we're done with it.
|
// destroy the protected context when we're done with it.
|
||||||
bool const recreateVaoName = p != &mDefaultVAO &&
|
bool const recreateVaoName = vao != &mDefaultVAO &&
|
||||||
((vao->vao[contextIndex] == 0) ||
|
((vao->vao[contextIndex] == 0) ||
|
||||||
(vao->nameVersion != state.age && contextIndex == 1));
|
(vao->nameVersion != state.age && contextIndex == 1));
|
||||||
if (UTILS_UNLIKELY(recreateVaoName)) {
|
if (UTILS_UNLIKELY(recreateVaoName)) {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@
|
|||||||
#include <backend/Platform.h>
|
#include <backend/Platform.h>
|
||||||
#include <backend/Program.h>
|
#include <backend/Program.h>
|
||||||
#include <backend/TargetBufferInfo.h>
|
#include <backend/TargetBufferInfo.h>
|
||||||
#include <backend/BufferObjectStreamDescriptor.h>
|
|
||||||
|
|
||||||
#include "private/backend/CommandStream.h"
|
#include "private/backend/CommandStream.h"
|
||||||
#include "private/backend/Dispatcher.h"
|
#include "private/backend/Dispatcher.h"
|
||||||
@@ -2837,38 +2836,6 @@ void OpenGLDriver::updateIndexBuffer(
|
|||||||
CHECK_GL_ERROR()
|
CHECK_GL_ERROR()
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
|
|
||||||
DEBUG_MARKER()
|
|
||||||
|
|
||||||
GLBufferObject const* bo = handle_cast<GLBufferObject*>(boh);
|
|
||||||
mStreamUniformDescriptors[bo->gl.id] = std::move(streams);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// specialization for mat3f (which has a different alignment, see std140 layout rules)
|
|
||||||
static void copyMat3f(void* addr, size_t const offset, const mat3f& v) noexcept {
|
|
||||||
struct mat43 {
|
|
||||||
float v[3][4];
|
|
||||||
};
|
|
||||||
|
|
||||||
addr = static_cast<char*>(addr) + offset;
|
|
||||||
mat43& temp = *static_cast<mat43*>(addr);
|
|
||||||
|
|
||||||
temp.v[0][0] = v[0][0];
|
|
||||||
temp.v[0][1] = v[0][1];
|
|
||||||
temp.v[0][2] = v[0][2];
|
|
||||||
|
|
||||||
temp.v[1][0] = v[1][0];
|
|
||||||
temp.v[1][1] = v[1][1];
|
|
||||||
temp.v[1][2] = v[1][2];
|
|
||||||
|
|
||||||
temp.v[2][0] = v[2][0];
|
|
||||||
temp.v[2][1] = v[2][1];
|
|
||||||
temp.v[2][2] = v[2][2];
|
|
||||||
|
|
||||||
// don't store anything in temp.v[][3] because there could be uniforms packed there
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLDriver::updateBufferObject(
|
void OpenGLDriver::updateBufferObject(
|
||||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||||
DEBUG_MARKER()
|
DEBUG_MARKER()
|
||||||
@@ -2882,19 +2849,6 @@ void OpenGLDriver::updateBufferObject(
|
|||||||
gl.bindVertexArray(nullptr);
|
gl.bindVertexArray(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UTILS_UNLIKELY(!mStreamUniformDescriptors.empty())) {
|
|
||||||
auto const streamDescriptors = mStreamUniformDescriptors.find(bo->gl.id);
|
|
||||||
if (streamDescriptors != mStreamUniformDescriptors.end()) {
|
|
||||||
for (auto const& [offset, stream, associationType] : streamDescriptors->second.mStreams) {
|
|
||||||
if (associationType == BufferObjectStreamAssociationType::TRANSFORM_MATRIX) {
|
|
||||||
auto transform = getStreamTransformMatrix(stream);
|
|
||||||
copyMat3f(bd.buffer, offset, transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mStreamUniformDescriptors.erase(streamDescriptors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||||
assert_invariant(bo->gl.buffer);
|
assert_invariant(bo->gl.buffer);
|
||||||
memcpy(static_cast<uint8_t*>(bo->gl.buffer) + byteOffset, bd.buffer, bd.size);
|
memcpy(static_cast<uint8_t*>(bo->gl.buffer) + byteOffset, bd.buffer, bd.size);
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include <backend/Platform.h>
|
#include <backend/Platform.h>
|
||||||
#include <backend/Program.h>
|
#include <backend/Program.h>
|
||||||
#include <backend/TargetBufferInfo.h>
|
#include <backend/TargetBufferInfo.h>
|
||||||
#include <backend/BufferObjectStreamDescriptor.h>
|
|
||||||
|
|
||||||
#include "private/backend/Driver.h"
|
#include "private/backend/Driver.h"
|
||||||
#include "private/backend/HandleAllocator.h"
|
#include "private/backend/HandleAllocator.h"
|
||||||
@@ -394,8 +393,6 @@ private:
|
|||||||
// the must be accessed from the user thread only
|
// the must be accessed from the user thread only
|
||||||
std::vector<GLStream*> mStreamsWithPendingAcquiredImage;
|
std::vector<GLStream*> mStreamsWithPendingAcquiredImage;
|
||||||
|
|
||||||
std::unordered_map<GLuint, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
|
|
||||||
|
|
||||||
void attachStream(GLTexture* t, GLStream* stream);
|
void attachStream(GLTexture* t, GLStream* stream);
|
||||||
void detachStream(GLTexture* t) noexcept;
|
void detachStream(GLTexture* t) noexcept;
|
||||||
void replaceStream(GLTexture* t, GLStream* stream) noexcept;
|
void replaceStream(GLTexture* t, GLStream* stream) noexcept;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
|
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__) || defined(FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__)
|
#if defined(__EMSCRIPTEN__)
|
||||||
# include <GLES3/gl3.h>
|
# include <GLES3/gl3.h>
|
||||||
|
|||||||
@@ -178,20 +178,30 @@ void PlatformEGLAndroid::beginFrame(
|
|||||||
int64_t const monotonic_clock_ns,
|
int64_t const monotonic_clock_ns,
|
||||||
int64_t refreshIntervalNs,
|
int64_t refreshIntervalNs,
|
||||||
uint32_t const frameId) noexcept {
|
uint32_t const frameId) noexcept {
|
||||||
// associate the user frameid with the system frame id
|
// if frameId is 0, it means we're not associated to a particular frame, which is the case
|
||||||
setPresentFrameId(mCurrentDrawSwapChain, frameId);
|
// for standalone views. And in this case we skip the performance hint (since we wouldn't get
|
||||||
|
// the right timing anyway as well as the frame info timing collections)
|
||||||
|
|
||||||
if (mPerformanceHintSession.isValid()) {
|
// associate the user frameid with the system frame id.
|
||||||
|
if (frameId && mCurrentDrawSwapChain) {
|
||||||
|
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView
|
||||||
|
setPresentFrameId(mCurrentDrawSwapChain, frameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameId && mPerformanceHintSession.isValid()) {
|
||||||
if (refreshIntervalNs <= 0) {
|
if (refreshIntervalNs <= 0) {
|
||||||
// we're not provided with a target time, use the display period, if everything fails,
|
// we're not provided with a target time, use the display period, if everything fails,
|
||||||
// assume 16.67ms
|
// assume 16.67ms
|
||||||
refreshIntervalNs = 16'666'667;
|
refreshIntervalNs = 16'666'667;
|
||||||
|
|
||||||
CompositorTiming compositorTiming{};
|
if (mCurrentDrawSwapChain) {
|
||||||
bool const hasCompositorTiming =
|
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView
|
||||||
queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming);
|
CompositorTiming compositorTiming{};
|
||||||
if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
|
bool const hasCompositorTiming =
|
||||||
refreshIntervalNs = compositorTiming.compositeInterval;
|
queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming);
|
||||||
|
if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
|
||||||
|
refreshIntervalNs = compositorTiming.compositeInterval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns));
|
mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns));
|
||||||
@@ -440,6 +450,10 @@ Platform::SwapChain* PlatformEGLAndroid::createSwapChain(
|
|||||||
void PlatformEGLAndroid::destroySwapChain(SwapChain* swapChain) noexcept {
|
void PlatformEGLAndroid::destroySwapChain(SwapChain* swapChain) noexcept {
|
||||||
if (swapChain) {
|
if (swapChain) {
|
||||||
SwapChainEGLAndroid* const sc = static_cast<SwapChainEGLAndroid*>(swapChain);
|
SwapChainEGLAndroid* const sc = static_cast<SwapChainEGLAndroid*>(swapChain);
|
||||||
|
if (mCurrentDrawSwapChain == sc) {
|
||||||
|
// don't keep a dangling pointer around
|
||||||
|
mCurrentDrawSwapChain = nullptr;
|
||||||
|
}
|
||||||
sc->terminate(*this);
|
sc->terminate(*this);
|
||||||
delete sc;
|
delete sc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#include <backend/platforms/PlatformEGLHeadless.h>
|
#include <backend/platforms/PlatformEGLHeadless.h>
|
||||||
|
|
||||||
#include "opengl/GLUtils.h"
|
#include <bluegl/BlueGL.h>
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <EGL/eglext.h>
|
#include <EGL/eglext.h>
|
||||||
@@ -35,16 +35,31 @@ PlatformEGLHeadless::PlatformEGLHeadless() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformEGLHeadless::isOpenGL() const noexcept {
|
bool PlatformEGLHeadless::isOpenGL() const noexcept {
|
||||||
return true;
|
#if defined(BACKEND_OPENGL_VERSION_GL)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif // defined(BACKEND_OPENGL_VERSION_GL)
|
||||||
}
|
}
|
||||||
|
|
||||||
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
|
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
|
||||||
const Platform::DriverConfig& driverConfig) {
|
const Platform::DriverConfig& driverConfig) {
|
||||||
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
|
auto bindApiHelper = [](EGLenum api, const char* errorString) -> bool {
|
||||||
if (UTILS_UNLIKELY(!bindAPI)) {
|
EGLBoolean bindAPI = eglBindAPI(api);
|
||||||
LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
|
if (UTILS_UNLIKELY(bindAPI == EGL_FALSE || bindAPI == EGL_BAD_PARAMETER)) {
|
||||||
|
logEglError(errorString);
|
||||||
|
eglReleaseThread();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
EGLenum api = isOpenGL() ? EGL_OPENGL_API : EGL_OPENGL_ES_API;
|
||||||
|
const char* apiString = isOpenGL() ? "eglBindAPI EGL_OPENGL_API" : "eglBindAPI EGL_OPENGL_ES_API";
|
||||||
|
if (!bindApiHelper(api, apiString)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bindBlueGL = bluegl::bind();
|
int bindBlueGL = bluegl::bind();
|
||||||
if (UTILS_UNLIKELY(bindBlueGL != 0)) {
|
if (UTILS_UNLIKELY(bindBlueGL != 0)) {
|
||||||
LOG(ERROR) << "bluegl bind failed";
|
LOG(ERROR) << "bluegl bind failed";
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <backend/BufferDescriptor.h>
|
#include <backend/BufferDescriptor.h>
|
||||||
#include <backend/BufferObjectStreamDescriptor.h>
|
|
||||||
#include <backend/DescriptorSetOffsetArray.h>
|
#include <backend/DescriptorSetOffsetArray.h>
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/PipelineState.h>
|
#include <backend/PipelineState.h>
|
||||||
@@ -434,10 +433,6 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
|
|||||||
<< ", user=" << b.getUser() << " }";
|
<< ", user=" << b.getUser() << " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
|
|
||||||
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
|
|
||||||
}
|
|
||||||
|
|
||||||
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
||||||
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
||||||
return out << "PixelBufferDescriptor{ " << base
|
return out << "PixelBufferDescriptor{ " << base
|
||||||
|
|||||||
@@ -62,8 +62,9 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
|||||||
// a memcpy if its available.
|
// a memcpy if its available.
|
||||||
bool const isStaticOrShared =
|
bool const isStaticOrShared =
|
||||||
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
|
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
|
||||||
bool const useMemcpy = (isUniformAndAvailable || isStaticOrShared) && isMemcopyable &&
|
bool const useMemcpy =
|
||||||
mStagingBufferBypassEnabled;
|
((isUniformAndAvailable && mStagingBufferBypassEnabled) || isStaticOrShared) &&
|
||||||
|
isMemcopyable;
|
||||||
if (useMemcpy) {
|
if (useMemcpy) {
|
||||||
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
||||||
byteOffset;
|
byteOffset;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "VulkanCommands.h"
|
#include "VulkanCommands.h"
|
||||||
#include "VulkanHandles.h"
|
#include "VulkanHandles.h"
|
||||||
#include "VulkanConstants.h"
|
#include "VulkanConstants.h"
|
||||||
|
#include "utils/compiler.h"
|
||||||
|
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
@@ -31,6 +32,18 @@ namespace filament::backend {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using Bitmask = fvkutils::UniformBufferBitmask;
|
||||||
|
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
||||||
|
|
||||||
|
Bitmask foldBitsInHalf(Bitmask bitset) {
|
||||||
|
Bitmask outBitset;
|
||||||
|
bitset.forEachSetBit([&](size_t index) {
|
||||||
|
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
|
||||||
|
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
|
||||||
|
});
|
||||||
|
return outBitset;
|
||||||
|
}
|
||||||
|
|
||||||
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
|
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
|
||||||
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
|
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
|
||||||
|
|
||||||
@@ -304,8 +317,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
|||||||
// This code actually binds the descriptor sets.
|
// This code actually binds the descriptor sets.
|
||||||
auto set = updateSets[index];
|
auto set = updateSets[index];
|
||||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||||
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
|
VkDescriptorSet const vkset = set->getVkSet();
|
||||||
set->getVkSet();
|
|
||||||
commands->acquire(set);
|
commands->acquire(set);
|
||||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||||
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||||
@@ -341,17 +353,39 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
|||||||
.pBufferInfo = &info,
|
.pBufferInfo = &info,
|
||||||
};
|
};
|
||||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||||
|
|
||||||
if (auto externalSamplerSet = set->getExternalSamplerVkSet();
|
|
||||||
externalSamplerSet != VK_NULL_HANDLE) {
|
|
||||||
descriptorWrite.dstSet = externalSamplerSet;
|
|
||||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
|
||||||
}
|
|
||||||
set->acquire(bufferObject);
|
set->acquire(bufferObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t binding,
|
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept {
|
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||||
|
VkSampler sampler, VkDescriptorSetLayout externalSamplerLayout) noexcept {
|
||||||
|
|
||||||
|
// We have to update a bound set for two use cases
|
||||||
|
// - streaming API (a changing feed of AHardwareBuffer)
|
||||||
|
// - external samplers - potential changing of dataspace per-frame
|
||||||
|
if (UTILS_UNLIKELY(set->isBound())) {
|
||||||
|
auto layout = set->getLayout();
|
||||||
|
// Build a new descriptor set from the new layout
|
||||||
|
auto genLayout = externalSamplerLayout != VK_NULL_HANDLE ?
|
||||||
|
externalSamplerLayout : layout->getVkLayout();
|
||||||
|
VkDescriptorSet const newSet = getVkSet(layout->count, genLayout);
|
||||||
|
Bitmask const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
||||||
|
Bitmask samplers = layout->bitmask.sampler;
|
||||||
|
samplers.unset(binding);
|
||||||
|
|
||||||
|
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
||||||
|
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
||||||
|
// bindings.
|
||||||
|
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
||||||
|
VkDescriptorSet const srcSet = set->getVkSet();
|
||||||
|
copySet(srcSet, newSet, copyBindings);
|
||||||
|
set->addNewSet(newSet,
|
||||||
|
[this, layoutCount = layout->count, genLayout, newSet](VulkanDescriptorSet*) {
|
||||||
|
this->manualRecycle(layoutCount, genLayout, newSet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDescriptorSet const vkset = set->getVkSet();
|
||||||
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||||
VkImageViewType const expectedType = texture->getViewType();
|
VkImageViewType const expectedType = texture->getViewType();
|
||||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||||
@@ -376,19 +410,6 @@ void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t
|
|||||||
.pImageInfo = &info,
|
.pImageInfo = &info,
|
||||||
};
|
};
|
||||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
|
||||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
|
||||||
VkSampler sampler) noexcept {
|
|
||||||
updateSamplerImpl(set->getVkSet(), binding, texture, sampler);
|
|
||||||
set->acquire(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDescriptorSetCache::updateSamplerForExternalSamplerSet(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept {
|
|
||||||
updateSamplerImpl(set->getExternalSamplerVkSet(), binding, texture, VK_NULL_HANDLE);
|
|
||||||
set->acquire(texture);
|
set->acquire(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +426,7 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
|
|||||||
auto const& count = layout->count;
|
auto const& count = layout->count;
|
||||||
auto const vklayout = layout->getVkLayout();
|
auto const vklayout = layout->getVkLayout();
|
||||||
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
|
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
|
||||||
mResourceManager, handle, layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
mResourceManager, handle, layout,
|
||||||
[vkSet, count, vklayout, this](
|
[vkSet, count, vklayout, this](
|
||||||
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
|
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
|
||||||
vkSet);
|
vkSet);
|
||||||
@@ -428,4 +449,22 @@ void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count co
|
|||||||
|
|
||||||
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
|
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
|
||||||
|
|
||||||
|
void VulkanDescriptorSetCache::copySet(VkDescriptorSet srcSet, VkDescriptorSet dstSet,
|
||||||
|
fvkutils::SamplerBitmask bindings) const {
|
||||||
|
// TODO: fix the size for better memory management
|
||||||
|
std::vector<VkCopyDescriptorSet> copies;
|
||||||
|
bindings.forEachSetBit([&](size_t index) {
|
||||||
|
copies.push_back({
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
||||||
|
.srcSet = srcSet,
|
||||||
|
.srcBinding = (uint32_t) index,
|
||||||
|
.dstSet = dstSet,
|
||||||
|
.dstBinding = (uint32_t) index,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
vkUpdateDescriptorSets(mDevice, 0, nullptr, copies.size(), copies.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ public:
|
|||||||
VkDeviceSize size) noexcept;
|
VkDeviceSize size) noexcept;
|
||||||
|
|
||||||
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler,
|
||||||
|
VkDescriptorSetLayout externalSamplerLayout = VK_NULL_HANDLE) noexcept;
|
||||||
|
|
||||||
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||||
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
|
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
|
||||||
@@ -90,8 +91,8 @@ public:
|
|||||||
void resetCachedState() noexcept { mLastBoundInfo = {}; }
|
void resetCachedState() noexcept { mLastBoundInfo = {}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
|
void copySet(VkDescriptorSet srcSet, VkDescriptorSet destSet,
|
||||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
fvkutils::SamplerBitmask copyBindings) const;
|
||||||
|
|
||||||
class DescriptorInfinitePool;
|
class DescriptorInfinitePool;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "vulkan/memory/ResourcePointer.h"
|
#include "vulkan/memory/ResourcePointer.h"
|
||||||
#include "vulkan/utils/Conversion.h"
|
#include "vulkan/utils/Conversion.h"
|
||||||
#include "vulkan/utils/Definitions.h"
|
#include "vulkan/utils/Definitions.h"
|
||||||
|
#include "vulkan/utils/Image.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/platforms/VulkanPlatform.h>
|
#include <backend/platforms/VulkanPlatform.h>
|
||||||
@@ -244,6 +245,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
|||||||
mQueryManager(mPlatform->getDevice()),
|
mQueryManager(mPlatform->getDevice()),
|
||||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||||
&mDescriptorSetLayoutCache),
|
&mDescriptorSetLayoutCache),
|
||||||
|
mStreamedImageManager(&mExternalImageManager),
|
||||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||||
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
|
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
|
||||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||||
@@ -420,12 +422,14 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
|||||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||||
|
|
||||||
if (mCurrentSwapChain) { // This should be guaranteed
|
// if frameId is 0, it means we're not associated to a particular frame, which is the case
|
||||||
|
// for standalone views. And in this case we skip the frame info timing collection.
|
||||||
|
if (frameId && mCurrentSwapChain) { // This should be guaranteed
|
||||||
mPlatform->setPresentFrameId(mCurrentSwapChain->swapChain, frameId);
|
mPlatform->setPresentFrameId(mCurrentSwapChain->swapChain, frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any command have finished and reset all its used resources. The resources
|
// 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
|
// won't be destroyed but their reference count will be decreased if the command is already
|
||||||
// completed.
|
// completed.
|
||||||
//
|
//
|
||||||
// This will let us check if any VulkanBuffer is currently in flight or not.
|
// This will let us check if any VulkanBuffer is currently in flight or not.
|
||||||
@@ -479,6 +483,9 @@ void VulkanDriver::updateDescriptorSetTexture(
|
|||||||
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) {
|
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) {
|
||||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||||
mAppState.hasBoundExternalImages = true;
|
mAppState.hasBoundExternalImages = true;
|
||||||
|
} else if (bool(texture->getStream())) {
|
||||||
|
mStreamedImageManager.bindStreamedTexture(set, binding, texture, params);
|
||||||
|
mAppState.hasBoundExternalImages = true;
|
||||||
} else {
|
} else {
|
||||||
VulkanSamplerCache::Params cacheParams = {
|
VulkanSamplerCache::Params cacheParams = {
|
||||||
.sampler = params,
|
.sampler = params,
|
||||||
@@ -486,6 +493,7 @@ void VulkanDriver::updateDescriptorSetTexture(
|
|||||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||||
mExternalImageManager.clearTextureBinding(set, binding);
|
mExternalImageManager.clearTextureBinding(set, binding);
|
||||||
|
mStreamedImageManager.unbindStreamedTexture(set, binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,10 +505,7 @@ void VulkanDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_
|
|||||||
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
|
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
|
||||||
assert_invariant(offset + data.size <= mmb->size);
|
assert_invariant(offset + data.size <= mmb->size);
|
||||||
|
|
||||||
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
|
// On UMA systems, this is a memcpy into the memory-mapped buffer, otherwise use a staging buffer.
|
||||||
// call updateBufferObject(). This could be a fallback implementation for when
|
|
||||||
// shared memory is not available.
|
|
||||||
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
|
|
||||||
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,6 +1113,12 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::destroyStream(Handle<HwStream> sh) {
|
void VulkanDriver::destroyStream(Handle<HwStream> sh) {
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
if (!sh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
|
||||||
|
stream.dec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::destroySync(Handle<HwSync> sh) {
|
void VulkanDriver::destroySync(Handle<HwSync> sh) {
|
||||||
@@ -1145,18 +1156,38 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
|
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
|
||||||
|
FVK_LOGW << "createStreamNative not supported in Vulkan.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) {
|
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) {
|
||||||
return {};
|
// @TODO This is still not thread-safe. We might have to revisit this question.
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
auto handle = mResourceManager.allocHandle<VulkanStream>();
|
||||||
|
auto stream = resource_ptr<VulkanStream>::make(&mResourceManager, handle);
|
||||||
|
stream.inc();
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
|
void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
|
||||||
CallbackHandler* handler, StreamCallback cb, void* userData) {
|
CallbackHandler* handler, StreamCallback cb, void* userData) {
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
|
||||||
|
assert_invariant(stream->streamType == StreamType::ACQUIRED);
|
||||||
|
assert_invariant(image != nullptr);
|
||||||
|
// This covers the case where setAcquiredImage is called back to back (no updateStreams in between)
|
||||||
|
if (stream->previousNeedsRelease()) {
|
||||||
|
scheduleRelease(stream->takePrevious());
|
||||||
|
}
|
||||||
|
stream->acquire({ image, cb, userData, handler});
|
||||||
|
mStreamsWithPendingAcquiredImage.push_back(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
void VulkanDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
|
||||||
|
stream->width = width;
|
||||||
|
stream->height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) {
|
int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) {
|
||||||
@@ -1164,6 +1195,70 @@ int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::updateStreams(CommandStream* driver) {
|
void VulkanDriver::updateStreams(CommandStream* driver) {
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
if (UTILS_UNLIKELY(!mStreamsWithPendingAcquiredImage.empty())) {
|
||||||
|
for (auto& stream: mStreamsWithPendingAcquiredImage) {
|
||||||
|
if (stream->previousNeedsRelease()) {
|
||||||
|
scheduleRelease(stream->takePrevious());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This executes on the backend thread (updateStreams is synchonous which means it
|
||||||
|
// executes on the user thread) Note: stream is captured by copy which is fine, this is
|
||||||
|
// a copy of a resource_ptr<VulkanStream>. We only need it find the associated stream
|
||||||
|
// inside the mStreamedImageManager texture bindings
|
||||||
|
driver->queueCommand([this, stream, s = stream.get(),
|
||||||
|
image = stream->getAcquired().image]() {
|
||||||
|
auto texture = s->getTexture(image);
|
||||||
|
if (!texture) {
|
||||||
|
auto externalImage = fvkutils::createExternalImageFromRaw(mPlatform, image, false);
|
||||||
|
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||||
|
auto imgData = mPlatform->createVkImageFromExternal(externalImage);
|
||||||
|
|
||||||
|
assert_invariant(imgData.internal.valid() || imgData.external.valid());
|
||||||
|
|
||||||
|
VkFormat vkformat = metadata.format;
|
||||||
|
VkImage vkimage = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
if (imgData.internal.valid()) {
|
||||||
|
metadata.externalFormat = 0;
|
||||||
|
vkimage = imgData.internal.image;
|
||||||
|
memory = imgData.internal.memory;
|
||||||
|
} else {
|
||||||
|
vkformat = VK_FORMAT_UNDEFINED;
|
||||||
|
vkimage = imgData.external.image;
|
||||||
|
memory = imgData.external.memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSamplerYcbcrConversion const conversion =
|
||||||
|
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
|
||||||
|
|
||||||
|
auto newTexture = resource_ptr<VulkanTexture>::construct(&mResourceManager,
|
||||||
|
mContext, mPlatform->getDevice(), mAllocator, &mResourceManager,
|
||||||
|
&mCommands, vkimage, memory, vkformat, conversion, metadata.samples,
|
||||||
|
metadata.width, metadata.height, metadata.layers,
|
||||||
|
metadata.filamentUsage, mStagePool);
|
||||||
|
|
||||||
|
auto& commands = mCommands.get();
|
||||||
|
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
||||||
|
// texture into the read layout.
|
||||||
|
newTexture->transitionLayout(&commands, newTexture->getPrimaryViewRange(),
|
||||||
|
VulkanLayout::FRAG_READ);
|
||||||
|
|
||||||
|
if (imgData.external.valid()) {
|
||||||
|
mExternalImageManager.addExternallySampledTexture(newTexture,
|
||||||
|
externalImage);
|
||||||
|
// Cache the AHB backed image. Acquires the image here.
|
||||||
|
s->pushImage(image, newTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = newTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStreamedImageManager.onStreamAcquireImage(texture, stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mStreamsWithPendingAcquiredImage.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::destroyFence(Handle<HwFence> fh) {
|
void VulkanDriver::destroyFence(Handle<HwFence> fh) {
|
||||||
@@ -1459,17 +1554,13 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
|||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
|
|
||||||
BufferObjectStreamDescriptor&& streams) {
|
|
||||||
// Noop
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||||
uint32_t byteOffset) {
|
uint32_t byteOffset) {
|
||||||
VulkanCommandBuffer& commands = mCommands.get();
|
VulkanCommandBuffer& commands = mCommands.get();
|
||||||
|
|
||||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||||
commands.acquire(bo);
|
commands.acquire(bo);
|
||||||
|
|
||||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||||
|
|
||||||
scheduleDestroy(std::move(bd));
|
scheduleDestroy(std::move(bd));
|
||||||
@@ -1537,6 +1628,12 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
||||||
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
auto t = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||||
|
assert_invariant(t);
|
||||||
|
auto s = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
|
||||||
|
assert_invariant(s);
|
||||||
|
t->setStream(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
|
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||||
|
|||||||
@@ -34,12 +34,14 @@
|
|||||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||||
#include "vulkan/VulkanExternalImageManager.h"
|
#include "vulkan/VulkanExternalImageManager.h"
|
||||||
|
#include "vulkan/VulkanStreamedImageManager.h"
|
||||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||||
#include "vulkan/memory/ResourceManager.h"
|
#include "vulkan/memory/ResourceManager.h"
|
||||||
#include "vulkan/memory/ResourcePointer.h"
|
#include "vulkan/memory/ResourcePointer.h"
|
||||||
#include "vulkan/utils/Definitions.h"
|
#include "vulkan/utils/Definitions.h"
|
||||||
|
|
||||||
#include "backend/DriverEnums.h"
|
#include "backend/DriverEnums.h"
|
||||||
|
#include "backend/BufferObjectStreamDescriptor.h"
|
||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
#include "private/backend/Driver.h"
|
#include "private/backend/Driver.h"
|
||||||
@@ -157,6 +159,8 @@ private:
|
|||||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||||
VulkanQueryManager mQueryManager;
|
VulkanQueryManager mQueryManager;
|
||||||
VulkanExternalImageManager mExternalImageManager;
|
VulkanExternalImageManager mExternalImageManager;
|
||||||
|
VulkanStreamedImageManager mStreamedImageManager;
|
||||||
|
|
||||||
|
|
||||||
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
|
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
|
||||||
// Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are
|
// Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are
|
||||||
@@ -202,6 +206,10 @@ private:
|
|||||||
bool const mIsSRGBSwapChainSupported;
|
bool const mIsSRGBSwapChainSupported;
|
||||||
bool const mIsMSAASwapChainSupported;
|
bool const mIsMSAASwapChainSupported;
|
||||||
backend::StereoscopicType const mStereoscopicType;
|
backend::StereoscopicType const mStereoscopicType;
|
||||||
|
|
||||||
|
// setAcquiredImage is a DECL_DRIVER_API_SYNCHRONOUS_N which means we don't necessarily have the
|
||||||
|
// data to process it at call time. So we store it and process it during updateStreams.
|
||||||
|
std::vector<resource_ptr<VulkanStream>> mStreamsWithPendingAcquiredImage;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ namespace filament::backend {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Bitmask = fvkutils::UniformBufferBitmask;
|
|
||||||
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
|
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
|
||||||
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
||||||
@@ -50,31 +47,6 @@ ImageData& findImage(std::vector<ImageData>& images,
|
|||||||
return *itr;
|
return *itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copySet(VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet, Bitmask bindings) {
|
|
||||||
// TODO: fix the size for better memory management
|
|
||||||
std::vector<VkCopyDescriptorSet> copies;
|
|
||||||
bindings.forEachSetBit([&](size_t index) {
|
|
||||||
copies.push_back({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
|
||||||
.srcSet = srcSet,
|
|
||||||
.srcBinding = (uint32_t) index,
|
|
||||||
.dstSet = dstSet,
|
|
||||||
.dstBinding = (uint32_t) index,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
vkUpdateDescriptorSets(device, 0, nullptr, copies.size(), copies.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmask foldBitsInHalf(Bitmask bitset) {
|
|
||||||
Bitmask outBitset;
|
|
||||||
bitset.forEachSetBit([&](size_t index) {
|
|
||||||
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
|
|
||||||
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
|
|
||||||
});
|
|
||||||
return outBitset;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace
|
}// namespace
|
||||||
|
|
||||||
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
||||||
@@ -141,6 +113,9 @@ void VulkanExternalImageManager::updateSetAndLayout(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto& imageData = findImage(mImages, bindingInfo.image);
|
auto& imageData = findImage(mImages, bindingInfo.image);
|
||||||
|
// For non YUV images (some ext images are NOT ext FMT)
|
||||||
|
// getVkSamplerYcbcrConversion(metadata) will return NULL, and image->conversion will be
|
||||||
|
// null
|
||||||
updateImage(&imageData);
|
updateImage(&imageData);
|
||||||
|
|
||||||
auto samplerParams = bindingInfo.samplerParams;
|
auto samplerParams = bindingInfo.samplerParams;
|
||||||
@@ -172,38 +147,14 @@ void VulkanExternalImageManager::updateSetAndLayout(
|
|||||||
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
||||||
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
|
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
|
||||||
|
|
||||||
VkDescriptorSetLayout const oldLayout = layout->getExternalSamplerVkLayout();
|
|
||||||
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
|
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
|
||||||
actualExternalSamplers, outSamplers);
|
actualExternalSamplers, outSamplers);
|
||||||
|
layout->setExternalSamplerVkLayout(newLayout);
|
||||||
// Need to copy the set
|
|
||||||
VkDescriptorSet const oldSet = set->getExternalSamplerVkSet();
|
|
||||||
if (oldLayout != newLayout || oldSet == VK_NULL_HANDLE) {
|
|
||||||
// Build a new descriptor set from the new layout
|
|
||||||
VkDescriptorSet const newSet = mDescriptorSetCache->getVkSet(layout->count, newLayout);
|
|
||||||
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
|
||||||
auto const samplers = layout->bitmask.sampler & (~actualExternalSamplers);
|
|
||||||
|
|
||||||
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
|
||||||
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
|
||||||
// bindings.
|
|
||||||
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
|
||||||
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
|
|
||||||
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
|
|
||||||
|
|
||||||
set->setExternalSamplerVkSet(newSet,
|
|
||||||
[&descriptorSetCache = mDescriptorSetCache, layoutCount = layout->count, newLayout,
|
|
||||||
newSet](VulkanDescriptorSet*) {
|
|
||||||
descriptorSetCache->manualRecycle(layoutCount, newLayout, newSet);
|
|
||||||
});
|
|
||||||
if (oldLayout != newLayout) {
|
|
||||||
layout->setExternalSamplerVkLayout(newLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the external samplers in the set
|
// Update the external samplers in the set
|
||||||
for (auto& [binding, sampler, image]: samplerAndBindings) {
|
for (auto& [binding, sampler, image]: samplerAndBindings) {
|
||||||
mDescriptorSetCache->updateSamplerForExternalSamplerSet(set, binding, image);
|
// We cannot call updateSamplerForExternalSamplerSet because some samplers are non NULL
|
||||||
|
// (RGB) and we cannot do a combined update with a NULL sampler.
|
||||||
|
mDescriptorSetCache->updateSampler(set, binding, image, sampler, newLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,13 +212,25 @@ void VulkanExternalImageManager::bindExternallySampledTexture(
|
|||||||
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
||||||
// Should we do duplicate validation here?
|
// Should we do duplicate validation here?
|
||||||
auto& imageData = findImage(mImages, image);
|
auto& imageData = findImage(mImages, image);
|
||||||
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
|
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
|
||||||
|
[&](SetBindingInfo const& binding) {
|
||||||
|
return (binding.set == set && binding.binding == bindingPoint);
|
||||||
|
});
|
||||||
|
if (itr == mSetBindings.end()) {
|
||||||
|
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
|
||||||
|
} else {
|
||||||
|
// override the image data in the binding point
|
||||||
|
itr->image = image;
|
||||||
|
itr->samplerParams = samplerParams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanExternalImageManager::addExternallySampledTexture(
|
void VulkanExternalImageManager::addExternallySampledTexture(
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image,
|
fvkmemory::resource_ptr<VulkanTexture> image,
|
||||||
Platform::ExternalImageHandleRef platformHandleRef) {
|
Platform::ExternalImageHandleRef platformHandleRef) {
|
||||||
mImages.push_back({ image, platformHandleRef, false });
|
// By passing VK_NULL_HANDLE which is already there by default.
|
||||||
|
// We make it clear that all default images do NOT have a chroma conversion.
|
||||||
|
mImages.push_back({ image, platformHandleRef, false, VK_NULL_HANDLE });
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanExternalImageManager::removeExternallySampledTexture(
|
void VulkanExternalImageManager::removeExternallySampledTexture(
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class VulkanSamplerCache;
|
|||||||
class VulkanDescriptorSetLayoutCache;
|
class VulkanDescriptorSetLayoutCache;
|
||||||
class VulkanDescriptorSetCache;
|
class VulkanDescriptorSetCache;
|
||||||
|
|
||||||
// Manages the logic of external images and their quirks wrt Vulikan.
|
// Manages the logic of external images and their quirks wrt Vulkan.
|
||||||
class VulkanExternalImageManager {
|
class VulkanExternalImageManager {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ namespace filament::backend {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// We don't have a good estimate for this magic number. This is used to remove empty
|
||||||
|
// slots from a descriptor set (a VulkanDescriptorset is backed by multiple
|
||||||
|
// VkDescriptorSet.
|
||||||
|
size_t DESCRIPTOR_SET_GC_LIMIT = 10;
|
||||||
|
|
||||||
inline VulkanBufferBinding getBufferObjectBinding(BufferObjectBinding bindingType) noexcept {
|
inline VulkanBufferBinding getBufferObjectBinding(BufferObjectBinding bindingType) noexcept {
|
||||||
switch (bindingType) {
|
switch (bindingType) {
|
||||||
case BufferObjectBinding::VERTEX:
|
case BufferObjectBinding::VERTEX:
|
||||||
@@ -192,15 +197,70 @@ VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayou
|
|||||||
return fromBackendLayout(layout);
|
return fromBackendLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will store an age associated with this command buffer into the VulkanBuffer, which
|
VulkanDescriptorSet::~VulkanDescriptorSet() {
|
||||||
// will allow us to determine whether a barrier is necessary or not.
|
for (auto setBundle: mSets) {
|
||||||
|
if (setBundle.onRecycleFn) {
|
||||||
|
setBundle.onRecycleFn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanDescriptorSet::VulkanDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||||
|
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
|
||||||
|
: dynamicUboMask(layout->bitmask.dynamicUbo),
|
||||||
|
uniqueDynamicUboCount(layout->count.dynamicUbo),
|
||||||
|
mLayout(layout),
|
||||||
|
mCurrentSetIndex(0) {
|
||||||
|
addNewSet(vkSet, std::move(onRecycleFn));
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) {
|
void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) {
|
||||||
|
// This will store an age associated with this command buffer into the VulkanBuffer, which
|
||||||
|
// will allow us to determine whether a barrier is necessary or not.
|
||||||
mUboMask.forEachSetBit([this, &commands](size_t index) {
|
mUboMask.forEachSetBit([this, &commands](size_t index) {
|
||||||
auto& res = mResources[index];
|
auto& res = mResources[index];
|
||||||
fvkmemory::resource_ptr<VulkanBufferObject> bo =
|
fvkmemory::resource_ptr<VulkanBufferObject> bo =
|
||||||
fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get());
|
fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get());
|
||||||
bo->referencedBy(commands);
|
bo->referencedBy(commands);
|
||||||
});
|
});
|
||||||
|
mSets[mCurrentSetIndex].fenceStatus = commands.getFenceStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDescriptorSet::gc() {
|
||||||
|
size_t empty = 0;
|
||||||
|
for (auto& setBundle: mSets) {
|
||||||
|
assert_invariant(setBundle.onRecycleFn);
|
||||||
|
if (setBundle.vkSet == VK_NULL_HANDLE) {
|
||||||
|
empty++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (setBundle.fenceStatus && setBundle.fenceStatus->getStatus() == VK_SUCCESS) {
|
||||||
|
setBundle.onRecycleFn(this);
|
||||||
|
setBundle.vkSet = VK_NULL_HANDLE;
|
||||||
|
setBundle.fenceStatus = {};
|
||||||
|
empty++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty > DESCRIPTOR_SET_GC_LIMIT) {
|
||||||
|
std::vector<InternalVkSet> retainedSets;
|
||||||
|
for (auto& setBundle: mSets) {
|
||||||
|
if (setBundle.vkSet != VK_NULL_HANDLE) {
|
||||||
|
retainedSets.push_back({
|
||||||
|
setBundle.vkSet,
|
||||||
|
std::move(setBundle.onRecycleFn),
|
||||||
|
std::move(setBundle.fenceStatus)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::swap(mSets, retainedSets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDescriptorSet::addNewSet(VkDescriptorSet vkSet, OnRecycle&& onRecycleFn) {
|
||||||
|
gc();
|
||||||
|
mCurrentSetIndex = mSets.size();
|
||||||
|
mSets.push_back({ vkSet, std::move(onRecycleFn) });
|
||||||
}
|
}
|
||||||
|
|
||||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
|
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "VulkanTexture.h"
|
#include "VulkanTexture.h"
|
||||||
#include "vulkan/VulkanCommands.h"
|
#include "vulkan/VulkanCommands.h"
|
||||||
#include "vulkan/memory/Resource.h"
|
#include "vulkan/memory/Resource.h"
|
||||||
|
#include "vulkan/memory/ResourcePointer.h"
|
||||||
#include "vulkan/utils/Definitions.h"
|
#include "vulkan/utils/Definitions.h"
|
||||||
#include "vulkan/utils/StaticVector.h"
|
#include "vulkan/utils/StaticVector.h"
|
||||||
|
|
||||||
@@ -57,6 +58,8 @@ inline uint8_t collapsedCount(Bitmask const& mask) {
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
class VulkanDescriptorSetCache;
|
||||||
|
|
||||||
struct VulkanBufferObject;
|
struct VulkanBufferObject;
|
||||||
|
|
||||||
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
|
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
|
||||||
@@ -159,38 +162,14 @@ public:
|
|||||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||||
|
|
||||||
VulkanDescriptorSet(
|
VulkanDescriptorSet(
|
||||||
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||||
uint8_t uniqueDynamicUboCount,
|
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet);
|
||||||
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
|
|
||||||
: dynamicUboMask(dynamicUboMask),
|
|
||||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
|
||||||
mVkSet(vkSet),
|
|
||||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(bugprone-exception-escape)
|
// NOLINTNEXTLINE(bugprone-exception-escape)
|
||||||
~VulkanDescriptorSet() {
|
~VulkanDescriptorSet();
|
||||||
if (mOnRecycleFn) {
|
|
||||||
mOnRecycleFn(this);
|
|
||||||
}
|
|
||||||
if (mOnRecycleExternalSamplerFn) {
|
|
||||||
mOnRecycleExternalSamplerFn(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkDescriptorSet getVkSet() const noexcept {
|
VkDescriptorSet getVkSet() const noexcept {
|
||||||
return mVkSet;
|
return mSets[mCurrentSetIndex].vkSet;
|
||||||
}
|
|
||||||
|
|
||||||
VkDescriptorSet getExternalSamplerVkSet() const noexcept {
|
|
||||||
return mExternalSamplerVkSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setExternalSamplerVkSet(VkDescriptorSet vkset, OnRecycle onRecycle) {
|
|
||||||
mExternalSamplerVkSet = vkset;
|
|
||||||
if (mOnRecycleExternalSamplerFn) {
|
|
||||||
mOnRecycleExternalSamplerFn(this);
|
|
||||||
}
|
|
||||||
mOnRecycleExternalSamplerFn = onRecycle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
||||||
@@ -211,17 +190,33 @@ public:
|
|||||||
|
|
||||||
void referencedBy(VulkanCommandBuffer& commands);
|
void referencedBy(VulkanCommandBuffer& commands);
|
||||||
|
|
||||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
bool isBound() const {
|
||||||
|
return bool(mSets[mCurrentSetIndex].fenceStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> getLayout() const { return mLayout; }
|
||||||
|
|
||||||
|
fvkutils::UniformBufferBitmask const& dynamicUboMask;
|
||||||
uint8_t const uniqueDynamicUboCount;
|
uint8_t const uniqueDynamicUboCount;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkDescriptorSet const mVkSet;
|
friend class VulkanDescriptorSetCache;
|
||||||
VkDescriptorSet mExternalSamplerVkSet = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
|
void addNewSet(VkDescriptorSet vkSet, OnRecycle&& onRecycleFn);
|
||||||
|
|
||||||
|
void gc();
|
||||||
|
|
||||||
|
struct InternalVkSet {
|
||||||
|
VkDescriptorSet vkSet = {};
|
||||||
|
OnRecycle onRecycleFn;
|
||||||
|
std::shared_ptr<VulkanCmdFence> fenceStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> mLayout;
|
||||||
backend::DescriptorSetOffsetArray mOffsets;
|
backend::DescriptorSetOffsetArray mOffsets;
|
||||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||||
OnRecycle mOnRecycleFn;
|
uint8_t mCurrentSetIndex;
|
||||||
OnRecycle mOnRecycleExternalSamplerFn;
|
std::vector<InternalVkSet> mSets;
|
||||||
fvkutils::UniformBufferBitmask mUboMask;
|
fvkutils::UniformBufferBitmask mUboMask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -191,9 +191,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
|||||||
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
|
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
|
||||||
.alphaToOneEnable = VK_FALSE,
|
.alphaToOneEnable = VK_FALSE,
|
||||||
};
|
};
|
||||||
|
bool const enableDepthTest =
|
||||||
|
raster.depthCompareOp != SamplerCompareFunc::A ||
|
||||||
|
raster.depthWriteEnable;
|
||||||
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||||
.depthTestEnable = VK_TRUE,
|
.depthTestEnable = enableDepthTest ? VK_TRUE : VK_FALSE,
|
||||||
.depthWriteEnable = raster.depthWriteEnable,
|
.depthWriteEnable = raster.depthWriteEnable,
|
||||||
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
|
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
|
||||||
.depthBoundsTestEnable = VK_FALSE,
|
.depthBoundsTestEnable = VK_FALSE,
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 "VulkanStreamedImageManager.h"
|
||||||
|
|
||||||
|
#include "VulkanDescriptorSetCache.h"
|
||||||
|
#include "VulkanExternalImageManager.h"
|
||||||
|
#include "VulkanDescriptorSetLayoutCache.h"
|
||||||
|
#include "VulkanSamplerCache.h"
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
VulkanStreamedImageManager::VulkanStreamedImageManager(VulkanExternalImageManager* manager)
|
||||||
|
: mExternalImageManager(manager) {}
|
||||||
|
|
||||||
|
VulkanStreamedImageManager::~VulkanStreamedImageManager() = default;
|
||||||
|
|
||||||
|
void VulkanStreamedImageManager::terminate() { mStreamedTexturesBindings.clear(); }
|
||||||
|
|
||||||
|
void VulkanStreamedImageManager::bindStreamedTexture(
|
||||||
|
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
|
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
|
||||||
|
SamplerParams samplerParams) {
|
||||||
|
mStreamedTexturesBindings.push_back({ bindingPoint, image, set, samplerParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanStreamedImageManager::unbindStreamedTexture(
|
||||||
|
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
|
uint8_t bindingPoint) {
|
||||||
|
auto iter = std::remove_if(mStreamedTexturesBindings.begin(), mStreamedTexturesBindings.end(),
|
||||||
|
[&](StreamedTextureBinding& binding) {
|
||||||
|
return ((binding.set == set) && (binding.binding == bindingPoint));
|
||||||
|
});
|
||||||
|
mStreamedTexturesBindings.erase(iter, mStreamedTexturesBindings.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanStreamedImageManager::onStreamAcquireImage(fvkmemory::resource_ptr<VulkanTexture> image,
|
||||||
|
fvkmemory::resource_ptr<VulkanStream> stream) {
|
||||||
|
for (StreamedTextureBinding const& data: mStreamedTexturesBindings) {
|
||||||
|
// Find the right stream
|
||||||
|
if (data.image->getStream() == stream) {
|
||||||
|
mExternalImageManager->bindExternallySampledTexture(data.set, data.binding, image,
|
||||||
|
data.samplerParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace filament::backend
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
|
||||||
|
#define TNT_FILAMENT_BACKEND_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
|
||||||
|
|
||||||
|
#include "VulkanHandles.h"
|
||||||
|
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class VulkanExternalImageManager;
|
||||||
|
class VulkanDescriptorSetCache;
|
||||||
|
class VulkanSamplerCache;
|
||||||
|
|
||||||
|
// Manages the logic of streamed images.
|
||||||
|
class VulkanStreamedImageManager {
|
||||||
|
public:
|
||||||
|
VulkanStreamedImageManager(
|
||||||
|
VulkanExternalImageManager* manager);
|
||||||
|
~VulkanStreamedImageManager();
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void bindStreamedTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
|
||||||
|
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams);
|
||||||
|
void unbindStreamedTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
|
uint8_t bindingPoint);
|
||||||
|
void onStreamAcquireImage(fvkmemory::resource_ptr<VulkanTexture> image,
|
||||||
|
fvkmemory::resource_ptr<VulkanStream> stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct StreamedTextureBinding {
|
||||||
|
uint8_t binding = 0;
|
||||||
|
fvkmemory::resource_ptr<VulkanTexture> image;
|
||||||
|
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
|
||||||
|
SamplerParams samplerParams;
|
||||||
|
};
|
||||||
|
// keep track of all the stream bindings
|
||||||
|
std::vector<StreamedTextureBinding> mStreamedTexturesBindings;
|
||||||
|
|
||||||
|
VulkanExternalImageManager* mExternalImageManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace filament::backend
|
||||||
|
|
||||||
|
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "VulkanCommands.h"
|
#include "VulkanCommands.h"
|
||||||
#include "VulkanTexture.h"
|
#include "VulkanTexture.h"
|
||||||
|
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
@@ -105,6 +106,11 @@ void VulkanSwapChain::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanSwapChain::present(DriverBase& driver) {
|
void VulkanSwapChain::present(DriverBase& driver) {
|
||||||
|
// The last acquire failed, so just skip presenting.
|
||||||
|
if (!mAcquired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
||||||
VulkanCommandBuffer& commands = mCommands->get();
|
VulkanCommandBuffer& commands = mCommands->get();
|
||||||
VkImageSubresourceRange const subresources{
|
VkImageSubresourceRange const subresources{
|
||||||
@@ -161,7 +167,15 @@ void VulkanSwapChain::acquire(bool& resized) {
|
|||||||
|
|
||||||
VulkanPlatform::ImageSyncData imageSyncData;
|
VulkanPlatform::ImageSyncData imageSyncData;
|
||||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
// We just don't set mAcquired here so the next present will just skip.
|
||||||
|
FVK_LOGD << "Failed to acquire next image in the swapchain result=" << (int) result;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||||
|
assert_invariant(mCurrentSwapIndex < mFinishedDrawing.size());
|
||||||
mFinishedDrawing[mCurrentSwapIndex] = {};
|
mFinishedDrawing[mCurrentSwapIndex] = {};
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||||
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
||||||
|
|||||||
@@ -36,6 +36,43 @@ namespace filament::backend {
|
|||||||
|
|
||||||
struct VulkanTexture;
|
struct VulkanTexture;
|
||||||
|
|
||||||
|
struct VulkanStream : public HwStream, fvkmemory::Resource {
|
||||||
|
|
||||||
|
//-- These methods are only called from the frontend
|
||||||
|
void acquire(const AcquiredImage& image) {
|
||||||
|
user_thread.mPrevious = user_thread.mAcquired;
|
||||||
|
user_thread.mAcquired = image;
|
||||||
|
}
|
||||||
|
bool previousNeedsRelease() const { return (user_thread.mPrevious.image != nullptr); }
|
||||||
|
// this function will null the previous once the caller takes it.
|
||||||
|
// It ensures we don't schedule for release twice.
|
||||||
|
AcquiredImage takePrevious() {
|
||||||
|
AcquiredImage previous = user_thread.mPrevious;
|
||||||
|
user_thread.mPrevious = {};
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
const AcquiredImage& getAcquired() const { return user_thread.mAcquired; }
|
||||||
|
|
||||||
|
//-- These methods are only called from the backend thread
|
||||||
|
fvkmemory::resource_ptr<VulkanTexture> getTexture(void* ahb) {
|
||||||
|
if (auto itr = mTextures.find(ahb); itr != mTextures.end()) {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
void pushImage(void* ahb, fvkmemory::resource_ptr<VulkanTexture> tex) { mTextures[ahb] = tex; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// These are only called from the frontend
|
||||||
|
struct {
|
||||||
|
AcquiredImage mAcquired;
|
||||||
|
AcquiredImage mPrevious;
|
||||||
|
} user_thread;
|
||||||
|
|
||||||
|
// #TODO b/442937292
|
||||||
|
std::unordered_map<void*, fvkmemory::resource_ptr<VulkanTexture>> mTextures;
|
||||||
|
};
|
||||||
|
|
||||||
struct VulkanTextureState : public fvkmemory::Resource {
|
struct VulkanTextureState : public fvkmemory::Resource {
|
||||||
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
|
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
|
||||||
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
|
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
|
||||||
@@ -74,6 +111,8 @@ private:
|
|||||||
|
|
||||||
// The texture with the sidecar owns the sidecar.
|
// The texture with the sidecar owns the sidecar.
|
||||||
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
|
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
|
||||||
|
// The stream this texture is associated with.
|
||||||
|
fvkmemory::resource_ptr<VulkanStream> mStream;
|
||||||
|
|
||||||
VkImage const mTextureImage;
|
VkImage const mTextureImage;
|
||||||
VkDeviceMemory const mTextureImageMemory;
|
VkDeviceMemory const mTextureImageMemory;
|
||||||
@@ -105,6 +144,7 @@ private:
|
|||||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||||
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
||||||
|
|
||||||
|
|
||||||
friend struct VulkanTexture;
|
friend struct VulkanTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,6 +222,14 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
|||||||
return mState->mSidecarMSAA;
|
return mState->mSidecarMSAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setStream(fvkmemory::resource_ptr<VulkanStream> stream) {
|
||||||
|
mState->mStream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
fvkmemory::resource_ptr<VulkanStream> getStream() const {
|
||||||
|
return mState->mStream;
|
||||||
|
}
|
||||||
|
|
||||||
bool isTransientAttachment() const {
|
bool isTransientAttachment() const {
|
||||||
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
|
|||||||
template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
||||||
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
||||||
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
||||||
|
template ResourceType getTypeEnum<VulkanStream>() noexcept;
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
ResourceType getTypeEnum() noexcept {
|
ResourceType getTypeEnum() noexcept {
|
||||||
@@ -104,6 +105,9 @@ ResourceType getTypeEnum() noexcept {
|
|||||||
if constexpr (std::is_same_v<D, VulkanSemaphore>) {
|
if constexpr (std::is_same_v<D, VulkanSemaphore>) {
|
||||||
return ResourceType::SEMAPHORE;
|
return ResourceType::SEMAPHORE;
|
||||||
}
|
}
|
||||||
|
if constexpr (std::is_same_v<D, VulkanStream>) {
|
||||||
|
return ResourceType::STREAM;
|
||||||
|
}
|
||||||
return ResourceType::UNDEFINED_TYPE;
|
return ResourceType::UNDEFINED_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +153,8 @@ std::string_view getTypeStr(ResourceType type) {
|
|||||||
return "VulkanMemoryMappedBuffer";
|
return "VulkanMemoryMappedBuffer";
|
||||||
case ResourceType::SEMAPHORE:
|
case ResourceType::SEMAPHORE:
|
||||||
return "Semaphore";
|
return "Semaphore";
|
||||||
|
case ResourceType::STREAM:
|
||||||
|
return "VulkanStream";
|
||||||
case ResourceType::UNDEFINED_TYPE:
|
case ResourceType::UNDEFINED_TYPE:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ enum class ResourceType : uint8_t {
|
|||||||
SYNC = 17,
|
SYNC = 17,
|
||||||
MEMORY_MAPPED_BUFFER = 18,
|
MEMORY_MAPPED_BUFFER = 18,
|
||||||
SEMAPHORE = 19,
|
SEMAPHORE = 19,
|
||||||
UNDEFINED_TYPE = 20, // Must be the last enum because we use it for iterating over the enums.
|
STREAM = 20,
|
||||||
|
UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
|||||||
case ResourceType::SEMAPHORE:
|
case ResourceType::SEMAPHORE:
|
||||||
destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id));
|
destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id));
|
||||||
break;
|
break;
|
||||||
|
case ResourceType::STREAM:
|
||||||
|
destruct<VulkanStream>(Handle<VulkanStream>(id));
|
||||||
|
break;
|
||||||
case ResourceType::UNDEFINED_TYPE:
|
case ResourceType::UNDEFINED_TYPE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
|
|
||||||
#include "vulkan/VulkanTexture.h"
|
#include "vulkan/VulkanTexture.h"
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include "backend/platforms/VulkanPlatformAndroid.h"
|
||||||
|
#include <android/hardware_buffer.h>
|
||||||
|
#include <android/native_window.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/debug.h>
|
#include <utils/debug.h>
|
||||||
@@ -234,6 +240,19 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
|||||||
return mostSignificantBit((sampleCount - 1) & mask);
|
return mostSignificantBit((sampleCount - 1) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filament::backend::Platform::ExternalImageHandle createExternalImageFromRaw(
|
||||||
|
filament::backend::VulkanPlatform* platform, void* image, bool sRGB) {
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
if (__builtin_available(android 26, *)) {
|
||||||
|
return static_cast<filament::backend::VulkanPlatformAndroid*>(platform)
|
||||||
|
->createExternalImage(reinterpret_cast<AHardwareBuffer const*>(image), sRGB);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace filament::backend::fvkutils
|
} // namespace filament::backend::fvkutils
|
||||||
|
|
||||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_IMAGE_H
|
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_IMAGE_H
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
|
#include <backend/platforms/VulkanPlatform.h>
|
||||||
|
|
||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
|
|
||||||
@@ -106,6 +107,9 @@ VkImageAspectFlags getImageAspect(VkFormat format);
|
|||||||
|
|
||||||
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
|
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
|
||||||
|
|
||||||
|
Platform::ExternalImageHandle createExternalImageFromRaw(filament::backend::VulkanPlatform* platform,
|
||||||
|
void* image,
|
||||||
|
bool sRGB);
|
||||||
} // namespace fvkutils
|
} // namespace fvkutils
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "WebGPUConstants.h"
|
#include "WebGPUConstants.h"
|
||||||
#include "WebGPUQueueManager.h"
|
#include "WebGPUQueueManager.h"
|
||||||
|
#include "WebGPUStagePool.h"
|
||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
#include <backend/BufferDescriptor.h>
|
#include <backend/BufferDescriptor.h>
|
||||||
@@ -65,7 +66,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
|
|||||||
// of 4 by padding with zeros.
|
// of 4 by padding with zeros.
|
||||||
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
||||||
const uint32_t byteOffset, wgpu::Device const& device,
|
const uint32_t byteOffset, wgpu::Device const& device,
|
||||||
WebGPUQueueManager* const webGPUQueueManager) {
|
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool) {
|
||||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
||||||
<< "updateGPUBuffer called with a null buffer";
|
<< "updateGPUBuffer called with a null buffer";
|
||||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= mBuffer.GetSize())
|
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= mBuffer.GetSize())
|
||||||
@@ -85,15 +86,12 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
|||||||
const size_t stagingBufferSize =
|
const size_t stagingBufferSize =
|
||||||
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
||||||
|
|
||||||
// create a staging buffer
|
wgpu::Buffer stagingBuffer = webGPUStagePool->acquireBuffer(stagingBufferSize,
|
||||||
wgpu::BufferDescriptor descriptor{
|
webGPUQueueManager->getLatestSubmissionState());
|
||||||
.label = "Filament WebGPU Staging Buffer",
|
|
||||||
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
|
||||||
.size = stagingBufferSize,
|
|
||||||
.mappedAtCreation = true };
|
|
||||||
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
|
|
||||||
|
|
||||||
void* mappedRange = stagingBuffer.GetMappedRange();
|
void* mappedRange = stagingBuffer.GetMappedRange();
|
||||||
|
assert_invariant(mappedRange);
|
||||||
|
|
||||||
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
|
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
|
||||||
|
|
||||||
// Make sure the padded memory is set to 0 to have deterministic behaviors
|
// Make sure the padded memory is set to 0 to have deterministic behaviors
|
||||||
@@ -106,7 +104,9 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
|||||||
|
|
||||||
// Copy the staging buffer contents to the destination buffer.
|
// Copy the staging buffer contents to the destination buffer.
|
||||||
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
|
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
|
||||||
byteOffset, stagingBufferSize);
|
byteOffset,
|
||||||
|
remainder == 0 ? bufferDescriptor.size
|
||||||
|
: mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace filament::backend {
|
|||||||
|
|
||||||
class BufferDescriptor;
|
class BufferDescriptor;
|
||||||
class WebGPUQueueManager;
|
class WebGPUQueueManager;
|
||||||
|
class WebGPUStagePool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for WebGPU buffer objects, providing common functionality for creating and
|
* A base class for WebGPU buffer objects, providing common functionality for creating and
|
||||||
@@ -40,7 +41,7 @@ public:
|
|||||||
* ensures the calls happen in the expected sequence.
|
* ensures the calls happen in the expected sequence.
|
||||||
*/
|
*/
|
||||||
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device,
|
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device,
|
||||||
WebGPUQueueManager* const webGPUQueueManager);
|
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool);
|
||||||
|
|
||||||
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
|
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
|||||||
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
||||||
mDevice{ mPlatform.requestDevice(mAdapter) },
|
mDevice{ mPlatform.requestDevice(mAdapter) },
|
||||||
mQueueManager{ mDevice },
|
mQueueManager{ mDevice },
|
||||||
|
mStagePool{ mDevice },
|
||||||
mPipelineLayoutCache{ mDevice },
|
mPipelineLayoutCache{ mDevice },
|
||||||
mPipelineCache{ mDevice },
|
mPipelineCache{ mDevice },
|
||||||
mRenderPassMipmapGenerator{ mDevice, &mQueueManager },
|
mRenderPassMipmapGenerator{ mDevice, &mQueueManager },
|
||||||
@@ -177,6 +178,9 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
|||||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||||
mCurrentDescriptorSets[i] = {};
|
mCurrentDescriptorSets[i] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Garbage collection (if necessary)
|
||||||
|
mStagePool.gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a command encoder is in flight then the encoder is finished and submitted to the GPU queue.
|
// If a command encoder is in flight then the encoder is finished and submitted to the GPU queue.
|
||||||
@@ -651,11 +655,6 @@ void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math
|
|||||||
//todo
|
//todo
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> bufferObjectHandle,
|
|
||||||
BufferObjectStreamDescriptor&& streams) {
|
|
||||||
//todo
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
||||||
//todo
|
//todo
|
||||||
}
|
}
|
||||||
@@ -856,7 +855,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
|
|||||||
// draw calls are made.
|
// draw calls are made.
|
||||||
flush();
|
flush();
|
||||||
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
|
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,14 +866,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
|
|||||||
// draw calls are made.
|
// draw calls are made.
|
||||||
flush();
|
flush();
|
||||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
||||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
||||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "webgpu/WebGPUPipelineLayoutCache.h"
|
#include "webgpu/WebGPUPipelineLayoutCache.h"
|
||||||
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
||||||
#include "webgpu/WebGPUQueueManager.h"
|
#include "webgpu/WebGPUQueueManager.h"
|
||||||
|
#include "webgpu/WebGPUStagePool.h"
|
||||||
#include "webgpu/utils/AsyncTaskCounter.h"
|
#include "webgpu/utils/AsyncTaskCounter.h"
|
||||||
#include <backend/platforms/WebGPUPlatform.h>
|
#include <backend/platforms/WebGPUPlatform.h>
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ private:
|
|||||||
wgpu::Device mDevice = nullptr;
|
wgpu::Device mDevice = nullptr;
|
||||||
wgpu::Limits mDeviceLimits = {};
|
wgpu::Limits mDeviceLimits = {};
|
||||||
WebGPUQueueManager mQueueManager;
|
WebGPUQueueManager mQueueManager;
|
||||||
|
WebGPUStagePool mStagePool;
|
||||||
void* mNativeWindow = nullptr;
|
void* mNativeWindow = nullptr;
|
||||||
WebGPUSwapChain* mSwapChain = nullptr;
|
WebGPUSwapChain* mSwapChain = nullptr;
|
||||||
uint64_t mNextFakeHandle = 1;
|
uint64_t mNextFakeHandle = 1;
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
|
|||||||
};
|
};
|
||||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||||
ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder.");
|
ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder.");
|
||||||
|
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
|
||||||
}
|
}
|
||||||
return mCommandEncoder;
|
return mCommandEncoder;
|
||||||
}
|
}
|
||||||
@@ -94,8 +95,6 @@ void WebGPUQueueManager::submit() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
|
|
||||||
|
|
||||||
wgpu::CommandBufferDescriptor commandBufferDescriptor{
|
wgpu::CommandBufferDescriptor commandBufferDescriptor{
|
||||||
.label = "Filament Command Buffer",
|
.label = "Filament Command Buffer",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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 "WebGPUStagePool.h"
|
||||||
|
|
||||||
|
#include "WebGPUConstants.h"
|
||||||
|
#include "WebGPUQueueManager.h"
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
WebGPUStagePool::WebGPUStagePool(wgpu::Device const& device) : mDevice(device) {}
|
||||||
|
|
||||||
|
WebGPUStagePool::~WebGPUStagePool() = default;
|
||||||
|
|
||||||
|
wgpu::Buffer WebGPUStagePool::acquireBuffer(size_t requiredSize,
|
||||||
|
std::shared_ptr<WebGPUSubmissionState> submissionState) {
|
||||||
|
wgpu::Buffer buffer;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
auto iter = mBuffers.lower_bound(requiredSize);
|
||||||
|
if (iter != mBuffers.end()) {
|
||||||
|
buffer = iter->second;
|
||||||
|
mBuffers.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!buffer.Get()) {
|
||||||
|
buffer = createNewBuffer(requiredSize);
|
||||||
|
}
|
||||||
|
mInProgress.push_back({submissionState, buffer});
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebGPUStagePool::recycleBuffer(wgpu::Buffer buffer) {
|
||||||
|
struct UserData final {
|
||||||
|
wgpu::Buffer buffer;
|
||||||
|
WebGPUStagePool* webGPUStagePool;
|
||||||
|
};
|
||||||
|
auto userData =
|
||||||
|
std::make_unique<UserData>(UserData{ .buffer = buffer, .webGPUStagePool = this });
|
||||||
|
buffer.MapAsync(wgpu::MapMode::Write, 0, buffer.GetSize(), wgpu::CallbackMode::AllowSpontaneous,
|
||||||
|
[data = std::move(userData)](wgpu::MapAsyncStatus status, const char* message) {
|
||||||
|
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
|
||||||
|
if (!data->webGPUStagePool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(data->webGPUStagePool->mMutex);
|
||||||
|
data->webGPUStagePool->mBuffers.insert(
|
||||||
|
{ data->buffer.GetSize(), data->buffer });
|
||||||
|
} else {
|
||||||
|
FWGPU_LOGE << "Failed to MapAsync when recycling staging buffer: " << message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebGPUStagePool::gc() {
|
||||||
|
// We found that MapAsync would sometimes lead to GetMappedRange returning nullptr if the
|
||||||
|
// command using that staging buffer has not finished executing, so here we only recycle those
|
||||||
|
// buffers that are not still being used by any command
|
||||||
|
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> stillInProgress;
|
||||||
|
for (auto& [st, buffer]: mInProgress) {
|
||||||
|
if (st->getStatus() == FenceStatus::CONDITION_SATISFIED) {
|
||||||
|
recycleBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
stillInProgress.push_back({st, buffer});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::swap(mInProgress, stillInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::Buffer WebGPUStagePool::createNewBuffer(size_t bufferSize) {
|
||||||
|
wgpu::BufferDescriptor descriptor{
|
||||||
|
.label = "Filament WebGPU Staging Buffer",
|
||||||
|
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
||||||
|
.size = bufferSize,
|
||||||
|
.mappedAtCreation = true };
|
||||||
|
return mDevice.CreateBuffer(&descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace filament::backend
|
||||||
@@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
|
||||||
|
#define TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
|
||||||
|
|
||||||
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
struct WebGPUSubmissionState;
|
||||||
|
|
||||||
|
class WebGPUStagePool {
|
||||||
|
public:
|
||||||
|
WebGPUStagePool(wgpu::Device const& device);
|
||||||
|
~WebGPUStagePool();
|
||||||
|
|
||||||
|
wgpu::Buffer acquireBuffer(size_t requiredSize,
|
||||||
|
std::shared_ptr<WebGPUSubmissionState> submissionState);
|
||||||
|
void recycleBuffer(wgpu::Buffer buffer);
|
||||||
|
void gc();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wgpu::Buffer createNewBuffer(size_t bufferSize);
|
||||||
|
std::multimap<uint32_t, wgpu::Buffer> mBuffers;
|
||||||
|
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> mInProgress;
|
||||||
|
std::mutex mMutex;
|
||||||
|
|
||||||
|
wgpu::Device mDevice;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace filament::backend
|
||||||
|
|
||||||
|
#endif // TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
|
||||||
@@ -192,6 +192,7 @@ public:
|
|||||||
using StereoscopicType = backend::StereoscopicType;
|
using StereoscopicType = backend::StereoscopicType;
|
||||||
using Driver = backend::Driver;
|
using Driver = backend::Driver;
|
||||||
using GpuContextPriority = backend::Platform::GpuContextPriority;
|
using GpuContextPriority = backend::Platform::GpuContextPriority;
|
||||||
|
using AsynchronousMode = backend::AsynchronousMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config is used to define the memory footprint used by the engine, such as the
|
* Config is used to define the memory footprint used by the engine, such as the
|
||||||
@@ -428,6 +429,11 @@ public:
|
|||||||
* initial memory usage.
|
* initial memory usage.
|
||||||
*/
|
*/
|
||||||
uint32_t sharedUboInitialSizeInBytes = 256 * 64;
|
uint32_t sharedUboInitialSizeInBytes = 256 * 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
|
||||||
|
*/
|
||||||
|
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -742,6 +748,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
|
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the engine is set up for asynchronous operation. If it returns true, the
|
||||||
|
* asynchronous versions of the APIs are available for use.
|
||||||
|
*
|
||||||
|
* @return true if the engine supports asynchronous operation.
|
||||||
|
*/
|
||||||
|
bool isAsynchronousOperationSupported() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the configuration settings of this Engine.
|
* Retrieves the configuration settings of this Engine.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -69,6 +69,20 @@ public:
|
|||||||
using ShaderModel = backend::ShaderModel;
|
using ShaderModel = backend::ShaderModel;
|
||||||
using SubpassType = backend::SubpassType;
|
using SubpassType = backend::SubpassType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether a material instance should use UBO batching or not.
|
||||||
|
*/
|
||||||
|
enum class UboBatchingMode {
|
||||||
|
/**
|
||||||
|
* For default, it follows the engine settings.
|
||||||
|
* If UBO batching is enabled on the engine and the material domain is SURFACE, it
|
||||||
|
* turns on the UBO batching. Otherwise, it turns off the UBO batching.
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
//! Disable the Ubo Batching for this material
|
||||||
|
DISABLED,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds information about a material parameter.
|
* Holds information about a material parameter.
|
||||||
*/
|
*/
|
||||||
@@ -165,6 +179,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept;
|
Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the batching mode of the instances created from this material.
|
||||||
|
* @param uboBatchingMode
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Builder& uboBatching(UboBatchingMode uboBatchingMode) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the Material object and returns a pointer to it.
|
* Creates the Material object and returns a pointer to it.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -455,6 +455,10 @@ bool Engine::isStereoSupported(StereoscopicType) const noexcept {
|
|||||||
return downcast(this)->isStereoSupported();
|
return downcast(this)->isStereoSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Engine::isAsynchronousOperationSupported() const noexcept {
|
||||||
|
return downcast(this)->isAsynchronousOperationSupported();
|
||||||
|
}
|
||||||
|
|
||||||
size_t Engine::getMaxStereoscopicEyes() noexcept {
|
size_t Engine::getMaxStereoscopicEyes() noexcept {
|
||||||
return FEngine::getMaxStereoscopicEyes();
|
return FEngine::getMaxStereoscopicEyes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,11 +101,24 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
|
|||||||
auto& history = mFrameTimeHistory;
|
auto& history = mFrameTimeHistory;
|
||||||
// don't exceed the capacity, drop the oldest entry
|
// don't exceed the capacity, drop the oldest entry
|
||||||
if (UTILS_LIKELY(history.size() == history.capacity())) {
|
if (UTILS_LIKELY(history.size() == history.capacity())) {
|
||||||
if (!mDisableGpuFrameComplete) {
|
FrameInfoImpl& frameInfo = history.back();
|
||||||
assert_invariant(history.back().fence);
|
if (frameInfo.ready.load(std::memory_order_relaxed)) {
|
||||||
driver.destroyFence(std::move(history.back().fence));
|
if (!mDisableGpuFrameComplete) {
|
||||||
|
assert_invariant(frameInfo.fence);
|
||||||
|
driver.destroyFence(std::move(frameInfo.fence));
|
||||||
|
}
|
||||||
|
history.pop_back();
|
||||||
|
} else {
|
||||||
|
// This is a big problem, we ran out of space in the circular queue and that entry
|
||||||
|
// hasn't been processed yet. Because the code below keeps a reference to the
|
||||||
|
// front element of the queue, we can't pop/push. Our only option is to not record
|
||||||
|
// a new entry for this frame, which will create a false skipped frame in the
|
||||||
|
// data.
|
||||||
|
LOG(WARNING) << "FrameInfo's circular queue is full, but the oldest item hasn't "
|
||||||
|
"been processed yet. Skipping this frame, id = " << frameId;
|
||||||
|
mLastBeginFrameSkipped = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
history.pop_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new entry
|
// create a new entry
|
||||||
@@ -193,6 +206,14 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
|
void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
|
||||||
|
if (mLastBeginFrameSkipped) {
|
||||||
|
// if we had to skip the last beginFrame(), endFrame() needs to be skipped too
|
||||||
|
// because history.front() now references the wrong frame.
|
||||||
|
// It is guaranteed that if beginFrame() is called, endFrame() will be called too.
|
||||||
|
mLastBeginFrameSkipped = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& front = mFrameTimeHistory.front();
|
auto& front = mFrameTimeHistory.front();
|
||||||
front.endFrame = std::chrono::steady_clock::now();
|
front.endFrame = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
|||||||
@@ -95,33 +95,8 @@ struct FrameInfoImpl : public details::FrameInfo {
|
|||||||
assert_invariant(!fence);
|
assert_invariant(!fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameInfoImpl(FrameInfoImpl&& rhs) noexcept :
|
FrameInfoImpl(FrameInfoImpl& rhs) noexcept = delete;
|
||||||
details::FrameInfo(rhs),
|
FrameInfoImpl& operator=(FrameInfoImpl& rhs) noexcept = delete;
|
||||||
frameId(rhs.frameId),
|
|
||||||
beginFrame(rhs.beginFrame),
|
|
||||||
endFrame(rhs.endFrame),
|
|
||||||
backendBeginFrame(rhs.backendBeginFrame),
|
|
||||||
backendEndFrame(rhs.backendEndFrame),
|
|
||||||
gpuFrameComplete(rhs.gpuFrameComplete),
|
|
||||||
vsync(rhs.vsync),
|
|
||||||
fence(rhs.fence),
|
|
||||||
ready(rhs.ready.load())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameInfoImpl& operator=(FrameInfoImpl&& rhs) noexcept {
|
|
||||||
details::FrameInfo::operator=(rhs);
|
|
||||||
frameId = rhs.frameId;
|
|
||||||
beginFrame = rhs.beginFrame;
|
|
||||||
endFrame = rhs.endFrame;
|
|
||||||
backendBeginFrame = rhs.backendBeginFrame;
|
|
||||||
backendEndFrame = rhs.backendEndFrame;
|
|
||||||
gpuFrameComplete = rhs.gpuFrameComplete;
|
|
||||||
vsync = rhs.vsync;
|
|
||||||
fence = rhs.fence;
|
|
||||||
ready.store(rhs.ready.load());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, size_t CAPACITY>
|
template<typename T, size_t CAPACITY>
|
||||||
@@ -340,6 +315,7 @@ private:
|
|||||||
FrameHistoryQueue mFrameTimeHistory{};
|
FrameHistoryQueue mFrameTimeHistory{};
|
||||||
utils::AsyncJobQueue mJobQueue;
|
utils::AsyncJobQueue mJobQueue;
|
||||||
FSwapChain* mLastSeenSwapChain = nullptr;
|
FSwapChain* mLastSeenSwapChain = nullptr;
|
||||||
|
bool mLastBeginFrameSkipped = false;
|
||||||
bool const mHasTimerQueries = false;
|
bool const mHasTimerQueries = false;
|
||||||
bool const mDisableGpuFrameComplete = false;
|
bool const mDisableGpuFrameComplete = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ using namespace filaflat;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
||||||
return {
|
Platform::DriverConfig driverConfig {
|
||||||
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
||||||
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
|
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
|
||||||
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
|
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
|
||||||
@@ -116,7 +116,11 @@ backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
|||||||
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
||||||
.vulkanEnableStagingBufferBypass =
|
.vulkanEnableStagingBufferBypass =
|
||||||
instance->features.backend.vulkan.enable_staging_buffer_bypass,
|
instance->features.backend.vulkan.enable_staging_buffer_bypass,
|
||||||
|
.asynchronousMode = instance->features.backend.enable_asynchronous_operation ?
|
||||||
|
instance->getConfig().asynchronousMode : AsynchronousMode::NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return driverConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous
|
} // anonymous
|
||||||
@@ -727,7 +731,6 @@ void FEngine::prepare() {
|
|||||||
// post-process materials instances must be commited explicitly because their
|
// post-process materials instances must be commited explicitly because their
|
||||||
// parameters are typically not set at this point in time.
|
// parameters are typically not set at this point in time.
|
||||||
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
|
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||||
item->commitStreamUniformAssociations(driver);
|
|
||||||
item->commit(driver, uboManager);
|
item->commit(driver, uboManager);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -991,10 +994,9 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
|
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
||||||
UboBatchingMode batchingMode) noexcept {
|
const char* name) noexcept {
|
||||||
FMaterialInstance* p =
|
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name);
|
||||||
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
|
|
||||||
if (UTILS_LIKELY(p)) {
|
if (UTILS_LIKELY(p)) {
|
||||||
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
|
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
|
||||||
pos.first->second.insert(p);
|
pos.first->second.insert(p);
|
||||||
@@ -1503,6 +1505,11 @@ Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
|
|||||||
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
|
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FEngine::isAsynchronousOperationSupported() const noexcept {
|
||||||
|
return features.backend.enable_asynchronous_operation &&
|
||||||
|
mConfig.asynchronousMode != AsynchronousMode::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__)
|
#if defined(__EMSCRIPTEN__)
|
||||||
void FEngine::resetBackendState() noexcept {
|
void FEngine::resetBackendState() noexcept {
|
||||||
getDriverApi().resetState();
|
getDriverApi().resetState();
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ public:
|
|||||||
return getDriver().isStereoSupported();
|
return getDriver().isStereoSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAsynchronousOperationSupported() const noexcept;
|
||||||
|
|
||||||
static size_t getMaxStereoscopicEyes() noexcept {
|
static size_t getMaxStereoscopicEyes() noexcept {
|
||||||
return CONFIG_MAX_STEREOSCOPIC_EYES;
|
return CONFIG_MAX_STEREOSCOPIC_EYES;
|
||||||
}
|
}
|
||||||
@@ -345,21 +347,10 @@ public:
|
|||||||
|
|
||||||
FRenderer* createRenderer() noexcept;
|
FRenderer* createRenderer() noexcept;
|
||||||
|
|
||||||
// Defines whether a material instance should use UBO batching or not.
|
|
||||||
enum class UboBatchingMode {
|
|
||||||
// For default, it follows the engine settings.
|
|
||||||
// If UBO batching is enabled on the engine and the material domain is not SURFACE, it
|
|
||||||
// turns on the UBO batching. Otherwise, it turns off the UBO batching.
|
|
||||||
DEFAULT,
|
|
||||||
NO_UBO_BATCHING,
|
|
||||||
UBO_BATCHING
|
|
||||||
};
|
|
||||||
|
|
||||||
FMaterialInstance* createMaterialInstance(const FMaterial* material,
|
FMaterialInstance* createMaterialInstance(const FMaterial* material,
|
||||||
const FMaterialInstance* other, const char* name) noexcept;
|
const FMaterialInstance* other, const char* name) noexcept;
|
||||||
|
|
||||||
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
|
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name) noexcept;
|
||||||
UboBatchingMode batchingMode) noexcept;
|
|
||||||
|
|
||||||
FScene* createScene() noexcept;
|
FScene* createScene() noexcept;
|
||||||
FView* createView() noexcept;
|
FView* createView() noexcept;
|
||||||
@@ -780,7 +771,7 @@ public:
|
|||||||
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
||||||
} debug;
|
} debug;
|
||||||
struct {
|
struct {
|
||||||
bool disable_gpu_frame_complete_metric = false;
|
bool disable_gpu_frame_complete_metric = true;
|
||||||
} frame_info;
|
} frame_info;
|
||||||
} engine;
|
} engine;
|
||||||
struct {
|
struct {
|
||||||
@@ -797,6 +788,7 @@ public:
|
|||||||
bool disable_amortized_shader_compile = true;
|
bool disable_amortized_shader_compile = true;
|
||||||
bool disable_handle_use_after_free_check = false;
|
bool disable_handle_use_after_free_check = false;
|
||||||
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
||||||
|
bool enable_asynchronous_operation = false;
|
||||||
} backend;
|
} backend;
|
||||||
struct {
|
struct {
|
||||||
bool check_crc32_after_loading = false;
|
bool check_crc32_after_loading = false;
|
||||||
@@ -817,6 +809,9 @@ public:
|
|||||||
{ "backend.disable_heap_handle_tags",
|
{ "backend.disable_heap_handle_tags",
|
||||||
"Disable Handle<> tags for heap-allocated handles.",
|
"Disable Handle<> tags for heap-allocated handles.",
|
||||||
&features.backend.disable_heap_handle_tags, true },
|
&features.backend.disable_heap_handle_tags, true },
|
||||||
|
{ "backend.enable_asynchronous_operation",
|
||||||
|
"Enable asynchronous operation for resource management.",
|
||||||
|
&features.backend.enable_asynchronous_operation, true },
|
||||||
{ "backend.opengl.assert_native_window_is_valid",
|
{ "backend.opengl.assert_native_window_is_valid",
|
||||||
"Asserts that the ANativeWindow is valid when rendering starts.",
|
"Asserts that the ANativeWindow is valid when rendering starts.",
|
||||||
&features.backend.opengl.assert_native_window_is_valid, true },
|
&features.backend.opengl.assert_native_window_is_valid, true },
|
||||||
|
|||||||
@@ -70,10 +70,19 @@
|
|||||||
|
|
||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
using namespace backend;
|
using namespace backend;
|
||||||
using namespace filaflat;
|
using namespace filaflat;
|
||||||
using namespace utils;
|
using namespace utils;
|
||||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
using UboBatchingMode = Material::UboBatchingMode;
|
||||||
|
|
||||||
|
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
|
||||||
|
return batchingMode != UboBatchingMode::DISABLED && engine.isUboBatchingEnabled() &&
|
||||||
|
domain == MaterialDomain::SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
struct Material::BuilderDetails {
|
struct Material::BuilderDetails {
|
||||||
const void* mPayload = nullptr;
|
const void* mPayload = nullptr;
|
||||||
@@ -81,6 +90,7 @@ struct Material::BuilderDetails {
|
|||||||
bool mDefaultMaterial = false;
|
bool mDefaultMaterial = false;
|
||||||
int32_t mShBandsCount = 3;
|
int32_t mShBandsCount = 3;
|
||||||
Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW;
|
Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW;
|
||||||
|
UboBatchingMode mUboBatchingMode = UboBatchingMode::DEFAULT;
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
CString,
|
CString,
|
||||||
std::variant<int32_t, float, bool>,
|
std::variant<int32_t, float, bool>,
|
||||||
@@ -115,6 +125,11 @@ Material::Builder& Material::Builder::shadowSamplingQuality(ShadowSamplingQualit
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Material::Builder& Material::Builder::uboBatching(UboBatchingMode const mode) noexcept {
|
||||||
|
mImpl->mUboBatchingMode = mode;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, typename>
|
template<typename T, typename>
|
||||||
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
|
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
|
||||||
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
|
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
|
||||||
@@ -148,8 +163,12 @@ Material* Material::Builder::build(Engine& engine) const {
|
|||||||
FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition)
|
FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition)
|
||||||
: mDefinition(definition),
|
: mDefinition(definition),
|
||||||
mIsDefaultMaterial(builder->mDefaultMaterial),
|
mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||||
|
mUseUboBatching(shouldEnableBatching(engine, builder->mUboBatchingMode,
|
||||||
|
definition.materialDomain)),
|
||||||
mEngine(engine),
|
mEngine(engine),
|
||||||
mMaterialId(engine.getMaterialId()) {
|
mMaterialId(engine.getMaterialId()) {
|
||||||
|
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
|
||||||
|
<< "UBO batching is not enabled.";
|
||||||
mSpecializationConstants = processSpecializationConstants(builder);
|
mSpecializationConstants = processSpecializationConstants(builder);
|
||||||
precacheDepthVariants(engine);
|
precacheDepthVariants(engine);
|
||||||
|
|
||||||
@@ -282,14 +301,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
|
|||||||
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
|
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
|
||||||
} else {
|
} else {
|
||||||
// but if we don't, just create an instance with all the default parameters
|
// but if we don't, just create an instance with all the default parameters
|
||||||
return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
|
return mEngine.createMaterialInstance(this, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
|
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
|
||||||
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
|
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
|
||||||
mDefaultMaterialInstance =
|
mDefaultMaterialInstance =
|
||||||
mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
|
mEngine.createMaterialInstance(this, mDefinition.name.c_str());
|
||||||
mDefaultMaterialInstance->setDefaultInstance(true);
|
mDefaultMaterialInstance->setDefaultInstance(true);
|
||||||
}
|
}
|
||||||
return mDefaultMaterialInstance;
|
return mDefaultMaterialInstance;
|
||||||
|
|||||||
@@ -248,6 +248,10 @@ public:
|
|||||||
return mDefinition.perViewLayoutIndex;
|
return mDefinition.perViewLayoutIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool useUboBatching() const noexcept {
|
||||||
|
return mUseUboBatching;
|
||||||
|
}
|
||||||
|
|
||||||
#if FILAMENT_ENABLE_MATDBG
|
#if FILAMENT_ENABLE_MATDBG
|
||||||
void applyPendingEdits() noexcept;
|
void applyPendingEdits() noexcept;
|
||||||
|
|
||||||
@@ -310,6 +314,8 @@ private:
|
|||||||
|
|
||||||
bool mIsDefaultMaterial = false;
|
bool mIsDefaultMaterial = false;
|
||||||
|
|
||||||
|
bool mUseUboBatching = false;
|
||||||
|
|
||||||
// reserve some space to construct the default material instance
|
// reserve some space to construct the default material instance
|
||||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -58,18 +58,8 @@ using namespace utils;
|
|||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
using namespace backend;
|
using namespace backend;
|
||||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
|
||||||
|
|
||||||
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
|
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept
|
||||||
if (batchingMode != UboBatchingMode::DEFAULT) {
|
|
||||||
return batchingMode == UboBatchingMode::UBO_BATCHING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
|
|
||||||
UboBatchingMode batchingMode) noexcept
|
|
||||||
: mMaterial(material),
|
: mMaterial(material),
|
||||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||||
mCulling(CullingMode::BACK),
|
mCulling(CullingMode::BACK),
|
||||||
@@ -80,12 +70,9 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
|||||||
mHasScissor(false),
|
mHasScissor(false),
|
||||||
mIsDoubleSided(false),
|
mIsDoubleSided(false),
|
||||||
mIsDefaultInstance(false),
|
mIsDefaultInstance(false),
|
||||||
mUseUboBatching(
|
mUseUboBatching(material->useUboBatching()),
|
||||||
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
|
|
||||||
mTransparencyMode(TransparencyMode::DEFAULT),
|
mTransparencyMode(TransparencyMode::DEFAULT),
|
||||||
mName(name ? CString(name) : material->getName()) {
|
mName(name ? CString(name) : material->getName()) {
|
||||||
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
|
|
||||||
<< "UBO batching is not enabled.";
|
|
||||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||||
|
|
||||||
// even if the material doesn't have any parameters, we allocate a small UBO because it's
|
// even if the material doesn't have any parameters, we allocate a small UBO because it's
|
||||||
@@ -160,7 +147,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
|||||||
mUseUboBatching(other->mUseUboBatching),
|
mUseUboBatching(other->mUseUboBatching),
|
||||||
mScissorRect(other->mScissorRect),
|
mScissorRect(other->mScissorRect),
|
||||||
mName(name ? CString(name) : other->mName) {
|
mName(name ? CString(name) : other->mName) {
|
||||||
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
|
|
||||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||||
FMaterial const* const material = other->getMaterial();
|
FMaterial const* const material = other->getMaterial();
|
||||||
|
|
||||||
@@ -223,31 +209,6 @@ void FMaterialInstance::terminate(FEngine& engine) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driver) {
|
|
||||||
mHasStreamUniformAssociations = false;
|
|
||||||
if (!mTextureParameters.empty()) {
|
|
||||||
BufferObjectStreamDescriptor descriptor;
|
|
||||||
for (auto const& [binding, p]: mTextureParameters) {
|
|
||||||
ssize_t offset = mMaterial->getUniformInterfaceBlock().getTransformFieldOffset(binding);
|
|
||||||
if (offset >= 0) {
|
|
||||||
if (auto stream = p.texture->getStream()) {
|
|
||||||
mHasStreamUniformAssociations = true;
|
|
||||||
descriptor.mStreams.push_back({ uint32_t(offset), stream->getHandle(),
|
|
||||||
BufferObjectStreamAssociationType::TRANSFORM_MATRIX });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (descriptor.mStreams.size() > 0) {
|
|
||||||
// UBO batching is incompatible with stream uniform associations because streams require a
|
|
||||||
// dedicated UBO handle, not a sub-allocation. Assert that any instance here uses its own UBO.
|
|
||||||
assert_invariant(!mUseUboBatching);
|
|
||||||
auto const* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
|
|
||||||
assert_invariant(ubHandle);
|
|
||||||
driver.registerBufferObjectStreams(*ubHandle, std::move(descriptor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FMaterialInstance::commit(FEngine& engine) const {
|
void FMaterialInstance::commit(FEngine& engine) const {
|
||||||
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
|
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
|
||||||
commit(engine.getDriverApi(), engine.getUboManager());
|
commit(engine.getDriverApi(), engine.getUboManager());
|
||||||
@@ -255,10 +216,9 @@ void FMaterialInstance::commit(FEngine& engine) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
|
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
|
||||||
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
|
if (mUniforms.isDirty()) {
|
||||||
mUniforms.clean();
|
mUniforms.clean();
|
||||||
if (mUseUboBatching) {
|
if (isUsingUboBatching()) {
|
||||||
assert_invariant(uboManager != nullptr);
|
|
||||||
if (!BufferAllocator::isValid(getAllocationId())) {
|
if (!BufferAllocator::isValid(getAllocationId())) {
|
||||||
// The allocation hasn't happened yet, return.
|
// The allocation hasn't happened yet, return.
|
||||||
return;
|
return;
|
||||||
@@ -289,7 +249,7 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
|
|||||||
// TODO: eventually we should remove this in RELEASE builds
|
// TODO: eventually we should remove this in RELEASE builds
|
||||||
fixMissingSamplers();
|
fixMissingSamplers();
|
||||||
|
|
||||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,7 +399,7 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +436,7 @@ void FMaterialInstance::assignUboAllocation(
|
|||||||
const Handle<HwBufferObject>& ubHandle,
|
const Handle<HwBufferObject>& ubHandle,
|
||||||
BufferAllocator::AllocationId id,
|
BufferAllocator::AllocationId id,
|
||||||
BufferAllocator::allocation_size_t offset) {
|
BufferAllocator::allocation_size_t offset) {
|
||||||
assert_invariant(mUseUboBatching);
|
assert_invariant(isUsingUboBatching());
|
||||||
|
|
||||||
mUboData = id;
|
mUboData = id;
|
||||||
mUboOffset = offset;
|
mUboOffset = offset;
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ class FTexture;
|
|||||||
|
|
||||||
class FMaterialInstance : public MaterialInstance {
|
class FMaterialInstance : public MaterialInstance {
|
||||||
public:
|
public:
|
||||||
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
|
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept;
|
||||||
FEngine::UboBatchingMode batchingMode) noexcept;
|
|
||||||
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
|
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
|
||||||
FMaterialInstance(const FMaterialInstance& rhs) = delete;
|
FMaterialInstance(const FMaterialInstance& rhs) = delete;
|
||||||
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
|
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
|
||||||
@@ -68,8 +67,6 @@ public:
|
|||||||
|
|
||||||
void terminate(FEngine& engine);
|
void terminate(FEngine& engine);
|
||||||
|
|
||||||
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
|
|
||||||
|
|
||||||
void commit(FEngine& engine) const;
|
void commit(FEngine& engine) const;
|
||||||
|
|
||||||
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
|
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
|
||||||
@@ -288,7 +285,6 @@ private:
|
|||||||
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
|
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
|
||||||
mutable DescriptorSet mDescriptorSet;
|
mutable DescriptorSet mDescriptorSet;
|
||||||
UniformBuffer mUniforms;
|
UniformBuffer mUniforms;
|
||||||
bool mHasStreamUniformAssociations = false;
|
|
||||||
|
|
||||||
backend::PolygonOffset mPolygonOffset{};
|
backend::PolygonOffset mPolygonOffset{};
|
||||||
backend::StencilState mStencilState{};
|
backend::StencilState mStencilState{};
|
||||||
|
|||||||
@@ -292,16 +292,11 @@ void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
|
|||||||
// do this before engine.flush()
|
// do this before engine.flush()
|
||||||
mResourceAllocator->gc(true);
|
mResourceAllocator->gc(true);
|
||||||
|
|
||||||
// Run the component managers' GC in parallel
|
|
||||||
// WARNING: while doing this we can't access any component manager
|
|
||||||
auto& js = engine.getJobSystem();
|
|
||||||
|
|
||||||
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
|
||||||
|
|
||||||
engine.flush(); // flush command stream
|
engine.flush(); // flush command stream
|
||||||
|
|
||||||
// make sure we're done with the gcs
|
// Run the component managers' GC
|
||||||
js.waitAndRelease(job);
|
auto& js = engine.getJobSystem();
|
||||||
|
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FRenderer::shouldRenderFrame() const noexcept {
|
bool FRenderer::shouldRenderFrame() const noexcept {
|
||||||
@@ -468,16 +463,11 @@ void FRenderer::endFrame() {
|
|||||||
// do this before engine.flush()
|
// do this before engine.flush()
|
||||||
mResourceAllocator->gc();
|
mResourceAllocator->gc();
|
||||||
|
|
||||||
// Run the component managers' GC in parallel
|
|
||||||
// WARNING: while doing this we can't access any component manager
|
|
||||||
auto& js = engine.getJobSystem();
|
|
||||||
|
|
||||||
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
|
||||||
|
|
||||||
engine.flush(); // flush command stream
|
engine.flush(); // flush command stream
|
||||||
|
|
||||||
// make sure we're done with the gcs
|
// Run the component managers' GC
|
||||||
js.waitAndRelease(job);
|
auto& js = engine.getJobSystem();
|
||||||
|
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
||||||
}
|
}
|
||||||
|
|
||||||
void FRenderer::readPixels(
|
void FRenderer::readPixels(
|
||||||
@@ -571,7 +561,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
|||||||
|
|
||||||
if (UTILS_LIKELY(view->getScene())) {
|
if (UTILS_LIKELY(view->getScene())) {
|
||||||
mPreviousRenderTargets.clear();
|
mPreviousRenderTargets.clear();
|
||||||
mFrameId++;
|
|
||||||
|
|
||||||
// ask the engine to do what it needs to (e.g. updates light buffer, materials...)
|
// ask the engine to do what it needs to (e.g. updates light buffer, materials...)
|
||||||
FEngine& engine = mEngine;
|
FEngine& engine = mEngine;
|
||||||
@@ -581,8 +570,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
|||||||
driver.beginFrame(
|
driver.beginFrame(
|
||||||
steady_clock::now().time_since_epoch().count(),
|
steady_clock::now().time_since_epoch().count(),
|
||||||
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
|
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
|
||||||
1'000'000'000.0 / mDisplayInfo.refreshRate),
|
1'000'000'000.0 / mDisplayInfo.refreshRate), 0);
|
||||||
mFrameId);
|
|
||||||
|
|
||||||
// because we don't have a "present" call, we use flush so the driver can submit
|
// because we don't have a "present" call, we use flush so the driver can submit
|
||||||
// the command buffer; we do this before driver.endFrame() to mimic what would
|
// the command buffer; we do this before driver.endFrame() to mimic what would
|
||||||
@@ -591,7 +579,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
|||||||
|
|
||||||
engine.submitFrame();
|
engine.submitFrame();
|
||||||
|
|
||||||
driver.endFrame(mFrameId);
|
driver.endFrame(0);
|
||||||
|
|
||||||
// engine.flush() has already been called by renderInternal(), but we need an extra one
|
// engine.flush() has already been called by renderInternal(), but we need an extra one
|
||||||
// for endFrame() above. This operation in actually not too heavy, it just kicks the
|
// for endFrame() above. This operation in actually not too heavy, it just kicks the
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ private:
|
|||||||
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
|
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
|
||||||
FSwapChain* mSwapChain = nullptr;
|
FSwapChain* mSwapChain = nullptr;
|
||||||
size_t mCommandsHighWatermark = 0;
|
size_t mCommandsHighWatermark = 0;
|
||||||
uint32_t mFrameId = 0;
|
uint32_t mFrameId = 1; // id 0 is reserved for standalone views
|
||||||
FrameInfoManager mFrameInfoManager;
|
FrameInfoManager mFrameInfoManager;
|
||||||
backend::TextureFormat mHdrTranslucent;
|
backend::TextureFormat mHdrTranslucent;
|
||||||
backend::TextureFormat mHdrQualityMedium;
|
backend::TextureFormat mHdrQualityMedium;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ using allocation_size_t = BufferAllocator::allocation_size_t;
|
|||||||
// FenceManager
|
// FenceManager
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
void UboManager::FenceManager::track(DriverApi& driver, std::unordered_set<AllocationId>&& allocationIds) {
|
void UboManager::FenceManager::track(DriverApi& driver, AllocationIdContainer&& allocationIds) {
|
||||||
if (allocationIds.empty()) {
|
if (allocationIds.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -159,15 +159,16 @@ void UboManager::finishBeginFrame(DriverApi& driver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::endFrame(DriverApi& driver) {
|
void UboManager::endFrame(DriverApi& driver) {
|
||||||
std::unordered_set<AllocationId> allocationIds;
|
auto allocationIds =
|
||||||
for (const auto* mi : mManagedInstances) {
|
FenceManager::AllocationIdContainer::with_capacity(mManagedInstances.size());
|
||||||
|
for (const auto* mi: mManagedInstances) {
|
||||||
const AllocationId id = mi->getAllocationId();
|
const AllocationId id = mi->getAllocationId();
|
||||||
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
|
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAllocator.acquireGpu(id);
|
mAllocator.acquireGpu(id);
|
||||||
allocationIds.insert(id);
|
allocationIds.push_back(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
mFenceManager.track(driver, std::move(allocationIds));
|
mFenceManager.track(driver, std::move(allocationIds));
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
class FenceManager {
|
class FenceManager {
|
||||||
public:
|
public:
|
||||||
using AllocationId = BufferAllocator::AllocationId;
|
using AllocationId = BufferAllocator::AllocationId;
|
||||||
|
using AllocationIdContainer = utils::FixedCapacityVector<AllocationId>;
|
||||||
|
|
||||||
FenceManager() = default;
|
FenceManager() = default;
|
||||||
~FenceManager() = default;
|
~FenceManager() = default;
|
||||||
@@ -65,7 +66,7 @@ public:
|
|||||||
|
|
||||||
// Creates a new fence to track a set of allocation IDs for the current frame.
|
// Creates a new fence to track a set of allocation IDs for the current frame.
|
||||||
// This marks the beginning of GPU's usage of these resources.
|
// This marks the beginning of GPU's usage of these resources.
|
||||||
void track(backend::DriverApi& driver, std::unordered_set<AllocationId>&& allocationIds);
|
void track(backend::DriverApi& driver, AllocationIdContainer&& allocationIds);
|
||||||
|
|
||||||
|
|
||||||
// Checks all tracked fences and invokes a callback for resources associated with
|
// Checks all tracked fences and invokes a callback for resources associated with
|
||||||
@@ -80,7 +81,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
// Not ideal, but we need to know which slots to decrement gpuUseCount for each frame.
|
// Not ideal, but we need to know which slots to decrement gpuUseCount for each frame.
|
||||||
using FenceAndAllocations =
|
using FenceAndAllocations =
|
||||||
std::pair<backend::Handle<backend::HwFence>, std::unordered_set<AllocationId>>;
|
std::pair<backend::Handle<backend::HwFence>, AllocationIdContainer>;
|
||||||
std::vector<FenceAndAllocations> mFenceAllocationList;
|
std::vector<FenceAndAllocations> mFenceAllocationList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,25 @@ set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
|||||||
set(RESOURCE_DIR "${GENERATION_ROOT}/resources")
|
set(RESOURCE_DIR "${GENERATION_ROOT}/resources")
|
||||||
file(MAKE_DIRECTORY ${RESOURCE_DIR})
|
file(MAKE_DIRECTORY ${RESOURCE_DIR})
|
||||||
|
|
||||||
|
set(MATERIAL_DIR "${GENERATION_ROOT}/material")
|
||||||
|
file(MAKE_DIRECTORY ${MATERIAL_DIR})
|
||||||
|
|
||||||
|
set(SANDBOX_TRANSFORM_NAME_MAT ${CMAKE_CURRENT_SOURCE_DIR}/sandboxTransformName.mat)
|
||||||
|
set(TEST_MATERIAL_TRANSFORM_NAME_FILAMAT ${MATERIAL_DIR}/test_material_transformname.filamat)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT}
|
||||||
|
COMMAND matc ${MATC_BASE_FLAGS} -o ${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT} ${SANDBOX_TRANSFORM_NAME_MAT}
|
||||||
|
MAIN_DEPENDENCY ${SANDBOX_TRANSFORM_NAME_MAT}
|
||||||
|
DEPENDS matc
|
||||||
|
COMMENT "Compiling material ${SANDBOX_TRANSFORM_NAME_MAT}"
|
||||||
|
)
|
||||||
|
|
||||||
get_resgen_vars(${RESOURCE_DIR} filament_test_resources)
|
get_resgen_vars(${RESOURCE_DIR} filament_test_resources)
|
||||||
|
|
||||||
set(RESOURCE_BINS
|
set(RESOURCE_BINS
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_material.filamat
|
${CMAKE_CURRENT_SOURCE_DIR}/test_material.filamat
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_material_transformname.filamat
|
${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -11,7 +11,6 @@ OUTPUT_DIR="filament/test/"
|
|||||||
./build.sh -p desktop release matc
|
./build.sh -p desktop release matc
|
||||||
MATC=out/cmake-release/tools/matc/matc
|
MATC=out/cmake-release/tools/matc/matc
|
||||||
${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material.filamat samples/materials/sandboxLit.mat
|
${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material.filamat samples/materials/sandboxLit.mat
|
||||||
${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material_transformname.filamat samples/materials/sandboxTransformName.mat
|
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = "Filament"
|
spec.name = "Filament"
|
||||||
spec.version = "1.67.1"
|
spec.version = "1.68.0"
|
||||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||||
spec.homepage = "https://google.github.io/filament"
|
spec.homepage = "https://google.github.io/filament"
|
||||||
spec.authors = "Google LLC."
|
spec.authors = "Google LLC."
|
||||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||||
spec.platform = :ios, "11.0"
|
spec.platform = :ios, "11.0"
|
||||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.1/filament-v1.67.1-ios.tgz" }
|
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.68.0/filament-v1.68.0-ios.tgz" }
|
||||||
|
|
||||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||||
spec.pod_target_xcconfig = {
|
spec.pod_target_xcconfig = {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
// update this when a new version of filament wouldn't work with older materials
|
// update this when a new version of filament wouldn't work with older materials
|
||||||
static constexpr size_t MATERIAL_VERSION = 67;
|
static constexpr size_t MATERIAL_VERSION = 68;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported shading models
|
* Supported shading models
|
||||||
|
|||||||
@@ -170,10 +170,6 @@ public:
|
|||||||
// negative value if name doesn't exist or Panic if exceptions are enabled
|
// negative value if name doesn't exist or Panic if exceptions are enabled
|
||||||
ssize_t getFieldOffset(std::string_view name, size_t index) const;
|
ssize_t getFieldOffset(std::string_view name, size_t index) const;
|
||||||
|
|
||||||
// returns offset in bytes of the transform matrix for the given external texture binding
|
|
||||||
// returns -1 if the field doesn't exist
|
|
||||||
ssize_t getTransformFieldOffset(uint8_t binding) const;
|
|
||||||
|
|
||||||
FieldInfo const* getFieldInfo(std::string_view name) const;
|
FieldInfo const* getFieldInfo(std::string_view name) const;
|
||||||
|
|
||||||
bool hasField(std::string_view name) const noexcept {
|
bool hasField(std::string_view name) const noexcept {
|
||||||
@@ -201,7 +197,6 @@ private:
|
|||||||
utils::CString mName;
|
utils::CString mName;
|
||||||
utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
|
utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
|
||||||
std::unordered_map<std::string_view , uint32_t> mInfoMap;
|
std::unordered_map<std::string_view , uint32_t> mInfoMap;
|
||||||
std::unordered_map<uint8_t, uint32_t> mTransformOffsetMap;
|
|
||||||
uint32_t mSize = 0; // size in bytes rounded to multiple of 4
|
uint32_t mSize = 0; // size in bytes rounded to multiple of 4
|
||||||
Alignment mAlignment = Alignment::std140;
|
Alignment mAlignment = Alignment::std140;
|
||||||
Target mTarget = Target::UNIFORM;
|
Target mTarget = Target::UNIFORM;
|
||||||
|
|||||||
@@ -122,8 +122,6 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
|
|||||||
|
|
||||||
auto& uniformsInfoList = mFieldInfoList;
|
auto& uniformsInfoList = mFieldInfoList;
|
||||||
|
|
||||||
auto& transformOffsetMap = mTransformOffsetMap;
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
uint16_t offset = 0;
|
uint16_t offset = 0;
|
||||||
for (auto const& e : builder.mEntries) {
|
for (auto const& e : builder.mEntries) {
|
||||||
@@ -150,11 +148,6 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
|
|||||||
// record this uniform info
|
// record this uniform info
|
||||||
infoMap[{ info.name.data(), info.name.size() }] = i;
|
infoMap[{ info.name.data(), info.name.size() }] = i;
|
||||||
|
|
||||||
// record the transform offset if this is an external sampler
|
|
||||||
if (e.associatedSampler != 0) {
|
|
||||||
transformOffsetMap[e.associatedSampler] = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// advance offset to next slot
|
// advance offset to next slot
|
||||||
offset += stride * std::max(1u, e.size);
|
offset += stride * std::max(1u, e.size);
|
||||||
++i;
|
++i;
|
||||||
@@ -170,14 +163,6 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index
|
|||||||
return (ssize_t)info->getBufferOffset(index);
|
return (ssize_t)info->getBufferOffset(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t BufferInterfaceBlock::getTransformFieldOffset(uint8_t binding) const {
|
|
||||||
auto pos = mTransformOffsetMap.find(binding);
|
|
||||||
if (pos == mTransformOffsetMap.end()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return (ssize_t)pos->second * sizeof(uint32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
|
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
|
||||||
std::string_view name) const {
|
std::string_view name) const {
|
||||||
auto pos = mInfoMap.find(name);
|
auto pos = mInfoMap.find(name);
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
#ifndef FILAGUI_IMGUIHELPER_H_
|
#ifndef FILAGUI_IMGUIHELPER_H_
|
||||||
#define FILAGUI_IMGUIHELPER_H_
|
#define FILAGUI_IMGUIHELPER_H_
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <filament/Engine.h>
|
#include <filament/Engine.h>
|
||||||
#include <filament/IndexBuffer.h>
|
#include <filament/IndexBuffer.h>
|
||||||
#include <filament/Material.h>
|
#include <filament/Material.h>
|
||||||
@@ -32,6 +29,10 @@
|
|||||||
#include <utils/Entity.h>
|
#include <utils/Entity.h>
|
||||||
#include <utils/Path.h>
|
#include <utils/Path.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
struct ImDrawData;
|
struct ImDrawData;
|
||||||
struct ImGuiIO;
|
struct ImGuiIO;
|
||||||
struct ImGuiContext;
|
struct ImGuiContext;
|
||||||
@@ -100,6 +101,7 @@ public:
|
|||||||
filament::TextureSampler mSampler;
|
filament::TextureSampler mSampler;
|
||||||
bool mFlipVertical = false;
|
bool mFlipVertical = false;
|
||||||
utils::Path mSettingsPath;
|
utils::Path mSettingsPath;
|
||||||
|
std::unordered_set<filament::Texture*> mImGuiTextures;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filagui
|
} // namespace filagui
|
||||||
|
|||||||
@@ -148,6 +148,12 @@ ImGuiHelper::~ImGuiHelper() {
|
|||||||
mEngine->destroy(ib);
|
mEngine->destroy(ib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are textures that have been created by imgui but were not explicitly freed before
|
||||||
|
// shutdown.
|
||||||
|
for (auto itextures: mImGuiTextures) {
|
||||||
|
mEngine->destroy(itextures);
|
||||||
|
}
|
||||||
|
|
||||||
EntityManager& em = utils::EntityManager::get();
|
EntityManager& em = utils::EntityManager::get();
|
||||||
em.destroy(mRenderable);
|
em.destroy(mRenderable);
|
||||||
em.destroy(mCameraEntity);
|
em.destroy(mCameraEntity);
|
||||||
@@ -189,6 +195,48 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
|||||||
|
|
||||||
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
||||||
ImGui::SetCurrentContext(mImGuiContext);
|
ImGui::SetCurrentContext(mImGuiContext);
|
||||||
|
if (commands->Textures != nullptr) {
|
||||||
|
for (ImTextureData* tex : *commands->Textures) {
|
||||||
|
if (tex->Status == ImTextureStatus_OK) {
|
||||||
|
continue;
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantCreate) {
|
||||||
|
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
|
||||||
|
Texture* ftex = Texture::Builder()
|
||||||
|
.width(tex->Width)
|
||||||
|
.height(tex->Height)
|
||||||
|
.levels(1)
|
||||||
|
.format(Texture::InternalFormat::RGBA8)
|
||||||
|
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||||
|
.build(*mEngine);
|
||||||
|
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||||
|
const int size = tex->Width * tex->Height * 4;
|
||||||
|
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
|
||||||
|
Texture::Format::RGBA,
|
||||||
|
Texture::Type::UBYTE);
|
||||||
|
ftex->setImage(*mEngine, 0, std::move(pb));
|
||||||
|
|
||||||
|
tex->SetTexID((ImTextureID)ftex);
|
||||||
|
tex->SetStatus(ImTextureStatus_OK);
|
||||||
|
mImGuiTextures.insert(ftex);
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantUpdates) {
|
||||||
|
const int size = tex->Width * tex->Height * 4;
|
||||||
|
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
|
||||||
|
Texture::Format::RGBA,
|
||||||
|
Texture::Type::UBYTE);
|
||||||
|
filament::Texture* ftex = (filament::Texture*)tex->TexID;
|
||||||
|
ftex->setImage(*mEngine, 0, std::move(pb));
|
||||||
|
tex->SetStatus(ImTextureStatus_OK);
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantDestroy &&
|
||||||
|
tex->UnusedFrames > 0) {
|
||||||
|
|
||||||
|
filament::Texture* ftex = (filament::Texture*)tex->TexID;
|
||||||
|
mEngine->destroy(ftex);
|
||||||
|
tex->SetTexID(ImTextureID_Invalid);
|
||||||
|
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||||
|
mImGuiTextures.erase(ftex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mHasSynced = false;
|
mHasSynced = false;
|
||||||
auto& rcm = mEngine->getRenderableManager();
|
auto& rcm = mEngine->getRenderableManager();
|
||||||
@@ -228,7 +276,7 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
|||||||
if (pcmd.UserCallback) {
|
if (pcmd.UserCallback) {
|
||||||
pcmd.UserCallback(cmds, &pcmd);
|
pcmd.UserCallback(cmds, &pcmd);
|
||||||
} else {
|
} else {
|
||||||
auto texture = (Texture const*)pcmd.TextureId;
|
auto texture = (Texture const*)pcmd.GetTexID();
|
||||||
MaterialInstance* materialInstance;
|
MaterialInstance* materialInstance;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
||||||
|
|||||||
@@ -37,9 +37,13 @@ set(SRCS
|
|||||||
src/IcoSphere.cpp
|
src/IcoSphere.cpp
|
||||||
src/MeshAssimp.cpp
|
src/MeshAssimp.cpp
|
||||||
src/Sphere.cpp
|
src/Sphere.cpp
|
||||||
src/VulkanPlatformHelper.cpp
|
src/KeyInputConversion.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (FILAMENT_SUPPORTS_VULKAN)
|
||||||
|
list(APPEND SRCS src/VulkanPlatformHelper.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(LIBS
|
set(LIBS
|
||||||
assimp
|
assimp
|
||||||
camutils
|
camutils
|
||||||
@@ -79,7 +83,11 @@ endif()
|
|||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(APPEND SRCS src/NativeWindowHelperWindows.cpp)
|
list(APPEND SRCS src/NativeWindowHelperWindows.cpp)
|
||||||
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
|
||||||
|
if (FILAMENT_SUPPORTS_VULKAN)
|
||||||
|
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
list(APPEND LIBS sdl2main)
|
list(APPEND LIBS sdl2main)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include <filamentapp/FilamentApp.h>
|
#include <filamentapp/FilamentApp.h>
|
||||||
|
|
||||||
|
#include "KeyInputConversion.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
# include <SDL_syswm.h>
|
# include <SDL_syswm.h>
|
||||||
# include <utils/unwindows.h>
|
# include <utils/unwindows.h>
|
||||||
@@ -211,27 +213,6 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
|||||||
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
|
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
|
||||||
ImGui::GetMainViewport()->PlatformHandleRaw = wmInfo.info.win.window;
|
ImGui::GetMainViewport()->PlatformHandleRaw = wmInfo.info.win.window;
|
||||||
#endif
|
#endif
|
||||||
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
|
|
||||||
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
|
|
||||||
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
|
|
||||||
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
|
|
||||||
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
|
|
||||||
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
|
|
||||||
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
|
|
||||||
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
|
|
||||||
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
|
|
||||||
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
|
|
||||||
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
|
|
||||||
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
|
|
||||||
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
|
|
||||||
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
|
|
||||||
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
|
|
||||||
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
|
|
||||||
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
|
|
||||||
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
|
|
||||||
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
|
|
||||||
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
|
|
||||||
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
|
|
||||||
io.SetClipboardTextFn = [](void*, const char* text) {
|
io.SetClipboardTextFn = [](void*, const char* text) {
|
||||||
SDL_SetClipboardText(text);
|
SDL_SetClipboardText(text);
|
||||||
};
|
};
|
||||||
@@ -305,15 +286,19 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
|||||||
io.AddInputCharactersUTF8(event->text.text);
|
io.AddInputCharactersUTF8(event->text.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYUP:
|
||||||
case SDL_KEYUP: {
|
case SDL_KEYDOWN: {
|
||||||
int key = event->key.keysym.scancode;
|
SDL_Scancode const scancode = event->key.keysym.scancode;
|
||||||
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
SDL_Keycode const keycode = event->key.keysym.sym;
|
||||||
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
|
|
||||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
auto modState = SDL_GetModState();
|
||||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
io.AddKeyEvent(ImGuiMod_Ctrl, (modState & KMOD_CTRL) != 0);
|
||||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
io.AddKeyEvent(ImGuiMod_Shift, (modState & KMOD_SHIFT) != 0);
|
||||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
io.AddKeyEvent(ImGuiMod_Alt, (modState & KMOD_ALT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, (modState & KMOD_GUI) != 0);
|
||||||
|
io.AddKeyEvent(
|
||||||
|
filamentapp_utils::ImGui_ImplSDL2_KeyEventToImGuiKey(keycode, scancode),
|
||||||
|
event->type == SDL_KEYDOWN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
/*
|
||||||
|
* 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_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
|
#define TNT_FILAMENT_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
namespace filamentapp_utils {
|
||||||
|
|
||||||
|
// Copied from
|
||||||
|
// https://github.com/ocornut/imgui/blob/940627d008b8f0584b7f50d24574537cf24a32e1/backends/imgui_impl_sdl2.cpp#L206
|
||||||
|
ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) {
|
||||||
|
switch (keycode) {
|
||||||
|
case SDLK_TAB:
|
||||||
|
return ImGuiKey_Tab;
|
||||||
|
case SDLK_LEFT:
|
||||||
|
return ImGuiKey_LeftArrow;
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
return ImGuiKey_RightArrow;
|
||||||
|
case SDLK_UP:
|
||||||
|
return ImGuiKey_UpArrow;
|
||||||
|
case SDLK_DOWN:
|
||||||
|
return ImGuiKey_DownArrow;
|
||||||
|
case SDLK_PAGEUP:
|
||||||
|
return ImGuiKey_PageUp;
|
||||||
|
case SDLK_PAGEDOWN:
|
||||||
|
return ImGuiKey_PageDown;
|
||||||
|
case SDLK_HOME:
|
||||||
|
return ImGuiKey_Home;
|
||||||
|
case SDLK_END:
|
||||||
|
return ImGuiKey_End;
|
||||||
|
case SDLK_INSERT:
|
||||||
|
return ImGuiKey_Insert;
|
||||||
|
case SDLK_DELETE:
|
||||||
|
return ImGuiKey_Delete;
|
||||||
|
case SDLK_BACKSPACE:
|
||||||
|
return ImGuiKey_Backspace;
|
||||||
|
case SDLK_SPACE:
|
||||||
|
return ImGuiKey_Space;
|
||||||
|
case SDLK_RETURN:
|
||||||
|
return ImGuiKey_Enter;
|
||||||
|
case SDLK_ESCAPE:
|
||||||
|
return ImGuiKey_Escape;
|
||||||
|
// case SDLK_QUOTE: return ImGuiKey_Apostrophe;
|
||||||
|
case SDLK_COMMA:
|
||||||
|
return ImGuiKey_Comma;
|
||||||
|
// case SDLK_MINUS: return ImGuiKey_Minus;
|
||||||
|
case SDLK_PERIOD:
|
||||||
|
return ImGuiKey_Period;
|
||||||
|
// case SDLK_SLASH: return ImGuiKey_Slash;
|
||||||
|
case SDLK_SEMICOLON:
|
||||||
|
return ImGuiKey_Semicolon;
|
||||||
|
// case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||||
|
// case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||||
|
// case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||||
|
// case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||||
|
// case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
|
||||||
|
case SDLK_CAPSLOCK:
|
||||||
|
return ImGuiKey_CapsLock;
|
||||||
|
case SDLK_SCROLLLOCK:
|
||||||
|
return ImGuiKey_ScrollLock;
|
||||||
|
case SDLK_NUMLOCKCLEAR:
|
||||||
|
return ImGuiKey_NumLock;
|
||||||
|
case SDLK_PRINTSCREEN:
|
||||||
|
return ImGuiKey_PrintScreen;
|
||||||
|
case SDLK_PAUSE:
|
||||||
|
return ImGuiKey_Pause;
|
||||||
|
case SDLK_KP_0:
|
||||||
|
return ImGuiKey_Keypad0;
|
||||||
|
case SDLK_KP_1:
|
||||||
|
return ImGuiKey_Keypad1;
|
||||||
|
case SDLK_KP_2:
|
||||||
|
return ImGuiKey_Keypad2;
|
||||||
|
case SDLK_KP_3:
|
||||||
|
return ImGuiKey_Keypad3;
|
||||||
|
case SDLK_KP_4:
|
||||||
|
return ImGuiKey_Keypad4;
|
||||||
|
case SDLK_KP_5:
|
||||||
|
return ImGuiKey_Keypad5;
|
||||||
|
case SDLK_KP_6:
|
||||||
|
return ImGuiKey_Keypad6;
|
||||||
|
case SDLK_KP_7:
|
||||||
|
return ImGuiKey_Keypad7;
|
||||||
|
case SDLK_KP_8:
|
||||||
|
return ImGuiKey_Keypad8;
|
||||||
|
case SDLK_KP_9:
|
||||||
|
return ImGuiKey_Keypad9;
|
||||||
|
case SDLK_KP_PERIOD:
|
||||||
|
return ImGuiKey_KeypadDecimal;
|
||||||
|
case SDLK_KP_DIVIDE:
|
||||||
|
return ImGuiKey_KeypadDivide;
|
||||||
|
case SDLK_KP_MULTIPLY:
|
||||||
|
return ImGuiKey_KeypadMultiply;
|
||||||
|
case SDLK_KP_MINUS:
|
||||||
|
return ImGuiKey_KeypadSubtract;
|
||||||
|
case SDLK_KP_PLUS:
|
||||||
|
return ImGuiKey_KeypadAdd;
|
||||||
|
case SDLK_KP_ENTER:
|
||||||
|
return ImGuiKey_KeypadEnter;
|
||||||
|
case SDLK_KP_EQUALS:
|
||||||
|
return ImGuiKey_KeypadEqual;
|
||||||
|
case SDLK_LCTRL:
|
||||||
|
return ImGuiKey_LeftCtrl;
|
||||||
|
case SDLK_LSHIFT:
|
||||||
|
return ImGuiKey_LeftShift;
|
||||||
|
case SDLK_LALT:
|
||||||
|
return ImGuiKey_LeftAlt;
|
||||||
|
case SDLK_LGUI:
|
||||||
|
return ImGuiKey_LeftSuper;
|
||||||
|
case SDLK_RCTRL:
|
||||||
|
return ImGuiKey_RightCtrl;
|
||||||
|
case SDLK_RSHIFT:
|
||||||
|
return ImGuiKey_RightShift;
|
||||||
|
case SDLK_RALT:
|
||||||
|
return ImGuiKey_RightAlt;
|
||||||
|
case SDLK_RGUI:
|
||||||
|
return ImGuiKey_RightSuper;
|
||||||
|
case SDLK_APPLICATION:
|
||||||
|
return ImGuiKey_Menu;
|
||||||
|
case SDLK_0:
|
||||||
|
return ImGuiKey_0;
|
||||||
|
case SDLK_1:
|
||||||
|
return ImGuiKey_1;
|
||||||
|
case SDLK_2:
|
||||||
|
return ImGuiKey_2;
|
||||||
|
case SDLK_3:
|
||||||
|
return ImGuiKey_3;
|
||||||
|
case SDLK_4:
|
||||||
|
return ImGuiKey_4;
|
||||||
|
case SDLK_5:
|
||||||
|
return ImGuiKey_5;
|
||||||
|
case SDLK_6:
|
||||||
|
return ImGuiKey_6;
|
||||||
|
case SDLK_7:
|
||||||
|
return ImGuiKey_7;
|
||||||
|
case SDLK_8:
|
||||||
|
return ImGuiKey_8;
|
||||||
|
case SDLK_9:
|
||||||
|
return ImGuiKey_9;
|
||||||
|
case SDLK_a:
|
||||||
|
return ImGuiKey_A;
|
||||||
|
case SDLK_b:
|
||||||
|
return ImGuiKey_B;
|
||||||
|
case SDLK_c:
|
||||||
|
return ImGuiKey_C;
|
||||||
|
case SDLK_d:
|
||||||
|
return ImGuiKey_D;
|
||||||
|
case SDLK_e:
|
||||||
|
return ImGuiKey_E;
|
||||||
|
case SDLK_f:
|
||||||
|
return ImGuiKey_F;
|
||||||
|
case SDLK_g:
|
||||||
|
return ImGuiKey_G;
|
||||||
|
case SDLK_h:
|
||||||
|
return ImGuiKey_H;
|
||||||
|
case SDLK_i:
|
||||||
|
return ImGuiKey_I;
|
||||||
|
case SDLK_j:
|
||||||
|
return ImGuiKey_J;
|
||||||
|
case SDLK_k:
|
||||||
|
return ImGuiKey_K;
|
||||||
|
case SDLK_l:
|
||||||
|
return ImGuiKey_L;
|
||||||
|
case SDLK_m:
|
||||||
|
return ImGuiKey_M;
|
||||||
|
case SDLK_n:
|
||||||
|
return ImGuiKey_N;
|
||||||
|
case SDLK_o:
|
||||||
|
return ImGuiKey_O;
|
||||||
|
case SDLK_p:
|
||||||
|
return ImGuiKey_P;
|
||||||
|
case SDLK_q:
|
||||||
|
return ImGuiKey_Q;
|
||||||
|
case SDLK_r:
|
||||||
|
return ImGuiKey_R;
|
||||||
|
case SDLK_s:
|
||||||
|
return ImGuiKey_S;
|
||||||
|
case SDLK_t:
|
||||||
|
return ImGuiKey_T;
|
||||||
|
case SDLK_u:
|
||||||
|
return ImGuiKey_U;
|
||||||
|
case SDLK_v:
|
||||||
|
return ImGuiKey_V;
|
||||||
|
case SDLK_w:
|
||||||
|
return ImGuiKey_W;
|
||||||
|
case SDLK_x:
|
||||||
|
return ImGuiKey_X;
|
||||||
|
case SDLK_y:
|
||||||
|
return ImGuiKey_Y;
|
||||||
|
case SDLK_z:
|
||||||
|
return ImGuiKey_Z;
|
||||||
|
case SDLK_F1:
|
||||||
|
return ImGuiKey_F1;
|
||||||
|
case SDLK_F2:
|
||||||
|
return ImGuiKey_F2;
|
||||||
|
case SDLK_F3:
|
||||||
|
return ImGuiKey_F3;
|
||||||
|
case SDLK_F4:
|
||||||
|
return ImGuiKey_F4;
|
||||||
|
case SDLK_F5:
|
||||||
|
return ImGuiKey_F5;
|
||||||
|
case SDLK_F6:
|
||||||
|
return ImGuiKey_F6;
|
||||||
|
case SDLK_F7:
|
||||||
|
return ImGuiKey_F7;
|
||||||
|
case SDLK_F8:
|
||||||
|
return ImGuiKey_F8;
|
||||||
|
case SDLK_F9:
|
||||||
|
return ImGuiKey_F9;
|
||||||
|
case SDLK_F10:
|
||||||
|
return ImGuiKey_F10;
|
||||||
|
case SDLK_F11:
|
||||||
|
return ImGuiKey_F11;
|
||||||
|
case SDLK_F12:
|
||||||
|
return ImGuiKey_F12;
|
||||||
|
case SDLK_F13:
|
||||||
|
return ImGuiKey_F13;
|
||||||
|
case SDLK_F14:
|
||||||
|
return ImGuiKey_F14;
|
||||||
|
case SDLK_F15:
|
||||||
|
return ImGuiKey_F15;
|
||||||
|
case SDLK_F16:
|
||||||
|
return ImGuiKey_F16;
|
||||||
|
case SDLK_F17:
|
||||||
|
return ImGuiKey_F17;
|
||||||
|
case SDLK_F18:
|
||||||
|
return ImGuiKey_F18;
|
||||||
|
case SDLK_F19:
|
||||||
|
return ImGuiKey_F19;
|
||||||
|
case SDLK_F20:
|
||||||
|
return ImGuiKey_F20;
|
||||||
|
case SDLK_F21:
|
||||||
|
return ImGuiKey_F21;
|
||||||
|
case SDLK_F22:
|
||||||
|
return ImGuiKey_F22;
|
||||||
|
case SDLK_F23:
|
||||||
|
return ImGuiKey_F23;
|
||||||
|
case SDLK_F24:
|
||||||
|
return ImGuiKey_F24;
|
||||||
|
case SDLK_AC_BACK:
|
||||||
|
return ImGuiKey_AppBack;
|
||||||
|
case SDLK_AC_FORWARD:
|
||||||
|
return ImGuiKey_AppForward;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to scancode
|
||||||
|
switch (scancode) {
|
||||||
|
case SDL_SCANCODE_GRAVE:
|
||||||
|
return ImGuiKey_GraveAccent;
|
||||||
|
case SDL_SCANCODE_MINUS:
|
||||||
|
return ImGuiKey_Minus;
|
||||||
|
case SDL_SCANCODE_EQUALS:
|
||||||
|
return ImGuiKey_Equal;
|
||||||
|
case SDL_SCANCODE_LEFTBRACKET:
|
||||||
|
return ImGuiKey_LeftBracket;
|
||||||
|
case SDL_SCANCODE_RIGHTBRACKET:
|
||||||
|
return ImGuiKey_RightBracket;
|
||||||
|
case SDL_SCANCODE_NONUSBACKSLASH:
|
||||||
|
return ImGuiKey_Oem102;
|
||||||
|
case SDL_SCANCODE_BACKSLASH:
|
||||||
|
return ImGuiKey_Backslash;
|
||||||
|
case SDL_SCANCODE_SEMICOLON:
|
||||||
|
return ImGuiKey_Semicolon;
|
||||||
|
case SDL_SCANCODE_APOSTROPHE:
|
||||||
|
return ImGuiKey_Apostrophe;
|
||||||
|
case SDL_SCANCODE_COMMA:
|
||||||
|
return ImGuiKey_Comma;
|
||||||
|
case SDL_SCANCODE_PERIOD:
|
||||||
|
return ImGuiKey_Period;
|
||||||
|
case SDL_SCANCODE_SLASH:
|
||||||
|
return ImGuiKey_Slash;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace filamentapp_utils
|
||||||
|
|
||||||
|
#endif // TNT_FILAMENT_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
@@ -38,19 +38,18 @@ void* getNativeWindow(SDL_Window* sdlWindow) {
|
|||||||
SDL_GetWindowSize(sdlWindow, &width, &height);
|
SDL_GetWindowSize(sdlWindow, &width, &height);
|
||||||
|
|
||||||
// Static is used here to allocate the struct pointer for the lifetime of the program.
|
// Static is used here to allocate the struct pointer for the lifetime of the program.
|
||||||
// Without static the valid struct quickyly goes out of scope, and ends with seemingly
|
// Without static the valid struct quickly goes out of scope, and ends with seemingly
|
||||||
// random segfaults.
|
// random segfaults. We must update the values on each call.
|
||||||
static struct {
|
static struct {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
} wayland {
|
} wayland;
|
||||||
wmi.info.wl.display,
|
wayland.display = wmi.info.wl.display;
|
||||||
wmi.info.wl.surface,
|
wayland.surface = wmi.info.wl.surface;
|
||||||
static_cast<uint32_t>(width),
|
wayland.width = static_cast<uint32_t>(width);
|
||||||
static_cast<uint32_t>(height)
|
wayland.height = static_cast<uint32_t>(height);
|
||||||
};
|
|
||||||
return (void *) &wayland;
|
return (void *) &wayland;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,25 +18,125 @@
|
|||||||
|
|
||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
#include <utils/memalign.h>
|
||||||
|
|
||||||
static void BM_memcpy(benchmark::State& state) {
|
#include <cstddef>
|
||||||
char* src = new char[state.range(0)];
|
#include <cstdint>
|
||||||
char* dst = new char[state.range(0)];
|
#include <cstdlib>
|
||||||
memset(src, 'x', (size_t)state.range(0));
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
{
|
// ------------------------------------------------------------------------------------------------
|
||||||
PerformanceCounters pc(state);
|
|
||||||
for (auto _ : state) {
|
|
||||||
memcpy(dst, src, (size_t)state.range(0));
|
|
||||||
benchmark::DoNotOptimize(dst);
|
|
||||||
benchmark::DoNotOptimize(src);
|
|
||||||
benchmark::ClobberMemory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
|
|
||||||
|
|
||||||
delete[] src;
|
#ifdef __linux__
|
||||||
delete[] dst;
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns true if successful, false if failed
|
||||||
|
static bool pinThreadToCore(int core_id) {
|
||||||
|
#ifdef __linux__
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(core_id, &cpuset);
|
||||||
|
|
||||||
|
// 0 = current thread
|
||||||
|
int const result = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
|
||||||
|
return (result == 0);
|
||||||
|
#else
|
||||||
|
(void)core_id;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(BM_memcpy)->Range(8, 8192<<10)->Threads(1)->Threads(8);
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static constexpr size_t ALIGNMENT = 16 << 10;
|
||||||
|
|
||||||
|
template<class T, size_t Align>
|
||||||
|
struct AlignedAllocator {
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
struct rebind {
|
||||||
|
using other = AlignedAllocator<U, Align>;
|
||||||
|
};
|
||||||
|
|
||||||
|
AlignedAllocator() noexcept = default;
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
explicit AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept {}
|
||||||
|
|
||||||
|
T* allocate(size_t const n) {
|
||||||
|
size_t const bytes = n * sizeof(T);
|
||||||
|
// std::aligned_alloc requires size to be a multiple of alignment.
|
||||||
|
size_t const aligned_bytes = ((bytes + Align - 1) / Align) * Align;
|
||||||
|
void* ptr = utils::aligned_alloc(aligned_bytes, Align);
|
||||||
|
return static_cast<T*>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T* p, size_t) noexcept {
|
||||||
|
utils::aligned_free(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using PageAlignedVector = std::vector<T, AlignedAllocator<T, ALIGNMENT>>;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void BM_memcpy(benchmark::State& state) {
|
||||||
|
// pinThreadToCore(7);
|
||||||
|
int64_t const size = state.range(0);
|
||||||
|
PageAlignedVector<char> src(size);
|
||||||
|
PageAlignedVector<char> dst(size);
|
||||||
|
|
||||||
|
// make all these pages resident
|
||||||
|
memset(src.data(), 0, size);
|
||||||
|
memset(dst.data(), 0, size);
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
|
||||||
|
{
|
||||||
|
// PerformanceCounters const pc(state);
|
||||||
|
for (auto _: state) {
|
||||||
|
memcpy(dst.data(), src.data(), size);
|
||||||
|
benchmark::DoNotOptimize(dst);
|
||||||
|
benchmark::DoNotOptimize(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_memset(benchmark::State& state) {
|
||||||
|
//pinThreadToCore(7);
|
||||||
|
int64_t const size = state.range(0);
|
||||||
|
PageAlignedVector<char> src(size);
|
||||||
|
|
||||||
|
// make all these pages resident
|
||||||
|
memset(src.data(), 0, size);
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
|
||||||
|
{
|
||||||
|
// PerformanceCounters const pc(state);
|
||||||
|
for (auto _: state) {
|
||||||
|
memset(src.data(), 0, size);
|
||||||
|
benchmark::DoNotOptimize(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(BM_memcpy)
|
||||||
|
->DenseRange( 4<<10, 128<<10, 8<<10)
|
||||||
|
->DenseRange(128<<10, 4<<20, 128<<10)
|
||||||
|
->DenseRange( 4<<20, 16<<20, 1<<20)
|
||||||
|
->DenseRange( 16<<20, 32<<20, 2<<20)
|
||||||
|
;
|
||||||
|
|
||||||
|
BENCHMARK(BM_memset)
|
||||||
|
->DenseRange( 4<<10, 128<<10, 8<<10)
|
||||||
|
->DenseRange(128<<10, 4<<20, 128<<10)
|
||||||
|
->DenseRange( 4<<20, 16<<20, 1<<20)
|
||||||
|
->DenseRange( 16<<20, 32<<20, 2<<20)
|
||||||
|
;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
// Creates an Invocable that does not contain a functor.
|
// Creates an Invocable that does not contain a functor.
|
||||||
// Will evaluate to false.
|
// Will evaluate to false.
|
||||||
Invocable() = default;
|
Invocable() = default;
|
||||||
|
Invocable(std::nullptr_t) noexcept {}
|
||||||
|
|
||||||
~Invocable() noexcept;
|
~Invocable() noexcept;
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ public:
|
|||||||
|
|
||||||
Invocable& operator=(const Invocable&) = delete;
|
Invocable& operator=(const Invocable&) = delete;
|
||||||
Invocable& operator=(Invocable&& rhs) noexcept;
|
Invocable& operator=(Invocable&& rhs) noexcept;
|
||||||
|
Invocable& operator=(std::nullptr_t) noexcept;
|
||||||
|
|
||||||
// Invokes the invocable with the args passed in.
|
// Invokes the invocable with the args passed in.
|
||||||
// If the Invocable is empty, this will assert.
|
// If the Invocable is empty, this will assert.
|
||||||
@@ -141,6 +143,17 @@ Invocable<R(Args...)>& Invocable<R(Args...)>::operator=(Invocable&& rhs) noexcep
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename R, typename... Args>
|
||||||
|
Invocable<R(Args...)>& Invocable<R(Args...)>::operator=(std::nullptr_t) noexcept {
|
||||||
|
if (mDeleter) {
|
||||||
|
mDeleter(mInvocable);
|
||||||
|
}
|
||||||
|
mInvocable = nullptr;
|
||||||
|
mDeleter = nullptr;
|
||||||
|
mInvoker = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename R, typename... Args>
|
template<typename R, typename... Args>
|
||||||
template<typename... OperatorArgs>
|
template<typename... OperatorArgs>
|
||||||
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) {
|
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) {
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ AsyncJobQueue::AsyncJobQueue(const char* name, Priority priority) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncJobQueue::~AsyncJobQueue() noexcept {
|
AsyncJobQueue::~AsyncJobQueue() noexcept {
|
||||||
|
// wait for all pending callbacks to be called & terminate the thread
|
||||||
|
drainAndExit();
|
||||||
#if !defined(__EMSCRIPTEN__)
|
#if !defined(__EMSCRIPTEN__)
|
||||||
|
assert_invariant(!mThread.joinable());
|
||||||
assert_invariant(mQueue.empty());
|
assert_invariant(mQueue.empty());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -94,6 +97,9 @@ bool AsyncJobQueue::isValid() const noexcept {
|
|||||||
void AsyncJobQueue::drainAndExit() {
|
void AsyncJobQueue::drainAndExit() {
|
||||||
#if !defined(__EMSCRIPTEN__)
|
#if !defined(__EMSCRIPTEN__)
|
||||||
std::unique_lock lock(mLock);
|
std::unique_lock lock(mLock);
|
||||||
|
if (mExitRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// we request the service thread to exit, but we're guaranteed that it'll only exit
|
// we request the service thread to exit, but we're guaranteed that it'll only exit
|
||||||
// after all current callbacks are processed. In addition, once mExitRequested is set,
|
// after all current callbacks are processed. In addition, once mExitRequested is set,
|
||||||
// no new jobs can be added, so we can join the thread.
|
// no new jobs can be added, so we can join the thread.
|
||||||
|
|||||||
@@ -41,6 +41,36 @@ using namespace filament::math;
|
|||||||
namespace filament {
|
namespace filament {
|
||||||
namespace viewer {
|
namespace viewer {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ImGuiKey convertKey(int keycode) {
|
||||||
|
|
||||||
|
// The following table uses normal ANSI codes, which is consistent with the keyCode that
|
||||||
|
// comes from a web "keydown" event.
|
||||||
|
switch (keycode) {
|
||||||
|
case 9: return ImGuiKey_Tab;
|
||||||
|
case 37: return ImGuiKey_LeftArrow;
|
||||||
|
case 39: return ImGuiKey_RightArrow;
|
||||||
|
case 38: return ImGuiKey_UpArrow;
|
||||||
|
case 40: return ImGuiKey_DownArrow;
|
||||||
|
case 36: return ImGuiKey_Home;
|
||||||
|
case 35: return ImGuiKey_End;
|
||||||
|
case 46: return ImGuiKey_Delete;
|
||||||
|
case 8: return ImGuiKey_Backspace;
|
||||||
|
case 13: return ImGuiKey_Enter;
|
||||||
|
case 27: return ImGuiKey_Escape;
|
||||||
|
case 65: return ImGuiKey_A;
|
||||||
|
case 67: return ImGuiKey_C;
|
||||||
|
case 86: return ImGuiKey_V;
|
||||||
|
case 88: return ImGuiKey_X;
|
||||||
|
case 89: return ImGuiKey_Y;
|
||||||
|
case 90: return ImGuiKey_Z;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
mat4f fitIntoUnitCube(const Aabb& bounds, float zoffset) {
|
mat4f fitIntoUnitCube(const Aabb& bounds, float zoffset) {
|
||||||
float3 minpt = bounds.min;
|
float3 minpt = bounds.min;
|
||||||
float3 maxpt = bounds.max;
|
float3 maxpt = bounds.max;
|
||||||
@@ -563,27 +593,6 @@ void ViewerGui::renderUserInterface(float timeStepInSeconds, View* guiView, floa
|
|||||||
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
|
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
|
||||||
|
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
|
|
||||||
// The following table uses normal ANSI codes, which is consistent with the keyCode that
|
|
||||||
// comes from a web "keydown" event.
|
|
||||||
io.KeyMap[ImGuiKey_Tab] = 9;
|
|
||||||
io.KeyMap[ImGuiKey_LeftArrow] = 37;
|
|
||||||
io.KeyMap[ImGuiKey_RightArrow] = 39;
|
|
||||||
io.KeyMap[ImGuiKey_UpArrow] = 38;
|
|
||||||
io.KeyMap[ImGuiKey_DownArrow] = 40;
|
|
||||||
io.KeyMap[ImGuiKey_Home] = 36;
|
|
||||||
io.KeyMap[ImGuiKey_End] = 35;
|
|
||||||
io.KeyMap[ImGuiKey_Delete] = 46;
|
|
||||||
io.KeyMap[ImGuiKey_Backspace] = 8;
|
|
||||||
io.KeyMap[ImGuiKey_Enter] = 13;
|
|
||||||
io.KeyMap[ImGuiKey_Escape] = 27;
|
|
||||||
io.KeyMap[ImGuiKey_A] = 65;
|
|
||||||
io.KeyMap[ImGuiKey_C] = 67;
|
|
||||||
io.KeyMap[ImGuiKey_V] = 86;
|
|
||||||
io.KeyMap[ImGuiKey_X] = 88;
|
|
||||||
io.KeyMap[ImGuiKey_Y] = 89;
|
|
||||||
io.KeyMap[ImGuiKey_Z] = 90;
|
|
||||||
|
|
||||||
// TODO: this is not the best way to handle high DPI in ImGui, but it is fine when using the
|
// TODO: this is not the best way to handle high DPI in ImGui, but it is fine when using the
|
||||||
// proggy font. Users need to refresh their window when dragging between displays with
|
// proggy font. Users need to refresh their window when dragging between displays with
|
||||||
// different pixel ratios.
|
// different pixel ratios.
|
||||||
@@ -609,20 +618,28 @@ void ViewerGui::mouseEvent(float mouseX, float mouseY, bool mouseButton, float m
|
|||||||
io.MouseDown[0] = mouseButton != 0;
|
io.MouseDown[0] = mouseButton != 0;
|
||||||
io.MouseDown[1] = false;
|
io.MouseDown[1] = false;
|
||||||
io.MouseDown[2] = false;
|
io.MouseDown[2] = false;
|
||||||
io.KeyCtrl = control;
|
io.AddKeyEvent(ImGuiMod_Ctrl, control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyDownEvent(int keyCode) {
|
void ViewerGui::keyDownEvent(int keyCode) {
|
||||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
if (!mImGuiHelper) {
|
||||||
ImGui::GetIO().KeysDown[keyCode] = true;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(
|
||||||
|
convertKey(keyCode),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyUpEvent(int keyCode) {
|
void ViewerGui::keyUpEvent(int keyCode) {
|
||||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
if (!mImGuiHelper) {
|
||||||
ImGui::GetIO().KeysDown[keyCode] = false;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(
|
||||||
|
convertKey(keyCode),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyPressEvent(int charCode) {
|
void ViewerGui::keyPressEvent(int charCode) {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ set(MATERIAL_SRCS
|
|||||||
materials/sandboxLitSolidRefractionSsr.mat
|
materials/sandboxLitSolidRefractionSsr.mat
|
||||||
materials/sandboxSpecGloss.mat
|
materials/sandboxSpecGloss.mat
|
||||||
materials/sandboxSubsurface.mat
|
materials/sandboxSubsurface.mat
|
||||||
materials/sandboxTransformName.mat
|
|
||||||
materials/sandboxUnlit.mat
|
materials/sandboxUnlit.mat
|
||||||
materials/texturedLit.mat
|
materials/texturedLit.mat
|
||||||
)
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user