Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 5aded33477 Update spirv-tools to v2024.2 (dd4b663) 2024-06-27 10:23:14 -07:00
873 changed files with 53491 additions and 44882 deletions
@@ -1,17 +0,0 @@
name: 'Android Continuous'
inputs:
build-abi:
description: 'The target platform ABI'
required: true
default: 'armeabi-v7a'
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous ${{ inputs.build-abi }}
shell: bash
+22 -25
View File
@@ -8,35 +8,32 @@ on:
- rc/**
jobs:
build-android-armv7:
name: build-android-armv7
build-android:
name: build-android
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
- uses: actions/setup-java@v3
with:
build-abi: armeabi-v7a
build-android-armv8a:
name: build-android-armv8a
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v1.0.0
with:
build-abi: arm64-v8a
build-android-x86_64:
name: build-android-x86_64
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
name: filament-android
path: out/filament-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
build-abi: x86_64
name: filamat-android-full
path: out/filamat-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release
path: out/gltfio-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-utils-android-release
path: out/filament-utils-android-release.aar
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-ios
path: out/filament-release-ios.tgz
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-linux
path: out/filament-release-linux.tgz
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-mac
path: out/filament-release-darwin.tgz
+6 -8
View File
@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-16core]
os: [macos-14, ubuntu-22.04]
steps:
- uses: actions/checkout@v4.1.6
@@ -29,7 +29,7 @@ jobs:
build-windows:
name: build-windows
runs-on: windows-2019-32core
runs-on: windows-2019
steps:
- uses: actions/checkout@v4.1.6
@@ -40,7 +40,7 @@ jobs:
build-android:
name: build-android
runs-on: ubuntu-22.04-16core
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
@@ -49,14 +49,12 @@ jobs:
distribution: 'temurin'
java-version: '17'
- name: Run build script
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
run: |
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
cd build/android && printf "y" | ./build.sh presubmit
build-ios:
name: build-iOS
runs-on: macos-14-xlarge
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
@@ -69,7 +67,7 @@ jobs:
build-web:
name: build-web
runs-on: ubuntu-22.04-16core
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/android && printf "y" | ./build.sh release armeabi-v7a,arm64-v8a,x86,x86_64
cd build/android && printf "y" | ./build.sh release
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-web
path: out/filament-release-web.tgz
+2 -2
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-windows:
name: build-windows
runs-on: windows-2019-32core
runs-on: windows-2019
steps:
- uses: actions/checkout@v4.1.6
@@ -18,7 +18,7 @@ jobs:
run: |
build\windows\build-github.bat continuous
shell: cmd
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-windows
path: out/filament-windows.tgz
+12 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.54.5'
implementation 'com.google.android.filament:filament-android:1.53.1'
}
```
@@ -51,9 +51,19 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.54.5'
pod 'Filament', '~> 1.53.1'
```
### Snapshots
If you prefer to live on the edge, you can download a continuous build by following the following
steps:
1. Find the [commit](https://github.com/google/filament/commits/main) you're interested in.
2. Click the green check mark under the commit message.
3. Click on the _Details_ link for the platform you're interested in.
4. On the top left click _Summary_, then in the _Artifacts_ section choose the desired artifact.
## Documentation
- [Filament](https://google.github.io/filament/Filament.html), an in-depth explanation of
-34
View File
@@ -7,40 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.54.5
## v1.54.4
- Add support for multi-layered render target with array textures.
## v1.54.3
## v1.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
## v1.54.1
## v1.54.0
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
- Fix a crash when compiling shaders on IMG devices
## v1.53.5
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
certain GLES 3.0 drivers.
## v1.53.4
## v1.53.3
- Add drag and drop support for IBL files for desktop gltf_viewer.
## v1.53.2
@@ -253,10 +253,16 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIE
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferOffsetAt(JNIEnv*, jclass,
jlong nativeBuilder, int level, int primitiveIndex, int offset) {
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;
builder->morphing(level, primitiveIndex, offset);
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
} else {
builder->morphing(level, primitiveIndex, offset, count);
}
}
extern "C" JNIEXPORT void JNICALL
@@ -328,12 +334,18 @@ 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);
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
} else {
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset, (size_t) count);
}
}
extern "C" JNIEXPORT jint JNICALL
@@ -28,14 +28,6 @@
using namespace filament;
using namespace backend;
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *, jclass, jlong nativeRenderer,
jlong vsyncSteadyClockTimeNano) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *, jclass, jlong nativeRenderer,
jlong nativeSwapChain, jlong frameTimeNanos) {
@@ -531,12 +531,3 @@ Java_com_google_android_filament_View_nGetFogEntity(JNIEnv *env, jclass clazz,
View *view = (View *) nativeView;
return (jint)view->getFogEntity().getId();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass clazz,
jlong nativeView, jlong nativeEngine) {
View *view = (View *) nativeView;
Engine *engine = (Engine *) nativeEngine;
view->clearFrameHistory(*engine);
}
@@ -425,12 +425,9 @@ public class Engine {
public long resourceAllocatorCacheSizeMB = 64;
/*
* This value determines how many frames texture entries are kept for in the cache. This
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
* Typically only one texture is evicted per frame.
* The default is 1.
* This value determines for how many frames are texture entries kept in the cache.
*/
public long resourceAllocatorCacheMaxAge = 1;
public long resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
@@ -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) {
@@ -569,13 +569,40 @@ 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);
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, 0, offset, count);
return this;
}
/** @deprecated */
@Deprecated
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@NonNull MorphTargetBuffer morphTargetBuffer,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex,
morphTargetBuffer.getNativeObject(), offset, count);
return this;
}
/** @deprecated */
@Deprecated
@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 +705,34 @@ 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);
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex, 0, offset, count);
}
/** @deprecated */
@Deprecated
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@NonNull MorphTargetBuffer morphTargetBuffer,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex,
morphTargetBuffer.getNativeObject(), offset, count);
}
/** @deprecated */
@Deprecated
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());
}
/**
@@ -983,7 +1033,7 @@ public class RenderableManager {
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 +1043,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);
@@ -297,20 +297,6 @@ public class Renderer {
nSetVsyncTime(getNativeObject(), steadyClockTimeNano);
}
/**
* Call skipFrame when momentarily skipping frames, for instance if the content of the
* scene doesn't change.
*
* @param vsyncSteadyClockTimeNano The time in nanoseconds when the frame started being rendered,
* in the {@link System#nanoTime()} timebase. Divide this value by 1000000 to
* convert it to the {@link android.os.SystemClock#uptimeMillis()}
* time base. This typically comes from
* {@link android.view.Choreographer.FrameCallback}.
*/
public void skipFrame(long vsyncSteadyClockTimeNano) {
nSkipFrame(getNativeObject(), vsyncSteadyClockTimeNano);
}
/**
* Sets up a frame for this <code>Renderer</code>.
* <p><code>beginFrame</code> manages frame pacing, and returns whether or not a frame should be
@@ -730,7 +716,6 @@ public class Renderer {
private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos);
private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano);
private static native void nSkipFrame(long nativeObject, long vsyncSteadyClockTimeNano);
private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
private static native void nEndFrame(long nativeRenderer);
private static native void nRender(long nativeRenderer, long nativeView);
@@ -1233,18 +1233,6 @@ public class View {
return nGetFogEntity(getNativeObject());
}
/**
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
* keeps a history of previous frame renders associated with the Renderer the view was last
* used with. When switching Renderer, it may be necessary to clear that history by calling
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
* being very different.
*/
public void clearFrameHistory(Engine engine) {
nClearFrameHistory(getNativeObject(), engine.getNativeObject());
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed View");
@@ -1306,7 +1294,7 @@ public class View {
private static native void nSetMaterialGlobal(long nativeView, int index, float x, float y, float z, float w);
private static native void nGetMaterialGlobal(long nativeView, int index, float[] out);
private static native int nGetFogEntity(long nativeView);
private static native void nClearFrameHistory(long nativeView, long nativeEngine);
/**
* List of available ambient occlusion techniques.
@@ -125,11 +125,6 @@ extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBu
builder->groundPlane(a, b, c, d);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderPanning(JNIEnv*, jclass, jlong nativeBuilder, jboolean enabled) {
Builder* builder = (Builder*) nativeBuilder;
builder->panning(enabled);
}
extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) {
Builder* builder = (Builder*) nativeBuilder;
return (jlong) builder->build((Mode) mode);
@@ -274,17 +274,6 @@ public class Manipulator {
return this;
}
/**
* Sets whether panning is enabled in the manipulator.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder panning(Boolean enabled) {
nBuilderPanning(mNativeBuilder, enabled);
return this;
}
/**
* Creates and returns the <code>Manipulator</code> object.
*
@@ -494,7 +483,6 @@ public class Manipulator {
private static native void nBuilderFlightPanSpeed(long nativeBuilder, float x, float y);
private static native void nBuilderFlightMoveDamping(long nativeBuilder, float damping);
private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d);
private static native void nBuilderPanning(long nativeBuilder, Boolean enabled);
private static native long nBuilderBuild(long nativeBuilder, int mode);
private static native void nDestroyManipulator(long nativeManip);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.54.5
VERSION_NAME=1.53.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+7 -15
View File
@@ -60,7 +60,13 @@ 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"
fi
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
ANDROID_ABIS=
if [[ "$TARGET" == "presubmit" ]]; then
ANDROID_ABIS="-q arm64-v8a"
fi
# Build the Android sample-gltf-viewer APK during release.
BUILD_SAMPLES=
@@ -68,19 +74,5 @@ if [[ "$TARGET" == "release" ]]; then
BUILD_SAMPLES="-k sample-gltf-viewer"
fi
function build_android() {
local ABI=$1
# Do the following in two steps so that we do not run out of space
if [[ -n "${BUILD_DEBUG}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_DEBUG}
rm -rf out/cmake-android-debug-*
fi
if [[ -n "${BUILD_RELEASE}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_RELEASE}
rm -rf out/cmake-android-release-*
fi
}
pushd `dirname $0`/../.. > /dev/null
build_android $2
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android $ANDROID_ABIS -c $BUILD_SAMPLES $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
+5 -1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
if [ `uname` == "Linux" ];then
source `dirname $0`/../linux/ci-common.sh
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip
unzip -q ninja-linux.zip
elif [ `uname` == "Darwin" ];then
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
@@ -12,6 +13,9 @@ fi
chmod +x ninja
export PATH="$PWD:$PATH"
# FIXME: kokoro machines have node and npm but currently they are symlinked to non-existent files
# npm install -g typescript
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
+2 -11
View File
@@ -1308,12 +1308,7 @@ Description
declare a variable called `eyeDirection` you can access it in the fragment shader using
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
interpolant is `highp` in *both* the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
interpolant is of type `float4` (`vec4`) in the shaders.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
@@ -1325,11 +1320,7 @@ material {
}
],
variables : [
eyeDirection,
{
name : eyeColor,
precision : medium
}
eyeDirection
],
vertexDomain : device,
depthWrite : false,
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -61,7 +61,6 @@ set(SRCS
src/Engine.cpp
src/Exposure.cpp
src/Fence.cpp
src/FilamentBuilder.cpp
src/FrameInfo.cpp
src/FrameSkipper.cpp
src/Froxelizer.cpp
@@ -1058,7 +1058,7 @@ struct RasterState {
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
bool depthClamp : 1; // 32
uint8_t padding : 1; // 32
};
uint32_t u = 0;
};
@@ -1244,7 +1244,7 @@ enum class Workaround : uint16_t {
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
METAL_STATIC_TEXTURE_TARGET_ERROR,
A8X_STATIC_TEXTURE_TARGET_ERROR,
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
// Multiple workarounds needed for PowerVR GPUs
@@ -162,10 +162,6 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
@@ -309,7 +305,6 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
@@ -444,10 +439,12 @@ DECL_DRIVER_API_N(setPushConstant,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string)
const char*, string,
uint32_t, len = 0)
DECL_DRIVER_API_N(pushGroupMarker,
const char*, string)
const char*, string,
uint32_t, len = 0)
DECL_DRIVER_API_0(popGroupMarker)
@@ -20,12 +20,11 @@
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
@@ -40,7 +39,7 @@
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
namespace filament::backend {
@@ -174,10 +173,8 @@ 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;
// getHandleTag() is only called if the check fails.
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
<< "use-after-free of Handle with id=" << handle.getId()
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
"use-after-free of Handle with id=" << handle.getId();
}
}
@@ -204,29 +201,6 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the age to get the debug tag
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
}
}
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
if (!isPoolHandle(id)) {
return "(no tag)";
}
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
}
private:
template<typename D>
@@ -344,24 +318,12 @@ private:
}
}
// number if bits allotted to the handle's age (currently 4 max)
static constexpr uint32_t HANDLE_AGE_BIT_COUNT = 4;
// number if bits allotted to the handle's debug tag (HANDLE_AGE_BIT_COUNT max)
static constexpr uint32_t HANDLE_DEBUG_TAG_BIT_COUNT = 2;
// bit shift for both the age and debug tag
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// mask for the heap (vs pool) flag
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u;
// mask for the age
static constexpr uint32_t HANDLE_AGE_MASK =
((1 << HANDLE_AGE_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the debug tag
static constexpr uint32_t HANDLE_DEBUG_TAG_MASK =
((1 << HANDLE_DEBUG_TAG_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the index
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu;
static_assert(HANDLE_DEBUG_TAG_BIT_COUNT <= HANDLE_AGE_BIT_COUNT);
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
@@ -376,7 +338,7 @@ private:
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_AGE_MASK;
uint32_t const tag = id & HANDLE_TAG_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
}
@@ -391,7 +353,7 @@ private:
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_AGE_MASK;
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
return id;
}
@@ -401,7 +363,6 @@ private:
// Below is only used when running out of space in the HandleArena
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
+7 -10
View File
@@ -24,7 +24,7 @@
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__)
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
# include <sys/mman.h>
# include <unistd.h>
# define HAS_MMAP 1
@@ -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) <<
FILAMENT_CHECK_POSTCONDITION(data) <<
"couldn't allocate " << (size * 2 / 1024) <<
" KiB of virtual address space for the command buffer";
slog.w << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)" << io::endl;
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);
+5 -8
View File
@@ -101,8 +101,9 @@ void CommandBufferQueue::flush() noexcept {
size_t const used = std::distance(
static_cast<char const*>(begin), static_cast<char const*>(end));
std::unique_lock<utils::Mutex> lock(mLock);
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
// circular buffer is too small, we corrupted the stream
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
@@ -111,13 +112,11 @@ void CommandBufferQueue::flush() noexcept {
"Space used at this time: " << used <<
" bytes, overflow: " << used - mFreeSpace << " bytes";
mFreeSpace -= used;
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
// wait until there is enough space in the buffer
mFreeSpace -= used;
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
#ifndef NDEBUG
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
slog.d << "CommandStream used too much space (will block): "
@@ -154,10 +153,8 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
}
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
size_t const used = std::distance(
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
std::lock_guard<utils::Mutex> const lock(mLock);
mFreeSpace += used;
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
mCondition.notify_one();
}
+2 -8
View File
@@ -20,16 +20,11 @@
#include <utils/CallStack.h>
#endif
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Profiler.h>
#include <utils/Systrace.h>
#include <cstddef>
#include <functional>
#include <string>
#include <utility>
#ifdef __ANDROID__
#include <sys/system_properties.h>
@@ -79,8 +74,8 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
}
void CommandStream::execute(void* buffer) {
// NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also
// uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope.
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
Profiler profiler;
@@ -105,7 +100,6 @@ void CommandStream::execute(void* buffer) {
// we want to remove all this when tracing is completely disabled
profiler.stop();
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
-3
View File
@@ -80,9 +80,6 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
// few tags are set.
mDebugTags.reserve(512);
}
template <size_t P0, size_t P1, size_t P2>
-2
View File
@@ -160,8 +160,6 @@ public:
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
+2 -2
View File
@@ -53,8 +53,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
}
// mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful().
FILAMENT_CHECK_POSTCONDITION(mBuffer)
<< "Could not allocate Metal buffer of size " << size << ".";
}
MetalBuffer::~MetalBuffer() {
@@ -145,7 +145,6 @@ std::pair<id<MTLBuffer>, size_t> MetalBumpAllocator::allocateStagingArea(size_t
// We're finished with the current allocation.
mCurrentUploadBuffer = { [mDevice newBufferWithLength:mCapacity options:MTLStorageModeShared],
TrackedMetalBuffer::Type::STAGING };
mHead = size;
return { mCurrentUploadBuffer.get(), 0 };
}
+1 -2
View File
@@ -93,7 +93,7 @@ struct MetalContext {
} highestSupportedGpuFamily;
struct {
bool staticTextureTargetError;
bool a8xStaticTextureTargetError;
} bugs;
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
@@ -112,7 +112,6 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+2 -3
View File
@@ -57,6 +57,7 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
@@ -72,12 +73,10 @@ private:
/*
* Tasks run regularly on the driver thread.
* Not thread-safe; tasks are run from the driver thead and must be enqueued from the driver
* thread.
*/
void runAtNextTick(const std::function<void()>& fn) noexcept;
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
+16 -53
View File
@@ -53,14 +53,14 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
// MetalRenderPrimitive : 24 many
// MetalVertexBuffer : 32 moderate
// -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalIndexBuffer : 56 moderate
// MetalBufferObject : 64 many
// -- less than or equal 64 bytes
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 152 moderate
// MetalTexture : 152 moderate
// MetalSwapChain : 208 few
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
@@ -162,10 +162,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
}
mContext->bugs.staticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"] ||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
[mContext->device.name containsString:@"Apple A7 GPU"];
mContext->bugs.a8xStaticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"];
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
mContext->pipelineStateCache.setDevice(mContext->device);
@@ -320,40 +318,17 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
auto elementSize = (uint8_t) getElementTypeSize(elementType);
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -857,10 +832,6 @@ bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isDepthClampSupported() {
return true;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -869,8 +840,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return true;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.staticTextureTargetError;
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.a8xStaticTextureTargetError;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
default:
@@ -1302,11 +1273,11 @@ void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
pushConstants.setPushConstant(value, index);
}
void MetalDriver::insertEventMarker(const char* string) {
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
}
void MetalDriver::pushGroupMarker(const char* string) {
void MetalDriver::pushGroupMarker(const char* string, uint32_t len) {
mContext->groupMarkers.push(string);
}
@@ -1778,13 +1749,6 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
}
// depth clip mode
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
mContext->depthClampState.updateState(depthClipMode);
if (mContext->depthClampState.stateChanged()) {
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
}
// Set the depth-stencil state, if a state change is needed.
DepthStencilState depthState;
if (depthAttachment) {
@@ -2095,17 +2059,16 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
+14 -6
View File
@@ -257,6 +257,10 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
@@ -275,10 +279,14 @@ public:
[that->mDrawable present];
}
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
// mDrawable is acquired on the driver thread. Typically, we would release this object on
// the same thread, but after receiving consistent crash reports from within
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
#else
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
#endif
}
private:
@@ -548,6 +556,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
descriptor.usage = getMetalTextureUsage(usage);
descriptor.storageMode = MTLStorageModePrivate;
texture = [context.device newTextureWithDescriptor:descriptor];
FILAMENT_CHECK_POSTCONDITION(texture != nil)
<< "Could not create Metal texture. Out of memory?";
break;
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
@@ -562,6 +572,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
descriptor.usage = getMetalTextureUsage(usage);
descriptor.storageMode = MTLStorageModePrivate;
texture = [context.device newTextureWithDescriptor:descriptor];
FILAMENT_CHECK_POSTCONDITION(texture != nil)
<< "Could not create Metal texture. Out of memory?";
break;
case SamplerType::SAMPLER_3D:
descriptor = [MTLTextureDescriptor new];
@@ -574,6 +586,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
descriptor.usage = getMetalTextureUsage(usage);
descriptor.storageMode = MTLStorageModePrivate;
texture = [context.device newTextureWithDescriptor:descriptor];
FILAMENT_CHECK_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
@@ -582,12 +596,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 &&
-1
View File
@@ -382,7 +382,6 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
// Argument encoder
+2 -9
View File
@@ -202,10 +202,6 @@ bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isDepthClampSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -320,10 +316,10 @@ void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string) {
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
}
void NoopDriver::pushGroupMarker(char const* string) {
void NoopDriver::pushGroupMarker(char const* string, uint32_t len) {
}
void NoopDriver::popGroupMarker(int) {
@@ -392,7 +388,4 @@ void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void NoopDriver::resetState(int) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
} // namespace filament
@@ -676,7 +676,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
@@ -747,7 +746,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
ext->EXT_clip_cull_distance = true;
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext->EXT_depth_clamp = true;
ext->EXT_discard_framebuffer = false;
ext->EXT_disjoint_timer_query = true;
ext->EXT_multisampled_render_to_texture = false;
+3 -5
View File
@@ -220,9 +220,8 @@ public:
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_depth_clamp;
bool EXT_discard_framebuffer;
bool EXT_disjoint_timer_query;
bool EXT_discard_framebuffer;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
@@ -240,10 +239,10 @@ public:
bool KHR_parallel_shader_compile;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_EGL_image_external_essl3;
bool OES_depth24;
bool OES_depth_texture;
bool OES_depth24;
bool OES_packed_depth_stencil;
bool OES_EGL_image_external_essl3;
bool OES_rgb8_rgba8;
bool OES_standard_derivatives;
bool OES_texture_npot;
@@ -628,7 +627,6 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
#ifdef BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 10; break;
#endif
case GL_DEPTH_CLAMP: index = 11; break;
default: break;
}
assert_invariant(index < state.enables.caps.size());
+19 -85
View File
@@ -83,20 +83,12 @@
#define HAS_MAPBUFFERS 1
#endif
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level (for user markers [default: All])
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
// set to the desired debug level (for internal debugging [Default: None])
// set to the desired debug marker level
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
@@ -204,37 +196,11 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
: driver(driver) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
#endif
driver.pushGroupMarker(string, strlen(string));
}
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
#endif
driver.popGroupMarker();
}
// ------------------------------------------------------------------------------------------------
@@ -451,14 +417,6 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
} else {
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
if (gl.ext.EXT_depth_clamp) {
if (rs.depthClamp) {
gl.enable(GL_DEPTH_CLAMP);
} else {
gl.disable(GL_DEPTH_CLAMP);
}
}
}
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
@@ -1148,7 +1106,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
switch (t->target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
// this could be GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_ARRAY
@@ -1159,9 +1116,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
target = getCubemapTarget(binfo.layer);
// note: cubemaps can't be multi-sampled
break;
case SamplerType::SAMPLER_EXTERNAL:
// This is an error. We have asserted in debug build.
target = t->gl.target;
default:
break;
}
}
@@ -1179,7 +1134,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
if (rt->gl.samples <= 1 ||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) {
// On GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
// on GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
// If multisampled textures are not supported and we end-up here, things should
// still work, albeit without MSAA.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
@@ -1203,7 +1158,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
GL_RENDERBUFFER, t->gl.id);
}
break;
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -1317,7 +1271,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
GL_RENDERBUFFER, t->gl.id);
}
break;
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -2132,10 +2085,6 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
return getContext().ext.EXT_protected_textures;
}
bool OpenGLDriver::isDepthClampSupported() {
return getContext().ext.EXT_depth_clamp;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -2227,10 +2176,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
uint32_t index, Handle<HwBufferObject> boh) {
DEBUG_MARKER()
@@ -2384,7 +2329,9 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
GLSamplerGroup* const sb = handle_cast<GLSamplerGroup *>(sbh);
assert_invariant(sb->textureUnitEntries.size() == data.size / sizeof(SamplerDescriptor));
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
bool const es2 = context.isES2();
#endif
auto const* const pSamplers = (SamplerDescriptor const*)data.buffer;
for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) {
@@ -2394,15 +2341,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
GLTexture const* const t = handle_cast<const GLTexture*>(th);
assert_invariant(t);
if (UTILS_UNLIKELY(es2)
#if defined(GL_EXT_texture_filter_anisotropic)
|| UTILS_UNLIKELY(anisotropyWorkaround)
#endif
) {
// We must set texture parameters on the texture itself.
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
}
SamplerParams params = pSamplers[i].s;
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
// From OES_EGL_image_external spec:
@@ -3199,28 +3137,28 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::insertEventMarker(char const* string) {
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
auto& gl = mContext;
if (gl.ext.EXT_debug_marker) {
glInsertEventMarkerEXT(GLsizei(strlen(string)), string);
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
}
void OpenGLDriver::pushGroupMarker(char const* string) {
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
@@ -3230,14 +3168,14 @@ void OpenGLDriver::pushGroupMarker(char const* string) {
void OpenGLDriver::popGroupMarker(int) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
@@ -3943,7 +3881,6 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
@@ -3965,7 +3902,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
}
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
@@ -3986,7 +3922,6 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
}
void OpenGLDriver::scissor(Viewport scissor) {
DEBUG_MARKER()
setScissor(scissor);
}
@@ -4006,7 +3941,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
DEBUG_MARKER()
getShaderCompilerService().tick();
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
+1 -1
View File
@@ -81,7 +81,7 @@ public:
const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
UTILS_UNUSED OpenGLDriver& driver;
OpenGLDriver& driver;
public:
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
~DebugMarker() noexcept;
@@ -143,25 +143,21 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
auto state = weak.lock();
if (!state) {
// The timer query state has been destroyed on the way, very likely due to the IBL
// prefilter context destruction. We still return true to get this element removed from
// the query list.
return true;
if (state) {
GLuint available = 0;
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR(utils::slog.e)
if (!available) {
// we need to try this one again later
return false;
}
GLuint64 elapsedTime = 0;
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
} else {
state->elapsed.store(int64_t(TimerQueryResult::ERROR), std::memory_order_relaxed);
}
GLuint available = 0;
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR(utils::slog.e)
if (!available) {
// we need to try this one again later
return false;
}
GLuint64 elapsedTime = 0;
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
return true;
});
}
@@ -26,28 +26,16 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <array>
#include <cctype>
#include <chrono>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <variant>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
using namespace utils;
@@ -488,12 +476,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
// check status of program linking and shader compilation, logs error and free all resources
// in case of error.
bool const success = checkProgramStatus(token);
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
// The compilation error has been logged to log.e by this point.
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
if (UTILS_LIKELY(success)) {
program = token->gl.program;
// no need to keep the shaders around
@@ -597,31 +579,24 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
version = "#version 310 es\n";
}
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
const std::array<const char*, 5> sources = {
version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
const std::array<GLint, 5> lengths = {
(GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
GLuint const shaderId = glCreateShader(glShaderType);
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
glCompileShader(shaderId);
#ifndef NDEBUG
@@ -810,24 +785,25 @@ mediump vec4 unpackSnorm4x8(highp uint v) {
// - 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);
auto start = source.find("#version");
assert_invariant(start != std::string_view::npos);
auto version_eol = source.find('\n', version_start) + 1;
auto version_eol = source.find('\n', 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 = version_eol;
} 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);
auto eol = source.find('\n', pos) + 1;
assert_invariant(eol != std::string_view::npos);
std::string_view const version = source.substr(start, version_eol - start);
std::string_view const prolog = source.substr(version_eol, eol - version_eol);
std::string_view const body = source.substr(eol, source.length() - eol);
return { version, prolog, body };
}
-6
View File
@@ -201,12 +201,6 @@ using namespace glext;
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_EXT_depth_clamp)
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
#else
# define GL_DEPTH_CLAMP 0x864F
#endif
#if defined(GL_KHR_debug)
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
@@ -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;
};
@@ -59,11 +59,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
VkImageView VulkanAttachment::getImageView() {
assert_invariant(texture);
VkImageSubresourceRange range = getSubresourceRange();
if (range.layerCount > 1) {
return texture->getMultiviewAttachmentView(range);
}
return texture->getAttachmentView(range);
return texture->getAttachmentView(getSubresourceRange());
}
bool VulkanAttachment::isDepth() const {
@@ -77,7 +73,7 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
.baseMipLevel = uint32_t(level),
.levelCount = 1,
.baseArrayLayer = uint32_t(layer),
.layerCount = layerCount,
.layerCount = 1,
};
}
@@ -43,8 +43,6 @@ struct VulkanCommandBuffer;
struct VulkanAttachment {
VulkanTexture* texture = nullptr;
uint8_t level = 0;
uint8_t baseViewIndex = 0;
uint8_t layerCount = 1;
uint16_t layer = 0;
bool isDepth() const;
@@ -125,10 +123,6 @@ public:
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
+8 -27
View File
@@ -595,8 +595,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
colorTargets[i] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(color[i].handle),
.level = color[i].level,
.baseViewIndex = color[i].baseViewIndex,
.layerCount = layerCount,
.layer = color[i].layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D();
@@ -611,8 +609,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthStencil[0] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
.level = depth.level,
.baseViewIndex = depth.baseViewIndex,
.layerCount = layerCount,
.layer = depth.layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D();
@@ -625,8 +621,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthStencil[1] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
.level = stencil.level,
.baseViewIndex = stencil.baseViewIndex,
.layerCount = layerCount,
.layer = stencil.layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
@@ -643,7 +637,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
samples, colorTargets, depthStencil, mStagePool, layerCount);
samples, colorTargets, depthStencil, mStagePool);
mResourceManager.acquire(renderTarget);
}
@@ -944,10 +938,6 @@ bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -996,15 +986,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;
}
@@ -1277,8 +1267,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
}
uint8_t const renderTargetLayerCount = rt->getLayerCount();
// Create the VkRenderPass or fetch it from cache.
VulkanFboCache::RenderPassKey rpkey = {
.initialColorLayoutMask = 0,
@@ -1289,12 +1277,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.discardEnd = discardEndVal,
.samples = rt->getSamples(),
.subpassMask = uint8_t(params.subpassMask),
.viewCount = renderTargetLayerCount,
};
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
const VulkanAttachment& info = rt->getColor(i);
if (info.texture) {
assert_invariant(info.layerCount == renderTargetLayerCount);
rpkey.initialColorLayoutMask |= 1 << i;
rpkey.colorFormat[i] = info.getFormat();
if (rpkey.samples > 1 && info.texture->samples == 1) {
@@ -1606,13 +1592,13 @@ void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
value);
}
void VulkanDriver::insertEventMarker(char const* string) {
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, strlen(string));
mCommands.insertEventMarker(string, len);
#endif
}
void VulkanDriver::pushGroupMarker(char const* string) {
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
// Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.pushGroupMarker(string);
@@ -1820,7 +1806,6 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.depthClamp = rasterState.depthClamp,
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
@@ -2037,10 +2022,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceAllocator.associateHandle(handleId, std::move(tag));
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
@@ -43,7 +43,6 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
if (k1.samples != k2.samples) return false;
if (k1.needsResolveMask != k2.needsResolveMask) return false;
if (k1.subpassMask != k2.subpassMask) return false;
if (k1.viewCount != k2.viewCount) return false;
return true;
}
@@ -187,25 +186,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.pDependencies = dependencies
};
VkRenderPassMultiviewCreateInfo multiviewCreateInfo = {};
uint32_t const subpassViewMask = (1 << config.viewCount) - 1;
// Prepare a view mask array for the maximum number of subpasses. All subpasses have all views
// activated.
uint32_t const viewMasks[2] = {subpassViewMask, subpassViewMask};
if (config.viewCount > 1) {
// Fill the multiview create info.
multiviewCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
multiviewCreateInfo.pNext = nullptr;
multiviewCreateInfo.subpassCount = hasSubpasses ? 2u : 1u;
multiviewCreateInfo.pViewMasks = viewMasks;
multiviewCreateInfo.dependencyCount = 0;
multiviewCreateInfo.pViewOffsets = nullptr;
multiviewCreateInfo.correlationMaskCount = 1;
multiviewCreateInfo.pCorrelationMasks = &subpassViewMask;
renderPassInfo.pNext = &multiviewCreateInfo;
}
int attachmentIndex = 0;
// Populate the Color Attachments.
+1 -1
View File
@@ -64,7 +64,7 @@ public:
uint8_t samples; // 1 byte
uint8_t needsResolveMask; // 1 byte
uint8_t subpassMask; // 1 byte
uint8_t viewCount; // 1 byte
uint8_t padding2; // 1 byte
};
struct RenderPassVal {
VkRenderPass handle;
@@ -16,6 +16,7 @@
#include "VulkanHandles.h"
#include "VulkanDriver.h"
#include "VulkanConstants.h"
#include "VulkanDriver.h"
#include "VulkanMemory.h"
@@ -322,12 +323,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
: HwRenderTarget(width, height),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(true),
mSamples(samples),
mLayerCount(layerCount) {
mSamples(samples) {
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
mColor[index] = color[index];
}
+1 -3
View File
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
@@ -319,7 +319,6 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
VulkanAttachment& getMsaaDepth();
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
uint8_t getSamples() const { return mSamples; }
uint8_t getLayerCount() const { return mLayerCount; }
bool hasDepth() const { return mDepth.texture; }
bool isSwapChain() const { return !mOffscreen; }
void bindToSwapChain(VulkanSwapChain& surf);
@@ -331,7 +330,6 @@ private:
VulkanAttachment mMsaaDepthAttachment = {};
const bool mOffscreen : 1;
uint8_t mSamples : 7;
uint8_t mLayerCount = 1;
};
struct VulkanBufferObject;
@@ -184,7 +184,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
vkRaster.cullMode = raster.cullMode;
vkRaster.frontFace = raster.frontFace;
vkRaster.depthClampEnable = raster.depthClamp;
vkRaster.depthBiasEnable = raster.depthBiasEnable;
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
vkRaster.depthBiasClamp = 0.0f;
@@ -90,8 +90,7 @@ public:
VkBlendFactor srcAlphaBlendFactor : 5;
VkBlendFactor dstAlphaBlendFactor : 5;
VkColorComponentFlags colorWriteMask : 4;
uint8_t rasterizationSamples : 4;// offset = 4 bytes
uint8_t depthClamp : 4;
uint8_t rasterizationSamples; // offset = 4 bytes
uint8_t colorTargetCount; // offset = 5 bytes
BlendEquation colorBlendOp : 4; // offset = 6 bytes
BlendEquation alphaBlendOp : 4;
@@ -105,10 +105,6 @@ public:
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
+11 -11
View File
@@ -97,11 +97,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.extent.depth = 1;
}
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & TextureUsage::BLIT_DST)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// Filament expects blit() to work with any texture, so we almost always set these usage flags.
// TODO: investigate performance implications of setting these flags.
constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) {
imageInfo.usage |= blittable;
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
@@ -119,7 +121,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | blittable;
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
@@ -128,9 +130,10 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
imageInfo.usage |= blittable;
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
imageInfo.usage |= blittable;
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
@@ -384,15 +387,12 @@ void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel)
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
// Attachments should only have one mipmap level and one layer.
range.levelCount = 1;
range.layerCount = 1;
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
}
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
}
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
return getImageView(range, type, mSwizzle);
}
@@ -73,10 +73,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
// and the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange range);
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment when rendering with multiview.
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
// This is a workaround for the first few frames where we're waiting for the texture to actually
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
// imageView needs to be of the right type. Hence, we provide an option to indicate the
@@ -172,7 +172,7 @@ public:
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=" << mUnusedCount;
<< " capacity=" << mCapacity << " count=" << "%s";
mSize++;
return createSet(layout->bitmask, vkSet);
}
@@ -32,12 +32,12 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
}
// build the push constant layout key
uint32_t const pushConstantRangeCount = program->getPushConstantRangeCount();
auto const& pushConstantRanges = program->getPushConstantRanges();
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
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 (auto 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;
@@ -22,7 +22,7 @@
#include <utils/Hash.h>
#include <unordered_map>
#include <tsl/robin_map.h>
namespace filament::backend {
@@ -36,8 +36,8 @@ public:
void terminate() noexcept;
struct PushConstantKey {
uint8_t stage = 0;// We have one set of push constant per shader stage (fragment, vertex, etc).
uint8_t size = 0;
uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc).
uint8_t size;
// Note that there is also an offset parameter for push constants, but
// we always assume our update range will have the offset 0.
};
@@ -73,7 +73,7 @@ private:
}
};
using PipelineLayoutMap = std::unordered_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
VkDevice mDevice;
@@ -82,6 +82,6 @@ private:
PipelineLayoutMap mPipelineLayouts;
};
} // filament::backend
}
#endif // TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
@@ -325,7 +325,6 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.depthClamp,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
+4 -5
View File
@@ -64,15 +64,14 @@ First determine the Filament material and variant that causes the problem.
What we want is the "raw" GLSL version of the shader, before any optimizations / cross-compilation
happens.
We can use the `--save-raw-variants` debug flag in matc to export each GLSL
shader to a file. For example:
The easiest way I've found to do this is by adding a `std::cout` statement inside `MaterialBuilder`,
right after `sg.createFragmentProgram` or `sg.createVertexProgram` is called. For example,
```
matc --save-raw-variants --optimize-size --variant-filter fog,ssr,vsm,stereo \
-a all -p all -o mymaterial.filamat mymaterial.mat
if (v.variant.key == 8u) { std::cout << shader << std::endl; }
```
Files will be named like `mymaterial_0x05.frag` or `mymaterial_0x05.vert`.
Then, I invoke `matc` and redirect stdout to a .frag or .vert file.
Note that gltfio material "templates" first go through a build step. After building gltfio, the
gltfio Filament materials are output to:
+1 -16
View File
@@ -54,7 +54,7 @@ public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -78,21 +78,6 @@ public:
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
+2 -28
View File
@@ -350,12 +350,9 @@ public:
uint32_t resourceAllocatorCacheSizeMB = 64;
/*
* This value determines how many frames texture entries are kept for in the cache. This
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
* Typically only one texture is evicted per frame.
* The default is 1.
* This value determines for how many frames are texture entries kept in the cache.
*/
uint32_t resourceAllocatorCacheMaxAge = 1;
uint32_t resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
@@ -896,29 +893,6 @@ public:
/** Tells whether an InstanceBuffer object is valid */
bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const;
/**
* Retrieve the count of each resource tracked by Engine.
* This is intended for debugging.
* @{
*/
size_t getBufferObjectCount() const noexcept;
size_t getViewCount() const noexcept;
size_t getSceneCount() const noexcept;
size_t getSwapChainCount() const noexcept;
size_t getStreamCount() const noexcept;
size_t getIndexBufferCount() const noexcept;
size_t getSkinningBufferCount() const noexcept;
size_t getMorphTargetBufferCount() const noexcept;
size_t getInstanceBufferCount() const noexcept;
size_t getVertexBufferCount() const noexcept;
size_t getIndirectLightCount() const noexcept;
size_t getMaterialCount() const noexcept;
size_t getTextureCount() const noexcept;
size_t getSkyboxeCount() const noexcept;
size_t getColorGradingCount() const noexcept;
size_t getRenderTargetCount() const noexcept;
/** @} */
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that does guarantee that the
-18
View File
@@ -19,7 +19,6 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <stddef.h>
@@ -55,23 +54,6 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
utils::CString mName;
};
} // namespace filament
#endif // TNT_FILAMENT_FILAMENTAPI_H
+1 -16
View File
@@ -59,7 +59,7 @@ public:
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
};
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -83,21 +83,6 @@ public:
*/
Builder& bufferType(IndexType indexType) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
+1 -16
View File
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
@@ -70,21 +70,6 @@ public:
*/
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
+1 -16
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -63,21 +63,6 @@ public:
*/
Builder& count(size_t count) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
+41 -6
View File
@@ -489,20 +489,43 @@ public:
* For standard morphing, A MorphTargetBuffer must be provided.
* Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
* be enabled on the material definition: either via the legacyMorphing material attribute
* or by calling filamat::MaterialBuilder::useLegacyMorphing().
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
size_t offset, size_t count) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
*/
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
/**
* Specifies the the range of the MorphTargetBuffer to use with this primitive.
*
* @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 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)
*/
RenderableManager::Builder& morphing(uint8_t level,
size_t primitiveIndex, size_t offset) noexcept;
Builder& morphing(uint8_t level, size_t primitiveIndex,
size_t offset, size_t count) noexcept;
/**
@@ -763,13 +786,25 @@ public:
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferOffsetAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset);
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
/** @deprecated */
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
/** @deprecated */
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
/**
* Get a MorphTargetBuffer to the given renderable or null if it doesn't exist.
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
*/
MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBuffer(Instance instance) const noexcept;
MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance,
uint8_t level, size_t primitiveIndex) const noexcept;
/**
* Gets the number of morphing in the given entity.
-41
View File
@@ -22,11 +22,9 @@
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <math/vec4.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
@@ -83,37 +81,6 @@ public:
UTILS_DEPRECATED uint64_t vsyncOffsetNanos = 0;
};
/**
* Timing information about a frame
* @see getFrameInfoHistory()
*/
struct FrameInfo {
using time_point_ns = int64_t;
using duration_ns = int64_t;
uint32_t frameId; //!< monotonically increasing frame identifier
duration_ns frameTime; //!< frame duration on the GPU in nanosecond [ns]
duration_ns denoisedFrameTime; //!< denoised frame duration on the GPU in [ns]
time_point_ns beginFrame; //!< Renderer::beginFrame() time since epoch [ns]
time_point_ns endFrame; //!< Renderer::endFrame() time since epoch [ns]
time_point_ns backendBeginFrame; //!< Backend thread time of frame start since epoch [ns]
time_point_ns backendEndFrame; //!< Backend thread time of frame end since epoch [ns]
};
/**
* Retrieve an historic of frame timing information. The maximum frame history size is
* given by getMaxFrameHistorySize().
* @param historySize requested history size. The returned vector could be smaller.
* @return A vector of FrameInfo.
*/
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(
size_t historySize = 1) const noexcept;
/**
* @return the maximum supported frame history size.
* @see getFrameInfoHistory()
*/
size_t getMaxFrameHistorySize() const noexcept;
/**
* Use FrameRateOptions to set the desired frame rate and control how quickly the system
* reacts to GPU load changes.
@@ -271,14 +238,6 @@ public:
*/
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
/**
* Call skipFrame when momentarily skipping frames, for instance if the content of the
* scene doesn't change.
*
* @param vsyncSteadyClockTimeNano
*/
void skipFrame(uint64_t vsyncSteadyClockTimeNano = 0u);
/**
* Set-up a frame for this Renderer.
*
+1 -16
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -69,21 +69,6 @@ public:
*/
Builder& initialize(bool initialize = true) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
+1 -16
View File
@@ -94,7 +94,7 @@ public:
*
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
*/
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -136,21 +136,6 @@ public:
*/
Builder& height(uint32_t height) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Stream object and returns a pointer to it.
*
+1 -16
View File
@@ -112,7 +112,7 @@ public:
//! Use Builder to construct a Texture object instance
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -202,21 +202,6 @@ public:
*/
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Texture object and returns a pointer to it.
*
+1 -16
View File
@@ -61,7 +61,7 @@ public:
using AttributeType = backend::ElementType;
using BufferDescriptor = backend::BufferDescriptor;
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -158,21 +158,6 @@ public:
*/
Builder& advancedSkinning(bool enabled) noexcept;
/**
* Associate an optional name with this VertexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this VertexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+1 -21
View File
@@ -24,7 +24,6 @@
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/FixedCapacityVector.h>
#include <math/mathfwd.h>
@@ -41,7 +40,6 @@ class CallbackHandler;
class Camera;
class ColorGrading;
class Engine;
class MaterialInstance;
class RenderTarget;
class Scene;
@@ -570,13 +568,6 @@ public:
*/
void setShadowType(ShadowType shadow) noexcept;
/**
* Returns the shadow mapping technique used by this View.
*
* @return value set by setShadowType().
*/
ShadowType getShadowType() const noexcept;
/**
* Sets VSM shadowing options that apply across the entire View.
*
@@ -735,7 +726,7 @@ public:
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
/** Result of a picking query */
@@ -887,17 +878,6 @@ public:
*/
utils::Entity getFogEntity() const noexcept;
/**
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
* keeps a history of previous frame renders associated with the Renderer the view was last
* used with. When switching Renderer, it may be necessary to clear that history by calling
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
* being very different.
*/
void clearFrameHistory(Engine& engine) noexcept;
/**
* List of available ambient occlusion techniques
* @deprecated use AmbientOcclusionOptions::enabled instead
-67
View File
@@ -16,8 +16,6 @@
#include "details/Engine.h"
#include "ResourceAllocator.h"
#include "details/BufferObject.h"
#include "details/Camera.h"
#include "details/Fence.h"
@@ -268,71 +266,6 @@ bool Engine::isValid(const InstanceBuffer* p) const {
return downcast(this)->isValid(downcast(p));
}
size_t Engine::getBufferObjectCount() const noexcept {
return downcast(this)->getBufferObjectCount();
}
size_t Engine::getViewCount() const noexcept {
return downcast(this)->getViewCount();
}
size_t Engine::getSceneCount() const noexcept {
return downcast(this)->getSceneCount();
}
size_t Engine::getSwapChainCount() const noexcept {
return downcast(this)->getSwapChainCount();
}
size_t Engine::getStreamCount() const noexcept {
return downcast(this)->getStreamCount();
}
size_t Engine::getIndexBufferCount() const noexcept {
return downcast(this)->getIndexBufferCount();
}
size_t Engine::getSkinningBufferCount() const noexcept {
return downcast(this)->getSkinningBufferCount();
}
size_t Engine::getMorphTargetBufferCount() const noexcept {
return downcast(this)->getMorphTargetBufferCount();
}
size_t Engine::getInstanceBufferCount() const noexcept {
return downcast(this)->getInstanceBufferCount();
}
size_t Engine::getVertexBufferCount() const noexcept {
return downcast(this)->getVertexBufferCount();
}
size_t Engine::getIndirectLightCount() const noexcept {
return downcast(this)->getIndirectLightCount();
}
size_t Engine::getMaterialCount() const noexcept {
return downcast(this)->getMaterialCount();
}
size_t Engine::getTextureCount() const noexcept {
return downcast(this)->getTextureCount();
}
size_t Engine::getSkyboxeCount() const noexcept {
return downcast(this)->getSkyboxeCount();
}
size_t Engine::getColorGradingCount() const noexcept {
return downcast(this)->getColorGradingCount();
}
size_t Engine::getRenderTargetCount() const noexcept {
return downcast(this)->getRenderTargetCount();
}
void Engine::flushAndWait() {
downcast(this)->flushAndWait();
}
-31
View File
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <filament/FilamentAPI.h>
#include <algorithm>
namespace filament {
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
if (!name) {
return;
}
size_t const length = std::min(len, size_t { 128u });
outName = utils::CString(name, length);
}
} // namespace filament
-3
View File
@@ -21,9 +21,6 @@
#include <fg/FrameGraphTexture.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <stdint.h>
namespace filament {
+55 -149
View File
@@ -16,34 +16,30 @@
#include "FrameInfo.h"
#include <filament/Renderer.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Systrace.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <memory>
#include <ratio>
#include <math/scalar.h>
#include <stdint.h>
#include <stddef.h>
#include <cmath>
namespace filament {
using namespace utils;
using namespace backend;
// this is to avoid a call to memmove
template<class InputIterator, class OutputIterator>
static inline
void move_backward(InputIterator first, InputIterator last, OutputIterator result) {
while (first != last) {
*--result = *--last;
}
}
FrameInfoManager::FrameInfoManager(DriverApi& driver) noexcept {
for (auto& query : mQueries) {
query.handle = driver.createTimerQuery();
query = driver.createTimerQuery();
}
}
@@ -51,155 +47,65 @@ FrameInfoManager::~FrameInfoManager() noexcept = default;
void FrameInfoManager::terminate(DriverApi& driver) noexcept {
for (auto& query : mQueries) {
driver.destroyTimerQuery(query.handle);
driver.destroyTimerQuery(query);
}
}
void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint32_t frameId) noexcept {
auto& history = mFrameTimeHistory;
// don't exceed the capacity, drop the oldest entry
if (UTILS_LIKELY(history.size() == history.capacity())) {
history.pop_back();
}
// create a new entry
auto& front = history.emplace_front(frameId);
// store the current time
front.beginFrame = std::chrono::steady_clock::now();
// references are not invalidated by CircularQueue<>, so we can associate a reference to
// the slot we created to the timer query used to find the frame time.
mQueries[mIndex].pInfo = std::addressof(front);
// issue the timer query
driver.beginTimerQuery(mQueries[mIndex].handle);
// issue the custom backend command to get the backend time
driver.queueCommand([&front](){
front.backendBeginFrame = std::chrono::steady_clock::now();
});
// now is a good time to check the oldest active query
while (mLast != mIndex) {
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE: {
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("FrameInfo::elapsed", uint32_t(elapsed));
// conversion to our duration happens here
pFront = mQueries[mLast].pInfo;
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
mLast = (mLast + 1) % POOL_COUNT;
denoiseFrameTime(history, config);
break;
}
}
if (result != TimerQueryResult::AVAILABLE) {
void FrameInfoManager::beginFrame(DriverApi& driver,Config const& config, uint32_t) noexcept {
driver.beginTimerQuery(mQueries[mIndex]);
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast], &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE:
mLast = (mLast + 1) % POOL_COUNT;
// conversion to our duration happens here
mFrameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
break;
}
// read the pending timer queries until we find one that's not ready
}
// keep this just for debugging
if constexpr (false) {
using namespace utils;
auto h = getFrameInfoHistory(1);
if (!h.empty()) {
slog.d << frameId << ": "
<< h[0].frameId << " (" << frameId - h[0].frameId << ")"
<< ", Dm=" << h[0].endFrame - h[0].beginFrame
<< ", L =" << h[0].backendBeginFrame - h[0].beginFrame
<< ", Db=" << h[0].backendEndFrame - h[0].backendBeginFrame
<< ", T =" << h[0].frameTime
<< io::endl;
}
}
update(config, mFrameTime);
}
void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
auto& front = mFrameTimeHistory.front();
// close the timer query
driver.endTimerQuery(mQueries[mIndex].handle);
// queue custom backend command to query the current time
driver.queueCommand([&front](){
// backend frame end-time
front.backendEndFrame = std::chrono::steady_clock::now();
// signal that the data is available
front.ready.store(true, std::memory_order_release);
});
// and finally acquire the time on the main thread
front.endFrame = std::chrono::steady_clock::now();
driver.endTimerQuery(mQueries[mIndex]);
mIndex = (mIndex + 1) % POOL_COUNT;
}
void FrameInfoManager::denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept {
assert_invariant(!history.empty());
void FrameInfoManager::update(Config const& config,
FrameInfoManager::duration lastFrameTime) noexcept {
// keep an history of frame times
auto& history = mFrameTimeHistory;
// find the first slot that has a valid frame duration
size_t first = history.size();
for (size_t i = 0, c = history.size(); i < c; ++i) {
if (history[i].frameTime != duration(0)) {
first = i;
break;
}
// this is like doing { pop_back(); push_front(); }
filament::move_backward(history.begin(), history.end() - 1, history.end());
history[0].frameTime = lastFrameTime;
mFrameTimeHistorySize = std::min(++mFrameTimeHistorySize, uint32_t(MAX_FRAMETIME_HISTORY));
if (UTILS_UNLIKELY(mFrameTimeHistorySize < 3)) {
// not enough history to do anything useful
history[0].valid = false;
return;
}
assert_invariant(first != history.size());
// we need at least 3 valid frame time to calculate the median
if (history.size() >= first + 3) {
// apply a median filter to get a good representation of the frame time of the last
// N frames.
std::array<duration, MAX_FRAMETIME_HISTORY> median; // NOLINT -- it's initialized below
size_t const size = std::min({
history.size() - first,
median.size(),
size_t(config.historySize) });
// apply a median filter to get a good representation of the frame time of the last
// N frames.
std::array<duration, MAX_FRAMETIME_HISTORY> median; // NOLINT -- it's initialized below
size_t const size = std::min(mFrameTimeHistorySize,
std::min(config.historySize, (uint32_t)median.size()));
for (size_t i = 0; i < size; ++i) {
median[i] = history[i].frameTime;
}
std::sort(median.begin(), median.begin() + size);
duration const denoisedFrameTime = median[size / 2];
for (size_t i = 0; i < size; ++i) {
median[i] = history[first + i].frameTime;
}
std::sort(median.begin(), median.begin() + size);
duration const denoisedFrameTime = median[size / 2];
history[first].denoisedFrameTime = denoisedFrameTime;
history[first].valid = true;
}
history[0].denoisedFrameTime = denoisedFrameTime;
history[0].valid = true;
}
utils::FixedCapacityVector<Renderer::FrameInfo> FrameInfoManager::getFrameInfoHistory(
size_t historySize) const noexcept {
auto result = utils::FixedCapacityVector<Renderer::FrameInfo>::with_capacity(MAX_FRAMETIME_HISTORY);
auto const& history = mFrameTimeHistory;
size_t i = 0;
size_t const c = history.size();
for (; i < c; ++i) {
auto const& entry = history[i];
if (entry.ready.load(std::memory_order_acquire) && entry.valid) {
// once we found an entry ready,
// we know by construction that all following ones are too
break;
}
}
for (; i < c && historySize; ++i, --historySize) {
auto const& entry = history[i];
using namespace std::chrono;
result.push_back({
entry.frameId,
duration_cast<nanoseconds>(entry.frameTime).count(),
duration_cast<nanoseconds>(entry.denoisedFrameTime).count(),
duration_cast<nanoseconds>(entry.beginFrame.time_since_epoch()).count(),
duration_cast<nanoseconds>(entry.endFrame.time_since_epoch()).count(),
duration_cast<nanoseconds>(entry.backendBeginFrame.time_since_epoch()).count(),
duration_cast<nanoseconds>(entry.backendEndFrame.time_since_epoch()).count()
});
}
return result;
}
} // namespace filament
+17 -125
View File
@@ -17,136 +17,30 @@
#ifndef TNT_FILAMENT_FRAMEINFO_H
#define TNT_FILAMENT_FRAMEINFO_H
#include <filament/Renderer.h>
#include <backend/Handle.h>
#include "backend/Handle.h"
#include <private/backend/DriverApi.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <array>
#include <atomic>
#include <chrono>
#include <ratio>
#include <type_traits>
#include <stdint.h>
#include <stddef.h>
namespace filament {
class FEngine;
namespace details {
struct FrameInfo {
using duration = std::chrono::duration<float, std::milli>;
duration frameTime{}; // frame period
duration denoisedFrameTime{}; // frame period (median filter)
bool valid = false; // true if the data of the structure is valid
};
} // namespace details
struct FrameInfoImpl : public details::FrameInfo {
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
uint32_t const frameId;
time_point beginFrame; // main thread beginFrame time
time_point endFrame; // main thread endFrame time
time_point backendBeginFrame; // backend thread beginFrame time (makeCurrent time)
time_point backendEndFrame; // backend thread endFrame time (present time)
std::atomic_bool ready{}; // true once backend thread has populated its data
explicit FrameInfoImpl(uint32_t frameId) noexcept
: frameId(frameId) {
}
};
template<typename T, size_t CAPACITY>
class CircularQueue {
public:
using value_type = T;
using reference = value_type&;
using const_reference = value_type const&;
size_t capacity() const {
return CAPACITY;
}
size_t size() const {
return mSize;
}
bool empty() const noexcept {
return !size();
}
void pop_back() noexcept {
assert_invariant(!empty());
--mSize;
std::destroy_at(&mStorage[(mFront - mSize) % CAPACITY]);
}
void push_front(T const& v) noexcept {
assert_invariant(size() < CAPACITY);
mFront = advance(mFront);
new(&mStorage[mFront]) T(v);
++mSize;
}
void push_front(T&& v) noexcept {
assert_invariant(size() < CAPACITY);
mFront = advance(mFront);
new(&mStorage[mFront]) T(std::move(v));
++mSize;
}
template<typename ...Args>
T& emplace_front(Args&&... args) noexcept {
assert_invariant(size() < CAPACITY);
mFront = advance(mFront);
new(&mStorage[mFront]) T(std::forward<Args>(args)...);
++mSize;
return front();
}
T& operator[](size_t pos) noexcept {
assert_invariant(pos < size());
size_t const index = (mFront + CAPACITY - pos) % CAPACITY;
return *std::launder(reinterpret_cast<T*>(&mStorage[index]));
}
T const& operator[](size_t pos) const noexcept {
return const_cast<CircularQueue&>(*this)[pos];
}
T const& front() const noexcept {
assert_invariant(!empty());
return operator[](0);
}
T& front() noexcept {
assert_invariant(!empty());
return operator[](0);
}
private:
using Storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
Storage mStorage[CAPACITY];
uint32_t mFront = 0; // always index 0
uint32_t mSize = 0;
[[nodiscard]] inline uint32_t advance(uint32_t v) noexcept {
return (v + 1) % CAPACITY;
}
bool valid = false;
};
class FrameInfoManager {
static constexpr size_t POOL_COUNT = 4;
static constexpr size_t MAX_FRAMETIME_HISTORY = 16u;
static constexpr size_t MAX_FRAMETIME_HISTORY = 31u;
public:
using duration = FrameInfoImpl::duration;
using clock = FrameInfoImpl::clock;
using duration = FrameInfo::duration;
struct Config {
uint32_t historySize;
@@ -163,25 +57,23 @@ public:
// call this immediately before "swap buffers"
void endFrame(backend::DriverApi& driver) noexcept;
details::FrameInfo getLastFrameInfo() const noexcept {
// if pFront is not set yet, return FrameInfo(). But the `valid` field will be false in this case.
return pFront ? *pFront : details::FrameInfo{};
FrameInfo const& getLastFrameInfo() const noexcept {
return mFrameTimeHistory[0];
}
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept;
duration getLastFrameTime() const noexcept {
return getLastFrameInfo().frameTime;
}
private:
using FrameHistoryQueue = CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY>;
static void denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept;
struct Query {
backend::Handle<backend::HwTimerQuery> handle{};
FrameInfoImpl* pInfo = nullptr;
};
std::array<Query, POOL_COUNT> mQueries;
uint32_t mIndex = 0; // index of current query
uint32_t mLast = 0; // index of oldest query still active
FrameInfoImpl* pFront = nullptr; // the most recent slot with a valid frame time
FrameHistoryQueue mFrameTimeHistory;
void update(Config const& config, duration lastFrameTime) noexcept;
backend::Handle<backend::HwTimerQuery> mQueries[POOL_COUNT];
duration mFrameTime{};
uint32_t mIndex = 0;
uint32_t mLast = 0;
std::array<FrameInfo, MAX_FRAMETIME_HISTORY> mFrameTimeHistory;
uint32_t mFrameTimeHistorySize = 0;
};
+19 -25
View File
@@ -16,22 +16,17 @@
#include "FrameSkipper.h"
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(std::clamp(latency, size_t(1), MAX_FRAME_LATENCY) - 1) {
: mLast(latency - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
FrameSkipper::~FrameSkipper() noexcept = default;
@@ -46,32 +41,31 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
}
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
fence = driver.createFence();
}
} // namespace filament
+2 -16
View File
@@ -22,9 +22,6 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -32,18 +29,7 @@ namespace filament {
* outrun the GPU.
*/
class FrameSkipper {
/*
* The maximum frame latency acceptable on ANDROID is 2 because higher latencies will be
* throttled anyway in BufferQueueProducer::dequeueBuffer(), because ANDROID is generally
* triple-buffered no more; that case is actually pretty bad because the GL thread can block
* anywhere (usually inside the first draw command that touches the swapchain).
*
* A frame latency of 1 has the benefit of reducing render latency,
* but the drawback of preventing CPU / GPU overlap.
*
* Generally a frame latency of 2 is the best compromise.
*/
static constexpr size_t MAX_FRAME_LATENCY = 2;
static constexpr size_t MAX_FRAME_LATENCY = 3;
public:
/*
* The latency parameter defines how many unfinished frames we want to accept before we start
@@ -74,7 +60,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
uint8_t const mLast;
size_t mLast;
};
} // namespace filament
-1
View File
@@ -26,7 +26,6 @@
#include <filament/Viewport.h>
#include <utils/BinaryTreeArray.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <utils/debug.h>
-6
View File
@@ -354,12 +354,6 @@ bool MaterialParser::getSpecularAntiAliasingThreshold(float* value) const noexce
return mImpl.getFromSimpleChunk(ChunkType::MaterialSpecularAntiAliasingThreshold, value);
}
bool MaterialParser::getStereoscopicType(backend::StereoscopicType* value) const noexcept {
static_assert(sizeof(StereoscopicType) == sizeof(uint8_t),
"StereoscopicType expected size is wrong");
return mImpl.getFromSimpleChunk(ChunkType::MaterialStereoscopicType, reinterpret_cast<uint8_t*>(value));
}
bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcept {
uint32_t rawAttributes = 0;
if (!mImpl.getFromSimpleChunk(ChunkType::MaterialRequiredAttributes, &rawAttributes)) {
-1
View File
@@ -122,7 +122,6 @@ public:
bool hasSpecularAntiAliasing(bool* value) const noexcept;
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
bool getSpecularAntiAliasingThreshold(float* value) const noexcept;
bool getStereoscopicType(backend::StereoscopicType*) const noexcept;
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
Variant variant, backend::ShaderStage stage) noexcept;
+1 -3
View File
@@ -459,12 +459,10 @@ void PerViewUniforms::bind(backend::DriverApi& driver) noexcept {
void PerViewUniforms::unbindSamplers() noexcept {
auto& samplerGroup = mSamplers;
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::IBL_SPECULAR);
samplerGroup.clearSampler(PerViewSib::SSAO);
samplerGroup.clearSampler(PerViewSib::SSR);
samplerGroup.clearSampler(PerViewSib::STRUCTURE);
samplerGroup.clearSampler(PerViewSib::FOG);
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
}
} // namespace filament
+8 -18
View File
@@ -494,6 +494,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
struct StructureMipmapData {
FrameGraphId<FrameGraphTexture> depth;
uint32_t rt[8];
};
fg.addPass<StructureMipmapData>("StructureMipmap",
@@ -504,7 +505,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
.level = uint8_t(i)
});
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
builder.declareRenderPass("Structure mip target", {
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
.attachments = { .depth = out }
});
}
@@ -1964,8 +1965,8 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
// - visible bloom size changes with dynamic resolution in non-homogenous mode
// This allows us to use the 9 sample downsampling filter (instead of 13)
// for at least 4 levels.
uint32_t width = std::max(16u, uint32_t(std::floor(bloomWidth)));
uint32_t height = std::max(16u, uint32_t(std::floor(bloomHeight)));
uint32_t width = std::max(1u, uint32_t(std::floor(bloomWidth)));
uint32_t height = std::max(1u, uint32_t(std::floor(bloomHeight)));
width &= ~((1 << 4) - 1); // at least 4 levels
height &= ~((1 << 4) - 1);
bloomWidth = float(width);
@@ -1977,8 +1978,6 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
// we don't need to do the fireflies reduction if we have TAA (it already does it)
bool fireflies = threshold && !taaOptions.enabled;
assert_invariant(bloomWidth && bloomHeight);
while (2 * bloomWidth < float(desc.width) || 2 * bloomHeight < float(desc.height)) {
if (inoutBloomOptions.quality == QualityLevel::LOW ||
inoutBloomOptions.quality == QualityLevel::MEDIUM) {
@@ -2945,10 +2944,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
1.0f / outputDesc.height});
}
if (blitterNames[index] == "blitLow") {
mi->setParameter("levelOfDetail", 0.0f);
}
mi->setParameter("viewport", float4{
(float)vp.left / inputDesc.width,
(float)vp.bottom / inputDesc.height,
@@ -3007,8 +3002,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
SamplerMagFilter filterMag,
SamplerMinFilter filterMin) noexcept {
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
// TODO: add support for sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3034,8 +3030,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
// --------------------------------------------------------------------------------
// set uniforms
PostProcessMaterial const& material =
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {
.filterMag = filterMag,
@@ -3047,10 +3042,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("levelOfDetail", levelOfDetail);
if (layer) {
mi->setParameter("layerIndex", layer);
}
mi->commit(driver);
mi->use(driver);
@@ -3193,7 +3184,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
assert_invariant(dst);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
+42 -69
View File
@@ -38,7 +38,6 @@
#include <backend/PipelineState.h>
#include "private/backend/CircularBuffer.h"
#include "private/backend/CommandStream.h"
#include <utils/compiler.h>
#include <utils/debug.h>
@@ -118,11 +117,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
uint32_t const customCommandCount =
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
assert_invariant(commandBegin);
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
assert_invariant(curr);
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
static bool sLogOnce = true;
if (UTILS_UNLIKELY(sLogOnce)) {
sLogOnce = false;
@@ -131,7 +129,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
}
}
appendCommands(engine, { commandBegin, commandCount },
mCommandBegin = curr;
mCommandEnd = curr + commandCount + customCommandCount;
appendCommands(engine, { curr, commandCount },
builder.mUboHandle,
builder.mVisibleRenderables,
builder.mCommandTypeFlags,
@@ -142,36 +143,33 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
builder.mCameraForwardVector);
if (builder.mCustomCommands.has_value()) {
Command* p = commandBegin + commandCount;
Command* p = curr + commandCount;
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
appendCustomCommand(p++, channel, passId, command, order, fn);
}
}
// sort commands once we're done adding commands
commandEnd = resize(builder.mArena,
RenderPass::sortCommands(commandBegin, commandEnd));
sortCommands(builder.mArena);
if (engine.isAutomaticInstancingEnabled()) {
int32_t stereoscopicEyeCount = 1;
uint32_t stereoscopicEyeCount = 1;
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
}
commandEnd = resize(builder.mArena,
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
instanceify(engine, builder.mArena, stereoscopicEyeCount);
}
// these are `const` from this point on...
mCommandBegin = commandBegin;
mCommandEnd = commandEnd;
}
// this destructor is actually heavy because it inlines ~vector<>
RenderPass::~RenderPass() noexcept = default;
RenderPass::~RenderPass() noexcept {
}
RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexcept {
arena.rewind(last);
return last;
void RenderPass::resize(Arena& arena, size_t count) noexcept {
if (mCommandBegin) {
mCommandEnd = mCommandBegin + count;
arena.rewind(mCommandEnd);
}
}
void RenderPass::appendCommands(FEngine& engine,
@@ -223,7 +221,7 @@ void RenderPass::appendCommands(FEngine& engine,
work(vr.first, vr.size());
} else {
auto* jobCommandsParallel = jobs::parallel_for(js, nullptr, vr.first, (uint32_t)vr.size(),
std::cref(work), jobs::CountSplitter<JOBS_PARALLEL_FOR_COMMANDS_COUNT>());
std::cref(work), jobs::CountSplitter<JOBS_PARALLEL_FOR_COMMANDS_COUNT, 5>());
js.runAndWait(jobCommandsParallel);
}
@@ -262,19 +260,18 @@ void RenderPass::appendCustomCommand(Command* commands,
commands->key = cmd;
}
RenderPass::Command* RenderPass::sortCommands(
Command* const begin, Command* const end) noexcept {
SYSTRACE_NAME("sort commands");
void RenderPass::sortCommands(Arena& arena) noexcept {
SYSTRACE_NAME("sort and trim commands");
std::sort(begin, end);
std::sort(mCommandBegin, mCommandEnd);
// find the last command
Command* const last = std::partition_point(begin, end,
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
[](Command const& c) {
return c.key != uint64_t(Pass::SENTINEL);
});
return last;
resize(arena, uint32_t(last - mCommandBegin));
}
void RenderPass::execute(RenderPass const& pass,
@@ -287,9 +284,7 @@ void RenderPass::execute(RenderPass const& pass,
driver.endRenderPass();
}
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
Command* curr, Command* const last,
int32_t eyeCount) const noexcept {
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -302,12 +297,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
Command* curr = mCommandBegin;
Command* const last = mCommandEnd;
Command* firstSentinel = nullptr;
PerRenderableData const* uboData = nullptr;
PerRenderableData* stagingBuffer = nullptr;
uint32_t stagingBufferSize = 0;
uint32_t instancedPrimitiveOffset = 0;
size_t const count = last - curr;
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
@@ -346,6 +343,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
if (UTILS_UNLIKELY(!stagingBuffer)) {
// create a temporary UBO for instancing
size_t const count = mCommandEnd - mCommandBegin;
mInstancedUboHandle = BufferObjectSharedHandle{
engine.getDriverApi().createBufferObject(
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
@@ -387,7 +385,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
if (UTILS_UNLIKELY(firstSentinel)) {
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << count << io::endl;
// << mCommandEnd - mCommandBegin << io::endl;
// we have instanced primitives
DriverApi& driver = engine.getDriverApi();
@@ -403,15 +401,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
stagingBuffer = nullptr;
// remove all the canceled commands
auto lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
auto lastCommand = std::remove_if(firstSentinel, mCommandEnd, [](auto const& command) {
return command.key == uint64_t(Pass::SENTINEL);
});
return lastCommand;
resize(arena, uint32_t(lastCommand - mCommandBegin));
}
assert_invariant(stagingBuffer == nullptr);
return last;
}
@@ -419,8 +416,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
inline // and we don't need it in the compilation unit
void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* const UTILS_RESTRICT mi,
bool inverseFrontFaces, bool hasDepthClamp) noexcept {
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
variant = Variant::filterVariant(variant, ma->isVariantLit());
@@ -461,7 +457,6 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
cmdDraw.info.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
}
@@ -560,9 +555,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
bool const hasInstancedStereo =
renderFlags & IS_INSTANCED_STEREOSCOPIC;
bool const hasDepthClamp =
renderFlags & HAS_DEPTH_CLAMP;
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
@@ -582,7 +574,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false;
cmd.info.rasterState.depthClamp = hasDepthClamp;
}
for (uint32_t i = range.first; i < range.last; ++i) {
@@ -616,7 +607,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// Here, objects close to the camera (but behind) will be drawn first.
// An alternative that keeps the mathematical ordering is given here:
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
// calculate the per-primitive face winding order inversion
@@ -680,6 +671,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
*/
for (size_t pi = 0, c = primitives.size(); pi < c; ++pi) {
auto const& primitive = primitives[pi];
auto const& morphTargets = morphing.targets[pi];
FMaterialInstance const* const mi = primitive.getMaterialInstance();
FMaterial const* const ma = mi->getMaterial();
@@ -692,13 +684,11 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.indexOffset = primitive.getIndexOffset();
cmd.info.indexCount = primitive.getIndexCount();
cmd.info.type = primitive.getPrimitiveType();
cmd.info.morphTargetBuffer = morphing.morphTargetBuffer ?
morphing.morphTargetBuffer->getHwHandle() : SamplerGroupHandle{};
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle();
cmd.info.morphingOffset = morphTargets.offset;
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
inverseFrontFaces, hasDepthClamp);
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
if (blendPass) {
// TODO: at least for transparent objects, AABB should be per primitive
@@ -895,31 +885,14 @@ void RenderPass::Executor::execute(FEngine& engine,
// Maximum space occupied in the CircularBuffer by a single `Command`. This must be
// reevaluated when the inner loop below adds DriverApi commands or when we change the
// CommandStream protocol. Currently, the maximum is 320 bytes.
// The batch size is calculated by adding the size of all commands that can possibly be
// emitted per draw call:
constexpr size_t const maxCommandSizeInBytes =
sizeof(CustomCommand) +
sizeof(COMMAND_TYPE(scissor)) +
sizeof(COMMAND_TYPE(bindUniformBuffer)) +
sizeof(COMMAND_TYPE(bindSamplers)) +
sizeof(COMMAND_TYPE(bindBufferRange)) +
sizeof(COMMAND_TYPE(bindBufferRange)) +
sizeof(COMMAND_TYPE(bindSamplers)) +
sizeof(COMMAND_TYPE(bindSamplers)) +
sizeof(COMMAND_TYPE(bindUniformBuffer)) +
sizeof(COMMAND_TYPE(bindSamplers)) +
sizeof(COMMAND_TYPE(bindSamplers)) +
sizeof(COMMAND_TYPE(bindPipeline)) +
sizeof(COMMAND_TYPE(setPushConstant)) +
sizeof(COMMAND_TYPE(bindRenderPrimitive)) +
sizeof(COMMAND_TYPE(draw2));
// CommandStream protocol. Currently, the maximum is 240 bytes, and we use 256 to be on
// the safer side.
size_t const maxCommandSizeInBytes = 256;
// Number of Commands that can be issued and guaranteed to fit in the current
// CircularBuffer allocation. In practice, we'll have tons of headroom especially if
// skinning and morphing aren't used. With a 2 MiB buffer (the default) a batch is
// 6553 commands (i.e. draw calls).
// 8192 commands (i.e. draw calls).
size_t const batchCommandCount = capacity / maxCommandSizeInBytes;
while(first != last) {
Command const* const batchLast = std::min(first + batchCommandCount, last);
+10 -15
View File
@@ -284,7 +284,6 @@ public:
static constexpr RenderFlags HAS_SHADOWING = 0x01;
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
static constexpr RenderFlags HAS_DEPTH_CLAMP = 0x08;
// Arena used for commands
using Arena = utils::Arena<
@@ -409,19 +408,17 @@ private:
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
static Command* resize(Arena& arena, Command* const last) noexcept;
void resize(Arena& arena, size_t count) noexcept;
// sorts commands then trims sentinels
static Command* sortCommands(
Command* begin, Command* end) noexcept;
void sortCommands(Arena& arena) noexcept;
// instanceify commands then trims sentinels
RenderPass::Command* instanceify(FEngine& engine,
Command* begin, Command* end,
int32_t eyeCount) const noexcept;
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
// We choose the command count per job to minimize JobSystem overhead.
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
// On a Pixel 4, 2048 commands is about half a millisecond of processing.
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 2048;
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_SIZE =
sizeof(Command) * JOBS_PARALLEL_FOR_COMMANDS_COUNT;
@@ -445,17 +442,17 @@ private:
uint8_t instancedStereoEyeCount) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
FScene::RenderableSoa const& mRenderableSoa;
backend::Viewport const mScissorViewport;
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
Command* mCommandBegin = nullptr; // Pointer to the first command
Command* mCommandEnd = nullptr; // Pointer to one past the last command
// a UBO for instanced primitives
mutable BufferObjectSharedHandle mInstancedUboHandle;
BufferObjectSharedHandle mInstancedUboHandle;
// a vector for our custom commands
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
@@ -529,9 +526,7 @@ public:
// like above but allows to set specific flags
RenderPassBuilder& renderFlags(
RenderPass::RenderFlags mask, RenderPass::RenderFlags value) noexcept {
value &= mask;
mFlags &= ~mask;
mFlags |= value;
mFlags = (mFlags & mask) | (value & mask);
return *this;
}
-6
View File
@@ -56,7 +56,6 @@ public:
backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
uint32_t getIndexOffset() const noexcept { return mIndexOffset; }
uint32_t getIndexCount() const noexcept { return mIndexCount; }
uint32_t getMorphingBufferOffset() const noexcept { return mMorphingBufferOffset; }
backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; }
AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; }
@@ -73,10 +72,6 @@ public:
mGlobalBlendOrderEnabled = enabled;
}
void setMorphingBufferOffset(uint32_t offset) noexcept {
mMorphingBufferOffset = offset;
}
private:
// These first fields are dereferences from PrimitiveInfo, keep them together
struct {
@@ -85,7 +80,6 @@ private:
backend::Handle<backend::HwVertexBufferInfo> mVertexBufferInfoHandle = {};
uint32_t mIndexOffset = 0;
uint32_t mIndexCount = 0;
uint32_t mMorphingBufferOffset = 0;
};
AttributeBitset mEnabledAttributes = {};
+12 -6
View File
@@ -158,14 +158,20 @@ void RenderableManager::setMorphWeights(Instance instance, float const* weights,
downcast(this)->setMorphWeights(instance, weights, count, offset);
}
void RenderableManager::setMorphTargetBufferOffsetAt(Instance instance, uint8_t level,
size_t primitiveIndex,
size_t offset) {
downcast(this)->setMorphTargetBufferOffsetAt(instance, level, primitiveIndex, offset);
void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
downcast(morphTargetBuffer), offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBuffer(Instance instance) const noexcept {
return downcast(this)->getMorphTargetBuffer(instance);
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) {
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
}
size_t RenderableManager::getMorphTargetCount(Instance instance) const noexcept {
+1 -23
View File
@@ -14,21 +14,11 @@
* limitations under the License.
*/
#include <filament/Renderer.h>
#include "ResourceAllocator.h"
#include "details/Renderer.h"
#include "details/Engine.h"
#include "details/Renderer.h"
#include "details/View.h"
#include <utils/FixedCapacityVector.h>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
using namespace math;
@@ -46,10 +36,6 @@ void Renderer::setPresentationTime(int64_t monotonic_clock_ns) {
downcast(this)->setPresentationTime(monotonic_clock_ns);
}
void Renderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
downcast(this)->skipFrame(vsyncSteadyClockTimeNano);
}
bool Renderer::beginFrame(SwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
return downcast(this)->beginFrame(downcast(swapChain), vsyncSteadyClockTimeNano);
}
@@ -107,12 +93,4 @@ void Renderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
downcast(this)->setVsyncTime(steadyClockTimeNano);
}
utils::FixedCapacityVector<Renderer::FrameInfo> Renderer::getFrameInfoHistory(size_t historySize) const noexcept {
return downcast(this)->getFrameInfoHistory(historySize);
}
size_t Renderer::getMaxFrameHistorySize() const noexcept {
return downcast(this)->getMaxFrameHistorySize();
}
} // namespace filament

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