Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0709f7d59a | ||
|
|
2027c9e237 | ||
|
|
86456c9e34 | ||
|
|
fad58ba892 | ||
|
|
8ae296974c | ||
|
|
f9575675d9 | ||
|
|
71025cf25a | ||
|
|
a067fb02b1 | ||
|
|
ffde51ee6f | ||
|
|
35f99ed168 | ||
|
|
a939ad03f8 | ||
|
|
05e628fff9 | ||
|
|
ad8473760a | ||
|
|
cb672eb063 | ||
|
|
9811b785cb | ||
|
|
ff6d4d721e | ||
|
|
c8a5094c41 | ||
|
|
0f34603cfc | ||
|
|
300f80448d | ||
|
|
a41d343a75 |
+2
-1
@@ -1,7 +1,8 @@
|
||||
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((c++-mode . ((c-file-style . "filament")
|
||||
((c++-mode . ((eglot-ignored-server-capabilities . (:documentFormattingProvider))
|
||||
(c-file-style . "filament")
|
||||
(apheleia-inhibit . t)))
|
||||
(c-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: macos-14
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
@@ -757,6 +757,7 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
|
||||
add_subdirectory(${EXTERNAL}/spirv-tools)
|
||||
add_subdirectory(${EXTERNAL}/glslang/tnt)
|
||||
add_subdirectory(${EXTERNAL}/spirv-cross/tnt)
|
||||
add_subdirectory(${LIBRARIES}/astrict)
|
||||
add_subdirectory(${LIBRARIES}/filamat)
|
||||
|
||||
# the material debugger requires filamat
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.50.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.50.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.50.5'
|
||||
pod 'Filament', '~> 1.50.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,22 +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.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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,18 +356,6 @@ public class Engine {
|
||||
* @see Engine#getMaxStereoscopicEyes
|
||||
*/
|
||||
public long stereoscopicEyeCount = 2;
|
||||
|
||||
/*
|
||||
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
|
||||
* size of used render targets (typically the screen).
|
||||
*/
|
||||
public long resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
* The default value of 30 corresponds to about half a second at 60 fps.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 30;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -1281,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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.50.5
|
||||
VERSION_NAME=1.50.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.gltf"
|
||||
minSdkVersion versions.minSdk
|
||||
minSdkVersion 19
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
26.1.10909125
|
||||
25.1.8937393
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1212,14 +1212,6 @@ enum class Workaround : uint16_t {
|
||||
DISABLE_THREAD_AFFINITY
|
||||
};
|
||||
|
||||
//! The type of technique for stereoscopic rendering
|
||||
enum class StereoscopicType : uint8_t {
|
||||
// Stereoscopic rendering is performed using instanced rendering technique.
|
||||
INSTANCED,
|
||||
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
||||
|
||||
@@ -39,7 +39,6 @@ struct HwStream;
|
||||
struct HwSwapChain;
|
||||
struct HwTexture;
|
||||
struct HwTimerQuery;
|
||||
struct HwVertexBufferInfo;
|
||||
struct HwVertexBuffer;
|
||||
|
||||
/*
|
||||
@@ -63,6 +62,14 @@ public:
|
||||
// clear the handle, this doesn't free associated resources
|
||||
void clear() noexcept { object = nullid; }
|
||||
|
||||
// compare handles
|
||||
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
|
||||
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
|
||||
bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; }
|
||||
bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; }
|
||||
bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; }
|
||||
bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; }
|
||||
|
||||
// get this handle's handleId
|
||||
HandleId getId() const noexcept { return object; }
|
||||
|
||||
@@ -94,14 +101,6 @@ struct Handle : public HandleBase {
|
||||
|
||||
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
|
||||
|
||||
// compare handles of the same type
|
||||
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
|
||||
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
|
||||
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
|
||||
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
|
||||
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
|
||||
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
|
||||
|
||||
// type-safe Handle cast
|
||||
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
|
||||
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
|
||||
@@ -115,19 +114,18 @@ private:
|
||||
|
||||
// Types used by the command stream
|
||||
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct PipelineState {
|
||||
RasterState rasterState;
|
||||
StencilState stencilState;
|
||||
PolygonOffset polygonOffset;
|
||||
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
*
|
||||
* @return nullptr on failure, or a pointer to the newly created driver.
|
||||
*/
|
||||
virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
|
||||
virtual backend::Driver* createDriver(void* sharedContext,
|
||||
const DriverConfig& driverConfig) noexcept = 0;
|
||||
|
||||
/**
|
||||
@@ -102,8 +102,7 @@ public:
|
||||
* cache.
|
||||
*/
|
||||
using InsertBlobFunc = utils::Invocable<
|
||||
void(const void* UTILS_NONNULL key, size_t keySize,
|
||||
const void* UTILS_NONNULL value, size_t valueSize)>;
|
||||
void(const void* key, size_t keySize, const void* value, size_t valueSize)>;
|
||||
|
||||
/*
|
||||
* RetrieveBlobFunc is an Invocable to an application-provided function that a
|
||||
@@ -111,8 +110,7 @@ public:
|
||||
* cache.
|
||||
*/
|
||||
using RetrieveBlobFunc = utils::Invocable<
|
||||
size_t(const void* UTILS_NONNULL key, size_t keySize,
|
||||
void* UTILS_NONNULL value, size_t valueSize)>;
|
||||
size_t(const void* key, size_t keySize, void* value, size_t valueSize)>;
|
||||
|
||||
/**
|
||||
* Sets the callback functions that the backend can use to interact with caching functionality
|
||||
@@ -165,8 +163,7 @@ public:
|
||||
* @param value pointer to the beginning of the value data that is to be inserted
|
||||
* @param valueSize specifies the size in byte of the data pointed to by <value>
|
||||
*/
|
||||
void insertBlob(const void* UTILS_NONNULL key, size_t keySize,
|
||||
const void* UTILS_NONNULL value, size_t valueSize);
|
||||
void insertBlob(const void* key, size_t keySize, const void* value, size_t valueSize);
|
||||
|
||||
/**
|
||||
* To retrieve the binary value associated with a given key from the cache, a
|
||||
@@ -185,8 +182,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,8 +21,6 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -43,8 +41,8 @@ protected:
|
||||
* Derived classes can use this to instantiate the default OpenGLDriver backend.
|
||||
* This is typically called from your implementation of createDriver()
|
||||
*/
|
||||
static Driver* UTILS_NULLABLE createDefaultDriver(OpenGLPlatform* UTILS_NONNULL platform,
|
||||
void* UTILS_NULLABLE sharedContext, const DriverConfig& driverConfig);
|
||||
static Driver* createDefaultDriver(OpenGLPlatform* platform,
|
||||
void* sharedContext, const DriverConfig& driverConfig);
|
||||
|
||||
~OpenGLPlatform() noexcept override;
|
||||
|
||||
@@ -71,8 +69,7 @@ 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.
|
||||
@@ -93,14 +90,13 @@ public:
|
||||
* TODO: we need a more generic way of passing construction parameters
|
||||
* A void* might be enough.
|
||||
*/
|
||||
virtual SwapChain* UTILS_NULLABLE createSwapChain(
|
||||
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
|
||||
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver to destroys the SwapChain
|
||||
* @param swapChain SwapChain to be destroyed.
|
||||
*/
|
||||
virtual void destroySwapChain(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
|
||||
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Returns the set of buffers that must be preserved up to the call to commit().
|
||||
@@ -113,7 +109,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.
|
||||
@@ -127,16 +123,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;
|
||||
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.
|
||||
@@ -161,14 +155,14 @@ public:
|
||||
*
|
||||
* @return A Fence object. The default implementation returns nullptr.
|
||||
*/
|
||||
virtual Fence* UTILS_NULLABLE createFence() noexcept;
|
||||
virtual Fence* createFence() noexcept;
|
||||
|
||||
/**
|
||||
* Destroys a Fence object. The default implementation does nothing.
|
||||
*
|
||||
* @param fence Fence to destroy.
|
||||
*/
|
||||
virtual void destroyFence(Fence* UTILS_NONNULL fence) noexcept;
|
||||
virtual void destroyFence(Fence* fence) noexcept;
|
||||
|
||||
/**
|
||||
* Waits on a Fence.
|
||||
@@ -178,7 +172,7 @@ public:
|
||||
* @return Whether the fence signaled or timed out. See backend::FenceStatus.
|
||||
* The default implementation always return backend::FenceStatus::ERROR.
|
||||
*/
|
||||
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
|
||||
virtual backend::FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
@@ -192,13 +186,13 @@ public:
|
||||
* @param nativeStream The native stream, this parameter depends on the concrete implementation.
|
||||
* @return A new Stream object.
|
||||
*/
|
||||
virtual Stream* UTILS_NULLABLE createStream(void* UTILS_NULLABLE nativeStream) noexcept;
|
||||
virtual Stream* createStream(void* nativeStream) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys a Stream.
|
||||
* @param stream Stream to destroy.
|
||||
*/
|
||||
virtual void destroyStream(Stream* UTILS_NONNULL stream) noexcept;
|
||||
virtual void destroyStream(Stream* stream) noexcept;
|
||||
|
||||
/**
|
||||
* The specified stream takes ownership of the texture (tname) object
|
||||
@@ -208,21 +202,20 @@ public:
|
||||
* @param stream Stream to take ownership of the texture
|
||||
* @param tname GL texture id to "bind" to the Stream.
|
||||
*/
|
||||
virtual void attach(Stream* UTILS_NONNULL stream, intptr_t tname) noexcept;
|
||||
virtual void attach(Stream* stream, intptr_t tname) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys the texture associated to the stream
|
||||
* @param stream Stream to detach from its texture
|
||||
*/
|
||||
virtual void detach(Stream* UTILS_NONNULL stream) noexcept;
|
||||
virtual void detach(Stream* stream) noexcept;
|
||||
|
||||
/**
|
||||
* Updates the content of the texture attached to the stream.
|
||||
* @param stream Stream to update
|
||||
* @param timestamp Output parameter: Timestamp of the image bound to the texture.
|
||||
*/
|
||||
virtual void updateTexImage(Stream* UTILS_NONNULL stream,
|
||||
int64_t* UTILS_NONNULL timestamp) noexcept;
|
||||
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
@@ -235,13 +228,13 @@ public:
|
||||
* implementation could just return { 0, GL_TEXTURE_2D } at this point. The actual
|
||||
* values can be delayed until setExternalImage.
|
||||
*/
|
||||
virtual ExternalTexture* UTILS_NULLABLE createExternalImageTexture() noexcept;
|
||||
virtual ExternalTexture *createExternalImageTexture() noexcept;
|
||||
|
||||
/**
|
||||
* Destroys an external texture handle and associated data.
|
||||
* @param texture a pointer to the handle to destroy.
|
||||
*/
|
||||
virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept;
|
||||
virtual void destroyExternalImage(ExternalTexture* texture) noexcept;
|
||||
|
||||
// called on the application thread to allow Filament to take ownership of the image
|
||||
|
||||
@@ -254,7 +247,7 @@ public:
|
||||
* @param externalImage A token representing the platform's external image.
|
||||
* @see destroyExternalImage
|
||||
*/
|
||||
virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept;
|
||||
virtual void retainExternalImage(void* externalImage) noexcept;
|
||||
|
||||
/**
|
||||
* Called to bind the platform-specific externalImage to an ExternalTexture.
|
||||
@@ -268,8 +261,7 @@ public:
|
||||
* @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary.
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual bool setExternalImage(void* UTILS_NONNULL externalImage,
|
||||
ExternalTexture* UTILS_NONNULL texture) noexcept;
|
||||
virtual bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept;
|
||||
|
||||
/**
|
||||
* The method allows platforms to convert a user-supplied external image object into a new type
|
||||
|
||||
@@ -151,13 +151,6 @@ protected:
|
||||
} egl;
|
||||
} ext;
|
||||
|
||||
struct SwapChainEGL : public Platform::SwapChain {
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
Config attribs{};
|
||||
EGLNativeWindowType nativeWindow{};
|
||||
EGLConfig config{};
|
||||
};
|
||||
|
||||
void initializeGlExtensions() noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -40,36 +37,28 @@ public:
|
||||
|
||||
~CircularBuffer() noexcept;
|
||||
|
||||
static size_t getBlockSize() noexcept { return sPageSize; }
|
||||
|
||||
// Total size of circular buffer. This is a constant.
|
||||
size_t size() const noexcept { return mSize; }
|
||||
|
||||
// Allocates `s` bytes in the circular buffer and returns a pointer to the memory. All
|
||||
// allocations must not exceed size() bytes.
|
||||
inline void* allocate(size_t s) noexcept {
|
||||
// We can never allocate more that size().
|
||||
assert_invariant(getUsed() + s <= size());
|
||||
// allocates 'size' bytes in the circular buffer and returns a pointer to the memory
|
||||
// return the current head and moves it forward by size bytes
|
||||
inline void* allocate(size_t size) noexcept {
|
||||
char* const cur = static_cast<char*>(mHead);
|
||||
mHead = cur + s;
|
||||
mHead = cur + size;
|
||||
return cur;
|
||||
}
|
||||
|
||||
// Returns true if the buffer is empty, i.e.: no allocations were made since
|
||||
// calling getBuffer();
|
||||
// Total size of circular buffer
|
||||
size_t size() const noexcept { return mSize; }
|
||||
|
||||
// returns true if the buffer is empty (e.g. after calling flush)
|
||||
bool empty() const noexcept { return mTail == mHead; }
|
||||
|
||||
// Returns the size used since the last call to getBuffer()
|
||||
size_t getUsed() const noexcept { return intptr_t(mHead) - intptr_t(mTail); }
|
||||
void* getHead() const noexcept { return mHead; }
|
||||
|
||||
// Retrieves the current allocated range and frees it. It is the responsibility of the caller
|
||||
// to make sure the returned range is no longer in use by the time allocate() allocates
|
||||
// (size() - getUsed()) bytes.
|
||||
struct Range {
|
||||
void* tail;
|
||||
void* head;
|
||||
};
|
||||
Range getBuffer() noexcept;
|
||||
void* getTail() const noexcept { return mTail; }
|
||||
|
||||
// call at least once every getRequiredSize() bytes allocated from the buffer
|
||||
void circularize() noexcept;
|
||||
|
||||
static size_t getBlockSize() noexcept { return sPageSize; }
|
||||
|
||||
private:
|
||||
void* alloc(size_t size) noexcept;
|
||||
@@ -77,10 +66,10 @@ private:
|
||||
|
||||
// pointer to the beginning of the circular buffer (constant)
|
||||
void* mData = nullptr;
|
||||
int mAshmemFd = -1;
|
||||
int mUsesAshmem = -1;
|
||||
|
||||
// size of the circular buffer (constant)
|
||||
size_t const mSize;
|
||||
size_t mSize = 0;
|
||||
|
||||
// pointer to the beginning of recorded data
|
||||
void* mTail = nullptr;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace filament::backend {
|
||||
* A producer-consumer command queue that uses a CircularBuffer as main storage
|
||||
*/
|
||||
class CommandBufferQueue {
|
||||
struct Range {
|
||||
struct Slice {
|
||||
void* begin;
|
||||
void* end;
|
||||
};
|
||||
@@ -46,7 +46,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 +58,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.
|
||||
|
||||
@@ -213,8 +213,6 @@ public:
|
||||
CommandStream(CommandStream const& rhs) noexcept = delete;
|
||||
CommandStream& operator=(CommandStream const& rhs) noexcept = delete;
|
||||
|
||||
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
|
||||
|
||||
public:
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
inline void methodName(paramsDecl) { \
|
||||
|
||||
@@ -167,14 +167,11 @@ DECL_DRIVER_API_0(resetState)
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint8_t, bufferCount,
|
||||
uint8_t, attributeCount,
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint32_t, vertexCount,
|
||||
backend::VertexBufferInfoHandle, vbih)
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
|
||||
backend::ElementType, elementType,
|
||||
@@ -227,7 +224,11 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
|
||||
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
|
||||
backend::VertexBufferHandle, vbh,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
backend::PrimitiveType, pt)
|
||||
backend::PrimitiveType, pt,
|
||||
uint32_t, offset,
|
||||
uint32_t, minIndex,
|
||||
uint32_t, maxIndex,
|
||||
uint32_t, count)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
|
||||
backend::Program&&, program)
|
||||
@@ -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,7 +298,7 @@ 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_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(uint8_t, getMaxDrawBuffers)
|
||||
@@ -494,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
|
||||
|
||||
|
||||
@@ -24,31 +24,35 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
|
||||
#if !defined(NDEBUG) && UTILS_HAS_RTTI
|
||||
# define HANDLE_TYPE_SAFETY 1
|
||||
#else
|
||||
# define HANDLE_TYPE_SAFETY 0
|
||||
#endif
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
|
||||
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
|
||||
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/*
|
||||
* A utility class to efficiently allocate and manage Handle<>
|
||||
*/
|
||||
template<size_t P0, size_t P1, size_t P2>
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
class HandleAllocator {
|
||||
public:
|
||||
|
||||
HandleAllocator(const char* name, size_t size) noexcept;
|
||||
HandleAllocator(HandleAllocator const& rhs) = delete;
|
||||
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
|
||||
@@ -66,9 +70,14 @@ public:
|
||||
*/
|
||||
template<typename D, typename ... ARGS>
|
||||
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
|
||||
Handle<D> h{ allocateHandle<D>() };
|
||||
Handle<D> h{ allocateHandle<sizeof(D)>() };
|
||||
D* addr = handle_cast<D*>(h);
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -84,7 +93,13 @@ public:
|
||||
*/
|
||||
template<typename D>
|
||||
Handle<D> allocate() noexcept {
|
||||
Handle<D> h{ allocateHandle<D>() };
|
||||
Handle<D> h{ allocateHandle<sizeof(D)>() };
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
D* addr = handle_cast<D*>(h);
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -101,10 +116,17 @@ public:
|
||||
assert_invariant(handle);
|
||||
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
|
||||
assert_invariant(addr);
|
||||
|
||||
// currently we implement construct<> with dtor+ctor, we could use operator= also
|
||||
// but all our dtors are trivial, ~D() is actually a noop.
|
||||
addr->~D();
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -121,6 +143,12 @@ public:
|
||||
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
|
||||
assert_invariant(addr);
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -136,8 +164,19 @@ public:
|
||||
void deallocate(Handle<B>& handle, D const* p) noexcept {
|
||||
// allow to destroy the nullptr, similarly to operator delete
|
||||
if (p) {
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
auto typeId = mHandleTypeId[p];
|
||||
mHandleTypeId.erase(p);
|
||||
mLock.unlock();
|
||||
if (UTILS_UNLIKELY(typeId != typeid(D).name())) {
|
||||
utils::slog.e << "Destroying handle " << handle.getId() << ", type " << typeid(D).name()
|
||||
<< ", but handle's actual type is " << typeId << utils::io::endl;
|
||||
std::terminate();
|
||||
}
|
||||
#endif
|
||||
p->~D();
|
||||
deallocateHandle<D>(handle.getId());
|
||||
deallocateHandle<sizeof(D)>(handle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,17 +204,7 @@ public:
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B>& handle) noexcept {
|
||||
assert_invariant(handle);
|
||||
auto [p, tag] = handleToPointer(handle.getId());
|
||||
|
||||
if (isPoolHandle(handle.getId())) {
|
||||
// check for use after free
|
||||
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 +219,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, 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 +263,24 @@ private:
|
||||
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
|
||||
// allocation size this is always inlined, because all these do is to call
|
||||
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
|
||||
template<typename D>
|
||||
template<size_t SIZE>
|
||||
HandleBase::HandleId allocateHandle() noexcept {
|
||||
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
|
||||
return allocateHandleInPool<BUCKET_SIZE>();
|
||||
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
|
||||
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
|
||||
static_assert(SIZE <= P2);
|
||||
return allocateHandleInPool<P2>();
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
template<size_t SIZE>
|
||||
void deallocateHandle(HandleBase::HandleId id) noexcept {
|
||||
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
|
||||
deallocateHandleFromPool<BUCKET_SIZE>(id);
|
||||
if constexpr (SIZE <= P0) {
|
||||
deallocateHandleFromPool<P0>(id);
|
||||
} else if constexpr (SIZE <= P1) {
|
||||
deallocateHandleFromPool<P1>(id);
|
||||
} else {
|
||||
static_assert(SIZE <= P2);
|
||||
deallocateHandleFromPool<P2>(id);
|
||||
}
|
||||
}
|
||||
|
||||
// allocateHandleInPool()/deallocateHandleFromPool() is NOT inlined, which will cause three
|
||||
@@ -280,11 +289,9 @@ private:
|
||||
template<size_t SIZE>
|
||||
UTILS_NOINLINE
|
||||
HandleBase::HandleId allocateHandleInPool() noexcept {
|
||||
uint8_t age;
|
||||
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
|
||||
void* p = mHandleArena.alloc(SIZE);
|
||||
if (UTILS_LIKELY(p)) {
|
||||
uint32_t const tag = (uint32_t(age) << HANDLE_AGE_SHIFT) & HANDLE_AGE_MASK;
|
||||
return arenaPointerToHandle(p, tag);
|
||||
return pointerToHandle(p);
|
||||
} else {
|
||||
return allocateHandleSlow(SIZE);
|
||||
}
|
||||
@@ -294,51 +301,42 @@ private:
|
||||
UTILS_NOINLINE
|
||||
void deallocateHandleFromPool(HandleBase::HandleId id) noexcept {
|
||||
if (UTILS_LIKELY(isPoolHandle(id))) {
|
||||
auto [p, tag] = handleToPointer(id);
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
mHandleArena.free(p, SIZE, age);
|
||||
void* p = handleToPointer(id);
|
||||
mHandleArena.free(p, SIZE);
|
||||
} else {
|
||||
deallocateHandleSlow(id, SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// we handle a 4 bits age per address
|
||||
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
|
||||
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
|
||||
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
|
||||
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
|
||||
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
|
||||
static constexpr uint32_t HEAP_HANDLE_FLAG = 0x80000000u;
|
||||
|
||||
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
|
||||
return (id & HANDLE_HEAP_FLAG) == 0u;
|
||||
return (id & HEAP_HANDLE_FLAG) == 0u;
|
||||
}
|
||||
|
||||
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
|
||||
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
|
||||
|
||||
// We inline this because it's just 4 instructions in the fast case
|
||||
inline std::pair<void*, uint32_t> handleToPointer(HandleBase::HandleId id) const noexcept {
|
||||
inline void* handleToPointer(HandleBase::HandleId id) const noexcept {
|
||||
// note: the null handle will end-up returning nullptr b/c it'll be handled as
|
||||
// a non-pool handle.
|
||||
if (UTILS_LIKELY(isPoolHandle(id))) {
|
||||
char* const base = (char*)mHandleArena.getArea().begin();
|
||||
uint32_t const tag = id & HANDLE_TAG_MASK;
|
||||
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
|
||||
return { static_cast<void*>(base + offset), tag };
|
||||
size_t offset = id << Allocator::MIN_ALIGNMENT_SHIFT;
|
||||
return static_cast<void*>(base + offset);
|
||||
}
|
||||
return { handleToPointerSlow(id), 0 };
|
||||
return handleToPointerSlow(id);
|
||||
}
|
||||
|
||||
void* handleToPointerSlow(HandleBase::HandleId id) const noexcept;
|
||||
|
||||
// We inline this because it's just 3 instructions
|
||||
inline HandleBase::HandleId arenaPointerToHandle(void* p, uint32_t tag) const noexcept {
|
||||
inline HandleBase::HandleId pointerToHandle(void* p) const noexcept {
|
||||
char* const base = (char*)mHandleArena.getArea().begin();
|
||||
size_t const offset = (char*)p - base;
|
||||
assert_invariant((offset % Allocator::getAlignment()) == 0);
|
||||
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
|
||||
id |= tag & HANDLE_TAG_MASK;
|
||||
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
|
||||
size_t offset = (char*)p - base;
|
||||
auto id = HandleBase::HandleId(offset >> Allocator::MIN_ALIGNMENT_SHIFT);
|
||||
assert_invariant((id & HEAP_HANDLE_FLAG) == 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -348,6 +346,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,14 +722,8 @@ bool MetalDriver::isSRGBSwapChainSupported() {
|
||||
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() {
|
||||
@@ -1083,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) {
|
||||
@@ -1592,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);
|
||||
@@ -1610,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();
|
||||
|
||||
@@ -1731,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 };
|
||||
@@ -1808,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];
|
||||
}
|
||||
|
||||
@@ -1884,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.");
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,7 +174,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -343,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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -622,7 +622,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
|
||||
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
|
||||
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
|
||||
@@ -687,7 +686,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->OES_standard_derivatives = true;
|
||||
ext->OES_texture_npot = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = false;
|
||||
|
||||
@@ -205,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;
|
||||
|
||||
@@ -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
|
||||
@@ -388,10 +386,6 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
// Creating driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Handle<HwVertexBufferInfo> OpenGLDriver::createVertexBufferInfoS() noexcept {
|
||||
return initHandle<GLVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> OpenGLDriver::createVertexBufferS() noexcept {
|
||||
return initHandle<GLVertexBuffer>();
|
||||
}
|
||||
@@ -452,21 +446,14 @@ Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
|
||||
return initHandle<GLTimerQuery>();
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferInfoR(
|
||||
Handle<HwVertexBufferInfo> vbih,
|
||||
uint8_t bufferCount,
|
||||
uint8_t attributeCount,
|
||||
AttributeArray attributes) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferR(
|
||||
Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {
|
||||
uint8_t bufferCount,
|
||||
uint8_t attributeCount,
|
||||
uint32_t elementCount,
|
||||
AttributeArray attributes) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
|
||||
construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createIndexBufferR(
|
||||
@@ -514,11 +501,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);
|
||||
|
||||
@@ -526,14 +515,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
|
||||
|
||||
gl.procs.genVertexArrays(1, &rp->gl.vao);
|
||||
|
||||
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);
|
||||
@@ -856,6 +848,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
|
||||
// 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
|
||||
@@ -869,49 +862,42 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
// that it's always reset in draw
|
||||
}
|
||||
|
||||
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(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()) {
|
||||
@@ -1424,14 +1410,6 @@ void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// 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) {
|
||||
@@ -1921,20 +1899,12 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
|
||||
return mPlatform.isSRGBSwapChainSupported();
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -3728,8 +3698,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;
|
||||
|
||||
@@ -3764,15 +3733,16 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
|
||||
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
|
||||
}
|
||||
@@ -3784,10 +3754,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();
|
||||
|
||||
|
||||
@@ -94,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{};
|
||||
|
||||
@@ -18,14 +18,6 @@
|
||||
|
||||
#include "OpenGLDriverFactory.h"
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
|
||||
|
||||
@@ -261,11 +261,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
[this, &gl, program = std::move(program), token]() mutable {
|
||||
// compile the shaders
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
shaders,
|
||||
token->shaderSourceCode);
|
||||
shaderSourceCode);
|
||||
|
||||
// link the program
|
||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||
|
||||
@@ -18,31 +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/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
|
||||
@@ -63,6 +48,7 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
|
||||
}
|
||||
using namespace glext;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -151,7 +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.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;
|
||||
@@ -475,13 +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
|
||||
});
|
||||
return sc;
|
||||
return (SwapChain*)sur;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
@@ -515,42 +495,30 @@ 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(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);
|
||||
if (UTILS_UNLIKELY(dsc->sur == EGL_NO_SURFACE && rsc->sur == EGL_NO_SURFACE)) {
|
||||
return;
|
||||
EGLSurface drawSur = (EGLSurface) drawSwapChain;
|
||||
EGLSurface readSur = (EGLSurface) readSwapChain;
|
||||
if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) {
|
||||
makeCurrent(drawSur, readSur);
|
||||
}
|
||||
makeCurrent(dsc->sur, rsc->sur);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +561,7 @@ FenceStatus PlatformEGL::waitFence(
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,14 +764,8 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
|
||||
return mIsSRGBSwapChainSupported;
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -974,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,
|
||||
@@ -1138,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
|
||||
@@ -1290,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.
|
||||
@@ -1451,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) {
|
||||
@@ -1705,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");
|
||||
|
||||
@@ -1716,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);
|
||||
@@ -1732,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();
|
||||
@@ -1764,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);
|
||||
|
||||
@@ -1849,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();
|
||||
@@ -1898,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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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 {
|
||||
@@ -914,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
|
||||
|
||||
@@ -199,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
|
||||
// -------------------------
|
||||
@@ -404,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 = {};
|
||||
|
||||
|
||||
@@ -53,9 +53,6 @@ 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;
|
||||
|
||||
@@ -43,7 +43,6 @@ enum class VulkanResourceType : uint8_t {
|
||||
TEXTURE,
|
||||
TIMER_QUERY,
|
||||
VERTEX_BUFFER,
|
||||
VERTEX_BUFFER_INFO,
|
||||
|
||||
// Below are resources that are managed manually (i.e. not ref counted).
|
||||
FENCE,
|
||||
@@ -63,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;
|
||||
}
|
||||
|
||||
@@ -81,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;
|
||||
@@ -111,8 +107,6 @@ private:
|
||||
friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
|
||||
|
||||
struct VulkanThreadSafeResource {
|
||||
protected:
|
||||
explicit VulkanThreadSafeResource(VulkanResourceType type)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -143,7 +143,7 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
|
||||
|
||||
const size_t size = sizeof(math::float2) * 3;
|
||||
mBufferObject = mDriverApi.createBufferObject(size, BufferObjectBinding::VERTEX, BufferUsage::STATIC);
|
||||
mVertexBufferInfo = mDriverApi.createVertexBufferInfo(1, 1, attributes);
|
||||
mVertexBuffer = mDriverApi.createVertexBuffer(mVertexCount, mVertexBufferInfo);
|
||||
|
||||
mVertexBuffer = mDriverApi.createVertexBuffer(1, 1, mVertexCount, attributes);
|
||||
mDriverApi.setVertexBufferObject(mVertexBuffer, 0, mBufferObject);
|
||||
BufferDescriptor vertexBufferDesc(gVertices, size);
|
||||
mDriverApi.updateBufferObject(mBufferObject, std::move(vertexBufferDesc), 0);
|
||||
@@ -62,7 +60,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
|
||||
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(indexBufferDesc), 0);
|
||||
|
||||
mRenderPrimitive = mDriverApi.createRenderPrimitive(
|
||||
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES);
|
||||
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES, 0, 0, 2, 3);
|
||||
}
|
||||
|
||||
void TrianglePrimitive::updateVertices(const filament::math::float2 vertices[3]) noexcept {
|
||||
@@ -104,7 +102,6 @@ void TrianglePrimitive::updateIndices(const index_type* indices, int count, int
|
||||
TrianglePrimitive::~TrianglePrimitive() {
|
||||
mDriverApi.destroyBufferObject(mBufferObject);
|
||||
mDriverApi.destroyVertexBuffer(mVertexBuffer);
|
||||
mDriverApi.destroyVertexBufferInfo(mVertexBufferInfo);
|
||||
mDriverApi.destroyIndexBuffer(mIndexBuffer);
|
||||
mDriverApi.destroyRenderPrimitive(mRenderPrimitive);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
|
||||
using PrimitiveHandle = filament::backend::Handle<filament::backend::HwRenderPrimitive>;
|
||||
using BufferObjectHandle = filament::backend::Handle<filament::backend::HwBufferObject>;
|
||||
using VertexInfoHandle = filament::backend::Handle<filament::backend::HwVertexBufferInfo>;
|
||||
using VertexHandle = filament::backend::Handle<filament::backend::HwVertexBuffer>;
|
||||
using IndexHandle = filament::backend::Handle<filament::backend::HwIndexBuffer>;
|
||||
|
||||
@@ -64,7 +63,6 @@ private:
|
||||
|
||||
PrimitiveHandle mRenderPrimitive;
|
||||
BufferObjectHandle mBufferObject;
|
||||
VertexInfoHandle mVertexBufferInfo;
|
||||
VertexHandle mVertexBuffer;
|
||||
IndexHandle mIndexBuffer;
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
api.beginFrame(0, 0);
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.bindUniformBuffer(0, ubuffer);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ TEST_F(BackendTest, VertexBufferUpdate) {
|
||||
}
|
||||
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
triangleIndex++;
|
||||
|
||||
@@ -218,7 +218,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ TEST_F(BackendTest, MRT) {
|
||||
|
||||
// Draw a triangle.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
getDriverApi().flush();
|
||||
|
||||
@@ -168,7 +168,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.scissor = params.viewport;
|
||||
state.program = textureProgram;
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
@@ -202,8 +203,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
// Because the min level is 1, the result color should be the white triangle drawn in the
|
||||
// previous pass.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.scissor(params.viewport);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Adjust the base mip to 2.
|
||||
@@ -221,8 +221,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.scissor(params.viewport);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
|
||||
@@ -104,7 +104,7 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
|
||||
// Render a triangle.
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
getDriverApi().flush();
|
||||
|
||||
@@ -309,7 +309,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
@@ -438,7 +438,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
|
||||
// Render some content, just so we don't read back uninitialized data.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||
|
||||
@@ -120,7 +120,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
// Render a triangle.
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
getDriverApi().flush();
|
||||
@@ -242,7 +242,7 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
// Render a triangle.
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
getDriverApi().flush();
|
||||
|
||||
@@ -134,13 +134,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
ps.program = program;
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
ps.scissor = scissor;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget,
|
||||
@@ -225,21 +225,20 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
ps.program = program;
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
ps.scissor = scissor;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
params.viewport = topLeftViewport;
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
readPixelsAndAssertHash(
|
||||
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
ps.stencilState.front.ref = 0u;
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
@@ -240,7 +240,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||
@@ -255,7 +255,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
ps.stencilState.front.ref = 0u;
|
||||
|
||||
api.beginRenderPass(renderTarget1, params);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
|
||||
@@ -84,7 +84,8 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this BufferObject with.
|
||||
*
|
||||
* @return pointer to the newly created object
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
@@ -92,7 +93,7 @@ public:
|
||||
*
|
||||
* @see IndexBuffer::setBuffer
|
||||
*/
|
||||
BufferObject* UTILS_NONNULL build(Engine& engine);
|
||||
BufferObject* build(Engine& engine);
|
||||
private:
|
||||
friend class FBufferObject;
|
||||
};
|
||||
|
||||
@@ -306,7 +306,7 @@ public:
|
||||
* @see setCustomProjection
|
||||
* @see Engine::Config::stereoscopicEyeCount
|
||||
*/
|
||||
void setCustomEyeProjection(math::mat4 const* UTILS_NONNULL projection, size_t count,
|
||||
void setCustomEyeProjection(math::mat4 const* projection, size_t count,
|
||||
math::mat4 const& projectionForCulling, double near, double far);
|
||||
|
||||
/** Sets an additional matrix that scales the projection matrix.
|
||||
|
||||
@@ -203,7 +203,7 @@ public:
|
||||
*
|
||||
* @return This Builder, for chaining calls
|
||||
*/
|
||||
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
|
||||
Builder& toneMapper(const ToneMapper* toneMapper) noexcept;
|
||||
|
||||
/**
|
||||
* Selects the tone mapping operator to apply to the HDR color buffer as the last
|
||||
@@ -473,9 +473,10 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this ColorGrading with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*/
|
||||
ColorGrading* UTILS_NONNULL build(Engine& engine);
|
||||
ColorGrading* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FColorGrading;
|
||||
|
||||
@@ -39,12 +39,27 @@ namespace filament {
|
||||
class UTILS_PUBLIC DebugRegistry : public FilamentAPI {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Type of a property
|
||||
*/
|
||||
enum Type {
|
||||
BOOL, INT, FLOAT, FLOAT2, FLOAT3, FLOAT4
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about a property
|
||||
*/
|
||||
struct Property {
|
||||
const char* name; //!< property name
|
||||
Type type; //!< property type
|
||||
};
|
||||
|
||||
/**
|
||||
* Queries whether a property exists
|
||||
* @param name The name of the property to query
|
||||
* @return true if the property exists, false otherwise
|
||||
*/
|
||||
bool hasProperty(const char* UTILS_NONNULL name) const noexcept;
|
||||
bool hasProperty(const char* name) const noexcept;
|
||||
|
||||
/**
|
||||
* Queries the address of a property's data from its name
|
||||
@@ -52,30 +67,28 @@ public:
|
||||
* @return Address of the data of the \p name property
|
||||
* @{
|
||||
*/
|
||||
void* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name);
|
||||
void* getPropertyAddress(const char* name);
|
||||
|
||||
void const* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) const noexcept;
|
||||
void const* getPropertyAddress(const char* name) const noexcept;
|
||||
|
||||
template<typename T>
|
||||
inline T* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) {
|
||||
inline T* getPropertyAddress(const char* name) {
|
||||
return static_cast<T*>(getPropertyAddress(name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T const* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) const noexcept {
|
||||
inline T const* getPropertyAddress(const char* name) const noexcept {
|
||||
return static_cast<T*>(getPropertyAddress(name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool getPropertyAddress(const char* UTILS_NONNULL name,
|
||||
T* UTILS_NULLABLE* UTILS_NONNULL p) {
|
||||
inline bool getPropertyAddress(const char* name, T** p) {
|
||||
*p = getPropertyAddress<T>(name);
|
||||
return *p != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool getPropertyAddress(const char* UTILS_NONNULL name,
|
||||
T* const UTILS_NULLABLE* UTILS_NONNULL p) const noexcept {
|
||||
inline bool getPropertyAddress(const char* name, T* const* p) const noexcept {
|
||||
*p = getPropertyAddress<T>(name);
|
||||
return *p != nullptr;
|
||||
}
|
||||
@@ -88,12 +101,12 @@ public:
|
||||
* @return true if the operation was successful, false otherwise.
|
||||
* @{
|
||||
*/
|
||||
bool setProperty(const char* UTILS_NONNULL name, bool v) noexcept;
|
||||
bool setProperty(const char* UTILS_NONNULL name, int v) noexcept;
|
||||
bool setProperty(const char* UTILS_NONNULL name, float v) noexcept;
|
||||
bool setProperty(const char* UTILS_NONNULL name, math::float2 v) noexcept;
|
||||
bool setProperty(const char* UTILS_NONNULL name, math::float3 v) noexcept;
|
||||
bool setProperty(const char* UTILS_NONNULL name, math::float4 v) noexcept;
|
||||
bool setProperty(const char* name, bool v) noexcept;
|
||||
bool setProperty(const char* name, int v) noexcept;
|
||||
bool setProperty(const char* name, float v) noexcept;
|
||||
bool setProperty(const char* name, math::float2 v) noexcept;
|
||||
bool setProperty(const char* name, math::float3 v) noexcept;
|
||||
bool setProperty(const char* name, math::float4 v) noexcept;
|
||||
/** @}*/
|
||||
|
||||
/**
|
||||
@@ -103,20 +116,20 @@ public:
|
||||
* @return true if the call was successful and \p v was updated
|
||||
* @{
|
||||
*/
|
||||
bool getProperty(const char* UTILS_NONNULL name, bool* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* UTILS_NONNULL name, int* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* UTILS_NONNULL name, float* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* UTILS_NONNULL name, math::float2* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* UTILS_NONNULL name, math::float3* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* UTILS_NONNULL name, math::float4* UTILS_NONNULL v) const noexcept;
|
||||
bool getProperty(const char* name, bool* v) const noexcept;
|
||||
bool getProperty(const char* name, int* v) const noexcept;
|
||||
bool getProperty(const char* name, float* v) const noexcept;
|
||||
bool getProperty(const char* name, math::float2* v) const noexcept;
|
||||
bool getProperty(const char* name, math::float3* v) const noexcept;
|
||||
bool getProperty(const char* name, math::float4* v) const noexcept;
|
||||
/** @}*/
|
||||
|
||||
struct DataSource {
|
||||
void const* UTILS_NULLABLE data;
|
||||
void const* data;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
DataSource getDataSource(const char* UTILS_NONNULL name) const noexcept;
|
||||
DataSource getDataSource(const char* name) const noexcept;
|
||||
|
||||
struct FrameHistory {
|
||||
using duration_ms = float;
|
||||
|
||||
@@ -178,7 +178,6 @@ public:
|
||||
using Backend = backend::Backend;
|
||||
using DriverConfig = backend::Platform::DriverConfig;
|
||||
using FeatureLevel = backend::FeatureLevel;
|
||||
using StereoscopicType = backend::StereoscopicType;
|
||||
|
||||
/**
|
||||
* Config is used to define the memory footprint used by the engine, such as the
|
||||
@@ -298,25 +297,6 @@ public:
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
StereoscopicType stereoscopicType = StereoscopicType::INSTANCED;
|
||||
|
||||
/*
|
||||
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
|
||||
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
|
||||
@@ -325,23 +305,11 @@ public:
|
||||
* @see Engine::getMaxStereoscopicEyes
|
||||
*/
|
||||
uint8_t stereoscopicEyeCount = 2;
|
||||
|
||||
/*
|
||||
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
|
||||
* size of used render targets (typically the screen).
|
||||
*/
|
||||
uint32_t resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
* The default value of 30 corresponds to about half a second at 60 fps.
|
||||
*/
|
||||
uint32_t resourceAllocatorCacheMaxAge = 30;
|
||||
};
|
||||
|
||||
|
||||
#if UTILS_HAS_THREADING
|
||||
using CreateCallback = void(void* UTILS_NULLABLE user, void* UTILS_NONNULL token);
|
||||
using CreateCallback = void(void* user, void* token);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -380,7 +348,7 @@ public:
|
||||
*
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& platform(Platform* UTILS_NULLABLE platform) noexcept;
|
||||
Builder& platform(Platform* platform) noexcept;
|
||||
|
||||
/**
|
||||
* @param config A pointer to optional parameters to specify memory size
|
||||
@@ -388,7 +356,7 @@ public:
|
||||
*
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& config(const Config* UTILS_NULLABLE config) noexcept;
|
||||
Builder& config(const Config* config) noexcept;
|
||||
|
||||
/**
|
||||
* @param sharedContext A platform-dependant context used as a shared context
|
||||
@@ -396,7 +364,7 @@ public:
|
||||
*
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& sharedContext(void* UTILS_NULLABLE sharedContext) noexcept;
|
||||
Builder& sharedContext(void* sharedContext) noexcept;
|
||||
|
||||
/**
|
||||
* @param featureLevel The feature level at which initialize Filament.
|
||||
@@ -411,7 +379,7 @@ public:
|
||||
* @param callback Callback called once the engine is initialized and it is safe to
|
||||
* call Engine::getEngine().
|
||||
*/
|
||||
void build(utils::Invocable<void(void* UTILS_NONNULL token)>&& callback) const;
|
||||
void build(utils::Invocable<void(void* token)>&& callback) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -426,17 +394,16 @@ public:
|
||||
* allocate the command buffer. If exceptions are disabled, this condition if
|
||||
* fatal and this function will abort.
|
||||
*/
|
||||
Engine* UTILS_NULLABLE build() const;
|
||||
Engine* build() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Backward compatibility helper to create an Engine.
|
||||
* @see Builder
|
||||
*/
|
||||
static inline Engine* UTILS_NULLABLE create(Backend backend = Backend::DEFAULT,
|
||||
Platform* UTILS_NULLABLE platform = nullptr,
|
||||
void* UTILS_NULLABLE sharedContext = nullptr,
|
||||
const Config* UTILS_NULLABLE config = nullptr) {
|
||||
static inline Engine* create(Backend backend = Backend::DEFAULT,
|
||||
Platform* platform = nullptr, void* sharedContext = nullptr,
|
||||
const Config* config = nullptr) {
|
||||
return Engine::Builder()
|
||||
.backend(backend)
|
||||
.platform(platform)
|
||||
@@ -451,18 +418,16 @@ public:
|
||||
* Backward compatibility helper to create an Engine asynchronously.
|
||||
* @see Builder
|
||||
*/
|
||||
static inline void createAsync(CreateCallback callback,
|
||||
void* UTILS_NULLABLE user,
|
||||
static inline void createAsync(CreateCallback callback, void* user,
|
||||
Backend backend = Backend::DEFAULT,
|
||||
Platform* UTILS_NULLABLE platform = nullptr,
|
||||
void* UTILS_NULLABLE sharedContext = nullptr,
|
||||
const Config* UTILS_NULLABLE config = nullptr) {
|
||||
Platform* platform = nullptr, void* sharedContext = nullptr,
|
||||
const Config* config = nullptr) {
|
||||
Engine::Builder()
|
||||
.backend(backend)
|
||||
.platform(platform)
|
||||
.sharedContext(sharedContext)
|
||||
.config(config)
|
||||
.build([callback, user](void* UTILS_NONNULL token) {
|
||||
.build([callback, user](void* token) {
|
||||
callback(user, token);
|
||||
});
|
||||
}
|
||||
@@ -479,7 +444,7 @@ public:
|
||||
* allocate the command buffer. If exceptions are disabled, this condition if fatal and
|
||||
* this function will abort.
|
||||
*/
|
||||
static Engine* UTILS_NULLABLE getEngine(void* UTILS_NONNULL token);
|
||||
static Engine* getEngine(void* token);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -509,7 +474,7 @@ public:
|
||||
* \remark
|
||||
* This method is thread-safe.
|
||||
*/
|
||||
static void destroy(Engine* UTILS_NULLABLE* UTILS_NULLABLE engine);
|
||||
static void destroy(Engine** engine);
|
||||
|
||||
/**
|
||||
* Destroy the Engine instance and all associated resources.
|
||||
@@ -536,7 +501,7 @@ public:
|
||||
* \remark
|
||||
* This method is thread-safe.
|
||||
*/
|
||||
static void destroy(Engine* UTILS_NULLABLE engine);
|
||||
static void destroy(Engine* engine);
|
||||
|
||||
/**
|
||||
* Query the feature level supported by the selected backend.
|
||||
@@ -596,12 +561,12 @@ public:
|
||||
size_t getMaxAutomaticInstances() const noexcept;
|
||||
|
||||
/**
|
||||
* Queries the device and platform for support of the given stereoscopic type.
|
||||
* Queries the device and platform for instanced stereo rendering support.
|
||||
*
|
||||
* @return true if the given stereo rendering is supported, false otherwise
|
||||
* @return true if stereo rendering is supported, false otherwise
|
||||
* @see View::setStereoscopicOptions
|
||||
*/
|
||||
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
|
||||
bool isStereoSupported() const noexcept;
|
||||
|
||||
/**
|
||||
* Retrieves the configuration settings of this Engine.
|
||||
@@ -684,11 +649,11 @@ public:
|
||||
* `ANativeWindow*`.
|
||||
* @param flags One or more configuration flags as defined in `SwapChain`.
|
||||
*
|
||||
* @return A pointer to the newly created SwapChain.
|
||||
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
|
||||
*
|
||||
* @see Renderer.beginFrame()
|
||||
*/
|
||||
SwapChain* UTILS_NONNULL createSwapChain(void* UTILS_NULLABLE nativeWindow, uint64_t flags = 0) noexcept;
|
||||
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags = 0) noexcept;
|
||||
|
||||
|
||||
/**
|
||||
@@ -698,42 +663,42 @@ public:
|
||||
* @param height Height of the drawing buffer in pixels.
|
||||
* @param flags One or more configuration flags as defined in `SwapChain`.
|
||||
*
|
||||
* @return A pointer to the newly created SwapChain.
|
||||
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
|
||||
*
|
||||
* @see Renderer.beginFrame()
|
||||
*/
|
||||
SwapChain* UTILS_NONNULL createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
|
||||
|
||||
/**
|
||||
* Creates a renderer associated to this engine.
|
||||
*
|
||||
* A Renderer is intended to map to a *window* on screen.
|
||||
*
|
||||
* @return A pointer to the newly created Renderer.
|
||||
* @return A pointer to the newly created Renderer or nullptr if it couldn't be created.
|
||||
*/
|
||||
Renderer* UTILS_NONNULL createRenderer() noexcept;
|
||||
Renderer* createRenderer() noexcept;
|
||||
|
||||
/**
|
||||
* Creates a View.
|
||||
*
|
||||
* @return A pointer to the newly created View.
|
||||
* @return A pointer to the newly created View or nullptr if it couldn't be created.
|
||||
*/
|
||||
View* UTILS_NONNULL createView() noexcept;
|
||||
View* createView() noexcept;
|
||||
|
||||
/**
|
||||
* Creates a Scene.
|
||||
*
|
||||
* @return A pointer to the newly created Scene.
|
||||
* @return A pointer to the newly created Scene or nullptr if it couldn't be created.
|
||||
*/
|
||||
Scene* UTILS_NONNULL createScene() noexcept;
|
||||
Scene* createScene() noexcept;
|
||||
|
||||
/**
|
||||
* Creates a Camera component.
|
||||
*
|
||||
* @param entity Entity to add the camera component to.
|
||||
* @return A pointer to the newly created Camera.
|
||||
* @return A pointer to the newly created Camera or nullptr if it couldn't be created.
|
||||
*/
|
||||
Camera* UTILS_NONNULL createCamera(utils::Entity entity) noexcept;
|
||||
Camera* createCamera(utils::Entity entity) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the Camera component of the given entity.
|
||||
@@ -743,7 +708,7 @@ public:
|
||||
* have a Camera component. The pointer is valid until destroyCameraComponent()
|
||||
* is called or the entity itself is destroyed.
|
||||
*/
|
||||
Camera* UTILS_NULLABLE getCameraComponent(utils::Entity entity) noexcept;
|
||||
Camera* getCameraComponent(utils::Entity entity) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys the Camera component associated with the given entity.
|
||||
@@ -755,17 +720,17 @@ public:
|
||||
/**
|
||||
* Creates a Fence.
|
||||
*
|
||||
* @return A pointer to the newly created Fence.
|
||||
* @return A pointer to the newly created Fence or nullptr if it couldn't be created.
|
||||
*/
|
||||
Fence* UTILS_NONNULL createFence() noexcept;
|
||||
Fence* createFence() noexcept;
|
||||
|
||||
bool destroy(const BufferObject* UTILS_NULLABLE p); //!< Destroys a BufferObject object.
|
||||
bool destroy(const VertexBuffer* UTILS_NULLABLE p); //!< Destroys an VertexBuffer object.
|
||||
bool destroy(const Fence* UTILS_NULLABLE p); //!< Destroys a Fence object.
|
||||
bool destroy(const IndexBuffer* UTILS_NULLABLE p); //!< Destroys an IndexBuffer object.
|
||||
bool destroy(const SkinningBuffer* UTILS_NULLABLE p); //!< Destroys a SkinningBuffer object.
|
||||
bool destroy(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Destroys a MorphTargetBuffer object.
|
||||
bool destroy(const IndirectLight* UTILS_NULLABLE p); //!< Destroys an IndirectLight object.
|
||||
bool destroy(const BufferObject* p); //!< Destroys a BufferObject object.
|
||||
bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object.
|
||||
bool destroy(const Fence* p); //!< Destroys a Fence object.
|
||||
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
|
||||
bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object.
|
||||
bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object.
|
||||
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
|
||||
|
||||
/**
|
||||
* Destroys a Material object
|
||||
@@ -775,38 +740,38 @@ public:
|
||||
* @exception utils::PreConditionPanic is thrown if some MaterialInstances remain.
|
||||
* no-op if exceptions are disabled and some MaterialInstances remain.
|
||||
*/
|
||||
bool destroy(const Material* UTILS_NULLABLE p);
|
||||
bool destroy(const MaterialInstance* UTILS_NULLABLE p); //!< Destroys a MaterialInstance object.
|
||||
bool destroy(const Renderer* UTILS_NULLABLE p); //!< Destroys a Renderer object.
|
||||
bool destroy(const Scene* UTILS_NULLABLE p); //!< Destroys a Scene object.
|
||||
bool destroy(const Skybox* UTILS_NULLABLE p); //!< Destroys a SkyBox object.
|
||||
bool destroy(const ColorGrading* UTILS_NULLABLE p); //!< Destroys a ColorGrading object.
|
||||
bool destroy(const SwapChain* UTILS_NULLABLE p); //!< Destroys a SwapChain object.
|
||||
bool destroy(const Stream* UTILS_NULLABLE p); //!< Destroys a Stream object.
|
||||
bool destroy(const Texture* UTILS_NULLABLE p); //!< Destroys a Texture object.
|
||||
bool destroy(const RenderTarget* UTILS_NULLABLE p); //!< Destroys a RenderTarget object.
|
||||
bool destroy(const View* UTILS_NULLABLE p); //!< Destroys a View object.
|
||||
bool destroy(const InstanceBuffer* UTILS_NULLABLE p); //!< Destroys an InstanceBuffer object.
|
||||
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
|
||||
bool destroy(const Material* p);
|
||||
bool destroy(const MaterialInstance* p); //!< Destroys a MaterialInstance object.
|
||||
bool destroy(const Renderer* p); //!< Destroys a Renderer object.
|
||||
bool destroy(const Scene* p); //!< Destroys a Scene object.
|
||||
bool destroy(const Skybox* p); //!< Destroys a SkyBox object.
|
||||
bool destroy(const ColorGrading* p); //!< Destroys a ColorGrading object.
|
||||
bool destroy(const SwapChain* p); //!< Destroys a SwapChain object.
|
||||
bool destroy(const Stream* p); //!< Destroys a Stream object.
|
||||
bool destroy(const Texture* p); //!< Destroys a Texture object.
|
||||
bool destroy(const RenderTarget* p); //!< Destroys a RenderTarget object.
|
||||
bool destroy(const View* p); //!< Destroys a View object.
|
||||
bool destroy(const InstanceBuffer* p); //!< Destroys an InstanceBuffer object.
|
||||
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
|
||||
|
||||
bool isValid(const BufferObject* UTILS_NULLABLE p); //!< Tells whether a BufferObject object is valid
|
||||
bool isValid(const VertexBuffer* UTILS_NULLABLE p); //!< Tells whether an VertexBuffer object is valid
|
||||
bool isValid(const Fence* UTILS_NULLABLE p); //!< Tells whether a Fence object is valid
|
||||
bool isValid(const IndexBuffer* UTILS_NULLABLE p); //!< Tells whether an IndexBuffer object is valid
|
||||
bool isValid(const SkinningBuffer* UTILS_NULLABLE p); //!< Tells whether a SkinningBuffer object is valid
|
||||
bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Tells whether a MorphTargetBuffer object is valid
|
||||
bool isValid(const IndirectLight* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid
|
||||
bool isValid(const Material* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid
|
||||
bool isValid(const Renderer* UTILS_NULLABLE p); //!< Tells whether a Renderer object is valid
|
||||
bool isValid(const Scene* UTILS_NULLABLE p); //!< Tells whether a Scene object is valid
|
||||
bool isValid(const Skybox* UTILS_NULLABLE p); //!< Tells whether a SkyBox object is valid
|
||||
bool isValid(const ColorGrading* UTILS_NULLABLE p); //!< Tells whether a ColorGrading object is valid
|
||||
bool isValid(const SwapChain* UTILS_NULLABLE p); //!< Tells whether a SwapChain object is valid
|
||||
bool isValid(const Stream* UTILS_NULLABLE p); //!< Tells whether a Stream object is valid
|
||||
bool isValid(const Texture* UTILS_NULLABLE p); //!< Tells whether a Texture object is valid
|
||||
bool isValid(const RenderTarget* UTILS_NULLABLE p); //!< Tells whether a RenderTarget object is valid
|
||||
bool isValid(const View* UTILS_NULLABLE p); //!< Tells whether a View object is valid
|
||||
bool isValid(const InstanceBuffer* UTILS_NULLABLE p); //!< Tells whether an InstanceBuffer object is valid
|
||||
bool isValid(const BufferObject* p); //!< Tells whether a BufferObject object is valid
|
||||
bool isValid(const VertexBuffer* p); //!< Tells whether an VertexBuffer object is valid
|
||||
bool isValid(const Fence* p); //!< Tells whether a Fence object is valid
|
||||
bool isValid(const IndexBuffer* p); //!< Tells whether an IndexBuffer object is valid
|
||||
bool isValid(const SkinningBuffer* p); //!< Tells whether a SkinningBuffer object is valid
|
||||
bool isValid(const MorphTargetBuffer* p); //!< Tells whether a MorphTargetBuffer object is valid
|
||||
bool isValid(const IndirectLight* p); //!< Tells whether an IndirectLight object is valid
|
||||
bool isValid(const Material* p); //!< Tells whether an IndirectLight object is valid
|
||||
bool isValid(const Renderer* p); //!< Tells whether a Renderer object is valid
|
||||
bool isValid(const Scene* p); //!< Tells whether a Scene object is valid
|
||||
bool isValid(const Skybox* p); //!< Tells whether a SkyBox object is valid
|
||||
bool isValid(const ColorGrading* p); //!< Tells whether a ColorGrading object is valid
|
||||
bool isValid(const SwapChain* p); //!< Tells whether a SwapChain object is valid
|
||||
bool isValid(const Stream* p); //!< Tells whether a Stream object is valid
|
||||
bool isValid(const Texture* p); //!< Tells whether a Texture object is valid
|
||||
bool isValid(const RenderTarget* p); //!< Tells whether a RenderTarget object is valid
|
||||
bool isValid(const View* p); //!< Tells whether a View object is valid
|
||||
bool isValid(const InstanceBuffer* p); //!< Tells whether an InstanceBuffer object is valid
|
||||
|
||||
/**
|
||||
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
|
||||
@@ -846,7 +811,7 @@ public:
|
||||
*
|
||||
* @return A pointer to the default Material instance (a singleton).
|
||||
*/
|
||||
Material const* UTILS_NONNULL getDefaultMaterial() const noexcept;
|
||||
const Material* getDefaultMaterial() const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the resolved backend.
|
||||
@@ -877,7 +842,7 @@ public:
|
||||
* @return A pointer to the Platform object that was provided to Engine::create, or the
|
||||
* Filament-created one.
|
||||
*/
|
||||
Platform* UTILS_NULLABLE getPlatform() const noexcept;
|
||||
Platform* getPlatform() const noexcept;
|
||||
|
||||
/**
|
||||
* Allocate a small amount of memory directly in the command stream. The allocated memory is
|
||||
@@ -890,7 +855,7 @@ public:
|
||||
* @note there is no need to destroy this buffer, it will be freed automatically when
|
||||
* the current command buffer is executed.
|
||||
*/
|
||||
void* UTILS_NULLABLE streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept;
|
||||
void* streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept;
|
||||
|
||||
/**
|
||||
* Invokes one iteration of the render loop, used only on single-threaded platforms.
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
* @return FenceStatus::CONDITION_SATISFIED on success,
|
||||
* FenceStatus::ERROR otherwise.
|
||||
*/
|
||||
static FenceStatus waitAndDestroy(Fence* UTILS_NONNULL fence, Mode mode = Mode::FLUSH);
|
||||
static FenceStatus waitAndDestroy(Fence* fence, Mode mode = Mode::FLUSH);
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
|
||||
@@ -79,14 +79,14 @@ public:
|
||||
* @param planes six plane equations encoded as in getNormalizedPlane() in
|
||||
* left, right, bottom, top, far, near order
|
||||
*/
|
||||
void getNormalizedPlanes(math::float4 planes[UTILS_NONNULL 6]) const noexcept;
|
||||
void getNormalizedPlanes(math::float4 planes[6]) const noexcept;
|
||||
|
||||
/**
|
||||
* Returns all six frustum planes in left, right, bottom, top, far, near order
|
||||
* @return six plane equations encoded as in getNormalizedPlane() in
|
||||
* left, right, bottom, top, far, near order
|
||||
*/
|
||||
math::float4 const* UTILS_NONNULL getNormalizedPlanes() const noexcept { return mPlanes; }
|
||||
math::float4 const* getNormalizedPlanes() const noexcept { return mPlanes; }
|
||||
|
||||
/**
|
||||
* Returns whether a box intersects the frustum (i.e. is visible)
|
||||
|
||||
@@ -89,7 +89,8 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
@@ -97,7 +98,7 @@ public:
|
||||
*
|
||||
* @see IndexBuffer::setBuffer
|
||||
*/
|
||||
IndexBuffer* UTILS_NONNULL build(Engine& engine);
|
||||
IndexBuffer* build(Engine& engine);
|
||||
private:
|
||||
friend class FIndexBuffer;
|
||||
};
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
* @return This Builder, for chaining calls.
|
||||
*
|
||||
*/
|
||||
Builder& reflections(Texture const* UTILS_NULLABLE cubemap) noexcept;
|
||||
Builder& reflections(Texture const* cubemap) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the irradiance as Spherical Harmonics.
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
* Because the coefficients are pre-scaled, `sh[0]` is the environment's
|
||||
* average irradiance.
|
||||
*/
|
||||
Builder& irradiance(uint8_t bands, math::float3 const* UTILS_NONNULL sh) noexcept;
|
||||
Builder& irradiance(uint8_t bands, math::float3 const* sh) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the irradiance from the radiance expressed as Spherical Harmonics.
|
||||
@@ -194,7 +194,7 @@ public:
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& radiance(uint8_t bands, math::float3 const* UTILS_NONNULL sh) noexcept;
|
||||
Builder& radiance(uint8_t bands, math::float3 const* sh) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the irradiance as a cubemap.
|
||||
@@ -213,7 +213,7 @@ public:
|
||||
*
|
||||
* @see irradiance(uint8_t bands, math::float3 const* sh)
|
||||
*/
|
||||
Builder& irradiance(Texture const* UTILS_NULLABLE cubemap) noexcept;
|
||||
Builder& irradiance(Texture const* cubemap) noexcept;
|
||||
|
||||
/**
|
||||
* (optional) Environment intensity.
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
* memory or other resources.
|
||||
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
|
||||
*/
|
||||
IndirectLight* UTILS_NONNULL build(Engine& engine);
|
||||
IndirectLight* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FIndirectLight;
|
||||
@@ -286,12 +286,12 @@ public:
|
||||
/**
|
||||
* Returns the associated reflection map, or null if it does not exist.
|
||||
*/
|
||||
Texture const* UTILS_NULLABLE getReflectionsTexture() const noexcept;
|
||||
Texture const* getReflectionsTexture() const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the associated irradiance map, or null if it does not exist.
|
||||
*/
|
||||
Texture const* UTILS_NULLABLE getIrradianceTexture() const noexcept;
|
||||
Texture const* getIrradianceTexture() const noexcept;
|
||||
|
||||
/**
|
||||
* Helper to estimate the direction of the dominant light in the environment represented by
|
||||
@@ -314,7 +314,7 @@ public:
|
||||
* @see LightManager::Builder::direction()
|
||||
* @see getColorEstimate()
|
||||
*/
|
||||
static math::float3 getDirectionEstimate(const math::float3 sh[UTILS_NONNULL 9]) noexcept;
|
||||
static math::float3 getDirectionEstimate(const math::float3 sh[9]) noexcept;
|
||||
|
||||
/**
|
||||
* Helper to estimate the color and relative intensity of the environment represented by
|
||||
@@ -334,8 +334,7 @@ public:
|
||||
* @see LightManager::Builder::intensity()
|
||||
* @see getDirectionEstimate, getIntensity, setIntensity
|
||||
*/
|
||||
static math::float4 getColorEstimate(const math::float3 sh[UTILS_NONNULL 9],
|
||||
math::float3 direction) noexcept;
|
||||
static math::float4 getColorEstimate(const math::float3 sh[9], math::float3 direction) noexcept;
|
||||
|
||||
|
||||
/** @deprecated use static versions instead */
|
||||
|
||||
@@ -68,12 +68,12 @@ public:
|
||||
* @param localTransforms an array of math::mat4f with length instanceCount, must remain
|
||||
* valid until after build() is called
|
||||
*/
|
||||
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
|
||||
Builder& localTransforms(math::mat4f const* localTransforms) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the InstanceBuffer object and returns a pointer to it.
|
||||
*/
|
||||
InstanceBuffer* UTILS_NONNULL build(Engine& engine);
|
||||
InstanceBuffer* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FInstanceBuffer;
|
||||
@@ -93,8 +93,7 @@ public:
|
||||
* @param count the number of local transforms
|
||||
* @param offset index of the first instance to set local transforms
|
||||
*/
|
||||
void setLocalTransforms(math::mat4f const* UTILS_NONNULL localTransforms,
|
||||
size_t count, size_t offset = 0);
|
||||
void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset = 0);
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
* Retrieve the Entities of all the components of this manager.
|
||||
* @return A list, in no particular order, of all the entities managed by this manager.
|
||||
*/
|
||||
utils::Entity const* UTILS_NONNULL getEntities() const noexcept;
|
||||
utils::Entity const* getEntities() const noexcept;
|
||||
|
||||
/**
|
||||
* Gets an Instance representing the Light component associated with the given Entity.
|
||||
@@ -394,7 +394,7 @@ public:
|
||||
* positions into
|
||||
* @param cascades the number of shadow cascades, at most 4
|
||||
*/
|
||||
static void computeUniformSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades);
|
||||
static void computeUniformSplits(float* splitPositions, uint8_t cascades);
|
||||
|
||||
/**
|
||||
* Utility method to compute ShadowOptions::cascadeSplitPositions according to a logarithmic
|
||||
@@ -406,7 +406,7 @@ public:
|
||||
* @param near the camera near plane
|
||||
* @param far the camera far plane
|
||||
*/
|
||||
static void computeLogSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades,
|
||||
static void computeLogSplits(float* splitPositions, uint8_t cascades,
|
||||
float near, float far);
|
||||
|
||||
/**
|
||||
@@ -426,7 +426,7 @@ public:
|
||||
* @param lambda a float in the range [0, 1] that interpolates between log and
|
||||
* uniform split schemes
|
||||
*/
|
||||
static void computePracticalSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades,
|
||||
static void computePracticalSplits(float* splitPositions, uint8_t cascades,
|
||||
float near, float far, float lambda);
|
||||
};
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
*/
|
||||
struct ParameterInfo {
|
||||
//! Name of the parameter.
|
||||
const char* UTILS_NONNULL name;
|
||||
const char* name;
|
||||
//! Whether the parameter is a sampler (texture).
|
||||
bool isSampler;
|
||||
//! Whether the parameter is a subpass type.
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
* @param payload Pointer to the material data, must stay valid until build() is called.
|
||||
* @param size Size of the material data pointed to by "payload" in bytes.
|
||||
*/
|
||||
Builder& package(const void* UTILS_NONNULL payload, size_t size);
|
||||
Builder& package(const void* payload, size_t size);
|
||||
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = typename std::enable_if<
|
||||
@@ -132,11 +132,11 @@ public:
|
||||
* in the material definition.
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
Builder& constant(const char* UTILS_NONNULL name, size_t nameLength, T value);
|
||||
Builder& constant(const char* name, size_t nameLength, T value);
|
||||
|
||||
/** inline helper to provide the constant name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
inline Builder& constant(const char* UTILS_NONNULL name, T value) {
|
||||
inline Builder& constant(const char* name, T value) {
|
||||
return constant(name, strlen(name), value);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
* memory or other resources.
|
||||
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
|
||||
*/
|
||||
Material* UTILS_NULLABLE build(Engine& engine);
|
||||
Material* build(Engine& engine);
|
||||
private:
|
||||
friend class FMaterial;
|
||||
};
|
||||
@@ -198,22 +198,22 @@ public:
|
||||
*/
|
||||
void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterMask variants,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept;
|
||||
backend::CallbackHandler* handler = nullptr,
|
||||
utils::Invocable<void(Material*)>&& callback = {}) noexcept;
|
||||
|
||||
inline void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterBit variants,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept {
|
||||
backend::CallbackHandler* handler = nullptr,
|
||||
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
|
||||
compile(priority, UserVariantFilterMask(variants), handler,
|
||||
std::forward<utils::Invocable<void(Material* UTILS_NONNULL)>>(callback));
|
||||
std::forward<utils::Invocable<void(Material*)>>(callback));
|
||||
}
|
||||
|
||||
inline void compile(CompilerPriorityQueue priority,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept {
|
||||
backend::CallbackHandler* handler = nullptr,
|
||||
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
|
||||
compile(priority, UserVariantFilterBit::ALL, handler,
|
||||
std::forward<utils::Invocable<void(Material* UTILS_NONNULL)>>(callback));
|
||||
std::forward<utils::Invocable<void(Material*)>>(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,13 +225,13 @@ public:
|
||||
*
|
||||
* @return A pointer to the new instance.
|
||||
*/
|
||||
MaterialInstance* UTILS_NONNULL createInstance(const char* UTILS_NULLABLE name = nullptr) const noexcept;
|
||||
MaterialInstance* createInstance(const char* name = nullptr) const noexcept;
|
||||
|
||||
//! Returns the name of this material as a null-terminated string.
|
||||
const char* UTILS_NONNULL getName() const noexcept;
|
||||
const char* getName() const noexcept;
|
||||
|
||||
//! Returns the shading model of this material.
|
||||
Shading getShading() const noexcept;
|
||||
Shading getShading() const noexcept;
|
||||
|
||||
//! Returns the interpolation mode of this material. This affects how variables are interpolated.
|
||||
Interpolation getInterpolation() const noexcept;
|
||||
@@ -317,13 +317,13 @@ public:
|
||||
*
|
||||
* @return The number of parameters written to the parameters pointer.
|
||||
*/
|
||||
size_t getParameters(ParameterInfo* UTILS_NONNULL parameters, size_t count) const noexcept;
|
||||
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
|
||||
|
||||
//! Indicates whether a parameter of the given name exists on this material.
|
||||
bool hasParameter(const char* UTILS_NONNULL name) const noexcept;
|
||||
bool hasParameter(const char* name) const noexcept;
|
||||
|
||||
//! Indicates whether an existing parameter is a sampler or not.
|
||||
bool isSampler(const char* UTILS_NONNULL name) const noexcept;
|
||||
bool isSampler(const char* name) const noexcept;
|
||||
|
||||
/**
|
||||
* Sets the value of the given parameter on this material's default instance.
|
||||
@@ -334,7 +334,7 @@ public:
|
||||
* @see getDefaultInstance()
|
||||
*/
|
||||
template <typename T>
|
||||
void setDefaultParameter(const char* UTILS_NONNULL name, T value) noexcept {
|
||||
void setDefaultParameter(const char* name, T value) noexcept {
|
||||
getDefaultInstance()->setParameter(name, value);
|
||||
}
|
||||
|
||||
@@ -347,8 +347,8 @@ public:
|
||||
*
|
||||
* @see getDefaultInstance()
|
||||
*/
|
||||
void setDefaultParameter(const char* UTILS_NONNULL name,
|
||||
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) noexcept {
|
||||
void setDefaultParameter(const char* name, Texture const* texture,
|
||||
TextureSampler const& sampler) noexcept {
|
||||
getDefaultInstance()->setParameter(name, texture, sampler);
|
||||
}
|
||||
|
||||
@@ -361,7 +361,7 @@ public:
|
||||
*
|
||||
* @see getDefaultInstance()
|
||||
*/
|
||||
void setDefaultParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) noexcept {
|
||||
void setDefaultParameter(const char* name, RgbType type, math::float3 color) noexcept {
|
||||
getDefaultInstance()->setParameter(name, type, color);
|
||||
}
|
||||
|
||||
@@ -374,15 +374,15 @@ public:
|
||||
*
|
||||
* @see getDefaultInstance()
|
||||
*/
|
||||
void setDefaultParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) noexcept {
|
||||
void setDefaultParameter(const char* name, RgbaType type, math::float4 color) noexcept {
|
||||
getDefaultInstance()->setParameter(name, type, color);
|
||||
}
|
||||
|
||||
//! Returns this material's default instance.
|
||||
MaterialInstance* UTILS_NONNULL getDefaultInstance() noexcept;
|
||||
MaterialInstance* getDefaultInstance() noexcept;
|
||||
|
||||
//! Returns this material's default instance.
|
||||
MaterialInstance const* UTILS_NONNULL getDefaultInstance() const noexcept;
|
||||
MaterialInstance const* getDefaultInstance() const noexcept;
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
|
||||
@@ -47,7 +47,7 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
|
||||
using StringLiteralHelper = const char[N];
|
||||
|
||||
struct StringLiteral {
|
||||
const char* UTILS_NONNULL data;
|
||||
const char* data;
|
||||
size_t size;
|
||||
template<size_t N>
|
||||
StringLiteral(StringLiteralHelper<N> const& s) noexcept // NOLINT(google-explicit-constructor)
|
||||
@@ -95,18 +95,17 @@ public:
|
||||
* @param name A name for the new MaterialInstance or nullptr to use the template's name
|
||||
* @return A new MaterialInstance
|
||||
*/
|
||||
static MaterialInstance* UTILS_NONNULL duplicate(MaterialInstance const* UTILS_NONNULL other,
|
||||
const char* UTILS_NULLABLE name = nullptr) noexcept;
|
||||
static MaterialInstance* duplicate(MaterialInstance const* other, const char* name = nullptr) noexcept;
|
||||
|
||||
/**
|
||||
* @return the Material associated with this instance
|
||||
*/
|
||||
Material const* UTILS_NONNULL getMaterial() const noexcept;
|
||||
Material const* getMaterial() const noexcept;
|
||||
|
||||
/**
|
||||
* @return the name associated with this instance
|
||||
*/
|
||||
const char* UTILS_NONNULL getName() const noexcept;
|
||||
const char* getName() const noexcept;
|
||||
|
||||
/**
|
||||
* Set a uniform by name
|
||||
@@ -117,7 +116,7 @@ public:
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
void setParameter(const char* UTILS_NONNULL name, size_t nameLength, T const& value);
|
||||
void setParameter(const char* name, size_t nameLength, T const& value);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
@@ -127,7 +126,7 @@ public:
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
inline void setParameter(const char* UTILS_NONNULL name, T const& value) {
|
||||
inline void setParameter(const char* name, T const& value) {
|
||||
setParameter<T>(name, strlen(name), value);
|
||||
}
|
||||
|
||||
@@ -142,19 +141,17 @@ public:
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
|
||||
const T* UTILS_NONNULL values, size_t count);
|
||||
void setParameter(const char* name, size_t nameLength, const T* values, size_t count);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
inline void setParameter(StringLiteral name, const T* UTILS_NONNULL values, size_t count) {
|
||||
inline void setParameter(StringLiteral name, const T* values, size_t count) {
|
||||
setParameter<T>(name.data, name.size, values, count);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_parameter_t<T>>
|
||||
inline void setParameter(const char* UTILS_NONNULL name,
|
||||
const T* UTILS_NONNULL values, size_t count) {
|
||||
inline void setParameter(const char* name, const T* values, size_t count) {
|
||||
setParameter<T>(name, strlen(name), values, count);
|
||||
}
|
||||
|
||||
@@ -171,18 +168,18 @@ public:
|
||||
* @param sampler Sampler parameters.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
|
||||
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler);
|
||||
void setParameter(const char* name, size_t nameLength,
|
||||
Texture const* texture, TextureSampler const& sampler);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
inline void setParameter(StringLiteral name,
|
||||
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) {
|
||||
Texture const* texture, TextureSampler const& sampler) {
|
||||
setParameter(name.data, name.size, texture, sampler);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
inline void setParameter(const char* UTILS_NONNULL name,
|
||||
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) {
|
||||
inline void setParameter(const char* name,
|
||||
Texture const* texture, TextureSampler const& sampler) {
|
||||
setParameter(name, strlen(name), texture, sampler);
|
||||
}
|
||||
|
||||
@@ -197,8 +194,7 @@ public:
|
||||
* @param color Array of read, green, blue channels values.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
|
||||
RgbType type, math::float3 color);
|
||||
void setParameter(const char* name, size_t nameLength, RgbType type, math::float3 color);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
inline void setParameter(StringLiteral name, RgbType type, math::float3 color) {
|
||||
@@ -206,7 +202,7 @@ public:
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
inline void setParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) {
|
||||
inline void setParameter(const char* name, RgbType type, math::float3 color) {
|
||||
setParameter(name, strlen(name), type, color);
|
||||
}
|
||||
|
||||
@@ -221,8 +217,7 @@ public:
|
||||
* @param color Array of read, green, blue and alpha channels values.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
|
||||
RgbaType type, math::float4 color);
|
||||
void setParameter(const char* name, size_t nameLength, RgbaType type, math::float4 color);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
inline void setParameter(StringLiteral name, RgbaType type, math::float4 color) {
|
||||
@@ -230,7 +225,7 @@ public:
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
inline void setParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) {
|
||||
inline void setParameter(const char* name, RgbaType type, math::float4 color) {
|
||||
setParameter(name, strlen(name), type, color);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,13 +68,14 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this MorphTargetBuffer with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
|
||||
*/
|
||||
MorphTargetBuffer* UTILS_NONNULL build(Engine& engine);
|
||||
MorphTargetBuffer* build(Engine& engine);
|
||||
private:
|
||||
friend class FMorphTargetBuffer;
|
||||
};
|
||||
@@ -94,7 +95,7 @@ public:
|
||||
* @see setTangentsAt
|
||||
*/
|
||||
void setPositionsAt(Engine& engine, size_t targetIndex,
|
||||
math::float3 const* UTILS_NONNULL positions, size_t count, size_t offset = 0);
|
||||
math::float3 const* positions, size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* Updates positions for the given morph target.
|
||||
@@ -109,7 +110,7 @@ public:
|
||||
* @see setTangentsAt
|
||||
*/
|
||||
void setPositionsAt(Engine& engine, size_t targetIndex,
|
||||
math::float4 const* UTILS_NONNULL positions, size_t count, size_t offset = 0);
|
||||
math::float4 const* positions, size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* Updates tangents for the given morph target.
|
||||
@@ -125,7 +126,7 @@ public:
|
||||
* @see setPositionsAt
|
||||
*/
|
||||
void setTangentsAt(Engine& engine, size_t targetIndex,
|
||||
math::short4 const* UTILS_NONNULL tangents, size_t count, size_t offset = 0);
|
||||
math::short4 const* tangents, size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* Returns the vertex count of this MorphTargetBuffer.
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
* @param texture The associated texture object.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& texture(AttachmentPoint attachment, Texture* UTILS_NULLABLE texture) noexcept;
|
||||
Builder& texture(AttachmentPoint attachment, Texture* texture) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the mipmap level for a given attachment point.
|
||||
@@ -138,9 +138,10 @@ public:
|
||||
/**
|
||||
* Creates the RenderTarget object and returns a pointer to it.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*/
|
||||
RenderTarget* UTILS_NONNULL build(Engine& engine);
|
||||
RenderTarget* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FRenderTarget;
|
||||
@@ -151,7 +152,7 @@ public:
|
||||
* @param attachment Attachment point
|
||||
* @return A Texture object or nullptr if no texture is set for this attachment point
|
||||
*/
|
||||
Texture* UTILS_NULLABLE getTexture(AttachmentPoint attachment) const noexcept;
|
||||
Texture* getTexture(AttachmentPoint attachment) const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the mipmap level set on the given attachment point
|
||||
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
* Retrieve the Entities of all the components of this manager.
|
||||
* @return A list, in no particular order, of all the entities managed by this manager.
|
||||
*/
|
||||
utils::Entity const* UTILS_NONNULL getEntities() const noexcept;
|
||||
utils::Entity const* getEntities() const noexcept;
|
||||
|
||||
/**
|
||||
* The transformation associated with a skinning joint.
|
||||
@@ -190,19 +190,9 @@ public:
|
||||
* @param maxIndex specifies the maximum index contained in the index buffer
|
||||
* @param count number of indices to read (for triangles, this should be a multiple of 3)
|
||||
*/
|
||||
Builder& geometry(size_t index, PrimitiveType type,
|
||||
VertexBuffer* UTILS_NONNULL vertices,
|
||||
IndexBuffer* UTILS_NONNULL indices,
|
||||
size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
|
||||
|
||||
Builder& geometry(size_t index, PrimitiveType type,
|
||||
VertexBuffer* UTILS_NONNULL vertices,
|
||||
IndexBuffer* UTILS_NONNULL indices,
|
||||
size_t offset, size_t count) noexcept; //!< \overload
|
||||
|
||||
Builder& geometry(size_t index, PrimitiveType type,
|
||||
VertexBuffer* UTILS_NONNULL vertices,
|
||||
IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload
|
||||
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
|
||||
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t count) noexcept; //!< \overload
|
||||
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices) noexcept; //!< \overload
|
||||
|
||||
/**
|
||||
* Binds a material instance to the specified primitive.
|
||||
@@ -219,8 +209,7 @@ public:
|
||||
*
|
||||
* @see Engine::setActiveFeatureLevel
|
||||
*/
|
||||
Builder& material(size_t index,
|
||||
MaterialInstance const* UTILS_NONNULL materialInstance) noexcept;
|
||||
Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept;
|
||||
|
||||
/**
|
||||
* The axis-aligned bounding box of the renderable.
|
||||
@@ -368,8 +357,7 @@ public:
|
||||
* @param count 0 to disable, otherwise the number of bone transforms (up to 255)
|
||||
* @param offset offset in the SkinningBuffer
|
||||
*/
|
||||
Builder& skinning(SkinningBuffer* UTILS_NONNULL skinningBuffer,
|
||||
size_t count, size_t offset) noexcept;
|
||||
Builder& skinning(SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept;
|
||||
|
||||
|
||||
/**
|
||||
@@ -387,8 +375,8 @@ public:
|
||||
* @param boneCount 0 to disable, otherwise the number of bone transforms (up to 255)
|
||||
* @param transforms the initial set of transforms (one for each bone)
|
||||
*/
|
||||
Builder& skinning(size_t boneCount, math::mat4f const* UTILS_NONNULL transforms) noexcept;
|
||||
Builder& skinning(size_t boneCount, Bone const* UTILS_NONNULL bones) noexcept; //!< \overload
|
||||
Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept;
|
||||
Builder& skinning(size_t boneCount, Bone const* bones) noexcept; //!< \overload
|
||||
Builder& skinning(size_t boneCount) noexcept; //!< \overload
|
||||
|
||||
/**
|
||||
@@ -417,8 +405,7 @@ public:
|
||||
* @see VertexBuffer:Builder:advancedSkinning
|
||||
*/
|
||||
Builder& boneIndicesAndWeights(size_t primitiveIndex,
|
||||
math::float2 const* UTILS_NONNULL indicesAndWeights,
|
||||
size_t count, size_t bonesPerVertex) noexcept;
|
||||
math::float2 const* indicesAndWeights, size_t count, size_t bonesPerVertex) noexcept;
|
||||
|
||||
/**
|
||||
* Define bone indices and weights "pairs" for vertex skinning as a float2.
|
||||
@@ -481,11 +468,10 @@ public:
|
||||
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
|
||||
*/
|
||||
Builder& morphing(uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
|
||||
size_t offset, size_t count) noexcept;
|
||||
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept;
|
||||
|
||||
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
|
||||
MorphTargetBuffer* morphTargetBuffer) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the drawing order for blended primitives. The drawing order is either global or
|
||||
@@ -552,8 +538,7 @@ public:
|
||||
* the result of Engine::getMaxAutomaticInstances().
|
||||
* @param instanceBuffer an InstanceBuffer containing at least instanceCount transforms
|
||||
*/
|
||||
Builder& instances(size_t instanceCount,
|
||||
InstanceBuffer* UTILS_NONNULL instanceBuffer) noexcept;
|
||||
Builder& instances(size_t instanceCount, InstanceBuffer* instanceBuffer) noexcept;
|
||||
|
||||
/**
|
||||
* Adds the Renderable component to an entity.
|
||||
@@ -580,16 +565,18 @@ public:
|
||||
friend class FRenderPrimitive;
|
||||
friend class FRenderableManager;
|
||||
struct Entry {
|
||||
VertexBuffer* UTILS_NULLABLE vertices = nullptr;
|
||||
IndexBuffer* UTILS_NULLABLE indices = nullptr;
|
||||
VertexBuffer* vertices = nullptr;
|
||||
IndexBuffer* indices = nullptr;
|
||||
size_t offset = 0;
|
||||
size_t minIndex = 0;
|
||||
size_t maxIndex = 0;
|
||||
size_t count = 0;
|
||||
MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr;
|
||||
MaterialInstance const* materialInstance = nullptr;
|
||||
PrimitiveType type = PrimitiveType::TRIANGLES;
|
||||
uint16_t blendOrder = 0;
|
||||
bool globalBlendOrderEnabled = false;
|
||||
struct {
|
||||
MorphTargetBuffer* UTILS_NULLABLE buffer = nullptr;
|
||||
MorphTargetBuffer* buffer = nullptr;
|
||||
size_t offset = 0;
|
||||
size_t count = 0;
|
||||
} morphing;
|
||||
@@ -707,11 +694,8 @@ public:
|
||||
* Updates the bone transforms in the range [offset, offset + boneCount).
|
||||
* The bones must be pre-allocated using Builder::skinning().
|
||||
*/
|
||||
void setBones(Instance instance, Bone const* UTILS_NONNULL transforms,
|
||||
size_t boneCount = 1, size_t offset = 0);
|
||||
|
||||
void setBones(Instance instance, math::mat4f const* UTILS_NONNULL transforms,
|
||||
size_t boneCount = 1, size_t offset = 0); //!< \overload
|
||||
void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0);
|
||||
void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0); //!< \overload
|
||||
|
||||
/**
|
||||
* Associates a region of a SkinningBuffer to a renderable instance
|
||||
@@ -724,7 +708,7 @@ public:
|
||||
* @param count Size of the region in bones, must be smaller or equal to 256.
|
||||
* @param offset Start offset of the region in bones
|
||||
*/
|
||||
void setSkinningBuffer(Instance instance, SkinningBuffer* UTILS_NONNULL skinningBuffer,
|
||||
void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer,
|
||||
size_t count, size_t offset);
|
||||
|
||||
/**
|
||||
@@ -739,25 +723,24 @@ public:
|
||||
* @param offset Index of the first morph target weight to set at instance.
|
||||
*/
|
||||
void setMorphWeights(Instance instance,
|
||||
float const* UTILS_NONNULL weights, size_t count, size_t offset = 0);
|
||||
float const* weights, size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* Associates a MorphTargetBuffer to the given primitive.
|
||||
*/
|
||||
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
|
||||
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Utility method to change a MorphTargetBuffer to the given primitive
|
||||
*/
|
||||
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer);
|
||||
MorphTargetBuffer* morphTargetBuffer);
|
||||
|
||||
/**
|
||||
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
|
||||
*/
|
||||
MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance,
|
||||
uint8_t level, size_t primitiveIndex) const noexcept;
|
||||
MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
|
||||
|
||||
/**
|
||||
* Gets the number of morphing in the given entity.
|
||||
@@ -799,22 +782,20 @@ public:
|
||||
* @see Engine::setActiveFeatureLevel
|
||||
*/
|
||||
void setMaterialInstanceAt(Instance instance,
|
||||
size_t primitiveIndex, MaterialInstance const* UTILS_NONNULL materialInstance);
|
||||
size_t primitiveIndex, MaterialInstance const* materialInstance);
|
||||
|
||||
/**
|
||||
* Retrieves the material instance that is bound to the given primitive.
|
||||
*/
|
||||
MaterialInstance* UTILS_NULLABLE getMaterialInstanceAt(
|
||||
Instance instance, size_t primitiveIndex) const noexcept;
|
||||
MaterialInstance* getMaterialInstanceAt(Instance instance, size_t primitiveIndex) const noexcept;
|
||||
|
||||
/**
|
||||
* Changes the geometry for the given primitive.
|
||||
*
|
||||
* \see Builder::geometry()
|
||||
*/
|
||||
void setGeometryAt(Instance instance, size_t primitiveIndex, PrimitiveType type,
|
||||
VertexBuffer* UTILS_NONNULL vertices,
|
||||
IndexBuffer* UTILS_NONNULL indices,
|
||||
void setGeometryAt(Instance instance, size_t primitiveIndex,
|
||||
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
|
||||
size_t offset, size_t count) noexcept;
|
||||
|
||||
/**
|
||||
@@ -875,9 +856,7 @@ public:
|
||||
template<typename VECTOR, typename INDEX,
|
||||
typename = typename is_supported_vector_type<VECTOR>::type,
|
||||
typename = typename is_supported_index_type<INDEX>::type>
|
||||
static Box computeAABB(
|
||||
VECTOR const* UTILS_NONNULL vertices,
|
||||
INDEX const* UTILS_NONNULL indices, size_t count,
|
||||
static Box computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
|
||||
size_t stride = sizeof(VECTOR)) noexcept;
|
||||
|
||||
protected:
|
||||
@@ -885,25 +864,21 @@ protected:
|
||||
~RenderableManager() = default;
|
||||
};
|
||||
|
||||
RenderableManager::Builder& RenderableManager::Builder::morphing(
|
||||
uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
|
||||
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* morphTargetBuffer) noexcept {
|
||||
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
|
||||
morphTargetBuffer->getVertexCount());
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphTargetBufferAt(
|
||||
Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
|
||||
void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* morphTargetBuffer) {
|
||||
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
|
||||
morphTargetBuffer->getVertexCount());
|
||||
}
|
||||
|
||||
template<typename VECTOR, typename INDEX, typename, typename>
|
||||
Box RenderableManager::computeAABB(
|
||||
VECTOR const* UTILS_NONNULL vertices,
|
||||
INDEX const* UTILS_NONNULL indices,
|
||||
size_t count, size_t stride) noexcept {
|
||||
Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
|
||||
size_t stride) noexcept {
|
||||
math::float3 bmin(FLT_MAX);
|
||||
math::float3 bmax(-FLT_MAX);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
|
||||
@@ -181,14 +181,14 @@ public:
|
||||
*
|
||||
* @return A pointer to the Engine instance this Renderer is associated to.
|
||||
*/
|
||||
Engine* UTILS_NONNULL getEngine() noexcept;
|
||||
Engine* getEngine() noexcept;
|
||||
|
||||
/**
|
||||
* Get the Engine that created this Renderer.
|
||||
*
|
||||
* @return A constant pointer to the Engine instance this Renderer is associated to.
|
||||
*/
|
||||
inline Engine const* UTILS_NONNULL getEngine() const noexcept {
|
||||
inline Engine const* getEngine() const noexcept {
|
||||
return const_cast<Renderer *>(this)->getEngine();
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ public:
|
||||
* @see
|
||||
* endFrame()
|
||||
*/
|
||||
bool beginFrame(SwapChain* UTILS_NONNULL swapChain,
|
||||
bool beginFrame(SwapChain* swapChain,
|
||||
uint64_t vsyncSteadyClockTimeNano = 0u);
|
||||
|
||||
/**
|
||||
@@ -335,7 +335,7 @@ public:
|
||||
* beginFrame(), endFrame(), View
|
||||
*
|
||||
*/
|
||||
void render(View const* UTILS_NONNULL view);
|
||||
void render(View const* view);
|
||||
|
||||
/**
|
||||
* Copy the currently rendered view to the indicated swap chain, using the
|
||||
@@ -350,7 +350,7 @@ public:
|
||||
* copyFrame() should be called after a frame is rendered using render()
|
||||
* but before endFrame() is called.
|
||||
*/
|
||||
void copyFrame(SwapChain* UTILS_NONNULL dstSwapChain, Viewport const& dstViewport,
|
||||
void copyFrame(SwapChain* dstSwapChain, Viewport const& dstViewport,
|
||||
Viewport const& srcViewport, uint32_t flags = 0);
|
||||
|
||||
/**
|
||||
@@ -490,7 +490,7 @@ public:
|
||||
* readPixels() is intended for debugging and testing. It will impact performance significantly.
|
||||
*
|
||||
*/
|
||||
void readPixels(RenderTarget* UTILS_NONNULL renderTarget,
|
||||
void readPixels(RenderTarget* renderTarget,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
@@ -517,7 +517,7 @@ public:
|
||||
* However, internally, renderStandaloneView() is highly multi-threaded to both improve
|
||||
* performance in mitigate the call's latency.
|
||||
*/
|
||||
void renderStandaloneView(View const* UTILS_NONNULL view);
|
||||
void renderStandaloneView(View const* view);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,14 +75,14 @@ public:
|
||||
*
|
||||
* @param skybox The Skybox to use to fill untouched pixels, or nullptr to unset the Skybox.
|
||||
*/
|
||||
void setSkybox(Skybox* UTILS_NULLABLE skybox) noexcept;
|
||||
void setSkybox(Skybox* skybox) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the Skybox associated with the Scene.
|
||||
*
|
||||
* @return The associated Skybox, or nullptr if there is none.
|
||||
*/
|
||||
Skybox* UTILS_NULLABLE getSkybox() const noexcept;
|
||||
Skybox* getSkybox() const noexcept;
|
||||
|
||||
/**
|
||||
* Set the IndirectLight to use when rendering the Scene.
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
* @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset.
|
||||
* @see getIndirectLight
|
||||
*/
|
||||
void setIndirectLight(IndirectLight* UTILS_NULLABLE ibl) noexcept;
|
||||
void setIndirectLight(IndirectLight* ibl) noexcept;
|
||||
|
||||
/**
|
||||
* Get the IndirectLight or nullptr if none is set.
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
* @return the the IndirectLight or nullptr if none is set
|
||||
* @see setIndirectLight
|
||||
*/
|
||||
IndirectLight* UTILS_NULLABLE getIndirectLight() const noexcept;
|
||||
IndirectLight* getIndirectLight() const noexcept;
|
||||
|
||||
/**
|
||||
* Adds an Entity to the Scene.
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
* @param entities Array containing entities to add to the scene.
|
||||
* @param count Size of the entity array.
|
||||
*/
|
||||
void addEntities(const utils::Entity* UTILS_NONNULL entities, size_t count);
|
||||
void addEntities(const utils::Entity* entities, size_t count);
|
||||
|
||||
/**
|
||||
* Removes the Renderable from the Scene.
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
* @param entities Array containing entities to remove from the scene.
|
||||
* @param count Size of the entity array.
|
||||
*/
|
||||
void removeEntities(const utils::Entity* UTILS_NONNULL entities, size_t count);
|
||||
void removeEntities(const utils::Entity* entities, size_t count);
|
||||
|
||||
/**
|
||||
* Returns the total number of Entities in the Scene, whether alive or not.
|
||||
|
||||
@@ -74,7 +74,8 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this SkinningBuffer with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
@@ -82,7 +83,7 @@ public:
|
||||
*
|
||||
* @see SkinningBuffer::setBones
|
||||
*/
|
||||
SkinningBuffer* UTILS_NONNULL build(Engine& engine);
|
||||
SkinningBuffer* build(Engine& engine);
|
||||
private:
|
||||
friend class FSkinningBuffer;
|
||||
};
|
||||
@@ -95,7 +96,7 @@ public:
|
||||
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
|
||||
* @see RenderableManager::setSkinningBuffer
|
||||
*/
|
||||
void setBones(Engine& engine, RenderableManager::Bone const* UTILS_NONNULL transforms,
|
||||
void setBones(Engine& engine, RenderableManager::Bone const* transforms,
|
||||
size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
@@ -106,7 +107,7 @@ public:
|
||||
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
|
||||
* @see RenderableManager::setSkinningBuffer
|
||||
*/
|
||||
void setBones(Engine& engine, math::mat4f const* UTILS_NONNULL transforms,
|
||||
void setBones(Engine& engine, math::mat4f const* transforms,
|
||||
size_t count, size_t offset = 0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
*
|
||||
* @see Texture
|
||||
*/
|
||||
Builder& environment(Texture* UTILS_NONNULL cubemap) noexcept;
|
||||
Builder& environment(Texture* cubemap) noexcept;
|
||||
|
||||
/**
|
||||
* Indicates whether the sun should be rendered. The sun can only be
|
||||
@@ -136,9 +136,9 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this Skybox with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object, or nullptr if the light couldn't be created.
|
||||
*/
|
||||
Skybox* UTILS_NONNULL build(Engine& engine);
|
||||
Skybox* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FSkybox;
|
||||
@@ -172,9 +172,9 @@ public:
|
||||
float getIntensity() const noexcept;
|
||||
|
||||
/**
|
||||
* @return the associated texture
|
||||
* @return the associated texture, or null if it does not exist
|
||||
*/
|
||||
Texture const* UTILS_NONNULL getTexture() const noexcept;
|
||||
Texture const* getTexture() const noexcept;
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& stream(void* UTILS_NULLABLE stream) noexcept;
|
||||
Builder& stream(void* stream) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -141,9 +141,9 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this Stream with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object, or nullptr if the stream couldn't be created.
|
||||
*/
|
||||
Stream* UTILS_NONNULL build(Engine& engine);
|
||||
Stream* build(Engine& engine);
|
||||
|
||||
private:
|
||||
friend class FStream;
|
||||
@@ -171,12 +171,11 @@ public:
|
||||
*
|
||||
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
|
||||
* @param callback This is triggered by Filament when it wishes to release the image.
|
||||
* The callback tales two arguments: the AHardwareBuffer and the userdata.
|
||||
* It callback tales two arguments: the AHardwareBuffer and the userdata.
|
||||
* @param userdata Optional closure data. Filament will pass this into the callback when it
|
||||
* releases the image.
|
||||
*/
|
||||
void setAcquiredImage(void* UTILS_NONNULL image,
|
||||
Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept;
|
||||
void setAcquiredImage(void* image, Callback callback, void* userdata) noexcept;
|
||||
|
||||
/**
|
||||
* @see setAcquiredImage(void*, Callback, void*)
|
||||
@@ -188,9 +187,7 @@ public:
|
||||
* @param userdata Optional closure data. Filament will pass this into the callback when it
|
||||
* releases the image.
|
||||
*/
|
||||
void setAcquiredImage(void* UTILS_NONNULL image,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept;
|
||||
void setAcquiredImage(void* image, backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept;
|
||||
|
||||
/**
|
||||
* Updates the size of the incoming stream. Whether this value is used is
|
||||
|
||||
@@ -152,7 +152,7 @@ class Engine;
|
||||
class UTILS_PUBLIC SwapChain : public FilamentAPI {
|
||||
public:
|
||||
using FrameScheduledCallback = backend::FrameScheduledCallback;
|
||||
using FrameCompletedCallback = utils::Invocable<void(SwapChain* UTILS_NONNULL)>;
|
||||
using FrameCompletedCallback = utils::Invocable<void(SwapChain*)>;
|
||||
|
||||
/**
|
||||
* Requests a SwapChain with an alpha channel.
|
||||
@@ -236,7 +236,7 @@ public:
|
||||
*/
|
||||
static bool isSRGBSwapChainSupported(Engine& engine) noexcept;
|
||||
|
||||
void* UTILS_NULLABLE getNativeWindow() const noexcept;
|
||||
void* getNativeWindow() const noexcept;
|
||||
|
||||
/**
|
||||
* FrameScheduledCallback is a callback function that notifies an application when Filament has
|
||||
@@ -269,8 +269,7 @@ public:
|
||||
*
|
||||
* @see PresentCallable
|
||||
*/
|
||||
void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback,
|
||||
void* UTILS_NULLABLE user = nullptr);
|
||||
void setFrameScheduledCallback(FrameScheduledCallback callback, void* user = nullptr);
|
||||
|
||||
/**
|
||||
* FrameCompletedCallback is a callback function that notifies an application when a frame's
|
||||
@@ -291,7 +290,7 @@ public:
|
||||
*
|
||||
* @see CallbackHandler
|
||||
*/
|
||||
void setFrameCompletedCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
void setFrameCompletedCallback(backend::CallbackHandler* handler = nullptr,
|
||||
FrameCompletedCallback&& callback = {}) noexcept;
|
||||
|
||||
|
||||
|
||||
@@ -204,13 +204,14 @@ public:
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this Texture with.
|
||||
*
|
||||
* @return pointer to the newly created object.
|
||||
* @return pointer to the newly created object or nullptr if exceptions are disabled and
|
||||
* an error occurred.
|
||||
*
|
||||
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
|
||||
* memory or other resources.
|
||||
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
|
||||
*/
|
||||
Texture* UTILS_NONNULL build(Engine& engine);
|
||||
Texture* build(Engine& engine);
|
||||
|
||||
/* no user serviceable parts below */
|
||||
|
||||
@@ -399,7 +400,7 @@ public:
|
||||
* @see Builder::sampler()
|
||||
*
|
||||
*/
|
||||
void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept;
|
||||
void setExternalImage(Engine& engine, void* image) noexcept;
|
||||
|
||||
/**
|
||||
* Specify the external image and plane to associate with this Texture. Typically the external
|
||||
@@ -430,7 +431,7 @@ public:
|
||||
* kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms
|
||||
* other than iOS, this method is a no-op.
|
||||
*/
|
||||
void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane) noexcept;
|
||||
void setExternalImage(Engine& engine, void* image, size_t plane) noexcept;
|
||||
|
||||
/**
|
||||
* Specify the external stream to associate with this Texture. Typically the external
|
||||
@@ -449,7 +450,7 @@ public:
|
||||
* @see Builder::sampler(), Stream
|
||||
*
|
||||
*/
|
||||
void setExternalStream(Engine& engine, Stream* UTILS_NULLABLE stream) noexcept;
|
||||
void setExternalStream(Engine& engine, Stream* stream) noexcept;
|
||||
|
||||
/**
|
||||
* Generates all the mipmap levels automatically. This requires the texture to have a
|
||||
@@ -499,7 +500,7 @@ public:
|
||||
*/
|
||||
void generatePrefilterMipmap(Engine& engine,
|
||||
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
|
||||
PrefilterOptions const* UTILS_NULLABLE options = nullptr);
|
||||
PrefilterOptions const* options = nullptr);
|
||||
|
||||
|
||||
/** @deprecated */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user