Compare commits

..
Author SHA1 Message Date
Powei Feng 3e32e6cf2a backend-test: centralize read pixel logic 2024-05-16 15:08:56 -07:00
2840 changed files with 24674 additions and 50080 deletions
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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
+5 -5
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+5 -40
View File
@@ -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
```
+5 -7
View File
@@ -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)
@@ -746,7 +745,6 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/unordered_dense)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry)
+2 -3
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.53.2'
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.53.2'
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
-24
View File
@@ -7,30 +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.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
+6 -6
View File
@@ -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}"
}
+1
View File
@@ -128,6 +128,7 @@ target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../libs/utils/include)
# Force a relink when the version script is changed:
@@ -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
@@ -187,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);
}
@@ -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
@@ -1289,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) {
@@ -1384,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,19 +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);
}
/**
* 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
@@ -715,7 +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 boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
private static native void nEndFrame(long nativeRenderer);
private static native void nRender(long nativeRenderer, long nativeView);
@@ -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);
+1
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.53.2
VERSION_NAME=1.51.8
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -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
+2 -23
View File
@@ -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 ""
+1 -2
View File
@@ -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
View File
@@ -1 +1 @@
27.0.11718014
26.1.10909125
-2
View File
@@ -313,7 +313,6 @@ endif()
target_link_libraries(${TARGET} PUBLIC math)
target_link_libraries(${TARGET} PUBLIC utils)
target_link_libraries(${TARGET} PRIVATE unordered_dense)
# Android, iOS, and WebGL do not use bluegl.
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WEBGL)
@@ -420,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.
/**
@@ -1251,7 +1250,13 @@ enum class Workaround : uint16_t {
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,7 +300,7 @@ 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)
@@ -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
+2 -2
View File
@@ -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;
+8 -11
View File
@@ -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);
+11 -7
View File
@@ -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;
}
@@ -106,11 +108,11 @@ void CommandBufferQueue::flush() noexcept {
mCondition.notify_one();
// circular buffer is too small, we corrupted the stream
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
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: " << used <<
" bytes, overflow: " << used - mFreeSpace << " bytes";
"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;
@@ -129,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;
@@ -149,6 +149,10 @@ 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);
}
+2 -2
View File
@@ -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();
+1 -11
View File
@@ -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;
+5 -6
View File
@@ -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;
+52 -65
View File
@@ -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;
+36 -68
View File
@@ -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
+1 -23
View File
@@ -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:
+3 -37
View File
@@ -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
-16
View File
@@ -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) {}
@@ -122,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.
@@ -142,7 +127,6 @@ struct MetalContext {
utils::FixedCircularBuffer<Handle<HwTexture>> texturesToDestroy;
MetalBufferPool* bufferPool;
MetalBumpAllocator* bumpAllocator;
MetalSwapChain* currentDrawSwapChain = nil;
MetalSwapChain* currentReadSwapChain = nil;
+1 -65
View File
@@ -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
-2
View File
@@ -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
+124 -150
View File
@@ -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);
@@ -171,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, *)) {
@@ -183,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);
@@ -205,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;
@@ -228,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
}
}
@@ -293,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];
@@ -361,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));
}
@@ -402,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)];
@@ -414,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 };
}
@@ -804,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;
}
}
@@ -885,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));
@@ -925,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));
@@ -942,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);
}
@@ -965,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));
@@ -1112,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) {}
@@ -1264,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) {
@@ -1323,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.
@@ -1338,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
@@ -1416,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)
@@ -1465,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;
@@ -1518,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 ||
@@ -1541,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;
@@ -1656,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);
@@ -1829,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] = {};
@@ -1866,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 };
@@ -1883,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;
@@ -1916,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);
@@ -2017,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);
}
+3 -4
View File
@@ -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;
+34 -60
View File
@@ -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);
+2 -4
View File
@@ -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
+3 -9
View File
@@ -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;
}
+1 -1
View File
@@ -182,7 +182,7 @@ bool NoopDriver::isProtectedContentSupported() {
return false;
}
bool NoopDriver::isStereoSupported() {
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
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);
}
@@ -511,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{{
+38 -39
View File
@@ -304,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;
@@ -347,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) {
@@ -362,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;
}
@@ -1498,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
@@ -1518,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
@@ -2053,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;
}
}
@@ -2336,9 +2333,9 @@ 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);
SamplerParams params = pSamplers[i].s;
@@ -2394,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));
}
@@ -3609,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, {},
@@ -3769,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
+1 -6
View File
@@ -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)) {
+2 -13
View File
@@ -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
@@ -359,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;
@@ -572,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(),
@@ -668,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
@@ -708,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,
@@ -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, &region);
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;
@@ -86,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();
}
@@ -100,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);
}
@@ -134,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};
}
+1 -11
View File
@@ -120,32 +120,22 @@ public:
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.imageCubeArray == 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;
+66 -78
View File
@@ -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,
@@ -660,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,
@@ -672,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);
}
@@ -909,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;
}
}
@@ -986,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;
}
@@ -1099,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.
@@ -1505,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);
@@ -1646,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 },
@@ -1691,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.
@@ -1736,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);
@@ -1862,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;
}
@@ -2014,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
}
+1 -2
View File
@@ -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
@@ -64,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 {
@@ -95,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) << ", "
@@ -115,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;
}
@@ -306,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
@@ -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
}
@@ -53,7 +53,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipelin
// If a cached object exists, re-use it, otherwise create a new one.
if (PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements);
pipelineIter != mPipelines.end()) {
auto& pipeline = pipelineIter->second;
auto& pipeline = pipelineIter.value();
pipeline.lastUsed = mCurrentTime;
return &pipeline;
}
@@ -230,19 +230,18 @@ 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;
}
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first->second;
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
@@ -253,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
}
@@ -310,7 +309,7 @@ void VulkanPipelineCache::gc() noexcept {
// Any pipeline older than FVK_MAX_COMMAND_BUFFERS can be safely destroyed.
using ConstPipeIterator = decltype(mPipelines)::const_iterator;
for (ConstPipeIterator iter = mPipelines.begin(); iter != mPipelines.end();) {
const PipelineCacheEntry& cacheEntry = iter->second;
const PipelineCacheEntry& cacheEntry = iter.value();
if (cacheEntry.lastUsed + FVK_MAX_PIPELINE_AGE < mCurrentTime) {
vkDestroyPipeline(mDevice, iter->second.handle, VKALLOC);
iter = mPipelines.erase(iter);
@@ -33,11 +33,11 @@
#include <utils/compiler.h>
#include <utils/Hash.h>
#include <ankerl/unordered_dense.h>
#include <list>
#include <tsl/robin_map.h>
#include <type_traits>
#include <vector>
#include <unordered_map>
namespace filament::backend {
@@ -238,10 +238,7 @@ private:
// CACHE CONTAINERS
// ----------------
// using PipelineMap = tsl::robin_map<PipelineKey, PipelineCacheEntry,
// PipelineHashFn, PipelineEqual>;
using PipelineMap = ankerl::unordered_dense::map<PipelineKey, PipelineCacheEntry,
using PipelineMap = tsl::robin_map<PipelineKey, PipelineCacheEntry,
PipelineHashFn, PipelineEqual>;
private:
@@ -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
+36 -22
View File
@@ -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;
};
+10 -10
View File
@@ -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) {
@@ -450,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
@@ -539,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;
@@ -548,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
@@ -51,7 +51,7 @@ VkFormat getVkFormat(ElementType type, bool normalized, bool integer) {
case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SNORM;
case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UNORM;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
ASSERT_POSTCONDITION(false, "Normalized format does not exist.");
return VK_FORMAT_UNDEFINED;
}
}
+7 -7
View File
@@ -56,13 +56,13 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
// considered, but because the "variadic" part of the vk methods (i.e. the inputs) are before the
// non-variadic parts, this breaks the template type matching logic. Hence, we use a macro approach
// here.
#define EXPAND_ENUM(...) \
uint32_t size = 0; \
VkResult result = func(__VA_ARGS__, nullptr); \
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "enumerate size error"; \
utils::FixedCapacityVector<OutType> ret(size); \
result = func(__VA_ARGS__, ret.data()); \
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "enumerate error"; \
#define EXPAND_ENUM(...)\
uint32_t size = 0;\
VkResult result = func(__VA_ARGS__, nullptr);\
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate size error");\
utils::FixedCapacityVector<OutType> ret(size);\
result = func(__VA_ARGS__, ret.data());\
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate error");\
return std::move(ret);
#define EXPAND_ENUM_NO_ARGS() EXPAND_ENUM(&size)
@@ -170,9 +170,9 @@ public:
};
VkDescriptorSet vkSet;
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
<< " capacity=" << mCapacity << " count=" << "%s";
ASSERT_POSTCONDITION(result == VK_SUCCESS,
"Failed to allocate descriptor set code=%d size=%d capacity=%d count=%s", result,
mSize, mCapacity);
mSize++;
return createSet(layout->bitmask, vkSet);
}
@@ -33,11 +33,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
// build the push constant layout key
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
auto const& pushConstantRanges = program->getPushConstantRanges();
auto const& pushCostantRanges = program->getPushConstantRanges();
if (pushConstantRangeCount > 0) {
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
auto const& range = pushConstantRanges[i];
auto const& range = pushCostantRanges[i];
auto& pushConstant = key.pushConstant[i];
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
@@ -52,8 +52,9 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
}
}
if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
PipelineLayoutCacheEntry& entry = iter->second;
auto iter = mPipelineLayouts.find(key);
if (iter != mPipelineLayouts.end()) {
PipelineLayoutCacheEntry& entry = mPipelineLayouts[key];
entry.lastUsed = mTimestamp++;
return entry.handle;
}
@@ -64,7 +65,7 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = key.descSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges,
.pPushConstantRanges = pushCostantRanges,
};
VkPipelineLayout layout;
@@ -23,7 +23,6 @@
#include <utils/Hash.h>
#include <tsl/robin_map.h>
#include <ankerl/unordered_dense.h>
namespace filament::backend {
@@ -74,10 +73,7 @@ private:
}
};
// using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
// PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
using PipelineLayoutMap = ankerl::unordered_dense::map<PipelineLayoutKey, PipelineLayoutCacheEntry,
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
VkDevice mDevice;

Some files were not shown because too many files have changed in this diff Show More