Compare commits

..
20 Commits
Author SHA1 Message Date
Eliza Velasquez 0709f7d59a stuff 2024-03-06 15:02:15 -08:00
Eliza Velasquez 2027c9e237 stuff 2024-02-28 16:05:28 -08:00
Eliza Velasquez 86456c9e34 swizzle! struct indexing! 2024-02-27 17:04:25 -08:00
Eliza Velasquez fad58ba892 insert room for literal operand expressions 2024-02-27 15:39:40 -08:00
Eliza Velasquez 8ae296974c rename bunch of stuff 2024-02-27 14:31:32 -08:00
Eliza Velasquez f9575675d9 more global symbols 2024-02-27 12:54:41 -08:00
Eliza Velasquez 71025cf25a stringify, flesh out global symbols 2024-02-26 16:54:05 -08:00
Eliza Velasquez a067fb02b1 finish get_if 2024-02-26 14:37:14 -08:00
Eliza Velasquez ffde51ee6f continue eradicating get_if 2024-02-26 13:13:25 -08:00
Eliza Velasquez 35f99ed168 templatify 2024-02-23 15:07:18 -08:00
Eliza Velasquez a939ad03f8 stuff 2024-02-22 17:01:54 -08:00
Eliza Velasquez 05e628fff9 more codegen 2024-02-22 15:52:42 -08:00
Eliza Velasquez ad8473760a start literal value support 2024-02-21 17:09:01 -08:00
Eliza Velasquez cb672eb063 more rigorous glsl code gen 2024-02-21 16:04:20 -08:00
Eliza Velasquez 9811b785cb split locals and globals 2024-02-20 16:40:52 -08:00
Eliza Velasquez ff6d4d721e major refactor 2024-02-20 14:08:07 -08:00
Eliza Velasquez c8a5094c41 stuff 2024-02-16 15:03:53 -08:00
Eliza Velasquez 0f34603cfc stringify stuff 2024-02-14 17:11:24 -08:00
Eliza Velasquez 300f80448d test 2024-02-13 16:58:49 -08:00
Eliza Velasquez a41d343a75 wip: komi 2024-02-12 16:59:06 -08:00
293 changed files with 8007 additions and 9398 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
((c++-mode . ((eglot-ignored-server-capabilities . (:documentFormattingProvider))
(c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(apheleia-inhibit . t))))
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-android:
name: build-android
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
+1 -1
View File
@@ -13,7 +13,7 @@ on:
jobs:
cocoapods-deploy:
name: cocoapods-deploy
runs-on: macos-14
runs-on: macos-latest
steps:
- name: Check out iOS/CocoaPods directory
uses: Bhacaz/checkout-files@49fc3050859046bf4f4873678d46099985640e89
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-ios:
name: build-ios
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-mac:
name: build-mac
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs:
npm-deploy:
name: npm-deploy
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
with:
+4 -4
View File
@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [macos-14, ubuntu-22.04]
os: [macos-latest, ubuntu-22.04]
steps:
- uses: actions/checkout@v3.3.0
@@ -40,7 +40,7 @@ jobs:
build-android:
name: build-android
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
@@ -54,7 +54,7 @@ jobs:
build-ios:
name: build-iOS
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
@@ -67,7 +67,7 @@ jobs:
build-web:
name: build-web
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
+6 -6
View File
@@ -31,7 +31,7 @@ jobs:
strategy:
matrix:
os: [macos-14, ubuntu-22.04]
os: [macos-latest, ubuntu-22.04]
steps:
- name: Decide Git ref
@@ -65,7 +65,7 @@ jobs:
build-web:
name: build-web
runs-on: macos-14
runs-on: macos-latest
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps:
@@ -98,7 +98,7 @@ jobs:
build-android:
name: build-android
runs-on: macos-14
runs-on: macos-latest
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
steps:
@@ -129,7 +129,7 @@ jobs:
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
base64 --decode -i filament.jks.base64 > filament.jks
base64 --decode filament.jks.base64 > filament.jks
BUILD_TOOLS_VERSION=$(ls ${ANDROID_HOME}/build-tools | sort -V | tail -n 1)
APKSIGNER=${ANDROID_HOME}/build-tools/${BUILD_TOOLS_VERSION}/apksigner
IN_FILE="out/sample-gltf-viewer-release.apk"
@@ -152,7 +152,7 @@ jobs:
build-ios:
name: build-ios
runs-on: macos-14
runs-on: macos-latest
if: github.event_name == 'release' || github.event.inputs.platform == 'ios'
steps:
@@ -205,7 +205,7 @@ jobs:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
build\windows\build-github.bat release
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
move out\filament-windows.tgz out\filament-$Env:TAG-windows.tgz
shell: cmd
- uses: actions/github-script@v6
env:
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-web:
name: build-web
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
+2 -18
View File
@@ -45,8 +45,6 @@ option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
option(FILAMENT_ENABLE_MULTIVIEW "Enable multiview for Filament" OFF)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
@@ -533,21 +531,6 @@ else()
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" ON)
endif()
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
# The value can be either "instanced" or "multiview".
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING
"Stereoscopic type that shader files in gltfio and samples are built for."
)
string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE)
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ")
endif ()
# Compiling samples for multiview implies enabling multiview feature as well.
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
set(FILAMENT_ENABLE_MULTIVIEW ON)
endif ()
# ==================================================================================================
# Material compilation flags
# ==================================================================================================
@@ -723,7 +706,7 @@ function(get_resgen_vars ARCHIVE_DIR ARCHIVE_NAME)
set(RESGEN_OUTPUTS "${OUTPUTS}" PARENT_SCOPE)
set(RESGEN_FLAGS -qx ${ARCHIVE_DIR} -p ${ARCHIVE_NAME} PARENT_SCOPE)
set(RESGEN_SOURCE "${ARCHIVE_DIR}/${ARCHIVE_NAME}${ASM_SUFFIX}.S" PARENT_SCOPE)
set(RESGEN_SOURCE_FLAGS "-I'${ARCHIVE_DIR}' ${ASM_ARCH_FLAG}" PARENT_SCOPE)
set(RESGEN_SOURCE_FLAGS "-I${ARCHIVE_DIR} ${ASM_ARCH_FLAG}" PARENT_SCOPE)
endif()
endfunction()
@@ -774,6 +757,7 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
add_subdirectory(${EXTERNAL}/spirv-tools)
add_subdirectory(${EXTERNAL}/glslang/tnt)
add_subdirectory(${EXTERNAL}/spirv-cross/tnt)
add_subdirectory(${LIBRARIES}/astrict)
add_subdirectory(${LIBRARIES}/filamat)
# the material debugger requires filamat
-2
View File
@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.51.1'
implementation 'com.google.android.filament:filament-android:1.50.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.51.1'
pod 'Filament', '~> 1.50.0'
```
### Snapshots
-29
View File
@@ -7,35 +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.51.1
## v1.51.0
- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
## v1.50.6
- Add new API `SwapChain::getFrameScheduledCallback`
- vulkan: fixed validation error VUID-vkAcquireNextImageKHR-semaphore-01779
- opengl: Add support for protected content swapchains and contexts
## v1.50.5
- android: NDK 26.1.10909125 is used by default
- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1
- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance
- android: Change default frameRateOptions.interval to 1.0
## v1.50.4
## v1.50.3
## v1.50.2
## v1.50.1
- Metal: fix some shader artifacts by disabling fast math optimizations.
+1 -1
View File
@@ -80,7 +80,7 @@ buildscript {
ext.versions = [
'jdk': 17,
'minSdk': 21,
'minSdk': 19,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
@@ -391,13 +391,6 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
engine->flush();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
jlong nativeEngine, jboolean paused) {
Engine* engine = (Engine*) nativeEngine;
engine->setPaused(paused);
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -491,11 +484,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
jlong jobSystemThreadCount,
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
jint stereoscopicType, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
jboolean disableHandleUseAfterFreeCheck) {
jlong jobSystemThreadCount, jlong stereoscopicEyeCount) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -504,13 +493,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
.textureUseAfterFreePoolSize = (uint32_t) textureUseAfterFreePoolSize,
.disableParallelShaderCompile = (bool) disableParallelShaderCompile,
.stereoscopicType = (Engine::StereoscopicType) stereoscopicType,
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
};
builder->config(&config);
}
@@ -527,12 +510,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
builder->sharedContext((void*) sharedContext);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused(
JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->paused((bool) paused);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
@@ -34,15 +34,7 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(
JNIEnv *, jclass, jlong nativeEngine) {
Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(JNIEnv *, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)SwapChain::isSRGBSwapChainSupported(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
JNIEnv *, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)SwapChain::isProtectedContentSupported(*engine);
return (bool)SwapChain::isSRGBSwapChainSupported(*engine);
}
@@ -47,11 +47,6 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
return (jlong) new FilmicToneMapper();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*, jclass) {
return (jlong) new PBRNeutralToneMapper();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
@@ -158,16 +158,6 @@ public class Engine {
FEATURE_LEVEL_3,
};
/**
* The type of technique for stereoscopic rendering
*/
public enum StereoscopicType {
/** Stereoscopic rendering is performed using instanced rendering technique. */
INSTANCED,
/** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */
MULTIVIEW,
};
/**
* Constructs <code>Engine</code> objects using a builder pattern.
*/
@@ -221,11 +211,7 @@ public class Engine {
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
config.jobSystemThreadCount,
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
config.disableHandleUseAfterFreeCheck);
config.jobSystemThreadCount, config.stereoscopicEyeCount);
return this;
}
@@ -240,18 +226,6 @@ public class Engine {
return this;
}
/**
* Sets the initial paused state of the rendering thread.
*
* @param paused Whether to start the rendering thread paused.
* @return A reference to this Builder for chaining calls.
* @warning Experimental.
*/
public Builder paused(boolean paused) {
nSetBuilderPaused(mNativeBuilder, paused);
return this;
}
/**
* Creates an instance of Engine
*
@@ -374,35 +348,6 @@ public class Engine {
*/
public long jobSystemThreadCount = 0;
/**
* Number of most-recently destroyed textures to track for use-after-free.
*
* This will cause the backend to throw an exception when a texture is freed but still bound
* to a SamplerGroup and used in a draw call. 0 disables completely.
*
* Currently only respected by the Metal backend.
*/
public long textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
public boolean disableParallelShaderCompile = false;
/**
* The type of technique for stereoscopic rendering.
*
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
* Views created from the Engine must use the same stereoscopic type.
*
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
*
* @see View#setStereoscopicOptions
*/
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine#getMaxStereoscopicEyes() (inclusive).
@@ -411,21 +356,6 @@ public class Engine {
* @see Engine#getMaxStereoscopicEyes
*/
public long stereoscopicEyeCount = 2;
/*
* @Deprecated This value is no longer used.
*/
public long resourceAllocatorCacheSizeMB = 64;
/*
* This value determines for how many frames are texture entries kept in the cache.
*/
public long resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
*/
public boolean disableHandleUseAfterFreeCheck = false;
}
private Engine(long nativeEngine, Config config) {
@@ -672,7 +602,7 @@ public class Engine {
*/
@NonNull
public SwapChain createSwapChain(@NonNull Object surface) {
return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT);
return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
}
/**
@@ -680,15 +610,15 @@ public class Engine {
*
* @param surface on Android, <b>must be</b> an instance of {@link android.view.Surface}
*
* @param flags configuration flags, see {@link SwapChainFlags}
* @param flags configuration flags, see {@link SwapChain}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
*
*/
@NonNull
@@ -706,22 +636,21 @@ public class Engine {
*
* @param width width of the rendering buffer
* @param height height of the rendering buffer
* @param flags configuration flags, see {@link SwapChainFlags}
* @param flags configuration flags, see {@link SwapChain}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
*
*/
@NonNull
public SwapChain createSwapChain(int width, int height, long flags) {
if (width >= 0 && height >= 0) {
long nativeSwapChain =
nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
return new SwapChain(nativeSwapChain, null);
}
@@ -733,12 +662,11 @@ public class Engine {
*
* @param surface a properly initialized {@link NativeSurface}
*
* @param flags configuration flags, see {@link SwapChainFlags}
* @param flags configuration flags, see {@link SwapChain}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be
* created
* @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created
*/
@NonNull
public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) {
@@ -1207,13 +1135,6 @@ public class Engine {
nFlush(getNativeObject());
}
/**
* Pause or resume the rendering thread.
* @warning Experimental.
*/
public void setPaused(boolean paused) {
nSetPaused(getNativeObject(), paused);
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
@@ -1288,7 +1209,6 @@ public class Engine {
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native void nFlush(long nativeEngine);
private static native void nSetPaused(long nativeEngine, boolean paused);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
@@ -1307,12 +1227,8 @@ public class Engine {
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
int stereoscopicType, long stereoscopicEyeCount,
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
boolean disableHandleUseAfterFreeCheck);
long stereoscopicEyeCount);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
private static native long nBuilderBuild(long nativeBuilder);
}
@@ -101,7 +101,7 @@ public class Renderer {
/**
* Desired frame interval in unit of 1 / DisplayInfo.refreshRate.
*/
public float interval = 1.0f;
public float interval = 1.0f / 60.0f;
/**
* Additional headroom for the GPU as a ratio of the targetFrameTime.
@@ -68,30 +68,77 @@ public class SwapChain {
private final Object mSurface;
private long mNativeObject;
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
}
/**
* Return whether createSwapChain supports the CONFIG_PROTECTED_CONTENT flag.
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
* @see SwapChainFlags#CONFIG_PROTECTED_CONTENT
*/
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
return nIsProtectedContentSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise.
* @see SwapChainFlags#CONFIG_SRGB_COLORSPACE
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
@@ -139,5 +186,4 @@ public class SwapChain {
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
private static native boolean nIsProtectedContentSupported(long nativeEngine);
}
@@ -1,97 +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.
*/
package com.google.android.filament;
// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend
// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java
// classes.
/**
* Flags that a <code>SwapChain</code> can be created with to control behavior.
*
* @see Engine#createSwapChain
* @see Engine#createSwapChainFromNativeSurface
*/
public final class SwapChainFlags {
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
/**
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
}
@@ -16,14 +16,12 @@ package com.google.android.filament;
* <li>Configurable tone mapping operators</li>
* <ul>
* <li>GenericToneMapper</li>
* <li>AgXToneMapper</li>
* </ul>
* <li>Fixed-aesthetic tone mapping operators</li>
* <ul>
* <li>ACESToneMapper</li>
* <li>ACESLegacyToneMapper</li>
* <li>FilmicToneMapper</li>
* <li>PBRNeutralToneMapper</li>
* </ul>
* <li>Debug/validation tone mapping operators</li>
* <ul>
@@ -102,21 +100,11 @@ public class ToneMapper {
}
}
/**
* Khronos PBR Neutral tone mapping operator. This tone mapper was designed
* to preserve the appearance of materials across lighting conditions while
* avoiding artifacts in the highlights in high dynamic range conditions.
*/
public static class PBRNeutralToneMapper extends ToneMapper {
public PBRNeutralToneMapper() {
super(nCreatePBRNeutralToneMapper());
}
}
/**
* AgX tone mapping operator.
*/
public static class Agx extends ToneMapper {
public enum AgxLook {
/**
* Base contrast with no look applied
@@ -245,7 +233,6 @@ public class ToneMapper {
private static native long nCreateACESToneMapper();
private static native long nCreateACESLegacyToneMapper();
private static native long nCreateFilmicToneMapper();
private static native long nCreatePBRNeutralToneMapper();
private static native long nCreateAgxToneMapper(int look);
private static native long nCreateGenericToneMapper(
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
@@ -27,7 +27,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.filament.SwapChainFlags;
import com.google.android.filament.SwapChain;
/**
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
@@ -538,7 +538,7 @@ public class UiHelper {
* the options set on this UiHelper.
*/
public long getSwapChainFlags() {
return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT;
return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
}
/**
-8
View File
@@ -81,17 +81,9 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/TangentsJob.cpp
${GLTFIO_DIR}/src/TangentsJob.h
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Utility.cpp
${GLTFIO_DIR}/src/Utility.h
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/downcast.h
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.51.1
VERSION_NAME=1.50.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -30,7 +30,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.gltf"
minSdkVersion versions.minSdk
minSdkVersion 19
targetSdkVersion versions.targetSdk
}
+1 -1
View File
@@ -1 +1 @@
26.1.10909125
25.1.8937393
+1 -7
View File
@@ -125,13 +125,7 @@ if [[ "${has_universal}" == "true" ]]; then
arch_output="${OUTPUT_PATH%.a}_${arch}.a"
arch_outputs+=("$arch_output")
archives=()
while IFS= read -r -d $'\0'; do
archives+=("$REPLY")
done < <(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a' -print0)
combine_static_libs "$arch_output" "$archives"
combine_static_libs "$arch_output" $(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a')
done
# Finally, combine the single-architecture archives into a universal binary.
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH armv7a-linux-androideabi)
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH i686-linux-android)
+21 -44
View File
@@ -66,7 +66,6 @@ set(SRCS
src/Froxelizer.cpp
src/Frustum.cpp
src/HwRenderPrimitiveFactory.cpp
src/HwVertexBufferInfoFactory.cpp
src/IndexBuffer.cpp
src/IndirectLight.cpp
src/InstanceBuffer.cpp
@@ -138,7 +137,6 @@ set(SRCS
set(PRIVATE_HDRS
src/Allocators.h
src/Bimap.h
src/BufferPoolAllocator.h
src/ColorSpaceUtils.h
src/Culler.h
@@ -149,7 +147,6 @@ set(PRIVATE_HDRS
src/FrameSkipper.h
src/Froxelizer.h
src/HwRenderPrimitiveFactory.h
src/HwVertexBufferInfoFactory.h
src/Intersections.h
src/MaterialParser.h
src/PerViewUniforms.h
@@ -215,7 +212,6 @@ set(MATERIAL_SRCS
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/blitLow.mat
src/materials/blitArray.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
@@ -252,13 +248,8 @@ set(MATERIAL_SRCS
)
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial.mat
src/materials/skybox.mat
)
set(MATERIAL_MULTIVIEW_SRCS
src/materials/defaultMaterial.mat
src/materials/skybox.mat
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
# Embed the binary resource blob for materials.
@@ -290,11 +281,6 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
endif()
# Whether to include MULTIVIEW materials.
if (FILAMENT_ENABLE_MULTIVIEW)
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -325,43 +311,34 @@ foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${fullname} to ${output_path}"
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
string(REGEX REPLACE "[.]filamat$" "_fl0.filamat" output_path_fl0 ${output_path})
add_custom_command(
OUTPUT ${output_path_fl0}
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path_fl0} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${fullname} to ${output_path_fl0}"
)
list(APPEND MATERIAL_BINS ${output_path_fl0})
endif ()
list(FIND MATERIAL_MULTIVIEW_SRCS ${mat_src} index)
if (${index} GREATER -1 AND FILAMENT_ENABLE_MULTIVIEW)
string(REGEX REPLACE "[.]filamat$" "_multiview.filamat" output_path_multiview ${output_path})
add_custom_command(
OUTPUT ${output_path_multiview}
COMMAND matc ${MATC_BASE_FLAGS} -PstereoscopicType=multiview -o ${output_path_multiview} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${fullname} to ${output_path_multiview}"
)
list(APPEND MATERIAL_BINS ${output_path_multiview})
endif ()
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
foreach (mat_src ${MATERIAL_FL0_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach ()
endif ()
# Additional dependencies on included files for materials
add_custom_command(
-2
View File
@@ -166,8 +166,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/caching/VulkanDescriptorSet.cpp
src/vulkan/caching/VulkanDescriptorSet.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
+13 -51
View File
@@ -81,14 +81,7 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that the SwapChain should also contain a stencil component.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER = 0x20;
static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
/**
* The SwapChain contains protected content. Currently only supported by OpenGLPlatform and
* only when OpenGLPlatform::isProtectedContextSupported() is true.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = 0x20;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
@@ -136,12 +129,6 @@ enum class Backend : uint8_t {
NOOP = 4, //!< Selects the no-op driver for testing purposes.
};
enum class TimerQueryResult : int8_t {
ERROR = -1, // an error occurred, result won't be available
NOT_READY = 0, // result to ready yet
AVAILABLE = 1, // result is available
};
static constexpr const char* backendToString(Backend backend) {
switch (backend) {
case Backend::NOOP:
@@ -672,17 +659,16 @@ enum class TextureFormat : uint16_t {
};
//! Bitmask describing the intended Texture Usage
enum class TextureUsage : uint16_t {
NONE = 0x0000,
COLOR_ATTACHMENT = 0x0001, //!< Texture can be used as a color attachment
DEPTH_ATTACHMENT = 0x0002, //!< Texture can be used as a depth attachment
STENCIL_ATTACHMENT = 0x0004, //!< Texture can be used as a stencil attachment
UPLOADABLE = 0x0008, //!< Data can be uploaded into this texture (default)
SAMPLEABLE = 0x0010, //!< Texture can be sampled (default)
SUBPASS_INPUT = 0x0020, //!< Texture can be used as a subpass input
BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit()
BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit()
PROTECTED = 0x0100, //!< Texture can be used the destination of a blit()
enum class TextureUsage : uint8_t {
NONE = 0x00,
COLOR_ATTACHMENT = 0x01, //!< Texture can be used as a color attachment
DEPTH_ATTACHMENT = 0x02, //!< Texture can be used as a depth attachment
STENCIL_ATTACHMENT = 0x04, //!< Texture can be used as a stencil attachment
UPLOADABLE = 0x08, //!< Data can be uploaded into this texture (default)
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
BLIT_SRC = 0x40, //!< Texture can be used the source of a blit()
BLIT_DST = 0x80, //!< Texture can be used the destination of a blit()
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
};
@@ -1186,27 +1172,11 @@ struct StencilState {
//! Stencil operations for front-facing polygons
StencilOperations front = {
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
//! Stencil operations for back-facing polygons
StencilOperations back = {
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
//! Whether stencil-buffer writes are enabled
bool stencilWrite = false;
@@ -1242,14 +1212,6 @@ enum class Workaround : uint16_t {
DISABLE_THREAD_AFFINITY
};
//! The type of technique for stereoscopic rendering
enum class StereoscopicType : uint8_t {
// Stereoscopic rendering is performed using instanced rendering technique.
INSTANCED,
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
MULTIVIEW,
};
} // namespace filament::backend
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
+20 -22
View File
@@ -39,7 +39,6 @@ struct HwStream;
struct HwSwapChain;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
struct HwVertexBuffer;
/*
@@ -63,6 +62,14 @@ public:
// clear the handle, this doesn't free associated resources
void clear() noexcept { object = nullid; }
// compare handles
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; }
bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; }
bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; }
bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; }
// get this handle's handleId
HandleId getId() const noexcept { return object; }
@@ -94,14 +101,6 @@ struct Handle : public HandleBase {
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
// compare handles of the same type
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
// type-safe Handle cast
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
@@ -115,19 +114,18 @@ private:
// Types used by the command stream
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
} // namespace filament::backend
@@ -29,13 +29,11 @@ namespace filament::backend {
//! \privatesection
struct PipelineState {
Handle<HwProgram> program; // 4
Handle<HwVertexBufferInfo> vertexBufferInfo; // 4
RasterState rasterState; // 4
StencilState stencilState; // 12
PolygonOffset polygonOffset; // 8
PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
uint8_t padding[3] = {}; // 3
Handle<HwProgram> program;
RasterState rasterState;
StencilState stencilState;
PolygonOffset polygonOffset;
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
};
} // namespace filament::backend
+6 -47
View File
@@ -23,7 +23,6 @@
#include <utils/Invocable.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -57,14 +56,9 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
* Currently only honored by the GL backend.
*/
bool disableParallelShaderCompile = false;
/**
* Disable backend handles use-after-free checks.
*/
bool disableHandleUseAfterFreeCheck = false;
};
Platform() noexcept;
@@ -90,7 +84,7 @@ public:
*
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
virtual backend::Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) noexcept = 0;
/**
@@ -108,8 +102,7 @@ public:
* cache.
*/
using InsertBlobFunc = utils::Invocable<
void(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize)>;
void(const void* key, size_t keySize, const void* value, size_t valueSize)>;
/*
* RetrieveBlobFunc is an Invocable to an application-provided function that a
@@ -117,8 +110,7 @@ public:
* cache.
*/
using RetrieveBlobFunc = utils::Invocable<
size_t(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize)>;
size_t(const void* key, size_t keySize, void* value, size_t valueSize)>;
/**
* Sets the callback functions that the backend can use to interact with caching functionality
@@ -171,8 +163,7 @@ public:
* @param value pointer to the beginning of the value data that is to be inserted
* @param valueSize specifies the size in byte of the data pointed to by <value>
*/
void insertBlob(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize);
void insertBlob(const void* key, size_t keySize, const void* value, size_t valueSize);
/**
* To retrieve the binary value associated with a given key from the cache, a
@@ -191,43 +182,11 @@ public:
* @return If the cache contains a value associated with the given key then the
* size of that binary value in bytes is returned. Otherwise 0 is returned.
*/
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
/**
* Sets the callback function that the backend can use to update backend-specific statistics
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
* thread.
*
* @param debugUpdateStat an Invocable that updates debug statistics
*/
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
/**
* @return true if debugUpdateStat is valid.
*/
bool hasDebugUpdateStatFunc() const noexcept;
/**
* To track backend-specific statistics, the backend implementation can call the
* application-provided callback function debugUpdateStatFunc to associate or update a value
* with a given key. It is possible for this function to be called multiple times with the
* same key, in which case newer values should overwrite older values.
*
* This function is guaranteed to be called only on a single thread, the Filament driver
* thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param value the updated value of key
*/
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
size_t retrieveBlob(const void* key, size_t keySize, void* value, size_t valueSize);
private:
InsertBlobFunc mInsertBlob;
RetrieveBlobFunc mRetrieveBlob;
DebugUpdateStatFunc mDebugUpdateStat;
};
} // namespace filament
@@ -32,10 +32,6 @@ struct TargetBufferInfo {
// texture to be used as render target
Handle<HwTexture> handle;
// starting layer index for multiview. This value is only used when the `layerCount` for the
// render target is greater than 1.
uint8_t baseViewIndex = 0;
// level to be used
uint8_t level = 0;
@@ -84,7 +80,7 @@ public:
// this is here for backward compatibility
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: mInfos{{ handle, 0, level, layer }} {
: mInfos{{ handle, level, layer }} {
}
};
@@ -21,10 +21,6 @@
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -45,8 +41,8 @@ protected:
* Derived classes can use this to instantiate the default OpenGLDriver backend.
* This is typically called from your implementation of createDriver()
*/
static Driver* UTILS_NULLABLE createDefaultDriver(OpenGLPlatform* UTILS_NONNULL platform,
void* UTILS_NULLABLE sharedContext, const DriverConfig& driverConfig);
static Driver* createDefaultDriver(OpenGLPlatform* platform,
void* sharedContext, const DriverConfig& driverConfig);
~OpenGLPlatform() noexcept override;
@@ -64,22 +60,6 @@ public:
*/
virtual void terminate() noexcept = 0;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Return whether protected contexts are supported by this backend.
* If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be
* used when creating a SwapChain.
* The default implementation returns false.
*/
virtual bool isProtectedContextSupported() const noexcept;
/**
* Called by the driver to create a SwapChain for this driver.
*
@@ -89,8 +69,15 @@ public:
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept = 0;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Called by the driver create a headless SwapChain.
@@ -103,14 +90,13 @@ public:
* TODO: we need a more generic way of passing construction parameters
* A void* might be enough.
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
/**
* Called by the driver to destroys the SwapChain
* @param swapChain SwapChain to be destroyed.
*/
virtual void destroySwapChain(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
/**
* Returns the set of buffers that must be preserved up to the call to commit().
@@ -123,80 +109,28 @@ public:
* @return buffer that must be preserved
* @see commit()
*/
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Returns true if the swapchain is protected
*/
virtual bool isSwapChainProtected(Platform::SwapChain* UTILS_NONNULL swapChain) noexcept;
virtual TargetBufferFlags getPreservedFlags(SwapChain* swapChain) noexcept;
/**
* Called by the driver to establish the default FBO. The default implementation returns 0.
*
* This method can be called either on the regular or protected OpenGL contexts and can return
* a different or identical name, since these names exist in different namespaces.
*
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
*/
virtual uint32_t getDefaultFramebufferObject() noexcept;
/**
* Type of contexts available
*/
enum class ContextType {
NONE, //!< No current context
UNPROTECTED, //!< current context is unprotected
PROTECTED //!< current context supports protected content
};
/**
* Returns the type of the context currently in use. This value is updated by makeCurrent()
* and therefore can be cached between calls. ContextType::PROTECTED can only be returned
* if isProtectedContextSupported() is true.
* @return ContextType
*/
virtual ContextType getCurrentContextType() const noexcept;
/**
* Binds the requested context to the current thread and drawSwapChain to the default FBO
* returned by getDefaultFramebufferObject().
*
* @param type type of context to bind to the current thread.
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
* @return true on success, false on error.
*/
virtual bool makeCurrent(ContextType type,
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
virtual uint32_t createDefaultRenderTarget() noexcept;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
* the drawSwapChain to the default FBO returned by getDefaultFramebufferObject().
* The context used is either the default context or the protected context. When a context
* change is necessary, the preContextChange and postContextChange callbacks are called,
* before and after the context change respectively. postContextChange is given the index
* of the new context (0 for default and 1 for protected).
* The default implementation just calls makeCurrent(getCurrentContextType(), SwapChain*, SwapChain*).
*
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
* @param preContextChange called before the context changes
* @param postContextChange called after the context changes
*/
virtual void makeCurrent(
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept;
virtual void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept = 0;
/**
* Called by the driver once the current frame finishes drawing. Typically, this should present
* the drawSwapChain. This is for example where `eglMakeCurrent()` would be called.
* @param swapChain the SwapChain to present.
*/
virtual void commit(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
virtual void commit(SwapChain* swapChain) noexcept = 0;
/**
* Set the time the next committed buffer should be presented to the user at.
@@ -221,14 +155,14 @@ public:
*
* @return A Fence object. The default implementation returns nullptr.
*/
virtual Fence* UTILS_NULLABLE createFence() noexcept;
virtual Fence* createFence() noexcept;
/**
* Destroys a Fence object. The default implementation does nothing.
*
* @param fence Fence to destroy.
*/
virtual void destroyFence(Fence* UTILS_NONNULL fence) noexcept;
virtual void destroyFence(Fence* fence) noexcept;
/**
* Waits on a Fence.
@@ -238,7 +172,7 @@ public:
* @return Whether the fence signaled or timed out. See backend::FenceStatus.
* The default implementation always return backend::FenceStatus::ERROR.
*/
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
virtual backend::FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
@@ -252,13 +186,13 @@ public:
* @param nativeStream The native stream, this parameter depends on the concrete implementation.
* @return A new Stream object.
*/
virtual Stream* UTILS_NULLABLE createStream(void* UTILS_NULLABLE nativeStream) noexcept;
virtual Stream* createStream(void* nativeStream) noexcept;
/**
* Destroys a Stream.
* @param stream Stream to destroy.
*/
virtual void destroyStream(Stream* UTILS_NONNULL stream) noexcept;
virtual void destroyStream(Stream* stream) noexcept;
/**
* The specified stream takes ownership of the texture (tname) object
@@ -268,21 +202,20 @@ public:
* @param stream Stream to take ownership of the texture
* @param tname GL texture id to "bind" to the Stream.
*/
virtual void attach(Stream* UTILS_NONNULL stream, intptr_t tname) noexcept;
virtual void attach(Stream* stream, intptr_t tname) noexcept;
/**
* Destroys the texture associated to the stream
* @param stream Stream to detach from its texture
*/
virtual void detach(Stream* UTILS_NONNULL stream) noexcept;
virtual void detach(Stream* stream) noexcept;
/**
* Updates the content of the texture attached to the stream.
* @param stream Stream to update
* @param timestamp Output parameter: Timestamp of the image bound to the texture.
*/
virtual void updateTexImage(Stream* UTILS_NONNULL stream,
int64_t* UTILS_NONNULL timestamp) noexcept;
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept;
// --------------------------------------------------------------------------------------------
@@ -295,13 +228,13 @@ public:
* implementation could just return { 0, GL_TEXTURE_2D } at this point. The actual
* values can be delayed until setExternalImage.
*/
virtual ExternalTexture* UTILS_NULLABLE createExternalImageTexture() noexcept;
virtual ExternalTexture *createExternalImageTexture() noexcept;
/**
* Destroys an external texture handle and associated data.
* @param texture a pointer to the handle to destroy.
*/
virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept;
virtual void destroyExternalImage(ExternalTexture* texture) noexcept;
// called on the application thread to allow Filament to take ownership of the image
@@ -314,7 +247,7 @@ public:
* @param externalImage A token representing the platform's external image.
* @see destroyExternalImage
*/
virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept;
virtual void retainExternalImage(void* externalImage) noexcept;
/**
* Called to bind the platform-specific externalImage to an ExternalTexture.
@@ -328,8 +261,7 @@ public:
* @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary.
* @return true on success, false on error.
*/
virtual bool setExternalImage(void* UTILS_NONNULL externalImage,
ExternalTexture* UTILS_NONNULL texture) noexcept;
virtual bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept;
/**
* The method allows platforms to convert a user-supplied external image object into a new type
@@ -57,7 +57,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImage(ExternalTexture* texture) noexcept override;
@@ -45,7 +45,7 @@ public:
void terminate() noexcept override;
uint32_t getDefaultFramebufferObject() noexcept override;
uint32_t createDefaultRenderTarget() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
@@ -53,7 +53,7 @@ public:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
@@ -25,8 +25,6 @@
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <utils/Invocable.h>
#include <initializer_list>
#include <utility>
#include <vector>
@@ -43,11 +41,15 @@ class PlatformEGL : public OpenGLPlatform {
public:
PlatformEGL() noexcept;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
// Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default.
virtual bool isOpenGL() const noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Helper for EGL configs and attributes parameters
@@ -87,30 +89,13 @@ protected:
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
void terminate() noexcept override;
bool isProtectedContextSupported() const noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
ContextType getCurrentContextType() const noexcept override;
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
bool canCreateFence() noexcept override;
@@ -137,27 +122,16 @@ protected:
static void clearGlError() noexcept;
/**
* Always use this instead of eglMakeCurrent(), as it tracks some state.
* Always use this instead of eglMakeCurrent().
*/
EGLContext getContextForType(ContextType type) const noexcept;
// makes the draw and read surface current without changing the current context
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return egl.makeCurrent(drawSurface, readSurface);
}
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
EGLBoolean makeCurrent(EGLContext context) noexcept {
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept;
// TODO: this should probably use getters instead.
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
@@ -174,38 +148,13 @@ protected:
bool KHR_gl_colorspace = false;
bool KHR_no_config_context = false;
bool KHR_surfaceless_context = false;
bool EXT_protected_content = false;
} egl;
} ext;
struct SwapChainEGL : public Platform::SwapChain {
EGLSurface sur = EGL_NO_SURFACE;
Config attribs{};
EGLNativeWindowType nativeWindow{};
EGLConfig config{};
uint64_t flags{};
};
void initializeGlExtensions() noexcept;
protected:
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
private:
class EGL {
EGLDisplay& mEGLDisplay;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLContext mCurrentContext = EGL_NO_CONTEXT;
public:
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
} egl{ mEGLDisplay };
};
} // namespace filament::backend
@@ -51,7 +51,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
private:
@@ -53,7 +53,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
protected:
@@ -46,7 +46,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
};
@@ -17,10 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#include <utils/debug.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -40,36 +37,28 @@ public:
~CircularBuffer() noexcept;
static size_t getBlockSize() noexcept { return sPageSize; }
// Total size of circular buffer. This is a constant.
size_t size() const noexcept { return mSize; }
// Allocates `s` bytes in the circular buffer and returns a pointer to the memory. All
// allocations must not exceed size() bytes.
inline void* allocate(size_t s) noexcept {
// We can never allocate more that size().
assert_invariant(getUsed() + s <= size());
// allocates 'size' bytes in the circular buffer and returns a pointer to the memory
// return the current head and moves it forward by size bytes
inline void* allocate(size_t size) noexcept {
char* const cur = static_cast<char*>(mHead);
mHead = cur + s;
mHead = cur + size;
return cur;
}
// Returns true if the buffer is empty, i.e.: no allocations were made since
// calling getBuffer();
// Total size of circular buffer
size_t size() const noexcept { return mSize; }
// returns true if the buffer is empty (e.g. after calling flush)
bool empty() const noexcept { return mTail == mHead; }
// Returns the size used since the last call to getBuffer()
size_t getUsed() const noexcept { return intptr_t(mHead) - intptr_t(mTail); }
void* getHead() const noexcept { return mHead; }
// Retrieves the current allocated range and frees it. It is the responsibility of the caller
// to make sure the returned range is no longer in use by the time allocate() allocates
// (size() - getUsed()) bytes.
struct Range {
void* tail;
void* head;
};
Range getBuffer() noexcept;
void* getTail() const noexcept { return mTail; }
// call at least once every getRequiredSize() bytes allocated from the buffer
void circularize() noexcept;
static size_t getBlockSize() noexcept { return sPageSize; }
private:
void* alloc(size_t size) noexcept;
@@ -77,10 +66,10 @@ private:
// pointer to the beginning of the circular buffer (constant)
void* mData = nullptr;
int mAshmemFd = -1;
int mUsesAshmem = -1;
// size of the circular buffer (constant)
size_t const mSize;
size_t mSize = 0;
// pointer to the beginning of recorded data
void* mTail = nullptr;
@@ -33,7 +33,7 @@ namespace filament::backend {
* A producer-consumer command queue that uses a CircularBuffer as main storage
*/
class CommandBufferQueue {
struct Range {
struct Slice {
void* begin;
void* end;
};
@@ -46,33 +46,29 @@ class CommandBufferQueue {
mutable utils::Mutex mLock;
mutable utils::Condition mCondition;
mutable std::vector<Range> mCommandBuffersToExecute;
mutable std::vector<Slice> mCommandBuffersToExecute;
size_t mFreeSpace = 0;
size_t mHighWatermark = 0;
uint32_t mExitRequested = 0;
bool mPaused = false;
static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
public:
// requiredSize: guaranteed available space after flush()
CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused);
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
~CommandBufferQueue();
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
CircularBuffer const& getCircularBuffer() const noexcept { return mCircularBuffer; }
size_t getCapacity() const noexcept { return mRequiredSize; }
CircularBuffer& getCircularBuffer() { return mCircularBuffer; }
size_t getHighWatermark() const noexcept { return mHighWatermark; }
// wait for commands to be available and returns an array containing these commands
std::vector<Range> waitForCommands() const;
std::vector<Slice> waitForCommands() const;
// return the memory used by this command buffer to the circular buffer
// WARNING: releaseBuffer() must be called in sequence of the Slices returned by
// waitForCommands()
void releaseBuffer(Range const& buffer);
void releaseBuffer(Slice const& buffer);
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
@@ -81,9 +77,6 @@ public:
// returns from waitForCommands() immediately.
void requestExit();
// suspend or unsuspend the queue.
void setPaused(bool paused);
bool isExitRequested() const;
};
@@ -213,8 +213,6 @@ public:
CommandStream(CommandStream const& rhs) noexcept = delete;
CommandStream& operator=(CommandStream const& rhs) noexcept = delete;
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
public:
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
inline void methodName(paramsDecl) { \
@@ -167,14 +167,11 @@ DECL_DRIVER_API_0(resetState)
* -----------------------
*/
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint8_t, bufferCount,
uint8_t, attributeCount,
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint32_t, vertexCount,
backend::VertexBufferInfoHandle, vbih)
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
backend::ElementType, elementType,
@@ -227,7 +224,11 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
backend::VertexBufferHandle, vbh,
backend::IndexBufferHandle, ibh,
backend::PrimitiveType, pt)
backend::PrimitiveType, pt,
uint32_t, offset,
uint32_t, minIndex,
uint32_t, maxIndex,
uint32_t, count)
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
backend::Program&&, program)
@@ -239,7 +240,6 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
uint32_t, width,
uint32_t, height,
uint8_t, samples,
uint8_t, layerCount,
backend::MRT, color,
backend::TargetBufferInfo, depth,
backend::TargetBufferInfo, stencil)
@@ -264,7 +264,6 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
*/
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih)
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
@@ -299,16 +298,14 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
@@ -494,32 +491,15 @@ DECL_DRIVER_API_N(blit,
math::uint2, srcOrigin,
math::uint2, size)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(draw2,
uint32_t, indexOffset,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,
uint32_t, indexOffset,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
DECL_DRIVER_API_N(scissor,
Viewport, scissor)
#pragma clang diagnostic pop
@@ -24,32 +24,36 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
#include <cstddef>
#include <exception>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
#if !defined(NDEBUG) && UTILS_HAS_RTTI
# define HANDLE_TYPE_SAFETY 1
#else
# define HANDLE_TYPE_SAFETY 0
#endif
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
namespace filament::backend {
/*
* A utility class to efficiently allocate and manage Handle<>
*/
template<size_t P0, size_t P1, size_t P2>
template <size_t P0, size_t P1, size_t P2>
class HandleAllocator {
public:
HandleAllocator(const char* name, size_t size, bool disableUseAfterFreeCheck) noexcept;
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
~HandleAllocator();
@@ -66,9 +70,14 @@ public:
*/
template<typename D, typename ... ARGS>
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
D* addr = handle_cast<D*>(h);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -84,7 +93,13 @@ public:
*/
template<typename D>
Handle<D> allocate() noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
#if HANDLE_TYPE_SAFETY
D* addr = handle_cast<D*>(h);
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -101,10 +116,17 @@ public:
assert_invariant(handle);
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
// currently we implement construct<> with dtor+ctor, we could use operator= also
// but all our dtors are trivial, ~D() is actually a noop.
addr->~D();
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -121,6 +143,12 @@ public:
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -136,8 +164,19 @@ public:
void deallocate(Handle<B>& handle, D const* p) noexcept {
// allow to destroy the nullptr, similarly to operator delete
if (p) {
#if HANDLE_TYPE_SAFETY
mLock.lock();
auto typeId = mHandleTypeId[p];
mHandleTypeId.erase(p);
mLock.unlock();
if (UTILS_UNLIKELY(typeId != typeid(D).name())) {
utils::slog.e << "Destroying handle " << handle.getId() << ", type " << typeid(D).name()
<< ", but handle's actual type is " << typeId << utils::io::endl;
std::terminate();
}
#endif
p->~D();
deallocateHandle<D>(handle.getId());
deallocateHandle<sizeof(D)>(handle.getId());
}
}
@@ -165,19 +204,7 @@ public:
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B>& handle) noexcept {
assert_invariant(handle);
auto [p, tag] = handleToPointer(handle.getId());
if (isPoolHandle(handle.getId())) {
// check for use after free
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
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;
ASSERT_POSTCONDITION(expectedAge == age,
"use-after-free of Handle with id=%d", handle.getId());
}
}
void* const p = handleToPointer(handle.getId());
return static_cast<Dp>(p);
}
@@ -189,62 +216,32 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
private:
template<typename D>
static constexpr size_t getBucketSize() noexcept {
if constexpr (sizeof(D) <= P0) { return P0; }
if constexpr (sizeof(D) <= P1) { return P1; }
static_assert(sizeof(D) <= P2);
return P2;
}
// template <int P0, int P1, int P2>
class Allocator {
friend class HandleAllocator;
static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
struct Node { uint8_t age; };
// Note: using the `extra` parameter of PoolAllocator<>, even with a 1-byte structure,
// generally increases all pool allocations by 8-bytes because of alignment restrictions.
template<size_t SIZE>
using Pool = utils::PoolAllocator<SIZE, MIN_ALIGNMENT, sizeof(Node)>;
Pool<P0> mPool0;
Pool<P1> mPool1;
Pool<P2> mPool2;
utils::PoolAllocator<P0, 16> mPool0;
utils::PoolAllocator<P1, 16> mPool1;
utils::PoolAllocator<P2, 16> mPool2;
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
bool mUseAfterFreeCheckDisabled;
public:
explicit Allocator(const utils::AreaPolicy::HeapArea& area, bool disableUseAfterFreeCheck);
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
// this is in fact always called with a constexpr size argument
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t, uint8_t* outAge) noexcept {
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t extra) noexcept {
void* p = nullptr;
if (size <= mPool0.getSize()) p = mPool0.alloc(size);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size);
if (UTILS_LIKELY(p)) {
Node const* const pNode = static_cast<Node const*>(p);
// we are guaranteed to have at least sizeof<Node> bytes of extra storage before
// the allocation address.
*outAge = pNode[-1].age;
}
if (size <= mPool0.getSize()) p = mPool0.alloc(size, 16, extra);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size, 16, extra);
return p;
}
// this is in fact always called with a constexpr size argument
inline void free(void* p, size_t size, uint8_t age) noexcept {
inline void free(void* p, size_t size) noexcept {
assert_invariant(p >= mArea.begin() && (char*)p + size <= (char*)mArea.end());
// check for double-free
Node* const pNode = static_cast<Node*>(p);
uint8_t& expectedAge = pNode[-1].age;
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
ASSERT_POSTCONDITION(expectedAge == age,
"double-free of Handle of size %d at %p", size, p);
}
expectedAge = (expectedAge + 1) & 0xF; // fixme
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
if (size <= mPool1.getSize()) { mPool1.free(p); return; }
if (size <= mPool2.getSize()) { mPool2.free(p); return; }
@@ -266,16 +263,24 @@ private:
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
// allocation size this is always inlined, because all these do is to call
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
template<typename D>
template<size_t SIZE>
HandleBase::HandleId allocateHandle() noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
return allocateHandleInPool<BUCKET_SIZE>();
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
static_assert(SIZE <= P2);
return allocateHandleInPool<P2>();
}
template<typename D>
template<size_t SIZE>
void deallocateHandle(HandleBase::HandleId id) noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
deallocateHandleFromPool<BUCKET_SIZE>(id);
if constexpr (SIZE <= P0) {
deallocateHandleFromPool<P0>(id);
} else if constexpr (SIZE <= P1) {
deallocateHandleFromPool<P1>(id);
} else {
static_assert(SIZE <= P2);
deallocateHandleFromPool<P2>(id);
}
}
// allocateHandleInPool()/deallocateHandleFromPool() is NOT inlined, which will cause three
@@ -284,11 +289,9 @@ private:
template<size_t SIZE>
UTILS_NOINLINE
HandleBase::HandleId allocateHandleInPool() noexcept {
uint8_t age;
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
void* p = mHandleArena.alloc(SIZE);
if (UTILS_LIKELY(p)) {
uint32_t const tag = (uint32_t(age) << HANDLE_AGE_SHIFT) & HANDLE_AGE_MASK;
return arenaPointerToHandle(p, tag);
return pointerToHandle(p);
} else {
return allocateHandleSlow(SIZE);
}
@@ -298,51 +301,42 @@ private:
UTILS_NOINLINE
void deallocateHandleFromPool(HandleBase::HandleId id) noexcept {
if (UTILS_LIKELY(isPoolHandle(id))) {
auto [p, tag] = handleToPointer(id);
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
mHandleArena.free(p, SIZE, age);
void* p = handleToPointer(id);
mHandleArena.free(p, SIZE);
} else {
deallocateHandleSlow(id, SIZE);
}
}
// 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 constexpr uint32_t HEAP_HANDLE_FLAG = 0x80000000u;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
return (id & HEAP_HANDLE_FLAG) == 0u;
}
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
// We inline this because it's just 4 instructions in the fast case
inline std::pair<void*, uint32_t> handleToPointer(HandleBase::HandleId id) const noexcept {
inline void* handleToPointer(HandleBase::HandleId id) const noexcept {
// note: the null handle will end-up returning nullptr b/c it'll be handled as
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
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 };
size_t offset = id << Allocator::MIN_ALIGNMENT_SHIFT;
return static_cast<void*>(base + offset);
}
return { handleToPointerSlow(id), 0 };
return handleToPointerSlow(id);
}
void* handleToPointerSlow(HandleBase::HandleId id) const noexcept;
// We inline this because it's just 3 instructions
inline HandleBase::HandleId arenaPointerToHandle(void* p, uint32_t tag) const noexcept {
inline HandleBase::HandleId pointerToHandle(void* p) const noexcept {
char* const base = (char*)mHandleArena.getArea().begin();
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
size_t offset = (char*)p - base;
auto id = HandleBase::HandleId(offset >> Allocator::MIN_ALIGNMENT_SHIFT);
assert_invariant((id & HEAP_HANDLE_FLAG) == 0);
return id;
}
@@ -352,7 +346,9 @@ private:
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
#if HANDLE_TYPE_SAFETY
mutable std::unordered_map<const void*, const char*> mHandleTypeId;
#endif
};
} // namespace filament::backend
+26 -45
View File
@@ -16,14 +16,6 @@
#include "private/backend/CircularBuffer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/architecture.h>
#include <utils/ashmem.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
# include <sys/mman.h>
# include <unistd.h>
@@ -32,20 +24,23 @@
# define HAS_MMAP 0
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <utils/architecture.h>
#include <utils/ashmem.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
namespace filament::backend {
size_t CircularBuffer::sPageSize = arch::getPageSize();
CircularBuffer::CircularBuffer(size_t size)
: mSize(size) {
CircularBuffer::CircularBuffer(size_t size) {
mData = alloc(size);
mSize = size;
mTail = mData;
mHead = mData;
}
@@ -90,7 +85,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
MAP_PRIVATE, fd, (off_t)size);
if (vaddr_guard != MAP_FAILED && (vaddr_guard == (char*)vaddr_shadow + size)) {
// woo-hoo success!
mAshmemFd = fd;
mUsesAshmem = fd;
data = vaddr;
}
}
@@ -98,7 +93,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
}
}
if (UTILS_UNLIKELY(mAshmemFd < 0)) {
if (UTILS_UNLIKELY(mUsesAshmem < 0)) {
// ashmem failed
if (vaddr_guard != MAP_FAILED) {
munmap(vaddr_guard, size);
@@ -142,9 +137,9 @@ void CircularBuffer::dealloc() noexcept {
if (mData) {
size_t const BLOCK_SIZE = getBlockSize();
munmap(mData, mSize * 2 + BLOCK_SIZE);
if (mAshmemFd >= 0) {
close(mAshmemFd);
mAshmemFd = -1;
if (mUsesAshmem >= 0) {
close(mUsesAshmem);
mUsesAshmem = -1;
}
}
#else
@@ -154,37 +149,23 @@ void CircularBuffer::dealloc() noexcept {
}
CircularBuffer::Range CircularBuffer::getBuffer() noexcept {
Range const range{ .tail = mTail, .head = mHead };
char* const pData = static_cast<char*>(mData);
char const* const pEnd = pData + mSize;
char const* const pHead = static_cast<char const*>(mHead);
if (UTILS_UNLIKELY(pHead >= pEnd)) {
size_t const overflow = pHead - pEnd;
if (UTILS_LIKELY(mAshmemFd > 0)) {
assert_invariant(overflow <= mSize);
mHead = static_cast<void*>(pData + overflow);
// Data Tail End Head [virtual]
// v v v v
// +-------------:----+-----:--------------+
// | : | : |
// +-----:------------+--------------------+
// Head |<------ copy ------>| [physical]
} else {
// Data Tail End Head
// v v v v
// +-------------:----+-----:--------------+
// | : | : |
// +-----|------------+-----|--------------+
// |<---------------->|
// sliding window
void CircularBuffer::circularize() noexcept {
if (mUsesAshmem > 0) {
intptr_t const overflow = intptr_t(mHead) - (intptr_t(mData) + ssize_t(mSize));
if (overflow >= 0) {
assert_invariant(size_t(overflow) <= mSize);
mHead = (void *) (intptr_t(mData) + overflow);
#ifndef NDEBUG
memset(mData, 0xA5, size_t(overflow));
#endif
}
} else {
// Only circularize if mHead if in the second buffer.
if (intptr_t(mHead) - intptr_t(mData) > ssize_t(mSize)) {
mHead = mData;
}
}
mTail = mHead;
return range;
}
} // namespace filament::backend
+27 -51
View File
@@ -15,35 +15,23 @@
*/
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/CircularBuffer.h"
#include "private/backend/CommandStream.h"
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <algorithm>
#include <mutex>
#include <iterator>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#include "private/backend/BackendUtils.h"
#include "private/backend/CommandStream.h"
using namespace utils;
namespace filament::backend {
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused)
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
: mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)),
mCircularBuffer(bufferSize),
mFreeSpace(mCircularBuffer.size()),
mPaused(paused) {
mFreeSpace(mCircularBuffer.size()) {
assert_invariant(mCircularBuffer.size() > requiredSize);
}
@@ -57,15 +45,6 @@ void CommandBufferQueue::requestExit() {
mCondition.notify_one();
}
void CommandBufferQueue::setPaused(bool paused) {
if (paused) {
mPaused = true;
} else {
mPaused = false;
mCondition.notify_one();
}
}
bool CommandBufferQueue::isExitRequested() const {
std::lock_guard<utils::Mutex> const lock(mLock);
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
@@ -86,58 +65,55 @@ void CommandBufferQueue::flush() noexcept {
// always guaranteed to have enough space for the NoopCommand
new(circularBuffer.allocate(sizeof(NoopCommand))) NoopCommand(nullptr);
const size_t requiredSize = mRequiredSize;
// end of this slice
void* const head = circularBuffer.getHead();
// get the current buffer
auto const [begin, end] = circularBuffer.getBuffer();
// beginning of this slice
void* const tail = circularBuffer.getTail();
assert_invariant(circularBuffer.empty());
// size of this slice
uint32_t const used = uint32_t(intptr_t(head) - intptr_t(tail));
// size of the current buffer
size_t const used = std::distance(
static_cast<char const*>(begin), static_cast<char const*>(end));
circularBuffer.circularize();
std::unique_lock<utils::Mutex> lock(mLock);
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
mCommandBuffersToExecute.push_back({ tail, head });
// circular buffer is too small, we corrupted the stream
ASSERT_POSTCONDITION(used <= mFreeSpace,
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
"Space used at this time: %u bytes, overflow: %u bytes",
(unsigned)used, unsigned(used - mFreeSpace));
"Space used at this time: %u bytes",
(unsigned)used);
// wait until there is enough space in the buffer
mFreeSpace -= used;
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
const size_t requiredSize = mRequiredSize;
#ifndef NDEBUG
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
slog.d << "CommandStream used too much space (will block): "
<< "needed space " << requiredSize << " out of " << mFreeSpace
<< ", totalUsed=" << totalUsed << ", current=" << used
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers"
<< io::endl;
mHighWatermark = std::max(mHighWatermark, totalUsed);
size_t totalUsed = circularBuffer.size() - mFreeSpace;
mHighWatermark = std::max(mHighWatermark, totalUsed);
if (UTILS_UNLIKELY(totalUsed > requiredSize)) {
slog.d << "CommandStream used too much space: " << totalUsed
<< ", out of " << requiredSize << " (will block)" << io::endl;
}
#endif
mCondition.notify_one();
if (UTILS_LIKELY(mFreeSpace < requiredSize)) {
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
mCondition.wait(lock, [this, requiredSize]() -> bool {
// TODO: on macOS, we need to call pumpEvents from time to time
return mFreeSpace >= requiredSize;
});
}
}
std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() const {
std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() const {
if (!UTILS_HAS_THREADING) {
return std::move(mCommandBuffersToExecute);
}
std::unique_lock<utils::Mutex> lock(mLock);
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
while (mCommandBuffersToExecute.empty() && !mExitRequested) {
mCondition.wait(lock);
}
@@ -147,7 +123,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
return std::move(mCommandBuffersToExecute);
}
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Slice const& buffer) {
std::lock_guard<utils::Mutex> const lock(mLock);
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
mCondition.notify_one();
+16 -17
View File
@@ -49,25 +49,21 @@ struct AcquiredImage;
struct HwBase {
};
struct HwVertexBufferInfo : public HwBase {
struct HwVertexBuffer : public HwBase {
AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT
uint32_t vertexCount{}; // 4
uint8_t bufferCount{}; // 1
uint8_t attributeCount{}; // 1
bool padding[2]{}; // 2
HwVertexBufferInfo() noexcept = default;
HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept
: bufferCount(bufferCount),
attributeCount(attributeCount) {
}
};
bool padding{}; // 1
uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes
struct HwVertexBuffer : public HwBase {
uint32_t vertexCount{}; // 4
uint8_t bufferObjectsVersion{0xff}; // 1
bool padding[3]{}; // 2
HwVertexBuffer() noexcept = default;
explicit HwVertexBuffer(uint32_t vertextCount) noexcept
: vertexCount(vertextCount) {
HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
AttributeArray const& attributes) noexcept
: attributes(attributes),
vertexCount(elementCount),
bufferCount(bufferCount),
attributeCount(attributeCount) {
}
};
@@ -92,6 +88,11 @@ struct HwIndexBuffer : public HwBase {
};
struct HwRenderPrimitive : public HwBase {
uint32_t offset{};
uint32_t minIndex{};
uint32_t maxIndex{};
uint32_t count{};
uint32_t maxVertexCount{};
PrimitiveType type = PrimitiveType::TRIANGLES;
};
@@ -113,9 +114,7 @@ struct HwTexture : public HwBase {
uint8_t levels : 4; // This allows up to 15 levels (max texture size of 32768 x 32768)
uint8_t samples : 4; // Sample count per pixel (should always be a power of 2)
TextureFormat format{};
uint8_t reserved0 = 0;
TextureUsage usage{};
uint16_t reserved1 = 0;
HwStream* hwStream = nullptr;
HwTexture() noexcept : levels{}, samples{} {}
+15 -58
View File
@@ -16,22 +16,9 @@
#include "private/backend/HandleAllocator.h"
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <algorithm>
#include <exception>
#include <limits>
#include <mutex>
#include <stdlib.h>
#include <string.h>
namespace filament::backend {
@@ -39,47 +26,23 @@ using namespace utils;
template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area,
bool disableUseAfterFreeCheck)
: mArea(area),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// The largest handle this allocator can generate currently depends on the architecture's
// min alignment, typically 8 or 16 bytes.
// e.g. On Android armv8, the alignment is 16 bytes, so for a 1 MiB heap, the largest handle
// index will be 65536. Note that this is not the same as the number of handles (which
// will always be less).
// Because our maximum representable handle currently is 0x07FFFFFF, the maximum no-nonsensical
// heap size is 2 GiB, which amounts to 7.6 millions handles per pool (in the GL case).
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
slog.w << "HandleAllocator heap size reduced to "
<< maxHeapSize << " from " << area.size() << io::endl;
}
// make sure we start with a clean arena. This is needed to ensure that all blocks start
// with an age of 0.
memset(area.data(), 0, maxHeapSize);
// size the different pools so that they can all contain the same number of handles
size_t const count = maxHeapSize / (P0 + P1 + P2);
char* const p0 = static_cast<char*>(area.begin());
char* const p1 = p0 + count * P0;
char* const p2 = p1 + count * P1;
mPool0 = Pool<P0>(p0, count * P0);
mPool1 = Pool<P1>(p1, count * P1);
mPool2 = Pool<P2>(p2, count * P2);
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
: mArea(area) {
// TODO: we probably need a better way to set the size of these pools
const size_t unit = area.size() / 32;
const size_t offsetPool1 = unit;
const size_t offsetPool2 = 16 * unit;
char* const p = (char*)area.begin();
mPool0 = PoolAllocator< P0, 16>(p, p + offsetPool1);
mPool1 = PoolAllocator< P1, 16>(p + offsetPool1, p + offsetPool2);
mPool2 = PoolAllocator< P2, 16>(p + offsetPool2, area.end());
}
// ------------------------------------------------------------------------------------------------
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
: mHandleArena(name, size) {
}
template <size_t P0, size_t P1, size_t P2>
@@ -110,17 +73,11 @@ template <size_t P0, size_t P1, size_t P2>
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) noexcept {
void* p = ::malloc(size);
std::unique_lock lock(mLock);
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
HandleBase::HandleId id = (++mId) | HEAP_HANDLE_FLAG;
mOverflowMap.emplace(id, p);
lock.unlock();
if (UTILS_UNLIKELY(id == (HANDLE_HEAP_FLAG | 1u))) { // meaning id was zero
if (UTILS_UNLIKELY(id == (HEAP_HANDLE_FLAG|1u))) { // meaning id was zero
PANIC_LOG("HandleAllocator arena is full, using slower system heap. Please increase "
"the appropriate constant (e.g. FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB).");
}
@@ -129,7 +86,7 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
template <size_t P0, size_t P1, size_t P2>
void HandleAllocator<P0, P1, P2>::deallocateHandleSlow(HandleBase::HandleId id, size_t) noexcept {
assert_invariant(id & HANDLE_HEAP_FLAG);
assert_invariant(id & HEAP_HANDLE_FLAG);
void* p = nullptr;
auto& overflowMap = mOverflowMap;
-14
View File
@@ -53,18 +53,4 @@ size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size
return 0;
}
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
mDebugUpdateStat = std::move(debugUpdateStat);
}
bool Platform::hasDebugUpdateStatFunc() const noexcept {
return bool(mDebugUpdateStat);
}
void Platform::debugUpdateStat(const char* key, uint64_t value) {
if (mDebugUpdateStat) {
mDebugUpdateStat(key, value);
}
}
} // namespace filament::backend
+7 -47
View File
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
#include "MetalContext.h"
#include "MetalBufferPool.h"
#include <backend/DriverEnums.h>
@@ -27,50 +28,9 @@
#include <utility>
#include <memory>
#include <atomic>
namespace filament::backend {
class TrackedMetalBuffer {
public:
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
TrackedMetalBuffer(id<MTLBuffer> buffer) noexcept : mBuffer(buffer) {
if (buffer) {
aliveBuffers++;
}
}
~TrackedMetalBuffer() {
if (mBuffer) {
aliveBuffers--;
}
}
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
swap(rhs);
return *this;
}
id<MTLBuffer> get() const noexcept { return mBuffer; }
operator bool() const noexcept { return bool(mBuffer); }
static uint64_t getAliveBuffers() { return aliveBuffers; }
private:
void swap(TrackedMetalBuffer& other) noexcept {
id<MTLBuffer> temp = mBuffer;
mBuffer = other.mBuffer;
other.mBuffer = temp;
}
id<MTLBuffer> mBuffer;
static std::atomic<uint64_t> aliveBuffers;
};
class MetalBuffer {
public:
@@ -122,7 +82,7 @@ public:
private:
TrackedMetalBuffer mBuffer;
id<MTLBuffer> mBuffer = nil;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
@@ -191,7 +151,7 @@ public:
// finishes executing.
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
assert_invariant(mAuxBuffer);
return {mAuxBuffer.get(), 0};
return {mAuxBuffer, 0};
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
@@ -220,9 +180,9 @@ public:
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
@@ -231,8 +191,8 @@ public:
private:
id<MTLDevice> mDevice;
TrackedMetalBuffer mBuffer;
TrackedMetalBuffer mAuxBuffer;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
MTLResourceOptions mBufferOptions;
+4 -7
View File
@@ -15,15 +15,12 @@
*/
#include "MetalBuffer.h"
#include "MetalBufferPool.h"
#include "MetalContext.h"
namespace filament {
namespace backend {
std::atomic<uint64_t> TrackedMetalBuffer::aliveBuffers = 0;
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
@@ -64,7 +61,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
// Acquire a staging buffer to hold the contents of this update.
MetalBufferPool* bufferPool = mContext.bufferPool;
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
memcpy(staging->buffer.get().contents, src, size);
memcpy(staging->buffer.contents, src, size);
// The blit below requires that byteOffset be a multiple of 4.
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
@@ -73,9 +70,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @"Buffer upload blit";
[blitEncoder copyFromBuffer:staging->buffer.get()
[blitEncoder copyFromBuffer:staging->buffer
sourceOffset:0
toBuffer:mBuffer.get()
toBuffer:mBuffer
destinationOffset:byteOffset
size:size];
[blitEncoder endEncoding];
@@ -96,7 +93,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return nil;
}
assert_invariant(mBuffer);
return mBuffer.get();
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
+1 -3
View File
@@ -19,8 +19,6 @@
#include <Metal/Metal.h>
#include "MetalBuffer.h"
#include <map>
#include <mutex>
#include <unordered_set>
@@ -32,7 +30,7 @@ struct MetalContext;
// Immutable POD representing a shared CPU-GPU buffer.
struct MetalBufferPoolEntry {
TrackedMetalBuffer buffer;
id<MTLBuffer> buffer;
size_t capacity;
mutable uint64_t lastAccessed;
mutable uint32_t referenceCount;
@@ -45,12 +45,12 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
options:MTLResourceStorageModeShared];
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({
.buffer = buffer,
.capacity = numBytes,
.lastAccessed = mCurrentFrame,
.referenceCount = 1
};
});
mUsedStages.insert(stage);
return stage;
@@ -99,7 +99,6 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
DepthStencilStateCache depthStencilStateCache;
+4 -1
View File
@@ -133,9 +133,12 @@ private:
mHandleAllocator.deallocate(handle, p);
}
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void finalizeSamplerGroup(MetalSamplerGroup* sg);
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
+82 -181
View File
@@ -20,7 +20,6 @@
#include "metal/MetalDriver.h"
#include "MetalBlitter.h"
#include "MetalBufferPool.h"
#include "MetalContext.h"
#include "MetalDriverFactory.h"
#include "MetalEnums.h"
@@ -37,7 +36,6 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/sstream.h>
#include <algorithm>
@@ -45,42 +43,6 @@ namespace filament {
namespace backend {
Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform::DriverConfig& driverConfig) {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
// MetalRenderPrimitive : 24 many
// MetalVertexBuffer : 32 moderate
// -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 152 moderate
// MetalTexture : 152 moderate
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
<< "\nMetalTexture: " << sizeof(MetalTexture)
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
<< "\nMetalFence: " << sizeof(MetalFence)
<< "\nMetalProgram: " << sizeof(MetalProgram)
<< utils::io::endl;
#endif
return MetalDriver::create(platform, driverConfig);
}
@@ -100,9 +62,7 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(*platform),
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck) {
mHandleAllocator("Handles", driverConfig.handleArenaSize) {
mContext->driver = this;
mContext->device = mPlatform.createDevice();
@@ -184,10 +144,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
}
const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile
? MetalShaderCompiler::Mode::SYNCHRONOUS
: MetalShaderCompiler::Mode::ASYNCHRONOUS;
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode);
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
mContext->shaderCompiler->init();
#if defined(FILAMENT_METAL_PROFILING)
@@ -216,9 +173,6 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
#if defined(FILAMENT_METAL_PROFILING)
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
#endif
if (mPlatform.hasDebugUpdateStatFunc()) {
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
}
}
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
@@ -291,16 +245,10 @@ void MetalDriver::finish(int) {
[oneOffBuffer waitUntilCompleted];
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
}
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);
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) {
construct_handle<MetalVertexBuffer>(vbh, *mContext, bufferCount,
attributeCount, vertexCount, attributes);
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
@@ -368,9 +316,11 @@ void MetalDriver::createSamplerGroupR(
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
@@ -383,7 +333,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dum
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color,
uint8_t samples, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"createRenderTarget must be called outside of a render pass.");
@@ -456,10 +406,6 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
return alloc_handle<MetalVertexBufferInfo>();
}
Handle<HwVertexBuffer> MetalDriver::createVertexBufferS() noexcept {
return alloc_handle<MetalVertexBuffer>();
}
@@ -524,12 +470,6 @@ Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
return alloc_and_construct_handle<MetalTimerQuery, HwTimerQuery>();
}
void MetalDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
destruct_handle<MetalVertexBufferInfo>(vbih);
}
}
void MetalDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (vbh) {
destruct_handle<MetalVertexBuffer>(vbh);
@@ -782,33 +722,18 @@ bool MetalDriver::isSRGBSwapChainSupported() {
return false;
}
bool MetalDriver::isProtectedContentSupported() {
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
return false;
}
bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return true;
case backend::StereoscopicType::MULTIVIEW:
// TODO: implement multiview feature in Metal.
return false;
}
bool MetalDriver::isStereoSupported() {
return true;
}
bool MetalDriver::isParallelShaderCompileSupported() {
return mContext->shaderCompiler->isParallelShaderCompileSupported();
return true;
}
bool MetalDriver::isDepthStencilResolveSupported() {
return false;
}
bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -935,10 +860,9 @@ void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint3
void MetalDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}
TimerQueryResult MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
bool MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
auto* tq = handle_cast<MetalTimerQuery>(tqh);
return mContext->timerQueryImpl->getQueryResult(tq, elapsedTime) ?
TimerQueryResult::AVAILABLE : TimerQueryResult::NOT_READY;
return mContext->timerQueryImpl->getQueryResult(tq, elapsedTime);
}
void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
@@ -1105,14 +1029,23 @@ void MetalDriver::endRenderPass(int dummy) {
mContext->currentRenderPassEncoder = nil;
}
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
primitive->setBuffers(vertexBuffer, indexBuffer);
}
void MetalDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
uint32_t count) {
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
primitive->type = pt;
primitive->offset = offset * primitive->indexBuffer->elementSize;
primitive->count = count;
primitive->minIndex = minIndex;
primitive->maxIndex = maxIndex > minIndex ? maxIndex : primitive->maxVertexCount - 1;
}
void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> schRead) {
@@ -1614,13 +1547,10 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::bindPipeline(PipelineState ps) {
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"bindPipeline() without a valid command encoder.");
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
"Attempted to draw without a valid command encoder.");
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
auto program = handle_cast<MetalProgram>(ps.program);
const auto& rs = ps.rasterState;
@@ -1634,7 +1564,7 @@ void MetalDriver::bindPipeline(PipelineState ps) {
return;
}
ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1661,7 +1591,7 @@ void MetalDriver::bindPipeline(PipelineState ps) {
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
.vertexDescription = vbi->vertexDescription,
.vertexDescription = primitive->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
colorPixelFormat[1],
@@ -1755,6 +1685,50 @@ void MetalDriver::bindPipeline(PipelineState ps) {
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Set scissor-rectangle.
// In order to do this, we compute the intersection between:
// 1. the scissor rectangle
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
// coordinates are doubles.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
// Attachment extent
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
const float aleft = 0.0f, atop = 0.0f;
const float aright = static_cast<float>(attachmentSize.x);
const float abottom = static_cast<float>(attachmentSize.y);
const auto left = std::fmax(sleft, aleft);
const auto right = std::fmin(sright, aright);
const auto top = std::fmax(stop, atop);
const auto bottom = std::fmin(sbottom, abottom);
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
.y = static_cast<NSUInteger>(top),
.width = static_cast<NSUInteger>(right - left),
.height = static_cast<NSUInteger>(bottom - top)
};
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Bind sampler groups (argument buffers).
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
@@ -1785,29 +1759,19 @@ void MetalDriver::bindPipeline(PipelineState ps) {
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
}
}
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"bindRenderPrimitive() without a valid command encoder.");
// Bind the user vertex buffers.
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t maxBufferIndex = 0;
MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
mContext->currentRenderPrimitive = rph;
auto vb = primitive->vertexBuffer;
for (auto m : vbi->bufferMapping) {
for (auto m : primitive->bufferMapping) {
assert_invariant(
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
}
@@ -1822,50 +1786,19 @@ void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
[mContext->currentRenderPassEncoder setVertexBytes:bytes
length:16
atIndex:ZERO_VERTEX_BUFFER_BINDING];
}
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"draw() without a valid command encoder.");
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
id<MTLBuffer> metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer);
[mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type)
indexCount:indexCount
indexCount:primitive->count
indexType:getIndexType(indexBuffer->elementSize)
indexBuffer:metalIndexBuffer
indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize
indexBufferOffset:primitive->offset
instanceCount:instanceCount];
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
MetalRenderPrimitive const* const rp = handle_cast<MetalRenderPrimitive>(rph);
ps.primitiveType = rp->type;
ps.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(ps);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
@@ -1935,38 +1868,6 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
[computeEncoder endEncoding];
}
void MetalDriver::scissor(Viewport scissorBox) {
// Set scissor-rectangle.
// In order to do this, we compute the intersection between:
// 1. the scissor rectangle
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
// coordinates are doubles.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(scissorBox);
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
// Attachment extent
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
const float aleft = 0.0f, atop = 0.0f;
const float aright = static_cast<float>(attachmentSize.x);
const float abottom = static_cast<float>(attachmentSize.y);
const auto left = std::fmax(sleft, aleft);
const auto right = std::fmin(sright, aright);
const auto top = std::fmax(stop, atop);
const auto bottom = std::fmin(sbottom, abottom);
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
.y = static_cast<NSUInteger>(top),
.width = static_cast<NSUInteger>(right - left),
.height = static_cast<NSUInteger>(bottom - top)
};
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"beginTimerQuery must be called outside of a render pass.");
+22 -29
View File
@@ -145,9 +145,28 @@ private:
MetalBuffer buffer;
};
struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
struct MetalVertexBuffer : public HwVertexBuffer {
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
uint32_t vertexCount, AttributeArray const& attributes);
utils::FixedCapacityVector<MetalBuffer*> buffers;
};
struct MetalIndexBuffer : public HwIndexBuffer {
MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount);
MetalBuffer buffer;
};
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
@@ -166,32 +185,6 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo {
utils::FixedCapacityVector<Entry> bufferMapping;
};
struct MetalVertexBuffer : public HwVertexBuffer {
MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih);
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<MetalBuffer*> buffers;
};
struct MetalIndexBuffer : public HwIndexBuffer {
MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount);
MetalBuffer buffer;
};
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
};
class MetalProgram : public HwProgram {
public:
MetalProgram(MetalContext& context, Program&& program) noexcept;
+53 -79
View File
@@ -19,7 +19,6 @@
#include "MetalBlitter.h"
#include "MetalEnums.h"
#include "MetalUtils.h"
#include "MetalBufferPool.h"
#include <filament/SwapChain.h>
@@ -246,53 +245,30 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
PresentDrawableData(const PresentDrawableData&) = delete;
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
static PresentDrawableData* create(id<CAMetalDrawable> drawable, MetalDriver* driver) {
assert_invariant(driver);
return new PresentDrawableData(drawable, driver);
}
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
if (shouldPresent) {
[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:
PresentDrawableData(id<CAMetalDrawable> drawable, MetalDriver* driver)
: mDrawable(drawable), mDriver(driver) {}
static void cleanupAndDestroy(PresentDrawableData *that) {
that->mDrawable = nil;
that->mDriver = nullptr;
delete that;
}
id<CAMetalDrawable> mDrawable;
MetalDriver* mDriver = nullptr;
struct PresentDrawableData {
void* drawable = nullptr;
MetalDriver* driver = nullptr;
};
void presentDrawable(bool presentFrame, void* user) {
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
PresentDrawableData::maybePresentAndDestroyAsync(presentDrawableData, presentFrame);
// CFBridgingRelease here is used to balance the CFBridgingRetain inside acquireDrawable.
id<CAMetalDrawable> drawable =
(id<CAMetalDrawable>)CFBridgingRelease(presentDrawableData->drawable);
if (presentFrame) {
[drawable present];
}
// Schedule the drawable destruction on the driver thread.
void* voidDrawable = (void*) CFBridgingRetain(drawable);
MetalDriver* driver = presentDrawableData->driver;
driver->runAtNextTick([voidDrawable]() {
// The drawable is released here.
CFBridgingRelease(voidDrawable);
});
delete presentDrawableData;
}
void MetalSwapChain::scheduleFrameScheduledCallback() {
@@ -301,16 +277,21 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
}
assert_invariant(drawable);
// Destroy this by calling maybePresentAndDestroyAsync() later.
auto* presentData = PresentDrawableData::create(drawable, context.driver);
FrameScheduledCallback userCallback = frameScheduledCallback;
FrameScheduledCallback callback = frameScheduledCallback;
// This block strongly captures drawable to keep it alive until the handler executes.
// We cannot simply reference this->drawable inside the block because the block would then only
// capture the _this_ pointer (MetalSwapChain*) instead of the drawable.
id<CAMetalDrawable> d = drawable;
void* userData = frameScheduledUserData;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
PresentCallable callable(presentDrawable, static_cast<void*>(presentData));
userCallback(callable, userData);
// CFBridgingRetain is used here to give the drawable a +1 retain count before
// casting it to a void*.
auto* presentDrawableData = new PresentDrawableData;
presentDrawableData->drawable = (void*) CFBridgingRetain(d);
presentDrawableData->driver = driver;
PresentCallable callable(presentDrawable, (void*) presentDrawableData);
callback(callable, userData);
}];
}
@@ -341,12 +322,23 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
buffer.copyIntoBufferUnsynchronized(data, size, byteOffset);
}
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
const size_t maxAttributeCount = attributes.size();
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
const size_t attributeCount = vertexBuffer->attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -386,8 +378,8 @@ MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t buff
}
};
for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) {
const auto& attribute = attributes[attributeIndex];
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vertexBuffer->attributes[attributeIndex];
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
// to read from missing vertex attributes.
@@ -414,24 +406,6 @@ MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t buff
}
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive() {
}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
@@ -771,13 +745,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.get().contents,
memcpy(entry->buffer.contents,
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
stagingBufferSize);
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
blitCommandEncoder.label = @"Texture upload buffer blit";
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
[blitCommandEncoder copyFromBuffer:entry->buffer
sourceOffset:0
sourceBytesPerRow:shape.bytesPerRow
sourceBytesPerImage:shape.bytesPerSlice
@@ -30,8 +30,6 @@
#include <array>
#include <memory>
#include <tuple>
#include <variant>
namespace filament::backend {
@@ -41,81 +39,43 @@ class MetalShaderCompiler {
struct MetalProgramToken;
public:
enum class Mode {
SYNCHRONOUS, // synchronous shader compilation
ASYNCHRONOUS // asynchronous shader compilation
};
class MetalFunctionBundle {
public:
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
using Compute = id<MTLFunction>;
using Error = std::tuple<NSString*, NSString*>; // error message, Program name
struct None {};
MetalFunctionBundle() : mPrograms{None{}} {}
explicit operator bool() const { return isValid(); }
bool isValid() const noexcept {
return std::holds_alternative<Raster>(mPrograms) ||
std::holds_alternative<Compute>(mPrograms);
}
/**
* Throws an NSException if this MetalFunctionBundle either contains an error or is empty.
*
* If this MetalFunctionBundle contains an error, will throw a MetalCompilationFailure
* NSException with the error string and program name passed to
* MetalFunctionBundle::error(NSString*, NSString*).
*
* If this MetalFunctionBundle is empty, will throw a MetalEmptyFunctionBundle NSException.
*/
void validate() const;
Raster getRasterFunctions() const {
assert_invariant(std::holds_alternative<Raster>(mPrograms));
return std::get<Raster>(mPrograms);
}
Compute getComputeFunction() const {
assert_invariant(std::holds_alternative<Compute>(mPrograms));
return std::get<Compute>(mPrograms);
}
static MetalFunctionBundle none() {
return MetalFunctionBundle(None{});
}
static MetalFunctionBundle raster(id<MTLFunction> fragment, id<MTLFunction> vertex) {
MetalFunctionBundle() = default;
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
: functions{fragment, vertex} {
assert_invariant(fragment && vertex);
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
return MetalFunctionBundle(Raster{fragment, vertex});
}
static MetalFunctionBundle compute(id<MTLFunction> compute) {
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
assert_invariant(compute);
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
return MetalFunctionBundle(Compute{compute});
}
static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) {
return MetalFunctionBundle(Error{errorMessage, programName});
std::pair<id<MTLFunction>, id<MTLFunction>> getRasterFunctions() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeFragment);
assert_invariant(functions[1].functionType == MTLFunctionTypeVertex);
return {functions[0], functions[1]};
}
id<MTLFunction> getComputeFunction() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
return functions[0];
}
explicit operator bool() const { return functions[0] != nil; }
private:
MetalFunctionBundle(None&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Raster&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Compute&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Error&& t) : mPrograms(std::move(t)) {}
std::variant<Raster, Compute, None, Error> mPrograms;
// Can hold two functions, either:
// - fragment and vertex (for rasterization pipelines)
// - compute (for compute pipelines)
id<MTLFunction> functions[2] = {nil, nil};
};
using program_token_t = std::shared_ptr<MetalProgramToken>;
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode);
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver);
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
@@ -125,15 +85,15 @@ public:
void init() noexcept;
void terminate() noexcept;
bool isParallelShaderCompileSupported() const noexcept;
// Creates a program, either synchronously or asynchronously, depending on the Mode
// MetalShaderCompiler was constructed with.
// Creates a program asynchronously
program_token_t createProgram(utils::CString const& name, Program&& program);
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
MetalFunctionBundle getProgram(program_token_t& token);
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
static void terminate(program_token_t& token);
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -143,7 +103,6 @@ private:
CompilerThreadPool mCompilerThreadPool;
id<MTLDevice> mDevice;
CallbackManager mCallbackManager;
Mode mMode;
};
} // namespace filament::backend
@@ -70,31 +70,22 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode)
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver)
: mDevice(device),
mCallbackManager(driver),
mMode(mode) {
mCallbackManager(driver) {
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 1;
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
const uint32_t poolSize = 2;
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
void MetalShaderCompiler::terminate() noexcept {
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.terminate();
}
mCompilerThreadPool.terminate();
mCallbackManager.terminate();
}
bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
return mMode == Mode::ASYNCHRONOUS;
}
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
const Program& program, id<MTLDevice> device) {
std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
@@ -127,16 +118,13 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
options:options
error:&error];
if (library == nil) {
NSString* errorMessage = @"unknown error";
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.w << description << utils::io::endl;
errorMessage = error.localizedDescription;
}
PANIC_LOG("Failed to compile Metal program.");
NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()];
return MetalFunctionBundle::error(errorMessage, programName);
return {};
}
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
@@ -172,15 +160,14 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
assert_invariant(isRasterizationProgram != isComputeProgram);
if (isRasterizationProgram) {
return MetalFunctionBundle::raster(fragmentFunction, vertexFunction);
return {fragmentFunction, vertexFunction};
}
if (isComputeProgram) {
return MetalFunctionBundle::compute(computeFunction);
return MetalFunctionBundle{computeFunction};
}
// Should never reach here.
return MetalFunctionBundle::none();
return {};
}
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
@@ -189,26 +176,14 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
token->handle = mCallbackManager.get();
switch (mMode) {
case Mode::ASYNCHRONOUS: {
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
break;
}
case Mode::SYNCHRONOUS: {
MetalFunctionBundle compiledProgram = compileProgram(program, mDevice);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
break;
}
}
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
return token;
}
@@ -216,46 +191,38 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
assert_invariant(token);
if (mMode == Mode::ASYNCHRONOUS) {
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
}
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
}
}
// The job isn't guaranteed to have finished yet. We may have failed to dequeue it above,
// which means it's currently running. In that case get() will block until it finishes.
MetalShaderCompiler::MetalFunctionBundle program = token->get();
token = nullptr;
return program;
}
/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
assert_invariant(token);
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now (or has already executed).
token->wait();
} else {
// The job has not executed yet.
token->compiler.mCallbackManager.put(token->handle);
}
token.reset();
}
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
mCallbackManager.setCallback(handler, callback, user);
}
UTILS_NOINLINE
void MetalShaderCompiler::MetalFunctionBundle::validate() const {
if (UTILS_UNLIKELY(std::holds_alternative<Error>(mPrograms))) {
auto [errorMessage, programName] = std::get<Error>(mPrograms);
NSString* reason =
[NSString stringWithFormat:
@"Attempting to draw with an id<MTLFunction> that failed to compile.\n"
@"Program: %@\n"
@"%@", programName, errorMessage];
[[NSException exceptionWithName:@"MetalCompilationFailure"
reason:reason
userInfo:nil] raise];
} else if (UTILS_UNLIKELY(std::holds_alternative<None>(mPrograms))) {
NSString* reason = @"Attempting to draw with an empty id<MTLFunction>.";
[[NSException exceptionWithName:@"MetalEmptyFunctionBundle"
reason:reason
userInfo:nil] raise];
}
}
} // namespace filament::backend
+4 -28
View File
@@ -77,9 +77,6 @@ void NoopDriver::finish(int) {
void NoopDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void NoopDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
}
void NoopDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
}
@@ -177,11 +174,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
return false;
}
bool NoopDriver::isProtectedContentSupported() {
return false;
}
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
bool NoopDriver::isStereoSupported() {
return false;
}
@@ -193,10 +186,6 @@ bool NoopDriver::isDepthStencilResolveSupported() {
return true;
}
bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -252,8 +241,8 @@ void NoopDriver::update3DImage(Handle<HwTexture> th,
void NoopDriver::setupExternalImage(void* image) {
}
TimerQueryResult NoopDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
return TimerQueryResult::ERROR;
bool NoopDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
return false;
}
void NoopDriver::setExternalImage(Handle<HwTexture> th, void* image) {
@@ -350,26 +339,13 @@ void NoopDriver::blit(
math::uint2 size) {
}
void NoopDriver::bindPipeline(PipelineState pipelineState) {
}
void NoopDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
uint32_t instanceCount) {
}
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
}
void NoopDriver::scissor(
Viewport scissor) {
}
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
}
+13 -135
View File
@@ -16,25 +16,10 @@
#include "OpenGLContext.h"
#include "GLUtils.h"
#include "OpenGLTimerQuery.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <functional>
#include <string_view>
#include <utility>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
// change to true to display all GL extensions in the console on start-up
#define DEBUG_PRINT_EXTENSIONS false
@@ -63,8 +48,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
#endif
}
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
: mPlatform(platform) {
OpenGLContext::OpenGLContext() noexcept {
state.vao.p = &mDefaultVAO;
@@ -247,49 +231,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
glDebugMessageCallback(cb, nullptr);
}
#endif
mTimerQueryFactory = TimerQueryFactory::init(platform, *this);
}
OpenGLContext::~OpenGLContext() noexcept {
delete mTimerQueryFactory;
}
void OpenGLContext::destroyWithContext(
size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept {
if (index == 0) {
// Note: we only need to delay the destruction of objects on the unprotected context
// (index 0) because the protected context is always immediately destroyed and all its
// active objects and bindings are then automatically destroyed.
// TODO: this is only guaranteed for EGLPlatform, but that's the only one we care about.
mDestroyWithNormalContext.push_back(closure);
}
}
void OpenGLContext::unbindEverything() noexcept {
// TODO: we're supposed to unbind everything here so that resources don't get
// stuck in this context (contextIndex) when destroyed in the other context.
// However, because EGLPlatform always immediately destroys the protected context (1),
// the bindings will automatically be severed when we switch back to the default context.
// Since bindings now only exist in one context, we don't have a ref-counting issue to
// worry about.
}
void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
// if we're just switching back to context 0, run all the pending destructors
if (index == 0) {
auto list = std::move(mDestroyWithNormalContext);
for (auto&& fn: list) {
fn(*this);
}
}
// the default FBO could be invalid
mDefaultFbo[index].reset();
contextIndex = index;
resetState();
}
void OpenGLContext::setDefaultState() noexcept {
@@ -658,7 +599,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
ext->EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
ext->EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
ext->EXT_protected_textures = exts.has("GL_EXT_protected_textures"sv);
#endif
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
#ifndef __EMSCRIPTEN__
@@ -682,7 +622,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
@@ -747,7 +686,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->OES_standard_derivatives = true;
ext->OES_texture_npot = true;
ext->OES_vertex_array_object = true;
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
ext->WEBGL_compressed_texture_etc = false;
ext->WEBGL_compressed_texture_s3tc = false;
ext->WEBGL_compressed_texture_s3tc_srgb = false;
@@ -769,51 +707,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
#endif // BACKEND_OPENGL_VERSION_GL
GLuint OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
if (UTILS_UNLIKELY(buffer == 0)) {
// we're binding the default frame buffer, resolve its actual name
auto& defaultFboForThisContext = mDefaultFbo[contextIndex];
if (UTILS_UNLIKELY(!defaultFboForThisContext.has_value())) {
defaultFboForThisContext = GLuint(mPlatform.getDefaultFramebufferObject());
}
buffer = defaultFboForThisContext.value();
}
bindFramebufferResolved(target, buffer);
return buffer;
}
void OpenGLContext::unbindFramebuffer(GLenum target) noexcept {
bindFramebufferResolved(target, 0);
}
void OpenGLContext::bindFramebufferResolved(GLenum target, GLuint buffer) noexcept {
switch (target) {
case GL_FRAMEBUFFER:
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
state.draw_fbo = state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_DRAW_FRAMEBUFFER:
if (state.draw_fbo != buffer) {
state.draw_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
case GL_READ_FRAMEBUFFER:
if (state.read_fbo != buffer) {
state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#endif
default:
break;
}
}
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
if (target == GL_ELEMENT_ARRAY_BUFFER) {
constexpr size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
@@ -924,22 +817,19 @@ void OpenGLContext::deleteBuffers(GLsizei n, const GLuint* buffers, GLenum targe
#endif
}
void OpenGLContext::deleteVertexArray(GLuint vao) noexcept {
if (UTILS_LIKELY(vao)) {
procs.deleteVertexArrays(1, &vao);
// if the destroyed VAO is bound, clear the binding.
if (state.vao.p->vao[contextIndex] == vao) {
void OpenGLContext::deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept {
procs.deleteVertexArrays(n, arrays);
// if one of the destroyed VAO is bound, clear the binding.
for (GLsizei i = 0; i < n; ++i) {
if (state.vao.p->vao == arrays[i]) {
bindVertexArray(nullptr);
break;
}
}
}
void OpenGLContext::resetState() noexcept {
// Force GL state to match the Filament state
// increase the state version so other parts of the state know to reset
state.age++;
if (state.major > 2) {
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, state.draw_fbo);
@@ -956,8 +846,11 @@ void OpenGLContext::resetState() noexcept {
glUseProgram(state.program.use);
// state.vao
state.vao.p = nullptr;
bindVertexArray(nullptr);
if (state.vao.p) {
procs.bindVertexArray(state.vao.p->vao);
} else {
bindVertexArray(nullptr);
}
// state.raster
glFrontFace(state.raster.frontFace);
@@ -1113,22 +1006,7 @@ void OpenGLContext::resetState() noexcept {
state.window.viewport.w
);
glDepthRangef(state.window.depthRange.x, state.window.depthRange.y);
}
void OpenGLContext::createTimerQuery(GLTimerQuery* query) {
mTimerQueryFactory->createTimerQuery(query);
}
void OpenGLContext::destroyTimerQuery(GLTimerQuery* query) {
mTimerQueryFactory->destroyTimerQuery(query);
}
void OpenGLContext::beginTimeElapsedQuery(GLTimerQuery* query) {
mTimerQueryFactory->beginTimeElapsedQuery(query);
}
void OpenGLContext::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) {
mTimerQueryFactory->endTimeElapsedQuery(driver, query);
}
} // namesapce filament
+62 -110
View File
@@ -17,37 +17,25 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
#define TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
#include "OpenGLTimerQuery.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "gl_headers.h"
#include <utils/compiler.h>
#include <utils/bitset.h>
#include <utils/debug.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <array>
#include <functional>
#include <optional>
#include <tuple>
#include <vector>
#include <utils/CString.h>
#include <utils/debug.h>
#include <stddef.h>
#include <stdint.h>
#include <backend/Handle.h>
#include "GLUtils.h"
#include <array>
#include <set>
#include <tuple>
#include <utility>
namespace filament::backend {
class OpenGLPlatform;
class OpenGLContext final : public TimerQueryFactoryInterface {
class OpenGLContext {
public:
static constexpr const size_t MAX_TEXTURE_UNIT_COUNT = MAX_SAMPLER_COUNT;
static constexpr const size_t DUMMY_TEXTURE_BINDING = 7; // highest binding guaranteed to work with ES2
@@ -58,29 +46,19 @@ public:
struct RenderPrimitive {
static_assert(MAX_VERTEX_ATTRIBUTE_COUNT <= 16);
GLuint vao[2] = {}; // 4
GLuint vao = 0; // 4
GLuint elementArray = 0; // 4
mutable utils::bitset<uint16_t> vertexAttribArray; // 2
utils::bitset<uint16_t> vertexAttribArray; // 2
// if this differs from vertexBufferWithObjects->bufferObjectsVersion, this VAO needs to
// be updated (see OpenGLDriver::updateVertexArrayObject())
// If this version number does not match vertexBufferWithObjects->bufferObjectsVersion,
// then the VAO needs to be updated.
uint8_t vertexBufferVersion = 0; // 1
// if this differs from OpenGLContext::state.age, this VAO needs to
// be updated (see OpenGLDriver::updateVertexArrayObject())
uint8_t stateVersion = 0; // 1
// If this differs from OpenGLContext::state.age, this VAO's name needs to be updated.
// See OpenGLContext::bindVertexArray()
uint8_t nameVersion = 0; // 1
// Size in bytes of indices in the index buffer
uint8_t indicesSize = 0; // 1
// The optional 32-bit handle to a GLVertexBuffer is necessary only if the referenced
// VertexBuffer supports buffer objects. If this is zero, then the VBO handles array is
// immutable.
Handle<HwVertexBuffer> vertexBufferWithObjects; // 4
Handle<HwVertexBuffer> vertexBufferWithObjects = {}; // 4
GLenum getIndicesType() const noexcept {
return indicesSize == 4 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
@@ -89,18 +67,7 @@ public:
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
~OpenGLContext() noexcept final;
// TimerQueryInterface ------------------------------------------------------------------------
// note: OpenGLContext being final ensures (clang) these are not called through the vtable
void createTimerQuery(GLTimerQuery* query) override;
void destroyTimerQuery(GLTimerQuery* query) override;
void beginTimeElapsedQuery(GLTimerQuery* query) override;
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
// --------------------------------------------------------------------------------------------
OpenGLContext() noexcept;
template<int MAJOR, int MINOR>
inline bool isAtLeastGL() const noexcept {
@@ -156,11 +123,10 @@ public:
inline void bindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size) noexcept;
GLuint bindFramebuffer(GLenum target, GLuint buffer) noexcept;
void unbindFramebuffer(GLenum target) noexcept;
inline void bindFramebuffer(GLenum target, GLuint buffer) noexcept;
inline void enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
inline void disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
inline void enableVertexAttribArray(GLuint index) noexcept;
inline void disableVertexAttribArray(GLuint index) noexcept;
inline void enable(GLenum cap) noexcept;
inline void disable(GLenum cap) noexcept;
inline void frontFace(GLenum mode) noexcept;
@@ -182,9 +148,7 @@ public:
inline void depthRange(GLclampf near, GLclampf far) noexcept;
void deleteBuffers(GLsizei n, const GLuint* buffers, GLenum target) noexcept;
void deleteVertexArray(GLuint vao) noexcept;
void destroyWithContext(size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept;
void deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept;
// glGet*() values
struct Gets {
@@ -219,7 +183,6 @@ public:
bool EXT_discard_framebuffer;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
bool EXT_shader_framebuffer_fetch;
bool EXT_texture_compression_bptc;
bool EXT_texture_compression_etc2;
@@ -242,7 +205,6 @@ public:
bool OES_standard_derivatives;
bool OES_texture_npot;
bool OES_vertex_array_object;
bool OVR_multiview2;
bool WEBGL_compressed_texture_etc;
bool WEBGL_compressed_texture_s3tc;
bool WEBGL_compressed_texture_s3tc_srgb;
@@ -328,19 +290,8 @@ public:
FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
// This is the index of the context in use. Must be 0 or 1. This is used to manange the
// OpenGL name of ContainerObjects within each context.
uint32_t contextIndex = 0;
// Try to keep the State structure sorted by data-access patterns
struct State {
State() noexcept = default;
// make sure we don't copy this state by accident
State(State const& rhs) = delete;
State(State&& rhs) noexcept = delete;
State& operator=(State const& rhs) = delete;
State& operator=(State&& rhs) noexcept = delete;
GLint major = 0;
GLint minor = 0;
@@ -445,7 +396,6 @@ public:
vec4gli viewport { 0 };
vec2glf depthRange { 0.0f, 1.0f };
} window;
uint8_t age = 0;
} state;
struct Procs {
@@ -465,19 +415,9 @@ public:
void (* maxShaderCompilerThreadsKHR)(GLuint count);
} procs{};
void unbindEverything() noexcept;
void synchronizeStateAndCache(size_t index) noexcept;
private:
OpenGLPlatform& mPlatform;
ShaderModel mShaderModel = ShaderModel::MOBILE;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
RenderPrimitive mDefaultVAO;
std::optional<GLuint> mDefaultFbo[2];
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
{ bugs.disable_glFlush,
@@ -530,6 +470,8 @@ private:
""},
}};
RenderPrimitive mDefaultVAO;
// this is chosen to minimize code size
#if defined(BACKEND_OPENGL_VERSION_GLES)
static void initExtensionsGLES(Extensions* ext, GLint major, GLint minor) noexcept;
@@ -687,26 +629,11 @@ void OpenGLContext::depthRange(GLclampf near, GLclampf far) noexcept {
void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
RenderPrimitive* vao = p ? const_cast<RenderPrimitive *>(p) : &mDefaultVAO;
update_state(state.vao.p, vao, [&]() {
// See if we need to create a name for this VAO on the fly, this would happen if:
// - we're not the default VAO, because its name is always 0
// - our name is 0, this could happen if this VAO was created in the "other" context
// - the nameVersion is out of date *and* we're on the protected context, in this case:
// - the name must be stale from a previous use of this context because we always
// destroy the protected context when we're done with it.
bool const recreateVaoName = p != &mDefaultVAO &&
((vao->vao[contextIndex] == 0) ||
(vao->nameVersion != state.age && contextIndex == 1));
if (UTILS_UNLIKELY(recreateVaoName)) {
vao->nameVersion = state.age;
procs.genVertexArrays(1, &vao->vao[contextIndex]);
}
procs.bindVertexArray(vao->vao[contextIndex]);
procs.bindVertexArray(vao->vao);
// update GL_ELEMENT_ARRAY_BUFFER, which is updated by glBindVertexArray
size_t const targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
state.buffers.genericBinding[targetIndex] = vao->elementArray;
if (UTILS_UNLIKELY(bugs.vao_doesnt_store_element_array_buffer_binding || recreateVaoName)) {
if (UTILS_UNLIKELY(bugs.vao_doesnt_store_element_array_buffer_binding)) {
// This shouldn't be needed, but it looks like some drivers don't do the implicit
// glBindBuffer().
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->elementArray);
@@ -744,6 +671,33 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
#endif
}
void OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
switch (target) {
case GL_FRAMEBUFFER:
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
state.draw_fbo = state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_DRAW_FRAMEBUFFER:
if (state.draw_fbo != buffer) {
state.draw_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
case GL_READ_FRAMEBUFFER:
if (state.read_fbo != buffer) {
state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#endif
default:
break;
}
}
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept {
assert_invariant(targetIndex == getIndexForTextureTarget(target));
assert_invariant(targetIndex < TEXTURE_TARGET_COUNT);
@@ -763,22 +717,20 @@ void OpenGLContext::useProgram(GLuint program) noexcept {
});
}
void OpenGLContext::enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept {
assert_invariant(rp);
assert_invariant(index < rp->vertexAttribArray.size());
bool const force = rp->stateVersion != state.age;
if (UTILS_UNLIKELY(force || !rp->vertexAttribArray[index])) {
rp->vertexAttribArray.set(index);
void OpenGLContext::enableVertexAttribArray(GLuint index) noexcept {
assert_invariant(state.vao.p);
assert_invariant(index < state.vao.p->vertexAttribArray.size());
if (UTILS_UNLIKELY(!state.vao.p->vertexAttribArray[index])) {
state.vao.p->vertexAttribArray.set(index);
glEnableVertexAttribArray(index);
}
}
void OpenGLContext::disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept {
assert_invariant(rp);
assert_invariant(index < rp->vertexAttribArray.size());
bool const force = rp->stateVersion != state.age;
if (UTILS_UNLIKELY(force || rp->vertexAttribArray[index])) {
rp->vertexAttribArray.unset(index);
void OpenGLContext::disableVertexAttribArray(GLuint index) noexcept {
assert_invariant(state.vao.p);
assert_invariant(index < state.vao.p->vertexAttribArray.size());
if (UTILS_UNLIKELY(state.vao.p->vertexAttribArray[index])) {
state.vao.p->vertexAttribArray.unset(index);
glDisableVertexAttribArray(index);
}
}
+159 -320
View File
@@ -16,54 +16,21 @@
#include "OpenGLDriver.h"
#include "private/backend/DriverApi.h"
#include "CommandStreamDispatcher.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLDriverFactory.h"
#include "OpenGLProgram.h"
#include "OpenGLTimerQuery.h"
#include "gl_headers.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/BufferDescriptor.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/Platform.h>
#include <backend/Program.h>
#include <backend/SamplerDescriptor.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/Dispatcher.h"
#include "private/backend/DriverApi.h"
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <algorithm>
#include <chrono>
#include <functional>
#include <limits>
#include <memory>
#include <mutex>
#include <new>
#include <utility>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten.h>
@@ -123,28 +90,26 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// GLFence : 8 few
// GLIndexBuffer : 8 moderate
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
// GLFence : 24 few
// GLRenderPrimitive : 32 many
// GLBufferObject : 32 many
// -- less than or equal 32 bytes
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// GLSamplerGroup : 8 few
// -- less than or equal 16 bytes
// GLBufferObject : 24 many
// GLSync : 24 few
// GLTimerQuery : 32 few
// OpenGLProgram : 32 moderate
// GLRenderPrimitive : 48 many
// -- less than or equal 64 bytes
// GLVertexBuffer : 76 moderate
// GLStream : 104 few
// GLTexture : 72 moderate
// GLRenderTarget : 112 few
// GLVertexBufferInfo : 132 moderate
// -- less than or equal to 136 bytes
// GLStream : 184 few
// GLVertexBuffer : 200 moderate
// -- less than or equal to 208 bytes
slog.d
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
<< "HwFence: " << sizeof(HwFence)
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
<< "\nGLSamplerGroup: " << sizeof(GLSamplerGroup)
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
@@ -152,7 +117,7 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
<< "\nGLTimerQuery: " << sizeof(GLTimerQuery)
<< "\nGLStream: " << sizeof(GLStream)
<< "\nGLRenderTarget: " << sizeof(GLRenderTarget)
<< "\nGLFence: " << sizeof(GLFence)
<< "\nGLSync: " << sizeof(GLSync)
<< "\nOpenGLProgram: " << sizeof(OpenGLProgram)
<< io::endl;
#endif
@@ -203,11 +168,9 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(*platform),
mContext(mPlatform),
mContext(),
mShaderCompilerService(*this),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mSamplerMap(32),
mDriverConfig(driverConfig) {
@@ -229,6 +192,8 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
assert_invariant(mContext.ext.EXT_disjoint_timer_query);
#endif
mTimerQueryImpl = OpenGLTimerQueryFactory::init(mPlatform, *this);
mShaderCompilerService.init();
}
@@ -273,6 +238,8 @@ void OpenGLDriver::terminate() {
}
#endif
delete mTimerQueryImpl;
mPlatform.terminate();
}
@@ -419,10 +386,6 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
// Creating driver objects
// ------------------------------------------------------------------------------------------------
Handle<HwVertexBufferInfo> OpenGLDriver::createVertexBufferInfoS() noexcept {
return initHandle<GLVertexBufferInfo>();
}
Handle<HwVertexBuffer> OpenGLDriver::createVertexBufferS() noexcept {
return initHandle<GLVertexBuffer>();
}
@@ -483,21 +446,14 @@ Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
return initHandle<GLTimerQuery>();
}
void OpenGLDriver::createVertexBufferInfoR(
Handle<HwVertexBufferInfo> vbih,
uint8_t bufferCount,
uint8_t attributeCount,
AttributeArray attributes) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
void OpenGLDriver::createVertexBufferR(
Handle<HwVertexBuffer> vbh,
uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
uint8_t bufferCount,
uint8_t attributeCount,
uint32_t elementCount,
AttributeArray attributes) {
DEBUG_MARKER()
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
}
void OpenGLDriver::createIndexBufferR(
@@ -545,33 +501,31 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
DEBUG_MARKER()
auto& gl = mContext;
GLVertexBuffer const* const eb = handle_cast<const GLVertexBuffer*>(vbh);
GLIndexBuffer const* const ib = handle_cast<const GLIndexBuffer*>(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
GLVertexBuffer* const vb = handle_cast<GLVertexBuffer*>(vbh);
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive*>(rph);
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
rp->vbih = vb->vbih;
rp->offset = offset * rp->gl.indicesSize;
rp->count = count;
rp->minIndex = minIndex;
rp->maxIndex = maxIndex > minIndex ? maxIndex : rp->maxVertexCount - 1; // sanitize max index
// create a name for this VAO in the current context
gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]);
gl.procs.genVertexArrays(1, &rp->gl.vao);
// this implies our name is up-to-date
rp->gl.nameVersion = gl.state.age;
// binding the VAO will actually create it
gl.bindVertexArray(&rp->gl);
// Note: we don't update the vertex buffer bindings in the VAO just yet, we will do that
// later in draw() or bindRenderPrimitive(). At this point, the HwVertexBuffer might not
// have all its buffers set.
// update the VBO bindings in the VAO
updateVertexArrayObject(rp, eb);
// this records the index buffer into the currently bound VAO
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
@@ -618,22 +572,13 @@ void OpenGLDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size
UTILS_NOINLINE
void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
uint32_t width, uint32_t height, uint32_t depth, bool useProtectedMemory) noexcept {
uint32_t width, uint32_t height, uint32_t depth) noexcept {
auto& gl = mContext;
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
#ifdef GL_EXT_protected_textures
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(useProtectedMemory)) {
assert_invariant(gl.ext.EXT_protected_textures);
glTexParameteri(t->gl.target, GL_TEXTURE_PROTECTED_EXT, 1);
}
#endif
#endif
switch (t->gl.target) {
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
@@ -724,12 +669,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
GLenum internalFormat = getInternalFormat(format);
assert_invariant(internalFormat);
if (UTILS_UNLIKELY(usage & TextureUsage::PROTECTED)) {
// renderbuffers don't have a protected mode, so we need to use a texture
// because protected textures are only supported on GLES 3.2, MSAA will be available.
usage |= TextureUsage::SAMPLEABLE;
}
auto& gl = mContext;
samples = std::clamp(samples, uint8_t(1u), uint8_t(gl.gets.max_samples));
GLTexture* t = construct<GLTexture>(th, target, levels, samples, w, h, depth, format, usage);
@@ -765,23 +704,23 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
// we can't be here -- doesn't matter what we do
case SamplerType::SAMPLER_2D:
t->gl.target = GL_TEXTURE_2D;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
break;
case SamplerType::SAMPLER_3D:
t->gl.target = GL_TEXTURE_3D;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
break;
case SamplerType::SAMPLER_2D_ARRAY:
t->gl.target = GL_TEXTURE_2D_ARRAY;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
break;
case SamplerType::SAMPLER_CUBEMAP:
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}
@@ -793,14 +732,13 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
t->gl.targetIndex = (uint8_t)
OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
#endif
}
textureStorage(t, w, h, depth, bool(usage & TextureUsage::PROTECTED));
textureStorage(t, w, h, depth);
}
} else {
assert_invariant(any(usage & (
@@ -809,7 +747,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
TextureUsage::STENCIL_ATTACHMENT)));
assert_invariant(levels == 1);
assert_invariant(target == SamplerType::SAMPLER_2D);
assert_invariant(none(usage & TextureUsage::PROTECTED));
t->gl.internalFormat = internalFormat;
t->gl.target = GL_RENDERBUFFER;
glGenRenderbuffers(1, &t->gl.id);
@@ -864,27 +801,27 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
switch (target) {
case SamplerType::SAMPLER_EXTERNAL:
t->gl.target = GL_TEXTURE_EXTERNAL_OES;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
break;
case SamplerType::SAMPLER_2D:
t->gl.target = GL_TEXTURE_2D;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
break;
case SamplerType::SAMPLER_3D:
t->gl.target = GL_TEXTURE_3D;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
break;
case SamplerType::SAMPLER_2D_ARRAY:
t->gl.target = GL_TEXTURE_2D_ARRAY;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
break;
case SamplerType::SAMPLER_CUBEMAP:
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}
@@ -895,7 +832,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
@@ -906,65 +843,61 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
}
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
// NOTE: this is called from draw() and must be as efficient as possible.
auto& gl = mContext;
// NOTE: this is called from draw() and must be as efficient as possible.
if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) {
// The VAO for the given render primitive must already be bound.
#ifndef NDEBUG
GLint vaoBinding;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vaoBinding);
assert_invariant(vaoBinding == (GLint)rp->gl.vao[gl.contextIndex]);
assert_invariant(vaoBinding == (GLint)rp->gl.vao);
#endif
rp->gl.vertexBufferVersion = vb->bufferObjectsVersion;
} else {
// if we don't have OES_vertex_array_object, we never update the buffer version so
// that it's always reset in draw
}
if (UTILS_LIKELY(rp->gl.vertexBufferVersion == vb->bufferObjectsVersion &&
rp->gl.stateVersion == gl.state.age)) {
return;
}
GLVertexBufferInfo const* const vbi = handle_cast<const GLVertexBufferInfo*>(vb->vbih);
for (size_t i = 0, n = vbi->attributes.size(); i < n; i++) {
const auto& attribute = vbi->attributes[i];
rp->maxVertexCount = vb->vertexCount;
for (size_t i = 0, n = vb->attributes.size(); i < n; i++) {
const auto& attribute = vb->attributes[i];
const uint8_t bi = attribute.buffer;
if (bi != Attribute::BUFFER_UNUSED) {
// if a buffer is defined it must not be invalid.
assert_invariant(vb->gl.buffers[bi]);
// if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET
// Invoking glVertexAttribPointer without a bound VBO is an invalid operation, so we must
// take care to avoid it. This can occur when VertexBuffer is only partially populated with
// BufferObject items.
if (bi != Attribute::BUFFER_UNUSED && UTILS_LIKELY(vb->gl.buffers[bi] != 0)) {
assert_invariant(!(gl.isES2() && (attribute.flags & Attribute::FLAG_INTEGER_TARGET)));
gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]);
GLuint const index = i;
GLint const size = (GLint)getComponentCount(attribute.type);
GLenum const type = getComponentType(attribute.type);
GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED);
GLsizei const stride = attribute.stride;
void const* pointer = reinterpret_cast<void const *>(attribute.offset);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
// integer attributes can't be floats
assert_invariant(type == GL_BYTE || type == GL_UNSIGNED_BYTE || type == GL_SHORT ||
type == GL_UNSIGNED_SHORT || type == GL_INT || type == GL_UNSIGNED_INT);
glVertexAttribIPointer(index, size, type, stride, pointer);
glVertexAttribIPointer(GLuint(i),
(GLint)getComponentCount(attribute.type),
getComponentType(attribute.type),
attribute.stride,
(void*) uintptr_t(attribute.offset));
} else
#endif
{
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
glVertexAttribPointer(GLuint(i),
(GLint)getComponentCount(attribute.type),
getComponentType(attribute.type),
getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED),
attribute.stride,
(void*) uintptr_t(attribute.offset));
}
gl.enableVertexAttribArray(&rp->gl, GLuint(i));
gl.enableVertexAttribArray(GLuint(i));
} else {
// In some OpenGL implementations, we must supply a properly-typed placeholder for
// every integer input that is declared in the vertex shader.
// Note that the corresponding doesn't have to be enabled and in fact won't be. If it
// was enabled, it would indicate a user-error (providing the wrong type of array).
// With a disabled array, the vertex shader gets the attribute from glVertexAttrib,
// and must have the proper intergerness.
// But at this point, we don't know what the shader requirements are, and so we must
// rely on the attribute.
// In some OpenGL implementations, we must supply a properly-typed placeholder for
// every integer input that is declared in the vertex shader, even if disabled.
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
if (!gl.isES2()) {
@@ -977,21 +910,13 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
glVertexAttrib4f(GLuint(i), 0, 0, 0, 0);
}
gl.disableVertexAttribArray(&rp->gl, GLuint(i));
gl.disableVertexAttribArray(GLuint(i));
}
}
rp->gl.stateVersion = gl.state.age;
if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) {
rp->gl.vertexBufferVersion = vb->bufferObjectsVersion;
} else {
// if we don't have OES_vertex_array_object, we never update the buffer version so
// that it's always reset in draw
}
}
void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept {
GLRenderTarget const* rt, GLenum attachment) noexcept {
#if !defined(NDEBUG)
// Only used by assert_invariant() checks below
@@ -1129,20 +1054,9 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
// TODO: support multiview for iOS and WebGL
#if !defined(__EMSCRIPTEN__) && !defined(IOS)
if (layerCount > 1) {
// if layerCount > 1, it means we use the multiview extension.
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
t->gl.id, 0, binfo.baseViewIndex, layerCount);
} else
#endif // !defined(__EMSCRIPTEN__) && !defined(IOS)
{
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
}
#endif
break;
default:
@@ -1303,9 +1217,11 @@ void OpenGLDriver::createDefaultRenderTargetR(
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
uint32_t const framebuffer = mPlatform.createDefaultRenderTarget();
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
rt->gl.isDefault = true;
rt->gl.fbo = 0; // the actual id is resolved at binding time
rt->gl.fbo = framebuffer;
rt->gl.samples = 1;
// FIXME: these flags should reflect the actual attachments present
rt->targets = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
@@ -1316,7 +1232,6 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
uint32_t width,
uint32_t height,
uint8_t samples,
uint8_t layerCount,
MRT color,
TargetBufferInfo depth,
TargetBufferInfo stencil) {
@@ -1383,7 +1298,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (any(targets & getTargetBufferFlagsAt(i))) {
assert_invariant(color[i].handle);
rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i, layerCount);
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i);
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
checkDimensions(rt->gl.color[i], color[i].level);
}
@@ -1405,7 +1320,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
// either we supplied only the depth handle or both depth/stencil are identical and not null
if (depth.handle && (stencil.handle == depth.handle || !stencil.handle)) {
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT, layerCount);
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT);
specialCased = true;
checkDimensions(rt->gl.depth, depth.level);
}
@@ -1416,13 +1331,13 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (any(targets & TargetBufferFlags::DEPTH)) {
assert_invariant(depth.handle);
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT, layerCount);
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT);
checkDimensions(rt->gl.depth, depth.level);
}
if (any(targets & TargetBufferFlags::STENCIL)) {
assert_invariant(stencil.handle);
rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT, layerCount);
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT);
checkDimensions(rt->gl.stencil, stencil.level);
}
}
@@ -1464,13 +1379,6 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
#if !defined(__EMSCRIPTEN__)
// note: in practice this should never happen on Android
ASSERT_POSTCONDITION(sc->swapChain,
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
nativeWindow, flags);
#endif
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
@@ -1485,13 +1393,6 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
#if !defined(__EMSCRIPTEN__)
// note: in practice this should never happen on Android
ASSERT_POSTCONDITION(sc->swapChain,
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
width, height, flags);
#endif
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
@@ -1502,21 +1403,13 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.createTimerQuery(tq);
mTimerQueryImpl->createTimerQuery(tq);
}
// ------------------------------------------------------------------------------------------------
// Destroying driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
DEBUG_MARKER()
if (vbih) {
GLVertexBufferInfo const* vbi = handle_cast<const GLVertexBufferInfo*>(vbih);
destruct(vbih, vbi);
}
}
void OpenGLDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
DEBUG_MARKER()
if (vbh) {
@@ -1556,20 +1449,7 @@ void OpenGLDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (rph) {
auto& gl = mContext;
GLRenderPrimitive const* rp = handle_cast<const GLRenderPrimitive*>(rph);
gl.deleteVertexArray(rp->gl.vao[gl.contextIndex]);
// If we have a name in the "other" context, we need to schedule the destroy for
// later, because it can't be done here. VAOs are "container objects" and are not
// shared between contexts.
size_t const otherContextIndex = 1 - gl.contextIndex;
GLuint const nameInOtherContext = rp->gl.vao[otherContextIndex];
if (UTILS_UNLIKELY(nameInOtherContext)) {
gl.destroyWithContext(otherContextIndex,
[name = nameInOtherContext](OpenGLContext& gl) {
gl.deleteVertexArray(name);
});
}
gl.deleteVertexArrays(1, &rp->gl.vao);
destruct(rph, rp);
}
}
@@ -1634,11 +1514,11 @@ void OpenGLDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
if (rt->gl.fbo) {
// first unbind this framebuffer if needed
gl.unbindFramebuffer(GL_FRAMEBUFFER);
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
}
if (rt->gl.fbo_read) {
// first unbind this framebuffer if needed
gl.unbindFramebuffer(GL_FRAMEBUFFER);
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -1710,7 +1590,7 @@ void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (tqh) {
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.destroyTimerQuery(tq);
mTimerQueryImpl->destroyTimerQuery(tq);
destruct(tqh, tq);
}
}
@@ -2001,7 +1881,7 @@ bool OpenGLDriver::isFrameBufferFetchMultiSampleSupported() {
}
bool OpenGLDriver::isFrameTimeSupported() {
return TimerQueryFactory::isGpuTimeSupported();
return OpenGLTimerQueryFactory::isGpuTimeSupported();
}
bool OpenGLDriver::isAutoDepthResolveSupported() {
@@ -2019,24 +1899,12 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
return mPlatform.isSRGBSwapChainSupported();
}
bool OpenGLDriver::isProtectedContentSupported() {
return mPlatform.isProtectedContextSupported();
}
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
// Instanced-stereo requires instancing and EXT_clip_cull_distance.
// Multiview-stereo requires ES 3.0 and OVR_multiview2.
bool OpenGLDriver::isStereoSupported() {
// Stereo requires instancing and EXT_clip_cull_distance.
if (UTILS_UNLIKELY(mContext.isES2())) {
return false;
}
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return mContext.ext.EXT_clip_cull_distance;
case backend::StereoscopicType::MULTIVIEW:
return mContext.ext.OVR_multiview2;
default:
return false;
}
return mContext.ext.EXT_clip_cull_distance;
}
bool OpenGLDriver::isParallelShaderCompileSupported() {
@@ -2047,10 +1915,6 @@ bool OpenGLDriver::isDepthStencilResolveSupported() {
return true;
}
bool OpenGLDriver::isProtectedTexturesSupported() {
return getContext().ext.EXT_protected_textures;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -2118,18 +1982,7 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
GLSwapChain* scDraw = handle_cast<GLSwapChain*>(schDraw);
GLSwapChain* scRead = handle_cast<GLSwapChain*>(schRead);
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain,
[this]() {
// OpenGL context is about to change, unbind everything
mContext.unbindEverything();
},
[this](size_t index) {
// OpenGL context has changed, resynchronize the state with the cache
mContext.synchronizeStateAndCache(index);
slog.d << "*** OpenGL context change : " << (index ? "protected" : "default") << io::endl;
});
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain);
mCurrentDrawSwapChain = scDraw;
// From the GL spec for glViewport and glScissor:
@@ -2537,7 +2390,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
// specify levels as they become available.
if (int8_t(level) < t->gl.baseLevel) {
t->gl.baseLevel = int8_t(level);
@@ -2638,7 +2491,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
// specify levels as they become available.
if (int8_t(level) < t->gl.baseLevel) {
t->gl.baseLevel = int8_t(level);
@@ -2777,21 +2630,21 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
void OpenGLDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.beginTimeElapsedQuery(tq);
mTimerQueryImpl->beginTimeElapsedQuery(tq);
}
void OpenGLDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.endTimeElapsedQuery(*this, tq);
mTimerQueryImpl->endTimeElapsedQuery(tq);
}
TimerQueryResult OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
bool OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
return TimerQueryFactoryInterface::getTimerQueryValue(tq, elapsedTime);
return OpenGLTimerQueryInterface::getTimerQueryValue(tq, elapsedTime);
}
void OpenGLDriver::compilePrograms(CompilerPriorityQueue,
void OpenGLDriver::compilePrograms(CompilerPriorityQueue priority,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
if (callback) {
getShaderCompilerService().notifyWhenAllProgramsAreReady(handler, callback, user);
@@ -2819,13 +2672,13 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
const TargetBufferFlags clearFlags = params.flags.clear & rt->targets;
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
if (gl.ext.EXT_discard_framebuffer
&& !gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, discardFlags, !fbo);
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2913,9 +2766,9 @@ void OpenGLDriver::endRenderPass(int) {
}
if (!gl.bugs.disable_invalidate_framebuffer) {
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, effectiveDiscardFlags, !fbo);
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2977,48 +2830,50 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
}
GLsizei OpenGLDriver::getAttachments(AttachmentArray& attachments,
TargetBufferFlags buffers, bool isDefaultFramebuffer) noexcept {
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept {
assert_invariant(buffers <= rt->targets);
GLsizei attachmentCount = 0;
// the default framebuffer uses different constants!!!
const bool defaultFramebuffer = (rt->gl.fbo == 0);
if (any(buffers & TargetBufferFlags::COLOR0)) {
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
attachments[attachmentCount++] = defaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (any(buffers & TargetBufferFlags::COLOR1)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT1;
}
if (any(buffers & TargetBufferFlags::COLOR2)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT2;
}
if (any(buffers & TargetBufferFlags::COLOR3)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT3;
}
if (any(buffers & TargetBufferFlags::COLOR4)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT4;
}
if (any(buffers & TargetBufferFlags::COLOR5)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT5;
}
if (any(buffers & TargetBufferFlags::COLOR6)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT6;
}
if (any(buffers & TargetBufferFlags::COLOR7)) {
assert_invariant(!isDefaultFramebuffer);
assert_invariant(!defaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT7;
}
#endif
if (any(buffers & TargetBufferFlags::DEPTH)) {
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
attachments[attachmentCount++] = defaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
}
if (any(buffers & TargetBufferFlags::STENCIL)) {
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
attachments[attachmentCount++] = defaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
}
return attachmentCount;
}
@@ -3738,8 +3593,8 @@ void OpenGLDriver::blit(
mask, GL_NEAREST);
CHECK_GL_ERROR(utils::slog.e)
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(2, fbo);
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
@@ -3843,52 +3698,51 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::bindPipeline(PipelineState state) {
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
DEBUG_MARKER()
auto& gl = mContext;
setRasterState(state.rasterState);
setStencilState(state.stencilState);
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);
}
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
DEBUG_MARKER()
auto& gl = mContext;
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors that can occur during the draw call when the program
// is invalid. The shader compile error has already been dumped to the console at this
// point, so it's fine to simply return early.
return;
}
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
if (UTILS_UNLIKELY(!vb)) {
mBoundRenderPrimitive = nullptr;
return;
}
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
gl.bindVertexArray(&rp->gl);
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
updateVertexArrayObject(rp, glvb);
mBoundRenderPrimitive = rp;
}
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
if (UTILS_UNLIKELY(rp->gl.vertexBufferVersion != glvb->bufferObjectsVersion)) {
updateVertexArrayObject(rp, glvb);
}
setRasterState(state.rasterState);
setStencilState(state.stencilState);
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
setScissor(state.scissor);
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize));
glDrawElements(GLenum(rp->type), (GLsizei)rp->count, rp->gl.getIndicesType(),
reinterpret_cast<const void*>(rp->offset));
} else {
assert_invariant(!mContext.isES2());
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)rp->count,
rp->gl.getIndicesType(), reinterpret_cast<const void*>(rp->offset),
(GLsizei)instanceCount);
#endif
}
@@ -3900,21 +3754,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
#endif
}
void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
+32 -57
View File
@@ -18,42 +18,27 @@
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVER_H
#include "DriverBase.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLTimerQuery.h"
#include "ShaderCompilerService.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h"
#include <utils/FixedCapacityVector.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/AcquiredImage.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Allocator.h>
#include <math/vec4.h>
#include <tsl/robin_map.h>
#include <array>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#include <set>
#ifndef FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB 4
@@ -66,17 +51,15 @@ class PixelBufferDescriptor;
struct TargetBufferInfo;
class OpenGLProgram;
class TimerQueryFactoryInterface;
class OpenGLTimerQueryInterface;
class OpenGLDriver final : public DriverBase {
inline explicit OpenGLDriver(OpenGLPlatform* platform,
const Platform::DriverConfig& driverConfig) noexcept;
inline explicit OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept;
~OpenGLDriver() noexcept final;
Dispatcher getDispatcher() const noexcept final;
public:
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept;
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
OpenGLDriver& driver;
@@ -111,22 +94,8 @@ public:
uint16_t age = 0;
};
struct GLVertexBufferInfo : public HwVertexBufferInfo {
GLVertexBufferInfo() noexcept = default;
GLVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {
}
AttributeArray attributes;
};
struct GLVertexBuffer : public HwVertexBuffer {
GLVertexBuffer() noexcept = default;
GLVertexBuffer(uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih) {
}
Handle<HwVertexBufferInfo> vbih;
using HwVertexBuffer::HwVertexBuffer;
struct {
// 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes
std::array<GLuint, MAX_VERTEX_ATTRIBUTE_COUNT> buffers{};
@@ -154,7 +123,6 @@ public:
struct GLRenderPrimitive : public HwRenderPrimitive {
using HwRenderPrimitive::HwRenderPrimitive;
OpenGLContext::RenderPrimitive gl;
Handle<HwVertexBufferInfo> vbih;
};
struct GLTexture : public HwTexture {
@@ -179,7 +147,15 @@ public:
OpenGLPlatform::ExternalTexture* externalTexture = nullptr;
};
using GLTimerQuery = filament::backend::GLTimerQuery;
struct GLTimerQuery : public HwTimerQuery {
struct State {
struct {
GLuint query;
} gl;
std::atomic<int64_t> elapsed{};
};
std::shared_ptr<State> state;
};
struct GLStream : public HwStream {
using HwStream::HwStream;
@@ -233,8 +209,8 @@ private:
OpenGLContext mContext;
ShaderCompilerService mShaderCompilerService;
friend class TimerQueryFactory;
friend class TimerQueryNativeFactory;
friend class OpenGLTimerQueryFactory;
friend class TimerQueryNative;
OpenGLContext& getContext() noexcept { return mContext; }
ShaderCompilerService& getShaderCompilerService() noexcept {
@@ -314,7 +290,7 @@ private:
void updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb);
void framebufferTexture(TargetBufferInfo const& binfo,
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept;
GLRenderTarget const* rt, GLenum attachment) noexcept;
void setRasterState(RasterState rs) noexcept;
@@ -335,8 +311,8 @@ private:
void renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width,
uint32_t height, uint8_t samples) const noexcept;
void textureStorage(OpenGLDriver::GLTexture* t, uint32_t width, uint32_t height,
uint32_t depth, bool useProtectedMemory) noexcept;
void textureStorage(GLTexture* t,
uint32_t width, uint32_t height, uint32_t depth) noexcept;
/* State tracking GL wrappers... */
@@ -369,8 +345,8 @@ private:
}
using AttachmentArray = std::array<GLenum, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 2>;
static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers,
bool isDefaultFramebuffer) noexcept;
static GLsizei getAttachments(AttachmentArray& attachments,
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept;
// state required to represent the current render pass
Handle<HwRenderTarget> mRenderPassTarget;
@@ -379,10 +355,6 @@ private:
GLboolean mRenderPassDepthWrite{};
GLboolean mRenderPassStencilWrite{};
GLRenderPrimitive const* mBoundRenderPrimitive = nullptr;
bool mValidProgram = false;
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
@@ -425,6 +397,9 @@ private:
void executeEveryNowAndThenOps() noexcept;
std::vector<std::function<bool()>> mEveryNowAndThenOps;
// timer query implementation
OpenGLTimerQueryInterface* mTimerQueryImpl = nullptr;
const Platform::DriverConfig mDriverConfig;
Platform::DriverConfig const& getDriverConfig() const noexcept { return mDriverConfig; }
+2 -30
View File
@@ -18,17 +18,6 @@
#include "OpenGLDriverFactory.h"
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
namespace filament::backend {
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
@@ -38,27 +27,14 @@ Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
OpenGLPlatform::~OpenGLPlatform() noexcept = default;
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
}
bool OpenGLPlatform::isProtectedContextSupported() const noexcept {
return false;
}
bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
uint32_t OpenGLPlatform::createDefaultRenderTarget() noexcept {
return 0;
}
OpenGLPlatform::ContextType OpenGLPlatform::getCurrentContextType() const noexcept {
return ContextType::UNPROTECTED;
}
void OpenGLPlatform::setPresentationTime(
UTILS_UNUSED int64_t presentationTimeInNanosecond) noexcept {
}
@@ -129,14 +105,10 @@ AcquiredImage OpenGLPlatform::transformAcquiredImage(AcquiredImage source) noexc
return source;
}
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain*) noexcept {
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain* swapChain) noexcept {
return TargetBufferFlags::NONE;
}
bool OpenGLPlatform::isSwapChainProtected(UTILS_UNUSED SwapChain*) noexcept {
return false;
}
bool OpenGLPlatform::isExtraContextSupported() const noexcept {
return false;
}
+2 -11
View File
@@ -16,25 +16,16 @@
#include "OpenGLProgram.h"
#include "GLUtils.h"
#include "BlobCacheKey.h"
#include "OpenGLDriver.h"
#include "ShaderCompilerService.h"
#include <backend/Program.h>
#include <private/backend/BackendUtils.h>
#include <utils/debug.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <array>
#include <string_view>
#include <utility>
#include <new>
#include <stddef.h>
#include <private/backend/BackendUtils.h>
namespace filament::backend {
@@ -28,9 +28,6 @@
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <array>
#include <limits>
#include <stddef.h>
#include <stdint.h>
@@ -16,132 +16,110 @@
#include "OpenGLTimerQuery.h"
#include "GLUtils.h"
#include "OpenGLDriver.h"
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/Systrace.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <new>
#include <utility>
#include <stdint.h>
#include <utils/debug.h>
namespace filament::backend {
using namespace backend;
using namespace GLUtils;
class OpenGLDriver;
// ------------------------------------------------------------------------------------------------
bool TimerQueryFactory::mGpuTimeSupported = false;
bool OpenGLTimerQueryFactory::mGpuTimeSupported = false;
TimerQueryFactoryInterface* TimerQueryFactory::init(
OpenGLPlatform& platform, OpenGLContext& context) noexcept {
(void)context;
OpenGLTimerQueryInterface* OpenGLTimerQueryFactory::init(
OpenGLPlatform& platform, OpenGLDriver& driver) noexcept {
(void)driver;
TimerQueryFactoryInterface* impl = nullptr;
OpenGLTimerQueryInterface* impl;
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
auto& context = driver.getContext();
if (context.ext.EXT_disjoint_timer_query) {
// timer queries are available
if (context.bugs.dont_use_timer_query && platform.canCreateFence()) {
// however, they don't work well, revert to using fences if we can.
impl = new(std::nothrow) TimerQueryFenceFactory(platform);
impl = new(std::nothrow) OpenGLTimerQueryFence(platform);
} else {
impl = new(std::nothrow) TimerQueryNativeFactory(context);
impl = new(std::nothrow) TimerQueryNative(driver);
}
mGpuTimeSupported = true;
} else
#endif
if (platform.canCreateFence()) {
// no timer queries, but we can use fences
impl = new(std::nothrow) TimerQueryFenceFactory(platform);
impl = new(std::nothrow) OpenGLTimerQueryFence(platform);
mGpuTimeSupported = true;
} else {
// no queries, no fences -- that's a problem
impl = new(std::nothrow) TimerQueryFallbackFactory();
impl = new(std::nothrow) TimerQueryFallback();
mGpuTimeSupported = false;
}
assert_invariant(impl);
return impl;
}
// ------------------------------------------------------------------------------------------------
TimerQueryFactoryInterface::~TimerQueryFactoryInterface() = default;
OpenGLTimerQueryInterface::~OpenGLTimerQueryInterface() = default;
// This is a backend synchronous call
TimerQueryResult TimerQueryFactoryInterface::getTimerQueryValue(
GLTimerQuery* tq, uint64_t* elapsedTime) noexcept {
bool OpenGLTimerQueryInterface::getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept {
if (UTILS_LIKELY(tq->state)) {
int64_t const elapsed = tq->state->elapsed.load(std::memory_order_relaxed);
if (elapsed > 0) {
bool const available = elapsed > 0;
if (available) {
*elapsedTime = elapsed;
return TimerQueryResult::AVAILABLE;
}
return TimerQueryResult(elapsed);
return available;
}
return TimerQueryResult::ERROR;
return false;
}
// ------------------------------------------------------------------------------------------------
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
TimerQueryNativeFactory::TimerQueryNativeFactory(OpenGLContext& context)
: mContext(context) {
TimerQueryNative::TimerQueryNative(OpenGLDriver& driver)
: mDriver(driver) {
}
TimerQueryNativeFactory::~TimerQueryNativeFactory() = default;
TimerQueryNative::~TimerQueryNative() = default;
void TimerQueryNativeFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
mContext.procs.genQueries(1u, &tq->state->gl.query);
void TimerQueryNative::createTimerQuery(GLTimerQuery* tq) {
if (UTILS_UNLIKELY(!tq->state)) {
tq->state = std::make_shared<GLTimerQuery::State>();
}
mDriver.getContext().procs.genQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNativeFactory::destroyTimerQuery(GLTimerQuery* tq) {
void TimerQueryNative::destroyTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
mContext.procs.deleteQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR(utils::slog.e)
tq->state.reset();
}
void TimerQueryNativeFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
mContext.procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
mDriver.getContext().procs.deleteQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) {
void TimerQueryNative::beginTimeElapsedQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
tq->state->elapsed.store(0);
mDriver.getContext().procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
CHECK_GL_ERROR(utils::slog.e)
}
mContext.procs.endQuery(GL_TIME_ELAPSED);
void TimerQueryNative::endTimeElapsedQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
mDriver.getContext().procs.endQuery(GL_TIME_ELAPSED);
CHECK_GL_ERROR(utils::slog.e)
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
mDriver.runEveryNowAndThen([context = mDriver.getContext(), weak]() -> bool {
auto state = weak.lock();
if (state) {
GLuint available = 0;
@@ -155,8 +133,6 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
// 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);
}
return true;
});
@@ -166,7 +142,7 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
// ------------------------------------------------------------------------------------------------
TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
OpenGLTimerQueryFence::OpenGLTimerQueryFence(OpenGLPlatform& platform)
: mPlatform(platform) {
mQueue.reserve(2);
mThread = std::thread([this]() {
@@ -190,8 +166,7 @@ TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
});
}
TimerQueryFenceFactory::~TimerQueryFenceFactory() {
assert_invariant(mQueue.empty());
OpenGLTimerQueryFence::~OpenGLTimerQueryFence() {
if (mThread.joinable()) {
std::unique_lock<utils::Mutex> lock(mLock);
mExitRequested = true;
@@ -203,26 +178,27 @@ TimerQueryFenceFactory::~TimerQueryFenceFactory() {
}
}
void TimerQueryFenceFactory::push(TimerQueryFenceFactory::Job&& job) {
void OpenGLTimerQueryFence::enqueue(OpenGLTimerQueryFence::Job&& job) {
std::unique_lock<utils::Mutex> const lock(mLock);
mQueue.push_back(std::move(job));
mQueue.push_back(std::forward<Job>(job));
mCondition.notify_one();
}
void TimerQueryFenceFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
void OpenGLTimerQueryFence::createTimerQuery(GLTimerQuery* tq) {
if (UTILS_UNLIKELY(!tq->state)) {
tq->state = std::make_shared<GLTimerQuery::State>();
}
}
void TimerQueryFenceFactory::destroyTimerQuery(GLTimerQuery* tq) {
void OpenGLTimerQueryFence::destroyTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
tq->state.reset();
}
void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
void OpenGLTimerQueryFence::beginTimeElapsedQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
tq->state->elapsed.store(0);
Platform::Fence* fence = mPlatform.createFence();
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
// FIXME: this implementation of beginTimeElapsedQuery is usually wrong; it ends up
@@ -231,11 +207,12 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
// on a dummy target for instance, or somehow latch the begin time at the next renderpass
// start.
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
push([&platform = mPlatform, fence, weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
state->then = clock::now().time_since_epoch().count();
int64_t const then = clock::now().time_since_epoch().count();
state->elapsed.store(-then, std::memory_order_relaxed);
SYSTRACE_CONTEXT();
SYSTRACE_ASYNC_BEGIN("OpenGLTimerQueryFence", intptr_t(state.get()));
}
@@ -243,16 +220,19 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
});
}
void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq) {
void OpenGLTimerQueryFence::endTimeElapsedQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
Platform::Fence* fence = mPlatform.createFence();
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
push([&platform = mPlatform, fence, weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
int64_t const now = clock::now().time_since_epoch().count();
state->elapsed.store(now - state->then, std::memory_order_relaxed);
int64_t const then = state->elapsed.load(std::memory_order_relaxed);
assert_invariant(then < 0);
state->elapsed.store(now + then, std::memory_order_relaxed);
SYSTRACE_CONTEXT();
SYSTRACE_ASYNC_END("OpenGLTimerQueryFence", intptr_t(state.get()));
}
@@ -262,32 +242,34 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
// ------------------------------------------------------------------------------------------------
TimerQueryFallbackFactory::TimerQueryFallbackFactory() = default;
TimerQueryFallback::TimerQueryFallback() = default;
TimerQueryFallbackFactory::~TimerQueryFallbackFactory() = default;
TimerQueryFallback::~TimerQueryFallback() = default;
void TimerQueryFallbackFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
void TimerQueryFallback::createTimerQuery(GLTimerQuery* tq) {
if (UTILS_UNLIKELY(!tq->state)) {
tq->state = std::make_shared<GLTimerQuery::State>();
}
}
void TimerQueryFallbackFactory::destroyTimerQuery(GLTimerQuery* tq) {
void TimerQueryFallback::destroyTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
tq->state.reset();
}
void TimerQueryFallbackFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
void TimerQueryFallback::beginTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* tq) {
assert_invariant(tq->state);
// this implementation measures the CPU time, but we have no h/w support
tq->state->then = clock::now().time_since_epoch().count();
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
int64_t const then = clock::now().time_since_epoch().count();
tq->state->elapsed.store(-then, std::memory_order_relaxed);
}
void TimerQueryFallbackFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq) {
void TimerQueryFallback::endTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* tq) {
assert_invariant(tq->state);
// this implementation measures the CPU time, but we have no h/w support
int64_t const now = clock::now().time_since_epoch().count();
tq->state->elapsed.store(now - tq->state->then, std::memory_order_relaxed);
int64_t const then = tq->state->elapsed.load(std::memory_order_relaxed);
assert_invariant(then < 0);
tq->state->elapsed.store(now + then, std::memory_order_relaxed);
}
} // namespace filament::backend
+30 -50
View File
@@ -17,41 +17,18 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_TIMERQUERY_H
#define TNT_FILAMENT_BACKEND_OPENGL_TIMERQUERY_H
#include <backend/DriverEnums.h>
#include "DriverBase.h"
#include "OpenGLDriver.h"
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include "gl_headers.h"
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <thread>
#include <vector>
#include <stdint.h>
namespace filament::backend {
class OpenGLPlatform;
class OpenGLContext;
class OpenGLDriver;
class TimerQueryFactoryInterface;
struct GLTimerQuery : public HwTimerQuery {
struct State {
struct {
GLuint query;
} gl;
int64_t then{};
std::atomic<int64_t> elapsed{};
};
std::shared_ptr<State> state;
};
class OpenGLTimerQueryInterface;
/*
* We need two implementation of timer queries (only elapsed time), because
@@ -61,80 +38,83 @@ struct GLTimerQuery : public HwTimerQuery {
* These classes implement the various strategies...
*/
class TimerQueryFactory {
class OpenGLTimerQueryFactory {
static bool mGpuTimeSupported;
public:
static TimerQueryFactoryInterface* init(
OpenGLPlatform& platform, OpenGLContext& context) noexcept;
static OpenGLTimerQueryInterface* init(
OpenGLPlatform& platform, OpenGLDriver& driver) noexcept;
static bool isGpuTimeSupported() noexcept {
return mGpuTimeSupported;
}
};
class TimerQueryFactoryInterface {
class OpenGLTimerQueryInterface {
protected:
using GLTimerQuery = filament::backend::GLTimerQuery;
using GLTimerQuery = OpenGLDriver::GLTimerQuery;
using clock = std::chrono::steady_clock;
public:
virtual ~TimerQueryFactoryInterface();
virtual ~OpenGLTimerQueryInterface();
virtual void createTimerQuery(GLTimerQuery* query) = 0;
virtual void destroyTimerQuery(GLTimerQuery* query) = 0;
virtual void beginTimeElapsedQuery(GLTimerQuery* query) = 0;
virtual void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) = 0;
virtual void endTimeElapsedQuery(GLTimerQuery* query) = 0;
static TimerQueryResult getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept;
static bool getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept;
};
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
class TimerQueryNativeFactory final : public TimerQueryFactoryInterface {
class TimerQueryNative : public OpenGLTimerQueryInterface {
public:
explicit TimerQueryNativeFactory(OpenGLContext& context);
~TimerQueryNativeFactory() override;
explicit TimerQueryNative(OpenGLDriver& driver);
~TimerQueryNative() override;
private:
void createTimerQuery(GLTimerQuery* query) override;
void destroyTimerQuery(GLTimerQuery* query) override;
void beginTimeElapsedQuery(GLTimerQuery* query) override;
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
OpenGLContext& mContext;
void endTimeElapsedQuery(GLTimerQuery* query) override;
OpenGLDriver& mDriver;
};
#endif
class TimerQueryFenceFactory final : public TimerQueryFactoryInterface {
class OpenGLTimerQueryFence : public OpenGLTimerQueryInterface {
public:
explicit TimerQueryFenceFactory(OpenGLPlatform& platform);
~TimerQueryFenceFactory() override;
explicit OpenGLTimerQueryFence(OpenGLPlatform& platform);
~OpenGLTimerQueryFence() override;
private:
using Job = std::function<void()>;
using Container = std::vector<Job>;
void createTimerQuery(GLTimerQuery* query) override;
void destroyTimerQuery(GLTimerQuery* query) override;
void beginTimeElapsedQuery(GLTimerQuery* tq) override;
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) override;
void endTimeElapsedQuery(GLTimerQuery* tq) override;
void push(Job&& job);
void enqueue(Job&& job);
template<typename CALLABLE, typename ... ARGS>
void push(CALLABLE&& func, ARGS&& ... args) {
enqueue(Job(std::bind(std::forward<CALLABLE>(func), std::forward<ARGS>(args)...)));
}
OpenGLPlatform& mPlatform;
std::thread mThread;
mutable utils::Mutex mLock;
mutable utils::Condition mCondition;
Container mQueue;
std::vector<Job> mQueue;
bool mExitRequested = false;
};
class TimerQueryFallbackFactory final : public TimerQueryFactoryInterface {
class TimerQueryFallback : public OpenGLTimerQueryInterface {
public:
explicit TimerQueryFallbackFactory();
~TimerQueryFallbackFactory() override;
explicit TimerQueryFallback();
~TimerQueryFallback() override;
private:
void createTimerQuery(GLTimerQuery* query) override;
void destroyTimerQuery(GLTimerQuery* query) override;
void beginTimeElapsedQuery(GLTimerQuery* query) override;
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
void endTimeElapsedQuery(GLTimerQuery* query) override;
};
} // namespace filament::backend
@@ -261,11 +261,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
[this, &gl, program = std::move(program), token]() mutable {
// compile the shaders
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
shaders,
token->shaderSourceCode);
shaderSourceCode);
// link the program
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
@@ -67,9 +67,6 @@ PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#ifdef GL_KHR_parallel_shader_compile
PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#endif
#ifdef GL_OVR_multiview
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
@@ -120,9 +117,6 @@ void importGLESExtensionsEntryPoints() {
#ifdef GL_KHR_parallel_shader_compile
getProcAddress(glMaxShaderCompilerThreadsKHR, "glMaxShaderCompilerThreadsKHR");
#endif
#ifdef GL_OVR_multiview
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
getProcAddress(glDispatchCompute, "glDispatchCompute");
#endif
-3
View File
@@ -151,9 +151,6 @@ extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#ifdef GL_KHR_parallel_shader_compile
extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#endif
#ifdef GL_OVR_multiview
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
@@ -247,8 +247,8 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
delete cocoaSwapChain;
}
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
@@ -287,7 +287,6 @@ bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
swapChain->previousBounds = currentBounds;
swapChain->previousWindowFrame = currentWindowFrame;
return true;
}
void PlatformCocoaGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -143,12 +143,11 @@ void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noex
}
}
uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
uint32_t PlatformCocoaTouchGL::createDefaultRenderTarget() noexcept {
return pImpl->mDefaultFramebuffer;
}
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
@@ -183,7 +182,6 @@ bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChai
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
}
return true;
}
void PlatformCocoaTouchGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -18,33 +18,16 @@
#include "opengl/GLUtils.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/Platform.h>
#include <backend/DriverEnums.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <algorithm>
#include <new>
#include <initializer_list>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <utils/Panic.h>
#ifndef EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
# define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483
@@ -65,6 +48,7 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
}
using namespace glext;
// ---------------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------------
@@ -153,8 +137,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
ext.egl.KHR_gl_colorspace = extensions.has("EGL_KHR_gl_colorspace");
ext.egl.KHR_create_context = extensions.has("EGL_KHR_create_context");
ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context");
ext.egl.KHR_surfaceless_context = extensions.has("EGL_KHR_surfaceless_context");
ext.egl.EXT_protected_content = extensions.has("EGL_EXT_protected_content");
ext.egl.KHR_surfaceless_context = extensions.has("KHR_surfaceless_context");
if (ext.egl.KHR_create_context) {
// KHR_create_context implies KHR_surfaceless_context for ES3.x contexts
ext.egl.KHR_surfaceless_context = true;
@@ -283,14 +266,12 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
}
}
if (UTILS_UNLIKELY(
egl.makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
if (UTILS_UNLIKELY(!makeCurrent(mEGLDummySurface, mEGLDummySurface))) {
// eglMakeCurrent failed
logEglError("eglMakeCurrent");
goto error;
}
mCurrentContextType = ContextType::UNPROTECTED;
mContextAttribs = std::move(contextAttribs);
initializeGlExtensions();
@@ -309,13 +290,9 @@ error:
if (mEGLContext) {
eglDestroyContext(mEGLDisplay, mEGLContext);
}
if (mEGLContextProtected) {
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
}
mEGLDummySurface = EGL_NO_SURFACE;
mEGLContext = EGL_NO_CONTEXT;
mEGLContextProtected = EGL_NO_CONTEXT;
eglTerminate(mEGLDisplay);
eglReleaseThread();
@@ -327,10 +304,6 @@ bool PlatformEGL::isExtraContextSupported() const noexcept {
return ext.egl.KHR_surfaceless_context;
}
bool PlatformEGL::isProtectedContextSupported() const noexcept {
return ext.egl.EXT_protected_content;
}
void PlatformEGL::createContext(bool shared) {
EGLConfig config = ext.egl.KHR_no_config_context ? EGL_NO_CONFIG_KHR : mEGLConfig;
@@ -365,6 +338,15 @@ void PlatformEGL::releaseContext() noexcept {
eglReleaseThread();
}
EGLBoolean PlatformEGL::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
if (UTILS_UNLIKELY((drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
mCurrentDrawSurface = drawSurface;
mCurrentReadSurface = readSurface;
return eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext);
}
return EGL_TRUE;
}
void PlatformEGL::terminate() noexcept {
// it's always allowed to use EGL_NO_SURFACE, EGL_NO_CONTEXT
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -372,9 +354,6 @@ void PlatformEGL::terminate() noexcept {
eglDestroySurface(mEGLDisplay, mEGLDummySurface);
}
eglDestroyContext(mEGLDisplay, mEGLContext);
if (mEGLContextProtected != EGL_NO_CONTEXT) {
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
}
for (auto context : mAdditionalContexts) {
eglDestroyContext(mEGLDisplay, context);
}
@@ -445,8 +424,6 @@ EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbu
return config;
}
// -----------------------------------------------------------------------------------------------
bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
@@ -454,23 +431,6 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
Config attribs;
if (ext.egl.KHR_gl_colorspace) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
}
} else {
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
}
if (ext.egl.EXT_protected_content) {
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
} else {
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
@@ -478,34 +438,46 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
config = mEGLConfig;
}
EGLSurface sur = EGL_NO_SURFACE;
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
sur = eglCreateWindowSurface(mEGLDisplay, config,
(EGLNativeWindowType)nativeWindow, attribs.data());
if (UTILS_LIKELY(sur != EGL_NO_SURFACE)) {
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
} else {
logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface");
}
} else {
// error already logged
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.nativeWindow = (EGLNativeWindowType)nativeWindow,
.config = config,
.flags = flags
});
return sc;
Config attribs;
if (ext.egl.KHR_gl_colorspace) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
}
}
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config,
(EGLNativeWindowType)nativeWindow, attribs.data());
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreateWindowSurface");
return nullptr;
}
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
return (SwapChain*)sur;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept {
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, false, true);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
Config attribs = {
{ EGL_WIDTH, EGLint(width) },
{ EGL_HEIGHT, EGLint(height) },
@@ -515,149 +487,41 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
}
} else {
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
}
if (ext.egl.EXT_protected_content) {
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
} else {
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
} else {
config = mEGLConfig;
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreatePbufferSurface");
return nullptr;
}
EGLSurface sur = EGL_NO_SURFACE;
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface");
}
} else {
// error already logged
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.config = config,
.flags = flags
});
return sc;
return (SwapChain*)sur;
}
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
egl.makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sc->sur);
delete sc;
}
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sur);
}
}
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
}
return false;
}
OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept {
return mCurrentContextType;
}
bool PlatformEGL::makeCurrent(ContextType type,
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
EGLContext context = getContextForType(type);
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
return success == EGL_TRUE ? true : false;
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept {
assert_invariant(drawSwapChain);
assert_invariant(readSwapChain);
ContextType type = ContextType::UNPROTECTED;
if (ext.egl.EXT_protected_content) {
bool const swapChainProtected = isSwapChainProtected(drawSwapChain);
if (UTILS_UNLIKELY(swapChainProtected)) {
// we need a protected context
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
// we don't have one, create it!
EGLConfig config = ext.egl.KHR_no_config_context ? EGL_NO_CONFIG_KHR : mEGLConfig;
Config protectedContextAttribs{ mContextAttribs };
protectedContextAttribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
mEGLContextProtected = eglCreateContext(mEGLDisplay, config, mEGLContext,
protectedContextAttribs.data());
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
// couldn't create the protected context
logEglError("eglCreateContext[EGL_PROTECTED_CONTENT_EXT]");
ext.egl.EXT_protected_content = false;
goto error;
}
}
type = ContextType::PROTECTED;
error: ;
}
bool const contextChange = type != mCurrentContextType;
mCurrentContextType = type;
if (UTILS_UNLIKELY(contextChange)) {
preContextChange();
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
if (UTILS_UNLIKELY(!success)) {
logEglError("PlatformEGL::makeCurrent");
if (mEGLContextProtected != EGL_NO_CONTEXT) {
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
mEGLContextProtected = EGL_NO_CONTEXT;
}
mCurrentContextType = ContextType::UNPROTECTED;
}
if (UTILS_LIKELY(!swapChainProtected && mEGLContextProtected != EGL_NO_CONTEXT)) {
// We don't need the protected context anymore, unbind and destroy right away.
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
mEGLContextProtected = EGL_NO_CONTEXT;
}
size_t const contextIndex = (mCurrentContextType == ContextType::PROTECTED) ? 1 : 0;
postContextChange(contextIndex);
return;
}
}
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
if (UTILS_UNLIKELY(!success)) {
logEglError("PlatformEGL::makeCurrent");
Platform::SwapChain* readSwapChain) noexcept {
EGLSurface drawSur = (EGLSurface) drawSwapChain;
EGLSurface readSur = (EGLSurface) readSwapChain;
if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) {
makeCurrent(drawSur, readSur);
}
}
void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
eglSwapBuffers(mEGLDisplay, sc->sur);
}
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
eglSwapBuffers(mEGLDisplay, sur);
}
}
// -----------------------------------------------------------------------------------------------
bool PlatformEGL::canCreateFence() noexcept {
return true;
}
@@ -696,10 +560,8 @@ FenceStatus PlatformEGL::waitFence(
return FenceStatus::ERROR;
}
// -----------------------------------------------------------------------------------------------
OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new(std::nothrow) ExternalTexture{};
ExternalTexture* outTexture = new ExternalTexture{};
glGenTextures(1, &outTexture->id);
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
outTexture->target = GL_TEXTURE_EXTERNAL_OES;
@@ -728,8 +590,6 @@ bool PlatformEGL::setExternalImage(void* externalImage,
return true;
}
// -----------------------------------------------------------------------------------------------
void PlatformEGL::initializeGlExtensions() noexcept {
// We're guaranteed to be on an ES platform, since we're using EGL
GLUtils::unordered_string_set glExtensions;
@@ -738,17 +598,6 @@ void PlatformEGL::initializeGlExtensions() noexcept {
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
}
EGLContext PlatformEGL::getContextForType(OpenGLPlatform::ContextType type) const noexcept {
switch (type) {
case ContextType::NONE:
return EGL_NO_CONTEXT;
case ContextType::UNPROTECTED:
return mEGLContext;
case ContextType::PROTECTED:
return mEGLContextProtected;
}
}
// ---------------------------------------------------------------------------------------------
PlatformEGL::Config::Config() = default;
@@ -785,23 +634,6 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
}
}
// ------------------------------------------------------------------------------------------------
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
EGLSurface readSurface) noexcept {
if (UTILS_UNLIKELY((
mCurrentContext != context ||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
EGLBoolean const success = eglMakeCurrent(
mEGLDisplay, drawSurface, readSurface, context);
if (success) {
mCurrentDrawSurface = drawSurface;
mCurrentReadSurface = readSurface;
mCurrentContext = context;
}
return success;
}
return EGL_TRUE;
}
} // namespace filament::backend
// ---------------------------------------------------------------------------------------------
@@ -14,34 +14,21 @@
* limitations under the License.
*/
#include <backend/AcquiredImage.h>
#include <backend/Platform.h>
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include "opengl/GLUtils.h"
#include "ExternalStreamManagerAndroid.h"
#include <android/api-level.h>
#include <android/hardware_buffer.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/Log.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <sys/system_properties.h>
#include <jni.h>
#include <new>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
// We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
@@ -142,12 +129,11 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
}
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
if (currentDrawSurface != EGL_NO_SURFACE) {
if (mCurrentDrawSurface != EGL_NO_SURFACE) {
if (eglPresentationTimeANDROID) {
eglPresentationTimeANDROID(
mEGLDisplay,
currentDrawSurface,
mCurrentDrawSurface,
presentationTimeInNanosecond);
}
}
@@ -179,28 +165,14 @@ int PlatformEGLAndroid::getOSVersion() const noexcept {
AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept {
// Convert the AHardwareBuffer to EGLImage.
AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image;
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image);
if (!clientBuffer) {
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
return {};
}
PlatformEGL::Config attributes;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(pHardwareBuffer, &desc);
bool const isProtectedContent =
desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
if (isProtectedContent) {
attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
}
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
// Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT.
EGLint attrs[] = { EGL_NONE, EGL_NONE };
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
if (eglImage == EGL_NO_IMAGE_KHR) {
slog.e << "eglCreateImageKHR returned no image." << io::endl;
return {};
@@ -265,11 +265,10 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
}
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
void PlatformGLX::makeCurrent(
Platform::SwapChain* drawSwapChain, Platform::SwapChain* readSwapChain) noexcept {
g_glx.setCurrentContext(mGLXDisplay,
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
return true;
}
void PlatformGLX::commit(Platform::SwapChain* swapChain) noexcept {
@@ -255,8 +255,8 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
wglMakeCurrent(mWhdc, mContext);
}
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformWGL does not support distinct draw/read swap chains.");
@@ -269,7 +269,6 @@ bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
wglMakeCurrent(0, NULL);
}
}
return true;
}
void PlatformWGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -46,9 +46,8 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
return true;
void PlatformWebGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
}
void PlatformWebGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -154,9 +154,15 @@ struct BlitterUniforms {
}// anonymous namespace
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
: mPhysicalDevice(physicalDevice),
mCommands(commands) {}
VulkanBlitter::VulkanBlitter() noexcept = default;
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands) noexcept {
mPhysicalDevice = physicalDevice;
mDevice = device;
mAllocator = allocator;
mCommands = commands;
}
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
+6 -1
View File
@@ -32,7 +32,10 @@ struct VulkanProgram;
class VulkanBlitter {
public:
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
VulkanBlitter() noexcept;
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands) noexcept;
void blit(VkFilter filter,
VulkanAttachment dst, const VkOffset3D* dstRectPair,
@@ -44,6 +47,8 @@ public:
private:
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
VkDevice mDevice;
VmaAllocator mAllocator;
VulkanCommands* mCommands;
};
@@ -155,7 +155,7 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
#endif
}
void VulkanCommands::terminate() {
VulkanCommands::~VulkanCommands() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
@@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() {
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
<< utils::io::flush;
#else
mGroupMarkers->pop();
#endif
+47 -48
View File
@@ -139,72 +139,71 @@ public:
// - We do this because vkGetFenceStatus must be called from the rendering thread.
//
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator);
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator);
~VulkanCommands();
void terminate();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer& get();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer& get();
// Submits the current command buffer if it exists, then sets "current" to null.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
// Submits the current command buffer if it exists, then sets "current" to null.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal();
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal();
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
void injectDependency(VkSemaphore next);
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
void injectDependency(VkSemaphore next);
// Destroys all command buffers that are no longer in use.
void gc();
// Destroys all command buffers that are no longer in use.
void gc();
// Waits for all outstanding command buffers to finish.
void wait();
// Waits for all outstanding command buffers to finish.
void wait();
// Updates the atomic "status" variable in every extant fence.
void updateFences();
// Updates the atomic "status" variable in every extant fence.
void updateFences();
// Sets an observer who is notified every time a new command buffer has been made "current".
// The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
// Sets an observer who is notified every time a new command buffer has been made "current".
// The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
std::string getTopGroupMarker() const;
#endif
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
#endif
};
+5 -33
View File
@@ -39,12 +39,7 @@
// not required for validation.
// FVK is short for Filament Vulkan
// Enables Android systrace
#define FVK_DEBUG_SYSTRACE 0x00000001
// Group markers are used to denote collection of GPU commands. It is typically at the granualarity
// of a renderpass.
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
#define FVK_DEBUG_TEXTURE 0x00000004
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
@@ -60,15 +55,11 @@
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
#define FVK_DEBUG_ALLOCATION 0x00004000
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE
FVK_DEBUG_SYSTRACE | \
FVK_DEBUG_GROUP_MARKERS
#define FVK_DEBUG_CORRECTNESS \
FVK_DEBUG_VALIDATION | \
@@ -81,40 +72,21 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
#else
#define FVK_DEBUG_FLAGS 0
#endif
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
// Group marker only works only if validation or debug utils is enabled since it uses
// vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
// Ensure dependencies are met between debug options
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
#endif
// Only enable debug utils if validation is enabled.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
// end dependcy checks
// Shorthand for combination of enabled debug flags
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
#else
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
#endif
// end shorthands
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
+187 -332
View File
@@ -24,7 +24,6 @@
#include "VulkanHandles.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
#include "VulkanTexture.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -118,9 +117,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
utils::slog.e << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
void* pUserData) {
@@ -139,63 +136,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
utils::slog.e << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
}// anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
using DebugUtils = VulkanDriver::DebugUtils;
DebugUtils* DebugUtils::mSingleton = nullptr;
DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context)
: mInstance(instance), mDevice(device), mEnabled(context->isDebugUtilsSupported()) {
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// Also initialize debug utils messenger here
if (mEnabled) {
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = debugUtilsCallback,
};
VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo,
VKALLOC, &mDebugMessenger);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
}
DebugUtils* DebugUtils::get() {
assert_invariant(DebugUtils::mSingleton);
return DebugUtils::mSingleton;
}
DebugUtils::~DebugUtils() {
if (mDebugMessenger) {
vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, VKALLOC);
}
}
void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
auto impl = DebugUtils::get();
if (!impl->mEnabled) {
return;
}
VkDebugUtilsObjectNameInfoEXT const info = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.pNext = nullptr,
.objectType = type,
.objectHandle = handle,
.pObjectName = name,
};
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
}
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
} // anonymous namespace
using ImgUtil = VulkanImageUtility;
@@ -209,47 +152,60 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
mContext(context),
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
mResourceAllocator(driverConfig.handleArenaSize),
mResourceManager(&mResourceAllocator),
mThreadSafeResourceManager(&mResourceAllocator),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
mPipelineCache(&mResourceAllocator),
mStagePool(mAllocator, &mCommands),
mFramebufferCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
DebugUtils::mSingleton =
new DebugUtils(mPlatform->getInstance(), mPlatform->getDevice(), &context);
#endif
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback
= vkCreateDebugReportCallbackEXT;
if (!context.isDebugUtilsSupported() && createDebugReportCallback) {
VkResult result;
if (mContext.isDebugUtilsSupported()) {
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = debugUtilsCallback,
};
result = vkCreateDebugUtilsMessengerEXT(mPlatform->getInstance(), &createInfo, VKALLOC,
&mDebugMessenger);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
} else if (createDebugReportCallback) {
VkDebugReportCallbackCreateInfoEXT const cbinfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT,
.pfnCallback = debugReportCallback,
};
VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
&mDebugCallback);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
}
#endif
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
mCommands.setObserver(&mPipelineCache);
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
&mResourceAllocator);
mCommands->setObserver(&mPipelineCache);
mPipelineCache.setDevice(mPlatform->getDevice(), mAllocator);
mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, &mCommands, mStagePool);
// TOOD: move them all to be initialized by constructor
mStagePool.initialize(mAllocator, mCommands.get());
mFramebufferCache.initialize(mPlatform->getDevice());
mSamplerCache.initialize(mPlatform->getDevice());
mEmptyTexture.reset(createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, mCommands.get(), mStagePool));
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
mCommands.get());
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -257,43 +213,6 @@ VulkanDriver::~VulkanDriver() noexcept = default;
UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// VulkanSamplerGroup : 16 few
// HwStream : 24 few
// VulkanFence : 32 few
// VulkanProgram : 32 moderate
// VulkanIndexBuffer : 64 moderate
// VulkanBufferObject : 64 many
// -- less than or equal 64 bytes
// VulkanRenderPrimitive : 72 many
// VulkanVertexBufferInfo : 96 moderate
// VulkanSwapChain : 104 few
// VulkanTimerQuery : 160 few
// -- less than or equal 160 bytes
// VulkanVertexBuffer : 192 moderate
// VulkanTexture : 224 moderate
// VulkanRenderTarget : 312 few
// -- less than or equal to 312 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
<< "\nVulkanTexture: " << sizeof(VulkanTexture)
<< "\nVulkanTimerQuery: " << sizeof(VulkanTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nVulkanRenderTarget: " << sizeof(VulkanRenderTarget)
<< "\nVulkanFence: " << sizeof(VulkanFence)
<< "\nVulkanProgram: " << sizeof(VulkanProgram)
<< utils::io::endl;
#endif
assert_invariant(platform);
size_t defaultSize = FVK_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
@@ -312,11 +231,8 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
void VulkanDriver::terminate() {
// Command buffers should come first since it might have commands depending on resources that
// are about to be destroyed.
mCommands.terminate();
delete mEmptyTexture;
mResourceManager.clear();
mCommands.reset();
mEmptyTexture.reset();
mTimestamps.reset();
mBlitter.terminate();
@@ -335,17 +251,14 @@ void VulkanDriver::terminate() {
if (mDebugCallback) {
vkDestroyDebugReportCallbackEXT(mPlatform->getInstance(), mDebugCallback, VKALLOC);
}
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
assert_invariant(DebugUtils::mSingleton);
delete DebugUtils::mSingleton;
#endif
if (mDebugMessenger) {
vkDestroyDebugUtilsMessengerEXT(mPlatform->getInstance(), mDebugMessenger, VKALLOC);
}
mPlatform->terminate();
}
void VulkanDriver::tick(int) {
mCommands.updateFences();
mCommands->updateFences();
}
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
@@ -357,14 +270,9 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_START("gc");
// Command buffers need to be submitted and completed before other resources can be gc'd. And
// its gc() function carrys out the *wait*.
mCommands.gc();
mCommands->gc();
mStagePool.gc();
mFramebufferCache.gc();
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceAllocator.print();
#endif
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -385,7 +293,7 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
void VulkanDriver::endFrame(uint32_t frameId) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endframe");
mCommands.flush();
mCommands->flush();
collectGarbage();
FVK_SYSTRACE_END();
}
@@ -393,7 +301,7 @@ void VulkanDriver::endFrame(uint32_t frameId) {
void VulkanDriver::flush(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("flush");
mCommands.flush();
mCommands->flush();
FVK_SYSTRACE_END();
}
@@ -401,8 +309,8 @@ void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("finish");
mCommands.flush();
mCommands.wait();
mCommands->flush();
mCommands->wait();
mReadPixels.runUntilComplete();
FVK_SYSTRACE_END();
@@ -416,10 +324,12 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph,
&mResourceAllocator, pt, vbh, ibh);
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
mResourceManager.acquire(rp);
VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -430,26 +340,10 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
mResourceManager.release(rp);
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
auto vbi = mResourceAllocator.construct<VulkanVertexBufferInfo>(vbih,
bufferCount, attributeCount, attributes);
mResourceManager.acquire(vbi);
}
void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (!vbih) {
return;
}
auto vbi = mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(vbih);
mResourceManager.release(vbi);
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh,
mContext, mStagePool, &mResourceAllocator, vertexCount, vbih);
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes) {
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh, mContext, mStagePool,
&mResourceAllocator, bufferCount, attributeCount, elementCount, attributes);
mResourceManager.acquire(vertexBuffer);
}
@@ -499,7 +393,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
format, samples, w, h, depth, usage, mStagePool);
mResourceManager.acquire(vktexture);
}
@@ -511,7 +405,7 @@ void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targ
TextureSwizzle swizzleArray[] = {r, g, b, a};
const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
format, samples, w, h, depth, usage, mStagePool, false /*heap allocated */, swizzleMap);
mResourceManager.acquire(vktexture);
}
@@ -554,7 +448,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
@@ -606,7 +500,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
assert_invariant(tmin.x >= width && tmin.y >= height);
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), width, height,
samples, colorTargets, depthStencil, mStagePool);
mResourceManager.acquire(renderTarget);
}
@@ -624,7 +518,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
VulkanCommandBuffer const& commandBuffer = mCommands.get();
VulkanCommandBuffer const& commandBuffer = mCommands->get();
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
}
@@ -635,7 +529,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
mAllocator, &mCommands, mStagePool, nativeWindow, flags);
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags);
mResourceManager.acquire(swapChain);
}
@@ -648,7 +542,7 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
}
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
mAllocator, &mCommands, mStagePool, nullptr, flags, VkExtent2D{width, height});
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
mResourceManager.acquire(swapChain);
}
@@ -656,10 +550,6 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
return mResourceAllocator.allocHandle<VulkanVertexBufferInfo>();
}
Handle<HwVertexBuffer> VulkanDriver::createVertexBufferS() noexcept {
return mResourceAllocator.allocHandle<VulkanVertexBuffer>();
}
@@ -874,19 +764,8 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
return mIsSRGBSwapChainSupported;
}
bool VulkanDriver::isProtectedContentSupported() {
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
return false;
}
bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return true;
case backend::StereoscopicType::MULTIVIEW:
// TODO: implement multiview feature in Vulkan.
return false;
}
bool VulkanDriver::isStereoSupported() {
return true;
}
bool VulkanDriver::isParallelShaderCompileSupported() {
@@ -898,10 +777,6 @@ bool VulkanDriver::isDepthStencilResolveSupported() {
return false;
}
bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -988,12 +863,13 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto vb = mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh);
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
vb->setBuffer(mResourceAllocator, bo, index);
vb->setBuffer(bo, index);
}
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = mCommands->get();
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
commands.acquire(ib);
ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size);
@@ -1003,7 +879,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = mCommands->get();
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo);
@@ -1014,7 +890,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
BufferDescriptor&& bd, uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = mCommands->get();
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo);
// TODO: implement unsynchronized version
@@ -1047,10 +923,10 @@ void VulkanDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t
void VulkanDriver::setupExternalImage(void* image) {
}
TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
bool VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
if (!vtq->isCompleted()) {
return TimerQueryResult::NOT_READY;
return false;
}
auto results = mTimestamps->getResult(vtq);
@@ -1060,7 +936,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
uint64_t available1 = results[3];
if (available0 == 0 || available1 == 0) {
return TimerQueryResult::NOT_READY;
return false;
}
ASSERT_POSTCONDITION(timestamp1 >= timestamp0, "Timestamps are not monotonically increasing.");
@@ -1072,7 +948,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
float const period = mContext.getPhysicalDeviceLimits().timestampPeriod;
uint64_t delta = uint64_t(float(timestamp1 - timestamp0) * period);
*elapsedTime = delta;
return TimerQueryResult::AVAILABLE;
return true;
}
void VulkanDriver::setExternalImage(Handle<HwTexture> th, void* image) {
@@ -1152,7 +1028,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
const VkExtent2D extent = rt->getExtent();
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
assert_invariant(extent.width > 0 && extent.height > 0);
// Filament has the expectation that the contents of the swap chain are not preserved on the
// first render pass. Note however that its contents are often preserved on subsequent render
@@ -1179,7 +1055,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
// the non-sampling case.
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer();
UTILS_NOUNROLL
@@ -1304,14 +1180,22 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
auto const topMarker = mCommands.getTopGroupMarker();
if (!topMarker.empty()) {
DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast<uint64_t>(vkfb),
topMarker.c_str());
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) {
auto const topMarker = mCommands->getTopGroupMarker();
if (!topMarker.empty()) {
const VkDebugUtilsObjectNameInfoEXT info = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
nullptr,
VK_OBJECT_TYPE_FRAMEBUFFER,
reinterpret_cast<uint64_t>(vkfb),
topMarker.c_str(),
};
vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info);
}
}
#endif
#endif
// The current command buffer now owns a reference to the render target and its attachments.
// Note that we must acquire parent textures, not sidecars.
@@ -1395,7 +1279,7 @@ void VulkanDriver::endRenderPass(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endRenderPass");
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer cmdbuffer = commands.buffer();
vkCmdEndRenderPass(cmdbuffer);
@@ -1448,7 +1332,7 @@ void VulkanDriver::nextSubpass(int) {
assert_invariant(renderTarget);
assert_invariant(mCurrentRenderPass.params.subpassMask);
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
++mCurrentRenderPass.currentSubpass);
@@ -1465,6 +1349,24 @@ void VulkanDriver::nextSubpass(int) {
}
}
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive->setBuffers(mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh),
mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh));
}
void VulkanDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
auto& primitive = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive.setPrimitiveType(pt);
primitive.offset = offset * primitive.indexBuffer->elementSize;
primitive.count = count;
primitive.minIndex = minIndex;
primitive.maxIndex = maxIndex > minIndex ? maxIndex : primitive.maxVertexCount - 1;
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
@@ -1529,14 +1431,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, len);
mCommands->insertEventMarker(string, len);
#endif
}
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);
mCommands->pushGroupMarker(string);
#endif
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START(string);
@@ -1544,7 +1446,7 @@ void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
void VulkanDriver::popGroupMarker(int) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.popGroupMarker();
mCommands->popGroupMarker();
#endif
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_END();
@@ -1557,7 +1459,7 @@ void VulkanDriver::stopCapture(int) {}
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& pbd) {
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
mCommands.flush();
mCommands->flush();
mReadPixels.run(
srcTarget, x, y, width, height, mPlatform->getGraphicsQueueFamilyIndex(),
std::move(pbd),
@@ -1710,58 +1612,68 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
FVK_SYSTRACE_END();
}
void VulkanDriver::bindPipeline(PipelineState pipelineState) {
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
const uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands.get();
const VulkanVertexBufferInfo& vbi =
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
Handle<HwProgram> programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
PolygonOffset depthOffset = pipelineState.polygonOffset;
Viewport const& scissorBox = pipelineState.scissor;
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
commands->acquire(program);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
VulkanPipelineCache::RasterState const vulkanRasterState{
.cullMode = getCullMode(rasterState.culling),
.frontFace = getFrontFace(rasterState.inverseFrontFaces),
.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false,
.blendEnable = rasterState.hasBlending(),
.depthWriteEnable = rasterState.depthWrite,
.alphaToCoverageEnable = rasterState.alphaToCoverage,
.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB),
.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB),
.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha),
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
.depthCompareOp = rasterState.depthFunc,
.depthBiasConstantFactor = depthOffset.constant,
.depthBiasSlopeFactor = depthOffset.slope
};
// unfortunately in Vulkan the topology is per pipeline
VkPrimitiveTopology const topology =
VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType);
auto vkraster = mPipelineCache.getCurrentRasterState();
vkraster.cullMode = getCullMode(rasterState.culling);
vkraster.frontFace = getFrontFace(rasterState.inverseFrontFaces);
vkraster.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false;
vkraster.depthBiasConstantFactor = depthOffset.constant;
vkraster.depthBiasSlopeFactor = depthOffset.slope;
vkraster.blendEnable = rasterState.hasBlending();
vkraster.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB);
vkraster.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB);
vkraster.colorBlendOp = rasterState.blendEquationRGB;
vkraster.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha);
vkraster.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha);
vkraster.alphaBlendOp = rasterState.blendEquationAlpha;
vkraster.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0);
vkraster.depthWriteEnable = rasterState.depthWrite;
vkraster.depthCompareOp = rasterState.depthFunc;
vkraster.rasterizationSamples = rt->getSamples();
vkraster.alphaToCoverageEnable = rasterState.alphaToCoverage;
vkraster.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass);
mPipelineCache.setCurrentRasterState(vkraster);
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions();
uint32_t const bufferCount = prim.vertexBuffer->attributes.size();
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindPrimitiveTopology(topology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount());
mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState());
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
// Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding),
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
@@ -1771,11 +1683,7 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto const& bindingToName = program->getBindingToName();
#endif
VulkanPipelineCache::UsageFlags usage = program->getUsage();
UTILS_NOUNROLL
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
@@ -1809,21 +1717,15 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
utils::slog.w << " at binding point " << +binding << utils::io::endl;
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
#endif
texture = mEmptyTexture;
texture = mEmptyTexture.get();
}
SamplerParams const& samplerParams = boundSampler->s;
VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
reinterpret_cast<uint64_t>(vksampler), bindingToName[binding].c_str());
#endif
VkImageView imageView = VK_NULL_HANDLE;
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -1847,51 +1749,6 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
FVK_SYSTRACE_END();
}
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindRenderPrimitive");
VulkanCommandBuffer* commands = &mCommands.get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->getAttributeCount();
VkDeviceSize const* offsets = vbi->getOffsets();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// it would be rare for a client to make consecutive draw calls with the same render primitive.
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw2");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
// message since the validation layers already do so.
@@ -1899,34 +1756,6 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
return;
}
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::scissor(Viewport scissorBox) {
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
// Set scissoring.
// clamp left-bottom to 0,0 and avoid overflows
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
@@ -1945,19 +1774,45 @@ void VulkanDriver::scissor(Viewport scissorBox) {
.extent = { uint32_t(r - l), uint32_t(t - b) }
};
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
rt->transformClientRectToPlatform(&scissor);
mPipelineCache.bindScissor(cmdbuffer, scissor);
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// it would be rare for a client to make consecutive draw calls with the same render primitive.
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
// Finally, make the actual draw call. TODO: support subranges
const uint32_t indexCount = prim.count;
const uint32_t firstIndex = prim.offset / prim.indexBuffer->elementSize;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
mTimestamps->beginQuery(&(mCommands.get()), vtq);
mTimestamps->beginQuery(&(mCommands->get()), vtq);
}
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
mTimestamps->endQuery(&(mCommands.get()), vtq);
mTimestamps->endQuery(&(mCommands->get()), vtq);
}
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
+9 -27
View File
@@ -45,30 +45,6 @@ public:
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept;
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
// vkSetDebugUtilsObjectNameEXT and vkCreateDebugUtilsMessengerEXT
class DebugUtils {
public:
static void setName(VkObjectType type, uint64_t handle, char const* name);
private:
static DebugUtils* get();
DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context);
~DebugUtils();
VkInstance const mInstance;
VkDevice const mDevice;
bool const mEnabled;
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
static DebugUtils* mSingleton;
friend class VulkanDriver;
};
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
private:
void debugCommandBegin(CommandStream* cmds, bool synchronous,
const char* methodName) noexcept override;
@@ -101,18 +77,25 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh);
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
std::unique_ptr<VulkanCommands> mCommands;
std::unique_ptr<VulkanTimestamps> mTimestamps;
VulkanTexture* mEmptyTexture;
std::unique_ptr<VulkanTexture> mEmptyTexture;
VulkanSwapChain* mCurrentSwapChain = nullptr;
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
VulkanContext mContext = {};
VulkanResourceAllocator mResourceAllocator;
@@ -122,7 +105,6 @@ private:
// thread.
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
VulkanCommands mCommands;
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanFboCache mFramebufferCache;
@@ -64,8 +64,7 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
return true;
}
VulkanFboCache::VulkanFboCache(VkDevice device)
: mDevice(device) {}
void VulkanFboCache::initialize(VkDevice device) noexcept { mDevice = device; }
VulkanFboCache::~VulkanFboCache() {
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
+2 -1
View File
@@ -104,9 +104,10 @@ public:
bool operator()(const FboKey& k1, const FboKey& k2) const;
};
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
void initialize(VkDevice device) noexcept;
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey config) noexcept;
+55 -72
View File
@@ -16,9 +16,7 @@
#include "VulkanHandles.h"
#include "VulkanDriver.h"
#include "VulkanConstants.h"
#include "VulkanDriver.h"
#include "VulkanMemory.h"
#include "spirv/VulkanSpirvUtils.h"
@@ -80,29 +78,8 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
};
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
std::string name{ builder.getName().c_str(), builder.getName().size() };
switch (static_cast<ShaderStage>(i)) {
case ShaderStage::VERTEX:
name += "_vs";
break;
case ShaderStage::FRAGMENT:
name += "_fs";
break;
default:
PANIC_POSTCONDITION("Unexpected stage");
break;
}
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE,
reinterpret_cast<uint64_t>(module), name.c_str());
#endif
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto& bindingToName = mInfo->bindingToName;
#endif
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -113,17 +90,13 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
uint32_t const binding = samplers[i].binding;
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
bindingToName[binding] = samplers[i].name.c_str();
#endif
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")" << utils::io::endl;
#endif
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
#endif
}
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
@@ -289,21 +262,22 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co
return count;
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
mInfo(attributes.size()) {
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attribs)
: HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
mInfo(new PipelineInfo(attribs.size())),
mResources(allocator) {
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo->mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo->mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo.mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo.mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute attrib = attributes[attribIndex];
for (uint32_t attribIndex = 0; attribIndex < attribs.size(); attribIndex++) {
Attribute attrib = attribs[attribIndex];
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger);
@@ -315,7 +289,7 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
attrib = attributes[0];
attrib = attribs[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -331,23 +305,14 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
}
}
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
mResources(allocator) {
VulkanVertexBuffer::~VulkanVertexBuffer() {
delete mInfo;
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
size_t const count = vbi->getAttributeCount();
VkBuffer* const vkbuffers = getVkBuffers();
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
void VulkanVertexBuffer::setBuffer(VulkanBufferObject* bufferObject, uint32_t index) {
size_t count = attributes.size();
auto vkbuffers = mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
auto attribToBuffer = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
@@ -363,6 +328,35 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
this->type = pt;
switch (pt) {
case PrimitiveType::POINTS:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
case PrimitiveType::LINES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
break;
case PrimitiveType::LINE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
break;
case PrimitiveType::TRIANGLES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
break;
case PrimitiveType::TRIANGLE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
break;
}
}
void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
VulkanIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
@@ -394,15 +388,4 @@ bool VulkanTimerQuery::isCompleted() noexcept {
VulkanTimerQuery::~VulkanTimerQuery() = default;
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(resourceAllocator) {
type = pt;
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
} // namespace filament::backend
+29 -54
View File
@@ -64,12 +64,6 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
return mInfo->bindingToName;
}
#endif
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
@@ -86,12 +80,6 @@ private:
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
// We store the sampler name mapped from binding index (only for debug purposes).
utils::FixedCapacityVector<std::string> bindingToName;
#endif
};
PipelineInfo* mInfo;
@@ -141,72 +129,55 @@ private:
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attributes);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
~VulkanVertexBuffer();
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
}
inline VkVertexInputBindingDescription const* getBufferDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
inline VkVertexInputBindingDescription const* getBufferDescriptions() {
return mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
}
inline int8_t const* getAttributeToBuffer() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
inline VkBuffer const* getVkBuffers() const {
return mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
}
inline VkDeviceSize const* getOffsets() const {
return mInfo.mSoa.data<PipelineInfo::OFFSETS>();
}
size_t getAttributeCount() const noexcept {
return mInfo.mSoa.size();
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
}
private:
struct PipelineInfo {
PipelineInfo(size_t size) : mSoa(size /* capacity */) {
PipelineInfo(size_t size)
: mSoa(size /* capacity */) {
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
// These corresponds to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t OFFSETS = 2;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3;
static constexpr uint8_t VK_BUFFER = 2;
static constexpr uint8_t OFFSETS = 3;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4;
utils::StructureOfArrays<
VkVertexInputAttributeDescription,
VkVertexInputBindingDescription,
VkBuffer,
VkDeviceSize,
int8_t
> mSoa;
};
PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
inline VkBuffer const* getVkBuffers() const {
return mBuffers.data();
}
inline VkBuffer* getVkBuffers() {
return mBuffers.data();
}
Handle<HwVertexBufferInfo> vbih;
private:
utils::FixedCapacityVector<VkBuffer> mBuffers;
PipelineInfo* mInfo;
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
};
@@ -239,15 +210,19 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
VulkanRenderPrimitive(VulkanResourceAllocator* allocator)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(allocator) {}
~VulkanRenderPrimitive() {
mResources.clear();
}
void setPrimitiveType(PrimitiveType pt);
void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer);
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
VkPrimitiveTopology primitiveTopology;
private:
// Keep references to the vertex buffer and the index buffer.
@@ -34,6 +34,8 @@ using namespace bluevk;
namespace filament::backend {
static VulkanPipelineCache::RasterState createDefaultRasterState();
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
// NOTE: if you modify this function, you also need to modify getUsageFlags.
assert_invariant(binding < MAX_SAMPLER_COUNT);
@@ -71,7 +73,8 @@ VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t
}
VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator)
: mResourceAllocator(allocator),
: mCurrentRasterState(createDefaultRasterState()),
mResourceAllocator(allocator),
mPipelineBoundResources(allocator) {
mDummyBufferWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummyBufferWriteInfo.pNext = nullptr;
@@ -563,18 +566,10 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
mPipelineRequirements.rasterState = rasterState;
mPipelineRequirements.rasterState = mCurrentRasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
@@ -636,8 +631,12 @@ void VulkanPipelineCache::unbindImageView(VkImageView imageView) noexcept {
void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
VkBuffer buffer = bufferObject->buffer.getGpuBuffer();
bindUniformBuffer(bindingIndex, bufferObject->buffer.getGpuBuffer(), offset, size);
mPipelineBoundResources.acquire(bufferObject);
}
void VulkanPipelineCache::bindUniformBuffer(uint32_t bindingIndex, VkBuffer buffer,
VkDeviceSize offset, VkDeviceSize size) noexcept {
ASSERT_POSTCONDITION(bindingIndex < UBUFFER_BINDING_COUNT,
"Uniform bindings overflow: index = %d, capacity = %d.", bindingIndex,
UBUFFER_BINDING_COUNT);
@@ -653,8 +652,6 @@ void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex,
key.uniformBufferOffsets[bindingIndex] = offset;
key.uniformBufferSizes[bindingIndex] = size;
mPipelineBoundResources.acquire(bufferObject);
}
void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT],
@@ -920,6 +917,23 @@ bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1,
return true;
}
static VulkanPipelineCache::RasterState createDefaultRasterState() {
return VulkanPipelineCache::RasterState {
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.blendEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE,
.alphaToCoverageEnable = true,
.colorWriteMask = 0xf,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.colorTargetCount = 1,
.depthCompareOp = SamplerCompareFunc::LE,
.depthBiasConstantFactor = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
}
} // namespace filament::backend
#pragma clang diagnostic pop
@@ -157,6 +157,8 @@ public:
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept;
void bindUniformBuffer(uint32_t bindingIndex, VkBuffer buffer,
VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept;
void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT],
VulkanTexture* textures[SAMPLER_BINDING_COUNT], UsageFlags flags) noexcept;
void bindInputAttachment(uint32_t bindingIndex, VkDescriptorImageInfo imageInfo) noexcept;
@@ -191,19 +193,19 @@ public:
mDummyTargetInfo.imageView = imageView;
}
static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept {
switch (pt) {
case PrimitiveType::POINTS:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PrimitiveType::LINES:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case PrimitiveType::LINE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case PrimitiveType::TRIANGLES:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PrimitiveType::TRIANGLE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
}
// Acquires a resource to be bound to the current pipeline. The ownership of the resource
// will be transferred to the corresponding pipeline when pipeline is bound.
void acquireResource(VulkanResource* resource) {
mPipelineBoundResources.acquire(resource);
}
inline RasterState getCurrentRasterState() const noexcept {
return mCurrentRasterState;
}
// We need to update this outside of bindRasterState due to VulkanDriver::draw.
inline void setCurrentRasterState(RasterState const& rasterState) noexcept {
mCurrentRasterState = rasterState;
}
private:
@@ -411,6 +413,7 @@ private:
VmaAllocator mAllocator = VK_NULL_HANDLE;
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include <private/backend/HandleAllocator.h>
@@ -30,17 +29,18 @@
namespace filament::backend {
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType.
#define RESOURCE_TYPE_COUNT 12
#define DEBUG_RESOURCE_LEAKS 0
#if DEBUG_RESOURCE_LEAKS
#define TRACK_INCREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
#define TRACK_INCREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]++; \
}
#define TRACK_DECREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
#define TRACK_DECREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]--; \
}
#else
// No-op
@@ -51,8 +51,8 @@ namespace filament::backend {
class VulkanResourceAllocator {
public:
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
VulkanResourceAllocator(size_t arenaSize)
: mHandleAllocatorImpl("Handles", arenaSize)
#if DEBUG_RESOURCE_LEAKS
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
@@ -14,10 +14,10 @@
* limitations under the License.
*/
#include "VulkanResources.h"
#include "VulkanHandles.h"
#include "VulkanResourceAllocator.h"
#include "caching/VulkanDescriptorSet.h"
namespace filament::backend {
@@ -53,24 +53,16 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
case VulkanResourceType::TIMER_QUERY:
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
break;
case VulkanResourceType::VERTEX_BUFFER_INFO:
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
break;
case VulkanResourceType::VERTEX_BUFFER:
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
break;
case VulkanResourceType::RENDER_PRIMITIVE:
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
break;
case VulkanResourceType::DESCRIPTOR_SET:
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
break;
// If the resource is heap allocated, then the resource manager just skip refcounted
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
case VulkanResourceType::END_TYPE:
break;
}
}

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