Compare commits
1 Commits
pf/test-st
...
pf/backend
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e32e6cf2a |
2
.github/workflows/android-continuous.yml
vendored
2
.github/workflows/android-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
|
||||
2
.github/workflows/ios-continuous.yml
vendored
2
.github/workflows/ios-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
|
||||
2
.github/workflows/linux-continuous.yml
vendored
2
.github/workflows/linux-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
|
||||
2
.github/workflows/mac-continuous.yml
vendored
2
.github/workflows/mac-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
|
||||
2
.github/workflows/npm-deploy.yml
vendored
2
.github/workflows/npm-deploy.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: npm-deploy
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.release_tag }}
|
||||
# Setup .npmrc file to publish to npm
|
||||
|
||||
10
.github/workflows/presubmit.yml
vendored
10
.github/workflows/presubmit.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
os: [macos-14, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat presubmit
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh presubmit
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh presubmit
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
TAG=${REF##*/}
|
||||
echo "ref=${REF}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
TAG=${REF##*/}
|
||||
echo "ref=${REF}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
TAG=${REF##*/}
|
||||
echo "ref=${REF}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- uses: actions/setup-java@v3
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
TAG=${REF##*/}
|
||||
echo "ref=${REF}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
echo "ref=${REF}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
|
||||
2
.github/workflows/web-continuous.yml
vendored
2
.github/workflows/web-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
|
||||
2
.github/workflows/windows-continuous.yml
vendored
2
.github/workflows/windows-continuous.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: actions/checkout@v3.3.0
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
|
||||
45
BUILDING.md
45
BUILDING.md
@@ -437,11 +437,10 @@ Finally simply open `docs/html/index.html` in your web browser.
|
||||
|
||||
## Software Rasterization
|
||||
|
||||
We have tested swiftshader and Mesa for software rasterization on the Vulkan/GL backends.
|
||||
|
||||
To use this for Vulkan, please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is
|
||||
installed on your machine. If you are doing a manual installation of the SDK on Linux, you will have
|
||||
to source `setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded.
|
||||
We have tested swiftshader for running software rasterization on the Vulkan backend. To use this,
|
||||
please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is installed on
|
||||
your machine. If you are doing a manual installation of the SDK on Linux, you will have to source
|
||||
`setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded.
|
||||
|
||||
### Swiftshader (Vulkan) [tested on macOS and Linux]
|
||||
|
||||
@@ -458,39 +457,5 @@ and then set `VK_ICD_FILENAMES` to the ICD json produced in the build. For examp
|
||||
export VK_ICD_FILENAMES=/Users/user/swiftshader/build/Darwin/vk_swiftshader_icd.json
|
||||
```
|
||||
|
||||
Build and run Filament as usual and specify the Vulkan backend when creating the Engine.
|
||||
Build Filament as normal and use the vulkan backend.
|
||||
|
||||
### Mesa's LLVMPipe (GL) and Lavapipe (Vulkan) [tested on Linux]
|
||||
|
||||
We will only cover steps that build Mesa from source. The official documentation of Mesa mentioned
|
||||
that in general precompiled libraries [are **not** made available](https://docs.mesa3d.org/precompiled.html).
|
||||
|
||||
Download the repo and make sure you have the build depedencies. For example (assuming an Ubuntu/Debian distro),
|
||||
```shell
|
||||
git clone https://gitlab.freedesktop.org/mesa/mesa.git
|
||||
sudo apt-get build-dep mesa
|
||||
```
|
||||
|
||||
To build both the GL and Vulkan rasterizers,
|
||||
|
||||
```shell
|
||||
cd mesa
|
||||
mkdir -p out
|
||||
meson setup builddir/ -Dprefix=$(pwd)/out -Dglx=xlib -Dgallium-drivers=swrast -Dvulkan-drivers=swrast
|
||||
meson install -C builddir/
|
||||
```
|
||||
|
||||
For GL, we need to ensure that we load the GL lib from the mesa output directory. For example, to run
|
||||
the debug `gltf_viewer`, we would execute
|
||||
```shell
|
||||
LD_LIBRARY_PATH=/Users/user/mesa/out/lib/x86_64-linux-gnu \
|
||||
./out/cmake-debug/samples/gltf_viewer -a opengl
|
||||
```
|
||||
|
||||
For Vulkan, we need to set the path to the ICD json, which tells the loader where to find the driver
|
||||
library. To run `gltf_viewer`, we would execute
|
||||
```shell
|
||||
VK_ICD_FILENAMES=/Users/user/mesa/out/share/vulkan/icd.d/lvp_icd.x86_64.json \
|
||||
./out/cmake-debug/samples/gltf_viewer -a vulkan
|
||||
|
||||
```
|
||||
|
||||
@@ -524,15 +524,13 @@ else()
|
||||
endif()
|
||||
|
||||
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
|
||||
# The value can be either "instanced", "multiview", or "none"
|
||||
set(FILAMENT_SAMPLES_STEREO_TYPE "none" CACHE STRING
|
||||
# The value can be either "instanced" or "multiview".
|
||||
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING
|
||||
"Stereoscopic type that shader files in gltfio and samples are built for."
|
||||
)
|
||||
string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE)
|
||||
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced"
|
||||
AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview"
|
||||
AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "none")
|
||||
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\", \"multiview\", or \"none\" ")
|
||||
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ")
|
||||
endif ()
|
||||
|
||||
# Compiling samples for multiview implies enabling multiview feature as well.
|
||||
@@ -735,6 +733,7 @@ add_subdirectory(${FILAMENT}/filament)
|
||||
add_subdirectory(${FILAMENT}/shaders)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/hat-trie/tnt)
|
||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
||||
add_subdirectory(${EXTERNAL}/smol-v/tnt)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.54.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.51.8'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.54.0'
|
||||
pod 'Filament', '~> 1.51.8'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
@@ -176,7 +176,6 @@ steps:
|
||||
- [x] KHR_materials_unlit
|
||||
- [x] KHR_materials_variants
|
||||
- [x] KHR_materials_volume
|
||||
- [x] KHR_materials_specular
|
||||
- [x] KHR_mesh_quantization
|
||||
- [x] KHR_texture_basisu
|
||||
- [x] KHR_texture_transform
|
||||
|
||||
@@ -7,46 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.54.1
|
||||
|
||||
|
||||
## v1.54.0
|
||||
|
||||
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
|
||||
- Fix a crash when compiling shaders on IMG devices
|
||||
|
||||
## v1.53.5
|
||||
|
||||
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
|
||||
certain GLES 3.0 drivers.
|
||||
|
||||
## v1.53.4
|
||||
|
||||
|
||||
## v1.53.3
|
||||
|
||||
- Add drag and drop support for IBL files for desktop gltf_viewer.
|
||||
|
||||
## v1.53.2
|
||||
|
||||
|
||||
## v1.53.1
|
||||
|
||||
|
||||
## v1.53.0
|
||||
|
||||
- engine: fix skinning normals with large transforms (b/342459864) [⚠️ **New Material Version**]
|
||||
|
||||
## v1.52.3
|
||||
|
||||
|
||||
## v1.52.2
|
||||
|
||||
|
||||
## v1.52.1
|
||||
|
||||
- Add instructions for using Mesa for software rasterization
|
||||
|
||||
## v1.51.9
|
||||
|
||||
|
||||
|
||||
@@ -83,12 +83,12 @@ buildscript {
|
||||
'minSdk': 21,
|
||||
'targetSdk': 34,
|
||||
'compileSdk': 34,
|
||||
'kotlin': '2.0.0',
|
||||
'kotlin_coroutines': '1.9.0-RC',
|
||||
'kotlin': '1.9.21',
|
||||
'kotlin_coroutines': '1.7.3',
|
||||
'buildTools': '34.0.0',
|
||||
'ndk': '27.0.11718014',
|
||||
'androidx_core': '1.13.1',
|
||||
'androidx_annotations': '1.8.0'
|
||||
'ndk': '26.1.10909125',
|
||||
'androidx_core': '1.12.0',
|
||||
'androidx_annotations': '1.7.0'
|
||||
]
|
||||
|
||||
ext.deps = [
|
||||
@@ -104,7 +104,7 @@ buildscript {
|
||||
]
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.4.1'
|
||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
}
|
||||
|
||||
|
||||
@@ -420,13 +420,6 @@ Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
|
||||
engine->setPaused(paused);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Engine_nUnprotected(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean paused) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
engine->unprotected();
|
||||
}
|
||||
|
||||
// Managers...
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
@@ -571,9 +564,3 @@ Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nat
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
return (jlong) builder->build();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Engine_getSteadyClockTimeNano(JNIEnv *env, jclass clazz) {
|
||||
return (jlong)Engine::getSteadyClockTimeNano();
|
||||
}
|
||||
|
||||
@@ -25,17 +25,12 @@ using namespace filament;
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount) {
|
||||
jlong nativeEngine, jobject buffer_, jint size) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
AutoBuffer buffer(env, buffer_, size);
|
||||
auto builder = Material::Builder();
|
||||
if (shBandCount) {
|
||||
builder.sphericalHarmonicsBandCount(shBandCount);
|
||||
}
|
||||
Material* material = builder
|
||||
Material* material = Material::Builder()
|
||||
.package(buffer.getData(), buffer.getSize())
|
||||
.build(*engine);
|
||||
|
||||
return (jlong) material;
|
||||
}
|
||||
|
||||
|
||||
@@ -245,18 +245,12 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jlong nativeMorphTargetBuffer) {
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferAt(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, int level, int primitiveIndex, jlong nativeMorphTargetBuffer,
|
||||
int offset, int count) {
|
||||
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
|
||||
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
|
||||
builder->morphing(morphTargetBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferOffsetAt(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, int level, int primitiveIndex, int offset) {
|
||||
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
|
||||
builder->morphing(level, primitiveIndex, offset);
|
||||
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@@ -328,12 +322,13 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferOffsetAt(JNIEnv*,
|
||||
Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEnv*,
|
||||
jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex,
|
||||
jlong, jint offset) {
|
||||
jlong nativeMorphTargetBuffer, jint offset, jint count) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level,
|
||||
(size_t) primitiveIndex, (size_t) offset);
|
||||
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
|
||||
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
|
||||
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
|
||||
@@ -28,14 +28,6 @@
|
||||
using namespace filament;
|
||||
using namespace backend;
|
||||
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *, jclass, jlong nativeRenderer,
|
||||
jlong vsyncSteadyClockTimeNano) {
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *, jclass, jlong nativeRenderer,
|
||||
jlong nativeSwapChain, jlong frameTimeNanos) {
|
||||
@@ -195,10 +187,3 @@ Java_com_google_android_filament_Renderer_nSetPresentationTime(JNIEnv *, jclass
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->setPresentationTime(monotonicClockNanos);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Renderer_nSetVsyncTime(JNIEnv *, jclass,
|
||||
jlong nativeRenderer, jlong steadyClockTimeNano) {
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->setVsyncTime(steadyClockTimeNano);
|
||||
}
|
||||
|
||||
@@ -531,12 +531,3 @@ Java_com_google_android_filament_View_nGetFogEntity(JNIEnv *env, jclass clazz,
|
||||
View *view = (View *) nativeView;
|
||||
return (jint)view->getFogEntity().getId();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass clazz,
|
||||
jlong nativeView, jlong nativeEngine) {
|
||||
View *view = (View *) nativeView;
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
view->clearFrameHistory(*engine);
|
||||
}
|
||||
|
||||
@@ -159,12 +159,9 @@ public class Engine {
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of technique for stereoscopic rendering. (Note that the materials used will need to be
|
||||
* compatible with the chosen technique.)
|
||||
* The type of technique for stereoscopic rendering
|
||||
*/
|
||||
public enum StereoscopicType {
|
||||
/** No stereoscopic rendering. */
|
||||
NONE,
|
||||
/** Stereoscopic rendering is performed using instanced rendering technique. */
|
||||
INSTANCED,
|
||||
/** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */
|
||||
@@ -408,7 +405,7 @@ public class Engine {
|
||||
*
|
||||
* @see View#setStereoscopicOptions
|
||||
*/
|
||||
public StereoscopicType stereoscopicType = StereoscopicType.NONE;
|
||||
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
|
||||
|
||||
/**
|
||||
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
|
||||
@@ -425,12 +422,9 @@ public class Engine {
|
||||
public long resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines how many frames texture entries are kept for in the cache. This
|
||||
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
|
||||
* Typically only one texture is evicted per frame.
|
||||
* The default is 1.
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 1;
|
||||
public long resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
@@ -1292,24 +1286,6 @@ public class Engine {
|
||||
nSetPaused(getNativeObject(), paused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the command queue to unprotected mode. Protected mode can be activated via
|
||||
* Renderer::beginFrame() using a protected SwapChain.
|
||||
* @see Renderer
|
||||
* @see SwapChain
|
||||
*/
|
||||
public void unprotected() {
|
||||
nUnprotected(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current time. This is a convenience function that simply returns the
|
||||
* time in nanosecond since epoch of std::chrono::steady_clock.
|
||||
* @return current time in nanosecond since epoch of std::chrono::steady_clock.
|
||||
* @see Renderer#beginFrame
|
||||
*/
|
||||
public static native long getSteadyClockTimeNano();
|
||||
|
||||
@UsedByReflection("TextureHelper.java")
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
@@ -1387,7 +1363,6 @@ public class Engine {
|
||||
private static native void nFlush(long nativeEngine);
|
||||
private static native boolean nIsPaused(long nativeEngine);
|
||||
private static native void nSetPaused(long nativeEngine, boolean paused);
|
||||
private static native void nUnprotected(long nativeEngine);
|
||||
private static native long nGetTransformManager(long nativeEngine);
|
||||
private static native long nGetLightManager(long nativeEngine);
|
||||
private static native long nGetRenderableManager(long nativeEngine);
|
||||
|
||||
@@ -346,7 +346,6 @@ public class Material {
|
||||
public static class Builder {
|
||||
private Buffer mBuffer;
|
||||
private int mSize;
|
||||
private int mShBandCount = 0;
|
||||
|
||||
/**
|
||||
* Specifies the material data. The material data is a binary blob produced by
|
||||
@@ -362,22 +361,6 @@ public class Material {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the indirect lights computations. This is only taken into account
|
||||
* if this material is lit and in the surface domain. This setting will affect the
|
||||
* IndirectLight computation if one is specified on the Scene and Spherical Harmonics
|
||||
* are used for the irradiance.
|
||||
*
|
||||
* @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default).
|
||||
* @return Reference to this Builder for chaining calls.
|
||||
* @see IndirectLight
|
||||
*/
|
||||
@NonNull
|
||||
public Builder sphericalHarmonicsBandCount(@IntRange(from = 0) int shBandCount) {
|
||||
mShBandCount = shBandCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the Material object.
|
||||
*
|
||||
@@ -389,8 +372,7 @@ public class Material {
|
||||
*/
|
||||
@NonNull
|
||||
public Material build(@NonNull Engine engine) {
|
||||
long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
|
||||
mBuffer, mSize, mShBandCount);
|
||||
long nativeMaterial = nBuilderBuild(engine.getNativeObject(), mBuffer, mSize);
|
||||
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
|
||||
return new Material(nativeMaterial);
|
||||
}
|
||||
@@ -1041,7 +1023,7 @@ public class Material {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount);
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size);
|
||||
private static native long nCreateInstance(long nativeMaterial);
|
||||
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
||||
private static native long nGetDefaultInstance(long nativeMaterial);
|
||||
|
||||
@@ -74,7 +74,7 @@ public class MorphTargetBuffer {
|
||||
*
|
||||
* @exception IllegalStateException if the MorphTargetBuffer could not be created
|
||||
*
|
||||
* @see #setMorphTargetBufferOffsetAt
|
||||
* @see #setMorphTargetBufferAt
|
||||
*/
|
||||
@NonNull
|
||||
public MorphTargetBuffer build(@NonNull Engine engine) {
|
||||
|
||||
@@ -524,7 +524,14 @@ public class RenderableManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls if the renderable has legacy vertex morphing targets, zero by default.
|
||||
* Controls if the renderable has vertex morphing targets, zero by default. This is
|
||||
* required to enable GPU morphing.
|
||||
*
|
||||
* <p>Filament supports two morphing modes: standard (default) and legacy.</p>
|
||||
*
|
||||
* <p>For standard morphing, A {@link MorphTargetBuffer} must be created and provided via
|
||||
* {@link RenderableManager#setMorphTargetBufferAt}. Standard morphing supports up to
|
||||
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
|
||||
*
|
||||
* For legacy morphing, the attached {@link VertexBuffer} must provide data in the
|
||||
* appropriate {@link VertexBuffer.VertexAttribute} slots (<code>MORPH_POSITION_0</code> etc).
|
||||
@@ -542,22 +549,6 @@ public class RenderableManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls if the renderable has vertex morphing targets, zero by default.
|
||||
*
|
||||
* <p>For standard morphing, A {@link MorphTargetBuffer} must be provided.
|
||||
* Standard morphing supports up to
|
||||
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
|
||||
*
|
||||
* <p>See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis
|
||||
* to advance the animation.</p>
|
||||
*/
|
||||
@NonNull
|
||||
public Builder morphing(@NonNull MorphTargetBuffer morphTargetBuffer) {
|
||||
nBuilderMorphingStandard(mNativeBuilder, morphTargetBuffer.getNativeObject());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the morph target buffer for a primitive.
|
||||
*
|
||||
@@ -569,13 +560,31 @@ public class RenderableManager {
|
||||
*
|
||||
* @param level the level of detail (lod), only 0 can be specified
|
||||
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
|
||||
* @param morphTargetBuffer specifies the morph target buffer
|
||||
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
|
||||
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder morphing(@IntRange(from = 0) int level,
|
||||
@IntRange(from = 0) int primitiveIndex,
|
||||
@IntRange(from = 0) int offset) {
|
||||
nBuilderSetMorphTargetBufferOffsetAt(mNativeBuilder, level, primitiveIndex, offset);
|
||||
@NonNull MorphTargetBuffer morphTargetBuffer,
|
||||
@IntRange(from = 0) int offset,
|
||||
@IntRange(from = 0) int count) {
|
||||
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex,
|
||||
morphTargetBuffer.getNativeObject(), offset, count);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to specify morph target buffer for a primitive.
|
||||
* For details, see the {@link RenderableManager.Builder#morphing}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder morphing(@IntRange(from = 0) int level,
|
||||
@IntRange(from = 0) int primitiveIndex,
|
||||
@NonNull MorphTargetBuffer morphTargetBuffer) {
|
||||
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex,
|
||||
morphTargetBuffer.getNativeObject(), 0, morphTargetBuffer.getVertexCount());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -678,11 +687,26 @@ public class RenderableManager {
|
||||
*
|
||||
* @see Builder#morphing
|
||||
*/
|
||||
public void setMorphTargetBufferOffsetAt(@EntityInstance int i,
|
||||
public void setMorphTargetBufferAt(@EntityInstance int i,
|
||||
@IntRange(from = 0) int level,
|
||||
@IntRange(from = 0) int primitiveIndex,
|
||||
@IntRange(from = 0) int offset) {
|
||||
nSetMorphTargetBufferOffsetAt(mNativeObject, i, level, primitiveIndex, 0, offset);
|
||||
@NonNull MorphTargetBuffer morphTargetBuffer,
|
||||
@IntRange(from = 0) int offset,
|
||||
@IntRange(from = 0) int count) {
|
||||
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex,
|
||||
morphTargetBuffer.getNativeObject(), offset, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to change morph target buffer for the given primitive.
|
||||
* For details, see the {@link RenderableManager#setMorphTargetBufferAt}.
|
||||
*/
|
||||
public void setMorphTargetBufferAt(@EntityInstance int i,
|
||||
@IntRange(from = 0) int level,
|
||||
@IntRange(from = 0) int primitiveIndex,
|
||||
@NonNull MorphTargetBuffer morphTargetBuffer) {
|
||||
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex,
|
||||
morphTargetBuffer.getNativeObject(), 0, morphTargetBuffer.getVertexCount());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -982,8 +1006,7 @@ public class RenderableManager {
|
||||
private static native int nBuilderSkinningBones(long nativeBuilder, int boneCount, Buffer bones, int remaining);
|
||||
private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset);
|
||||
private static native void nBuilderMorphing(long nativeBuilder, int targetCount);
|
||||
private static native void nBuilderMorphingStandard(long nativeBuilder, long nativeMorphTargetBuffer);
|
||||
private static native void nBuilderSetMorphTargetBufferOffsetAt(long nativeBuilder, int level, int primitiveIndex, int offset);
|
||||
private static native void nBuilderSetMorphTargetBufferAt(long nativeBuilder, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
|
||||
private static native void nBuilderEnableSkinningBuffers(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderFog(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderLightChannel(long nativeRenderableManager, int channel, boolean enable);
|
||||
@@ -993,7 +1016,7 @@ public class RenderableManager {
|
||||
private static native int nSetBonesAsMatrices(long nativeObject, int i, Buffer matrices, int remaining, int boneCount, int offset);
|
||||
private static native int nSetBonesAsQuaternions(long nativeObject, int i, Buffer quaternions, int remaining, int boneCount, int offset);
|
||||
private static native void nSetMorphWeights(long nativeObject, int instance, float[] weights, int offset);
|
||||
private static native void nSetMorphTargetBufferOffsetAt(long nativeObject, int i, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset);
|
||||
private static native void nSetMorphTargetBufferAt(long nativeObject, int i, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
|
||||
private static native int nGetMorphTargetCount(long nativeObject, int i);
|
||||
private static native void nSetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float cx, float cy, float cz, float ex, float ey, float ez);
|
||||
private static native void nSetLayerMask(long nativeRenderableManager, int i, int select, int value);
|
||||
|
||||
@@ -284,33 +284,6 @@ public class Renderer {
|
||||
nSetPresentationTime(getNativeObject(), monotonicClockNanos);
|
||||
}
|
||||
|
||||
/**
|
||||
* The use of this method is optional. It sets the VSYNC time expressed as the duration in
|
||||
* nanosecond since epoch of std::chrono::steady_clock.
|
||||
* If called, passing 0 to frameTimeNanos in Renderer.BeginFrame will use this
|
||||
* time instead.
|
||||
* @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock
|
||||
* @see Engine#getSteadyClockTimeNano
|
||||
* @see Renderer#beginFrame
|
||||
*/
|
||||
public void setVsyncTime(long steadyClockTimeNano) {
|
||||
nSetVsyncTime(getNativeObject(), steadyClockTimeNano);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call skipFrame when momentarily skipping frames, for instance if the content of the
|
||||
* scene doesn't change.
|
||||
*
|
||||
* @param vsyncSteadyClockTimeNano The time in nanoseconds when the frame started being rendered,
|
||||
* in the {@link System#nanoTime()} timebase. Divide this value by 1000000 to
|
||||
* convert it to the {@link android.os.SystemClock#uptimeMillis()}
|
||||
* time base. This typically comes from
|
||||
* {@link android.view.Choreographer.FrameCallback}.
|
||||
*/
|
||||
public void skipFrame(long vsyncSteadyClockTimeNano) {
|
||||
nSkipFrame(getNativeObject(), vsyncSteadyClockTimeNano);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a frame for this <code>Renderer</code>.
|
||||
* <p><code>beginFrame</code> manages frame pacing, and returns whether or not a frame should be
|
||||
@@ -729,8 +702,6 @@ public class Renderer {
|
||||
}
|
||||
|
||||
private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos);
|
||||
private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano);
|
||||
private static native void nSkipFrame(long nativeObject, long vsyncSteadyClockTimeNano);
|
||||
private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
|
||||
private static native void nEndFrame(long nativeRenderer);
|
||||
private static native void nRender(long nativeRenderer, long nativeView);
|
||||
|
||||
@@ -1233,18 +1233,6 @@ public class View {
|
||||
return nGetFogEntity(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
|
||||
* keeps a history of previous frame renders associated with the Renderer the view was last
|
||||
* used with. When switching Renderer, it may be necessary to clear that history by calling
|
||||
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
|
||||
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
|
||||
* being very different.
|
||||
*/
|
||||
public void clearFrameHistory(Engine engine) {
|
||||
nClearFrameHistory(getNativeObject(), engine.getNativeObject());
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed View");
|
||||
@@ -1306,7 +1294,7 @@ public class View {
|
||||
private static native void nSetMaterialGlobal(long nativeView, int index, float x, float y, float z, float w);
|
||||
private static native void nGetMaterialGlobal(long nativeView, int index, float[] out);
|
||||
private static native int nGetFogEntity(long nativeView);
|
||||
private static native void nClearFrameHistory(long nativeView, long nativeEngine);
|
||||
|
||||
|
||||
/**
|
||||
* List of available ambient occlusion techniques.
|
||||
|
||||
@@ -125,11 +125,6 @@ extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBu
|
||||
builder->groundPlane(a, b, c, d);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderPanning(JNIEnv*, jclass, jlong nativeBuilder, jboolean enabled) {
|
||||
Builder* builder = (Builder*) nativeBuilder;
|
||||
builder->panning(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) {
|
||||
Builder* builder = (Builder*) nativeBuilder;
|
||||
return (jlong) builder->build((Mode) mode);
|
||||
|
||||
@@ -274,17 +274,6 @@ public class Manipulator {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether panning is enabled in the manipulator.
|
||||
*
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder panning(Boolean enabled) {
|
||||
nBuilderPanning(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the <code>Manipulator</code> object.
|
||||
*
|
||||
@@ -494,7 +483,6 @@ public class Manipulator {
|
||||
private static native void nBuilderFlightPanSpeed(long nativeBuilder, float x, float y);
|
||||
private static native void nBuilderFlightMoveDamping(long nativeBuilder, float damping);
|
||||
private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d);
|
||||
private static native void nBuilderPanning(long nativeBuilder, Boolean enabled);
|
||||
private static native long nBuilderBuild(long nativeBuilder, int mode);
|
||||
|
||||
private static native void nDestroyManipulator(long nativeManip);
|
||||
|
||||
@@ -120,6 +120,7 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../third_party/cgltf
|
||||
../../third_party/meshoptimizer/src
|
||||
../../third_party/robin-map
|
||||
../../third_party/hat-trie
|
||||
../../third_party/stb
|
||||
../../libs/utils/include
|
||||
../../libs/ktxreader/include
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.54.0
|
||||
VERSION_NAME=1.51.8
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 17 10:40:18 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
25
build.sh
25
build.sh
@@ -64,9 +64,6 @@ function print_help {
|
||||
echo " enabling debug paths in the backend from the build script. For example, make a"
|
||||
echo " systrace-enabled build without directly changing #defines. Remember to add -f when"
|
||||
echo " changing this option."
|
||||
echo " -S type"
|
||||
echo " Enable stereoscopic rendering where type is one of [instanced|multiview]. This is only"
|
||||
echo " meant for building the samples."
|
||||
echo ""
|
||||
echo "Build types:"
|
||||
echo " release"
|
||||
@@ -178,8 +175,6 @@ ASAN_UBSAN_OPTION=""
|
||||
|
||||
BACKEND_DEBUG_FLAG_OPTION=""
|
||||
|
||||
STEREOSCOPIC_OPTION=""
|
||||
|
||||
IOS_BUILD_SIMULATOR=false
|
||||
BUILD_UNIVERSAL_LIBRARIES=false
|
||||
|
||||
@@ -239,7 +234,6 @@ function build_desktop_target {
|
||||
${MATOPT_OPTION} \
|
||||
${ASAN_UBSAN_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
${STEREOSCOPIC_OPTION} \
|
||||
${architectures} \
|
||||
../..
|
||||
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
|
||||
@@ -374,7 +368,6 @@ function build_android_target {
|
||||
${MATOPT_OPTION} \
|
||||
${VULKAN_ANDROID_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
${STEREOSCOPIC_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
@@ -609,7 +602,7 @@ function build_ios_target {
|
||||
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
${STEREOSCOPIC_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
@@ -796,7 +789,7 @@ function check_debug_release_build {
|
||||
|
||||
pushd "$(dirname "$0")" > /dev/null
|
||||
|
||||
while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" opt; do
|
||||
while getopts ":hacCfgijmp:q:uvslwedk:bx:" opt; do
|
||||
case ${opt} in
|
||||
h)
|
||||
print_help
|
||||
@@ -936,20 +929,6 @@ while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" opt; do
|
||||
;;
|
||||
x) BACKEND_DEBUG_FLAG_OPTION="-DFILAMENT_BACKEND_DEBUG_FLAG=${OPTARG}"
|
||||
;;
|
||||
S) case $(echo "${OPTARG}" | tr '[:upper:]' '[:lower:]') in
|
||||
instanced)
|
||||
STEREOSCOPIC_OPTION="-DFILAMENT_SAMPLES_STEREO_TYPE=instanced"
|
||||
;;
|
||||
multiview)
|
||||
STEREOSCOPIC_OPTION="-DFILAMENT_SAMPLES_STEREO_TYPE=multiview"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown stereoscopic type ${OPTARG}"
|
||||
echo "Type must be one of [instanced|multiview]"
|
||||
echo ""
|
||||
exit 1
|
||||
esac
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -${OPTARG}" >&2
|
||||
echo ""
|
||||
|
||||
@@ -57,8 +57,7 @@ FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/ndk.version)}
|
||||
|
||||
# Install the required NDK version specifically (if not present)
|
||||
if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
|
||||
yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses
|
||||
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION"
|
||||
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION" > /dev/null
|
||||
fi
|
||||
|
||||
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
|
||||
|
||||
@@ -1 +1 @@
|
||||
27.0.11718014
|
||||
26.1.10909125
|
||||
@@ -419,7 +419,6 @@ if (APPLE OR LINUX)
|
||||
test/test_BufferUpdates.cpp
|
||||
test/test_Callbacks.cpp
|
||||
test/test_MRT.cpp
|
||||
test/test_PushConstants.cpp
|
||||
test/test_LoadImage.cpp
|
||||
test/test_StencilBuffer.cpp
|
||||
test/test_Scissor.cpp
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/unwindows.h> // Because we define ERROR in the FenceStatus enum.
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/PresentCallable.h>
|
||||
|
||||
#include <utils/Invocable.h>
|
||||
@@ -118,7 +117,7 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
|
||||
"The number of buffer objects that can be attached to a VertexBuffer must be "
|
||||
"less than or equal to the maximum number of vertex attributes.");
|
||||
|
||||
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
|
||||
|
||||
/**
|
||||
@@ -1058,7 +1057,7 @@ struct RasterState {
|
||||
bool inverseFrontFaces : 1; // 31
|
||||
|
||||
//! padding, must be 0
|
||||
bool depthClamp : 1; // 32
|
||||
uint8_t padding : 1; // 32
|
||||
};
|
||||
uint32_t u = 0;
|
||||
};
|
||||
@@ -1244,14 +1243,20 @@ enum class Workaround : uint16_t {
|
||||
ADRENO_UNIFORM_ARRAY_CRASH,
|
||||
// Workaround a Metal pipeline compilation error with the message:
|
||||
// "Could not statically determine the target of a texture". See light_indirect.fs
|
||||
METAL_STATIC_TEXTURE_TARGET_ERROR,
|
||||
A8X_STATIC_TEXTURE_TARGET_ERROR,
|
||||
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
|
||||
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
|
||||
// Multiple workarounds needed for PowerVR GPUs
|
||||
POWER_VR_SHADER_WORKAROUNDS,
|
||||
};
|
||||
|
||||
using StereoscopicType = backend::Platform::StereoscopicType;
|
||||
//! The type of technique for stereoscopic rendering
|
||||
enum class StereoscopicType : uint8_t {
|
||||
// Stereoscopic rendering is performed using instanced rendering technique.
|
||||
INSTANCED,
|
||||
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -41,26 +41,6 @@ public:
|
||||
struct Fence {};
|
||||
struct Stream {};
|
||||
|
||||
/**
|
||||
* The type of technique for stereoscopic rendering. (Note that the materials used will need to
|
||||
* be compatible with the chosen technique.)
|
||||
*/
|
||||
enum class StereoscopicType : uint8_t {
|
||||
/**
|
||||
* No stereoscopic rendering
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* Stereoscopic rendering is performed using instanced rendering technique.
|
||||
*/
|
||||
INSTANCED,
|
||||
/**
|
||||
* Stereoscopic rendering is performed using the multiview feature from the graphics
|
||||
* backend.
|
||||
*/
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
struct DriverConfig {
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
@@ -75,8 +55,6 @@ public:
|
||||
*/
|
||||
size_t textureUseAfterFreePoolSize = 0;
|
||||
|
||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
@@ -93,11 +71,6 @@ public:
|
||||
* GLES 3.x backends.
|
||||
*/
|
||||
bool forceGLES2Context = false;
|
||||
|
||||
/**
|
||||
* Sets the technique for stereoscopic rendering.
|
||||
*/
|
||||
StereoscopicType stereoscopicType = StereoscopicType::NONE;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -90,13 +90,8 @@ protected:
|
||||
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
|
||||
|
||||
private:
|
||||
struct InitializeJvmForPerformanceManagerIfNeeded {
|
||||
InitializeJvmForPerformanceManagerIfNeeded();
|
||||
};
|
||||
|
||||
int mOSVersion;
|
||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
|
||||
utils::PerformanceHintManager mPerformanceHintManager;
|
||||
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
||||
|
||||
|
||||
@@ -90,20 +90,6 @@ public:
|
||||
VkExtent2D extent = {0, 0};
|
||||
};
|
||||
|
||||
struct ImageSyncData {
|
||||
static constexpr uint32_t INVALID_IMAGE_INDEX = UINT32_MAX;
|
||||
|
||||
// The index of the next image as returned by vkAcquireNextImage or equivalent.
|
||||
uint32_t imageIndex = INVALID_IMAGE_INDEX;
|
||||
|
||||
// Semaphore to be signaled once the image is available.
|
||||
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
|
||||
|
||||
// A function called right before vkQueueSubmit. After this call, the image must be
|
||||
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
|
||||
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
|
||||
};
|
||||
|
||||
VulkanPlatform();
|
||||
|
||||
~VulkanPlatform() override;
|
||||
@@ -141,12 +127,6 @@ public:
|
||||
* before recreating the swapchain. Default is true.
|
||||
*/
|
||||
bool flushAndWaitOnWindowResize = true;
|
||||
|
||||
/**
|
||||
* Whether the swapchain image should be transitioned to a layout suitable for
|
||||
* presentation. Default is true.
|
||||
*/
|
||||
bool transitionSwapChainImageLayoutForPresent = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -175,10 +155,13 @@ public:
|
||||
* corresponding VkImage will be used as the output color attachment. The client should signal
|
||||
* the `clientSignal` semaphore when the image is ready to be used by the backend.
|
||||
* @param handle The handle returned by createSwapChain()
|
||||
* @param outImageSyncData The synchronization data used for image readiness
|
||||
* @param clientSignal The semaphore that the client will signal to indicate that the backend
|
||||
* may render into the image.
|
||||
* @param index Pointer to memory that will be filled with the index that corresponding
|
||||
* to an image in the `SwapChainBundle.colors` array.
|
||||
* @return Result of acquire
|
||||
*/
|
||||
virtual VkResult acquire(SwapChainPtr handle, ImageSyncData* outImageSyncData);
|
||||
virtual VkResult acquire(SwapChainPtr handle, VkSemaphore clientSignal, uint32_t* index);
|
||||
|
||||
/**
|
||||
* Present the image corresponding to `index` to the display. The client should wait on
|
||||
|
||||
@@ -300,12 +300,11 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -173,26 +173,14 @@ public:
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"use-after-free of Handle with id=" << handle.getId();
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<Dp>(p);
|
||||
}
|
||||
|
||||
template<typename B>
|
||||
bool is_valid(Handle<B>& handle) {
|
||||
if (handle && isPoolHandle(handle.getId())) {
|
||||
auto [p, tag] = handleToPointer(handle.getId());
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
return expectedAge == age;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Dp, typename B>
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
@@ -252,8 +240,8 @@ private:
|
||||
Node* const pNode = static_cast<Node*>(p);
|
||||
uint8_t& expectedAge = pNode[-1].age;
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"double-free of Handle of size " << size << " at " << p;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
}
|
||||
expectedAge = (expectedAge + 1) & 0xF; // fixme
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ PresentCallable::PresentCallable(PresentFn fn, void* user) noexcept
|
||||
}
|
||||
|
||||
void PresentCallable::operator()(bool presentFrame) noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(mPresentFn) << "This PresentCallable was already called. "
|
||||
"PresentCallables should be called exactly once.";
|
||||
ASSERT_PRECONDITION(mPresentFn, "This PresentCallable was already called. " \
|
||||
"PresentCallables should be called exactly once.");
|
||||
mPresentFn(presentFrame, mUser);
|
||||
// Set mPresentFn to nullptr to denote that the callable has been called.
|
||||
mPresentFn = nullptr;
|
||||
|
||||
@@ -32,11 +32,10 @@
|
||||
# define HAS_MMAP 0
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -82,9 +81,6 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
// map the circular buffer once...
|
||||
vaddr = mmap(reserve_vaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
if (vaddr != MAP_FAILED) {
|
||||
// populate the address space with pages (because this is a circular buffer,
|
||||
// all the pages will be allocated eventually, might as well do it now)
|
||||
memset(vaddr, 0, size);
|
||||
// and map the circular buffer again, behind the previous copy...
|
||||
vaddr_shadow = mmap((char*)vaddr + size, size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
@@ -105,7 +101,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
if (UTILS_UNLIKELY(mAshmemFd < 0)) {
|
||||
// ashmem failed
|
||||
if (vaddr_guard != MAP_FAILED) {
|
||||
munmap(vaddr_guard, BLOCK_SIZE);
|
||||
munmap(vaddr_guard, size);
|
||||
}
|
||||
|
||||
if (vaddr_shadow != MAP_FAILED) {
|
||||
@@ -123,11 +119,12 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
data = mmap(nullptr, size * 2 + BLOCK_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(data != MAP_FAILED) <<
|
||||
"couldn't allocate " << (size * 2 / 1024) <<
|
||||
" KiB of virtual address space for the command buffer";
|
||||
ASSERT_POSTCONDITION(data,
|
||||
"couldn't allocate %u KiB of virtual address space for the command buffer",
|
||||
(size * 2 / 1024));
|
||||
|
||||
slog.w << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)" << io::endl;
|
||||
slog.d << "WARNING: Using soft CircularBuffer (" << (size * 2 / 1024) << " KiB)"
|
||||
<< io::endl;
|
||||
|
||||
// guard page at the end
|
||||
void* guard = (void*)(uintptr_t(data) + size * 2);
|
||||
|
||||
@@ -74,6 +74,8 @@ void CommandBufferQueue::setPaused(bool paused) {
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
|
||||
return (bool)mExitRequested;
|
||||
}
|
||||
|
||||
@@ -101,23 +103,22 @@ void CommandBufferQueue::flush() noexcept {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
||||
|
||||
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
||||
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
||||
"Space used at this time: " << used <<
|
||||
" bytes, overflow: " << used - mFreeSpace << " bytes";
|
||||
|
||||
mFreeSpace -= used;
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
ASSERT_POSTCONDITION(used <= mFreeSpace,
|
||||
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
||||
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
||||
"Space used at this time: %u bytes, overflow: %u bytes",
|
||||
(unsigned)used, unsigned(used - mFreeSpace));
|
||||
|
||||
// wait until there is enough space in the buffer
|
||||
mFreeSpace -= used;
|
||||
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
slog.d << "CommandStream used too much space (will block): "
|
||||
@@ -130,11 +131,9 @@ void CommandBufferQueue::flush() noexcept {
|
||||
#endif
|
||||
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(!mPaused) <<
|
||||
ASSERT_POSTCONDITION(!mPaused,
|
||||
"CommandStream is full, but since the rendering thread is paused, "
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.";
|
||||
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.");
|
||||
mCondition.wait(lock, [this, requiredSize]() -> bool {
|
||||
// TODO: on macOS, we need to call pumpEvents from time to time
|
||||
return mFreeSpace >= requiredSize;
|
||||
@@ -150,14 +149,16 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
|
||||
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
mFreeSpace += used;
|
||||
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,9 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
|
||||
|
||||
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(mId < HANDLE_HEAP_FLAG) <<
|
||||
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
|
||||
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
|
||||
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB";
|
||||
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
|
||||
|
||||
mOverflowMap.emplace(id, p);
|
||||
lock.unlock();
|
||||
|
||||
@@ -16,23 +16,13 @@
|
||||
|
||||
#include "private/backend/VirtualMachineEnv.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
JavaVM* VirtualMachineEnv::sVirtualMachine = nullptr;
|
||||
|
||||
/*
|
||||
* This is typically called by filament_jni.so when it is loaded. If filament_jni.so is not used,
|
||||
* then this must be called manually -- however, this is a problem because VirtualMachineEnv.h
|
||||
* is currently private and part of backend.
|
||||
* For now, we authorize this usage, but we will need to fix it; by making a proper public
|
||||
* API for this.
|
||||
*/
|
||||
UTILS_PUBLIC
|
||||
// This is called when the library is loaded. We need this to get a reference to the global VM
|
||||
UTILS_NOINLINE
|
||||
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
|
||||
JNIEnv* env = nullptr;
|
||||
|
||||
@@ -109,8 +109,9 @@ inline bool MTLSizeEqual(T a, T b) noexcept {
|
||||
MetalBlitter::MetalBlitter(MetalContext& context) noexcept : mContext(context) { }
|
||||
|
||||
void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, const char* label) {
|
||||
FILAMENT_CHECK_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth)
|
||||
<< "Blitting requires the source and destination regions to have the same depth.";
|
||||
|
||||
ASSERT_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth,
|
||||
"Blitting requires the source and destination regions to have the same depth.");
|
||||
|
||||
// Determine if the blit for color or depth are eligible to use a MTLBlitCommandEncoder.
|
||||
// blitFastPath returns true upon success.
|
||||
@@ -326,8 +327,7 @@ id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
<< "Unable to compile fragment shader for MetalBlitter.";
|
||||
ASSERT_POSTCONDITION(library && function, "Unable to compile fragment shader for MetalBlitter.");
|
||||
|
||||
return function;
|
||||
}
|
||||
@@ -352,8 +352,7 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
<< "Unable to compile vertex shader for MetalBlitter.";
|
||||
ASSERT_POSTCONDITION(library && function, "Unable to compile vertex shader for MetalBlitter.");
|
||||
|
||||
mVertexFunction = function;
|
||||
|
||||
|
||||
@@ -65,9 +65,12 @@ private:
|
||||
const char* mName;
|
||||
};
|
||||
|
||||
class TrackedMetalBuffer {
|
||||
public:
|
||||
#ifndef FILAMENT_METAL_BUFFER_TRACKING
|
||||
#define FILAMENT_METAL_BUFFER_TRACKING 0
|
||||
#endif
|
||||
|
||||
class MetalBufferTracking {
|
||||
public:
|
||||
static constexpr size_t EXCESS_BUFFER_COUNT = 30000;
|
||||
|
||||
enum class Type {
|
||||
@@ -91,66 +94,57 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
|
||||
TrackedMetalBuffer(nullptr_t) noexcept : mBuffer(nil) {}
|
||||
TrackedMetalBuffer(id<MTLBuffer> buffer, Type type) : mBuffer(buffer), mType(type) {
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
static void initialize() {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
for (size_t i = 0; i < TypeCount; i++) {
|
||||
aliveBuffers[i] = [NSHashTable weakObjectsHashTable];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void setPlatform(MetalPlatform* p) { platform = p; }
|
||||
|
||||
static void track(id<MTLBuffer> buffer, Type type) {
|
||||
assert_invariant(type != Type::NONE);
|
||||
if (buffer) {
|
||||
aliveBuffers[toIndex(type)]++;
|
||||
mType = type;
|
||||
if (getAliveBuffers() >= EXCESS_BUFFER_COUNT) {
|
||||
if (platform && platform->hasDebugUpdateStatFunc()) {
|
||||
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
|
||||
TrackedMetalBuffer::getAliveBuffers());
|
||||
}
|
||||
if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) {
|
||||
if (platform && platform->hasDebugUpdateStatFunc()) {
|
||||
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
|
||||
MetalBufferTracking::getAliveBuffers());
|
||||
}
|
||||
}
|
||||
[aliveBuffers[toIndex(type)] addObject:buffer];
|
||||
}
|
||||
|
||||
~TrackedMetalBuffer() {
|
||||
if (mBuffer) {
|
||||
assert_invariant(mType != Type::NONE);
|
||||
aliveBuffers[toIndex(mType)]--;
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
|
||||
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
|
||||
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
id<MTLBuffer> get() const noexcept { return mBuffer; }
|
||||
operator bool() const noexcept { return bool(mBuffer); }
|
||||
|
||||
static uint64_t getAliveBuffers() {
|
||||
uint64_t sum = 0;
|
||||
for (const auto& v : aliveBuffers) {
|
||||
sum += v;
|
||||
for (size_t i = 1; i < TypeCount; i++) {
|
||||
sum += getAliveBuffers(static_cast<Type>(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static uint64_t getAliveBuffers(Type type) {
|
||||
assert_invariant(type != Type::NONE);
|
||||
return aliveBuffers[toIndex(type)];
|
||||
NSHashTable* hashTable = aliveBuffers[toIndex(type)];
|
||||
// Caution! We can't simply use hashTable.count here, which is inaccurate.
|
||||
// See http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
|
||||
return hashTable.objectEnumerator.allObjects.count;
|
||||
}
|
||||
static void setPlatform(MetalPlatform* p) { platform = p; }
|
||||
#else
|
||||
static void initialize() {}
|
||||
static void setPlatform(MetalPlatform* p) {}
|
||||
static id<MTLBuffer> track(id<MTLBuffer> buffer, Type type) { return buffer; }
|
||||
static uint64_t getAliveBuffers() { return 0; }
|
||||
static uint64_t getAliveBuffers(Type type) { return 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
void swap(TrackedMetalBuffer& other) noexcept {
|
||||
std::swap(mBuffer, other.mBuffer);
|
||||
std::swap(mType, other.mType);
|
||||
}
|
||||
|
||||
id<MTLBuffer> mBuffer;
|
||||
Type mType = Type::NONE;
|
||||
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
static std::array<NSHashTable<id<MTLBuffer>>*, TypeCount> aliveBuffers;
|
||||
static MetalPlatform* platform;
|
||||
static std::array<uint64_t, TypeCount> aliveBuffers;
|
||||
#endif
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
@@ -204,16 +198,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
enum class UploadStrategy {
|
||||
POOL,
|
||||
BUMP_ALLOCATOR,
|
||||
};
|
||||
|
||||
void uploadWithPoolBuffer(void* src, size_t size, size_t byteOffset) const;
|
||||
void uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const;
|
||||
|
||||
UploadStrategy mUploadStrategy;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
size_t mBufferSize = 0;
|
||||
void* mCpuBuffer = nullptr;
|
||||
MetalContext& mContext;
|
||||
@@ -262,9 +247,11 @@ public:
|
||||
mBufferOptions(options),
|
||||
mSlotSizeBytes(computeSlotSize(layout)),
|
||||
mSlotCount(slotCount) {
|
||||
ScopedAllocationTimer timer("ring");
|
||||
mBuffer = { [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions],
|
||||
TrackedMetalBuffer::Type::RING };
|
||||
{
|
||||
ScopedAllocationTimer timer("ring");
|
||||
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
|
||||
}
|
||||
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING);
|
||||
assert_invariant(mBuffer);
|
||||
}
|
||||
|
||||
@@ -284,11 +271,11 @@ public:
|
||||
// finishes executing.
|
||||
{
|
||||
ScopedAllocationTimer timer("ring");
|
||||
mAuxBuffer = { [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions],
|
||||
TrackedMetalBuffer::Type::RING };
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
}
|
||||
MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING);
|
||||
assert_invariant(mAuxBuffer);
|
||||
return { mAuxBuffer.get(), 0 };
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -317,9 +304,9 @@ public:
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
return { mAuxBuffer.get(), 0 };
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
|
||||
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
|
||||
}
|
||||
|
||||
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
|
||||
@@ -328,8 +315,8 @@ public:
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
TrackedMetalBuffer mAuxBuffer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
id<MTLBuffer> mAuxBuffer;
|
||||
|
||||
MTLResourceOptions mBufferOptions;
|
||||
|
||||
|
||||
@@ -22,21 +22,16 @@
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
|
||||
MetalPlatform* TrackedMetalBuffer::platform = nullptr;
|
||||
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
|
||||
|
||||
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer)
|
||||
: mBufferSize(size), mContext(context) {
|
||||
const MetalBumpAllocator& allocator = *mContext.bumpAllocator;
|
||||
// VERTEX is also used for index buffers
|
||||
if (allocator.getCapacity() > 0 && bindingType == BufferObjectBinding::VERTEX) {
|
||||
mUploadStrategy = UploadStrategy::BUMP_ALLOCATOR;
|
||||
} else {
|
||||
mUploadStrategy = UploadStrategy::POOL;
|
||||
}
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
std::array<NSHashTable<id<MTLBuffer>>*, MetalBufferTracking::TypeCount>
|
||||
MetalBufferTracking::aliveBuffers;
|
||||
MetalPlatform* MetalBufferTracking::platform = nullptr;
|
||||
#endif
|
||||
|
||||
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
|
||||
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
|
||||
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
|
||||
// This won't work for SSBOs, since they are read/write.
|
||||
@@ -50,11 +45,10 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
|
||||
// Otherwise, we allocate a private GPU buffer.
|
||||
{
|
||||
ScopedAllocationTimer timer("generic");
|
||||
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
|
||||
TrackedMetalBuffer::Type::GENERIC };
|
||||
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(mBuffer)
|
||||
<< "Could not allocate Metal buffer of size " << size << ".";
|
||||
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC);
|
||||
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
|
||||
}
|
||||
|
||||
MetalBuffer::~MetalBuffer() {
|
||||
@@ -67,26 +61,37 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(size + byteOffset <= mBufferSize)
|
||||
<< "Attempting to copy " << size << " bytes into a buffer of size " << mBufferSize
|
||||
<< " at offset " << byteOffset;
|
||||
// The copy blit requires that byteOffset be a multiple of 4.
|
||||
FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3)) << "byteOffset must be a multiple of 4";
|
||||
ASSERT_PRECONDITION(size + byteOffset <= mBufferSize,
|
||||
"Attempting to copy %zu bytes into a buffer of size %zu at offset %zu",
|
||||
size, mBufferSize, byteOffset);
|
||||
|
||||
// If we have a cpu buffer, we can directly copy into it.
|
||||
// Either copy into the Metal buffer or into our cpu buffer.
|
||||
if (mCpuBuffer) {
|
||||
memcpy(static_cast<uint8_t*>(mCpuBuffer) + byteOffset, src, size);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mUploadStrategy) {
|
||||
case UploadStrategy::BUMP_ALLOCATOR:
|
||||
uploadWithBumpAllocator(src, size, byteOffset);
|
||||
break;
|
||||
case UploadStrategy::POOL:
|
||||
uploadWithPoolBuffer(src, size, byteOffset);
|
||||
break;
|
||||
}
|
||||
// Acquire a staging buffer to hold the contents of this update.
|
||||
MetalBufferPool* bufferPool = mContext.bufferPool;
|
||||
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
|
||||
memcpy(staging->buffer.contents, src, size);
|
||||
|
||||
// The blit below requires that byteOffset be a multiple of 4.
|
||||
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
|
||||
|
||||
// Encode a blit from the staging buffer into the private GPU buffer.
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit";
|
||||
[blitEncoder copyFromBuffer:staging->buffer
|
||||
sourceOffset:0
|
||||
toBuffer:mBuffer
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
|
||||
bufferPool->releaseBuffer(staging);
|
||||
}];
|
||||
}
|
||||
|
||||
void MetalBuffer::copyIntoBufferUnsynchronized(void* src, size_t size, size_t byteOffset) {
|
||||
@@ -101,7 +106,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
|
||||
return nil;
|
||||
}
|
||||
assert_invariant(mBuffer);
|
||||
return mBuffer.get();
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
|
||||
@@ -197,42 +202,5 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncod
|
||||
}
|
||||
}
|
||||
|
||||
void MetalBuffer::uploadWithPoolBuffer(void* src, size_t size, size_t byteOffset) const {
|
||||
MetalBufferPool* bufferPool = mContext.bufferPool;
|
||||
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
|
||||
memcpy(staging->buffer.get().contents, src, size);
|
||||
|
||||
// Encode a blit from the staging buffer into the private GPU buffer.
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit - pool buffer";
|
||||
[blitEncoder copyFromBuffer:staging->buffer.get()
|
||||
sourceOffset:0
|
||||
toBuffer:mBuffer.get()
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
|
||||
bufferPool->releaseBuffer(staging);
|
||||
}];
|
||||
}
|
||||
|
||||
void MetalBuffer::uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const {
|
||||
MetalBumpAllocator& allocator = *mContext.bumpAllocator;
|
||||
auto [buffer, offset] = allocator.allocateStagingArea(size);
|
||||
memcpy(static_cast<char*>(buffer.contents) + offset, src, size);
|
||||
|
||||
// Encode a blit from the staging buffer into the private GPU buffer.
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit - bump allocator";
|
||||
[blitEncoder copyFromBuffer:buffer
|
||||
sourceOffset:offset
|
||||
toBuffer:mBuffer.get()
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -32,34 +32,12 @@ struct MetalContext;
|
||||
|
||||
// Immutable POD representing a shared CPU-GPU buffer.
|
||||
struct MetalBufferPoolEntry {
|
||||
TrackedMetalBuffer buffer;
|
||||
id<MTLBuffer> buffer;
|
||||
size_t capacity;
|
||||
mutable uint64_t lastAccessed;
|
||||
mutable uint32_t referenceCount;
|
||||
};
|
||||
|
||||
class MetalBumpAllocator {
|
||||
public:
|
||||
MetalBumpAllocator(id<MTLDevice> device, size_t capacity);
|
||||
|
||||
/**
|
||||
* Allocates a staging area of the given size. Returns a pair of the buffer and the offset
|
||||
* within the buffer. The buffer is guaranteed to be at least the given size, but may be larger.
|
||||
* Clients must not write to the buffer beyond the returned offset + size.
|
||||
* Clients are responsible for holding a reference to the returned buffer.
|
||||
* Allocations are guaranteed to be aligned to 4 bytes.
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, size_t> allocateStagingArea(size_t size);
|
||||
|
||||
size_t getCapacity() const noexcept { return mCapacity; }
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
TrackedMetalBuffer mCurrentUploadBuffer = nil;
|
||||
size_t mHead = 0;
|
||||
size_t mCapacity;
|
||||
};
|
||||
|
||||
// Manages a pool of Metal buffers, periodically releasing ones that have been unused for awhile.
|
||||
class MetalBufferPool {
|
||||
public:
|
||||
|
||||
@@ -48,10 +48,10 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
|
||||
buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(buffer)
|
||||
<< "Could not allocate Metal staging buffer of size " << numBytes << ".";
|
||||
MetalBufferTracking::track(buffer, MetalBufferTracking::Type::STAGING);
|
||||
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
|
||||
.buffer = { buffer, TrackedMetalBuffer::Type::STAGING },
|
||||
.buffer = buffer,
|
||||
.capacity = numBytes,
|
||||
.lastAccessed = mCurrentFrame,
|
||||
.referenceCount = 1
|
||||
@@ -116,39 +116,5 @@ void MetalBufferPool::reset() noexcept {
|
||||
mFreeStages.clear();
|
||||
}
|
||||
|
||||
MetalBumpAllocator::MetalBumpAllocator(id<MTLDevice> device, size_t capacity)
|
||||
: mDevice(device), mCapacity(capacity) {
|
||||
if (mCapacity > 0) {
|
||||
mCurrentUploadBuffer = { [device newBufferWithLength:capacity options:MTLStorageModeShared],
|
||||
TrackedMetalBuffer::Type::STAGING };
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<id<MTLBuffer>, size_t> MetalBumpAllocator::allocateStagingArea(size_t size) {
|
||||
if (size == 0) {
|
||||
return { nil, 0 };
|
||||
}
|
||||
if (size > mCapacity) {
|
||||
return { [mDevice newBufferWithLength:size options:MTLStorageModeShared], 0 };
|
||||
}
|
||||
assert_invariant(mCurrentUploadBuffer);
|
||||
|
||||
// Align the head to a 4-byte boundary.
|
||||
mHead = (mHead + 3) & ~3;
|
||||
|
||||
if (UTILS_LIKELY(mHead + size <= mCapacity)) {
|
||||
const size_t oldHead = mHead;
|
||||
mHead += size;
|
||||
return { mCurrentUploadBuffer.get(), oldHead };
|
||||
}
|
||||
|
||||
// We're finished with the current allocation.
|
||||
mCurrentUploadBuffer = { [mDevice newBufferWithLength:mCapacity options:MTLStorageModeShared],
|
||||
TrackedMetalBuffer::Type::STAGING };
|
||||
mHead = size;
|
||||
|
||||
return { mCurrentUploadBuffer.get(), 0 };
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace backend {
|
||||
class MetalDriver;
|
||||
class MetalBlitter;
|
||||
class MetalBufferPool;
|
||||
class MetalBumpAllocator;
|
||||
class MetalRenderTarget;
|
||||
class MetalSamplerGroup;
|
||||
class MetalSwapChain;
|
||||
@@ -56,18 +55,6 @@ struct MetalVertexBuffer;
|
||||
|
||||
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
|
||||
|
||||
class MetalPushConstantBuffer {
|
||||
public:
|
||||
void setPushConstant(PushConstantVariant value, uint8_t index);
|
||||
bool isDirty() const { return mDirty; }
|
||||
void setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<PushConstantVariant> mPushConstants;
|
||||
bool mDirty = false;
|
||||
};
|
||||
|
||||
struct MetalContext {
|
||||
explicit MetalContext(size_t metalFreedTextureListSize)
|
||||
: texturesToDestroy(metalFreedTextureListSize) {}
|
||||
@@ -93,7 +80,7 @@ struct MetalContext {
|
||||
} highestSupportedGpuFamily;
|
||||
|
||||
struct {
|
||||
bool staticTextureTargetError;
|
||||
bool a8xStaticTextureTargetError;
|
||||
} bugs;
|
||||
|
||||
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
|
||||
@@ -112,7 +99,6 @@ struct MetalContext {
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
DepthClampStateTracker depthClampState;
|
||||
Handle<HwRenderPrimitive> currentRenderPrimitive;
|
||||
|
||||
// State caches.
|
||||
@@ -123,8 +109,6 @@ struct MetalContext {
|
||||
|
||||
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
|
||||
|
||||
std::array<MetalPushConstantBuffer, Program::SHADER_TYPE_COUNT> currentPushConstants;
|
||||
|
||||
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
|
||||
|
||||
// Keeps track of sampler groups we've finalized for the current render pass.
|
||||
@@ -143,7 +127,6 @@ struct MetalContext {
|
||||
utils::FixedCircularBuffer<Handle<HwTexture>> texturesToDestroy;
|
||||
|
||||
MetalBufferPool* bufferPool;
|
||||
MetalBumpAllocator* bumpAllocator;
|
||||
|
||||
MetalSwapChain* currentDrawSwapChain = nil;
|
||||
MetalSwapChain* currentReadSwapChain = nil;
|
||||
|
||||
@@ -113,8 +113,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
}
|
||||
}
|
||||
}];
|
||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||
<< "Could not obtain command buffer.";
|
||||
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
|
||||
return context->pendingCommandBuffer;
|
||||
}
|
||||
|
||||
@@ -154,68 +153,5 @@ bool isInRenderPass(MetalContext* context) {
|
||||
return context->currentRenderPassEncoder != nil;
|
||||
}
|
||||
|
||||
void MetalPushConstantBuffer::setPushConstant(PushConstantVariant value, uint8_t index) {
|
||||
if (mPushConstants.size() <= index) {
|
||||
mPushConstants.resize(index + 1);
|
||||
mDirty = true;
|
||||
}
|
||||
if (UTILS_LIKELY(mPushConstants[index] != value)) {
|
||||
mDirty = true;
|
||||
mPushConstants[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalPushConstantBuffer::setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage) {
|
||||
constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4;
|
||||
constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 26;
|
||||
|
||||
static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES];
|
||||
assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT);
|
||||
|
||||
size_t bufferSize = PUSH_CONSTANT_SIZE_BYTES * mPushConstants.size();
|
||||
for (size_t i = 0; i < mPushConstants.size(); i++) {
|
||||
const auto& constant = mPushConstants[i];
|
||||
std::visit(
|
||||
[i](auto arg) {
|
||||
if constexpr (std::is_same_v<decltype(arg), bool>) {
|
||||
// bool push constants are converted to uints in MSL.
|
||||
// We must ensure we write all the bytes for boolean values to work
|
||||
// correctly.
|
||||
uint32_t boolAsUint = arg ? 0x00000001 : 0x00000000;
|
||||
*(reinterpret_cast<uint32_t*>(buffer + PUSH_CONSTANT_SIZE_BYTES * i)) =
|
||||
boolAsUint;
|
||||
} else {
|
||||
*(decltype(arg)*)(buffer + PUSH_CONSTANT_SIZE_BYTES * i) = arg;
|
||||
}
|
||||
},
|
||||
constant);
|
||||
}
|
||||
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX:
|
||||
[(id<MTLRenderCommandEncoder>)encoder setVertexBytes:buffer
|
||||
length:bufferSize
|
||||
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
|
||||
break;
|
||||
case ShaderStage::FRAGMENT:
|
||||
[(id<MTLRenderCommandEncoder>)encoder setFragmentBytes:buffer
|
||||
length:bufferSize
|
||||
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
|
||||
break;
|
||||
case ShaderStage::COMPUTE:
|
||||
[(id<MTLComputeCommandEncoder>)encoder setBytes:buffer
|
||||
length:bufferSize
|
||||
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
|
||||
break;
|
||||
}
|
||||
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
void MetalPushConstantBuffer::clear() {
|
||||
mPushConstants.clear();
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_DRIVER_METALDRIVER_H
|
||||
#define TNT_FILAMENT_DRIVER_METALDRIVER_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include "private/backend/Driver.h"
|
||||
#include "DriverBase.h"
|
||||
|
||||
@@ -141,7 +140,6 @@ private:
|
||||
void enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
|
||||
@@ -53,14 +53,14 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
|
||||
// MetalRenderPrimitive : 24 many
|
||||
// MetalVertexBuffer : 32 moderate
|
||||
// -- less than or equal 32 bytes
|
||||
// MetalIndexBuffer : 40 moderate
|
||||
// MetalFence : 48 few
|
||||
// MetalIndexBuffer : 56 moderate
|
||||
// MetalBufferObject : 64 many
|
||||
// -- less than or equal 64 bytes
|
||||
// MetalBufferObject : 48 many
|
||||
// -- less than or equal 48 bytes
|
||||
// MetalSamplerGroup : 112 few
|
||||
// MetalProgram : 152 moderate
|
||||
// MetalTexture : 152 moderate
|
||||
// MetalSwapChain : 208 few
|
||||
// MetalSwapChain : 184 few
|
||||
// MetalRenderTarget : 272 few
|
||||
// MetalVertexBufferInfo : 552 moderate
|
||||
// -- less than or equal to 552 bytes
|
||||
@@ -102,12 +102,12 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
driverConfig.disableHandleUseAfterFreeCheck) {
|
||||
mContext->driver = this;
|
||||
|
||||
TrackedMetalBuffer::setPlatform(platform);
|
||||
ScopedAllocationTimer::setPlatform(platform);
|
||||
MetalBufferTracking::initialize();
|
||||
MetalBufferTracking::setPlatform(platform);
|
||||
|
||||
mContext->device = mPlatform.createDevice();
|
||||
assert_invariant(mContext->device);
|
||||
@@ -162,10 +162,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
|
||||
}
|
||||
|
||||
mContext->bugs.staticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A7 GPU"];
|
||||
mContext->bugs.a8xStaticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"];
|
||||
|
||||
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
|
||||
mContext->pipelineStateCache.setDevice(mContext->device);
|
||||
@@ -173,8 +171,6 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext->samplerStateCache.setDevice(mContext->device);
|
||||
mContext->argumentEncoderCache.setDevice(mContext->device);
|
||||
mContext->bufferPool = new MetalBufferPool(*mContext);
|
||||
mContext->bumpAllocator =
|
||||
new MetalBumpAllocator(mContext->device, driverConfig.metalUploadBufferSizeBytes);
|
||||
mContext->blitter = new MetalBlitter(*mContext);
|
||||
|
||||
if (@available(iOS 12, *)) {
|
||||
@@ -185,8 +181,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
|
||||
CVReturn success = CVMetalTextureCacheCreate(kCFAllocatorDefault, nullptr, mContext->device,
|
||||
nullptr, &mContext->textureCache);
|
||||
FILAMENT_CHECK_POSTCONDITION(success == kCVReturnSuccess)
|
||||
<< "Could not create Metal texture cache.";
|
||||
ASSERT_POSTCONDITION(success == kCVReturnSuccess, "Could not create Metal texture cache.");
|
||||
|
||||
if (@available(iOS 12, *)) {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
|
||||
@@ -207,13 +202,12 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
}
|
||||
|
||||
MetalDriver::~MetalDriver() noexcept {
|
||||
TrackedMetalBuffer::setPlatform(nullptr);
|
||||
MetalBufferTracking::setPlatform(nullptr);
|
||||
ScopedAllocationTimer::setPlatform(nullptr);
|
||||
mContext->device = nil;
|
||||
mContext->emptyTexture = nil;
|
||||
CFRelease(mContext->textureCache);
|
||||
delete mContext->bufferPool;
|
||||
delete mContext->bumpAllocator;
|
||||
delete mContext->blitter;
|
||||
delete mContext->timerQueryImpl;
|
||||
delete mContext->shaderCompiler;
|
||||
@@ -230,13 +224,16 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
|
||||
#endif
|
||||
if (mPlatform.hasDebugUpdateStatFunc()) {
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic",
|
||||
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::GENERIC));
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring",
|
||||
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::RING));
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging",
|
||||
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::STAGING));
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
const uint64_t generic = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::GENERIC);
|
||||
const uint64_t ring = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::RING);
|
||||
const uint64_t staging = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::STAGING);
|
||||
const uint64_t total = generic + ring + staging;
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers", total);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic", generic);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring", ring);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging", staging);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,14 +292,14 @@ void MetalDriver::endFrame(uint32_t frameId) {
|
||||
}
|
||||
|
||||
void MetalDriver::flush(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "flush must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"flush must be called outside of a render pass.");
|
||||
submitPendingCommands(mContext);
|
||||
}
|
||||
|
||||
void MetalDriver::finish(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "finish must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"finish must be called outside of a render pass.");
|
||||
// Wait for all frames to finish by submitting and waiting on a dummy command buffer.
|
||||
submitPendingCommands(mContext);
|
||||
id<MTLCommandBuffer> oneOffBuffer = [mContext->commandQueue commandBuffer];
|
||||
@@ -363,19 +360,19 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage) {
|
||||
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
|
||||
<< "Imported id<MTLTexture> width (" << metalTexture.width
|
||||
<< ") != Filament texture width (" << width << ")";
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.height == height)
|
||||
<< "Imported id<MTLTexture> height (" << metalTexture.height
|
||||
<< ") != Filament texture height (" << height << ")";
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.mipmapLevelCount == levels)
|
||||
<< "Imported id<MTLTexture> levels (" << metalTexture.mipmapLevelCount
|
||||
<< ") != Filament texture levels (" << levels << ")";
|
||||
ASSERT_PRECONDITION(metalTexture.width == width,
|
||||
"Imported id<MTLTexture> width (%d) != Filament texture width (%d)",
|
||||
metalTexture.width, width);
|
||||
ASSERT_PRECONDITION(metalTexture.height == height,
|
||||
"Imported id<MTLTexture> height (%d) != Filament texture height (%d)",
|
||||
metalTexture.height, height);
|
||||
ASSERT_PRECONDITION(metalTexture.mipmapLevelCount == levels,
|
||||
"Imported id<MTLTexture> levels (%d) != Filament texture levels (%d)",
|
||||
metalTexture.mipmapLevelCount, levels);
|
||||
MTLTextureType filamentMetalType = getMetalType(target);
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.textureType == filamentMetalType)
|
||||
<< "Imported id<MTLTexture> type (" << metalTexture.textureType
|
||||
<< ") != Filament texture type (" << filamentMetalType << ")";
|
||||
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
|
||||
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
|
||||
metalTexture.textureType, filamentMetalType);
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, metalTexture));
|
||||
}
|
||||
@@ -404,8 +401,8 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "createRenderTarget must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"createRenderTarget must be called outside of a render pass.");
|
||||
// Clamp sample count to what the device supports.
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
@@ -416,33 +413,33 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
continue;
|
||||
}
|
||||
const auto& buffer = color[i];
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.handle)
|
||||
<< "The COLOR" << i << " flag was specified, but invalid color handle provided.";
|
||||
ASSERT_PRECONDITION(buffer.handle,
|
||||
"The COLOR%u flag was specified, but invalid color handle provided.", i);
|
||||
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(colorTexture->getMtlTextureForWrite())
|
||||
<< "Color texture passed to render target has no texture allocation";
|
||||
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
|
||||
"Color texture passed to render target has no texture allocation");
|
||||
colorTexture->extendLodRangeTo(buffer.level);
|
||||
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment depthAttachment = {};
|
||||
if (any(targetBufferFlags & TargetBufferFlags::DEPTH)) {
|
||||
FILAMENT_CHECK_PRECONDITION(depth.handle)
|
||||
<< "The DEPTH flag was specified, but invalid depth handle provided.";
|
||||
ASSERT_PRECONDITION(depth.handle,
|
||||
"The DEPTH flag was specified, but invalid depth handle provided.");
|
||||
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(depthTexture->getMtlTextureForWrite())
|
||||
<< "Depth texture passed to render target has no texture allocation.";
|
||||
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
|
||||
"Depth texture passed to render target has no texture allocation.");
|
||||
depthTexture->extendLodRangeTo(depth.level);
|
||||
depthAttachment = { depthTexture, depth.level, depth.layer };
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment stencilAttachment = {};
|
||||
if (any(targetBufferFlags & TargetBufferFlags::STENCIL)) {
|
||||
FILAMENT_CHECK_PRECONDITION(stencil.handle)
|
||||
<< "The STENCIL flag was specified, but invalid stencil handle provided.";
|
||||
ASSERT_PRECONDITION(stencil.handle,
|
||||
"The STENCIL flag was specified, but invalid stencil handle provided.");
|
||||
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(stencilTexture->getMtlTextureForWrite())
|
||||
<< "Stencil texture passed to render target has no texture allocation.";
|
||||
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
|
||||
"Stencil texture passed to render target has no texture allocation.");
|
||||
stencilTexture->extendLodRangeTo(stencil.level);
|
||||
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
|
||||
}
|
||||
@@ -806,15 +803,13 @@ bool MetalDriver::isProtectedContentSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isStereoSupported() {
|
||||
switch (mStereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Metal.
|
||||
return false;
|
||||
case backend::StereoscopicType::NONE:
|
||||
return false;
|
||||
bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Metal.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,10 +829,6 @@ bool MetalDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthClampSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -846,8 +837,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
return true;
|
||||
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
|
||||
return false;
|
||||
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.staticTextureTargetError;
|
||||
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.a8xStaticTextureTargetError;
|
||||
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
|
||||
return false;
|
||||
default:
|
||||
@@ -891,8 +882,8 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
|
||||
|
||||
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "updateBufferObject must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateBufferObject must be called outside of a render pass.");
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
bo->updateBuffer(data.buffer, data.size, byteOffset);
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -931,8 +922,8 @@ void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "update3DImage must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"update3DImage must be called outside of a render pass.");
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
tex->loadImage(level, MTLRegionMake3D(xoffset, yoffset, zoffset, width, height, depth), data);
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -948,15 +939,15 @@ void MetalDriver::setupExternalImage(void* image) {
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "setExternalImage must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImage must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image);
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "setExternalImagePlane must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImagePlane must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image, plane);
|
||||
}
|
||||
@@ -971,15 +962,15 @@ TimerQueryResult MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint6
|
||||
}
|
||||
|
||||
void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "generateMipmaps must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"generateMipmaps must be called outside of a render pass.");
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
tex->generateMipmaps();
|
||||
}
|
||||
|
||||
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "updateSamplerGroup must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateSamplerGroup must be called outside of a render pass.");
|
||||
|
||||
auto sb = handle_cast<MetalSamplerGroup>(sbh);
|
||||
assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor));
|
||||
@@ -1118,10 +1109,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
mContext->currentPolygonOffset = {0.0f, 0.0f};
|
||||
|
||||
mContext->finalizedSamplerGroups.clear();
|
||||
|
||||
for (auto& pc : mContext->currentPushConstants) {
|
||||
pc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::nextSubpass(int dummy) {}
|
||||
@@ -1270,14 +1257,7 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
}
|
||||
|
||||
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
FILAMENT_CHECK_PRECONDITION(isInRenderPass(mContext))
|
||||
<< "setPushConstant must be called inside a render pass.";
|
||||
assert_invariant(static_cast<size_t>(stage) < mContext->currentPushConstants.size());
|
||||
MetalPushConstantBuffer& pushConstants =
|
||||
mContext->currentPushConstants[static_cast<size_t>(stage)];
|
||||
pushConstants.setPushConstant(value, index);
|
||||
}
|
||||
backend::PushConstantVariant value) {}
|
||||
|
||||
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
|
||||
|
||||
@@ -1329,8 +1309,8 @@ void MetalDriver::stopCapture(int) {
|
||||
|
||||
void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "readPixels must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"readPixels must be called outside of a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
// We always readPixels from the COLOR0 attachment.
|
||||
@@ -1344,19 +1324,17 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
width = std::min(static_cast<uint32_t>(srcTextureSize.width), width);
|
||||
|
||||
const MTLPixelFormat format = getMetalFormat(data.format, data.type);
|
||||
FILAMENT_CHECK_PRECONDITION(format != MTLPixelFormatInvalid)
|
||||
<< "The chosen combination of PixelDataFormat (" << (int)data.format
|
||||
<< ") and PixelDataType (" << (int)data.type
|
||||
<< ") is not supported for "
|
||||
"readPixels.";
|
||||
ASSERT_PRECONDITION(format != MTLPixelFormatInvalid,
|
||||
"The chosen combination of PixelDataFormat (%d) and PixelDataType (%d) is not supported for "
|
||||
"readPixels.", (int) data.format, (int) data.type);
|
||||
|
||||
const bool formatConversionNecessary = srcTexture.pixelFormat != format;
|
||||
|
||||
// TODO: MetalBlitter does not currently support format conversions to integer types.
|
||||
// The format and type must match the source pixel format exactly.
|
||||
FILAMENT_CHECK_PRECONDITION(!formatConversionNecessary || !isMetalFormatInteger(format))
|
||||
<< "readPixels does not support integer format conversions from MTLPixelFormat ("
|
||||
<< (int)srcTexture.pixelFormat << ") to (" << (int)format << ").";
|
||||
ASSERT_PRECONDITION(!formatConversionNecessary || !isMetalFormatInteger(format),
|
||||
"readPixels does not support integer format conversions from MTLPixelFormat (%d) to (%d).",
|
||||
(int) srcTexture.pixelFormat, (int) format);
|
||||
|
||||
MTLTextureDescriptor* textureDescriptor =
|
||||
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
|
||||
@@ -1422,31 +1400,31 @@ void MetalDriver::resolve(
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +srcTexture->samples
|
||||
<< ", dst.samples=" << +dstTexture->samples;
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isDepthFormat(srcTexture->format))
|
||||
<< "can't resolve depth formats";
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isStencilFormat(srcTexture->format))
|
||||
<< "can't resolve stencil formats";
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
// FIXME: on metal the blit() call below always take the slow path (using a shader)
|
||||
|
||||
@@ -1471,22 +1449,21 @@ void MetalDriver::blit(
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType))
|
||||
<< "Metal does not support blitting to/from non-2D textures.";
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
@@ -1524,18 +1501,18 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
// It is called between beginFrame and endFrame, but should never be called in the middle of
|
||||
// a render pass.
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
auto dstTarget = handle_cast<MetalRenderTarget>(dst);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
auto isBlitableTextureType = [](MTLTextureType t) {
|
||||
return t == MTLTextureType2D || t == MTLTextureType2DMultisample ||
|
||||
@@ -1547,10 +1524,9 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType))
|
||||
<< "Metal does not support blitting to/from non-2D textures.";
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = filter;
|
||||
@@ -1662,8 +1638,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "bindPipeline() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindPipeline() without a valid command encoder.");
|
||||
|
||||
MetalVertexBufferInfo const* const vbi =
|
||||
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
|
||||
@@ -1755,13 +1731,6 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
|
||||
}
|
||||
|
||||
// depth clip mode
|
||||
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
|
||||
mContext->depthClampState.updateState(depthClipMode);
|
||||
if (mContext->depthClampState.stateChanged()) {
|
||||
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
|
||||
}
|
||||
|
||||
// Set the depth-stencil state, if a state change is needed.
|
||||
DepthStencilState depthState;
|
||||
if (depthAttachment) {
|
||||
@@ -1842,8 +1811,8 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "bindRenderPrimitive() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindRenderPrimitive() without a valid command encoder.");
|
||||
|
||||
// Bind the user vertex buffers.
|
||||
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
@@ -1879,8 +1848,8 @@ void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "draw() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"draw() without a valid command encoder.");
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
@@ -1896,14 +1865,6 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t inst
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
|
||||
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
// Update push constants.
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
auto& pushConstants = mContext->currentPushConstants[i];
|
||||
if (UTILS_UNLIKELY(pushConstants.isDirty())) {
|
||||
pushConstants.setBytes(mContext->currentRenderPassEncoder, static_cast<ShaderStage>(i));
|
||||
}
|
||||
}
|
||||
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
|
||||
|
||||
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
|
||||
@@ -1929,8 +1890,8 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
|
||||
}
|
||||
|
||||
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "dispatchCompute must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"dispatchCompute must be called outside of a render pass.");
|
||||
|
||||
auto mtlProgram = handle_cast<MetalProgram>(program);
|
||||
|
||||
@@ -2030,15 +1991,15 @@ void MetalDriver::scissor(Viewport scissorBox) {
|
||||
}
|
||||
|
||||
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "beginTimerQuery must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"beginTimerQuery must be called outside of a render pass.");
|
||||
auto* tq = handle_cast<MetalTimerQuery>(tqh);
|
||||
mContext->timerQueryImpl->beginTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "endTimerQuery must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"endTimerQuery must be called outside of a render pass.");
|
||||
auto* tq = handle_cast<MetalTimerQuery>(tqh);
|
||||
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ constexpr inline MTLIndexType getIndexType(size_t elementSize) noexcept {
|
||||
} else if (elementSize == 4) {
|
||||
return MTLIndexTypeUInt32;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Index element size not supported.";
|
||||
ASSERT_POSTCONDITION(false, "Index element size not supported.");
|
||||
}
|
||||
|
||||
constexpr inline MTLVertexFormat getMetalFormat(ElementType type, bool normalized) noexcept {
|
||||
@@ -100,7 +100,7 @@ constexpr inline MTLVertexFormat getMetalFormat(ElementType type, bool normalize
|
||||
case ElementType::SHORT4: return MTLVertexFormatShort4Normalized;
|
||||
case ElementType::USHORT4: return MTLVertexFormatUShort4Normalized;
|
||||
default:
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
|
||||
ASSERT_POSTCONDITION(false, "Normalized format does not exist.");
|
||||
return MTLVertexFormatInvalid;
|
||||
}
|
||||
}
|
||||
@@ -326,8 +326,7 @@ constexpr inline MTLCullMode getMetalCullMode(CullingMode cullMode) noexcept {
|
||||
case CullingMode::FRONT: return MTLCullModeFront;
|
||||
case CullingMode::BACK: return MTLCullModeBack;
|
||||
case CullingMode::FRONT_AND_BACK:
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "FRONT_AND_BACK culling is not supported in Metal.";
|
||||
ASSERT_POSTCONDITION(false, "FRONT_AND_BACK culling is not supported in Metal.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; \
|
||||
utils::slog.e << description << utils::io::endl; \
|
||||
} \
|
||||
FILAMENT_CHECK_POSTCONDITION(error == nil) << message;
|
||||
ASSERT_POSTCONDITION(error == nil, message);
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
@@ -86,14 +86,14 @@ void MetalExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "Metal external images must be in either 32BGRA or 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"Metal external images must be in either 32BGRA or 420f format.");
|
||||
|
||||
size_t planeCount = CVPixelBufferGetPlaneCount(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(planeCount == 0 || planeCount == 2)
|
||||
<< "The Metal backend does not support images with plane counts of " << planeCount
|
||||
<< ".";
|
||||
ASSERT_POSTCONDITION(planeCount == 0 || planeCount == 2,
|
||||
"The Metal backend does not support images with plane counts of %d.", planeCount);
|
||||
|
||||
|
||||
if (planeCount == 0) {
|
||||
mImage = image;
|
||||
@@ -138,8 +138,8 @@ void MetalExternalImage::set(CVPixelBufferRef image, size_t plane) noexcept {
|
||||
}
|
||||
|
||||
const OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "Metal planar external images must be in the 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"Metal planar external images must be in the 420f format.");
|
||||
|
||||
mImage = image;
|
||||
|
||||
@@ -191,8 +191,8 @@ CVMetalTextureRef MetalExternalImage::createTextureFromImage(CVPixelBufferRef im
|
||||
CVMetalTextureRef texture;
|
||||
CVReturn result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
|
||||
mContext.textureCache, image, nullptr, format, width, height, plane, &texture);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == kCVReturnSuccess)
|
||||
<< "Could not create a CVMetalTexture from CVPixelBuffer.";
|
||||
ASSERT_POSTCONDITION(result == kCVReturnSuccess,
|
||||
"Could not create a CVMetalTexture from CVPixelBuffer.");
|
||||
|
||||
return texture;
|
||||
}
|
||||
@@ -203,8 +203,8 @@ void MetalExternalImage::shutdown(MetalContext& context) noexcept {
|
||||
|
||||
void MetalExternalImage::assertWritableImage(CVPixelBufferRef image) {
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_PRECONDITION(formatType == kCVPixelFormatType_32BGRA)
|
||||
<< "Metal SwapChain images must be in the 32BGRA format.";
|
||||
ASSERT_PRECONDITION(formatType == kCVPixelFormatType_32BGRA,
|
||||
"Metal SwapChain images must be in the 32BGRA format.");
|
||||
}
|
||||
|
||||
void MetalExternalImage::unset() {
|
||||
|
||||
@@ -109,7 +109,6 @@ private:
|
||||
NSUInteger headlessWidth = 0;
|
||||
NSUInteger headlessHeight = 0;
|
||||
CAMetalLayer* layer = nullptr;
|
||||
std::shared_ptr<std::mutex> layerDrawableMutex;
|
||||
MetalExternalImage externalImage;
|
||||
SwapChainType type;
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow
|
||||
: context(context),
|
||||
depthStencilFormat(decideDepthStencilFormat(flags)),
|
||||
layer(nativeWindow),
|
||||
layerDrawableMutex(std::make_shared<std::mutex>()),
|
||||
externalImage(context),
|
||||
type(SwapChainType::CAMETALLAYER) {
|
||||
|
||||
@@ -175,24 +174,14 @@ id<MTLTexture> MetalSwapChain::acquireDrawable() {
|
||||
}
|
||||
|
||||
assert_invariant(isCaMetalLayer());
|
||||
drawable = [layer nextDrawable];
|
||||
|
||||
// CAMetalLayer's drawable pool is not thread safe. Use a mutex when
|
||||
// calling -nextDrawable, or when releasing the last known reference
|
||||
// to any CAMetalDrawable returned from a previous -nextDrawable.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
|
||||
drawable = [layer nextDrawable];
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(drawable != nil) << "Could not obtain drawable.";
|
||||
ASSERT_POSTCONDITION(drawable != nil, "Could not obtain drawable.");
|
||||
return drawable.texture;
|
||||
}
|
||||
|
||||
void MetalSwapChain::releaseDrawable() {
|
||||
if (drawable) {
|
||||
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
|
||||
drawable = nil;
|
||||
}
|
||||
drawable = nil;
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalSwapChain::acquireDepthTexture() {
|
||||
@@ -267,11 +256,9 @@ public:
|
||||
PresentDrawableData(const PresentDrawableData&) = delete;
|
||||
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
|
||||
|
||||
static PresentDrawableData* create(id<CAMetalDrawable> drawable,
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver) {
|
||||
assert_invariant(drawableMutex);
|
||||
static PresentDrawableData* create(id<CAMetalDrawable> drawable, MetalDriver* driver) {
|
||||
assert_invariant(driver);
|
||||
return new PresentDrawableData(drawable, drawableMutex, driver);
|
||||
return new PresentDrawableData(drawable, driver);
|
||||
}
|
||||
|
||||
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
|
||||
@@ -290,22 +277,16 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
PresentDrawableData(id<CAMetalDrawable> drawable, std::shared_ptr<std::mutex> drawableMutex,
|
||||
MetalDriver* driver)
|
||||
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver) {}
|
||||
PresentDrawableData(id<CAMetalDrawable> drawable, MetalDriver* driver)
|
||||
: mDrawable(drawable), mDriver(driver) {}
|
||||
|
||||
static void cleanupAndDestroy(PresentDrawableData *that) {
|
||||
if (that->mDrawable) {
|
||||
std::lock_guard<std::mutex> lock(*(that->mDrawableMutex));
|
||||
that->mDrawable = nil;
|
||||
}
|
||||
that->mDrawableMutex.reset();
|
||||
that->mDrawable = nil;
|
||||
that->mDriver = nullptr;
|
||||
delete that;
|
||||
}
|
||||
|
||||
id<CAMetalDrawable> mDrawable;
|
||||
std::shared_ptr<std::mutex> mDrawableMutex;
|
||||
MetalDriver* mDriver = nullptr;
|
||||
};
|
||||
|
||||
@@ -323,8 +304,8 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
|
||||
struct Callback {
|
||||
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver)
|
||||
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver)) {}
|
||||
MetalDriver* driver)
|
||||
: f(callback), data(PresentDrawableData::create(drawable, driver)) {}
|
||||
std::shared_ptr<FrameScheduledCallback> f;
|
||||
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
|
||||
std::unique_ptr<PresentDrawableData> data;
|
||||
@@ -339,8 +320,8 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
|
||||
// This callback pointer will be captured by the block. Even if the scheduled handler is never
|
||||
// called, the unique_ptr will still ensure we don't leak memory.
|
||||
__block auto callback = std::make_unique<Callback>(
|
||||
frameScheduled.callback, drawable, layerDrawableMutex, context.driver);
|
||||
__block auto callback =
|
||||
std::make_unique<Callback>(frameScheduled.callback, drawable, context.driver);
|
||||
|
||||
backend::CallbackHandler* handler = frameScheduled.handler;
|
||||
MetalDriver* driver = context.driver;
|
||||
@@ -512,16 +493,15 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
externalImage(context, r, g, b, a) {
|
||||
|
||||
devicePixelFormat = decidePixelFormat(&context, format);
|
||||
FILAMENT_CHECK_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid)
|
||||
<< "Texture format not supported.";
|
||||
ASSERT_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid, "Texture format not supported.");
|
||||
|
||||
const BOOL mipmapped = levels > 1;
|
||||
const BOOL multisampled = samples > 1;
|
||||
|
||||
#if defined(IOS)
|
||||
const BOOL textureArray = target == SamplerType::SAMPLER_2D_ARRAY;
|
||||
FILAMENT_CHECK_PRECONDITION(!textureArray || !multisampled)
|
||||
<< "iOS does not support multisampled texture arrays.";
|
||||
ASSERT_PRECONDITION(!textureArray || !multisampled,
|
||||
"iOS does not support multisampled texture arrays.");
|
||||
#endif
|
||||
|
||||
const auto get2DTextureType = [](SamplerType target, bool isMultisampled) {
|
||||
@@ -556,12 +536,12 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
FILAMENT_CHECK_POSTCONDITION(!multisampled)
|
||||
<< "Multisampled cubemap faces not supported.";
|
||||
FILAMENT_CHECK_POSTCONDITION(width == height) << "Cubemap faces must be square.";
|
||||
ASSERT_POSTCONDITION(!multisampled, "Multisampled cubemap faces not supported.");
|
||||
ASSERT_POSTCONDITION(width == height, "Cubemap faces must be square.");
|
||||
descriptor = [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:devicePixelFormat
|
||||
size:width
|
||||
mipmapped:mipmapped];
|
||||
@@ -570,6 +550,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
descriptor = [MTLTextureDescriptor new];
|
||||
@@ -582,6 +563,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// If we're using external textures (CVPixelBufferRefs), we don't need to make any
|
||||
@@ -590,12 +572,6 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
break;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(target == SamplerType::SAMPLER_EXTERNAL || texture != nil)
|
||||
<< "Could not create Metal texture (SamplerType = " << int(target)
|
||||
<< ", levels = " << int(levels) << ", MTLPixelFormat = " << int(devicePixelFormat)
|
||||
<< ", width = " << width << ", height = " << height << ", depth = " << depth
|
||||
<< "). Out of memory?";
|
||||
|
||||
// If swizzling is set, set up a swizzled texture view that we'll use when sampling this texture.
|
||||
const bool isDefaultSwizzle =
|
||||
r == TextureSwizzle::CHANNEL_0 &&
|
||||
@@ -789,9 +765,9 @@ void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffs
|
||||
PixelBufferDescriptor const& data) noexcept {
|
||||
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(data.size >= shape.totalBytes)
|
||||
<< "Expected buffer size of at least " << shape.totalBytes
|
||||
<< " but received PixelBufferDescriptor with size " << data.size << ".";
|
||||
ASSERT_PRECONDITION(data.size >= shape.totalBytes,
|
||||
"Expected buffer size of at least %d but "
|
||||
"received PixelBufferDescriptor with size %d.", shape.totalBytes, data.size);
|
||||
|
||||
// Earlier versions of iOS don't have the maxBufferLength query, but 256 MB is a safe bet.
|
||||
NSUInteger deviceMaxBufferLength = 256 * 1024 * 1024; // 256 MB
|
||||
@@ -824,13 +800,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
|
||||
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
|
||||
const size_t stagingBufferSize = shape.totalBytes;
|
||||
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
|
||||
memcpy(entry->buffer.get().contents,
|
||||
memcpy(entry->buffer.contents,
|
||||
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
|
||||
stagingBufferSize);
|
||||
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
|
||||
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
|
||||
blitCommandEncoder.label = @"Texture upload buffer blit";
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:shape.bytesPerRow
|
||||
sourceBytesPerImage:shape.bytesPerSlice
|
||||
@@ -1012,9 +988,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
}
|
||||
color[i] = colorAttachments[i];
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(color[i].getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA COLOR" << i
|
||||
<< " texture, but sample count is " << samples << ".";
|
||||
ASSERT_PRECONDITION(color[i].getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA COLOR%d texture, but sample count is %d.",
|
||||
i, samples);
|
||||
|
||||
auto t = color[i].metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> color[i].level);
|
||||
@@ -1037,10 +1013,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (depthAttachment) {
|
||||
depth = depthAttachment;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(depth.getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count "
|
||||
"is "
|
||||
<< samples << ".";
|
||||
ASSERT_PRECONDITION(depth.getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count is %d.",
|
||||
samples);
|
||||
|
||||
auto t = depth.metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> depth.level);
|
||||
@@ -1063,10 +1038,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (stencilAttachment) {
|
||||
stencil = stencilAttachment;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(stencil.getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample "
|
||||
"count is "
|
||||
<< samples << ".";
|
||||
ASSERT_PRECONDITION(stencil.getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample count is %d.",
|
||||
samples);
|
||||
|
||||
auto t = stencil.metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> stencil.level);
|
||||
|
||||
@@ -43,8 +43,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
|
||||
// ------------------------------------------------------
|
||||
// 0 Zero buffer (placeholder vertex buffer) 1
|
||||
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
|
||||
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
|
||||
// 26 Push constants 1
|
||||
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
|
||||
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
|
||||
//
|
||||
// Total 31
|
||||
@@ -54,8 +53,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
|
||||
// Bindings Buffer name Count
|
||||
// ------------------------------------------------------
|
||||
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
|
||||
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
|
||||
// 26 Push constants 1
|
||||
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
|
||||
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
|
||||
//
|
||||
// Total 18
|
||||
@@ -382,7 +380,6 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
|
||||
|
||||
using CullModeStateTracker = StateTracker<MTLCullMode>;
|
||||
using WindingStateTracker = StateTracker<MTLWinding>;
|
||||
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
|
||||
|
||||
// Argument encoder
|
||||
|
||||
|
||||
@@ -90,17 +90,11 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
|
||||
NSError* error = nullptr;
|
||||
id<MTLRenderPipelineState> pipeline = [device newRenderPipelineStateWithDescriptor:descriptor
|
||||
error:&error];
|
||||
if (UTILS_UNLIKELY(pipeline == nil)) {
|
||||
NSString *errorMessage =
|
||||
[NSString stringWithFormat:@"Could not create Metal pipeline state: %@",
|
||||
error ? error.localizedDescription : @"unknown error"];
|
||||
auto description = [errorMessage cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
if (error) {
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
[[NSException exceptionWithName:@"MetalRenderPipelineFailure"
|
||||
reason:errorMessage
|
||||
userInfo:nil] raise];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(error == nil) << "Could not create Metal pipeline state.";
|
||||
ASSERT_POSTCONDITION(error == nil, "Could not create Metal pipeline state.");
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ bool NoopDriver::isProtectedContentSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -202,10 +202,6 @@ bool NoopDriver::isProtectedTexturesSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthClampSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,8 +66,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
Platform::DriverConfig const& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mSamplerMap(32),
|
||||
mDriverConfig(driverConfig) {
|
||||
mSamplerMap(32) {
|
||||
|
||||
state.vao.p = &mDefaultVAO;
|
||||
|
||||
@@ -367,8 +366,7 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ext.EXT_clip_cull_distance
|
||||
&& mDriverConfig.stereoscopicType == StereoscopicType::INSTANCED) {
|
||||
if (ext.EXT_clip_cull_distance) {
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
glEnable(GL_CLIP_DISTANCE1);
|
||||
}
|
||||
@@ -541,9 +539,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
bugs->delay_fbo_destruction = true;
|
||||
// PowerVR seems to have no problem with this (which is good for us)
|
||||
bugs->allow_read_only_ancillary_feedback_loop = true;
|
||||
// PowerVR doesn't respect lengths passed to glShaderSource, so concatenate them into a
|
||||
// single string.
|
||||
bugs->concatenate_shader_strings = true;
|
||||
} else if (strstr(renderer, "Apple")) {
|
||||
// Apple GPU
|
||||
} else if (strstr(renderer, "Tegra") ||
|
||||
@@ -679,7 +674,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
#endif
|
||||
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
|
||||
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
|
||||
@@ -750,7 +744,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
|
||||
ext->EXT_clip_cull_distance = true;
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
ext->EXT_depth_clamp = true;
|
||||
ext->EXT_discard_framebuffer = false;
|
||||
ext->EXT_disjoint_timer_query = true;
|
||||
ext->EXT_multisampled_render_to_texture = false;
|
||||
|
||||
@@ -220,9 +220,8 @@ public:
|
||||
bool EXT_color_buffer_float;
|
||||
bool EXT_color_buffer_half_float;
|
||||
bool EXT_debug_marker;
|
||||
bool EXT_depth_clamp;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_disjoint_timer_query;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_multisampled_render_to_texture2;
|
||||
bool EXT_multisampled_render_to_texture;
|
||||
bool EXT_protected_textures;
|
||||
@@ -240,10 +239,10 @@ public:
|
||||
bool KHR_parallel_shader_compile;
|
||||
bool KHR_texture_compression_astc_hdr;
|
||||
bool KHR_texture_compression_astc_ldr;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool OES_depth24;
|
||||
bool OES_depth_texture;
|
||||
bool OES_depth24;
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool OES_rgb8_rgba8;
|
||||
bool OES_standard_derivatives;
|
||||
bool OES_texture_npot;
|
||||
@@ -316,12 +315,6 @@ public:
|
||||
// bugs or performance issues.
|
||||
bool force_feature_level0;
|
||||
|
||||
// Some drivers don't respect the length argument of glShaderSource() and (apparently)
|
||||
// require each shader source string to be null-terminated. This works around the issue by
|
||||
// concatenating the strings into a single null-terminated string before passing it to
|
||||
// glShaderSource().
|
||||
bool concatenate_shader_strings;
|
||||
|
||||
} bugs = {};
|
||||
|
||||
// state getters -- as needed.
|
||||
@@ -518,8 +511,6 @@ private:
|
||||
mutable tsl::robin_map<SamplerParams, GLuint,
|
||||
SamplerParams::Hasher, SamplerParams::EqualTo> mSamplerMap;
|
||||
|
||||
Platform::DriverConfig const mDriverConfig;
|
||||
|
||||
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
|
||||
|
||||
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
|
||||
@@ -568,9 +559,6 @@ private:
|
||||
{ bugs.force_feature_level0,
|
||||
"force_feature_level0",
|
||||
""},
|
||||
{ bugs.concatenate_shader_strings,
|
||||
"concatenate_shader_strings",
|
||||
""},
|
||||
}};
|
||||
|
||||
// this is chosen to minimize code size
|
||||
@@ -637,7 +625,6 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
|
||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||
case GL_PROGRAM_POINT_SIZE: index = 10; break;
|
||||
#endif
|
||||
case GL_DEPTH_CLAMP: index = 11; break;
|
||||
default: break;
|
||||
}
|
||||
assert_invariant(index < state.enables.caps.size());
|
||||
|
||||
@@ -83,20 +83,12 @@
|
||||
#define HAS_MAPBUFFERS 1
|
||||
#endif
|
||||
|
||||
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
// set to the desired debug marker level (for user markers [default: All])
|
||||
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
|
||||
|
||||
// set to the desired debug level (for internal debugging [Default: None])
|
||||
// set to the desired debug marker level
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
|
||||
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
@@ -204,37 +196,11 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
|
||||
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
|
||||
: driver(driver) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
#endif
|
||||
driver.pushGroupMarker(string, strlen(string));
|
||||
}
|
||||
|
||||
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
#endif
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -338,13 +304,6 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
|
||||
void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
if (UTILS_UNLIKELY(!mValidProgram)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
utils::Slice<std::pair<GLint, ConstantType>> constants;
|
||||
if (stage == ShaderStage::VERTEX) {
|
||||
constants = mCurrentPushConstants->vertexConstants;
|
||||
@@ -381,11 +340,15 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
|
||||
}
|
||||
|
||||
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
|
||||
bool const success = p->use(this, mContext);
|
||||
assert_invariant(success == p->isValid());
|
||||
if (UTILS_UNLIKELY(!p->isValid())) {
|
||||
// If the program is not valid, we can't call use().
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2() && success)) {
|
||||
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
|
||||
p->use(this, mContext);
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
|
||||
auto [id, buffer, age] = mContext.getEs2UniformBinding(i);
|
||||
if (buffer) {
|
||||
@@ -396,8 +359,7 @@ bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
// when mPlatform.isSRGBSwapChainSupported() is false (no need to check though).
|
||||
p->setRec709ColorSpace(mRec709OutputColorspace);
|
||||
}
|
||||
|
||||
return success;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -451,14 +413,6 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
|
||||
} else {
|
||||
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
}
|
||||
|
||||
if (gl.ext.EXT_depth_clamp) {
|
||||
if (rs.depthClamp) {
|
||||
gl.enable(GL_DEPTH_CLAMP);
|
||||
} else {
|
||||
gl.disable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
@@ -1540,8 +1494,9 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
FILAMENT_CHECK_POSTCONDITION(sc->swapChain) << "createSwapChain(" << nativeWindow << ", "
|
||||
<< flags << ") failed. See logs for details.";
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
|
||||
nativeWindow, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
@@ -1560,9 +1515,9 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
FILAMENT_CHECK_POSTCONDITION(sc->swapChain)
|
||||
<< "createSwapChainHeadless(" << width << ", " << height << ", " << flags
|
||||
<< ") failed. See logs for details.";
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
|
||||
width, height, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
@@ -2095,19 +2050,19 @@ bool OpenGLDriver::isProtectedContentSupported() {
|
||||
return mPlatform.isProtectedContextSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isStereoSupported() {
|
||||
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
// Instanced-stereo requires instancing and EXT_clip_cull_distance.
|
||||
// Multiview-stereo requires ES 3.0 and OVR_multiview2.
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
return false;
|
||||
}
|
||||
switch (mDriverConfig.stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
return mContext.ext.OVR_multiview2;
|
||||
case backend::StereoscopicType::NONE:
|
||||
return false;
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
return mContext.ext.OVR_multiview2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2127,10 +2082,6 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
|
||||
return getContext().ext.EXT_protected_textures;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isDepthClampSupported() {
|
||||
return getContext().ext.EXT_depth_clamp;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -2382,20 +2333,11 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
auto const* const pSamplers = (SamplerDescriptor const*)data.buffer;
|
||||
for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) {
|
||||
GLuint samplerId = 0u;
|
||||
Handle<HwTexture> th = pSamplers[i].t;
|
||||
if (UTILS_LIKELY(th)) {
|
||||
GLTexture const* const t = handle_cast<const GLTexture*>(th);
|
||||
const GLTexture* t = nullptr;
|
||||
if (UTILS_LIKELY(pSamplers[i].t)) {
|
||||
t = handle_cast<const GLTexture*>(pSamplers[i].t);
|
||||
assert_invariant(t);
|
||||
|
||||
if (UTILS_UNLIKELY(es2)
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
|| UTILS_UNLIKELY(anisotropyWorkaround)
|
||||
#endif
|
||||
) {
|
||||
// We must set texture parameters on the texture itself.
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
}
|
||||
|
||||
SamplerParams params = pSamplers[i].s;
|
||||
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
|
||||
// From OES_EGL_image_external spec:
|
||||
@@ -2449,7 +2391,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
// which is not an error.
|
||||
}
|
||||
|
||||
sb->textureUnitEntries[i] = { th, samplerId };
|
||||
sb->textureUnitEntries[i] = { t, samplerId };
|
||||
}
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
@@ -3206,14 +3148,14 @@ void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
@@ -3223,14 +3165,14 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
void OpenGLDriver::popGroupMarker(int) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
@@ -3664,11 +3606,13 @@ void OpenGLDriver::resolve(
|
||||
assert_invariant(s);
|
||||
assert_invariant(d);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(d->width == s->width && d->height == s->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
d->width == s->width && d->height == s->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(s->samples > 1 && d->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +s->samples << ", dst.samples=" << +d->samples;
|
||||
ASSERT_PRECONDITION(s->samples > 1 && d->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+s->samples, +d->samples);
|
||||
|
||||
blit( dst, dstLevel, dstLayer, {},
|
||||
src, srcLevel, srcLayer, {},
|
||||
@@ -3824,12 +3768,12 @@ void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
UTILS_UNUSED_IN_RELEASE auto& gl = mContext;
|
||||
assert_invariant(!gl.isES2());
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
|
||||
@@ -3936,7 +3880,6 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3958,7 +3901,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3979,7 +3921,6 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
|
||||
}
|
||||
|
||||
void OpenGLDriver::scissor(Viewport scissor) {
|
||||
DEBUG_MARKER()
|
||||
setScissor(scissor);
|
||||
}
|
||||
|
||||
@@ -3999,7 +3940,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
DEBUG_MARKER()
|
||||
getShaderCompilerService().tick();
|
||||
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
class DebugMarker {
|
||||
UTILS_UNUSED OpenGLDriver& driver;
|
||||
OpenGLDriver& driver;
|
||||
public:
|
||||
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
|
||||
~DebugMarker() noexcept;
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
struct GLSamplerGroup : public HwSamplerGroup {
|
||||
using HwSamplerGroup::HwSamplerGroup;
|
||||
struct Entry {
|
||||
Handle<HwTexture> th;
|
||||
GLTexture const* texture = nullptr;
|
||||
GLuint sampler = 0u;
|
||||
};
|
||||
utils::FixedCapacityVector<Entry> textureUnitEntries;
|
||||
@@ -256,11 +256,6 @@ private:
|
||||
return mHandleAllocator.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
template<typename B>
|
||||
bool is_valid(Handle<B>& handle) {
|
||||
return mHandleAllocator.is_valid(handle);
|
||||
}
|
||||
|
||||
template<typename Dp, typename B>
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
|
||||
@@ -20,24 +20,21 @@
|
||||
#include "OpenGLDriver.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <private/backend/BackendUtils.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <new>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -244,9 +241,8 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
|
||||
assert_invariant(sb);
|
||||
if (!sb) continue; // should never happen, this would be a user error.
|
||||
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
|
||||
Handle<HwTexture> th = sb->textureUnitEntries[j].th;
|
||||
if (th) { // program may not use all samplers of sampler group
|
||||
GLTexture const* const t = gld->handle_cast<GLTexture const*>(th);
|
||||
const GLTexture* const t = sb->textureUnitEntries[j].texture;
|
||||
if (t) { // program may not use all samplers of sampler group
|
||||
gld->bindTexture(tmu, t);
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_LIKELY(!es2)) {
|
||||
|
||||
@@ -53,19 +53,9 @@ public:
|
||||
|
||||
bool isValid() const noexcept { return mToken || gl.program != 0; }
|
||||
|
||||
bool use(OpenGLDriver* const gld, OpenGLContext& context) noexcept {
|
||||
// both non-null is impossible by construction
|
||||
assert_invariant(!mToken || !gl.program);
|
||||
|
||||
if (UTILS_UNLIKELY(mToken && !gl.program)) {
|
||||
// first time a program is used
|
||||
initialize(*gld);
|
||||
}
|
||||
|
||||
void use(OpenGLDriver* const gld, OpenGLContext& context) noexcept {
|
||||
if (UTILS_UNLIKELY(!gl.program)) {
|
||||
// compilation failed (token should be null)
|
||||
assert_invariant(!mToken);
|
||||
return false;
|
||||
initialize(*gld);
|
||||
}
|
||||
|
||||
context.useProgram(gl.program);
|
||||
@@ -84,7 +74,6 @@ public:
|
||||
|
||||
updateSamplers(gld);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// For ES2 only
|
||||
|
||||
@@ -143,25 +143,21 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
|
||||
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
|
||||
auto state = weak.lock();
|
||||
if (!state) {
|
||||
// The timer query state has been destroyed on the way, very likely due to the IBL
|
||||
// prefilter context destruction. We still return true to get this element removed from
|
||||
// the query list.
|
||||
return true;
|
||||
if (state) {
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
} else {
|
||||
state->elapsed.store(int64_t(TimerQueryResult::ERROR), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,28 +26,16 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace utils;
|
||||
@@ -371,7 +359,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t& token) {
|
||||
GLuint const program = initialize(token);
|
||||
assert_invariant(token == nullptr);
|
||||
#if !FILAMENT_ENABLE_MATDBG
|
||||
#ifndef FILAMENT_ENABLE_MATDBG
|
||||
assert_invariant(program);
|
||||
#endif
|
||||
return program;
|
||||
@@ -488,12 +476,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
// check status of program linking and shader compilation, logs error and free all resources
|
||||
// in case of error.
|
||||
bool const success = checkProgramStatus(token);
|
||||
|
||||
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
|
||||
// The compilation error has been logged to log.e by this point.
|
||||
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
|
||||
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
|
||||
|
||||
if (UTILS_LIKELY(success)) {
|
||||
program = token->gl.program;
|
||||
// no need to keep the shaders around
|
||||
@@ -590,23 +572,16 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
|
||||
// split shader source, so we can insert the specialization constants and the packing
|
||||
// functions
|
||||
auto [version, prolog, body] = splitShaderSource({ shader_src, shader_len });
|
||||
auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
|
||||
|
||||
// enable ESSL 3.10 if available
|
||||
if (context.isAtLeastGLES<3, 1>()) {
|
||||
version = "#version 310 es\n";
|
||||
}
|
||||
|
||||
const std::array<const char*, 5> sources = {
|
||||
version.data(),
|
||||
const std::array<const char*, 4> sources = {
|
||||
prolog.data(),
|
||||
specializationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
};
|
||||
|
||||
const std::array<GLint, 5> lengths = {
|
||||
(GLint)version.length(),
|
||||
const std::array<GLint, 4> lengths = {
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specializationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
@@ -614,24 +589,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
};
|
||||
|
||||
GLuint const shaderId = glCreateShader(glShaderType);
|
||||
|
||||
if (UTILS_UNLIKELY(context.bugs.concatenate_shader_strings)) {
|
||||
size_t totalSize = 0;
|
||||
for (size_t i = 0; i < sources.size(); i++) {
|
||||
totalSize += lengths[i];
|
||||
}
|
||||
std::string concatenatedShaderSource;
|
||||
concatenatedShaderSource.reserve(totalSize);
|
||||
for (size_t i = 0; i < sources.size(); i++) {
|
||||
concatenatedShaderSource.append(sources[i], lengths[i]);
|
||||
}
|
||||
const GLchar* ptr = concatenatedShaderSource.c_str();
|
||||
GLint length = concatenatedShaderSource.length();
|
||||
glShaderSource(shaderId, 1, &ptr, &length);
|
||||
} else {
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
}
|
||||
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -703,7 +661,6 @@ void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
// macOS doesn't support GL_ARB_shading_language_packing
|
||||
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
||||
std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
|
||||
using namespace std::literals;
|
||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||
@@ -743,102 +700,31 @@ highp uint packHalf2x16(vec2 v) {
|
||||
highp uint y = fp32tou16(v.y);
|
||||
return (y << 16u) | x;
|
||||
}
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
|
||||
return R"(
|
||||
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif // BACKEND_OPENGL_VERSION_GLES
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
// split shader source code in three:
|
||||
// - the version line
|
||||
// - extensions
|
||||
// - everything else
|
||||
std::array<std::string_view, 3> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
|
||||
auto version_start = source.find("#version");
|
||||
assert_invariant(version_start != std::string_view::npos);
|
||||
// split shader source code in two, the first section goes from the start to the line after the
|
||||
// last #extension, and the 2nd part goes from there to the end.
|
||||
std::array<std::string_view, 2> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
|
||||
auto start = source.find("#version");
|
||||
assert_invariant(start != std::string_view::npos);
|
||||
|
||||
auto version_eol = source.find('\n', version_start) + 1;
|
||||
assert_invariant(version_eol != std::string_view::npos);
|
||||
|
||||
auto prolog_start = version_eol;
|
||||
auto prolog_eol = source.rfind("\n#extension"); // last #extension line
|
||||
if (prolog_eol == std::string_view::npos) {
|
||||
prolog_eol = prolog_start;
|
||||
auto pos = source.rfind("\n#extension");
|
||||
if (pos == std::string_view::npos) {
|
||||
pos = start;
|
||||
} else {
|
||||
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
|
||||
++pos;
|
||||
}
|
||||
auto body_start = prolog_eol;
|
||||
|
||||
std::string_view const version = source.substr(version_start, version_eol - version_start);
|
||||
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
|
||||
std::string_view const body = source.substr(body_start, source.length() - body_start);
|
||||
return { version, prolog, body };
|
||||
auto eol = source.find('\n', pos) + 1;
|
||||
assert_invariant(eol != std::string_view::npos);
|
||||
|
||||
std::string_view const version = source.substr(start, eol - start);
|
||||
std::string_view const body = source.substr(version.length(), source.length() - version.length());
|
||||
return { version, body };
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -146,7 +146,7 @@ private:
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
|
||||
static std::array<std::string_view, 2> splitShaderSource(std::string_view source) noexcept;
|
||||
|
||||
static GLuint linkProgram(OpenGLContext& context,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
||||
|
||||
@@ -201,12 +201,6 @@ using namespace glext;
|
||||
# define GL_CLIP_DISTANCE1 0x3001
|
||||
#endif
|
||||
|
||||
#if defined(GL_EXT_depth_clamp)
|
||||
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
|
||||
#else
|
||||
# define GL_DEPTH_CLAMP 0x864F
|
||||
#endif
|
||||
|
||||
#if defined(GL_KHR_debug)
|
||||
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
|
||||
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
|
||||
|
||||
@@ -111,8 +111,8 @@ bool CocoaExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA)
|
||||
<< "macOS external images must be 32BGRA format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA,
|
||||
"macOS external images must be 32BGRA format.");
|
||||
|
||||
// The pixel buffer must be locked whenever we do rendering with it. We'll unlock it before
|
||||
// releasing.
|
||||
|
||||
@@ -135,14 +135,13 @@ bool CocoaTouchExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "iOS external images must be in either 32BGRA or 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"iOS external images must be in either 32BGRA or 420f format.");
|
||||
|
||||
size_t planeCount = CVPixelBufferGetPlaneCount(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(planeCount == 0 || planeCount == 2)
|
||||
<< "The OpenGL backend does not support images with plane counts of " << planeCount
|
||||
<< ".";
|
||||
ASSERT_POSTCONDITION(planeCount == 0 || planeCount == 2,
|
||||
"The OpenGL backend does not support images with plane counts of %d.", planeCount);
|
||||
|
||||
// The pixel buffer must be locked whenever we do rendering with it. We'll unlock it before
|
||||
// releasing.
|
||||
|
||||
@@ -162,7 +162,7 @@ Driver* PlatformCocoaGL::createDriver(void* sharedContext, const Platform::Drive
|
||||
pImpl->mGLContext = nsOpenGLContext;
|
||||
|
||||
int result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE CVReturn success = CVOpenGLTextureCacheCreate(kCFAllocatorDefault, nullptr,
|
||||
[pImpl->mGLContext CGLContextObj], [pImpl->mGLContext.pixelFormat CGLPixelFormatObj], nullptr,
|
||||
|
||||
@@ -61,7 +61,7 @@ Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Pl
|
||||
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
|
||||
|
||||
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
|
||||
FILAMENT_CHECK_POSTCONDITION(context) << "Unable to create OpenGL ES context.";
|
||||
ASSERT_POSTCONDITION(context, "Unable to create OpenGL ES context.");
|
||||
|
||||
[EAGLContext setCurrentContext:context];
|
||||
|
||||
@@ -103,7 +103,7 @@ void PlatformCocoaTouchGL::createContext(bool shared) {
|
||||
EAGLContext* const context = [[EAGLContext alloc]
|
||||
initWithAPI:kEAGLRenderingAPIOpenGLES3
|
||||
sharegroup:sharegroup];
|
||||
FILAMENT_CHECK_POSTCONDITION(context) << "Unable to create extra OpenGL ES context.";
|
||||
ASSERT_POSTCONDITION(context, "Unable to create extra OpenGL ES context.");
|
||||
[EAGLContext setCurrentContext:context];
|
||||
pImpl->mAdditionalContexts.push_back(context);
|
||||
}
|
||||
@@ -180,8 +180,7 @@ bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChai
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, pImpl->mDefaultFramebuffer);
|
||||
GLenum const status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
FILAMENT_CHECK_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE)
|
||||
<< "Incomplete framebuffer.";
|
||||
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
#include <backend/platforms/PlatformEGLAndroid.h>
|
||||
|
||||
#include <private/backend/VirtualMachineEnv.h>
|
||||
|
||||
#include "opengl/GLUtils.h"
|
||||
#include "ExternalStreamManagerAndroid.h"
|
||||
|
||||
@@ -84,23 +82,9 @@ using EGLStream = Platform::Stream;
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmForPerformanceManagerIfNeeded() {
|
||||
// PerformanceHintManager() needs the calling thread to be a Java thread; so we need
|
||||
// to attach this thread to the JVM before we initialize PerformanceHintManager.
|
||||
// This should be done in PerformanceHintManager(), but libutils doesn't have access to
|
||||
// VirtualMachineEnv.
|
||||
if (PerformanceHintManager::isSupported()) {
|
||||
(void)VirtualMachineEnv::get().getEnvironment();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
||||
: PlatformEGL(),
|
||||
mExternalStreamManager(ExternalStreamManagerAndroid::create()),
|
||||
mInitializeJvmForPerformanceManagerIfNeeded(),
|
||||
mPerformanceHintManager() {
|
||||
mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
|
||||
|
||||
char scratch[PROP_VALUE_MAX + 1];
|
||||
int length = __system_property_get("ro.build.version.release", scratch);
|
||||
|
||||
@@ -226,7 +226,7 @@ Driver* PlatformGLX::createDriver(void* const sharedGLContext,
|
||||
g_glx.setCurrentContext(mGLXDisplay, mDummySurface, mDummySurface, mGLXContext);
|
||||
|
||||
int result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
|
||||
}
|
||||
|
||||
result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
VulkanAttachment src, VulkanAttachment dst,
|
||||
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
@@ -76,7 +76,7 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
VulkanAttachment src, VulkanAttachment dst) {
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
|
||||
@@ -28,7 +28,6 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
: mAllocator(allocator),
|
||||
mStagePool(stagePool),
|
||||
mUsage(usage),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedBytes(0) {
|
||||
// for now make sure that only 1 bit is set in usage
|
||||
// (because loadFromCpu() assumes that somewhat)
|
||||
@@ -81,7 +80,6 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.offset = mUpdatedOffset,
|
||||
.size = mUpdatedBytes,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1,
|
||||
@@ -95,7 +93,6 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
};
|
||||
vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
|
||||
@@ -42,7 +42,6 @@ private:
|
||||
VmaAllocation mGpuMemory = VK_NULL_HANDLE;
|
||||
VkBuffer mGpuBuffer = VK_NULL_HANDLE;
|
||||
VkBufferUsageFlags mUsage = {};
|
||||
uint32_t mUpdatedOffset = 0;
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ VulkanCommandBuffer& VulkanCommands::get() {
|
||||
// presenting the swap chain or waiting on a fence.
|
||||
while (mAvailableBufferCount == 0) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
FVK_LOGI << "VulkanCommands has stalled. "
|
||||
slog.i << "VulkanCommands has stalled. "
|
||||
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
|
||||
<< io::endl;
|
||||
#endif
|
||||
@@ -289,7 +289,7 @@ bool VulkanCommands::flush() {
|
||||
};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
FVK_LOGI << "Submitting cmdbuffer=" << cmdbuffer
|
||||
slog.i << "Submitting cmdbuffer=" << cmdbuffer
|
||||
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
|
||||
<< " signal=" << renderingFinished
|
||||
<< " fence=" << currentbuf->fence->fence
|
||||
@@ -305,7 +305,7 @@ bool VulkanCommands::flush() {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
if (result != VK_SUCCESS) {
|
||||
FVK_LOGD << "Failed command buffer submission result: " << result << utils::io::endl;
|
||||
utils::slog.d << "Failed command buffer submission result: " << result << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
@@ -320,7 +320,7 @@ VkSemaphore VulkanCommands::acquireFinishedSignal() {
|
||||
VkSemaphore semaphore = mSubmissionSignal;
|
||||
mSubmissionSignal = VK_NULL_HANDLE;
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
FVK_LOGI << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
|
||||
slog.i << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
|
||||
#endif
|
||||
return semaphore;
|
||||
}
|
||||
@@ -329,7 +329,7 @@ void VulkanCommands::injectDependency(VkSemaphore next) {
|
||||
assert_invariant(mInjectedSignal == VK_NULL_HANDLE);
|
||||
mInjectedSignal = next;
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
FVK_LOGI << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
|
||||
slog.i << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
|
||||
// If the timestamp is not 0, then we are carrying over a marker across buffer submits.
|
||||
// If it is 0, then this is a normal marker push and we should just print debug line as usual.
|
||||
if (timestamp.time_since_epoch().count() == 0.0) {
|
||||
FVK_LOGD << "----> " << str << utils::io::endl;
|
||||
utils::slog.d << "----> " << str << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -436,7 +436,7 @@ void VulkanCommands::popGroupMarker() {
|
||||
auto const [marker, startTime] = mGroupMarkers->pop();
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
<< utils::io::endl;
|
||||
#else
|
||||
mGroupMarkers->pop();
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// In debug builds, we enable validation layers and set up a debug callback.
|
||||
@@ -72,11 +70,6 @@
|
||||
// the currently active resources.
|
||||
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
|
||||
|
||||
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
|
||||
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
|
||||
// order of calls).
|
||||
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
|
||||
|
||||
// Useful default combinations
|
||||
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
#define FVK_DEBUG_PERFORMANCE \
|
||||
@@ -140,18 +133,6 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#define FVK_HANDLE_ARENA_SIZE_IN_MB 8
|
||||
#endif
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FVK_LOGI (utils::slog.i)
|
||||
#define FVK_LOGD FVK_LOGI
|
||||
#define FVK_LOGE FVK_LOGI
|
||||
#define FVK_LOGW FVK_LOGI
|
||||
#else
|
||||
#define FVK_LOGE (utils::slog.e)
|
||||
#define FVK_LOGW (utils::slog.w)
|
||||
#define FVK_LOGD (utils::slog.d)
|
||||
#define FVK_LOGI (utils::slog.i)
|
||||
#endif
|
||||
|
||||
// All vkCreate* functions take an optional allocator. For now we select the default allocator by
|
||||
// passing in a null pointer, and we highlight the argument by using the VKALLOC constant.
|
||||
constexpr struct VkAllocationCallbacks* VKALLOC = nullptr;
|
||||
|
||||
@@ -59,11 +59,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
|
||||
|
||||
VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
VkImageSubresourceRange range = getSubresourceRange();
|
||||
if (range.layerCount > 1) {
|
||||
return texture->getMultiviewAttachmentView(range);
|
||||
}
|
||||
return texture->getAttachmentView(range);
|
||||
return texture->getAttachmentView(getSubresourceRange());
|
||||
}
|
||||
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
@@ -77,7 +73,7 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
.layerCount = 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,7 +86,7 @@ VulkanTimestamps::VulkanTimestamps(VkDevice device) : mDevice(device) {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
tqpCreateInfo.queryCount = mUsed.size() * 2;
|
||||
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateQueryPool error.");
|
||||
mUsed.reset();
|
||||
}
|
||||
|
||||
@@ -104,7 +100,7 @@ std::tuple<uint32_t, uint32_t> VulkanTimestamps::getNextQuery() {
|
||||
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
|
||||
}
|
||||
}
|
||||
FVK_LOGE << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
|
||||
utils::slog.e << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
|
||||
return std::make_tuple(0, 1);
|
||||
}
|
||||
|
||||
@@ -138,8 +134,8 @@ VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const
|
||||
VkResult vkresult =
|
||||
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
|
||||
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
|
||||
<< "vkGetQueryPoolResults error: " << static_cast<int32_t>(vkresult);
|
||||
ASSERT_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY,
|
||||
"vkGetQueryPoolResults error: %d", static_cast<int32_t>(vkresult));
|
||||
if (vkresult == VK_NOT_READY) {
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ struct VulkanCommandBuffer;
|
||||
struct VulkanAttachment {
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t level = 0;
|
||||
uint8_t baseViewIndex = 0;
|
||||
uint8_t layerCount = 1;
|
||||
uint16_t layer = 0;
|
||||
|
||||
bool isDepth() const;
|
||||
@@ -122,36 +120,22 @@ public:
|
||||
}
|
||||
|
||||
inline bool isImageCubeArraySupported() const noexcept {
|
||||
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isDepthClampSupported() const noexcept {
|
||||
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
|
||||
return mPhysicalDeviceFeatures.imageCubeArray;
|
||||
}
|
||||
|
||||
inline bool isDebugMarkersSupported() const noexcept {
|
||||
return mDebugMarkersSupported;
|
||||
}
|
||||
|
||||
inline bool isDebugUtilsSupported() const noexcept {
|
||||
return mDebugUtilsSupported;
|
||||
}
|
||||
|
||||
inline bool isMultiviewEnabled() const noexcept {
|
||||
return mMultiviewEnabled;
|
||||
}
|
||||
|
||||
inline bool isClipDistanceSupported() const noexcept {
|
||||
return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE;
|
||||
}
|
||||
|
||||
private:
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||
VkPhysicalDeviceProperties mPhysicalDeviceProperties = {};
|
||||
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {};
|
||||
bool mDebugMarkersSupported = false;
|
||||
bool mDebugUtilsSupported = false;
|
||||
bool mMultiviewEnabled = false;
|
||||
|
||||
VkFormatList mDepthStencilFormats;
|
||||
VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
@@ -115,15 +115,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
|
||||
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
||||
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
utils::slog.e << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(pMessage, "ALL_GRAPHICS_BIT") || strstr(pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
utils::slog.w << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
}
|
||||
FVK_LOGE << utils::io::endl;
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
@@ -133,18 +133,18 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
|
||||
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
|
||||
void* pUserData) {
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
utils::slog.e << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT")
|
||||
|| strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
utils::slog.w << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
}
|
||||
FVK_LOGE << utils::io::endl;
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
@@ -173,8 +173,7 @@ DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const
|
||||
};
|
||||
VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo,
|
||||
VKALLOC, &mDebugMessenger);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan debug messenger.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
}
|
||||
@@ -229,8 +228,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mDescriptorSetManager(mPlatform->getDevice(), &mResourceAllocator),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
DebugUtils::mSingleton =
|
||||
@@ -248,8 +246,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
};
|
||||
VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
|
||||
&mDebugCallback);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan debug callback.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -292,7 +289,7 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
|
||||
// VulkanRenderTarget : 312 few
|
||||
// -- less than or equal to 312 bytes
|
||||
|
||||
FVK_LOGD
|
||||
utils::slog.d
|
||||
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
|
||||
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
|
||||
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
|
||||
@@ -325,10 +322,6 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
|
||||
}
|
||||
|
||||
void VulkanDriver::terminate() {
|
||||
// Flush and wait here to make sure all queued commands are executed and resources that are tied
|
||||
// to those commands are no longer referenced.
|
||||
finish(0);
|
||||
|
||||
delete mEmptyBufferObject;
|
||||
delete mEmptyTexture;
|
||||
|
||||
@@ -397,10 +390,7 @@ void VulkanDriver::collectGarbage() {
|
||||
}
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("beginFrame");
|
||||
// Do nothing.
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -595,8 +585,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
colorTargets[i] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(color[i].handle),
|
||||
.level = color[i].level,
|
||||
.baseViewIndex = color[i].baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = color[i].layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D();
|
||||
@@ -606,13 +594,11 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
}
|
||||
}
|
||||
|
||||
VulkanAttachment depthStencil;
|
||||
VulkanAttachment depthStencil[2] = {};
|
||||
if (depth.handle) {
|
||||
depthStencil[0] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
|
||||
.level = depth.level,
|
||||
.baseViewIndex = depth.baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = depth.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D();
|
||||
@@ -621,8 +607,17 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
attachmentCount++;
|
||||
}
|
||||
|
||||
// The stencil buffer is always assumed to be part of the depth-stencil buffer.
|
||||
assert_invariant(!stencil.handle || stencil.handle == depth.handle);
|
||||
if (stencil.handle) {
|
||||
depthStencil[1] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
|
||||
.level = stencil.level,
|
||||
.layer = stencil.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
|
||||
tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) };
|
||||
tmax = { std::max(tmax.x, extent.width), std::max(tmax.y, extent.height) };
|
||||
attachmentCount++;
|
||||
}
|
||||
|
||||
// All attachments must have the same dimensions, which must be greater than or equal to the
|
||||
// render target dimensions.
|
||||
@@ -632,7 +627,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
|
||||
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
|
||||
samples, colorTargets, depthStencil, mStagePool, layerCount);
|
||||
samples, colorTargets, depthStencil, mStagePool);
|
||||
mResourceManager.acquire(renderTarget);
|
||||
}
|
||||
|
||||
@@ -655,8 +650,8 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
|
||||
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
utils::slog.w << "sRGB swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
@@ -667,7 +662,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags) {
|
||||
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
|
||||
utils::slog.w << "sRGB swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
@@ -904,14 +899,13 @@ bool VulkanDriver::isProtectedContentSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isStereoSupported() {
|
||||
switch (mStereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return mContext.isClipDistanceSupported();
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
return mContext.isMultiviewEnabled();
|
||||
case backend::StereoscopicType::NONE:
|
||||
return false;
|
||||
bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Vulkan.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,10 +927,6 @@ bool VulkanDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthClampSupported() {
|
||||
return mContext.isDepthClampSupported();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -985,15 +975,15 @@ FeatureLevel VulkanDriver::getFeatureLevel() {
|
||||
|
||||
// If the max sampler counts do not meet FL2 standards, then this is an FL1 device.
|
||||
const auto& fl2 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2];
|
||||
if (limits.maxPerStageDescriptorSamplers < fl2.MAX_VERTEX_SAMPLER_COUNT ||
|
||||
limits.maxPerStageDescriptorSamplers < fl2.MAX_FRAGMENT_SAMPLER_COUNT) {
|
||||
if (fl2.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl2.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_1;
|
||||
}
|
||||
|
||||
// If the max sampler counts do not meet FL3 standards, then this is an FL2 device.
|
||||
const auto& fl3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
|
||||
if (limits.maxPerStageDescriptorSamplers < fl3.MAX_VERTEX_SAMPLER_COUNT||
|
||||
limits.maxPerStageDescriptorSamplers < fl3.MAX_FRAGMENT_SAMPLER_COUNT) {
|
||||
if (fl3.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl3.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_2;
|
||||
}
|
||||
|
||||
@@ -1098,8 +1088,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
return TimerQueryResult::NOT_READY;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(timestamp1 >= timestamp0)
|
||||
<< "Timestamps are not monotonically increasing.";
|
||||
ASSERT_POSTCONDITION(timestamp1 >= timestamp0, "Timestamps are not monotonically increasing.");
|
||||
|
||||
// NOTE: MoltenVK currently writes system time so the following delta will always be zero.
|
||||
// However there are plans for implementing this properly. See the following GitHub ticket.
|
||||
@@ -1266,8 +1255,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const renderTargetLayerCount = rt->getLayerCount();
|
||||
|
||||
// Create the VkRenderPass or fetch it from cache.
|
||||
VulkanFboCache::RenderPassKey rpkey = {
|
||||
.initialColorLayoutMask = 0,
|
||||
@@ -1278,12 +1265,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
.discardEnd = discardEndVal,
|
||||
.samples = rt->getSamples(),
|
||||
.subpassMask = uint8_t(params.subpassMask),
|
||||
.viewCount = renderTargetLayerCount,
|
||||
};
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
const VulkanAttachment& info = rt->getColor(i);
|
||||
if (info.texture) {
|
||||
assert_invariant(info.layerCount == renderTargetLayerCount);
|
||||
rpkey.initialColorLayoutMask |= 1 << i;
|
||||
rpkey.colorFormat[i] = info.getFormat();
|
||||
if (rpkey.samples > 1 && info.texture->samples == 1) {
|
||||
@@ -1508,8 +1493,8 @@ void VulkanDriver::endRenderPass(int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::nextSubpass(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
|
||||
<< "Only two subpasses are currently supported.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.currentSubpass == 0,
|
||||
"Only two subpasses are currently supported.");
|
||||
|
||||
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
|
||||
assert_invariant(renderTarget);
|
||||
@@ -1649,36 +1634,36 @@ void VulkanDriver::resolve(
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("resolve");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +srcTexture->samples
|
||||
<< ", dst.samples=" << +dstTexture->samples;
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isDepthFormat(srcTexture->format))
|
||||
<< "can't resolve depth formats";
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isStencilFormat(srcTexture->format))
|
||||
<< "can't resolve stencil formats";
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
mBlitter.resolve(
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
|
||||
@@ -1694,20 +1679,20 @@ void VulkanDriver::blit(
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blit");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
// The Y inversion below makes it so that Vk matches GL and Metal.
|
||||
|
||||
@@ -1739,15 +1724,15 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
|
||||
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
VulkanRenderTarget* dstTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(dst);
|
||||
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
|
||||
@@ -1809,8 +1794,6 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
|
||||
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
|
||||
.rasterizationSamples = rt->getSamples(),
|
||||
.depthClamp = rasterState.depthClamp,
|
||||
.reserved = 0,
|
||||
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
|
||||
.colorBlendOp = rasterState.blendEquationRGB,
|
||||
.alphaBlendOp = rasterState.blendEquationAlpha,
|
||||
@@ -1867,9 +1850,9 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// matching characteristics. (e.g. if the missing texture is a 3D texture)
|
||||
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) && FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
FVK_LOGW << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
|
||||
FVK_LOGW << " in material '" << program->name.c_str() << "'";
|
||||
FVK_LOGW << " at binding point " << +binding << utils::io::endl;
|
||||
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
|
||||
utils::slog.w << " in material '" << program->name.c_str() << "'";
|
||||
utils::slog.w << " at binding point " << +binding << utils::io::endl;
|
||||
#endif
|
||||
texture = mEmptyTexture;
|
||||
}
|
||||
@@ -2019,7 +2002,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
|
||||
assert_invariant(inRenderPass);
|
||||
inRenderPass = false;
|
||||
} else if (inRenderPass && OUTSIDE_COMMANDS.find(command) != OUTSIDE_COMMANDS.end()) {
|
||||
FVK_LOGE << command.data() << " issued inside a render pass." << utils::io::endl;
|
||||
utils::slog.e << command.data() << " issued inside a render pass." << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "caching/VulkanDescriptorSetManager.h"
|
||||
#include "caching/VulkanPipelineLayoutCache.h"
|
||||
|
||||
@@ -167,10 +166,10 @@ private:
|
||||
VkPipelineLayout pipelineLayout;
|
||||
};
|
||||
BoundPipeline mBoundPipeline = {};
|
||||
|
||||
RenderPassFboBundle mRenderPassFboInfo;
|
||||
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -43,7 +43,6 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
|
||||
if (k1.samples != k2.samples) return false;
|
||||
if (k1.needsResolveMask != k2.needsResolveMask) return false;
|
||||
if (k1.subpassMask != k2.subpassMask) return false;
|
||||
if (k1.viewCount != k2.viewCount) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -65,8 +64,8 @@ VulkanFboCache::VulkanFboCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VulkanFboCache::~VulkanFboCache() {
|
||||
FILAMENT_CHECK_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty())
|
||||
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
||||
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
|
||||
"Please explicitly call terminate() while the VkDevice is still alive.");
|
||||
}
|
||||
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
|
||||
@@ -96,7 +95,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
FVK_LOGD << "Creating framebuffer " << config.width << "x" << config.height << " "
|
||||
utils::slog.d << "Creating framebuffer " << config.width << "x" << config.height << " "
|
||||
<< "for render pass " << config.renderPass << ", "
|
||||
<< "samples = " << int(config.samples) << ", "
|
||||
<< "depth = " << (config.depth ? 1 : 0) << ", "
|
||||
@@ -116,7 +115,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
|
||||
mRenderPassRefCount[info.renderPass]++;
|
||||
VkFramebuffer framebuffer;
|
||||
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create framebuffer.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create framebuffer.");
|
||||
mFramebufferCache[config] = {framebuffer, mCurrentTime};
|
||||
return framebuffer;
|
||||
}
|
||||
@@ -187,25 +186,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.pDependencies = dependencies
|
||||
};
|
||||
|
||||
VkRenderPassMultiviewCreateInfo multiviewCreateInfo = {};
|
||||
uint32_t const subpassViewMask = (1 << config.viewCount) - 1;
|
||||
// Prepare a view mask array for the maximum number of subpasses. All subpasses have all views
|
||||
// activated.
|
||||
uint32_t const viewMasks[2] = {subpassViewMask, subpassViewMask};
|
||||
if (config.viewCount > 1) {
|
||||
// Fill the multiview create info.
|
||||
multiviewCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
|
||||
multiviewCreateInfo.pNext = nullptr;
|
||||
multiviewCreateInfo.subpassCount = hasSubpasses ? 2u : 1u;
|
||||
multiviewCreateInfo.pViewMasks = viewMasks;
|
||||
multiviewCreateInfo.dependencyCount = 0;
|
||||
multiviewCreateInfo.pViewOffsets = nullptr;
|
||||
multiviewCreateInfo.correlationMaskCount = 1;
|
||||
multiviewCreateInfo.pCorrelationMasks = &subpassViewMask;
|
||||
|
||||
renderPassInfo.pNext = &multiviewCreateInfo;
|
||||
}
|
||||
|
||||
int attachmentIndex = 0;
|
||||
|
||||
// Populate the Color Attachments.
|
||||
@@ -326,11 +306,11 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
// Finally, create the VkRenderPass.
|
||||
VkRenderPass renderPass;
|
||||
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create render pass.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create render pass.");
|
||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
FVK_LOGD << "Created render pass " << renderPass << " with "
|
||||
utils::slog.d << "Created render pass " << renderPass << " with "
|
||||
<< "samples = " << int(config.samples) << ", "
|
||||
<< "depth = " << (hasDepth ? 1 : 0) << ", "
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
uint8_t samples; // 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
uint8_t subpassMask; // 1 byte
|
||||
uint8_t viewCount; // 1 byte
|
||||
uint8_t padding2; // 1 byte
|
||||
};
|
||||
struct RenderPassVal {
|
||||
VkRenderPass handle;
|
||||
|
||||
@@ -237,7 +237,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
.pCode = data,
|
||||
};
|
||||
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create shader module.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
std::string name{ builder.getName().c_str(), builder.getName().size() };
|
||||
@@ -292,8 +292,8 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
|
||||
<< ", " << modules[1] << ")" << utils::io::endl;
|
||||
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
|
||||
<< ", " << modules[1] << ")" << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
||||
assert_invariant(!mOffscreen);
|
||||
VkExtent2D const extent = swapChain.getExtent();
|
||||
mColor[0] = { .texture = swapChain.getCurrentColor() };
|
||||
mDepthStencil = { .texture = swapChain.getDepth() };
|
||||
mDepth = { .texture = swapChain.getDepth() };
|
||||
width = extent.width;
|
||||
height = extent.height;
|
||||
}
|
||||
@@ -323,17 +323,16 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
uint32_t width, uint32_t height, uint8_t samples,
|
||||
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil, VulkanStagePool& stagePool, uint8_t layerCount)
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
|
||||
: HwRenderTarget(width, height),
|
||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||
mOffscreen(true),
|
||||
mSamples(samples),
|
||||
mLayerCount(layerCount) {
|
||||
mSamples(samples) {
|
||||
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
||||
mColor[index] = color[index];
|
||||
}
|
||||
mDepthStencil = depthStencil[0];
|
||||
VulkanTexture* depthTexture = (VulkanTexture*) mDepthStencil.texture;
|
||||
mDepth = depthStencil[0];
|
||||
VulkanTexture* depthTexture = (VulkanTexture*) mDepth.texture;
|
||||
|
||||
if (samples == 1) {
|
||||
return;
|
||||
@@ -372,7 +371,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
|
||||
// There is no need for sidecar depth if the depth texture is already MSAA.
|
||||
if (depthTexture->samples > 1) {
|
||||
mMsaaDepthAttachment = mDepthStencil;
|
||||
mMsaaDepthAttachment = mDepth;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,7 +391,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
mMsaaDepthAttachment = {
|
||||
.texture = msTexture,
|
||||
.level = msLevel,
|
||||
.layer = mDepthStencil.layer,
|
||||
.layer = mDepth.layer,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -418,8 +417,8 @@ VulkanAttachment& VulkanRenderTarget::getMsaaColor(int target) {
|
||||
return mMsaaAttachments[target];
|
||||
}
|
||||
|
||||
VulkanAttachment& VulkanRenderTarget::getDepthStencil() {
|
||||
return mDepthStencil;
|
||||
VulkanAttachment& VulkanRenderTarget::getDepth() {
|
||||
return mDepth;
|
||||
}
|
||||
|
||||
VulkanAttachment& VulkanRenderTarget::getMsaaDepth() {
|
||||
|
||||
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil, VulkanStagePool& stagePool, uint8_t layerCount);
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
explicit VulkanRenderTarget();
|
||||
@@ -319,19 +319,17 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanAttachment& getMsaaDepth();
|
||||
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
|
||||
uint8_t getSamples() const { return mSamples; }
|
||||
uint8_t getLayerCount() const { return mLayerCount; }
|
||||
bool hasDepth() const { return mDepth.texture; }
|
||||
bool isSwapChain() const { return !mOffscreen; }
|
||||
void bindToSwapChain(VulkanSwapChain& surf);
|
||||
|
||||
private:
|
||||
VulkanAttachment mColor[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
VulkanAttachment mDepthStencil = {};
|
||||
VulkanAttachment mDepth = {};
|
||||
VulkanAttachment mMsaaAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
VulkanAttachment mMsaaDepthAttachment = {};
|
||||
const bool mOffscreen : 1;
|
||||
uint8_t mSamples : 7;
|
||||
uint8_t mLayerCount = 1;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject;
|
||||
|
||||
@@ -184,7 +184,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
vkRaster.cullMode = raster.cullMode;
|
||||
vkRaster.frontFace = raster.frontFace;
|
||||
vkRaster.depthClampEnable = raster.depthClamp;
|
||||
vkRaster.depthBiasEnable = raster.depthBiasEnable;
|
||||
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
|
||||
vkRaster.depthBiasClamp = 0.0f;
|
||||
@@ -231,15 +230,14 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
PipelineCacheEntry cacheEntry = {};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
|
||||
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"
|
||||
<< utils::io::endl;
|
||||
utils::slog.d << "vkCreateGraphicsPipelines with shaders = ("
|
||||
<< shaderStages[0].module << ", " << shaderStages[1].module << ")" << utils::io::endl;
|
||||
#endif
|
||||
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
|
||||
VKALLOC, &cacheEntry.handle);
|
||||
assert_invariant(error == VK_SUCCESS);
|
||||
if (error != VK_SUCCESS) {
|
||||
FVK_LOGE << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
|
||||
utils::slog.e << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -254,7 +252,7 @@ void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
|
||||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
|
||||
FVK_LOGE << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
|
||||
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -90,9 +90,7 @@ public:
|
||||
VkBlendFactor srcAlphaBlendFactor : 5;
|
||||
VkBlendFactor dstAlphaBlendFactor : 5;
|
||||
VkColorComponentFlags colorWriteMask : 4;
|
||||
uint8_t rasterizationSamples : 4;// offset = 4 bytes
|
||||
uint8_t depthClamp : 1;
|
||||
uint8_t reserved : 3;
|
||||
uint8_t rasterizationSamples; // offset = 4 bytes
|
||||
uint8_t colorTargetCount; // offset = 5 bytes
|
||||
BlendEquation colorBlendOp : 4; // offset = 6 bytes
|
||||
BlendEquation alphaBlendOp : 4;
|
||||
|
||||
@@ -71,8 +71,8 @@ void TaskHandler::shutdown() {
|
||||
}
|
||||
mHasTaskCondition.notify_one();
|
||||
mThread.join();
|
||||
FILAMENT_CHECK_POSTCONDITION(mTaskQueue.empty())
|
||||
<< "ReadPixels handler has tasks in the queue after shutdown";
|
||||
ASSERT_POSTCONDITION(mTaskQueue.empty(),
|
||||
"ReadPixels handler has tasks in the queue after shutdown");
|
||||
}
|
||||
|
||||
void TaskHandler::loop() {
|
||||
@@ -167,9 +167,9 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_READ_PIXELS)
|
||||
FVK_LOGD << "readPixels created image=" << stagingImage
|
||||
<< " to copy from image=" << srcTexture->getVkImage()
|
||||
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
|
||||
utils::slog.d << "readPixels created image=" << stagingImage
|
||||
<< " to copy from image=" << srcTexture->getVkImage()
|
||||
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
|
||||
#endif
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
@@ -185,13 +185,13 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
if (memoryTypeIndex >= VK_MAX_MEMORY_TYPES) {
|
||||
memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
FVK_LOGW
|
||||
utils::slog.w
|
||||
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available"
|
||||
<< utils::io::endl;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanReadPixels: unable to find a memory type that meets requirements.";
|
||||
ASSERT_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES,
|
||||
"VulkanReadPixels: unable to find a memory type that meets requirements.");
|
||||
|
||||
VkMemoryAllocateInfo const allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
@@ -303,7 +303,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
|
||||
// Fence hasn't been reached. Try waiting again.
|
||||
if (status != VK_SUCCESS) {
|
||||
FVK_LOGE << "Failed to wait for readPixels fence" << utils::io::endl;
|
||||
utils::slog.e << "Failed to wait for readPixels fence" << utils::io::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
getComponentCount(srcFormat), srcPixels,
|
||||
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
|
||||
static_cast<int>(height), swizzle)) {
|
||||
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
}
|
||||
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
|
||||
@@ -111,11 +111,11 @@ private:
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
public:
|
||||
void print() {
|
||||
FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl;
|
||||
utils::slog.d << "Resource Allocator state (debug only)" << utils::io::endl;
|
||||
for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) {
|
||||
FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
|
||||
utils::slog.d << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
|
||||
}
|
||||
FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
|
||||
utils::slog.d << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
|
||||
}
|
||||
private:
|
||||
utils::FixedCapacityVector<size_t> mDebugOnlyResourceCount;
|
||||
|
||||
@@ -122,7 +122,7 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
};
|
||||
VkSampler sampler;
|
||||
VkResult error = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create sampler.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create sampler.");
|
||||
mCache.insert({params, sampler});
|
||||
return sampler;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
if (result != VK_SUCCESS) {
|
||||
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
|
||||
utils::slog.e << "Allocation error: " << result << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -35,12 +35,27 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
mStagePool(stagePool),
|
||||
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
|
||||
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
|
||||
mTransitionSwapChainImageLayoutForPresent(
|
||||
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
|
||||
mCurrentImageReadyIndex(0),
|
||||
mAcquired(false),
|
||||
mIsFirstRenderPass(true) {
|
||||
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
||||
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
|
||||
ASSERT_POSTCONDITION(swapChain, "Unable to create swapchain");
|
||||
|
||||
VkSemaphoreCreateInfo const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
};
|
||||
|
||||
// No need to wait on this semaphore before drawing when in Headless mode.
|
||||
if (mHeadless) {
|
||||
// Set all sempahores to VK_NULL_HANDLE
|
||||
memset(mImageReady, 0, sizeof(mImageReady[0]) * IMAGE_READY_SEMAPHORE_COUNT);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
|
||||
VkResult result =
|
||||
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, mImageReady + i);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -52,6 +67,11 @@ VulkanSwapChain::~VulkanSwapChain() {
|
||||
mCommands->wait();
|
||||
|
||||
mPlatform->destroy(swapChain);
|
||||
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
|
||||
if (mImageReady[i] != VK_NULL_HANDLE) {
|
||||
vkDestroySemaphore(mPlatform->getDevice(), mImageReady[i], VKALLOC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanSwapChain::update() {
|
||||
@@ -74,7 +94,7 @@ void VulkanSwapChain::update() {
|
||||
}
|
||||
|
||||
void VulkanSwapChain::present() {
|
||||
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
||||
if (!mHeadless) {
|
||||
VkCommandBuffer const cmdbuf = mCommands->get().buffer();
|
||||
VkImageSubresourceRange const subresources{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
@@ -85,21 +105,16 @@ void VulkanSwapChain::present() {
|
||||
};
|
||||
mColors[mCurrentSwapIndex]->transitionLayout(cmdbuf, subresources, VulkanLayout::PRESENT);
|
||||
}
|
||||
|
||||
mCommands->flush();
|
||||
|
||||
// call the image ready wait function
|
||||
if (mExplicitImageReadyWait != nullptr) {
|
||||
mExplicitImageReadyWait(swapChain);
|
||||
}
|
||||
|
||||
// We only present if it is not headless. No-op for headless.
|
||||
// We only present if it is not headless. No-op for headless (but note that we still need the
|
||||
// flush() in the above line).
|
||||
if (!mHeadless) {
|
||||
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
||||
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
|
||||
result == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
<< "Cannot present in swapchain.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
|
||||
result == VK_ERROR_OUT_OF_DATE_KHR,
|
||||
"Cannot present in swapchain.");
|
||||
}
|
||||
|
||||
// We presented the last acquired buffer.
|
||||
@@ -123,14 +138,13 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
update();
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageSyncData imageSyncData;
|
||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain.";
|
||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(imageSyncData.imageReadySemaphore);
|
||||
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
|
||||
const VkSemaphore imageReady = mImageReady[mCurrentImageReadyIndex];
|
||||
VkResult const result = mPlatform->acquire(swapChain, imageReady, &mCurrentSwapIndex);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR,
|
||||
"Cannot acquire in swapchain.");
|
||||
if (imageReady != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(imageReady);
|
||||
}
|
||||
mAcquired = true;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,7 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
|
||||
void acquire(bool& reized);
|
||||
|
||||
inline VulkanTexture* getCurrentColor() const noexcept {
|
||||
uint32_t const imageIndex = mCurrentSwapIndex;
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX);
|
||||
return mColors[imageIndex].get();
|
||||
return mColors[mCurrentSwapIndex].get();
|
||||
}
|
||||
|
||||
inline VulkanTexture* getDepth() const noexcept {
|
||||
@@ -83,15 +80,15 @@ private:
|
||||
VulkanStagePool& mStagePool;
|
||||
bool const mHeadless;
|
||||
bool const mFlushAndWaitOnResize;
|
||||
bool const mTransitionSwapChainImageLayoutForPresent;
|
||||
|
||||
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
|
||||
// transitions, which are useful here.
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
|
||||
std::unique_ptr<VulkanTexture> mDepth;
|
||||
VkExtent2D mExtent;
|
||||
VkSemaphore mImageReady[IMAGE_READY_SEMAPHORE_COUNT];
|
||||
uint32_t mCurrentImageReadyIndex;
|
||||
uint32_t mCurrentSwapIndex;
|
||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||
bool mAcquired;
|
||||
bool mIsFirstRenderPass;
|
||||
};
|
||||
|
||||
@@ -113,7 +113,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, mVkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mVkFormat << " is not "
|
||||
utils::slog.w << "Texture usage is SAMPLEABLE but format " << mVkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
@@ -163,7 +163,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
|
||||
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
|
||||
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
|
||||
FVK_LOGD << "vkCreateImage: "
|
||||
utils::slog.d << "vkCreateImage: "
|
||||
<< "image = " << mTextureImage << ", "
|
||||
<< "result = " << error << ", "
|
||||
<< "handle = " << utils::io::hex << mTextureImage << utils::io::dec << ", "
|
||||
@@ -177,7 +177,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << mVkFormat << utils::io::endl;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create image.");
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkMemoryRequirements memReqs = {};
|
||||
@@ -186,8 +186,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanTexture: unable to find a memory type that meets requirements.";
|
||||
ASSERT_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES,
|
||||
"VulkanTexture: unable to find a memory type that meets requirements.");
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
@@ -195,9 +195,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
error = vkAllocateMemory(mDevice, &allocInfo, nullptr, &mTextureImageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to allocate image memory.");
|
||||
error = vkBindImageMemory(mDevice, mTextureImage, mTextureImageMemory, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to bind image.");
|
||||
|
||||
uint32_t layerCount = 0;
|
||||
if (target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
@@ -387,15 +387,12 @@ void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel)
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
// Attachments should only have one mipmap level and one layer.
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
return getImageView(range, type, mSwizzle);
|
||||
}
|
||||
@@ -453,7 +450,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
|
||||
FVK_LOGD << "transition texture=" << mTextureImage
|
||||
utils::slog.d << "transition texture=" << mTextureImage
|
||||
<< " (" << range.baseArrayLayer
|
||||
<< "," << range.baseMipLevel << ")"
|
||||
<< " count=(" << range.layerCount
|
||||
@@ -542,7 +539,7 @@ void VulkanTexture::print() const {
|
||||
layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) &&
|
||||
level >= mPrimaryViewRange.baseMipLevel &&
|
||||
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
|
||||
FVK_LOGD << "[" << mTextureImage << "]: (" << layer << "," << level
|
||||
utils::slog.d << "[" << mTextureImage << "]: (" << layer << "," << level
|
||||
<< ")=" << getLayout(layer, level)
|
||||
<< " primary=" << primary
|
||||
<< utils::io::endl;
|
||||
@@ -551,7 +548,7 @@ void VulkanTexture::print() const {
|
||||
|
||||
for (auto view: mCachedImageViews) {
|
||||
auto& range = view.first.range;
|
||||
FVK_LOGD << "[" << mTextureImage << ", imageView=" << view.second << "]=>"
|
||||
utils::slog.d << "[" << mTextureImage << ", imageView=" << view.second << "]=>"
|
||||
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
|
||||
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
|
||||
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user