Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3da96d424 | ||
|
|
a068143953 | ||
|
|
90d90094dc | ||
|
|
54df4524eb | ||
|
|
d70f2e1b81 | ||
|
|
2f359b73a4 | ||
|
|
abb0cbc98e | ||
|
|
2763931b47 | ||
|
|
0ead96b606 | ||
|
|
317c1bb7ea | ||
|
|
9aaaad9271 | ||
|
|
d2ce714e73 | ||
|
|
8398175d9c | ||
|
|
26258a4718 | ||
|
|
3644e7f808 | ||
|
|
4d774820d9 | ||
|
|
aad45d9119 | ||
|
|
5389b37002 | ||
|
|
d5fe9e236f | ||
|
|
59890ac85a | ||
|
|
435969f565 | ||
|
|
e3db39105f | ||
|
|
dfa821d351 | ||
|
|
1ab223c965 | ||
|
|
29bb60cd94 | ||
|
|
93b15dac87 | ||
|
|
a8fda9b4d0 | ||
|
|
ba9cb2fe43 | ||
|
|
0f7cffc407 | ||
|
|
dace5fd695 | ||
|
|
b23ee1bce4 | ||
|
|
25f017b883 | ||
|
|
0def77eaa8 | ||
|
|
3c25dab22f | ||
|
|
93a80fd084 | ||
|
|
963e097bdc | ||
|
|
86479781a5 | ||
|
|
4ea5872b26 | ||
|
|
f27f0ef4fc | ||
|
|
348454781f | ||
|
|
bb6c8ef1c8 | ||
|
|
fa6b4ebd04 | ||
|
|
9aad4df441 | ||
|
|
2e581be8fd | ||
|
|
6d7eaf31d3 | ||
|
|
dea345d28e |
@@ -45,6 +45,8 @@ option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
|
||||
|
||||
option(FILAMENT_ENABLE_MULTIVIEW "Enable multiview for Filament" OFF)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
@@ -531,6 +533,21 @@ else()
|
||||
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" ON)
|
||||
endif()
|
||||
|
||||
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
|
||||
# The value can be either "instanced" or "multiview".
|
||||
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING
|
||||
"Stereoscopic type that shader files in gltfio and samples are built for."
|
||||
)
|
||||
string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE)
|
||||
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ")
|
||||
endif ()
|
||||
|
||||
# Compiling samples for multiview implies enabling multiview feature as well.
|
||||
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
set(FILAMENT_ENABLE_MULTIVIEW ON)
|
||||
endif ()
|
||||
|
||||
# ==================================================================================================
|
||||
# Material compilation flags
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -7,3 +7,5 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
|
||||
|
||||
@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.51.1
|
||||
|
||||
|
||||
## v1.51.0
|
||||
|
||||
- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
|
||||
|
||||
@@ -391,6 +391,13 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
|
||||
engine->flush();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean paused) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
engine->setPaused(paused);
|
||||
}
|
||||
|
||||
// Managers...
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
@@ -487,7 +494,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
jlong jobSystemThreadCount,
|
||||
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
|
||||
jint stereoscopicType, jlong stereoscopicEyeCount,
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
|
||||
jboolean disableHandleUseAfterFreeCheck) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -502,6 +510,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
|
||||
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
|
||||
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
|
||||
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
@@ -518,6 +527,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
builder->sharedContext((void*) sharedContext);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused(
|
||||
JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
builder->paused((bool) paused);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
|
||||
@@ -104,6 +104,14 @@ Java_com_google_android_filament_RenderableManager_nBuilderGeometry__JIIJJIIII(J
|
||||
(size_t) count);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderGeometryType(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, int type) {
|
||||
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
|
||||
builder->geometryType((RenderableManager::Builder::GeometryType)type);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderMaterial(JNIEnv*, jclass,
|
||||
|
||||
@@ -224,7 +224,8 @@ public class Engine {
|
||||
config.jobSystemThreadCount,
|
||||
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
|
||||
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
|
||||
config.disableHandleUseAfterFreeCheck);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -239,6 +240,20 @@ public class Engine {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial paused state of the rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API. See {@link Engine#setPaused(boolean)} for
|
||||
* caveats.
|
||||
*
|
||||
* @param paused Whether to start the rendering thread paused.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
public Builder paused(boolean paused) {
|
||||
nSetBuilderPaused(mNativeBuilder, paused);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of Engine
|
||||
*
|
||||
@@ -408,6 +423,11 @@ public class Engine {
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
public boolean disableHandleUseAfterFreeCheck = false;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -1189,6 +1209,22 @@ public class Engine {
|
||||
nFlush(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause or resume the rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API. In particular, note the following caveats.
|
||||
*
|
||||
* <ul><li>
|
||||
* Buffer callbacks will never be called as long as the rendering thread is paused.
|
||||
* Do not rely on a buffer callback to unpause the thread.
|
||||
* </li><li>
|
||||
* While the rendering thread is paused, rendering commands will continue to be queued until the
|
||||
* buffer limit is reached. When the limit is reached, the program will abort.
|
||||
* </li></ul>
|
||||
*/
|
||||
public void setPaused(boolean paused) {
|
||||
nSetPaused(getNativeObject(), paused);
|
||||
}
|
||||
|
||||
@UsedByReflection("TextureHelper.java")
|
||||
public long getNativeObject() {
|
||||
@@ -1263,6 +1299,7 @@ public class Engine {
|
||||
private static native void nDestroyEntity(long nativeEngine, int entity);
|
||||
private static native void nFlushAndWait(long nativeEngine);
|
||||
private static native void nFlush(long nativeEngine);
|
||||
private static native void nSetPaused(long nativeEngine, boolean paused);
|
||||
private static native long nGetTransformManager(long nativeEngine);
|
||||
private static native long nGetLightManager(long nativeEngine);
|
||||
private static native long nGetRenderableManager(long nativeEngine);
|
||||
@@ -1283,8 +1320,10 @@ public class Engine {
|
||||
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
|
||||
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
|
||||
int stereoscopicType, long stereoscopicEyeCount,
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
|
||||
boolean disableHandleUseAfterFreeCheck);
|
||||
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
|
||||
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
|
||||
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
|
||||
private static native long nBuilderBuild(long nativeBuilder);
|
||||
}
|
||||
|
||||
+27
@@ -175,6 +175,32 @@ public class RenderableManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of geometry for a Renderable
|
||||
*/
|
||||
public enum GeometryType {
|
||||
/** dynamic gemoetry has no restriction */
|
||||
DYNAMIC,
|
||||
/** bounds and world space transform are immutable */
|
||||
STATIC_BOUNDS,
|
||||
/** skinning/morphing not allowed and Vertex/IndexBuffer immutables */
|
||||
STATIC
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether this renderable has static bounds. In this context his means that
|
||||
* the renderable's bounding box cannot change and that the renderable's transform is
|
||||
* assumed immutable. Changing the renderable's transform via the TransformManager
|
||||
* can lead to corrupted graphics. Note that skinning and morphing are not forbidden.
|
||||
* Disabled by default.
|
||||
* @param enable whether this renderable has static bounds. false by default.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder geometryType(GeometryType type) {
|
||||
nBuilderGeometryType(mNativeBuilder, type.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a material instance to the specified primitive.
|
||||
*
|
||||
@@ -964,6 +990,7 @@ public class RenderableManager {
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer);
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count);
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int minIndex, int maxIndex, int count);
|
||||
private static native void nBuilderGeometryType(long nativeBuilder, int type);
|
||||
private static native void nBuilderMaterial(long nativeBuilder, int index, long nativeMaterialInstance);
|
||||
private static native void nBuilderBlendOrder(long nativeBuilder, int index, int blendOrder);
|
||||
private static native void nBuilderGlobalBlendOrderEnabled(long nativeBuilder, int index, boolean enabled);
|
||||
|
||||
@@ -81,9 +81,17 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/src/TangentsJob.cpp
|
||||
${GLTFIO_DIR}/src/TangentsJob.h
|
||||
${GLTFIO_DIR}/src/UbershaderProvider.cpp
|
||||
${GLTFIO_DIR}/src/Utility.cpp
|
||||
${GLTFIO_DIR}/src/Utility.h
|
||||
${GLTFIO_DIR}/src/Wireframe.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.h
|
||||
${GLTFIO_DIR}/src/downcast.h
|
||||
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
|
||||
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
|
||||
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
|
||||
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
|
||||
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
|
||||
|
||||
|
||||
src/main/cpp/Animator.cpp
|
||||
src/main/cpp/AssetLoader.cpp
|
||||
|
||||
@@ -516,7 +516,7 @@ function build_android {
|
||||
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
|
||||
for sample in ${ANDROID_SAMPLES}; do
|
||||
echo "Installing out/${sample}-debug.apk"
|
||||
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug-unsigned.apk \
|
||||
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug.apk \
|
||||
../out/${sample}-debug.apk
|
||||
done
|
||||
fi
|
||||
|
||||
+36
-15
@@ -256,6 +256,11 @@ set(MATERIAL_FL0_SRCS
|
||||
src/materials/skybox.mat
|
||||
)
|
||||
|
||||
set(MATERIAL_MULTIVIEW_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/skybox.mat
|
||||
)
|
||||
|
||||
# Embed the binary resource blob for materials.
|
||||
get_resgen_vars(${RESOURCE_DIR} materials)
|
||||
list(APPEND PRIVATE_HDRS ${RESGEN_HEADER})
|
||||
@@ -285,6 +290,11 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
endif()
|
||||
|
||||
# Whether to include MULTIVIEW materials.
|
||||
if (FILAMENT_ENABLE_MULTIVIEW)
|
||||
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
@@ -315,30 +325,41 @@ foreach (mat_src ${MATERIAL_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
|
||||
list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
|
||||
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
string(REGEX REPLACE "[.]filamat$" "0.filamat" output_path0 ${output_path})
|
||||
string(REGEX REPLACE "[.]filamat$" "_fl0.filamat" output_path_fl0 ${output_path})
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path} ${output_path0}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path0} ${fullname}
|
||||
OUTPUT ${output_path_fl0}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path_fl0} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path} and ${output_path0}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path0})
|
||||
else ()
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path}"
|
||||
COMMENT "Compiling material ${fullname} to ${output_path_fl0}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path_fl0})
|
||||
endif ()
|
||||
|
||||
list(FIND MATERIAL_MULTIVIEW_SRCS ${mat_src} index)
|
||||
if (${index} GREATER -1 AND FILAMENT_ENABLE_MULTIVIEW)
|
||||
string(REGEX REPLACE "[.]filamat$" "_multiview.filamat" output_path_multiview ${output_path})
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path_multiview}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -PstereoscopicType=multiview -o ${output_path_multiview} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path_multiview}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path_multiview})
|
||||
endif ()
|
||||
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
endforeach()
|
||||
|
||||
# Additional dependencies on included files for materials
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -59,6 +60,11 @@ public:
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
*/
|
||||
bool disableParallelShaderCompile = false;
|
||||
|
||||
/**
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
bool disableHandleUseAfterFreeCheck = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
@@ -188,9 +194,40 @@ public:
|
||||
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
|
||||
void* UTILS_NONNULL value, size_t valueSize);
|
||||
|
||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
|
||||
|
||||
/**
|
||||
* Sets the callback function that the backend can use to update backend-specific statistics
|
||||
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param debugUpdateStat an Invocable that updates debug statistics
|
||||
*/
|
||||
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
|
||||
|
||||
/**
|
||||
* @return true if debugUpdateStat is valid.
|
||||
*/
|
||||
bool hasDebugUpdateStatFunc() const noexcept;
|
||||
|
||||
/**
|
||||
* To track backend-specific statistics, the backend implementation can call the
|
||||
* application-provided callback function debugUpdateStatFunc to associate or update a value
|
||||
* with a given key. It is possible for this function to be called multiple times with the
|
||||
* same key, in which case newer values should overwrite older values.
|
||||
*
|
||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param key a null-terminated C-string with the key of the debug statistic
|
||||
* @param value the updated value of key
|
||||
*/
|
||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
|
||||
|
||||
private:
|
||||
InsertBlobFunc mInsertBlob;
|
||||
RetrieveBlobFunc mRetrieveBlob;
|
||||
DebugUpdateStatFunc mDebugUpdateStat;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -116,6 +116,8 @@ public:
|
||||
|
||||
Program& cacheId(uint64_t cacheId) noexcept;
|
||||
|
||||
Program& multiview(bool multiview) noexcept;
|
||||
|
||||
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
|
||||
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
|
||||
|
||||
@@ -143,6 +145,8 @@ public:
|
||||
|
||||
uint64_t getCacheId() const noexcept { return mCacheId; }
|
||||
|
||||
bool isMultiview() const noexcept { return mMultiview; }
|
||||
|
||||
CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; }
|
||||
|
||||
private:
|
||||
@@ -158,6 +162,11 @@ private:
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
|
||||
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
|
||||
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
|
||||
// Indicates the current engine was initialized with multiview stereo, and the variant for this
|
||||
// program contains STE flag. This will be referred later for the OpenGL shader compiler to
|
||||
// determine whether shader code replacement for the num_views should be performed.
|
||||
// This variable could be promoted as a more generic variable later if other similar needs occur.
|
||||
bool mMultiview = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -32,6 +32,10 @@ struct TargetBufferInfo {
|
||||
// texture to be used as render target
|
||||
Handle<HwTexture> handle;
|
||||
|
||||
// starting layer index for multiview. This value is only used when the `layerCount` for the
|
||||
// render target is greater than 1.
|
||||
uint8_t baseViewIndex = 0;
|
||||
|
||||
// level to be used
|
||||
uint8_t level = 0;
|
||||
|
||||
@@ -80,7 +84,7 @@ public:
|
||||
|
||||
// this is here for backward compatibility
|
||||
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
|
||||
: mInfos{{ handle, level, layer }} {
|
||||
: mInfos{{ handle, 0, level, layer }} {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
* @return The driver's SwapChain object.
|
||||
*
|
||||
*/
|
||||
virtual SwapChain* UTILS_NONNULL createSwapChain(
|
||||
virtual SwapChain* UTILS_NULLABLE createSwapChain(
|
||||
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
|
||||
|
||||
/**
|
||||
@@ -126,29 +126,59 @@ public:
|
||||
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to establish the default FBO. The default implementation returns 0.
|
||||
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
|
||||
* Returns true if the swapchain is protected
|
||||
*/
|
||||
virtual uint32_t createDefaultRenderTarget() noexcept;
|
||||
virtual bool isSwapChainProtected(Platform::SwapChain* UTILS_NONNULL swapChain) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to make the OpenGL context active on the calling thread and bind
|
||||
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
|
||||
* Called by the driver to establish the default FBO. The default implementation returns 0.
|
||||
*
|
||||
* This method can be called either on the regular or protected OpenGL contexts and can return
|
||||
* a different or identical name, since these names exist in different namespaces.
|
||||
*
|
||||
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
|
||||
*/
|
||||
virtual uint32_t getDefaultFramebufferObject() noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Type of contexts available
|
||||
*/
|
||||
enum class ContextType {
|
||||
NONE, //!< No current context
|
||||
UNPROTECTED, //!< current context is unprotected
|
||||
PROTECTED //!< current context supports protected content
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the type of the context currently in use. This value is updated by makeCurrent()
|
||||
* and therefore can be cached between calls. ContextType::PROTECTED can only be returned
|
||||
* if isProtectedContextSupported() is true.
|
||||
* @return ContextType
|
||||
*/
|
||||
virtual ContextType getCurrentContextType() const noexcept;
|
||||
|
||||
/**
|
||||
* Binds the requested context to the current thread and drawSwapChain to the default FBO
|
||||
* returned by getDefaultFramebufferObject().
|
||||
*
|
||||
* @param type type of context to bind to the current thread.
|
||||
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
|
||||
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual void makeCurrent(
|
||||
virtual bool makeCurrent(ContextType type,
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver to make the OpenGL context active on the calling thread and bind
|
||||
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
|
||||
* the drawSwapChain to the default FBO returned by getDefaultFramebufferObject().
|
||||
* The context used is either the default context or the protected context. When a context
|
||||
* change is necessary, the preContextChange and postContextChange callbacks are called,
|
||||
* before and after the context change respectively. postContextChange is given the index
|
||||
* of the new context (0 for default and 1 for protected).
|
||||
* The default implementation just calls makeCurrent(SwapChain*, SwapChain*).
|
||||
* The default implementation just calls makeCurrent(getCurrentContextType(), SwapChain*, SwapChain*).
|
||||
*
|
||||
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
|
||||
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
|
||||
|
||||
@@ -57,7 +57,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
void destroyExternalImage(ExternalTexture* texture) noexcept override;
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
void terminate() noexcept override;
|
||||
|
||||
uint32_t createDefaultRenderTarget() noexcept override;
|
||||
uint32_t getDefaultFramebufferObject() noexcept override;
|
||||
|
||||
bool isExtraContextSupported() const noexcept override;
|
||||
void createContext(bool shared) override;
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
|
||||
@@ -99,11 +99,18 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
|
||||
|
||||
ContextType getCurrentContextType() const noexcept override;
|
||||
|
||||
bool makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept override;
|
||||
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
|
||||
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
bool canCreateFence() noexcept override;
|
||||
@@ -132,21 +139,25 @@ protected:
|
||||
/**
|
||||
* Always use this instead of eglMakeCurrent(), as it tracks some state.
|
||||
*/
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
|
||||
/**
|
||||
* Returns true if the swapchain is protected
|
||||
*/
|
||||
static bool isSwapChainProtected(Platform::SwapChain const* swapChain) noexcept;
|
||||
EGLContext getContextForType(ContextType type) const noexcept;
|
||||
|
||||
// makes the draw and read surface current without changing the current context
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
return egl.makeCurrent(drawSurface, readSurface);
|
||||
}
|
||||
|
||||
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
|
||||
EGLBoolean makeCurrent(EGLContext context) noexcept {
|
||||
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
|
||||
}
|
||||
|
||||
// TODO: this should probably use getters instead.
|
||||
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
|
||||
EGLContext mEGLContext = EGL_NO_CONTEXT;
|
||||
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
|
||||
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
|
||||
EGLContext mCurrentContext = EGL_NO_CONTEXT;
|
||||
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
|
||||
ContextType mCurrentContextType = ContextType::NONE;
|
||||
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
|
||||
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
|
||||
Config mContextAttribs;
|
||||
@@ -181,8 +192,20 @@ protected:
|
||||
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
|
||||
|
||||
private:
|
||||
EGLBoolean makeCurrent(EGLContext context,
|
||||
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
class EGL {
|
||||
EGLDisplay& mEGLDisplay;
|
||||
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
|
||||
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
|
||||
EGLContext mCurrentContext = EGL_NO_CONTEXT;
|
||||
public:
|
||||
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
|
||||
EGLBoolean makeCurrent(EGLContext context,
|
||||
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||
}
|
||||
} egl{ mEGLDisplay };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -46,7 +46,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
};
|
||||
|
||||
|
||||
@@ -50,12 +50,13 @@ class CommandBufferQueue {
|
||||
size_t mFreeSpace = 0;
|
||||
size_t mHighWatermark = 0;
|
||||
uint32_t mExitRequested = 0;
|
||||
bool mPaused = false;
|
||||
|
||||
static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
|
||||
|
||||
public:
|
||||
// requiredSize: guaranteed available space after flush()
|
||||
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
|
||||
CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused);
|
||||
~CommandBufferQueue();
|
||||
|
||||
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
|
||||
@@ -80,6 +81,9 @@ public:
|
||||
// returns from waitForCommands() immediately.
|
||||
void requestExit();
|
||||
|
||||
// suspend or unsuspend the queue.
|
||||
void setPaused(bool paused);
|
||||
|
||||
bool isExitRequested() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
uint8_t, samples,
|
||||
uint8_t, layerCount,
|
||||
backend::MRT, color,
|
||||
backend::TargetBufferInfo, depth,
|
||||
backend::TargetBufferInfo, stencil)
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace filament::backend {
|
||||
template<size_t P0, size_t P1, size_t P2>
|
||||
class HandleAllocator {
|
||||
public:
|
||||
HandleAllocator(const char* name, size_t size) noexcept;
|
||||
HandleAllocator(const char* name, size_t size, bool disableUseAfterFreeCheck) noexcept;
|
||||
HandleAllocator(HandleAllocator const& rhs) = delete;
|
||||
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
|
||||
~HandleAllocator();
|
||||
@@ -169,11 +169,13 @@ public:
|
||||
|
||||
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());
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<Dp>(p);
|
||||
@@ -187,7 +189,6 @@ public:
|
||||
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template<typename D>
|
||||
@@ -210,8 +211,9 @@ private:
|
||||
Pool<P1> mPool1;
|
||||
Pool<P2> mPool2;
|
||||
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
|
||||
bool mUseAfterFreeCheckDisabled;
|
||||
public:
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area, bool disableUseAfterFreeCheck);
|
||||
|
||||
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
|
||||
|
||||
@@ -237,8 +239,10 @@ private:
|
||||
// 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);
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
}
|
||||
expectedAge = (expectedAge + 1) & 0xF; // fixme
|
||||
|
||||
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
|
||||
@@ -348,6 +352,7 @@ private:
|
||||
mutable utils::Mutex mLock;
|
||||
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
|
||||
HandleBase::HandleId mId = 0;
|
||||
bool mUseAfterFreeCheckDisabled = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -39,10 +39,11 @@ using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
|
||||
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused)
|
||||
: mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)),
|
||||
mCircularBuffer(bufferSize),
|
||||
mFreeSpace(mCircularBuffer.size()) {
|
||||
mFreeSpace(mCircularBuffer.size()),
|
||||
mPaused(paused) {
|
||||
assert_invariant(mCircularBuffer.size() > requiredSize);
|
||||
}
|
||||
|
||||
@@ -56,6 +57,16 @@ void CommandBufferQueue::requestExit() {
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
void CommandBufferQueue::setPaused(bool paused) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
if (paused) {
|
||||
mPaused = true;
|
||||
} else {
|
||||
mPaused = false;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
@@ -115,6 +126,9 @@ void CommandBufferQueue::flush() noexcept {
|
||||
#endif
|
||||
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
ASSERT_POSTCONDITION(!mPaused,
|
||||
"CommandStream is full, but since the rendering thread is paused, "
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.");
|
||||
mCondition.wait(lock, [this, requiredSize]() -> bool {
|
||||
// TODO: on macOS, we need to call pumpEvents from time to time
|
||||
return mFreeSpace >= requiredSize;
|
||||
@@ -127,7 +141,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
while (mCommandBuffersToExecute.empty() && !mExitRequested) {
|
||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@ using namespace utils;
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
UTILS_NOINLINE
|
||||
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
|
||||
: mArea(area) {
|
||||
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area,
|
||||
bool disableUseAfterFreeCheck)
|
||||
: mArea(area),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
|
||||
// The largest handle this allocator can generate currently depends on the architecture's
|
||||
// min alignment, typically 8 or 16 bytes.
|
||||
@@ -74,8 +76,10 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
|
||||
: mHandleArena(name, size) {
|
||||
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
|
||||
bool disableUseAfterFreeCheck) noexcept
|
||||
: mHandleArena(name, size, disableUseAfterFreeCheck),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
}
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
|
||||
@@ -53,4 +53,18 @@ size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
|
||||
mDebugUpdateStat = std::move(debugUpdateStat);
|
||||
}
|
||||
|
||||
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
||||
return bool(mDebugUpdateStat);
|
||||
}
|
||||
|
||||
void Platform::debugUpdateStat(const char* key, uint64_t value) {
|
||||
if (mDebugUpdateStat) {
|
||||
mDebugUpdateStat(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -91,6 +91,11 @@ Program& Program::cacheId(uint64_t cacheId) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::multiview(bool multiview) noexcept {
|
||||
mMultiview = multiview;
|
||||
return *this;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const Program& builder) {
|
||||
out << "Program{";
|
||||
builder.mLogger(out);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
|
||||
|
||||
#include "MetalContext.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -28,9 +27,50 @@
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class TrackedMetalBuffer {
|
||||
public:
|
||||
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
|
||||
TrackedMetalBuffer(id<MTLBuffer> buffer) noexcept : mBuffer(buffer) {
|
||||
if (buffer) {
|
||||
aliveBuffers++;
|
||||
}
|
||||
}
|
||||
~TrackedMetalBuffer() {
|
||||
if (mBuffer) {
|
||||
aliveBuffers--;
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
|
||||
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
|
||||
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
id<MTLBuffer> get() const noexcept { return mBuffer; }
|
||||
operator bool() const noexcept { return bool(mBuffer); }
|
||||
|
||||
static uint64_t getAliveBuffers() { return aliveBuffers; }
|
||||
|
||||
private:
|
||||
void swap(TrackedMetalBuffer& other) noexcept {
|
||||
id<MTLBuffer> temp = mBuffer;
|
||||
mBuffer = other.mBuffer;
|
||||
other.mBuffer = temp;
|
||||
}
|
||||
|
||||
id<MTLBuffer> mBuffer;
|
||||
|
||||
static std::atomic<uint64_t> aliveBuffers;
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
public:
|
||||
|
||||
@@ -82,7 +122,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
id<MTLBuffer> mBuffer = nil;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
size_t mBufferSize = 0;
|
||||
void* mCpuBuffer = nullptr;
|
||||
MetalContext& mContext;
|
||||
@@ -151,7 +191,7 @@ public:
|
||||
// finishes executing.
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
assert_invariant(mAuxBuffer);
|
||||
return {mAuxBuffer, 0};
|
||||
return {mAuxBuffer.get(), 0};
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -180,9 +220,9 @@ public:
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
return { mAuxBuffer, 0 };
|
||||
return { mAuxBuffer.get(), 0 };
|
||||
}
|
||||
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
|
||||
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
|
||||
}
|
||||
|
||||
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
|
||||
@@ -191,8 +231,8 @@ public:
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
id<MTLBuffer> mBuffer;
|
||||
id<MTLBuffer> mAuxBuffer;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
TrackedMetalBuffer mAuxBuffer;
|
||||
|
||||
MTLResourceOptions mBufferOptions;
|
||||
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
*/
|
||||
|
||||
#include "MetalBuffer.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include "MetalContext.h"
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::atomic<uint64_t> TrackedMetalBuffer::aliveBuffers = 0;
|
||||
|
||||
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
|
||||
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
|
||||
@@ -61,7 +64,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
// Acquire a staging buffer to hold the contents of this update.
|
||||
MetalBufferPool* bufferPool = mContext.bufferPool;
|
||||
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
|
||||
memcpy(staging->buffer.contents, src, size);
|
||||
memcpy(staging->buffer.get().contents, src, size);
|
||||
|
||||
// The blit below requires that byteOffset be a multiple of 4.
|
||||
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
|
||||
@@ -70,9 +73,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit";
|
||||
[blitEncoder copyFromBuffer:staging->buffer
|
||||
[blitEncoder copyFromBuffer:staging->buffer.get()
|
||||
sourceOffset:0
|
||||
toBuffer:mBuffer
|
||||
toBuffer:mBuffer.get()
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
@@ -93,7 +96,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
|
||||
return nil;
|
||||
}
|
||||
assert_invariant(mBuffer);
|
||||
return mBuffer;
|
||||
return mBuffer.get();
|
||||
}
|
||||
|
||||
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include "MetalBuffer.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
@@ -30,7 +32,7 @@ struct MetalContext;
|
||||
|
||||
// Immutable POD representing a shared CPU-GPU buffer.
|
||||
struct MetalBufferPoolEntry {
|
||||
id<MTLBuffer> buffer;
|
||||
TrackedMetalBuffer buffer;
|
||||
size_t capacity;
|
||||
mutable uint64_t lastAccessed;
|
||||
mutable uint32_t referenceCount;
|
||||
|
||||
@@ -45,12 +45,12 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
|
||||
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
|
||||
.buffer = buffer,
|
||||
.capacity = numBytes,
|
||||
.lastAccessed = mCurrentFrame,
|
||||
.referenceCount = 1
|
||||
});
|
||||
};
|
||||
mUsedStages.insert(stage);
|
||||
|
||||
return stage;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "metal/MetalDriver.h"
|
||||
|
||||
#include "MetalBlitter.h"
|
||||
#include "MetalBufferPool.h"
|
||||
#include "MetalContext.h"
|
||||
#include "MetalDriverFactory.h"
|
||||
#include "MetalEnums.h"
|
||||
@@ -36,6 +37,7 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/sstream.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -98,7 +100,9 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
|
||||
MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize) {
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck) {
|
||||
mContext->driver = this;
|
||||
|
||||
mContext->device = mPlatform.createDevice();
|
||||
@@ -212,6 +216,9 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
|
||||
#endif
|
||||
if (mPlatform.hasDebugUpdateStatFunc()) {
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -376,7 +383,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dum
|
||||
|
||||
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, MRT color,
|
||||
uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"createRenderTarget must be called outside of a render pass.");
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "MetalBlitter.h"
|
||||
#include "MetalEnums.h"
|
||||
#include "MetalUtils.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include <filament/SwapChain.h>
|
||||
|
||||
@@ -770,13 +771,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
|
||||
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
|
||||
const size_t stagingBufferSize = shape.totalBytes;
|
||||
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
|
||||
memcpy(entry->buffer.contents,
|
||||
memcpy(entry->buffer.get().contents,
|
||||
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
|
||||
stagingBufferSize);
|
||||
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
|
||||
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
|
||||
blitCommandEncoder.label = @"Texture upload buffer blit";
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:shape.bytesPerRow
|
||||
sourceBytesPerImage:shape.bytesPerSlice
|
||||
|
||||
@@ -16,11 +16,25 @@
|
||||
|
||||
#include "OpenGLContext.h"
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// change to true to display all GL extensions in the console on start-up
|
||||
#define DEBUG_PRINT_EXTENSIONS false
|
||||
|
||||
@@ -49,7 +63,8 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept {
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
|
||||
state.vao.p = &mDefaultVAO;
|
||||
|
||||
@@ -270,6 +285,9 @@ void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// the default FBO could be invalid
|
||||
mDefaultFbo[index].reset();
|
||||
|
||||
contextIndex = index;
|
||||
resetState();
|
||||
}
|
||||
@@ -751,6 +769,51 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
|
||||
#endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
|
||||
GLuint OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
|
||||
if (UTILS_UNLIKELY(buffer == 0)) {
|
||||
// we're binding the default frame buffer, resolve its actual name
|
||||
auto& defaultFboForThisContext = mDefaultFbo[contextIndex];
|
||||
if (UTILS_UNLIKELY(!defaultFboForThisContext.has_value())) {
|
||||
defaultFboForThisContext = GLuint(mPlatform.getDefaultFramebufferObject());
|
||||
}
|
||||
buffer = defaultFboForThisContext.value();
|
||||
}
|
||||
bindFramebufferResolved(target, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindFramebuffer(GLenum target) noexcept {
|
||||
bindFramebufferResolved(target, 0);
|
||||
}
|
||||
|
||||
void OpenGLContext::bindFramebufferResolved(GLenum target, GLuint buffer) noexcept {
|
||||
switch (target) {
|
||||
case GL_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
|
||||
state.draw_fbo = state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
case GL_DRAW_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer) {
|
||||
state.draw_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
case GL_READ_FRAMEBUFFER:
|
||||
if (state.read_fbo != buffer) {
|
||||
state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
|
||||
if (target == GL_ELEMENT_ARRAY_BUFFER) {
|
||||
constexpr size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
@@ -17,24 +17,32 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include "OpenGLTimerQuery.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLPlatform;
|
||||
@@ -82,7 +90,7 @@ public:
|
||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||
|
||||
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
|
||||
~OpenGLContext() noexcept;
|
||||
~OpenGLContext() noexcept final;
|
||||
|
||||
// TimerQueryInterface ------------------------------------------------------------------------
|
||||
|
||||
@@ -148,7 +156,8 @@ public:
|
||||
inline void bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size) noexcept;
|
||||
|
||||
inline void bindFramebuffer(GLenum target, GLuint buffer) noexcept;
|
||||
GLuint bindFramebuffer(GLenum target, GLuint buffer) noexcept;
|
||||
void unbindFramebuffer(GLenum target) noexcept;
|
||||
|
||||
inline void enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
|
||||
inline void disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
|
||||
@@ -460,10 +469,15 @@ public:
|
||||
void synchronizeStateAndCache(size_t index) noexcept;
|
||||
|
||||
private:
|
||||
OpenGLPlatform& mPlatform;
|
||||
ShaderModel mShaderModel = ShaderModel::MOBILE;
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
|
||||
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
|
||||
RenderPrimitive mDefaultVAO;
|
||||
std::optional<GLuint> mDefaultFbo[2];
|
||||
|
||||
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
|
||||
|
||||
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
|
||||
{ bugs.disable_glFlush,
|
||||
@@ -516,8 +530,6 @@ private:
|
||||
""},
|
||||
}};
|
||||
|
||||
RenderPrimitive mDefaultVAO;
|
||||
|
||||
// this is chosen to minimize code size
|
||||
#if defined(BACKEND_OPENGL_VERSION_GLES)
|
||||
static void initExtensionsGLES(Extensions* ext, GLint major, GLint minor) noexcept;
|
||||
@@ -732,33 +744,6 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
|
||||
switch (target) {
|
||||
case GL_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
|
||||
state.draw_fbo = state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
case GL_DRAW_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer) {
|
||||
state.draw_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
case GL_READ_FRAMEBUFFER:
|
||||
if (state.read_fbo != buffer) {
|
||||
state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept {
|
||||
assert_invariant(targetIndex == getIndexForTextureTarget(target));
|
||||
assert_invariant(targetIndex < TEXTURE_TARGET_COUNT);
|
||||
|
||||
@@ -16,21 +16,54 @@
|
||||
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "OpenGLProgram.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/SamplerDescriptor.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PipelineState.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/SamplerDescriptor.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
@@ -172,7 +205,9 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
: mPlatform(*platform),
|
||||
mContext(mPlatform),
|
||||
mShaderCompilerService(*this),
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mSamplerMap(32),
|
||||
mDriverConfig(driverConfig) {
|
||||
|
||||
@@ -730,23 +765,23 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
// we can't be here -- doesn't matter what we do
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = GL_TEXTURE_3D;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_2D_ARRAY;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -758,7 +793,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
t->gl.targetIndex = (uint8_t)
|
||||
gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -829,27 +864,27 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
t->gl.target = GL_TEXTURE_EXTERNAL_OES;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = GL_TEXTURE_3D;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_2D_ARRAY;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -860,7 +895,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
if (gl.features.multisample_texture) {
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -901,7 +936,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
|
||||
gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]);
|
||||
GLuint const index = i;
|
||||
GLint const size = getComponentCount(attribute.type);
|
||||
GLint const size = (GLint)getComponentCount(attribute.type);
|
||||
GLenum const type = getComponentType(attribute.type);
|
||||
GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED);
|
||||
GLsizei const stride = attribute.stride;
|
||||
@@ -956,7 +991,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
}
|
||||
|
||||
void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GLRenderTarget const* rt, GLenum attachment) noexcept {
|
||||
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept {
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
// Only used by assert_invariant() checks below
|
||||
@@ -1094,9 +1129,20 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
case GL_TEXTURE_2D_ARRAY:
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
|
||||
|
||||
// TODO: support multiview for iOS and WebGL
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
if (layerCount > 1) {
|
||||
// if layerCount > 1, it means we use the multiview extension.
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, 0, binfo.baseViewIndex, layerCount);
|
||||
} else
|
||||
#endif // !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
{
|
||||
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, binfo.level, binfo.layer);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
@@ -1257,11 +1303,9 @@ void OpenGLDriver::createDefaultRenderTargetR(
|
||||
|
||||
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
|
||||
|
||||
uint32_t const framebuffer = mPlatform.createDefaultRenderTarget();
|
||||
|
||||
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
|
||||
rt->gl.isDefault = true;
|
||||
rt->gl.fbo = framebuffer;
|
||||
rt->gl.fbo = 0; // the actual id is resolved at binding time
|
||||
rt->gl.samples = 1;
|
||||
// FIXME: these flags should reflect the actual attachments present
|
||||
rt->targets = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
|
||||
@@ -1272,6 +1316,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint8_t samples,
|
||||
uint8_t layerCount,
|
||||
MRT color,
|
||||
TargetBufferInfo depth,
|
||||
TargetBufferInfo stencil) {
|
||||
@@ -1338,7 +1383,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
if (any(targets & getTargetBufferFlagsAt(i))) {
|
||||
assert_invariant(color[i].handle);
|
||||
rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
|
||||
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i);
|
||||
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i, layerCount);
|
||||
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
checkDimensions(rt->gl.color[i], color[i].level);
|
||||
}
|
||||
@@ -1360,7 +1405,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
// either we supplied only the depth handle or both depth/stencil are identical and not null
|
||||
if (depth.handle && (stencil.handle == depth.handle || !stencil.handle)) {
|
||||
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT, layerCount);
|
||||
specialCased = true;
|
||||
checkDimensions(rt->gl.depth, depth.level);
|
||||
}
|
||||
@@ -1371,13 +1416,13 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
if (any(targets & TargetBufferFlags::DEPTH)) {
|
||||
assert_invariant(depth.handle);
|
||||
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT, layerCount);
|
||||
checkDimensions(rt->gl.depth, depth.level);
|
||||
}
|
||||
if (any(targets & TargetBufferFlags::STENCIL)) {
|
||||
assert_invariant(stencil.handle);
|
||||
rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
|
||||
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT);
|
||||
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT, layerCount);
|
||||
checkDimensions(rt->gl.stencil, stencil.level);
|
||||
}
|
||||
}
|
||||
@@ -1419,6 +1464,13 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
|
||||
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
|
||||
nativeWindow, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
|
||||
@@ -1433,6 +1485,13 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
|
||||
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
|
||||
width, height, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
|
||||
@@ -1575,11 +1634,11 @@ void OpenGLDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
|
||||
if (rt->gl.fbo) {
|
||||
// first unbind this framebuffer if needed
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
gl.unbindFramebuffer(GL_FRAMEBUFFER);
|
||||
}
|
||||
if (rt->gl.fbo_read) {
|
||||
// first unbind this framebuffer if needed
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
gl.unbindFramebuffer(GL_FRAMEBUFFER);
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
@@ -2413,7 +2472,9 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
|
||||
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
|
||||
void const* const buffer = static_cast<char const*>(p.buffer) + p.left * bpp + bpr * p.top;
|
||||
size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride"
|
||||
void const* const buffer = static_cast<char const*>(p.buffer)
|
||||
+ bpp* p.left + bpr * p.top + bpl * 0; // TODO: PBD should have a p.depth
|
||||
|
||||
switch (t->target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
@@ -2478,7 +2539,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (!gl.isES2()) {
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
if (int8_t(level) < t->gl.baseLevel) {
|
||||
t->gl.baseLevel = int8_t(level);
|
||||
@@ -2579,7 +2640,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (!gl.isES2()) {
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
if (int8_t(level) < t->gl.baseLevel) {
|
||||
t->gl.baseLevel = int8_t(level);
|
||||
@@ -2732,7 +2793,7 @@ TimerQueryResult OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
return TimerQueryFactoryInterface::getTimerQueryValue(tq, elapsedTime);
|
||||
}
|
||||
|
||||
void OpenGLDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
void OpenGLDriver::compilePrograms(CompilerPriorityQueue,
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
|
||||
if (callback) {
|
||||
getShaderCompilerService().notifyWhenAllProgramsAreReady(handler, callback, user);
|
||||
@@ -2760,13 +2821,13 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
const TargetBufferFlags clearFlags = params.flags.clear & rt->targets;
|
||||
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
|
||||
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
|
||||
if (gl.ext.EXT_discard_framebuffer
|
||||
&& !gl.bugs.disable_invalidate_framebuffer) {
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
|
||||
GLsizei const attachmentCount = getAttachments(attachments, discardFlags, !fbo);
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2854,9 +2915,9 @@ void OpenGLDriver::endRenderPass(int) {
|
||||
}
|
||||
if (!gl.bugs.disable_invalidate_framebuffer) {
|
||||
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
|
||||
GLsizei const attachmentCount = getAttachments(attachments, effectiveDiscardFlags, !fbo);
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2918,50 +2979,48 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
|
||||
}
|
||||
|
||||
GLsizei OpenGLDriver::getAttachments(AttachmentArray& attachments,
|
||||
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept {
|
||||
assert_invariant(buffers <= rt->targets);
|
||||
|
||||
TargetBufferFlags buffers, bool isDefaultFramebuffer) noexcept {
|
||||
GLsizei attachmentCount = 0;
|
||||
// the default framebuffer uses different constants!!!
|
||||
const bool defaultFramebuffer = (rt->gl.fbo == 0);
|
||||
|
||||
if (any(buffers & TargetBufferFlags::COLOR0)) {
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
|
||||
}
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (any(buffers & TargetBufferFlags::COLOR1)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT1;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR2)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT2;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR3)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT3;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR4)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT4;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR5)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT5;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR6)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT6;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR7)) {
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT7;
|
||||
}
|
||||
#endif
|
||||
if (any(buffers & TargetBufferFlags::DEPTH)) {
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::STENCIL)) {
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
|
||||
}
|
||||
return attachmentCount;
|
||||
}
|
||||
@@ -3681,8 +3740,8 @@ void OpenGLDriver::blit(
|
||||
mask, GL_NEAREST);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||
glDeleteFramebuffers(2, fbo);
|
||||
|
||||
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
@@ -18,31 +18,41 @@
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVER_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB
|
||||
@@ -59,12 +69,14 @@ class OpenGLProgram;
|
||||
class TimerQueryFactoryInterface;
|
||||
|
||||
class OpenGLDriver final : public DriverBase {
|
||||
inline explicit OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
inline explicit OpenGLDriver(OpenGLPlatform* platform,
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
~OpenGLDriver() noexcept final;
|
||||
Dispatcher getDispatcher() const noexcept final;
|
||||
|
||||
public:
|
||||
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
class DebugMarker {
|
||||
OpenGLDriver& driver;
|
||||
@@ -302,7 +314,7 @@ private:
|
||||
void updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb);
|
||||
|
||||
void framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GLRenderTarget const* rt, GLenum attachment) noexcept;
|
||||
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept;
|
||||
|
||||
void setRasterState(RasterState rs) noexcept;
|
||||
|
||||
@@ -357,8 +369,8 @@ private:
|
||||
}
|
||||
|
||||
using AttachmentArray = std::array<GLenum, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 2>;
|
||||
static GLsizei getAttachments(AttachmentArray& attachments,
|
||||
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept;
|
||||
static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers,
|
||||
bool isDefaultFramebuffer) noexcept;
|
||||
|
||||
// state required to represent the current render pass
|
||||
Handle<HwRenderTarget> mRenderPassTarget;
|
||||
|
||||
@@ -40,7 +40,7 @@ OpenGLPlatform::~OpenGLPlatform() noexcept = default;
|
||||
|
||||
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
|
||||
makeCurrent(drawSwapChain, readSwapChain);
|
||||
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isProtectedContextSupported() const noexcept {
|
||||
@@ -51,10 +51,14 @@ bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t OpenGLPlatform::createDefaultRenderTarget() noexcept {
|
||||
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OpenGLPlatform::ContextType OpenGLPlatform::getCurrentContextType() const noexcept {
|
||||
return ContextType::UNPROTECTED;
|
||||
}
|
||||
|
||||
void OpenGLPlatform::setPresentationTime(
|
||||
UTILS_UNUSED int64_t presentationTimeInNanosecond) noexcept {
|
||||
}
|
||||
@@ -125,10 +129,14 @@ AcquiredImage OpenGLPlatform::transformAcquiredImage(AcquiredImage source) noexc
|
||||
return source;
|
||||
}
|
||||
|
||||
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain* swapChain) noexcept {
|
||||
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain*) noexcept {
|
||||
return TargetBufferFlags::NONE;
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isSwapChainProtected(UTILS_UNUSED SwapChain*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isExtraContextSupported() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,25 @@
|
||||
|
||||
#include "OpenGLProgram.h"
|
||||
|
||||
#include "BlobCacheKey.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLDriver.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <new>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -264,6 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
shaders,
|
||||
token->shaderSourceCode);
|
||||
|
||||
@@ -299,6 +301,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
token->gl.shaders,
|
||||
token->shaderSourceCode);
|
||||
|
||||
@@ -501,6 +504,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
|
||||
@@ -514,8 +518,16 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
};
|
||||
|
||||
std::string specializationConstantString;
|
||||
int32_t numViews = 2;
|
||||
for (auto const& sc : specializationConstants) {
|
||||
appendSpecConstantString(specializationConstantString, sc);
|
||||
if (sc.id == 8) {
|
||||
// This constant must match
|
||||
// ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT
|
||||
// which we can't use here because it's defined in EngineEnums.h.
|
||||
// (we're breaking layering here, but it's for the good cause).
|
||||
numViews = std::get<int32_t>(sc.value);
|
||||
}
|
||||
}
|
||||
if (!specializationConstantString.empty()) {
|
||||
specializationConstantString += '\n';
|
||||
@@ -544,17 +556,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
|
||||
if (UTILS_LIKELY(!shadersSource[i].empty())) {
|
||||
Program::ShaderBlob& shader = shadersSource[i];
|
||||
char* shader_src = reinterpret_cast<char*>(shader.data());
|
||||
size_t shader_len = shader.size();
|
||||
|
||||
// remove GOOGLE_cpp_style_line_directive
|
||||
std::string_view const source = process_GOOGLE_cpp_style_line_directive(context,
|
||||
reinterpret_cast<char*>(shader.data()), shader.size());
|
||||
process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len);
|
||||
|
||||
// replace the value of layout(num_views = X) for multiview extension
|
||||
if (multiview && stage == ShaderStage::VERTEX) {
|
||||
process_OVR_multiview2(context, numViews, shader_src, shader_len);
|
||||
}
|
||||
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
auto const packingFunctions = process_ARB_shading_language_packing(context);
|
||||
|
||||
// split shader source, so we can insert the specialization constants and the packing
|
||||
// functions
|
||||
auto const [prolog, body] = splitShaderSource(source);
|
||||
auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
|
||||
|
||||
const std::array<const char*, 4> sources = {
|
||||
prolog.data(),
|
||||
@@ -577,7 +595,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
#ifndef NDEBUG
|
||||
// for debugging we return the original shader source (without the modifications we
|
||||
// made here), otherwise the line numbers wouldn't match.
|
||||
outShaderSourceCode[i] = { source.data(), source.length() };
|
||||
outShaderSourceCode[i] = { shader_src, shader_len };
|
||||
#endif
|
||||
|
||||
outShaders[i] = shaderId;
|
||||
@@ -586,15 +604,59 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks. This happens in-place.
|
||||
std::string_view ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
// drivers don't allow the quotation marks. This source modification happens in-place.
|
||||
void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept {
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||
removeGoogleLineDirectives(source, len); // length is unaffected
|
||||
}
|
||||
}
|
||||
return { source, len };
|
||||
}
|
||||
|
||||
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
||||
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
||||
// the number of views, which is assumed as a single digit, for multiview.
|
||||
// This source modification happens in-place.
|
||||
void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
|
||||
int32_t eyeCount, char* source, size_t len) noexcept {
|
||||
// We don't use regular expression in favor of performance.
|
||||
if (context.ext.OVR_multiview2) {
|
||||
const std::string_view shader{ source, len };
|
||||
const std::string_view layout = "layout";
|
||||
const std::string_view num_views = "num_views";
|
||||
size_t found = 0;
|
||||
while (true) {
|
||||
found = shader.find(layout, found);
|
||||
if (found == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + layout.size());
|
||||
if (found == std::string_view::npos || shader[found] != '(') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + num_views.size());
|
||||
if (found == std::string_view::npos || shader[found] != '=') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
// We assume the value should be one-digit number.
|
||||
assert_invariant(eyeCount < 10);
|
||||
assert_invariant(!::isdigit(source[found + 1]));
|
||||
source[found] = '0' + eyeCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
|
||||
@@ -134,10 +134,14 @@ private:
|
||||
OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
||||
|
||||
static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
@@ -67,12 +67,14 @@ PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
|
||||
// use getProcAddress for ES3.1 and above entry points.
|
||||
PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
static std::once_flag sGlExtInitialized;
|
||||
#endif // __EMSCRIPTEN__
|
||||
@@ -118,9 +120,11 @@ void importGLESExtensionsEntryPoints() {
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
getProcAddress(glMaxShaderCompilerThreadsKHR, "glMaxShaderCompilerThreadsKHR");
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
||||
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
||||
#endif
|
||||
});
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
@@ -151,9 +151,11 @@ extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
#endif // __EMSCRIPTEN__
|
||||
} // namespace glext
|
||||
|
||||
@@ -247,8 +247,8 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
|
||||
delete cocoaSwapChain;
|
||||
}
|
||||
|
||||
void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
|
||||
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
|
||||
@@ -287,6 +287,7 @@ void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
|
||||
swapChain->previousBounds = currentBounds;
|
||||
swapChain->previousWindowFrame = currentWindowFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformCocoaGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -143,11 +143,12 @@ void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noex
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PlatformCocoaTouchGL::createDefaultRenderTarget() noexcept {
|
||||
uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
|
||||
return pImpl->mDefaultFramebuffer;
|
||||
}
|
||||
|
||||
void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
|
||||
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
|
||||
@@ -182,6 +183,7 @@ void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* read
|
||||
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformCocoaTouchGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#include <utils/compiler.h>
|
||||
@@ -282,12 +283,14 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
|
||||
if (UTILS_UNLIKELY(
|
||||
egl.makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
|
||||
// eglMakeCurrent failed
|
||||
logEglError("eglMakeCurrent");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mCurrentContextType = ContextType::UNPROTECTED;
|
||||
mContextAttribs = std::move(contextAttribs);
|
||||
|
||||
initializeGlExtensions();
|
||||
@@ -362,26 +365,6 @@ void PlatformEGL::releaseContext() noexcept {
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
EGLBoolean PlatformEGL::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||
}
|
||||
|
||||
EGLBoolean PlatformEGL::makeCurrent(EGLContext context,
|
||||
EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
if (UTILS_UNLIKELY((mCurrentContext != context ||
|
||||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||
EGLBoolean const success = eglMakeCurrent(
|
||||
mEGLDisplay, drawSurface, readSurface, context);
|
||||
if (success) {
|
||||
mCurrentDrawSurface = drawSurface;
|
||||
mCurrentReadSurface = readSurface;
|
||||
mCurrentContext = context;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
void PlatformEGL::terminate() noexcept {
|
||||
// it's always allowed to use EGL_NO_SURFACE, EGL_NO_CONTEXT
|
||||
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
@@ -468,30 +451,10 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
|
||||
return ext.egl.KHR_gl_colorspace;
|
||||
}
|
||||
|
||||
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain const* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
void* nativeWindow, uint64_t flags) noexcept {
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, true, false);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Config attribs;
|
||||
|
||||
if (ext.egl.KHR_gl_colorspace) {
|
||||
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
|
||||
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
|
||||
@@ -508,16 +471,27 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
|
||||
}
|
||||
|
||||
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config,
|
||||
(EGLNativeWindowType)nativeWindow, attribs.data());
|
||||
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("eglCreateWindowSurface");
|
||||
return nullptr;
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, true, false);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
// this is not fatal
|
||||
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
|
||||
sur = eglCreateWindowSurface(mEGLDisplay, config,
|
||||
(EGLNativeWindowType)nativeWindow, attribs.data());
|
||||
|
||||
if (UTILS_LIKELY(sur != EGL_NO_SURFACE)) {
|
||||
// this is not fatal
|
||||
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
|
||||
} else {
|
||||
logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface");
|
||||
}
|
||||
} else {
|
||||
// error already logged
|
||||
}
|
||||
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
@@ -532,17 +506,6 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
uint32_t width, uint32_t height, uint64_t flags) noexcept {
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, false, true);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Config attribs = {
|
||||
{ EGL_WIDTH, EGLint(width) },
|
||||
{ EGL_HEIGHT, EGLint(height) },
|
||||
@@ -564,17 +527,28 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
|
||||
}
|
||||
|
||||
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, true, false);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("eglCreatePbufferSurface");
|
||||
return nullptr;
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
|
||||
sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface");
|
||||
}
|
||||
} else {
|
||||
// error already logged
|
||||
}
|
||||
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
.attribs = std::move(attribs),
|
||||
.config = config
|
||||
.config = config,
|
||||
.flags = flags
|
||||
});
|
||||
return sc;
|
||||
}
|
||||
@@ -583,29 +557,45 @@ void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
if (sc->sur != EGL_NO_SURFACE) {
|
||||
makeCurrent(mCurrentContext, mEGLDummySurface, mEGLDummySurface);
|
||||
egl.makeCurrent(mEGLDummySurface, mEGLDummySurface);
|
||||
eglDestroySurface(mEGLDisplay, sc->sur);
|
||||
delete sc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept {
|
||||
return mCurrentContextType;
|
||||
}
|
||||
|
||||
bool PlatformEGL::makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
|
||||
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
|
||||
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
||||
makeCurrent(mEGLContext, dsc->sur, rsc->sur);
|
||||
EGLContext context = getContextForType(type);
|
||||
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
|
||||
return success == EGL_TRUE ? true : false;
|
||||
}
|
||||
|
||||
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept {
|
||||
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
|
||||
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
||||
EGLContext context = mEGLContext;
|
||||
|
||||
assert_invariant(drawSwapChain);
|
||||
assert_invariant(readSwapChain);
|
||||
|
||||
ContextType type = ContextType::UNPROTECTED;
|
||||
if (ext.egl.EXT_protected_content) {
|
||||
bool const swapChainProtected = PlatformEGL::isSwapChainProtected(dsc);
|
||||
bool const swapChainProtected = isSwapChainProtected(drawSwapChain);
|
||||
if (UTILS_UNLIKELY(swapChainProtected)) {
|
||||
// we need a protected context
|
||||
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
|
||||
@@ -622,34 +612,36 @@ void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
context = mEGLContextProtected;
|
||||
type = ContextType::PROTECTED;
|
||||
error: ;
|
||||
}
|
||||
|
||||
bool const contextChange = context != mCurrentContext;
|
||||
bool const contextChange = type != mCurrentContextType;
|
||||
mCurrentContextType = type;
|
||||
|
||||
if (UTILS_UNLIKELY(contextChange)) {
|
||||
preContextChange();
|
||||
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
|
||||
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
logEglError("PlatformEGL::makeCurrent");
|
||||
if (mEGLContextProtected != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
}
|
||||
context = mEGLContext;
|
||||
mCurrentContextType = ContextType::UNPROTECTED;
|
||||
}
|
||||
if (UTILS_LIKELY(!swapChainProtected && mEGLContextProtected != EGL_NO_CONTEXT)) {
|
||||
// We don't need the protected context anymore, unbind and destroy right away.
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
}
|
||||
size_t const contextIndex = (context == mEGLContext) ? 0 : 1;
|
||||
size_t const contextIndex = (mCurrentContextType == ContextType::PROTECTED) ? 1 : 0;
|
||||
postContextChange(contextIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
|
||||
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
logEglError("PlatformEGL::makeCurrent");
|
||||
}
|
||||
@@ -746,6 +738,17 @@ void PlatformEGL::initializeGlExtensions() noexcept {
|
||||
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
|
||||
}
|
||||
|
||||
EGLContext PlatformEGL::getContextForType(OpenGLPlatform::ContextType type) const noexcept {
|
||||
switch (type) {
|
||||
case ContextType::NONE:
|
||||
return EGL_NO_CONTEXT;
|
||||
case ContextType::UNPROTECTED:
|
||||
return mEGLContext;
|
||||
case ContextType::PROTECTED:
|
||||
return mEGLContextProtected;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGL::Config::Config() = default;
|
||||
@@ -782,4 +785,23 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
|
||||
EGLSurface readSurface) noexcept {
|
||||
if (UTILS_UNLIKELY((
|
||||
mCurrentContext != context ||
|
||||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||
EGLBoolean const success = eglMakeCurrent(
|
||||
mEGLDisplay, drawSurface, readSurface, context);
|
||||
if (success) {
|
||||
mCurrentDrawSurface = drawSurface;
|
||||
mCurrentReadSurface = readSurface;
|
||||
mCurrentContext = context;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -142,11 +142,12 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
|
||||
if (mCurrentDrawSurface != EGL_NO_SURFACE) {
|
||||
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
|
||||
if (currentDrawSurface != EGL_NO_SURFACE) {
|
||||
if (eglPresentationTimeANDROID) {
|
||||
eglPresentationTimeANDROID(
|
||||
mEGLDisplay,
|
||||
mCurrentDrawSurface,
|
||||
currentDrawSurface,
|
||||
presentationTimeInNanosecond);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,10 +265,11 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformGLX::makeCurrent(
|
||||
Platform::SwapChain* drawSwapChain, Platform::SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
g_glx.setCurrentContext(mGLXDisplay,
|
||||
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformGLX::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -255,8 +255,8 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
wglMakeCurrent(mWhdc, mContext);
|
||||
}
|
||||
|
||||
void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformWGL does not support distinct draw/read swap chains.");
|
||||
|
||||
@@ -269,6 +269,7 @@ void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
wglMakeCurrent(0, NULL);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformWGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -46,8 +46,9 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
|
||||
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
void PlatformWebGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformWebGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -154,15 +154,9 @@ struct BlitterUniforms {
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
VulkanBlitter::VulkanBlitter() noexcept = default;
|
||||
|
||||
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept {
|
||||
mPhysicalDevice = physicalDevice;
|
||||
mDevice = device;
|
||||
mAllocator = allocator;
|
||||
mCommands = commands;
|
||||
}
|
||||
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
|
||||
: mPhysicalDevice(physicalDevice),
|
||||
mCommands(commands) {}
|
||||
|
||||
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
|
||||
|
||||
@@ -32,10 +32,7 @@ struct VulkanProgram;
|
||||
|
||||
class VulkanBlitter {
|
||||
public:
|
||||
VulkanBlitter() noexcept;
|
||||
|
||||
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept;
|
||||
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
|
||||
|
||||
void blit(VkFilter filter,
|
||||
VulkanAttachment dst, const VkOffset3D* dstRectPair,
|
||||
@@ -47,8 +44,6 @@ public:
|
||||
|
||||
private:
|
||||
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
|
||||
VkDevice mDevice;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanCommands* mCommands;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
|
||||
#endif
|
||||
}
|
||||
|
||||
VulkanCommands::~VulkanCommands() {
|
||||
void VulkanCommands::terminate() {
|
||||
wait();
|
||||
gc();
|
||||
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
||||
|
||||
@@ -139,71 +139,72 @@ public:
|
||||
// - We do this because vkGetFenceStatus must be called from the rendering thread.
|
||||
//
|
||||
class VulkanCommands {
|
||||
public:
|
||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VulkanContext* context, VulkanResourceAllocator* allocator);
|
||||
~VulkanCommands();
|
||||
public:
|
||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VulkanContext* context, VulkanResourceAllocator* allocator);
|
||||
|
||||
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
||||
VulkanCommandBuffer& get();
|
||||
void terminate();
|
||||
|
||||
// Submits the current command buffer if it exists, then sets "current" to null.
|
||||
// If there are no outstanding commands then nothing happens and this returns false.
|
||||
bool flush();
|
||||
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
||||
VulkanCommandBuffer& get();
|
||||
|
||||
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
||||
// it from the existing dependency chain. This is especially useful for setting up
|
||||
// vkQueuePresentKHR.
|
||||
VkSemaphore acquireFinishedSignal();
|
||||
// Submits the current command buffer if it exists, then sets "current" to null.
|
||||
// If there are no outstanding commands then nothing happens and this returns false.
|
||||
bool flush();
|
||||
|
||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
||||
void injectDependency(VkSemaphore next);
|
||||
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
||||
// it from the existing dependency chain. This is especially useful for setting up
|
||||
// vkQueuePresentKHR.
|
||||
VkSemaphore acquireFinishedSignal();
|
||||
|
||||
// Destroys all command buffers that are no longer in use.
|
||||
void gc();
|
||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
||||
void injectDependency(VkSemaphore next);
|
||||
|
||||
// Waits for all outstanding command buffers to finish.
|
||||
void wait();
|
||||
// Destroys all command buffers that are no longer in use.
|
||||
void gc();
|
||||
|
||||
// Updates the atomic "status" variable in every extant fence.
|
||||
void updateFences();
|
||||
// Waits for all outstanding command buffers to finish.
|
||||
void wait();
|
||||
|
||||
// Sets an observer who is notified every time a new command buffer has been made "current".
|
||||
// The observer's event handler can only be called during get().
|
||||
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
|
||||
// Updates the atomic "status" variable in every extant fence.
|
||||
void updateFences();
|
||||
|
||||
// Sets an observer who is notified every time a new command buffer has been made "current".
|
||||
// The observer's event handler can only be called during get().
|
||||
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
||||
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
||||
|
||||
void popGroupMarker();
|
||||
void popGroupMarker();
|
||||
|
||||
void insertEventMarker(char const* string, uint32_t len);
|
||||
void insertEventMarker(char const* string, uint32_t len);
|
||||
|
||||
std::string getTopGroupMarker() const;
|
||||
std::string getTopGroupMarker() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
VkDevice const mDevice;
|
||||
VkQueue const mQueue;
|
||||
VkCommandPool const mPool;
|
||||
VulkanContext const* mContext;
|
||||
private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
VkDevice const mDevice;
|
||||
VkQueue const mQueue;
|
||||
VkCommandPool const mPool;
|
||||
VulkanContext const* mContext;
|
||||
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
int8_t mCurrentCommandBufferIndex = -1;
|
||||
VkSemaphore mSubmissionSignal = {};
|
||||
VkSemaphore mInjectedSignal = {};
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
||||
VkFence mFences[CAPACITY] = {};
|
||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
||||
uint8_t mAvailableBufferCount = CAPACITY;
|
||||
CommandBufferObserver* mObserver = nullptr;
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
int8_t mCurrentCommandBufferIndex = -1;
|
||||
VkSemaphore mSubmissionSignal = {};
|
||||
VkSemaphore mInjectedSignal = {};
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
||||
VkFence mFences[CAPACITY] = {};
|
||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
||||
uint8_t mAvailableBufferCount = CAPACITY;
|
||||
CommandBufferObserver* mObserver = nullptr;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -43,9 +43,12 @@
|
||||
// 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
|
||||
// Group markers are used to denote collections of GPU commands. It is typically at the
|
||||
// granualarity of a renderpass. You can enable this along with FVK_DEBUG_DEBUG_UTILS to take
|
||||
// advantage of vkCmdBegin/EndDebugUtilsLabelEXT. You can also just enable this with
|
||||
// FVK_DEBUG_PRINT_GROUP_MARKERS to print the current marker to stdout.
|
||||
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
|
||||
|
||||
#define FVK_DEBUG_TEXTURE 0x00000004
|
||||
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
|
||||
#define FVK_DEBUG_COMMAND_BUFFER 0x00000010
|
||||
@@ -63,9 +66,11 @@
|
||||
// Enable the debug utils extension if it is available.
|
||||
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
|
||||
|
||||
// Use this to debug potential Handle/Resource leakage. It will print out reference counts for all
|
||||
// the currently active resources.
|
||||
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
|
||||
|
||||
// Usefaul default combinations
|
||||
// Useful default combinations
|
||||
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
#define FVK_DEBUG_PERFORMANCE \
|
||||
FVK_DEBUG_SYSTRACE
|
||||
|
||||
@@ -209,10 +209,16 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||
mPlatform->getDevice())),
|
||||
mContext(context),
|
||||
mResourceAllocator(driverConfig.handleArenaSize),
|
||||
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mResourceManager(&mResourceAllocator),
|
||||
mThreadSafeResourceManager(&mResourceAllocator),
|
||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
|
||||
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
|
||||
mPipelineCache(&mResourceAllocator),
|
||||
mStagePool(mAllocator, &mCommands),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice()),
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
|
||||
|
||||
@@ -237,23 +243,13 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
#endif
|
||||
|
||||
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
|
||||
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
|
||||
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
|
||||
&mResourceAllocator);
|
||||
mCommands->setObserver(&mPipelineCache);
|
||||
mCommands.setObserver(&mPipelineCache);
|
||||
mPipelineCache.setDevice(mPlatform->getDevice(), mAllocator);
|
||||
|
||||
// TOOD: move them all to be initialized by constructor
|
||||
mStagePool.initialize(mAllocator, mCommands.get());
|
||||
mFramebufferCache.initialize(mPlatform->getDevice());
|
||||
mSamplerCache.initialize(mPlatform->getDevice());
|
||||
|
||||
mEmptyTexture.reset(createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
|
||||
mContext, mAllocator, mCommands.get(), mStagePool));
|
||||
mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
|
||||
mContext, mAllocator, &mCommands, mStagePool);
|
||||
|
||||
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
|
||||
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
|
||||
mCommands.get());
|
||||
}
|
||||
|
||||
VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
@@ -316,8 +312,11 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
|
||||
void VulkanDriver::terminate() {
|
||||
// Command buffers should come first since it might have commands depending on resources that
|
||||
// are about to be destroyed.
|
||||
mCommands.reset();
|
||||
mEmptyTexture.reset();
|
||||
mCommands.terminate();
|
||||
|
||||
delete mEmptyTexture;
|
||||
mResourceManager.clear();
|
||||
|
||||
mTimestamps.reset();
|
||||
|
||||
mBlitter.terminate();
|
||||
@@ -346,7 +345,7 @@ void VulkanDriver::terminate() {
|
||||
}
|
||||
|
||||
void VulkanDriver::tick(int) {
|
||||
mCommands->updateFences();
|
||||
mCommands.updateFences();
|
||||
}
|
||||
|
||||
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
|
||||
@@ -358,7 +357,7 @@ void VulkanDriver::collectGarbage() {
|
||||
FVK_SYSTRACE_START("gc");
|
||||
// Command buffers need to be submitted and completed before other resources can be gc'd. And
|
||||
// its gc() function carrys out the *wait*.
|
||||
mCommands->gc();
|
||||
mCommands.gc();
|
||||
mStagePool.gc();
|
||||
mFramebufferCache.gc();
|
||||
|
||||
@@ -386,7 +385,7 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
void VulkanDriver::endFrame(uint32_t frameId) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endframe");
|
||||
mCommands->flush();
|
||||
mCommands.flush();
|
||||
collectGarbage();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
@@ -394,7 +393,7 @@ void VulkanDriver::endFrame(uint32_t frameId) {
|
||||
void VulkanDriver::flush(int) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("flush");
|
||||
mCommands->flush();
|
||||
mCommands.flush();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
@@ -402,8 +401,8 @@ void VulkanDriver::finish(int dummy) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("finish");
|
||||
|
||||
mCommands->flush();
|
||||
mCommands->wait();
|
||||
mCommands.flush();
|
||||
mCommands.wait();
|
||||
|
||||
mReadPixels.runUntilComplete();
|
||||
FVK_SYSTRACE_END();
|
||||
@@ -500,7 +499,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {
|
||||
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
|
||||
format, samples, w, h, depth, usage, mStagePool);
|
||||
mResourceManager.acquire(vktexture);
|
||||
}
|
||||
@@ -512,7 +511,7 @@ void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targ
|
||||
TextureSwizzle swizzleArray[] = {r, g, b, a};
|
||||
const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
|
||||
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
|
||||
format, samples, w, h, depth, usage, mStagePool, false /*heap allocated */, swizzleMap);
|
||||
mResourceManager.acquire(vktexture);
|
||||
}
|
||||
@@ -555,7 +554,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
|
||||
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
|
||||
MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
|
||||
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
|
||||
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
|
||||
@@ -607,7 +606,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
assert_invariant(tmin.x >= width && tmin.y >= height);
|
||||
|
||||
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), width, height,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
|
||||
samples, colorTargets, depthStencil, mStagePool);
|
||||
mResourceManager.acquire(renderTarget);
|
||||
}
|
||||
@@ -625,7 +624,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
VulkanCommandBuffer const& commandBuffer = mCommands->get();
|
||||
VulkanCommandBuffer const& commandBuffer = mCommands.get();
|
||||
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
|
||||
}
|
||||
|
||||
@@ -636,7 +635,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags);
|
||||
mAllocator, &mCommands, mStagePool, nativeWindow, flags);
|
||||
mResourceManager.acquire(swapChain);
|
||||
}
|
||||
|
||||
@@ -649,7 +648,7 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
}
|
||||
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
mAllocator, &mCommands, mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
mResourceManager.acquire(swapChain);
|
||||
}
|
||||
|
||||
@@ -994,7 +993,7 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
|
||||
|
||||
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
|
||||
commands.acquire(ib);
|
||||
ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size);
|
||||
@@ -1004,7 +1003,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
|
||||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
commands.acquire(bo);
|
||||
@@ -1015,7 +1014,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
|
||||
void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& bd, uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
commands.acquire(bo);
|
||||
// TODO: implement unsynchronized version
|
||||
@@ -1180,7 +1179,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
|
||||
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
|
||||
// the non-sampling case.
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
|
||||
UTILS_NOUNROLL
|
||||
@@ -1307,7 +1306,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
|
||||
// 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();
|
||||
auto const topMarker = mCommands.getTopGroupMarker();
|
||||
if (!topMarker.empty()) {
|
||||
DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast<uint64_t>(vkfb),
|
||||
topMarker.c_str());
|
||||
@@ -1396,7 +1395,7 @@ void VulkanDriver::endRenderPass(int) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endRenderPass");
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
|
||||
@@ -1449,7 +1448,7 @@ void VulkanDriver::nextSubpass(int) {
|
||||
assert_invariant(renderTarget);
|
||||
assert_invariant(mCurrentRenderPass.params.subpassMask);
|
||||
|
||||
vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
||||
++mCurrentRenderPass.currentSubpass);
|
||||
@@ -1530,14 +1529,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands->insertEventMarker(string, len);
|
||||
mCommands.insertEventMarker(string, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
|
||||
// Turns out all the markers are 0-terminated, so we can just pass it without len.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands->pushGroupMarker(string);
|
||||
mCommands.pushGroupMarker(string);
|
||||
#endif
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START(string);
|
||||
@@ -1545,7 +1544,7 @@ void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
|
||||
|
||||
void VulkanDriver::popGroupMarker(int) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands->popGroupMarker();
|
||||
mCommands.popGroupMarker();
|
||||
#endif
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_END();
|
||||
@@ -1558,7 +1557,7 @@ void VulkanDriver::stopCapture(int) {}
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& pbd) {
|
||||
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
|
||||
mCommands->flush();
|
||||
mCommands.flush();
|
||||
mReadPixels.run(
|
||||
srcTarget, x, y, width, height, mPlatform->getGraphicsQueueFamilyIndex(),
|
||||
std::move(pbd),
|
||||
@@ -1715,7 +1714,7 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
const VulkanVertexBufferInfo& vbi =
|
||||
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
|
||||
|
||||
@@ -1814,7 +1813,7 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
utils::slog.w << " in material '" << program->name.c_str() << "'";
|
||||
utils::slog.w << " at binding point " << +binding << utils::io::endl;
|
||||
#endif
|
||||
texture = mEmptyTexture.get();
|
||||
texture = mEmptyTexture;
|
||||
}
|
||||
|
||||
SamplerParams const& samplerParams = boundSampler->s;
|
||||
@@ -1861,7 +1860,7 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("bindRenderPrimitive");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
commands->acquire(prim.indexBuffer);
|
||||
@@ -1890,8 +1889,8 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw2");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
|
||||
// Bind new descriptor sets if they need to change.
|
||||
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
|
||||
@@ -1925,8 +1924,8 @@ void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
|
||||
}
|
||||
|
||||
void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
|
||||
// Set scissoring.
|
||||
// clamp left-bottom to 0,0 and avoid overflows
|
||||
@@ -1953,12 +1952,12 @@ void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
|
||||
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
|
||||
mTimestamps->beginQuery(&(mCommands->get()), vtq);
|
||||
mTimestamps->beginQuery(&(mCommands.get()), vtq);
|
||||
}
|
||||
|
||||
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
|
||||
mTimestamps->endQuery(&(mCommands->get()), vtq);
|
||||
mTimestamps->endQuery(&(mCommands.get()), vtq);
|
||||
}
|
||||
|
||||
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
|
||||
@@ -104,9 +104,9 @@ private:
|
||||
void collectGarbage();
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
std::unique_ptr<VulkanCommands> mCommands;
|
||||
std::unique_ptr<VulkanTimestamps> mTimestamps;
|
||||
std::unique_ptr<VulkanTexture> mEmptyTexture;
|
||||
|
||||
VulkanTexture* mEmptyTexture;
|
||||
|
||||
VulkanSwapChain* mCurrentSwapChain = nullptr;
|
||||
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
|
||||
@@ -122,6 +122,7 @@ private:
|
||||
// thread.
|
||||
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
|
||||
|
||||
VulkanCommands mCommands;
|
||||
VulkanPipelineCache mPipelineCache;
|
||||
VulkanStagePool mStagePool;
|
||||
VulkanFboCache mFramebufferCache;
|
||||
|
||||
@@ -64,7 +64,8 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanFboCache::initialize(VkDevice device) noexcept { mDevice = device; }
|
||||
VulkanFboCache::VulkanFboCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VulkanFboCache::~VulkanFboCache() {
|
||||
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
|
||||
@@ -238,7 +239,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.format = config.colorFormat[i],
|
||||
.samples = (VkSampleCountFlagBits) config.samples,
|
||||
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
||||
.storeOp = config.samples == 1 ? kEnableStore : kDisableStore,
|
||||
.storeOp = kEnableStore,
|
||||
.stencilLoadOp = kDontCare,
|
||||
.stencilStoreOp = kDisableStore,
|
||||
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
|
||||
|
||||
@@ -104,10 +104,9 @@ public:
|
||||
bool operator()(const FboKey& k1, const FboKey& k2) const;
|
||||
};
|
||||
|
||||
explicit VulkanFboCache(VkDevice device);
|
||||
~VulkanFboCache();
|
||||
|
||||
void initialize(VkDevice device) noexcept;
|
||||
|
||||
// Retrieves or creates a VkFramebuffer handle.
|
||||
VkFramebuffer getFramebuffer(FboKey config) noexcept;
|
||||
|
||||
|
||||
@@ -77,7 +77,11 @@ private:
|
||||
|
||||
struct PipelineInfo {
|
||||
PipelineInfo()
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
, bindingToName(MAX_SAMPLER_COUNT, "")
|
||||
#endif
|
||||
{}
|
||||
|
||||
// This bitset maps to each of the sampler in the sampler groups associated with this
|
||||
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
|
||||
|
||||
@@ -51,8 +51,8 @@ namespace filament::backend {
|
||||
class VulkanResourceAllocator {
|
||||
|
||||
public:
|
||||
VulkanResourceAllocator(size_t arenaSize)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize)
|
||||
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
|
||||
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
|
||||
|
||||
@@ -95,7 +95,8 @@ constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
void VulkanSamplerCache::initialize(VkDevice device) { mDevice = device; }
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
auto iter = mCache.find(params);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace filament::backend {
|
||||
// Simple manager for VkSampler objects.
|
||||
class VulkanSamplerCache {
|
||||
public:
|
||||
void initialize(VkDevice device);
|
||||
explicit VulkanSamplerCache(VkDevice device);
|
||||
VkSampler getSampler(SamplerParams params) noexcept;
|
||||
void terminate() noexcept;
|
||||
private:
|
||||
|
||||
@@ -26,10 +26,9 @@ static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
void VulkanStagePool::initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept {
|
||||
mAllocator = allocator;
|
||||
mCommands = commands;
|
||||
}
|
||||
VulkanStagePool::VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands)
|
||||
: mAllocator(allocator),
|
||||
mCommands(commands) {}
|
||||
|
||||
VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
// First check if a stage exists whose capacity is greater than or equal to the requested size.
|
||||
|
||||
@@ -45,7 +45,7 @@ struct VulkanStageImage {
|
||||
// This class manages two types of host-mappable staging areas: buffer stages and image stages.
|
||||
class VulkanStagePool {
|
||||
public:
|
||||
void initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept;
|
||||
VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands);
|
||||
|
||||
// Finds or creates a stage whose capacity is at least the given number of bytes.
|
||||
// The stage is automatically released back to the pool after TIME_BEFORE_EVICTION frames.
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <DataReshaper.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <private/backend/BackendUtils.h>
|
||||
|
||||
#include "DataReshaper.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -100,9 +100,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
|
||||
// Filament expects blit() to work with any texture, so we almost always set these usage flags.
|
||||
// TODO: investigate performance implications of setting these flags.
|
||||
const VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) {
|
||||
imageInfo.usage |= blittable;
|
||||
}
|
||||
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
|
||||
@@ -51,7 +51,7 @@ void BackendTest::init(Backend backend, bool isMobilePlatform) {
|
||||
}
|
||||
|
||||
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
||||
CONFIG_COMMAND_BUFFERS_SIZE) {
|
||||
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
||||
initializeDriver();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ void ComputeTest::init(Backend backend) {
|
||||
}
|
||||
|
||||
ComputeTest::ComputeTest()
|
||||
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
|
||||
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE,
|
||||
/*paused=*/false) {
|
||||
}
|
||||
|
||||
ComputeTest::~ComputeTest() = default;
|
||||
|
||||
@@ -226,9 +226,9 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, 0 }, {}, {});
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
|
||||
}
|
||||
|
||||
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
@@ -301,9 +301,9 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, 0 }, {}, {});
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
|
||||
}
|
||||
|
||||
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
@@ -369,12 +369,12 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
|
||||
// Create a 4-sample render target with the 4-sample texture.
|
||||
Handle<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount,
|
||||
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
|
||||
{{ srcColorTexture }}, {}, {});
|
||||
|
||||
// Create a 1-sample render target with the 1-sample texture.
|
||||
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1,
|
||||
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
|
||||
{{ dstColorTexture }}, {}, {});
|
||||
|
||||
// Prep for rendering.
|
||||
@@ -473,9 +473,9 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
// Create two RenderTargets.
|
||||
const int level = 0;
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, kSrcTexLayer }, {}, {});
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {});
|
||||
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, kDstTexLayer }, {}, {});
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {});
|
||||
|
||||
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
|
||||
const int srcLevel = 0;
|
||||
@@ -565,10 +565,10 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
// that this case is handled correctly.
|
||||
Handle<HwRenderTarget> srcRenderTarget =
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
|
||||
srcRect.height, 1, {srcTexture, kSrcLevel, 0}, {}, {});
|
||||
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {});
|
||||
Handle<HwRenderTarget> dstRenderTarget =
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, {dstTexture, kDstLevel, 0}, {}, {});
|
||||
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {});
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
|
||||
SamplerMagFilter::LINEAR);
|
||||
@@ -637,7 +637,7 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
}
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
|
||||
@@ -216,7 +216,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
|
||||
auto colorTexture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
|
||||
auto renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, {{colorTexture}}, {}, {});
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {});
|
||||
|
||||
// Upload uniforms for the first triangle.
|
||||
{
|
||||
|
||||
@@ -160,7 +160,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
slog.i << "Level " << int(level) << ": " <<
|
||||
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
|
||||
renderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kTexWidth >> level, kTexHeight >> level, 1, { texture, level, 0 }, {}, {});
|
||||
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
|
||||
}
|
||||
|
||||
// Fill the base level of the texture with interesting colors.
|
||||
|
||||
@@ -105,7 +105,8 @@ TEST_F(BackendTest, MRT) {
|
||||
512, // width
|
||||
512, // height
|
||||
1, // samples
|
||||
{{textureA },{textureB }}, // color
|
||||
0, // layerCount
|
||||
{{textureA },{textureB }}, // color
|
||||
{}, // depth
|
||||
{}); // stencil
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
// Render a white triangle into level 2.
|
||||
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
|
||||
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, 32, 32, 1,
|
||||
TargetBufferFlags::COLOR, 32, 32, 1, 0,
|
||||
{texture, 2 /* level */, 0 /* layer */}, {}, {});
|
||||
{
|
||||
RenderPassParams params = {};
|
||||
|
||||
@@ -279,7 +279,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
// level (at least for OpenGL).
|
||||
renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), t.samples, {{ texture, uint8_t(t.mipLevel) }}, {},
|
||||
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
|
||||
{});
|
||||
}
|
||||
|
||||
@@ -318,9 +318,8 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
// correct mip.
|
||||
RenderPassParams p = params;
|
||||
Handle<HwRenderTarget> mipLevelOneRT = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1,
|
||||
{{ texture }}, {},
|
||||
{});
|
||||
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
|
||||
{{ texture }}, {}, {});
|
||||
p.clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||
getDriverApi().beginRenderPass(mipLevelOneRT, p);
|
||||
getDriverApi().endRenderPass();
|
||||
@@ -402,6 +401,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
renderTargetSize, // width
|
||||
renderTargetSize, // height
|
||||
1, // samples
|
||||
0, // layerCount
|
||||
{{ texture }}, // color
|
||||
{}, // depth
|
||||
{}); // stencil
|
||||
|
||||
@@ -113,11 +113,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
// We purposely set the render target width and height to smaller than the texture, to check
|
||||
// that this case is handled correctly.
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1,
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1, 0,
|
||||
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
|
||||
|
||||
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1,
|
||||
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
|
||||
{srcTexture, kSrcLevel, 0}, {}, {});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
@@ -208,7 +208,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
(uint32_t)std::numeric_limits<int32_t>::max()};
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, 512, 512, 1,
|
||||
TargetBufferFlags::COLOR, 512, 512, 1, 0,
|
||||
{srcTexture, 0, 0}, {}, {});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
@@ -150,7 +150,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
auto stencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT);
|
||||
auto renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1,
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {}, {{stencilTexture}});
|
||||
|
||||
RunTest(renderTarget);
|
||||
@@ -174,7 +174,7 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
auto depthStencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
|
||||
auto renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1,
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
|
||||
|
||||
RunTest(renderTarget);
|
||||
@@ -202,10 +202,10 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
|
||||
auto renderTarget0 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
|
||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
auto renderTarget1 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
|
||||
api.startCapture(0);
|
||||
@@ -273,4 +273,4 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
api.destroyRenderTarget(renderTarget1);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace test
|
||||
|
||||
@@ -335,6 +335,11 @@ public:
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
*/
|
||||
uint32_t resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
bool disableHandleUseAfterFreeCheck = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -402,6 +407,14 @@ public:
|
||||
*/
|
||||
Builder& featureLevel(FeatureLevel featureLevel) noexcept;
|
||||
|
||||
/**
|
||||
* Warning: This is an experimental API. See Engine::setPaused(bool) for caveats.
|
||||
*
|
||||
* @param paused Whether to start the rendering thread paused.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
Builder& paused(bool paused) noexcept;
|
||||
|
||||
#if UTILS_HAS_THREADING
|
||||
/**
|
||||
* Creates the filament Engine asynchronously.
|
||||
@@ -827,6 +840,21 @@ public:
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Pause or resume rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API. In particular, note the following caveats.
|
||||
*
|
||||
* <ul><li>
|
||||
* Buffer callbacks will never be called as long as the rendering thread is paused.
|
||||
* Do not rely on a buffer callback to unpause the thread.
|
||||
* </li><li>
|
||||
* While the rendering thread is paused, rendering commands will continue to be queued until the
|
||||
* buffer limit is reached. When the limit is reached, the program will abort.
|
||||
* </li></ul>
|
||||
*/
|
||||
void setPaused(bool paused);
|
||||
|
||||
/**
|
||||
* Drains the user callback message queue and immediately execute all pending callbacks.
|
||||
*
|
||||
|
||||
@@ -156,6 +156,15 @@ public:
|
||||
*/
|
||||
static constexpr uint8_t DEFAULT_CHANNEL = 2u;
|
||||
|
||||
/**
|
||||
* Type of geometry for a Renderable
|
||||
*/
|
||||
enum class GeometryType : uint8_t {
|
||||
DYNAMIC, //!< dynamic gemoetry has no restriction
|
||||
STATIC_BOUNDS, //!< bounds and world space transform are immutable
|
||||
STATIC //!< skinning/morphing not allowed and Vertex/IndexBuffer immutables
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a builder for renderable components.
|
||||
*
|
||||
@@ -204,6 +213,17 @@ public:
|
||||
VertexBuffer* UTILS_NONNULL vertices,
|
||||
IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload
|
||||
|
||||
|
||||
/**
|
||||
* Specify the type of geometry for this renderable. DYNAMIC geometry has no restriction,
|
||||
* STATIC_BOUNDS geometry means that both the bounds and the world-space transform of the
|
||||
* the renderable are immutable.
|
||||
* STATIC geometry has the same restrictions as STATIC_BOUNDS, but in addition disallows
|
||||
* skinning, morphing and changing the VertexBuffer or IndexBuffer in any way.
|
||||
* @param enable whether this renderable has static bounds. false by default.
|
||||
*/
|
||||
Builder& geometryType(GeometryType type) noexcept;
|
||||
|
||||
/**
|
||||
* Binds a material instance to the specified primitive.
|
||||
*
|
||||
@@ -603,11 +623,12 @@ public:
|
||||
|
||||
/**
|
||||
* Changes the bounding box used for frustum culling.
|
||||
* The renderable must not have staticGeometry enabled.
|
||||
*
|
||||
* \see Builder::boundingBox()
|
||||
* \see RenderableManager::getAxisAlignedBoundingBox()
|
||||
*/
|
||||
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept;
|
||||
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb);
|
||||
|
||||
/**
|
||||
* Changes the visibility bits.
|
||||
|
||||
@@ -295,6 +295,11 @@ utils::JobSystem& Engine::getJobSystem() noexcept {
|
||||
return downcast(this)->getJobSystem();
|
||||
}
|
||||
|
||||
void Engine::setPaused(bool paused) {
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
|
||||
downcast(this)->setPaused(paused);
|
||||
}
|
||||
|
||||
DebugRegistry& Engine::getDebugRegistry() noexcept {
|
||||
return downcast(this)->getDebugRegistry();
|
||||
}
|
||||
|
||||
@@ -33,18 +33,17 @@ namespace filament {
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
size_t HwRenderPrimitiveFactory::KeyHash::operator()(
|
||||
HwRenderPrimitiveFactory::Key const& p) const noexcept {
|
||||
return utils::hash::combine(
|
||||
p.params.vbh.getId(),
|
||||
utils::hash::combine(p.params.ibh.getId(), (size_t)p.params.type));
|
||||
size_t HwRenderPrimitiveFactory::Parameters::hash() const noexcept {
|
||||
return utils::hash::combine(vbh.getId(),
|
||||
utils::hash::combine(ibh.getId(),
|
||||
(size_t)type));
|
||||
}
|
||||
|
||||
bool operator==(HwRenderPrimitiveFactory::Key const& lhs,
|
||||
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
|
||||
return lhs.params.vbh == rhs.params.vbh &&
|
||||
lhs.params.ibh == rhs.params.ibh &&
|
||||
lhs.params.type == rhs.params.type;
|
||||
bool operator==(HwRenderPrimitiveFactory::Parameters const& lhs,
|
||||
HwRenderPrimitiveFactory::Parameters const& rhs) noexcept {
|
||||
return lhs.vbh == rhs.vbh &&
|
||||
lhs.ibh == rhs.ibh &&
|
||||
lhs.type == rhs.type;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -67,7 +66,7 @@ auto HwRenderPrimitiveFactory::create(DriverApi& driver,
|
||||
backend::PrimitiveType type) noexcept -> Handle {
|
||||
|
||||
// see if we already have seen this RenderPrimitive
|
||||
Key const key{{ vbh, ibh, type }, 1 };
|
||||
Key const key({ vbh, ibh, type });
|
||||
auto pos = mBimap.find(key);
|
||||
|
||||
// the common case is that we've never seen it (i.e.: no reuse)
|
||||
@@ -77,7 +76,7 @@ auto HwRenderPrimitiveFactory::create(DriverApi& driver,
|
||||
return handle;
|
||||
}
|
||||
|
||||
pos->first.pKey->refs++;
|
||||
++(pos->first.pKey->refs);
|
||||
return pos->second.handle;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,11 @@ public:
|
||||
backend::VertexBufferHandle vbh; // 4
|
||||
backend::IndexBufferHandle ibh; // 4
|
||||
backend::PrimitiveType type; // 4
|
||||
size_t hash() const noexcept;
|
||||
};
|
||||
|
||||
friend bool operator==(Parameters const& lhs, Parameters const& rhs) noexcept;
|
||||
|
||||
Handle create(backend::DriverApi& driver,
|
||||
backend::VertexBufferHandle vbh,
|
||||
backend::IndexBufferHandle ibh,
|
||||
@@ -63,29 +66,36 @@ public:
|
||||
|
||||
private:
|
||||
struct Key {
|
||||
// The key should not be copyable, unfortunately due to how the Bimap works we have
|
||||
// to copy-construct it once.
|
||||
Key(Key const&) = default;
|
||||
Key& operator=(Key const&) = delete;
|
||||
Key& operator=(Key&&) noexcept = delete;
|
||||
explicit Key(Parameters const& params) : params(params), refs(1) { }
|
||||
Parameters params;
|
||||
mutable uint32_t refs; // 4 bytes
|
||||
bool operator==(Key const& rhs) const noexcept {
|
||||
return params == rhs.params;
|
||||
}
|
||||
};
|
||||
|
||||
struct KeyHash {
|
||||
size_t operator()(Key const& p) const noexcept;
|
||||
struct KeyHasher {
|
||||
size_t operator()(Key const& p) const noexcept {
|
||||
return p.params.hash();
|
||||
}
|
||||
};
|
||||
|
||||
friend bool operator==(Key const& lhs, Key const& rhs) noexcept;
|
||||
|
||||
|
||||
struct Value { // 4 bytes
|
||||
Handle handle;
|
||||
};
|
||||
|
||||
struct ValueHash {
|
||||
size_t operator()(Value const& p) const noexcept {
|
||||
std::hash<Handle::HandleId> const h;
|
||||
return h(p.handle.getId());
|
||||
struct ValueHasher {
|
||||
size_t operator()(Value const v) const noexcept {
|
||||
return std::hash<Handle::HandleId>()(v.handle.getId());
|
||||
}
|
||||
};
|
||||
|
||||
friend bool operator==(Value const& lhs, Value const& rhs) noexcept {
|
||||
friend bool operator==(Value const lhs, Value const rhs) noexcept {
|
||||
return lhs.handle == rhs.handle;
|
||||
}
|
||||
|
||||
@@ -104,7 +114,7 @@ private:
|
||||
PoolAllocatorArena mArena;
|
||||
|
||||
// The special Bimap
|
||||
Bimap<Key, Value, KeyHash, ValueHash,
|
||||
Bimap<Key, Value, KeyHasher, ValueHasher,
|
||||
utils::STLAllocator<Key, PoolAllocatorArena>> mBimap;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <private/backend/DriverApi.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Hash.h>
|
||||
@@ -66,7 +65,7 @@ auto HwVertexBufferInfoFactory::create(DriverApi& driver,
|
||||
uint8_t attributeCount,
|
||||
backend::AttributeArray attributes) noexcept -> Handle {
|
||||
|
||||
Key const key{{ bufferCount, attributeCount, {}, attributes }, 1 };
|
||||
Key const key({ bufferCount, attributeCount, {}, attributes });
|
||||
auto pos = mBimap.find(key);
|
||||
|
||||
// the common case is that we've never seen it (i.e.: no reuse)
|
||||
|
||||
@@ -67,6 +67,12 @@ public:
|
||||
|
||||
private:
|
||||
struct Key { // 140 bytes
|
||||
// The key should not be copyable, unfortunately due to how the Bimap works we have
|
||||
// to copy-construct it once.
|
||||
Key(Key const&) = default;
|
||||
Key& operator=(Key const&) = delete;
|
||||
Key& operator=(Key&&) noexcept = delete;
|
||||
explicit Key(Parameters const& params) : params(params), refs(1) { }
|
||||
Parameters params;
|
||||
mutable uint32_t refs; // 4 bytes
|
||||
bool operator==(Key const& rhs) const noexcept {
|
||||
@@ -85,13 +91,12 @@ private:
|
||||
};
|
||||
|
||||
struct ValueHasher {
|
||||
size_t operator()(Value v) const noexcept {
|
||||
std::hash<Handle::HandleId> const hasher;
|
||||
return hasher(v.handle.getId());
|
||||
size_t operator()(Value const v) const noexcept {
|
||||
return std::hash<Handle::HandleId>()(v.handle.getId());
|
||||
}
|
||||
};
|
||||
|
||||
friend bool operator==(Value const& lhs, Value const& rhs) noexcept {
|
||||
friend bool operator==(Value const lhs, Value const rhs) noexcept {
|
||||
return lhs.handle == rhs.handle;
|
||||
}
|
||||
|
||||
|
||||
@@ -3292,8 +3292,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
SamplerMagFilter filterMag,
|
||||
SamplerMinFilter filterMin) noexcept {
|
||||
|
||||
assert_invariant(fg.getDescriptor(input).depth > 1);
|
||||
assert_invariant(fg.getDescriptor(input).type == SamplerType::SAMPLER_2D_ARRAY);
|
||||
auto& inputTextureDesc = fg.getDescriptor(input);
|
||||
assert_invariant(inputTextureDesc.depth > 1);
|
||||
assert_invariant(inputTextureDesc.type == SamplerType::SAMPLER_2D_ARRAY);
|
||||
|
||||
// TODO: add support for sub-resources
|
||||
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
|
||||
@@ -3329,14 +3330,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
.filterMag = filterMag,
|
||||
.filterMin = filterMin
|
||||
});
|
||||
mi->setParameter("layerIndex", 0);
|
||||
mi->setParameter("viewport", float4{
|
||||
float(vp.left) / inputDesc.width,
|
||||
float(vp.bottom) / inputDesc.height,
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
@@ -3347,18 +3346,19 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
|
||||
// Blit the scene rendered to the first layer to the left half of the screen.
|
||||
out.params.viewport.width /= 2;
|
||||
render(out, pipeline, driver);
|
||||
// The width of each view takes up 1/depth of the screen width.
|
||||
out.params.viewport.width /= inputTextureDesc.depth;
|
||||
|
||||
// Blit the scene rendered to the second layer to the right half of the screen.
|
||||
// Don't clear or discard the target to keep the previously rendered left half image.
|
||||
mi->setParameter("layerIndex", 1);
|
||||
mi->commit(driver);
|
||||
out.params.flags.clear = filament::backend::TargetBufferFlags::NONE;
|
||||
out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE;
|
||||
out.params.viewport.left += out.params.viewport.width;
|
||||
render(out, pipeline, driver);
|
||||
// Render all layers of the texture to the screen side-by-side.
|
||||
for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) {
|
||||
mi->setParameter("layerIndex", i);
|
||||
mi->commit(driver);
|
||||
render(out, pipeline, driver);
|
||||
// From the second draw, don't clear the targetbuffer.
|
||||
out.params.flags.clear = filament::backend::TargetBufferFlags::NONE;
|
||||
out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE;
|
||||
out.params.viewport.left += out.params.viewport.width;
|
||||
}
|
||||
});
|
||||
|
||||
return ppQuadBlit->output;
|
||||
|
||||
@@ -184,8 +184,7 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
Command* curr = commands.data();
|
||||
size_t const commandCount = commands.size();
|
||||
|
||||
auto stereoscopicEyeCount =
|
||||
renderFlags & IS_STEREOSCOPIC ? engine.getConfig().stereoscopicEyeCount : 1;
|
||||
auto stereoscopicEyeCount = engine.getConfig().stereoscopicEyeCount;
|
||||
|
||||
const float3 cameraPosition(mCameraPosition);
|
||||
const float3 cameraForwardVector(mCameraForwardVector);
|
||||
@@ -521,7 +520,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
|
||||
const bool hasShadowing = renderFlags & HAS_SHADOWING;
|
||||
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
|
||||
const bool hasStereo = renderFlags & IS_STEREOSCOPIC;
|
||||
const bool hasInstancedStereo = renderFlags & IS_INSTANCED_STEREOSCOPIC;
|
||||
|
||||
Command cmdColor;
|
||||
|
||||
@@ -590,7 +589,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// soaInstanceInfo[i].count is the number of instances the user has requested, either for
|
||||
// manual or hybrid instancing. Instanced stereo multiplies the number of instances by the
|
||||
// eye count.
|
||||
if (UTILS_UNLIKELY(hasStereo)) {
|
||||
if (UTILS_UNLIKELY(hasInstancedStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
@@ -624,7 +623,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
cmdDepth.primitive.skinningTexture = skinning.handleSampler;
|
||||
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
|
||||
|
||||
if (UTILS_UNLIKELY(hasStereo)) {
|
||||
if (UTILS_UNLIKELY(hasInstancedStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
|
||||
@@ -275,9 +275,9 @@ public:
|
||||
"Command isn't trivially destructible");
|
||||
|
||||
using RenderFlags = uint8_t;
|
||||
static constexpr RenderFlags HAS_SHADOWING = 0x01;
|
||||
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
|
||||
static constexpr RenderFlags IS_STEREOSCOPIC = 0x04;
|
||||
static constexpr RenderFlags HAS_SHADOWING = 0x01;
|
||||
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
|
||||
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
|
||||
|
||||
// Arena used for commands
|
||||
using Arena = utils::Arena<
|
||||
|
||||
@@ -56,7 +56,7 @@ void RenderableManager::destroy(utils::Entity e) noexcept {
|
||||
return downcast(this)->destroy(e);
|
||||
}
|
||||
|
||||
void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept {
|
||||
void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) {
|
||||
downcast(this)->setAxisAlignedBoundingBox(instance, aabb);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
|
||||
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
|
||||
uint8_t layerCount = 0;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
@@ -124,7 +125,9 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
// depth auto-resolve, in which case we must allocate the depth
|
||||
// buffer with MS and manually resolve it (see "Resolved Depth Buffer"
|
||||
// pass).
|
||||
.depth = colorBufferDesc.depth,
|
||||
.samples = canAutoResolveDepth ? colorBufferDesc.samples : uint8_t(config.msaa),
|
||||
.type = colorBufferDesc.type,
|
||||
.format = format,
|
||||
});
|
||||
if (config.enabledStencilBuffer) {
|
||||
@@ -150,7 +153,17 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
if (engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
|
||||
// Add sampleable usage flag for depth in multiview rendering, othewise it's
|
||||
// treated as renderbuffer in the backend and crashed.
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT |
|
||||
FrameGraphTexture::Usage::SAMPLEABLE);
|
||||
layerCount = engine.getConfig().stereoscopicEyeCount;
|
||||
} else {
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a bit of magic happening here regarding the viewport used.
|
||||
@@ -172,7 +185,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
.stencil = data.stencil },
|
||||
.clearColor = config.clearColor,
|
||||
.samples = config.msaa,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags });
|
||||
.layerCount = layerCount,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
[=, &view, &engine](FrameGraphResources const& resources,
|
||||
|
||||
@@ -129,10 +129,10 @@ void ResourceAllocator::terminate() noexcept {
|
||||
|
||||
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, MRT color, TargetBufferInfo depth,
|
||||
uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth,
|
||||
TargetBufferInfo stencil) noexcept {
|
||||
return mBackend.createRenderTarget(targetBufferFlags,
|
||||
width, height, samples ? samples : 1u, color, depth, stencil);
|
||||
width, height, samples ? samples : 1u, layerCount, color, depth, stencil);
|
||||
}
|
||||
|
||||
void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint8_t samples,
|
||||
uint8_t layerCount,
|
||||
backend::MRT color,
|
||||
backend::TargetBufferInfo depth,
|
||||
backend::TargetBufferInfo stencil) noexcept = 0;
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint8_t samples,
|
||||
uint8_t layerCount,
|
||||
backend::MRT color,
|
||||
backend::TargetBufferInfo depth,
|
||||
backend::TargetBufferInfo stencil) noexcept override;
|
||||
|
||||
@@ -253,7 +253,7 @@ float3 PBRNeutralToneMapper::operator()(math::float3 color) const noexcept {
|
||||
color *= newPeak / peak;
|
||||
|
||||
float g = 1.0f - 1.0f / (desaturation * (peak - newPeak) + 1.0f);
|
||||
return mix(color, float3(1.0f), g);
|
||||
return mix(color, float3(newPeak), g);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -58,6 +58,7 @@ struct RenderableManager::BuilderDetails {
|
||||
bool mScreenSpaceContactShadows : 1;
|
||||
bool mSkinningBufferMode : 1;
|
||||
bool mFogEnabled : 1;
|
||||
RenderableManager::Builder::GeometryType mGeometryType : 2;
|
||||
size_t mSkinningBoneCount = 0;
|
||||
size_t mMorphTargetCount = 0;
|
||||
Bone const* mUserBones = nullptr;
|
||||
@@ -75,7 +76,9 @@ struct RenderableManager::BuilderDetails {
|
||||
explicit BuilderDetails(size_t count)
|
||||
: mEntries(count), mCulling(true), mCastShadows(false),
|
||||
mReceiveShadows(true), mScreenSpaceContactShadows(false),
|
||||
mSkinningBufferMode(false), mFogEnabled(true), mBonePairs() {
|
||||
mSkinningBufferMode(false), mFogEnabled(true),
|
||||
mGeometryType(RenderableManager::Builder::GeometryType::DYNAMIC),
|
||||
mBonePairs() {
|
||||
}
|
||||
// this is only needed for the explicit instantiation below
|
||||
BuilderDetails() = default;
|
||||
@@ -121,6 +124,11 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index,
|
||||
return *this;
|
||||
}
|
||||
|
||||
RenderableManager::Builder& RenderableManager::Builder::geometryType(GeometryType type) noexcept {
|
||||
mImpl->mGeometryType = type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RenderableManager::Builder& RenderableManager::Builder::material(size_t index,
|
||||
MaterialInstance const* materialInstance) noexcept {
|
||||
if (index < mImpl->mEntries.size()) {
|
||||
@@ -388,11 +396,21 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
|
||||
ASSERT_PRECONDITION(mImpl->mSkinningBoneCount <= CONFIG_MAX_BONE_COUNT,
|
||||
"bone count > %u", CONFIG_MAX_BONE_COUNT);
|
||||
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount <= CONFIG_MAX_INSTANCES || !mImpl->mInstanceBuffer,
|
||||
"instance count is %zu, but instance count is limited to CONFIG_MAX_INSTANCES (%zu) "
|
||||
"instances when supplying transforms via an InstanceBuffer.",
|
||||
mImpl->mInstanceCount,
|
||||
CONFIG_MAX_INSTANCES);
|
||||
|
||||
if (mImpl->mGeometryType == GeometryType::STATIC) {
|
||||
ASSERT_PRECONDITION(mImpl->mSkinningBoneCount > 0,
|
||||
"Skinning can't be used with STATIC geometry");
|
||||
|
||||
ASSERT_PRECONDITION(mImpl->mMorphTargetCount > 0,
|
||||
"Morphing can't be used with STATIC geometry");
|
||||
}
|
||||
|
||||
if (mImpl->mInstanceBuffer) {
|
||||
size_t const bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount;
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount,
|
||||
@@ -519,6 +537,8 @@ void FRenderableManager::create(
|
||||
setSkinning(ci, false);
|
||||
setMorphing(ci, builder->mMorphTargetCount);
|
||||
setFogEnabled(ci, builder->mFogEnabled);
|
||||
// do this after calling setAxisAlignedBoundingBox
|
||||
static_cast<Visibility&>(mManager[ci].visibility).geometryType = builder->mGeometryType;
|
||||
mManager[ci].channels = builder->mLightChannels;
|
||||
|
||||
InstancesInfo& instances = manager[ci].instances;
|
||||
|
||||
@@ -22,22 +22,30 @@
|
||||
#include "HwRenderPrimitiveFactory.h"
|
||||
#include "UniformBuffer.h"
|
||||
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
#include <details/InstanceBuffer.h>
|
||||
|
||||
#include <filament/Box.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
|
||||
#include <details/InstanceBuffer.h>
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/SingleInstanceComponentManager.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/Range.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FBufferObject;
|
||||
@@ -49,9 +57,12 @@ class FSkinningBuffer;
|
||||
class FVertexBuffer;
|
||||
class FTexture;
|
||||
|
||||
class MorphTargetBuffer;
|
||||
|
||||
class FRenderableManager : public RenderableManager {
|
||||
public:
|
||||
using Instance = RenderableManager::Instance;
|
||||
using GeometryType = RenderableManager::Builder::GeometryType;
|
||||
|
||||
// TODO: consider renaming, this pertains to material variants, not strictly visibility.
|
||||
struct Visibility {
|
||||
@@ -60,11 +71,13 @@ public:
|
||||
bool castShadows : 1;
|
||||
bool receiveShadows : 1;
|
||||
bool culling : 1;
|
||||
|
||||
bool skinning : 1;
|
||||
bool morphing : 1;
|
||||
bool screenSpaceContactShadows : 1;
|
||||
bool reversedWindingOrder : 1;
|
||||
bool fog : 1;
|
||||
GeometryType geometryType : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Visibility) == sizeof(uint16_t), "Visibility should be 16 bits");
|
||||
@@ -115,7 +128,7 @@ public:
|
||||
|
||||
void destroy(utils::Entity e) noexcept;
|
||||
|
||||
inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept;
|
||||
inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb);
|
||||
|
||||
inline void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept;
|
||||
|
||||
@@ -136,13 +149,13 @@ public:
|
||||
|
||||
inline void setPrimitives(Instance instance, utils::Slice<FRenderPrimitive> const& primitives) noexcept;
|
||||
|
||||
inline void setSkinning(Instance instance, bool enable) noexcept;
|
||||
inline void setSkinning(Instance instance, bool enable);
|
||||
void setBones(Instance instance, Bone const* transforms, size_t boneCount, size_t offset = 0);
|
||||
void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount, size_t offset = 0);
|
||||
void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer,
|
||||
size_t count, size_t offset);
|
||||
|
||||
inline void setMorphing(Instance instance, bool enable) noexcept;
|
||||
inline void setMorphing(Instance instance, bool enable);
|
||||
void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset);
|
||||
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
|
||||
@@ -295,8 +308,12 @@ private:
|
||||
|
||||
FILAMENT_DOWNCAST(RenderableManager)
|
||||
|
||||
void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept {
|
||||
void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) {
|
||||
if (instance) {
|
||||
ASSERT_PRECONDITION(
|
||||
static_cast<Visibility const&>(
|
||||
mManager[instance].visibility).geometryType == GeometryType::DYNAMIC,
|
||||
"This renderable has staticBounds enabled; its AABB cannot change.");
|
||||
mManager[instance].aabb = aabb;
|
||||
}
|
||||
}
|
||||
@@ -368,16 +385,26 @@ bool FRenderableManager::getFogEnabled(RenderableManager::Instance instance) con
|
||||
return getVisibility(instance).fog;
|
||||
}
|
||||
|
||||
void FRenderableManager::setSkinning(Instance instance, bool enable) noexcept {
|
||||
void FRenderableManager::setSkinning(Instance instance, bool enable) {
|
||||
if (instance) {
|
||||
Visibility& visibility = mManager[instance].visibility;
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
visibility.geometryType != GeometryType::STATIC || !enable,
|
||||
"Skinning can't be used with STATIC geometry");
|
||||
|
||||
visibility.skinning = enable;
|
||||
}
|
||||
}
|
||||
|
||||
void FRenderableManager::setMorphing(Instance instance, bool enable) noexcept {
|
||||
void FRenderableManager::setMorphing(Instance instance, bool enable) {
|
||||
if (instance) {
|
||||
Visibility& visibility = mManager[instance].visibility;
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
visibility.geometryType != GeometryType::STATIC || !enable,
|
||||
"Morphing can't be used with STATIC geometry");
|
||||
|
||||
visibility.morphing = enable;
|
||||
}
|
||||
}
|
||||
@@ -446,22 +473,22 @@ FRenderableManager::getInstancesInfo(Instance instance) const noexcept {
|
||||
}
|
||||
|
||||
utils::Slice<FRenderPrimitive> const& FRenderableManager::getRenderPrimitives(
|
||||
Instance instance, uint8_t level) const noexcept {
|
||||
Instance instance, UTILS_UNUSED uint8_t level) const noexcept {
|
||||
return mManager[instance].primitives;
|
||||
}
|
||||
|
||||
utils::Slice<FRenderPrimitive>& FRenderableManager::getRenderPrimitives(
|
||||
Instance instance, uint8_t level) noexcept {
|
||||
Instance instance, UTILS_UNUSED uint8_t level) noexcept {
|
||||
return mManager[instance].primitives;
|
||||
}
|
||||
|
||||
utils::Slice<FRenderableManager::MorphTargets> const& FRenderableManager::getMorphTargets(
|
||||
Instance instance, uint8_t level) const noexcept {
|
||||
Instance instance, UTILS_UNUSED uint8_t level) const noexcept {
|
||||
return mManager[instance].morphTargets;
|
||||
}
|
||||
|
||||
utils::Slice<FRenderableManager::MorphTargets>& FRenderableManager::getMorphTargets(
|
||||
Instance instance, uint8_t level) noexcept {
|
||||
Instance instance, UTILS_UNUSED uint8_t level) noexcept {
|
||||
return mManager[instance].morphTargets;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ struct Engine::BuilderDetails {
|
||||
Engine::Config mConfig;
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
void* mSharedContext = nullptr;
|
||||
bool mPaused = false;
|
||||
static Config validateConfig(const Config* pConfig) noexcept;
|
||||
};
|
||||
|
||||
@@ -100,7 +101,8 @@ Engine* FEngine::create(Engine::Builder const& builder) {
|
||||
DriverConfig const driverConfig{
|
||||
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
||||
.textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize,
|
||||
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile
|
||||
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile,
|
||||
.disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -200,7 +202,8 @@ FEngine::FEngine(Engine::Builder const& builder) :
|
||||
mCameraManager(*this),
|
||||
mCommandBufferQueue(
|
||||
builder->mConfig.minCommandBufferSizeMB * MiB,
|
||||
builder->mConfig.commandBufferSizeMB * MiB),
|
||||
builder->mConfig.commandBufferSizeMB * MiB,
|
||||
builder->mPaused),
|
||||
mPerRenderPassArena(
|
||||
"FEngine::mPerRenderPassAllocator",
|
||||
builder->mConfig.perRenderPassArenaSizeMB * MiB),
|
||||
@@ -339,7 +342,7 @@ void FEngine::init() {
|
||||
if (UTILS_UNLIKELY(mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0)) {
|
||||
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL0_DATA, MATERIALS_DEFAULTMATERIAL0_SIZE);
|
||||
MATERIALS_DEFAULTMATERIAL_FL0_DATA, MATERIALS_DEFAULTMATERIAL_FL0_SIZE);
|
||||
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast<FEngine*>(this)));
|
||||
} else
|
||||
#endif
|
||||
@@ -347,8 +350,20 @@ void FEngine::init() {
|
||||
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
|
||||
|
||||
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE);
|
||||
switch (mConfig.stereoscopicType) {
|
||||
case StereoscopicType::INSTANCED:
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE);
|
||||
break;
|
||||
case StereoscopicType::MULTIVIEW:
|
||||
#ifdef FILAMENT_ENABLE_MULTIVIEW
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL_MULTIVIEW_DATA, MATERIALS_DEFAULTMATERIAL_MULTIVIEW_SIZE);
|
||||
#else
|
||||
assert_invariant(false);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast<FEngine*>(this)));
|
||||
|
||||
float3 dummyPositions[1] = {};
|
||||
@@ -655,7 +670,8 @@ int FEngine::loop() {
|
||||
DriverConfig const driverConfig {
|
||||
.handleArenaSize = getRequestedDriverHandleArenaSize(),
|
||||
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize,
|
||||
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile
|
||||
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile,
|
||||
.disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
@@ -1186,6 +1202,10 @@ void FEngine::destroy(FEngine* engine) {
|
||||
}
|
||||
}
|
||||
|
||||
void FEngine::setPaused(bool paused) {
|
||||
mCommandBufferQueue.setPaused(paused);
|
||||
}
|
||||
|
||||
Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
|
||||
FEngine::DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi();
|
||||
return driver.getFeatureLevel();
|
||||
@@ -1239,6 +1259,11 @@ Engine::Builder& Engine::Builder::sharedContext(void* sharedContext) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Engine::Builder& Engine::Builder::paused(bool paused) noexcept {
|
||||
mImpl->mPaused = paused;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if UTILS_HAS_THREADING
|
||||
|
||||
void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
|
||||
|
||||
@@ -340,6 +340,8 @@ public:
|
||||
|
||||
void destroy(utils::Entity e);
|
||||
|
||||
void setPaused(bool paused);
|
||||
|
||||
void flushAndWait();
|
||||
|
||||
// flush the current buffer
|
||||
@@ -588,6 +590,9 @@ public:
|
||||
struct {
|
||||
bool debug_froxel_visualization = false;
|
||||
} lighting;
|
||||
struct {
|
||||
bool combine_multiview_images = true;
|
||||
} stereo;
|
||||
matdbg::DebugServer* server = nullptr;
|
||||
} debug;
|
||||
};
|
||||
|
||||
@@ -641,6 +641,9 @@ void FMaterial::getSurfaceProgramSlow(Variant variant,
|
||||
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
pb.multiview(
|
||||
mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW &&
|
||||
Variant::isStereoVariant(variant));
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ struct RenderTarget::BuilderDetails {
|
||||
uint32_t mWidth{};
|
||||
uint32_t mHeight{};
|
||||
uint8_t mSamples = 1; // currently not settable in the public facing API
|
||||
uint8_t mLayerCount = 0;// currently not settable in the public facing API
|
||||
};
|
||||
|
||||
using BuilderType = RenderTarget;
|
||||
@@ -160,7 +161,8 @@ FRenderTarget::FRenderTarget(FEngine& engine, const RenderTarget::Builder& build
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createRenderTarget(mAttachmentMask,
|
||||
builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples, mrt, dinfo, {});
|
||||
builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples,
|
||||
builder.mImpl->mLayerCount, mrt, dinfo, {});
|
||||
}
|
||||
|
||||
void FRenderTarget::terminate(FEngine& engine) {
|
||||
|
||||
@@ -111,6 +111,8 @@ FRenderer::FRenderer(FEngine& engine) :
|
||||
&engine.debug.shadowmap.display_shadow_texture_level_count);
|
||||
debugRegistry.registerProperty("d.shadowmap.display_shadow_texture_power",
|
||||
&engine.debug.shadowmap.display_shadow_texture_power);
|
||||
debugRegistry.registerProperty("d.stereo.combine_multiview_images",
|
||||
&engine.debug.stereo.combine_multiview_images);
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
@@ -233,8 +235,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
||||
|
||||
SYSTRACE_CALL();
|
||||
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#if 0 && defined(__ANDROID__)
|
||||
char scratch[PROP_VALUE_MAX + 1];
|
||||
int length = __system_property_get("debug.filament.protected", scratch);
|
||||
if (swapChain && length > 0) {
|
||||
@@ -521,7 +522,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
// DEBUG: driver commands must all happen from the same thread. Enforce that on debug builds.
|
||||
driver.debugThreading();
|
||||
|
||||
const bool hasPostProcess = view.hasPostProcessPass();
|
||||
bool hasPostProcess = view.hasPostProcessPass();
|
||||
bool hasScreenSpaceRefraction = false;
|
||||
bool hasColorGrading = hasPostProcess;
|
||||
bool hasDithering = view.getDithering() == Dithering::TEMPORAL;
|
||||
@@ -537,7 +538,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
auto colorGrading = view.getColorGrading();
|
||||
auto ssReflectionsOptions = view.getScreenSpaceReflectionsOptions();
|
||||
auto guardBandOptions = view.getGuardBandOptions();
|
||||
const bool isRenderingMultiview = view.hasStereo() &&
|
||||
engine.getConfig().stereoscopicType == backend::StereoscopicType::MULTIVIEW;
|
||||
// FIXME: This is to override some settings that are not supported for multiview at the moment.
|
||||
// Remove this when all features are supported.
|
||||
if (isRenderingMultiview) {
|
||||
hasPostProcess = false;
|
||||
msaaOptions.enabled = false;
|
||||
}
|
||||
const uint8_t msaaSampleCount = msaaOptions.enabled ? msaaOptions.sampleCount : 1u;
|
||||
|
||||
if (!hasPostProcess) {
|
||||
// disable all effects that are part of post-processing
|
||||
dofOptions.enabled = false;
|
||||
@@ -701,7 +711,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
RenderPass::RenderFlags renderFlags = 0;
|
||||
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
|
||||
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
|
||||
if (view.hasStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC;
|
||||
if (view.hasStereo() &&
|
||||
engine.getConfig().stereoscopicType == backend::StereoscopicType::INSTANCED) {
|
||||
renderFlags |= RenderPass::IS_INSTANCED_STEREOSCOPIC;
|
||||
}
|
||||
|
||||
RenderPassBuilder passBuilder(commandArena);
|
||||
passBuilder.renderFlags(renderFlags);
|
||||
@@ -978,12 +991,18 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
|
||||
RenderPass const pass{ passBuilder.build(engine) };
|
||||
|
||||
FrameGraphTexture::Descriptor const desc = {
|
||||
FrameGraphTexture::Descriptor colorBufferDesc = {
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height,
|
||||
.format = config.hdrFormat
|
||||
};
|
||||
|
||||
// Set the depth to the number of layers if we're rendering multiview.
|
||||
if (isRenderingMultiview) {
|
||||
colorBufferDesc.depth = engine.getConfig().stereoscopicEyeCount;
|
||||
colorBufferDesc.type = backend::SamplerType::SAMPLER_2D_ARRAY;
|
||||
}
|
||||
|
||||
// a non-drawing pass to prepare everything that need to be before the color passes execute
|
||||
fg.addTrivialSideEffectPass("Prepare Color Passes",
|
||||
[=, &js, &view, &ppm](DriverApi& driver) {
|
||||
@@ -991,7 +1010,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
ppm.colorGradingPrepareSubpass(driver,
|
||||
colorGrading, colorGradingConfig, vignetteOptions,
|
||||
desc.width, desc.height);
|
||||
colorBufferDesc.width, colorBufferDesc.height);
|
||||
} else if (colorGradingConfig.customResolve) {
|
||||
ppm.customResolvePrepareSubpass(driver,
|
||||
PostProcessManager::CustomResolveOp::COMPRESS);
|
||||
@@ -1009,7 +1028,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
|
||||
// the color pass itself + color-grading as subpass if needed
|
||||
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view,
|
||||
desc, config, colorGradingConfigForColor, pass.getExecutor());
|
||||
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
|
||||
|
||||
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
|
||||
// this cancels the colorPass() call above if refraction is active.
|
||||
@@ -1153,6 +1172,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
}
|
||||
}
|
||||
|
||||
// Debug: combine the array texture for multiview into a single image.
|
||||
if (UTILS_UNLIKELY(isRenderingMultiview && engine.debug.stereo.combine_multiview_images)) {
|
||||
input = ppm.debugCombineArrayTexture(fg, blendModeTranslucent, input, xvp, {
|
||||
.width = vp.width, .height = vp.height,
|
||||
.format = colorGradingConfig.ldrFormat },
|
||||
SamplerMagFilter::NEAREST, SamplerMinFilter::NEAREST);
|
||||
}
|
||||
|
||||
// We need to do special processing when rendering directly into the swap-chain, that is when
|
||||
// the viewRenderTarget is the default render target (mRenderTarget) and we're rendering into
|
||||
// it.
|
||||
|
||||
@@ -120,11 +120,22 @@ FMaterial const* FSkybox::createMaterial(FEngine& engine) {
|
||||
Material::Builder builder;
|
||||
#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0
|
||||
if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == Engine::FeatureLevel::FEATURE_LEVEL_0)) {
|
||||
builder.package(MATERIALS_SKYBOX0_DATA, MATERIALS_SKYBOX0_SIZE);
|
||||
builder.package(MATERIALS_SKYBOX_FL0_DATA, MATERIALS_SKYBOX_FL0_SIZE);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE);
|
||||
switch (engine.getConfig().stereoscopicType) {
|
||||
case Engine::StereoscopicType::INSTANCED:
|
||||
builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE);
|
||||
break;
|
||||
case Engine::StereoscopicType::MULTIVIEW:
|
||||
#ifdef FILAMENT_ENABLE_MULTIVIEW
|
||||
builder.package(MATERIALS_SKYBOX_MULTIVIEW_DATA, MATERIALS_SKYBOX_MULTIVIEW_SIZE);
|
||||
#else
|
||||
assert_invariant(false);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto material = builder.build(engine);
|
||||
return downcast(material);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user