Compare commits

..
Author SHA1 Message Date
Eliza Velasquez ea9fdbeb5c wip: shader compilation benchmark 2024-01-16 17:03:41 -08:00
293 changed files with 3273 additions and 7638 deletions
+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
+4 -6
View File
@@ -634,9 +634,9 @@ function(combine_static_libs TARGET OUTPUT DEPS)
# Loop through the dependent libraries and query their location on disk.
set(DEPS_FILES )
foreach(DEPENDENCY ${DEPS})
if (TARGET ${DEPENDENCY})
if(TARGET ${DEPENDENCY})
get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE)
if (dep_type STREQUAL "STATIC_LIBRARY")
if(dep_type STREQUAL "STATIC_LIBRARY")
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
endif()
endif()
@@ -706,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()
@@ -721,6 +721,7 @@ add_subdirectory(${LIBRARIES}/filabridge)
add_subdirectory(${LIBRARIES}/filaflat)
add_subdirectory(${LIBRARIES}/filagui)
add_subdirectory(${LIBRARIES}/filameshio)
add_subdirectory(${LIBRARIES}/geometry)
add_subdirectory(${LIBRARIES}/gltfio)
add_subdirectory(${LIBRARIES}/ibl)
add_subdirectory(${LIBRARIES}/iblprefilter)
@@ -748,9 +749,6 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry)
if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
# spirv-tools must come before filamat, as filamat relies on the presence of the
# spirv-tools_SOURCE_DIR variable.
+2
View File
@@ -7,3 +7,5 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Metal: fix some shader artifacts by disabling fast math optimizations.
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.50.6'
implementation 'com.google.android.filament:filament-android:1.49.3'
}
```
@@ -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.50.6'
pod 'Filament', '~> 1.49.3'
```
### Snapshots
-28
View File
@@ -7,34 +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.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.
- backend: remove `atan2` overload which had a typo and wasn't useful. Fixes b/320856413.
- utils: remove usages of `SpinLock`. Fixes b/321101014.
## v1.50.0
- engine: TAA now supports 4x upscaling [BETA] [⚠️ **New Material Version**]
+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',
@@ -484,10 +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) {
jlong jobSystemThreadCount, jlong stereoscopicEyeCount) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -496,12 +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,
};
builder->config(&config);
}
@@ -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,10 +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.jobSystemThreadCount, config.stereoscopicEyeCount);
return this;
}
@@ -361,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).
@@ -398,16 +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;
}
private Engine(long nativeEngine, Config config) {
@@ -1279,9 +1227,7 @@ 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);
long stereoscopicEyeCount);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
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.
@@ -128,28 +128,11 @@ public class SwapChain {
*/
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;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
}
/**
* Return whether createSwapChain supports the CONFIG_PROTECTED_CONTENT flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
*/
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
return nIsProtectedContentSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
@@ -203,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);
}
@@ -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);
@@ -1882,7 +1882,7 @@ public class View {
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth`, `feedback` and `jitterPattern`, however, can be changed at any time.
* `filterWidth`, `feedback` and `jitterPattern`, however, could be changed at any time.
*
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
@@ -1925,7 +1925,6 @@ public class View {
UNIFORM_HELIX_X4,
HALTON_23_X8,
HALTON_23_X16,
HALTON_23_X32,
}
/**
@@ -1940,10 +1939,6 @@ public class View {
* texturing lod bias (typically -1 or -2)
*/
public float lodBias = -1.0f;
/**
* post-TAA sharpen, especially useful when upscaling is true.
*/
public float sharpness = 0.0f;
/**
* enables or disables temporal anti-aliasing
*/
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.50.6
VERSION_NAME=1.49.3
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)
File diff suppressed because one or more lines are too long
Binary file not shown.
-5
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
@@ -242,7 +238,6 @@ set(MATERIAL_SRCS
src/materials/resolveDepth.mat
src/materials/separableGaussianBlur.mat
src/materials/skybox.mat
src/materials/shadowmap.mat
src/materials/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat
src/materials/ssao/mipmapDepth.mat
-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
@@ -23,6 +23,7 @@
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#include <utils/compiler.h>
namespace filament::backend {
/**
+12 -35
View File
@@ -28,8 +28,7 @@
#include <math/vec4.h>
#include <array> // FIXME: STL headers are not allowed in public headers
#include <type_traits> // FIXME: STL headers are not allowed in public headers
#include <array> // FIXME: STL headers are not allowed in public headers
#include <stddef.h>
#include <stdint.h>
@@ -81,14 +80,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 +128,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 +658,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
};
@@ -1226,14 +1211,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>
+26 -26
View File
@@ -17,15 +17,17 @@
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
#define TNT_FILAMENT_BACKEND_HANDLE_H
#include <utils/compiler.h>
#if !defined(NDEBUG)
#include <utils/ostream.h>
#include <utils/Log.h>
#endif
#include <utils/debug.h>
#include <type_traits> // FIXME: STL headers are not allowed in public headers
#include <stdint.h>
#include <limits>
#include <type_traits>
namespace filament::backend {
struct HwBufferObject;
@@ -39,7 +41,6 @@ struct HwStream;
struct HwSwapChain;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
struct HwVertexBuffer;
/*
@@ -53,7 +54,7 @@ struct HwVertexBuffer;
class HandleBase {
public:
using HandleId = uint32_t;
static constexpr const HandleId nullid = HandleId{ UINT32_MAX };
static constexpr const HandleId nullid = HandleId{ std::numeric_limits<HandleId>::max() };
constexpr HandleBase() noexcept: object(nullid) {}
@@ -63,6 +64,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 +103,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 +116,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
@@ -20,7 +20,7 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <limits>
#include <stdint.h>
@@ -33,6 +33,10 @@ struct PipelineState {
RasterState rasterState;
StencilState stencilState;
PolygonOffset polygonOffset;
Viewport scissor{ 0, 0,
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max()
};
};
} // namespace filament::backend
@@ -24,7 +24,6 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
+11 -21
View File
@@ -19,11 +19,11 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORM_H
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <stddef.h>
namespace filament::backend {
class Driver;
@@ -41,24 +41,18 @@ public:
struct Stream {};
struct DriverConfig {
/**
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
/*
* size of handle arena in bytes. Setting to 0 indicates default value is to be used.
* Driver clamps to valid values.
*/
size_t handleArenaSize = 0;
/**
* This number of most-recently destroyed textures will be tracked for use-after-free.
/*
* this number of most-recently destroyed textures will be tracked for use-after-free.
* Throws 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.
*/
size_t textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
bool disableParallelShaderCompile = false;
};
Platform() noexcept;
@@ -84,7 +78,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;
/**
@@ -102,8 +96,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
@@ -111,8 +104,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
@@ -165,8 +157,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
@@ -185,8 +176,7 @@ 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);
size_t retrieveBlob(const void* key, size_t keySize, void* value, size_t valueSize);
private:
InsertBlobFunc mInsertBlob;
@@ -21,7 +21,8 @@
#include <utils/compiler.h>
namespace filament::backend {
namespace filament {
namespace backend {
/**
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
@@ -97,6 +98,7 @@ private:
*/
using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user);
} // namespace filament::backend
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRESENTCALLABLE
+4 -6
View File
@@ -17,19 +17,17 @@
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h>
#include <array> // FIXME: STL headers are not allowed in public headers
#include <utility> // FIXME: STL headers are not allowed in public headers
#include <variant> // FIXME: STL headers are not allowed in public headers
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <variant>
namespace filament::backend {
@@ -24,6 +24,9 @@
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
struct UTILS_PUBLIC SamplerDescriptor {
@@ -17,11 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
#define TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -18,15 +18,8 @@
#define TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_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>
namespace filament::backend {
class Driver;
@@ -45,8 +38,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 +57,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 +66,15 @@ public:
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* UTILS_NONNULL 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 +87,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,7 +106,7 @@ public:
* @return buffer that must be preserved
* @see commit()
*/
virtual TargetBufferFlags getPreservedFlags(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.
@@ -137,36 +120,14 @@ public:
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
*/
virtual void makeCurrent(
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
* 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(SwapChain*, SwapChain*).
*
* @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.
@@ -191,14 +152,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.
@@ -208,7 +169,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;
// --------------------------------------------------------------------------------------------
@@ -222,13 +183,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
@@ -238,21 +199,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;
// --------------------------------------------------------------------------------------------
@@ -265,13 +225,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
@@ -284,7 +244,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.
@@ -298,8 +258,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
@@ -17,10 +17,11 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#include <backend/DriverEnums.h>
#include <stdint.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <stdint.h>
#include <backend/DriverEnums.h>
namespace filament::backend {
@@ -30,7 +30,7 @@ struct PlatformCocoaTouchGLImpl;
class PlatformCocoaTouchGL : public OpenGLPlatform {
public:
PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override;
~PlatformCocoaTouchGL() noexcept;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -17,23 +17,18 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <stdint.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <utils/Invocable.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <initializer_list>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
/**
@@ -43,11 +38,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,23 +86,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;
void makeCurrent(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 commit(SwapChain* swapChain) noexcept override;
bool canCreateFence() noexcept override;
@@ -130,22 +119,15 @@ protected:
static void clearGlError() noexcept;
/**
* Always use this instead of eglMakeCurrent(), as it tracks some state.
* Always use this instead of eglMakeCurrent().
*/
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept;
/**
* Returns true if the swapchain is protected
*/
static bool isSwapChainProtected(Platform::SwapChain const* swapChain) 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;
EGLContext mCurrentContext = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
@@ -163,26 +145,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:
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
};
} // namespace filament::backend
@@ -17,14 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#include <backend/AcquiredImage.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/platforms/PlatformEGL.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
class ExternalStreamManagerAndroid;
@@ -20,18 +20,13 @@
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/PrivateImplementation.h>
#include <string_view>
#include <tuple>
#include <unordered_set>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
using SwapChain = Platform::SwapChain;
@@ -17,7 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#include <utils/debug.h>
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
@@ -40,36 +40,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 +69,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;
@@ -19,21 +19,19 @@
#include "private/backend/CircularBuffer.h"
#include <utils/compiler.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <vector>
#include <stddef.h>
#include <stdint.h>
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,7 +44,7 @@ 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;
@@ -58,20 +56,17 @@ public:
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.
@@ -32,13 +32,11 @@
#include <backend/TargetBufferInfo.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ThreadUtils.h>
#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
#ifndef NDEBUG
@@ -154,14 +152,14 @@ struct CommandType<void (Driver::*)(ARGS...)> {
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(std::size_t, void* ptr) {
inline void* operator new(std::size_t size, void* ptr) {
assert_invariant(ptr);
return ptr;
}
};
};
// convert a method of "class Driver" into a Command<> type
// convert an method of "class Driver" into a Command<> type
#define COMMAND_TYPE(method) CommandType<decltype(&Driver::method)>::Command<&Driver::method>
// ------------------------------------------------------------------------------------------------
@@ -213,8 +211,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) { \
@@ -29,7 +29,6 @@
#include <functional>
#include <stddef.h>
#include <stdint.h>
// Command debugging off. debugging virtuals are not called.
@@ -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)
@@ -263,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)
@@ -298,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)
@@ -496,17 +494,12 @@ DECL_DRIVER_API_N(blit,
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
@@ -22,33 +22,28 @@
#include <utils/Allocator.h>
#include <utils/Log.h>
#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>
#if !defined(NDEBUG) && UTILS_HAS_RTTI
# define HANDLE_TYPE_SAFETY 1
#else
# define HANDLE_TYPE_SAFETY 0
#endif
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
#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) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
@@ -66,9 +61,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 +84,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 +107,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 +134,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 +155,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,17 +195,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
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);
}
@@ -190,57 +210,29 @@ public:
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;
public:
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
// 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 alignment, 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;
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; }
@@ -262,16 +254,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
@@ -280,11 +280,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);
}
@@ -294,51 +292,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;
}
@@ -348,6 +337,9 @@ private:
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
HandleBase::HandleId mId = 0;
#if HANDLE_TYPE_SAFETY
mutable std::unordered_map<const void*, const char*> mHandleTypeId;
#endif
};
} // namespace filament::backend
@@ -19,9 +19,9 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
#define TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
#include <backend/DriverEnums.h>
#include "backend/DriverEnums.h"
#include <utils/compiler.h>
#include "utils/compiler.h"
namespace filament::backend {
@@ -17,12 +17,14 @@
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
#define TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/SamplerDescriptor.h>
#include "backend/DriverApiForward.h"
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/SamplerDescriptor.h>
#include <stddef.h>
+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
+24 -38
View File
@@ -15,25 +15,14 @@
*/
#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;
@@ -76,53 +65,50 @@ 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);
}
@@ -137,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();
-1
View File
@@ -19,7 +19,6 @@
#include <backend/PixelBufferDescriptor.h>
#include <cstring>
#include <stddef.h>
#include <stdint.h>
+1 -14
View File
@@ -21,12 +21,7 @@
#include <backend/AcquiredImage.h>
#include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Systrace.h>
#include <math/half.h>
@@ -34,13 +29,6 @@
#include <math/vec3.h>
#include <math/vec4.h>
#include <functional>
#include <mutex>
#include <utility>
#include <stddef.h>
#include <stdint.h>
using namespace utils;
using namespace filament::math;
@@ -131,8 +119,7 @@ void DriverBase::purge() noexcept {
// ------------------------------------------------------------------------------------------------
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
auto const handler = buffer.getHandler();
scheduleCallback(handler, [buffer = std::move(buffer)]() {
scheduleCallback(buffer.getHandler(), [buffer = std::move(buffer)]() {
// user callback is called when BufferDescriptor gets destroyed
});
}
+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{} {}
+11 -50
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 {
@@ -41,34 +28,14 @@ template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
: mArea(area) {
// 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);
// 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());
}
// ------------------------------------------------------------------------------------------------
@@ -106,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).");
}
@@ -125,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;
@@ -20,7 +20,6 @@
#include "MetalUtils.h"
#include <utils/Panic.h>
#include <utils/Log.h>
namespace filament::backend {
@@ -19,7 +19,6 @@
#include "MetalContext.h"
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/trap.h>
#include <thread>
+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);
+58 -116
View File
@@ -43,40 +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
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 144 moderate
// MetalTexture : 152 moderate
// MetalVertexBuffer : 152 moderate
// -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalRenderPrimitive : 584 many
// -- less than or equal to 592 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\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);
}
@@ -279,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,
@@ -356,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) {
@@ -444,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>();
}
@@ -512,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);
@@ -770,19 +722,8 @@ 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() {
@@ -793,10 +734,6 @@ bool MetalDriver::isDepthStencilResolveSupported() {
return false;
}
bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -923,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) {
@@ -1093,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) {
@@ -1602,8 +1547,7 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"Attempted to draw without a valid command encoder.");
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
@@ -1620,7 +1564,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
return;
}
functions.validate();
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1741,6 +1685,36 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
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 };
@@ -1818,10 +1792,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
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];
}
@@ -1894,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.");
@@ -21,7 +21,6 @@
#include "MetalUtils.h"
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/trap.h>
#define NSERROR_CHECK(message) \
+3 -12
View File
@@ -145,18 +145,10 @@ private:
MetalBuffer buffer;
};
struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
AttributeArray attributes;
};
struct MetalVertexBuffer : public HwVertexBuffer {
MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih);
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
uint32_t vertexCount, AttributeArray const& attributes);
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<MetalBuffer*> buffers;
};
@@ -169,8 +161,7 @@ struct MetalIndexBuffer : public HwIndexBuffer {
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
+40 -65
View File
@@ -245,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() {
@@ -300,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);
}];
}
@@ -340,16 +322,9 @@ 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),
attributes(attributes) {
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
@@ -358,12 +333,12 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
const size_t attributeCount = vbi->attributes.size();
const size_t attributeCount = vertexBuffer->attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -404,7 +379,7 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
};
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vbi->attributes[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.
@@ -30,8 +30,6 @@
#include <array>
#include <memory>
#include <tuple>
#include <variant>
namespace filament::backend {
@@ -43,69 +41,36 @@ class MetalShaderCompiler {
public:
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>;
@@ -77,7 +77,7 @@ MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driv
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 1;
const uint32_t poolSize = 2;
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
@@ -118,16 +118,13 @@ void MetalShaderCompiler::terminate() 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];
@@ -163,15 +160,14 @@ void MetalShaderCompiler::terminate() 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(
@@ -229,24 +225,4 @@ void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
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
-2
View File
@@ -18,8 +18,6 @@
#include "MetalEnums.h"
#include <utils/Log.h>
namespace filament {
namespace backend {
+4 -19
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) {
@@ -351,16 +340,12 @@ void NoopDriver::blit(
}
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) {
}
+14 -72
View File
@@ -18,7 +18,6 @@
#include <backend/platforms/OpenGLPlatform.h>
#include <functional>
#include <utility>
// change to true to display all GL extensions in the console on start-up
@@ -49,7 +48,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
#endif
}
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept {
OpenGLContext::OpenGLContext() noexcept {
state.vao.p = &mDefaultVAO;
@@ -232,46 +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);
}
}
contextIndex = index;
resetState();
}
void OpenGLContext::setDefaultState() noexcept {
@@ -468,6 +427,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
if (strstr(renderer, "Mali-T")) {
bugs->disable_glFlush = true;
bugs->disable_shared_context_draws = true;
bugs->texture_external_needs_rebind = true;
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
bugs->dont_use_timer_query = true;
}
@@ -640,7 +600,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__
@@ -664,7 +623,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);
@@ -729,7 +687,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;
@@ -861,22 +818,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);
@@ -893,8 +847,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);
@@ -1050,22 +1007,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
+30 -86
View File
@@ -19,8 +19,6 @@
#include <math/vec4.h>
#include "OpenGLTimerQuery.h"
#include <utils/CString.h>
#include <utils/debug.h>
@@ -29,17 +27,15 @@
#include "GLUtils.h"
#include <array>
#include <functional>
#include <set>
#include <tuple>
#include <utility>
#include <vector>
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
@@ -50,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;
@@ -81,18 +67,7 @@ public:
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
~OpenGLContext() noexcept;
// 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 {
@@ -150,8 +125,8 @@ public:
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;
@@ -173,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 {
@@ -210,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;
@@ -233,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;
@@ -251,6 +222,10 @@ public:
// Some drivers have gl state issues when drawing from shared contexts
bool disable_shared_context_draws;
// Some drivers require the GL_TEXTURE_EXTERNAL_OES target to be bound when
// the texture image changes, even if it's already bound to that texture
bool texture_external_needs_rebind;
// Some web browsers seem to immediately clear the default framebuffer when calling
// glInvalidateFramebuffer with WebGL 2.0
bool disable_invalidate_framebuffer;
@@ -319,19 +294,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;
@@ -436,7 +400,6 @@ public:
vec4gli viewport { 0 };
vec2glf depthRange { 0.0f, 1.0f };
} window;
uint8_t age = 0;
} state;
struct Procs {
@@ -456,14 +419,9 @@ public:
void (* maxShaderCompilerThreadsKHR)(GLuint count);
} procs{};
void unbindEverything() noexcept;
void synchronizeStateAndCache(size_t index) noexcept;
private:
ShaderModel mShaderModel = ShaderModel::MOBILE;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
{ bugs.disable_glFlush,
@@ -475,6 +433,9 @@ private:
{ bugs.disable_shared_context_draws,
"disable_shared_context_draws",
""},
{ bugs.texture_external_needs_rebind,
"texture_external_needs_rebind",
""},
{ bugs.disable_invalidate_framebuffer,
"disable_invalidate_framebuffer",
""},
@@ -675,26 +636,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);
@@ -765,7 +711,7 @@ void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t
update_state(state.textures.units[unit].targets[targetIndex].texture_id, texId, [&]() {
activeTexture(unit);
glBindTexture(target, texId);
}, target == GL_TEXTURE_EXTERNAL_OES);
}, (target == GL_TEXTURE_EXTERNAL_OES) && bugs.texture_external_needs_rebind);
}
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept {
@@ -778,22 +724,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);
}
}
+94 -183
View File
@@ -90,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
// GLRenderPrimitive : 20 many
// GLFence : 24 few
// -- less than or equal 24 bytes
// GLBufferObject : 32 many
// 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)
@@ -119,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
@@ -149,9 +147,9 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
#endif
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig };
Platform::DriverConfig validConfig {driverConfig};
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
OpenGLDriver* const driver = new OpenGLDriver(ec, validConfig);
return driver;
}
@@ -170,11 +168,10 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(*platform),
mContext(mPlatform),
mContext(),
mShaderCompilerService(*this),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mSamplerMap(32),
mDriverConfig(driverConfig) {
mSamplerMap(32) {
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
@@ -194,6 +191,8 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
assert_invariant(mContext.ext.EXT_disjoint_timer_query);
#endif
mTimerQueryImpl = OpenGLTimerQueryFactory::init(mPlatform, *this);
mShaderCompilerService.init();
}
@@ -238,6 +237,8 @@ void OpenGLDriver::terminate() {
}
#endif
delete mTimerQueryImpl;
mPlatform.terminate();
}
@@ -384,10 +385,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>();
}
@@ -448,21 +445,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(
@@ -510,11 +500,13 @@ 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);
@@ -522,19 +514,17 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
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);
@@ -581,22 +571,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:
@@ -687,12 +668,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);
@@ -762,8 +737,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
}
#endif
}
textureStorage(t, w, h, depth, bool(usage & TextureUsage::PROTECTED));
textureStorage(t, w, h, depth);
}
} else {
assert_invariant(any(usage & (
@@ -772,7 +746,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);
@@ -869,65 +842,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 = 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()) {
@@ -940,17 +909,9 @@ 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,
@@ -1441,21 +1402,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) {
@@ -1495,20 +1448,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);
}
}
@@ -1649,7 +1589,7 @@ void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (tqh) {
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.destroyTimerQuery(tq);
mTimerQueryImpl->destroyTimerQuery(tq);
destruct(tqh, tq);
}
}
@@ -1940,7 +1880,7 @@ bool OpenGLDriver::isFrameBufferFetchMultiSampleSupported() {
}
bool OpenGLDriver::isFrameTimeSupported() {
return TimerQueryFactory::isGpuTimeSupported();
return OpenGLTimerQueryFactory::isGpuTimeSupported();
}
bool OpenGLDriver::isAutoDepthResolveSupported() {
@@ -1958,24 +1898,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() {
@@ -1986,10 +1914,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:
@@ -2057,18 +1981,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:
@@ -2716,18 +2629,18 @@ 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 priority,
@@ -3784,8 +3697,7 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -3811,22 +3723,25 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
// If necessary, mutate the bindings in the VAO.
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
updateVertexArrayObject(rp, glvb);
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
}
@@ -3838,10 +3753,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
#endif
}
void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
+17 -28
View File
@@ -20,7 +20,6 @@
#include "DriverBase.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLTimerQuery.h"
#include "ShaderCompilerService.h"
#include "private/backend/Driver.h"
@@ -39,12 +38,8 @@
#include <tsl/robin_map.h>
#include <atomic>
#include <memory>
#include <set>
#include <stdint.h>
#ifndef FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB 4
#endif
@@ -56,7 +51,7 @@ 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;
@@ -99,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{};
@@ -166,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;
@@ -220,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 {
@@ -322,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... */
@@ -408,8 +397,8 @@ private:
void executeEveryNowAndThenOps() noexcept;
std::vector<std::function<bool()>> mEveryNowAndThenOps;
const Platform::DriverConfig mDriverConfig;
Platform::DriverConfig const& getDriverConfig() const noexcept { return mDriverConfig; }
// timer query implementation
OpenGLTimerQueryInterface* mTimerQueryImpl = nullptr;
// for ES2 sRGB support
GLSwapChain* mCurrentDrawSwapChain = nullptr;
@@ -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,15 +27,6 @@ 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(drawSwapChain, readSwapChain);
}
bool OpenGLPlatform::isProtectedContextSupported() const noexcept {
return false;
}
bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
@@ -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
@@ -151,12 +151,6 @@ bool ShaderCompilerService::isParallelShaderCompileSupported() const noexcept {
}
void ShaderCompilerService::init() noexcept {
if (UTILS_UNLIKELY(mDriver.getDriverConfig().disableParallelShaderCompile)) {
// user disabled parallel shader compile
mMode = Mode::SYNCHRONOUS;
return;
}
// Here we decide which mode we'll be using. We always prefer our own thread-pool if
// that mode is available because, we have no control on how the compilation queues are
// handled if done by the driver (so at the very least we'd need to decode this per-driver).
@@ -261,11 +255,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);
@@ -109,7 +109,7 @@ private:
};
enum class Mode {
UNDEFINED, // init() has not been called yet.
UNDEFINED, // init() has not beed called yet.
SYNCHRONOUS, // synchronous shader compilation
THREAD_POOL, // asynchronous shader compilation using a thread-pool (most common)
ASYNCHRONOUS // asynchronous shader compilation using KHR_parallel_shader_compile
@@ -72,7 +72,6 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
// use getProcAddress for ES3.1 and above entry points.
PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
static std::once_flag sGlExtInitialized;
#endif // __EMSCRIPTEN__
@@ -120,7 +119,6 @@ void importGLESExtensionsEntryPoints() {
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
getProcAddress(glDispatchCompute, "glDispatchCompute");
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
#endif
});
#endif // __EMSCRIPTEN__
-1
View File
@@ -153,7 +153,6 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#endif // __EMSCRIPTEN__
} // namespace glext
@@ -18,32 +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>
#if defined(__ANDROID__)
#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
@@ -64,6 +48,7 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
}
using namespace glext;
// ---------------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------------
@@ -152,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;
@@ -282,7 +266,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
}
}
if (UTILS_UNLIKELY(makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
if (UTILS_UNLIKELY(!makeCurrent(mEGLDummySurface, mEGLDummySurface))) {
// eglMakeCurrent failed
logEglError("eglMakeCurrent");
goto error;
@@ -306,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();
@@ -324,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;
@@ -363,21 +339,10 @@ void PlatformEGL::releaseContext() noexcept {
}
EGLBoolean PlatformEGL::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
EGLBoolean PlatformEGL::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;
if (UTILS_UNLIKELY((drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
mCurrentDrawSurface = drawSurface;
mCurrentReadSurface = readSurface;
return eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext);
}
return EGL_TRUE;
}
@@ -389,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);
}
@@ -462,20 +424,10 @@ EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbu
return config;
}
// -----------------------------------------------------------------------------------------------
bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain const* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
}
return false;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
@@ -496,16 +448,6 @@ 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 = eglCreateWindowSurface(mEGLDisplay, config,
@@ -519,14 +461,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.nativeWindow = (EGLNativeWindowType)nativeWindow,
.config = config,
.flags = flags
});
return sc;
return (SwapChain*)sur;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
@@ -552,16 +487,6 @@ 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());
@@ -570,102 +495,33 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
logEglError("eglCreatePbufferSurface");
return nullptr;
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.config = config
});
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) {
makeCurrent(mCurrentContext, mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sc->sur);
delete sc;
}
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sur);
}
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
makeCurrent(mEGLContext, dsc->sur, rsc->sur);
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
EGLContext context = mEGLContext;
if (ext.egl.EXT_protected_content) {
bool const swapChainProtected = PlatformEGL::isSwapChainProtected(dsc);
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;
}
}
context = mEGLContextProtected;
error: ;
}
bool const contextChange = context != mCurrentContext;
if (UTILS_UNLIKELY(contextChange)) {
preContextChange();
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
if (UTILS_UNLIKELY(!success)) {
logEglError("PlatformEGL::makeCurrent");
if (mEGLContextProtected != EGL_NO_CONTEXT) {
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
mEGLContextProtected = EGL_NO_CONTEXT;
}
context = mEGLContext;
}
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 = (context == mEGLContext) ? 0 : 1;
postContextChange(contextIndex);
return;
}
}
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
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;
}
@@ -704,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;
@@ -736,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;
@@ -783,3 +635,5 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
}
} // namespace filament::backend
// ---------------------------------------------------------------------------------------------
+1 -21
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,9 +55,6 @@
#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
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
@@ -80,7 +72,7 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
#else
#define FVK_DEBUG_FLAGS 0
#endif
@@ -88,23 +80,11 @@
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(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
// 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
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
+116 -223
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;
@@ -216,26 +159,35 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
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 = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
@@ -261,41 +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 : 24 few
// HwStream : 24 few
// VulkanFence : 40 few
// VulkanProgram : 40 moderate
// VulkanIndexBuffer : 72 moderate
// VulkanBufferObject : 72 many
// -- less than or equal 80 bytes
// VulkanRenderPrimitive : 104 many
// VulkanSwapChain : 112 few
// VulkanTimerQuery : 168 few
// -- less than or equal 176 bytes
// VulkanTexture : 232 moderate
// VulkanVertexBuffer : 312 moderate
// VulkanRenderTarget : 320 few
// -- less than or equal to 320 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\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};
@@ -334,12 +251,9 @@ 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();
}
@@ -410,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) {
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, pt, vbh, ibh);
VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -424,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);
}
@@ -650,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>();
}
@@ -868,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() {
@@ -892,10 +777,6 @@ bool VulkanDriver::isDepthStencilResolveSupported() {
return false;
}
bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -983,7 +864,7 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
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,
@@ -1042,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);
@@ -1055,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.");
@@ -1067,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) {
@@ -1147,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
@@ -1299,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.
@@ -1460,12 +1349,22 @@ void VulkanDriver::nextSubpass(int) {
}
}
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
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));
primitive->setPrimitiveType(pt);
}
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) {
@@ -1714,7 +1613,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
const uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
@@ -1725,6 +1624,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
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);
@@ -1741,31 +1641,29 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// 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
};
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.
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->attributes.size();
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();
@@ -1773,7 +1671,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState());
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
@@ -1785,7 +1683,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
UTILS_NOUNROLL
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
@@ -1858,37 +1756,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
return;
}
// 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 firstIndex = indexOffset;
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::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();
@@ -1907,9 +1774,35 @@ 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) {
+5 -26
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,10 +77,12 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh,
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;
@@ -117,6 +95,7 @@ private:
VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
VulkanContext mContext = {};
VulkanResourceAllocator mResourceAllocator;
+13 -46
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,23 +78,6 @@ 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
}
auto& groupInfo = builder.getSamplerGroupInfo();
@@ -112,10 +93,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << 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,
@@ -281,23 +262,13 @@ 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),
attributes(attributes) {
}
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attribs)
: HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mInfo(new PipelineInfo(attribs.size())),
mResources(allocator) {
VulkanVertexBufferInfo const* const vbi = allocator->handle_cast<VulkanVertexBufferInfo*>(vbih);
mInfo = new PipelineInfo(vbi->attributes.size());
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
@@ -305,8 +276,8 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
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 < vbi->attributes.size(); attribIndex++) {
Attribute attrib = vbi->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);
@@ -318,7 +289,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
attrib = vbi->attributes[0];
attrib = attribs[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -338,12 +309,8 @@ 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 count = vbi->attributes.size();
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++) {
+4 -13
View File
@@ -129,21 +129,14 @@ private:
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
AttributeArray attributes;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attributes);
~VulkanVertexBuffer();
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
@@ -161,8 +154,6 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
}
Handle<HwVertexBufferInfo> vbih;
private:
struct PipelineInfo {
PipelineInfo(size_t size)
@@ -170,7 +161,7 @@ private:
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 VK_BUFFER = 2;
@@ -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;
@@ -566,7 +569,7 @@ void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
mPipelineRequirements.rasterState = rasterState;
mPipelineRequirements.rasterState = mCurrentRasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
@@ -628,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);
@@ -645,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],
@@ -912,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;
@@ -197,6 +199,15 @@ public:
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:
// PIPELINE LAYOUT CACHE KEY
// -------------------------
@@ -402,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 = {};
@@ -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,19 +53,12 @@ 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:
+66 -20
View File
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
#include "VulkanUtility.h"
#include <backend/Handle.h>
#include <tsl/robin_set.h>
@@ -45,8 +43,6 @@ enum class VulkanResourceType : uint8_t {
TEXTURE,
TIMER_QUERY,
VERTEX_BUFFER,
VERTEX_BUFFER_INFO,
DESCRIPTOR_SET,
// Below are resources that are managed manually (i.e. not ref counted).
FENCE,
@@ -66,16 +62,15 @@ struct VulkanResourceBase {
protected:
explicit VulkanResourceBase(VulkanResourceType type)
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
mType(uint32_t(type)),
mHandleId(0) {
}
mType(type),
mHandleId(0) {}
private:
inline VulkanResourceType getType() const noexcept {
return VulkanResourceType(mType);
inline VulkanResourceType getType() {
return mType;
}
inline HandleBase::HandleId getId() const noexcept {
inline HandleBase::HandleId getId() {
return mHandleId;
}
@@ -84,27 +79,25 @@ private:
}
inline void ref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount < ((1<<24) - 1));
++mRefCount;
}
inline void deref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount > 0);
--mRefCount;
}
inline size_t refcount() const noexcept {
inline size_t refcount() noexcept {
return mRefCount;
}
uint32_t mRefCount : 24; // 16M is enough for the refcount
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
size_t mRefCount = 0;
VulkanResourceType mType = VulkanResourceType::BUFFER_OBJECT;
HandleBase::HandleId mHandleId;
friend struct VulkanThreadSafeResource;
@@ -114,8 +107,6 @@ private:
friend class VulkanResourceManagerImpl;
};
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
struct VulkanThreadSafeResource {
protected:
explicit VulkanThreadSafeResource(VulkanResourceType type)
@@ -165,7 +156,62 @@ namespace {
// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive),
// we just use a std::array to back the set.
template<std::size_t SIZE>
using FixedCapacityResourceSet = CappedArray<VulkanResource*, SIZE>;
class FixedCapacityResourceSet {
private:
using FixedSizeArray = std::array<VulkanResource*, SIZE>;
public:
using const_iterator = typename FixedSizeArray::const_iterator;
inline ~FixedCapacityResourceSet() {
clear();
}
inline const_iterator begin() {
if (mInd == 0) {
return mArray.cend();
}
return mArray.cbegin();
}
inline const_iterator end() {
if (mInd == 0) {
return mArray.cend();
}
if (mInd < SIZE) {
return mArray.begin() + mInd;
}
return mArray.cend();
}
inline const_iterator find(VulkanResource* resource) {
return std::find(begin(), end(), resource);
}
inline void insert(VulkanResource* resource) {
assert_invariant(mInd < SIZE);
mArray[mInd++] = resource;
}
inline void erase(VulkanResource* resource) {
assert_invariant(false && "FixedCapacityResourceSet::erase should not be called");
}
inline void clear() {
if (mInd == 0) {
return;
}
mInd = 0;
}
inline size_t size() {
return mInd;
}
private:
FixedSizeArray mArray{nullptr};
size_t mInd = 0;
};
// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set
// is cleared.
+11 -20
View File
@@ -35,7 +35,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
mStagePool(stagePool),
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mCurrentImageReadyIndex(0),
mImageReady(VK_NULL_HANDLE),
mAcquired(false),
mIsFirstRenderPass(true) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
@@ -46,15 +46,10 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
};
// No need to wait on this semaphore before drawing when in Headless mode.
if (mHeadless) {
// Set all sempahores to VK_NULL_HANDLE
memset(mImageReady, 0, sizeof(mImageReady[0]) * IMAGE_READY_SEMAPHORE_COUNT);
} else {
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
VkResult result =
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, mImageReady + i);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
}
if (!mHeadless) {
VkResult result =
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, &mImageReady);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
}
update();
@@ -67,11 +62,9 @@ VulkanSwapChain::~VulkanSwapChain() {
mCommands->wait();
mPlatform->destroy(swapChain);
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
if (mImageReady[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(mPlatform->getDevice(), mImageReady[i], VKALLOC);
}
}
if (mImageReady != VK_NULL_HANDLE) {
vkDestroySemaphore(mPlatform->getDevice(), mImageReady, VKALLOC);
}
}
void VulkanSwapChain::update() {
@@ -138,13 +131,11 @@ void VulkanSwapChain::acquire(bool& resized) {
update();
}
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
const VkSemaphore imageReady = mImageReady[mCurrentImageReadyIndex];
VkResult const result = mPlatform->acquire(swapChain, imageReady, &mCurrentSwapIndex);
VkResult const result = mPlatform->acquire(swapChain, mImageReady, &mCurrentSwapIndex);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR,
"Cannot acquire in swapchain.");
if (imageReady != VK_NULL_HANDLE) {
mCommands->injectDependency(imageReady);
if (mImageReady != VK_NULL_HANDLE) {
mCommands->injectDependency(mImageReady);
}
mAcquired = true;
}
@@ -69,8 +69,6 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
}
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
void update();
VulkanPlatform* mPlatform;
@@ -85,8 +83,7 @@ private:
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
std::unique_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
VkSemaphore mImageReady[IMAGE_READY_SEMAPHORE_COUNT];
uint32_t mCurrentImageReadyIndex;
VkSemaphore mImageReady;
uint32_t mCurrentSwapIndex;
bool mAcquired;
bool mIsFirstRenderPass;
+1 -93
View File
@@ -23,8 +23,6 @@
#include <bluevk/BlueVK.h>
#include <utility>
namespace filament::backend {
VkFormat getVkFormat(ElementType type, bool normalized, bool integer);
@@ -90,100 +88,10 @@ utils::FixedCapacityVector<OutType> enumerate(
#undef EXPAND_ENUM_NO_ARGS
#undef EXPAND_ENUM_ARGS
// Useful shorthands
using VkFormatList = utils::FixedCapacityVector<VkFormat>;
// An Array that will be fixed capacity, but the "size" (as in user added elements) is variable.
// Note that this class is movable.
template<typename T, uint16_t CAPACITY>
class CappedArray {
private:
using FixedSizeArray = std::array<T, CAPACITY>;
public:
using const_iterator = typename FixedSizeArray::const_iterator;
using iterator = typename FixedSizeArray::iterator;
CappedArray() = default;
// Delete copy constructor/assignment.
CappedArray(CappedArray const& rhs) = delete;
CappedArray& operator=(CappedArray& rhs) = delete;
CappedArray(CappedArray&& rhs) noexcept {
this->swap(rhs);
}
CappedArray& operator=(CappedArray&& rhs) noexcept {
this->swap(rhs);
return *this;
}
inline ~CappedArray() {
clear();
}
inline const_iterator begin() {
if (mInd == 0) {
return mArray.cend();
}
return mArray.cbegin();
}
inline const_iterator end() {
if (mInd > 0 && mInd < CAPACITY) {
return mArray.begin() + mInd;
}
return mArray.cend();
}
inline T back() {
assert_invariant(mInd > 0);
return *(mArray.begin() + mInd);
}
inline void pop_back() {
assert_invariant(mInd > 0);
mInd--;
}
inline const_iterator find(T item) {
return std::find(begin(), end(), item);
}
inline void insert(T item) {
assert_invariant(mInd < CAPACITY);
mArray[mInd++] = item;
}
inline void erase(T item) {
PANIC_PRECONDITION("CappedArray::erase should not be called");
}
inline void clear() {
if (mInd == 0) {
return;
}
mInd = 0;
}
inline T& operator[](uint16_t ind) {
return mArray[ind];
}
inline size_t size() {
return mInd;
}
private:
void swap(CappedArray& rhs) {
std::swap(mArray, rhs.mArray);
std::swap(mInd, rhs.mInd);
}
FixedSizeArray mArray;
size_t mInd = 0;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANUTILITY_H
@@ -1,647 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanDescriptorSet.h"
#include <vulkan/VulkanConstants.h>
#include <vulkan/VulkanResources.h>
#include <type_traits>
namespace filament::backend {
namespace {
template<typename MaskType>
using LayoutMap = tsl::robin_map<MaskType, VkDescriptorSetLayout>;
using LayoutArray = VulkanDescriptorSetManager::LayoutArray;
using UniformBufferBitmask = VulkanDescriptorSetManager::UniformBufferBitmask;
using SamplerBitmask = VulkanDescriptorSetManager::SamplerBitmask;
constexpr uint8_t EXPECTED_IN_FLIGHT_FRAMES = 3; // Asssume triple buffering
#define PORT_CONSTANT(NameSpace, K) constexpr decltype(NameSpace::K) K = NameSpace::K;
PORT_CONSTANT(VulkanDescriptorSetManager, MAX_SUPPORTED_SHADER_STAGE)
PORT_CONSTANT(VulkanDescriptorSetManager, VERTEX_STAGE)
PORT_CONSTANT(VulkanDescriptorSetManager, FRAGMENT_STAGE)
PORT_CONSTANT(Program, UNIFORM_BINDING_COUNT)
PORT_CONSTANT(Program, SAMPLER_BINDING_COUNT)
#undef PORT_CONSTANT
// Use constexpr to statically generate a bit count table for 8-bit numbers.
struct BitCountHelper {
constexpr BitCountHelper() : data{} {
for (uint16_t i = 0; i < 256; ++i) {
data[i] = 0;
for (auto j = i; j > 0; j /= 2) {
if (j & 1) {
data[i]++;
}
}
}
}
uint8_t data[256];
} BitCountImpl;
inline uint8_t countBits(uint32_t num) {
static constexpr uint8_t const* BIT_COUNT = BitCountImpl.data;
return BIT_COUNT[num & 0xFF] + BIT_COUNT[(num >> 8) & 0xFF] + BIT_COUNT[(num >> 16) & 0xFF] +
BIT_COUNT[(num >> 24) & 0xFF];
}
struct SamplerSet {
using Mask = UniformBufferBitmask;
struct Key {
Mask mask;
static_assert(sizeof(Mask) == 4);
uint32_t padding;// We need a padding to ensure 8-byte alignement.
VkDescriptorSetLayout layout;
VkSampler sampler[SAMPLER_BINDING_COUNT];
VkImageView imageView[SAMPLER_BINDING_COUNT];
VkImageLayout imageLayout[SAMPLER_BINDING_COUNT];
};
struct Equal {
bool operator()(Key const& k1, Key const& k2) const {
if (k1.mask != k2.mask) return false;
if (k1.layout != k2.layout) return false;
for (uint8_t i = 0, bitCount = countBits(k1.mask); i < bitCount; ++i) {
if (k1.sampler[i] != k2.sampler[i] || k1.imageView[i] != k2.imageView[i] ||
k1.imageLayout[i] != k2.imageLayout[i]) {
return false;
}
}
return true;
}
};
using HashFn = utils::hash::MurmurHashFn<Key>;
using Cache = std::unordered_map<Key, VulkanDescriptorSet*, HashFn, Equal>;
};
struct UBOSet {
using Mask = SamplerBitmask;
struct Key {
Mask mask;
static_assert(sizeof(Mask) == 4);
uint32_t padding;// We need a padding to ensure 8-byte alignement.
VkDescriptorSetLayout layout;
VkBuffer buffers[UNIFORM_BINDING_COUNT]; // 80 0
VkDeviceSize offsets[UNIFORM_BINDING_COUNT];// 40 1592
VkDeviceSize sizes[UNIFORM_BINDING_COUNT]; // 40 1632
};
struct Equal {
bool operator()(Key const& k1, Key const& k2) const {
if (k1.mask != k2.mask) return false;
if (k1.layout != k2.layout) return false;
for (uint8_t i = 0, bitCount = countBits(k1.mask); i < bitCount; ++i) {
if (k1.buffers[i] != k2.buffers[i] || k1.offsets[i] != k2.offsets[i] ||
k1.sizes[i] != k2.sizes[i]) {
return false;
}
}
return true;
}
};
using HashFn = utils::hash::MurmurHashFn<Key>;
using Cache = std::unordered_map<Key, VulkanDescriptorSet*, HashFn, Equal>;
};
constexpr uint8_t MAX_BINDINGS = UNIFORM_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE;
static_assert(MAX_BINDINGS >= SAMPLER_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE);
template<typename TYPE>
class DescriptorPool {
private:
static constexpr uint32_t COUNT_MULTIPLIER =
MAX_SUPPORTED_SHADER_STAGE * EXPECTED_IN_FLIGHT_FRAMES;
static constexpr uint32_t CAPACITY =
std::is_same_v<TYPE, UBOSet> ? Program::UNIFORM_BINDING_COUNT * COUNT_MULTIPLIER
: Program::SAMPLER_BINDING_COUNT * COUNT_MULTIPLIER;
public:
DescriptorPool()
: mDevice(VK_NULL_HANDLE),
mResourceAllocator(nullptr) {}
DescriptorPool(VkDevice device, VulkanResourceAllocator* allocator)
: mDevice(device),
mResourceAllocator(allocator) {
VkDescriptorPoolSize sizes[1] = {
{
.type = std::is_same_v<TYPE, UBOSet> ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = CAPACITY,
},
};
VkDescriptorPoolCreateInfo info{
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.maxSets = EXPECTED_IN_FLIGHT_FRAMES,
.poolSizeCount = 1,
.pPoolSizes = sizes,
};
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
}
DescriptorPool(DescriptorPool const&) = delete;
DescriptorPool& operator=(DescriptorPool const&) = delete;
~DescriptorPool() {
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
}
VulkanDescriptorSet* obtainSet(VkDescriptorSetLayout layout) {
if (mCount == CAPACITY) {
return nullptr;
}
if (auto unused = mUnused.find(layout);
unused != mUnused.end() && !unused->second.empty()) {
auto& unusedList = unused->second;
auto set = unusedList.back();
unusedList.pop_back();
mCount++;
return set;
}
// Creating a new set
VkDescriptorSetLayout layouts[1] = {layout};
VkDescriptorSetAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = mPool,
.descriptorSetCount = 1,
.pSetLayouts = layouts,
};
VkDescriptorSet vkSet;
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Cannot allocate descriptor set. error=%d",
(int) result);
mCount++;
return createSet(layout, vkSet);
}
private:
inline VulkanDescriptorSet* createSet(VkDescriptorSetLayout layout, VkDescriptorSet vkSet) {
return VulkanDescriptorSet::create(mResourceAllocator, vkSet, layout,
[this](VulkanDescriptorSet* set) {
auto const layout = set->layout;
auto const vkSet = set->vkSet;
auto listIter = mUnused.find(layout);
assert_invariant(listIter != mUnused.end());
auto& unusedList = listIter->second;
// At this point, we know that the pointer will be stale, and we need to remove
// any non-ref-counted references to it.
// TODO: this will be inefficient
auto iter = std::find(unusedList.begin(), unusedList.end(), set);
unusedList.erase(iter);
// We are recycling - release the vk resource back into the pool. Note that the
// vk handle has not changed, but we need to change the backend handle to allow
// for proper refcounting of resources referenced in this set.
unusedList.push_back(createSet(layout, vkSet));
mCount--;
});
}
VkDevice mDevice;
VulkanResourceAllocator* mResourceAllocator;
VkDescriptorPool mPool;
uint32_t mCount = 0;
std::unordered_map<VkDescriptorSetLayout, std::vector<VulkanDescriptorSet*>> mUnused;
};
template<typename TYPE>
class DescriptorInfinitePool {
public:
DescriptorInfinitePool(VkDevice device, VulkanResourceAllocator* allocator)
: mDevice(device),
mResourceAllocator(allocator) {}
~DescriptorInfinitePool() {
for (auto pool: mPools) {
delete pool;
}
}
VulkanDescriptorSet* obtainSet(VkDescriptorSetLayout layout) {
for (auto pool: mPools) {
auto set = pool->obtainSet(layout);
if (set) {
return set;
}
}
// We need to increase the number of pools
mPools.push_back(new DescriptorPool<TYPE>{mDevice, mResourceAllocator});
auto pool = mPools.back();
return pool->obtainSet(layout);
}
private:
VkDevice mDevice;
VulkanResourceAllocator* mResourceAllocator;
std::vector<DescriptorPool<TYPE>*> mPools;
};
// This holds a cache of descriptor sets of a TYPE (UBO or Sampler). The Key is defined as the
// layout and the content (for example specific samplers).
template<typename TYPE, typename Cache, typename Key>
class DescriptorSetCache {
public:
DescriptorSetCache(VkDevice device, VulkanResourceAllocator* allocator)
: mPool(device, allocator),
mResourceManager(allocator) {}
std::pair<VulkanDescriptorSet*, bool> obtainSet(Key const& key) {
mAge++;
if (auto result = mCache.find(key); result != mCache.end()) {
auto set = result->second;
auto const oldAge = mReverseHistory[set];
mHistory.erase(oldAge);
mHistory[mAge] = set;
mReverseHistory[set] = mAge;
return {set, true};
}
VulkanDescriptorSet* set = mPool.obtainSet(key.layout);
mCache[key] = set;
mReverseCache[set] = key;
mHistory[mAge] = set;
mReverseHistory[set] = mAge;
mResourceManager.acquire(set);
return {set, false};
}
// We need to periodically purge the descriptor sets so that we're not holding on to resources
// unnecessarily. The strategy for purging needs to be examined more.
void gc() noexcept {
constexpr uint32_t ALLOWED_ENTRIES = EXPECTED_IN_FLIGHT_FRAMES * 3;
int32_t const toCutCount = mHistory.size() - ALLOWED_ENTRIES;
if (toCutCount <= 0) {
return;
}
std::vector<uint64_t> remove;
for (auto entry = mHistory.begin(); entry != mHistory.end(); entry++) {
auto const& set = entry->second;
Key const& key = mReverseCache[set];
mCache.erase(key);
mReverseCache.erase(set);
mReverseHistory.erase(set);
remove.push_back(entry->first);
mResourceManager.release(set);
}
for (auto removeAge: remove) {
mHistory.erase(removeAge);
}
}
private:
DescriptorInfinitePool<TYPE> mPool;
// TODO: combine some of these data structures
Cache mCache;
std::unordered_map<VulkanDescriptorSet*, Key> mReverseCache;
// Use the ordering for purging if needed;
std::map<uint64_t, VulkanDescriptorSet*> mHistory;
std::unordered_map<VulkanDescriptorSet*, uint64_t> mReverseHistory;
uint64_t mAge;
// Note that instead of owning the resources (i.e. descriptor set) in the pools, we keep them
// here since all the allocated sets have to pass through this class, and this class has better
// knowledge to make decisions about gc.
VulkanResourceManager mResourceManager;
};
using UBOSetCache = DescriptorSetCache<UBOSet, UBOSet::Cache, UBOSet::Key>;
using SamplerSetCache = DescriptorSetCache<SamplerSet, SamplerSet::Cache, SamplerSet::Key>;
template<typename MaskType>
class LayoutCache {
public:
explicit LayoutCache(VkDevice device)
: mDevice(device) {}
~LayoutCache() {
for (auto [mask, layout]: mLayouts) {
vkDestroyDescriptorSetLayout(mDevice, layout, VKALLOC);
}
mLayouts.clear();
}
VkDescriptorSetLayout getLayout(MaskType stageMask) noexcept {
if (stageMask == 0) {
return VK_NULL_HANDLE;
}
if (auto iter = mLayouts.find(stageMask); iter != mLayouts.end()) {
return iter->second;
}
VkDescriptorType descriptorType;
if constexpr (std::is_same_v<MaskType, UBOSet::Mask>) {
descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
} else if constexpr (std::is_same_v<MaskType, SamplerSet::Mask>) {
descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
}
VkDescriptorSetLayoutBinding toBind[MAX_BINDINGS];
uint32_t count = 0;
for (uint8_t i = 0, maxBindings = sizeof(stageMask) * 4; i < maxBindings; i++) {
VkShaderStageFlags stages = 0;
if ((stageMask & (VERTEX_STAGE << i)) != 0) {
stages |= VK_SHADER_STAGE_VERTEX_BIT;
}
if ((stageMask & (FRAGMENT_STAGE << i)) == 0) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages == 0) {
continue;
}
auto& bindInfo = toBind[count++];
bindInfo = {
.binding = i,
.descriptorType = descriptorType,
.descriptorCount = 1,
.stageFlags = stages,
};
}
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout handle;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &handle);
mLayouts[stageMask] = handle;
return handle;
}
private:
VkDevice mDevice;
LayoutMap<MaskType> mLayouts;
};
using UBOSetLayoutCache = LayoutCache<UBOSet::Mask>;
using SamplerSetLayoutCache = LayoutCache<SamplerSet::Mask>;
} // anonymous namespace
VulkanDescriptorSet::VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn)
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
resources(allocator),
vkSet(rawSet),
layout(layout),
mOnRecycleFn(std::move(onRecycleFn)) {}
VulkanDescriptorSet::~VulkanDescriptorSet() {
if (mOnRecycleFn) {
mOnRecycleFn(this);
}
}
VulkanDescriptorSet* VulkanDescriptorSet::create(VulkanResourceAllocator* allocator,
VkDescriptorSet rawSet, VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn) {
auto handle = allocator->allocHandle<VulkanDescriptorSet>();
auto set = allocator->construct<VulkanDescriptorSet>(handle, allocator, rawSet, layout,
std::move(onRecycleFn));
return set;
}
class VulkanDescriptorSetManager::Impl {
public:
Impl(VkDevice device, VulkanResourceAllocator* allocator)
: mDevice(device),
mUBOLayoutCache(device),
mUBOCache(device, allocator),
mSamplerLayoutCache(device),
mSamplerCache(device, allocator),
mResources(allocator) {}
void setUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
mUbos.push_back({bindingIndex, bufferObject, offset, size});
// Between "set" and "commit", we need to ref the buffer object to avoid it being collected.
mResources.acquire(bufferObject);
}
void setSamplers(SamplerArray&& samplers) {
mSamplers = std::move(samplers);
for (auto const& sampler: mSamplers) {
mResources.acquire(sampler.texture);
}
}
void gc() noexcept {
mUBOCache.gc();
mSamplerCache.gc();
}
// This will write/update all of the descriptor set (and create a set if a one of the same
// layout is not available).
void bind(VulkanCommandBuffer* commands, GetPipelineLayoutFunction& getPipelineLayoutFn) {
LayoutArray layouts = {VK_NULL_HANDLE, VK_NULL_HANDLE};
std::array<VkDescriptorSet, 2> descSets = {VK_NULL_HANDLE, VK_NULL_HANDLE};
uint8_t descSetCount = 0;
VkWriteDescriptorSet descriptorWrites[UNIFORM_BINDING_COUNT + SAMPLER_BINDING_COUNT];
VkDescriptorBufferInfo uboWrite[UNIFORM_BINDING_COUNT];
VkDescriptorImageInfo samplerWrite[SAMPLER_BINDING_COUNT];
uint32_t nwrites = 0;
UBOSet::Key uboKey = {};
auto& uboMask = uboKey.mask;
uint8_t uboCount = 0;
for (auto const& ubo: mUbos) {
auto const& binding = std::get<0>(ubo);
uboKey.buffers[uboCount] = std::get<1>(ubo)->buffer.getGpuBuffer();
uboKey.offsets[uboCount] = std::get<2>(ubo);
uboKey.sizes[uboCount] = std::get<3>(ubo);
uboCount++;
// Currently we let ubo be visible in all stages.
uboMask |= (VERTEX_STAGE << binding);
uboMask |= (FRAGMENT_STAGE << binding);
}
if (uboMask) {
layouts[UBO_SET_INDEX] = uboKey.layout = mUBOLayoutCache.getLayout(uboMask);
auto [descriptorSet, cached] = mUBOCache.obtainSet(uboKey);
descSets[descSetCount++] = descriptorSet->vkSet;
// We need to write to the descriptor set since it wasn't cached.
if (!cached) {
// If it wasn't cached before, we need to ref the resources that it touches.
for (auto const& ubo: mUbos) {
auto const buffer = std::get<1>(ubo);
descriptorSet->resources.acquire(buffer);
}
for (uint8_t i = 0; i < uboCount; ++i) {
auto const& binding = std::get<0>(mUbos[i]);
VkWriteDescriptorSet& descriptorWrite = descriptorWrites[nwrites++];
auto& writeInfo = uboWrite[i];
writeInfo = {
.buffer = uboKey.buffers[i],
.offset = uboKey.offsets[i],
.range = uboKey.sizes[i],
};
descriptorWrite = {
.dstSet = descriptorSet->vkSet,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &writeInfo,
};
}
}
commands->acquire(descriptorSet);
}
SamplerSet::Key samplerKey = {};
auto& samplerMask = samplerKey.mask;
uint8_t samplerCount = 0;
for (auto const sampler: mSamplers) {
samplerMask |= (sampler.stage << sampler.binding);
samplerKey.sampler[samplerCount] = sampler.info.sampler;
samplerKey.imageView[samplerCount] = sampler.info.imageView;
samplerKey.imageLayout[samplerCount] = sampler.info.imageLayout;
samplerCount++;
}
if (samplerMask) {
layouts[SAMPLER_SET_INDEX] = samplerKey.layout =
mSamplerLayoutCache.getLayout(samplerMask);
auto [descriptorSet, cached] = mSamplerCache.obtainSet(samplerKey);
descSets[descSetCount++] = descriptorSet->vkSet;
if (!cached) {
for (auto const& sampler: mSamplers) {
descriptorSet->resources.acquire(sampler.texture);
}
for (uint8_t i = 0; i < samplerCount; ++i) {
auto const& binding = mSamplers[i].binding;
VkWriteDescriptorSet& descriptorWrite = descriptorWrites[nwrites++];
auto& writeInfo = samplerWrite[i];
writeInfo = mSamplers[i].info;
descriptorWrite = {
.dstSet = descriptorSet->vkSet,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &writeInfo,
};
}
}
commands->acquire(descriptorSet);
}
if (nwrites) {
vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr);
}
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(layouts);
VkCommandBuffer const cmdbuffer = commands->buffer();
BoundState state {
.cmdbuf = cmdbuffer,
.pipelineLayout = pipelineLayout,
};
if (state == mPreviousBoundState) {
return;
}
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
descSetCount, descSets.data(), 0, nullptr);
mPreviousBoundState = state;
// Once bound, the resources are now ref'd in the descriptor set and the references in this
// class can be released and the descriptor set is ref'd by the command buffer.
mResources.clear();
}
private:
struct BoundState {
VkCommandBuffer cmdbuf;
VkPipelineLayout pipelineLayout;
bool operator==(BoundState const& b) const {
return cmdbuf == b.cmdbuf && pipelineLayout == b.pipelineLayout;
}
};
VkDevice mDevice;
UBOSetLayoutCache mUBOLayoutCache;
UBOSetCache mUBOCache;
SamplerSetLayoutCache mSamplerLayoutCache;
SamplerSetCache mSamplerCache;
std::vector<std::tuple<uint32_t, VulkanBufferObject*, VkDeviceSize, VkDeviceSize>> mUbos;
SamplerArray mSamplers;
VulkanAcquireOnlyResourceManager mResources;
BoundState mPreviousBoundState;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
VulkanResourceAllocator* resourceAllocator)
: mImpl(new Impl(device, resourceAllocator)) {}
void VulkanDescriptorSetManager::terminate() noexcept {
assert_invariant(mImpl);
delete mImpl;
mImpl = nullptr;
}
void VulkanDescriptorSetManager::gc() noexcept { mImpl->gc(); }
void VulkanDescriptorSetManager::bind(VulkanCommandBuffer* commands,
GetPipelineLayoutFunction& getPipelineLayoutFn) {
mImpl->bind(commands, getPipelineLayoutFn);
}
void VulkanDescriptorSetManager::setUniformBufferObject(uint32_t bindingIndex,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
mImpl->setUniformBufferObject(bindingIndex, bufferObject, offset, size);
}
void VulkanDescriptorSetManager::setSamplers(SamplerArray&& samplers) {
mImpl->setSamplers(std::move(samplers));
}
} // namespace filament::backend
@@ -1,111 +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.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
#include <vulkan/VulkanResourceAllocator.h>
#include <vulkan/VulkanTexture.h>
#include <vulkan/VulkanUtility.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
namespace filament::backend {
// We need to make this class public to enable allocation on the HandleAllocator.
struct VulkanDescriptorSet : public VulkanResourceBase {
public:
// Because we need to recycle descriptor set not used, we allow for a callback that the "Pool"
// can use to repackage the vk handle.
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn);
~VulkanDescriptorSet();
static VulkanDescriptorSet* create(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn);
// TODO: maybe change to fixed size for performance.
VulkanAcquireOnlyResourceManager resources;
VkDescriptorSet const vkSet;
VkDescriptorSetLayout const layout;
private:
OnRecycle mOnRecycleFn;
};
// Abstraction over the pool and the layout cache.
class VulkanDescriptorSetManager {
public:
using StageBitMask = uint32_t;
using UniformBufferBitmask = StageBitMask;
using SamplerBitmask = StageBitMask;
static constexpr uint8_t CONCURRENT_DESCRIPTOR_SET_COUNT = 2;// UBO and samplers
static constexpr uint8_t MAX_SUPPORTED_SHADER_STAGE = 2; // Vertex and fragment.
static_assert(sizeof(UniformBufferBitmask) * 8 >=
(Program::UNIFORM_BINDING_COUNT) *MAX_SUPPORTED_SHADER_STAGE);
static_assert(sizeof(SamplerBitmask) * 8 >=
Program::SAMPLER_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE);
static constexpr StageBitMask VERTEX_STAGE = 0x1;
static constexpr StageBitMask FRAGMENT_STAGE = (0x1 << (sizeof(StageBitMask) / 4));
static constexpr uint8_t UBO_SET_INDEX = 0;
static constexpr uint8_t SAMPLER_SET_INDEX = 1;
using LayoutArray = std::array<VkDescriptorSetLayout, CONCURRENT_DESCRIPTOR_SET_COUNT>;
struct SamplerBundle {
VkDescriptorImageInfo info = {};
VulkanTexture* texture = nullptr;
uint8_t binding = 0;
SamplerBitmask stage = 0;
};
using SamplerArray = CappedArray<SamplerBundle, Program::SAMPLER_BINDING_COUNT>;
using GetPipelineLayoutFunction = std::function<VkPipelineLayout(LayoutArray)>;
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
void terminate() noexcept;
void gc() noexcept;
// This will write/update all of the descriptor set.
void bind(VulkanCommandBuffer* commands, GetPipelineLayoutFunction& getPipelineFn);
void setUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept;
void setSamplers(SamplerArray&& samplers);
private:
class Impl;
Impl* mImpl;
};
} // namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
@@ -23,7 +23,6 @@
#include <bluevk/BlueVK.h>
#include <utils/PrivateImplementation-impl.h>
#include <utils/Panic.h>
#define SWAPCHAIN_RET_FUNC(func, handle, ...) \
if (mImpl->mSurfaceSwapChains.find(handle) != mImpl->mSurfaceSwapChains.end()) { \
@@ -159,9 +158,7 @@ ExtensionSet getInstanceExtensions() {
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -184,9 +181,7 @@ ExtensionSet getInstanceExtensions() {
ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
std::string_view const TARGET_EXTS[] = {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
#endif
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
@@ -342,22 +337,36 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
ExtensionSet const& instExts, ExtensionSet const& deviceExts) {
ExtensionSet newInstExts = instExts;
ExtensionSet newDeviceExts = deviceExts;
if (vkGetPhysicalDeviceProperties2KHR) {
VkPhysicalDeviceDriverProperties driverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &driverProperties,
};
vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2);
char* driverInfo = driverProperties.driverInfo;
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()) {
// Workaround for Mesa drivers. See issue #6192
if ((driverInfo && strstr(driverInfo, "Mesa"))) {
newInstExts.erase(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
}
}
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// debugUtils and debugMarkers extensions are used mutually exclusively.
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()
&& newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
#endif
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// debugMarker must also request debugReport the instance extension. So check if that's present.
if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()
&& newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
#endif
return std::tuple(newInstExts, newDeviceExts);
}
@@ -662,12 +671,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
context.mDebugMarkersSupported
= deviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != deviceExts.end();
#ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions
ASSERT_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported,
"Debug utils should not be enabled in release build.");
#endif
context.mDepthFormats = findAttachmentDepthFormats(mImpl->mPhysicalDevice);
assert_invariant(context.mDepthFormats.size() > 0);

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