Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fc16bdcda | ||
|
|
4f021583f1 | ||
|
|
ef15a29c0c | ||
|
|
a0472d3c9f | ||
|
|
54a800a25d | ||
|
|
7f8fbe586c | ||
|
|
6f2c45c76d | ||
|
|
ad60008b6a | ||
|
|
d87f9b621b | ||
|
|
1b38cda0d5 | ||
|
|
a1dea7b1fa | ||
|
|
744708b5ca | ||
|
|
e901837317 | ||
|
|
17f71c9b81 | ||
|
|
b056c126fe | ||
|
|
6ac36d1aab | ||
|
|
a1752b363c | ||
|
|
e855f4aafb | ||
|
|
1c693e24cf | ||
|
|
af92a1f21b | ||
|
|
10ff1ee3e4 | ||
|
|
5449f46350 | ||
|
|
2967888d26 | ||
|
|
afe81d6889 | ||
|
|
95134883d6 | ||
|
|
600c122761 | ||
|
|
1cf5335964 | ||
|
|
4ac4ffd8d6 | ||
|
|
6c40721695 | ||
|
|
56fa078376 | ||
|
|
883507a26f | ||
|
|
d697d42c4a | ||
|
|
e7feee7d5b | ||
|
|
4c75e6e9a2 | ||
|
|
8ab8525359 | ||
|
|
65c794881f | ||
|
|
08d6d57ad0 | ||
|
|
254cf15f0e | ||
|
|
22d99bac3d | ||
|
|
420f06bef3 | ||
|
|
bf8bcef35c | ||
|
|
bfab9f9c32 | ||
|
|
c8335fade7 | ||
|
|
99eac62b4e | ||
|
|
6146d071ba | ||
|
|
d26c972f5b | ||
|
|
93dc218b83 | ||
|
|
b56379dc0f | ||
|
|
e4442a5eb2 | ||
|
|
490e8cf0d0 | ||
|
|
0d25f79421 | ||
|
|
d0586743f6 | ||
|
|
14a99cbc63 | ||
|
|
808b51c1cf |
+12
-10
@@ -71,6 +71,10 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING
|
||||
"Size of the Metal handle arena, default 8."
|
||||
)
|
||||
|
||||
set(FILAMENT_BACKEND_DEBUG_FLAG "" CACHE STRING
|
||||
"A debug flag meant for enabling/disabling backend debugging paths"
|
||||
)
|
||||
|
||||
# Enable exceptions by default in spirv-cross.
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF)
|
||||
|
||||
@@ -169,15 +173,6 @@ if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
|
||||
set(IS_HOST_PLATFORM TRUE)
|
||||
endif()
|
||||
|
||||
if (IOS)
|
||||
# Remove the headerpad_max_install_names linker flag on iOS. It causes warnings when linking
|
||||
# executables with bitcode.
|
||||
string(REPLACE "-Wl,-headerpad_max_install_names" "" CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS})
|
||||
string(REPLACE "-Wl,-headerpad_max_install_names" "" CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS})
|
||||
string(REPLACE "-Wl,-headerpad_max_install_names" "" CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
|
||||
string(REPLACE "-Wl,-headerpad_max_install_names" "" CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS})
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# Link statically against c/c++ lib to avoid missing redistriburable such as
|
||||
# "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
|
||||
@@ -389,8 +384,9 @@ endif()
|
||||
if (NOT MSVC AND NOT IOS)
|
||||
# Omitting stack frame pointers prevents the generation of readable stack traces in crash reports on iOS
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
|
||||
endif()
|
||||
|
||||
# These aren't compatible with -fembed-bitcode (and seem to have no effect on Apple platforms anyway)
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
|
||||
endif()
|
||||
|
||||
@@ -548,6 +544,12 @@ if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
set(FILAMENT_ENABLE_MULTIVIEW ON)
|
||||
endif ()
|
||||
|
||||
# Define backend flag for debug only
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT FILAMENT_BACKEND_DEBUG_FLAG STREQUAL "")
|
||||
add_definitions(-DFILAMENT_BACKEND_DEBUG_FLAG=${FILAMENT_BACKEND_DEBUG_FLAG})
|
||||
unset(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Material compilation flags
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.51.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.51.8'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.51.5'
|
||||
pod 'Filament', '~> 1.51.8'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,6 +7,23 @@ 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.9
|
||||
|
||||
|
||||
## v1.51.8
|
||||
|
||||
- filagui: Fix regression which broke WebGL
|
||||
- Add a new Engine::Config setting to control preferred shader language
|
||||
- Add `getEyeIndex` vertex API
|
||||
- ios: Remove bitcode from iOS builds
|
||||
|
||||
## v1.51.7
|
||||
|
||||
- Add new matedit tool
|
||||
- filagui: Support rendering `GL_TEXTURE_EXTERNAL_OES` textures.
|
||||
- `setFrameScheduledCallback` now takes a `utils::Invocable`.
|
||||
- engine: Add `isPaused()`
|
||||
|
||||
## v1.51.6
|
||||
|
||||
- Add new matedit tool
|
||||
|
||||
@@ -406,6 +406,13 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
|
||||
engine->flush();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nIsPaused(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean)engine->isPaused();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean paused) {
|
||||
@@ -510,7 +517,9 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
|
||||
jint stereoscopicType, jlong stereoscopicEyeCount,
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
|
||||
jboolean disableHandleUseAfterFreeCheck) {
|
||||
jboolean disableHandleUseAfterFreeCheck,
|
||||
jint preferredShaderLanguage,
|
||||
jboolean forceGLES2Context) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -526,6 +535,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
|
||||
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
|
||||
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
|
||||
.preferredShaderLanguage = (Engine::Config::ShaderLanguage) preferredShaderLanguage,
|
||||
.forceGLES2Context = (bool) forceGLES2Context,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,9 @@ public class Engine {
|
||||
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
|
||||
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
|
||||
config.disableHandleUseAfterFreeCheck);
|
||||
config.disableHandleUseAfterFreeCheck,
|
||||
config.preferredShaderLanguage.ordinal(),
|
||||
config.forceGLES2Context);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -428,6 +430,35 @@ public class Engine {
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
public boolean disableHandleUseAfterFreeCheck = false;
|
||||
|
||||
/*
|
||||
* Sets a preferred shader language for Filament to use.
|
||||
*
|
||||
* The Metal backend supports two shader languages: MSL (Metal Shading Language) and
|
||||
* METAL_LIBRARY (precompiled .metallib). This option controls which shader language is
|
||||
* used when materials contain both.
|
||||
*
|
||||
* By default, when preferredShaderLanguage is unset, Filament will prefer METAL_LIBRARY
|
||||
* shaders if present within a material, falling back to MSL. Setting
|
||||
* preferredShaderLanguage to ShaderLanguage::MSL will instead instruct Filament to check
|
||||
* for the presence of MSL in a material first, falling back to METAL_LIBRARY if MSL is not
|
||||
* present.
|
||||
*
|
||||
* When using a non-Metal backend, setting this has no effect.
|
||||
*/
|
||||
public enum ShaderLanguage {
|
||||
DEFAULT,
|
||||
MSL,
|
||||
METAL_LIBRARY,
|
||||
};
|
||||
public ShaderLanguage preferredShaderLanguage = ShaderLanguage.DEFAULT;
|
||||
|
||||
/*
|
||||
* When the OpenGL ES backend is used, setting this value to true will force a GLES2.0
|
||||
* context if supported by the Platform, or if not, will have the backend pretend
|
||||
* it's a GLES2 context. Ignored on other backends.
|
||||
*/
|
||||
public boolean forceGLES2Context = false;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -1227,6 +1258,17 @@ public class Engine {
|
||||
nFlush(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paused state of rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API.
|
||||
*
|
||||
* @see #setPaused
|
||||
*/
|
||||
public boolean isPaused() {
|
||||
return nIsPaused(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause or resume the rendering thread.
|
||||
*
|
||||
@@ -1319,6 +1361,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 boolean nIsPaused(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);
|
||||
@@ -1341,7 +1384,9 @@ public class Engine {
|
||||
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
|
||||
int stereoscopicType, long stereoscopicEyeCount,
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
|
||||
boolean disableHandleUseAfterFreeCheck);
|
||||
boolean disableHandleUseAfterFreeCheck,
|
||||
int preferredShaderLanguage,
|
||||
boolean forceGLES2Context);
|
||||
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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.51.5
|
||||
VERSION_NAME=1.51.8
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ function print_help {
|
||||
echo " -b"
|
||||
echo " Enable Address and Undefined Behavior Sanitizers (asan/ubsan) for debugging."
|
||||
echo " This is only for the desktop build."
|
||||
echo " -x value"
|
||||
echo " Define a preprocessor flag FILAMENT_BACKEND_DEBUG_FLAG with [value]. This is useful for"
|
||||
echo " enabling debug paths in the backend from the build script. For example, make a"
|
||||
echo " systrace-enabled build without directly changing #defines. Remember to add -f when"
|
||||
echo " changing this option."
|
||||
echo ""
|
||||
echo "Build types:"
|
||||
echo " release"
|
||||
@@ -172,6 +177,8 @@ MATOPT_GRADLE_OPTION=""
|
||||
|
||||
ASAN_UBSAN_OPTION=""
|
||||
|
||||
BACKEND_DEBUG_FLAG_OPTION=""
|
||||
|
||||
IOS_BUILD_SIMULATOR=false
|
||||
BUILD_UNIVERSAL_LIBRARIES=false
|
||||
|
||||
@@ -231,6 +238,7 @@ function build_desktop_target {
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
${ASAN_UBSAN_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
${architectures} \
|
||||
../..
|
||||
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
|
||||
@@ -289,6 +297,7 @@ function build_webgl_with_target {
|
||||
-DCMAKE_BUILD_TYPE="$1" \
|
||||
-DCMAKE_INSTALL_PREFIX="../webgl-${lc_target}/filament" \
|
||||
-DWEBGL=1 \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-webgl-${lc_target}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
@@ -363,6 +372,7 @@ function build_android_target {
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
${VULKAN_ANDROID_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
@@ -597,6 +607,7 @@ function build_ios_target {
|
||||
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
@@ -730,6 +741,13 @@ function validate_build_command {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Make sure FILAMENT_BACKEND_DEBUG_FLAG is only meant for debug builds
|
||||
if [[ "${ISSUE_DEBUG_BUILD}" != "true" ]] && [[ ! -z "${BACKEND_DEBUG_FLAG_OPTION}" ]]; then
|
||||
echo "Error: cannot specify FILAMENT_BACKEND_DEBUG_FLAG in non-debug build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
}
|
||||
|
||||
@@ -776,7 +794,7 @@ function check_debug_release_build {
|
||||
|
||||
pushd "$(dirname "$0")" > /dev/null
|
||||
|
||||
while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
while getopts ":hacCfgijmp:q:uvslwtedk:bx:" opt; do
|
||||
case ${opt} in
|
||||
h)
|
||||
print_help
|
||||
@@ -840,7 +858,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
echo "Platform must be one of [desktop|android|ios|webgl|all]"
|
||||
echo ""
|
||||
exit 1
|
||||
;;
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
@@ -918,6 +936,8 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
b) ASAN_UBSAN_OPTION="-DFILAMENT_ENABLE_ASAN_UBSAN=ON"
|
||||
echo "Enabled ASAN/UBSAN"
|
||||
;;
|
||||
x) BACKEND_DEBUG_FLAG_OPTION="-DFILAMENT_BACKEND_DEBUG_FLAG=${OPTARG}"
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -${OPTARG}" >&2
|
||||
echo ""
|
||||
|
||||
+2
-2
@@ -476,7 +476,7 @@ Energy conservation is one of the key components of a good BRDF for physically b
|
||||
<p>
|
||||
|
||||
|
||||
For the specular term, \(f_m\) is a mirror BRDF that can be modeled with the Fresnel law, noted \(F\) in the Cook-Torrance approximation of the microfacet model integration:
|
||||
For the specular term, \(f_r\) is a mirror BRDF that can be modeled with the Fresnel law, noted \(F\) in the Cook-Torrance approximation of the microfacet model integration:
|
||||
|
||||
</p><p>
|
||||
|
||||
@@ -857,7 +857,7 @@ The full GLSL implementation of the standard model is shown in <a href="#listing
|
||||
<span class="line"> <span class="hljs-comment">// perceptually linear roughness to roughness (see parameterization)</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> roughness = perceptualRoughness * perceptualRoughness;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> D = D_GGX(NoH, a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> D = D_GGX(NoH, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> F = F_Schlick(LoH, f0);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> V = V_SmithGGXCorrelated(NoV, NoL, roughness);</span>
|
||||
<span class="line"></span>
|
||||
|
||||
+141
-53
File diff suppressed because one or more lines are too long
@@ -2425,6 +2425,7 @@ The following APIs are only available from the vertex block:
|
||||
**getWorldFromModelMatrix()** | float4x4 | Matrix that converts from model (object) space to world space
|
||||
**getWorldFromModelNormalMatrix()** | float3x3 | Matrix that converts normals from model (object) space to world space
|
||||
**getVertexIndex()** | int | Index of the current vertex
|
||||
**getEyeIndex()** | int | Index of the eye being rendered, starting at 0
|
||||
|
||||
### Fragment only
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -43,7 +43,7 @@ filament-viewer::part(canvas) {
|
||||
</p>
|
||||
|
||||
</main>
|
||||
<script src="https://unpkg.com/filament@1.44.0/filament.js"></script>
|
||||
<script src="https://unpkg.com/filament@1.51.6/filament.js"></script>
|
||||
<script src="https://unpkg.com/gltumble"></script>
|
||||
<script src="filament-viewer.js" type="module"></script>
|
||||
</body>
|
||||
|
||||
@@ -163,6 +163,7 @@ set(PRIVATE_HDRS
|
||||
src/ResourceList.h
|
||||
src/ShadowMap.h
|
||||
src/ShadowMapManager.h
|
||||
src/SharedHandle.h
|
||||
src/TypedUniformBuffer.h
|
||||
src/UniformBuffer.h
|
||||
src/components/CameraManager.h
|
||||
@@ -214,6 +215,7 @@ set(PRIVATE_HDRS
|
||||
set(MATERIAL_SRCS
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/blitDepth.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/blitArray.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
|
||||
@@ -478,6 +478,10 @@ if (APPLE)
|
||||
# linker from removing "unused" symbols.
|
||||
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
|
||||
# This is needed after XCode 15.3
|
||||
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set_target_properties(backend_test_mac PROPERTIES INSTALL_RPATH /usr/local/lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
|
||||
#include <backend/PresentCallable.h>
|
||||
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <type_traits> // FIXME: STL headers are not allowed in public headers
|
||||
#include <variant> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -90,12 +92,15 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON
|
||||
*/
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
|
||||
|
||||
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
|
||||
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
|
||||
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.
|
||||
|
||||
static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte
|
||||
// of push constant (we assume 4-byte
|
||||
// types).
|
||||
|
||||
// Per feature level caps
|
||||
// Use (int)FeatureLevel to index this array
|
||||
static constexpr struct {
|
||||
@@ -331,7 +336,7 @@ enum class UniformType : uint8_t {
|
||||
/**
|
||||
* Supported constant parameter types
|
||||
*/
|
||||
enum class ConstantType : uint8_t {
|
||||
enum class ConstantType : uint8_t {
|
||||
INT,
|
||||
FLOAT,
|
||||
BOOL
|
||||
@@ -1218,13 +1223,15 @@ struct StencilState {
|
||||
uint8_t padding = 0;
|
||||
};
|
||||
|
||||
using PushConstantVariant = std::variant<int32_t, float, bool>;
|
||||
|
||||
static_assert(sizeof(StencilState::StencilOperations) == 5u,
|
||||
"StencilOperations size not what was intended");
|
||||
|
||||
static_assert(sizeof(StencilState) == 12u,
|
||||
"StencilState size not what was intended");
|
||||
|
||||
using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
|
||||
using FrameScheduledCallback = utils::Invocable<void(backend::PresentCallable)>;
|
||||
|
||||
enum class Workaround : uint16_t {
|
||||
// The EASU pass must split because shader compiler flattens early-exit branch
|
||||
|
||||
@@ -75,6 +75,19 @@ protected:
|
||||
HandleBase(HandleBase const& rhs) noexcept = default;
|
||||
HandleBase& operator=(HandleBase const& rhs) noexcept = default;
|
||||
|
||||
HandleBase(HandleBase&& rhs) noexcept
|
||||
: object(rhs.object) {
|
||||
rhs.object = nullid;
|
||||
}
|
||||
|
||||
HandleBase& operator=(HandleBase&& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
object = rhs.object;
|
||||
rhs.object = nullid;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
HandleId object;
|
||||
};
|
||||
@@ -89,8 +102,10 @@ struct Handle : public HandleBase {
|
||||
Handle() noexcept = default;
|
||||
|
||||
Handle(Handle const& rhs) noexcept = default;
|
||||
Handle(Handle&& rhs) noexcept = default;
|
||||
|
||||
Handle& operator=(Handle const& rhs) noexcept = default;
|
||||
Handle& operator=(Handle&& rhs) noexcept = default;
|
||||
|
||||
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
|
||||
|
||||
|
||||
@@ -65,6 +65,12 @@ public:
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
bool disableHandleUseAfterFreeCheck = false;
|
||||
|
||||
/**
|
||||
* Force GLES2 context if supported, or pretend the context is ES2. Only meaningful on
|
||||
* GLES 3.x backends.
|
||||
*/
|
||||
bool forceGLES2Context = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace filament::backend {
|
||||
* and optional user data:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr);
|
||||
* swapChain->setFrameScheduledCallback(nullptr, myFrameScheduledCallback);
|
||||
* if (renderer->beginFrame(swapChain)) {
|
||||
* renderer->render(view);
|
||||
* renderer->endFrame();
|
||||
@@ -58,8 +58,6 @@ namespace filament::backend {
|
||||
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
|
||||
* backends ignore the callback (which will never be called) and proceed normally.
|
||||
*
|
||||
* @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread.
|
||||
*
|
||||
* Applications *must* call each PresentCallable they receive. Each PresentCallable represents a
|
||||
* frame that is waiting to be presented. If an application fails to call a PresentCallable, a
|
||||
* memory leak could occur. To "cancel" the presentation of a frame, pass false to the
|
||||
|
||||
@@ -117,6 +117,14 @@ public:
|
||||
Program& specializationConstants(
|
||||
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;
|
||||
|
||||
struct PushConstant {
|
||||
utils::CString name;
|
||||
ConstantType type;
|
||||
};
|
||||
|
||||
Program& pushConstants(ShaderStage stage,
|
||||
utils::FixedCapacityVector<PushConstant> constants) noexcept;
|
||||
|
||||
Program& cacheId(uint64_t cacheId) noexcept;
|
||||
|
||||
Program& multiview(bool multiview) noexcept;
|
||||
@@ -148,6 +156,15 @@ public:
|
||||
return mSpecializationConstants;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<PushConstant> const& getPushConstants(
|
||||
ShaderStage stage) const noexcept {
|
||||
return mPushConstants[static_cast<uint8_t>(stage)];
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<PushConstant>& getPushConstants(ShaderStage stage) noexcept {
|
||||
return mPushConstants[static_cast<uint8_t>(stage)];
|
||||
}
|
||||
|
||||
uint64_t getCacheId() const noexcept { return mCacheId; }
|
||||
|
||||
bool isMultiview() const noexcept { return mMultiview; }
|
||||
@@ -165,6 +182,7 @@ private:
|
||||
uint64_t mCacheId{};
|
||||
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
|
||||
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
|
||||
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
|
||||
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
|
||||
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
|
||||
|
||||
@@ -29,17 +29,36 @@ namespace filament::backend {
|
||||
//! \privatesection
|
||||
|
||||
struct TargetBufferInfo {
|
||||
// note: the parameters of this constructor are not in the order of this structure's fields
|
||||
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer, uint8_t baseViewIndex) noexcept
|
||||
: handle(handle), baseViewIndex(baseViewIndex), level(level), layer(layer) {
|
||||
}
|
||||
|
||||
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
|
||||
: handle(handle), level(level), layer(layer) {
|
||||
}
|
||||
|
||||
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level) noexcept
|
||||
: handle(handle), level(level) {
|
||||
}
|
||||
|
||||
TargetBufferInfo(Handle<HwTexture> handle) noexcept // NOLINT(*-explicit-constructor)
|
||||
: handle(handle) {
|
||||
}
|
||||
|
||||
TargetBufferInfo() noexcept = default;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
// for cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping
|
||||
// For cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping
|
||||
uint16_t layer = 0;
|
||||
};
|
||||
|
||||
@@ -64,7 +83,7 @@ public:
|
||||
|
||||
MRT() noexcept = default;
|
||||
|
||||
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions)
|
||||
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions, *-explicit-constructor)
|
||||
: mInfos{ color } {
|
||||
}
|
||||
|
||||
@@ -84,7 +103,7 @@ public:
|
||||
|
||||
// this is here for backward compatibility
|
||||
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
|
||||
: mInfos{{ handle, 0, level, layer }} {
|
||||
: mInfos{{ handle, level, layer, 0 }} {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -140,6 +140,23 @@ public:
|
||||
*/
|
||||
virtual uint32_t getDefaultFramebufferObject() noexcept;
|
||||
|
||||
/**
|
||||
* Called by the backend when a frame starts.
|
||||
* @param steady_clock_ns vsync time point on the monotonic clock
|
||||
* @param refreshIntervalNs refresh interval in nanosecond
|
||||
* @param frameId a frame id
|
||||
*/
|
||||
virtual void beginFrame(
|
||||
int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs,
|
||||
uint32_t frameId) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the backend when a frame ends.
|
||||
* @param frameId the frame id used in beginFrame
|
||||
*/
|
||||
virtual void endFrame(
|
||||
uint32_t frameId) noexcept;
|
||||
|
||||
/**
|
||||
* Type of contexts available
|
||||
@@ -191,6 +208,12 @@ public:
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the backend just before calling commit()
|
||||
* @see commit()
|
||||
*/
|
||||
virtual void preCommit() noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver once the current frame finishes drawing. Typically, this should present
|
||||
* the drawSwapChain. This is for example where `eglMakeCurrent()` would be called.
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
|
||||
#include <utils/android/PerformanceHintManager.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -58,6 +62,13 @@ protected:
|
||||
|
||||
void terminate() noexcept override;
|
||||
|
||||
void beginFrame(
|
||||
int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs,
|
||||
uint32_t frameId) noexcept override;
|
||||
|
||||
void preCommit() noexcept override;
|
||||
|
||||
/**
|
||||
* Set the presentation time using `eglPresentationTimeANDROID`
|
||||
* @param presentationTimeInNanosecond
|
||||
@@ -81,6 +92,11 @@ protected:
|
||||
private:
|
||||
int mOSVersion;
|
||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||
utils::PerformanceHintManager mPerformanceHintManager;
|
||||
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
||||
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
clock::time_point mStartTimeOfActualWork;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -82,6 +82,7 @@ public:
|
||||
void requestExit();
|
||||
|
||||
// suspend or unsuspend the queue.
|
||||
bool isPaused() const noexcept;
|
||||
void setPaused(bool paused);
|
||||
|
||||
bool isExitRequested() const;
|
||||
|
||||
@@ -133,12 +133,13 @@ DECL_DRIVER_API_0(tick)
|
||||
|
||||
DECL_DRIVER_API_N(beginFrame,
|
||||
int64_t, monotonic_clock_ns,
|
||||
int64_t, refreshIntervalNs,
|
||||
uint32_t, frameId)
|
||||
|
||||
DECL_DRIVER_API_N(setFrameScheduledCallback,
|
||||
backend::SwapChainHandle, sch,
|
||||
backend::FrameScheduledCallback, callback,
|
||||
void*, user)
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::FrameScheduledCallback&&, callback)
|
||||
|
||||
DECL_DRIVER_API_N(setFrameCompletedCallback,
|
||||
backend::SwapChainHandle, sch,
|
||||
@@ -303,6 +304,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
@@ -432,6 +434,11 @@ DECL_DRIVER_API_N(bindSamplers,
|
||||
uint32_t, index,
|
||||
backend::SamplerGroupHandle, sbh)
|
||||
|
||||
DECL_DRIVER_API_N(setPushConstant,
|
||||
backend::ShaderStage, stage,
|
||||
uint8_t, index,
|
||||
backend::PushConstantVariant, value)
|
||||
|
||||
DECL_DRIVER_API_N(insertEventMarker,
|
||||
const char*, string,
|
||||
uint32_t, len = 0)
|
||||
|
||||
@@ -57,6 +57,11 @@ void CommandBufferQueue::requestExit() {
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isPaused() const noexcept {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
return mPaused;
|
||||
}
|
||||
|
||||
void CommandBufferQueue::setPaused(bool paused) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
if (paused) {
|
||||
|
||||
@@ -91,6 +91,12 @@ Program& Program::specializationConstants(
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::pushConstants(ShaderStage stage,
|
||||
utils::FixedCapacityVector<PushConstant> constants) noexcept {
|
||||
mPushConstants[static_cast<uint8_t>(stage)] = std::move(constants);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::cacheId(uint64_t cacheId) noexcept {
|
||||
mCacheId = cacheId;
|
||||
return *this;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
|
||||
|
||||
#include "MetalContext.h"
|
||||
#include "MetalPlatform.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -28,47 +29,122 @@
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class TrackedMetalBuffer {
|
||||
class ScopedAllocationTimer {
|
||||
public:
|
||||
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
|
||||
TrackedMetalBuffer(id<MTLBuffer> buffer) noexcept : mBuffer(buffer) {
|
||||
if (buffer) {
|
||||
aliveBuffers++;
|
||||
}
|
||||
}
|
||||
~TrackedMetalBuffer() {
|
||||
if (mBuffer) {
|
||||
aliveBuffers--;
|
||||
ScopedAllocationTimer(const char* name) : mBeginning(clock_t::now()), mName(name) {}
|
||||
~ScopedAllocationTimer() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
static constexpr std::chrono::seconds LONG_TIME_THRESHOLD = 10s;
|
||||
|
||||
auto end = clock_t::now();
|
||||
std::chrono::duration<double, std::micro> allocationTimeMicroseconds = end - mBeginning;
|
||||
|
||||
if (UTILS_UNLIKELY(allocationTimeMicroseconds > LONG_TIME_THRESHOLD)) {
|
||||
if (platform && platform->hasDebugUpdateStatFunc()) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "filament.metal.long_buffer_allocation_time.%s",
|
||||
mName);
|
||||
platform->debugUpdateStat(
|
||||
buffer, static_cast<uint64_t>(allocationTimeMicroseconds.count()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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; }
|
||||
static void setPlatform(MetalPlatform* p) { platform = p; }
|
||||
|
||||
private:
|
||||
void swap(TrackedMetalBuffer& other) noexcept {
|
||||
id<MTLBuffer> temp = mBuffer;
|
||||
mBuffer = other.mBuffer;
|
||||
other.mBuffer = temp;
|
||||
typedef std::chrono::steady_clock clock_t;
|
||||
|
||||
static MetalPlatform* platform;
|
||||
|
||||
std::chrono::time_point<clock_t> mBeginning;
|
||||
const char* mName;
|
||||
};
|
||||
|
||||
#ifndef FILAMENT_METAL_BUFFER_TRACKING
|
||||
#define FILAMENT_METAL_BUFFER_TRACKING 0
|
||||
#endif
|
||||
|
||||
class MetalBufferTracking {
|
||||
public:
|
||||
static constexpr size_t EXCESS_BUFFER_COUNT = 30000;
|
||||
|
||||
enum class Type {
|
||||
NONE = 0,
|
||||
GENERIC = 1,
|
||||
RING = 2,
|
||||
STAGING = 3,
|
||||
};
|
||||
static constexpr size_t TypeCount = 3;
|
||||
|
||||
static constexpr auto toIndex(Type t) {
|
||||
assert_invariant(t != Type::NONE);
|
||||
switch (t) {
|
||||
case Type::NONE:
|
||||
case Type::GENERIC:
|
||||
return 0;
|
||||
case Type::RING:
|
||||
return 1;
|
||||
case Type::STAGING:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLBuffer> mBuffer;
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
static void initialize() {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
for (size_t i = 0; i < TypeCount; i++) {
|
||||
aliveBuffers[i] = [NSHashTable weakObjectsHashTable];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static std::atomic<uint64_t> aliveBuffers;
|
||||
static void setPlatform(MetalPlatform* p) { platform = p; }
|
||||
|
||||
static void track(id<MTLBuffer> buffer, Type type) {
|
||||
assert_invariant(type != Type::NONE);
|
||||
if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) {
|
||||
if (platform && platform->hasDebugUpdateStatFunc()) {
|
||||
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
|
||||
MetalBufferTracking::getAliveBuffers());
|
||||
}
|
||||
}
|
||||
[aliveBuffers[toIndex(type)] addObject:buffer];
|
||||
}
|
||||
|
||||
static uint64_t getAliveBuffers() {
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 1; i < TypeCount; i++) {
|
||||
sum += getAliveBuffers(static_cast<Type>(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static uint64_t getAliveBuffers(Type type) {
|
||||
assert_invariant(type != Type::NONE);
|
||||
NSHashTable* hashTable = aliveBuffers[toIndex(type)];
|
||||
// Caution! We can't simply use hashTable.count here, which is inaccurate.
|
||||
// See http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
|
||||
return hashTable.objectEnumerator.allObjects.count;
|
||||
}
|
||||
#else
|
||||
static void initialize() {}
|
||||
static void setPlatform(MetalPlatform* p) {}
|
||||
static id<MTLBuffer> track(id<MTLBuffer> buffer, Type type) { return buffer; }
|
||||
static uint64_t getAliveBuffers() { return 0; }
|
||||
static uint64_t getAliveBuffers(Type type) { return 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
static std::array<NSHashTable<id<MTLBuffer>>*, TypeCount> aliveBuffers;
|
||||
static MetalPlatform* platform;
|
||||
#endif
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
@@ -122,7 +198,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
TrackedMetalBuffer mBuffer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
size_t mBufferSize = 0;
|
||||
void* mCpuBuffer = nullptr;
|
||||
MetalContext& mContext;
|
||||
@@ -171,7 +247,11 @@ public:
|
||||
mBufferOptions(options),
|
||||
mSlotSizeBytes(computeSlotSize(layout)),
|
||||
mSlotCount(slotCount) {
|
||||
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
|
||||
{
|
||||
ScopedAllocationTimer timer("ring");
|
||||
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
|
||||
}
|
||||
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING);
|
||||
assert_invariant(mBuffer);
|
||||
}
|
||||
|
||||
@@ -189,9 +269,13 @@ public:
|
||||
// If we already have an aux buffer, it will get freed here, unless it has been retained
|
||||
// by a MTLCommandBuffer. In that case, it will be freed when the command buffer
|
||||
// finishes executing.
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
{
|
||||
ScopedAllocationTimer timer("ring");
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
}
|
||||
MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING);
|
||||
assert_invariant(mAuxBuffer);
|
||||
return {mAuxBuffer.get(), 0};
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -220,9 +304,9 @@ public:
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
return { mAuxBuffer.get(), 0 };
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
|
||||
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
|
||||
}
|
||||
|
||||
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
|
||||
@@ -231,8 +315,8 @@ public:
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
TrackedMetalBuffer mAuxBuffer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
id<MTLBuffer> mAuxBuffer;
|
||||
|
||||
MTLResourceOptions mBufferOptions;
|
||||
|
||||
|
||||
@@ -22,7 +22,13 @@
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::atomic<uint64_t> TrackedMetalBuffer::aliveBuffers = 0;
|
||||
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
|
||||
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
std::array<NSHashTable<id<MTLBuffer>>*, MetalBufferTracking::TypeCount>
|
||||
MetalBufferTracking::aliveBuffers;
|
||||
MetalPlatform* MetalBufferTracking::platform = nullptr;
|
||||
#endif
|
||||
|
||||
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
|
||||
@@ -37,7 +43,11 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
|
||||
}
|
||||
|
||||
// Otherwise, we allocate a private GPU buffer.
|
||||
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
|
||||
{
|
||||
ScopedAllocationTimer timer("generic");
|
||||
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
|
||||
}
|
||||
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC);
|
||||
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
|
||||
}
|
||||
|
||||
@@ -64,7 +74,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
// Acquire a staging buffer to hold the contents of this update.
|
||||
MetalBufferPool* bufferPool = mContext.bufferPool;
|
||||
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
|
||||
memcpy(staging->buffer.get().contents, src, size);
|
||||
memcpy(staging->buffer.contents, src, size);
|
||||
|
||||
// The blit below requires that byteOffset be a multiple of 4.
|
||||
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
|
||||
@@ -73,9 +83,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit";
|
||||
[blitEncoder copyFromBuffer:staging->buffer.get()
|
||||
[blitEncoder copyFromBuffer:staging->buffer
|
||||
sourceOffset:0
|
||||
toBuffer:mBuffer.get()
|
||||
toBuffer:mBuffer
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
@@ -96,7 +106,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
|
||||
return nil;
|
||||
}
|
||||
assert_invariant(mBuffer);
|
||||
return mBuffer.get();
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
|
||||
|
||||
@@ -32,7 +32,7 @@ struct MetalContext;
|
||||
|
||||
// Immutable POD representing a shared CPU-GPU buffer.
|
||||
struct MetalBufferPoolEntry {
|
||||
TrackedMetalBuffer buffer;
|
||||
id<MTLBuffer> buffer;
|
||||
size_t capacity;
|
||||
mutable uint64_t lastAccessed;
|
||||
mutable uint32_t referenceCount;
|
||||
|
||||
@@ -42,8 +42,13 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
|
||||
}
|
||||
|
||||
// We were not able to find a sufficiently large stage, so create a new one.
|
||||
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
id<MTLBuffer> buffer = nil;
|
||||
{
|
||||
ScopedAllocationTimer timer("staging");
|
||||
buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
}
|
||||
MetalBufferTracking::track(buffer, MetalBufferTracking::Type::STAGING);
|
||||
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
|
||||
.buffer = buffer,
|
||||
|
||||
@@ -105,6 +105,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
driverConfig.disableHandleUseAfterFreeCheck) {
|
||||
mContext->driver = this;
|
||||
|
||||
ScopedAllocationTimer::setPlatform(platform);
|
||||
MetalBufferTracking::initialize();
|
||||
MetalBufferTracking::setPlatform(platform);
|
||||
|
||||
mContext->device = mPlatform.createDevice();
|
||||
assert_invariant(mContext->device);
|
||||
|
||||
@@ -198,6 +202,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
}
|
||||
|
||||
MetalDriver::~MetalDriver() noexcept {
|
||||
MetalBufferTracking::setPlatform(nullptr);
|
||||
ScopedAllocationTimer::setPlatform(nullptr);
|
||||
mContext->device = nil;
|
||||
mContext->emptyTexture = nil;
|
||||
CFRelease(mContext->textureCache);
|
||||
@@ -212,19 +218,29 @@ void MetalDriver::tick(int) {
|
||||
executeTickOps();
|
||||
}
|
||||
|
||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, 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());
|
||||
#if FILAMENT_METAL_BUFFER_TRACKING
|
||||
const uint64_t generic = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::GENERIC);
|
||||
const uint64_t ring = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::RING);
|
||||
const uint64_t staging = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::STAGING);
|
||||
const uint64_t total = generic + ring + staging;
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers", total);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic", generic);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring", ring);
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging", staging);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
FrameScheduledCallback callback, void* user) {
|
||||
void MetalDriver::setFrameScheduledCallback(
|
||||
Handle<HwSwapChain> sch, CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
auto* swapChain = handle_cast<MetalSwapChain>(sch);
|
||||
swapChain->setFrameScheduledCallback(callback, user);
|
||||
swapChain->setFrameScheduledCallback(handler, std::move(callback));
|
||||
}
|
||||
|
||||
void MetalDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
|
||||
@@ -805,6 +821,10 @@ bool MetalDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthStencilBlitSupported(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
@@ -1236,6 +1256,9 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
mContext->samplerBindings[index] = sb;
|
||||
}
|
||||
|
||||
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {}
|
||||
|
||||
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
@@ -71,9 +73,9 @@ public:
|
||||
|
||||
void releaseDrawable();
|
||||
|
||||
void setFrameScheduledCallback(FrameScheduledCallback callback, void* user);
|
||||
void setFrameCompletedCallback(CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user);
|
||||
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback);
|
||||
void setFrameCompletedCallback(
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
||||
|
||||
// For CAMetalLayer-backed SwapChains, presents the drawable or schedules a
|
||||
// FrameScheduledCallback.
|
||||
@@ -110,14 +112,15 @@ private:
|
||||
MetalExternalImage externalImage;
|
||||
SwapChainType type;
|
||||
|
||||
// These two fields store a callback and user data to notify the client that a frame is ready
|
||||
// for presentation.
|
||||
// If frameScheduledCallback is nullptr, then the Metal backend automatically calls
|
||||
// presentDrawable when the frame is committed.
|
||||
// Otherwise, the Metal backend will not automatically present the frame. Instead, clients bear
|
||||
// the responsibility of presenting the frame by calling the PresentCallable object.
|
||||
FrameScheduledCallback frameScheduledCallback = nullptr;
|
||||
void* frameScheduledUserData = nullptr;
|
||||
// These fields store a callback to notify the client that a frame is ready for presentation. If
|
||||
// !frameScheduled.callback, then the Metal backend automatically calls presentDrawable when the
|
||||
// frame is committed. Otherwise, the Metal backend will not automatically present the frame.
|
||||
// Instead, clients bear the responsibility of presenting the frame by calling the
|
||||
// PresentCallable object.
|
||||
struct {
|
||||
CallbackHandler* handler = nullptr;
|
||||
FrameScheduledCallback callback = {};
|
||||
} frameScheduled;
|
||||
|
||||
struct {
|
||||
CallbackHandler* handler = nullptr;
|
||||
|
||||
@@ -221,9 +221,10 @@ void MetalSwapChain::ensureDepthStencilTexture() {
|
||||
depthStencilTexture = [context.device newTextureWithDescriptor:descriptor];
|
||||
}
|
||||
|
||||
void MetalSwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) {
|
||||
frameScheduledCallback = callback;
|
||||
frameScheduledUserData = user;
|
||||
void MetalSwapChain::setFrameScheduledCallback(
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
frameScheduled.handler = handler;
|
||||
frameScheduled.callback = std::move(callback);
|
||||
}
|
||||
|
||||
void MetalSwapChain::setFrameCompletedCallback(CallbackHandler* handler,
|
||||
@@ -238,7 +239,7 @@ void MetalSwapChain::present() {
|
||||
scheduleFrameCompletedCallback();
|
||||
}
|
||||
if (drawable) {
|
||||
if (frameScheduledCallback) {
|
||||
if (frameScheduled.callback) {
|
||||
scheduleFrameScheduledCallback();
|
||||
} else {
|
||||
[getPendingCommandBuffer(&context) presentDrawable:drawable];
|
||||
@@ -296,21 +297,38 @@ void presentDrawable(bool presentFrame, void* user) {
|
||||
}
|
||||
|
||||
void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
if (!frameScheduledCallback) {
|
||||
if (!frameScheduled.callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_invariant(drawable);
|
||||
|
||||
// Destroy this by calling maybePresentAndDestroyAsync() later.
|
||||
auto* presentData = PresentDrawableData::create(drawable, context.driver);
|
||||
struct Callback {
|
||||
Callback(FrameScheduledCallback&& callback, id<CAMetalDrawable> drawable,
|
||||
MetalDriver* driver)
|
||||
: f(std::move(callback)), data(PresentDrawableData::create(drawable, driver)) {}
|
||||
FrameScheduledCallback f;
|
||||
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
|
||||
std::unique_ptr<PresentDrawableData> data;
|
||||
static void func(void* user) {
|
||||
auto* const c = reinterpret_cast<Callback*>(user);
|
||||
PresentDrawableData* presentDrawableData = c->data.release();
|
||||
PresentCallable presentCallable(presentDrawable, presentDrawableData);
|
||||
c->f(presentCallable);
|
||||
delete c;
|
||||
}
|
||||
};
|
||||
|
||||
FrameScheduledCallback userCallback = frameScheduledCallback;
|
||||
void* userData = frameScheduledUserData;
|
||||
// This callback pointer will be captured by the block. Even if the scheduled handler is never
|
||||
// called, the unique_ptr will still ensure we don't leak memory.
|
||||
__block auto callback =
|
||||
std::make_unique<Callback>(std::move(frameScheduled.callback), drawable, context.driver);
|
||||
|
||||
backend::CallbackHandler* handler = frameScheduled.handler;
|
||||
MetalDriver* driver = context.driver;
|
||||
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
|
||||
PresentCallable callable(presentDrawable, static_cast<void*>(presentData));
|
||||
userCallback(callable, userData);
|
||||
Callback* user = callback.release();
|
||||
driver->scheduleCallback(handler, user, &Callback::func);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -771,13 +789,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
|
||||
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
|
||||
const size_t stagingBufferSize = shape.totalBytes;
|
||||
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
|
||||
memcpy(entry->buffer.get().contents,
|
||||
memcpy(entry->buffer.contents,
|
||||
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
|
||||
stagingBufferSize);
|
||||
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
|
||||
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
|
||||
blitCommandEncoder.label = @"Texture upload buffer blit";
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:shape.bytesPerRow
|
||||
sourceBytesPerImage:shape.bytesPerSlice
|
||||
|
||||
@@ -169,6 +169,23 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
id<MTLFunction> function = [library newFunctionWithName:@"main0"
|
||||
constantValues:constants
|
||||
error:&error];
|
||||
if (function == nil) {
|
||||
// If the library loads but functions within it fail to load, it usually means the
|
||||
// GPU backend crashed. (This can happen if it's a Metallib shader that was compiled
|
||||
// with a minimum iOS version that's newer than this device.)
|
||||
NSString* errorMessage = @"unknown error";
|
||||
if (error) {
|
||||
auto description =
|
||||
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.w << description << utils::io::endl;
|
||||
errorMessage = error.localizedDescription;
|
||||
}
|
||||
PANIC_LOG("Failed to load main0 in Metal program.");
|
||||
NSString* programName =
|
||||
[NSString stringWithFormat:@"%s::main0", program.getName().c_str_safe()];
|
||||
return MetalFunctionBundle::error(errorMessage, programName);
|
||||
}
|
||||
|
||||
if (!program.getName().empty()) {
|
||||
function.label = @(program.getName().c_str());
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ void NoopDriver::terminate() {
|
||||
void NoopDriver::tick(int) {
|
||||
}
|
||||
|
||||
void NoopDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
void NoopDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
}
|
||||
|
||||
void NoopDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
FrameScheduledCallback callback, void* user) {
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
|
||||
}
|
||||
|
||||
@@ -193,6 +194,10 @@ bool NoopDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthStencilBlitSupported(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isProtectedTexturesSupported() {
|
||||
return true;
|
||||
}
|
||||
@@ -307,6 +312,10 @@ void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
}
|
||||
|
||||
void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
}
|
||||
|
||||
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace filament::backend {
|
||||
struct GLTexture : public HwTexture {
|
||||
using HwTexture::HwTexture;
|
||||
struct GL {
|
||||
GL() noexcept : imported(false), sidecarSamples(1), reserved(0) {}
|
||||
GL() noexcept : imported(false), sidecarSamples(1), reserved1(0) {}
|
||||
GLuint id = 0; // texture or renderbuffer id
|
||||
GLenum target = 0;
|
||||
GLenum internalFormat = 0;
|
||||
@@ -40,10 +40,10 @@ struct GLTexture : public HwTexture {
|
||||
GLfloat anisotropy = 1.0;
|
||||
int8_t baseLevel = 127;
|
||||
int8_t maxLevel = -1;
|
||||
uint8_t targetIndex = 0; // optimization: index corresponding to target
|
||||
uint8_t reserved0 = 0;
|
||||
bool imported : 1;
|
||||
uint8_t sidecarSamples : 4;
|
||||
uint8_t reserved : 3;
|
||||
uint8_t reserved1 : 3;
|
||||
} gl;
|
||||
|
||||
OpenGLPlatform::ExternalTexture* externalTexture = nullptr;
|
||||
|
||||
@@ -63,7 +63,8 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
Platform::DriverConfig const& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mSamplerMap(32) {
|
||||
|
||||
@@ -85,6 +86,13 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
|
||||
queryOpenGLVersion(&state.major, &state.minor);
|
||||
|
||||
#if defined(BACKEND_OPENGL_VERSION_GLES)
|
||||
if (UTILS_UNLIKELY(driverConfig.forceGLES2Context)) {
|
||||
state.major = 2;
|
||||
state.minor = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenGLContext::initExtensions(&ext, state.major, state.minor);
|
||||
|
||||
OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
|
||||
@@ -881,19 +889,32 @@ default_case:
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindTexture(GLenum target, GLuint texture_id) noexcept {
|
||||
void OpenGLContext::unbindTexture(
|
||||
UTILS_UNUSED_IN_RELEASE GLenum target, GLuint texture_id) noexcept {
|
||||
// unbind this texture from all the units it might be bound to
|
||||
// no need unbind the texture from FBOs because we're not tracking that state (and there is
|
||||
// no need to).
|
||||
const size_t index = getIndexForTextureTarget(target);
|
||||
UTILS_NOUNROLL
|
||||
for (GLuint unit = 0; unit < MAX_TEXTURE_UNIT_COUNT; unit++) {
|
||||
if (state.textures.units[unit].targets[index].texture_id == texture_id) {
|
||||
bindTexture(unit, target, (GLuint)0, index);
|
||||
// Never attempt to unbind texture 0. This could happen with external textures w/ streaming if
|
||||
// never populated.
|
||||
if (texture_id) {
|
||||
UTILS_NOUNROLL
|
||||
for (GLuint unit = 0; unit < MAX_TEXTURE_UNIT_COUNT; unit++) {
|
||||
if (state.textures.units[unit].id == texture_id) {
|
||||
// if this texture is bound, it should be at the same target
|
||||
assert_invariant(state.textures.units[unit].target == target);
|
||||
unbindTextureUnit(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindTextureUnit(GLuint unit) noexcept {
|
||||
update_state(state.textures.units[unit].id, 0u, [&]() {
|
||||
activeTexture(unit);
|
||||
glBindTexture(state.textures.units[unit].target, 0u);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindSampler(GLuint sampler) noexcept {
|
||||
// unbind this sampler from all the units it might be bound to
|
||||
UTILS_NOUNROLL // clang generates >800B of code!!!
|
||||
|
||||
@@ -91,7 +91,8 @@ public:
|
||||
|
||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||
|
||||
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
|
||||
explicit OpenGLContext(OpenGLPlatform& platform,
|
||||
Platform::DriverConfig const& driverConfig) noexcept;
|
||||
|
||||
~OpenGLContext() noexcept final;
|
||||
|
||||
@@ -137,7 +138,6 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static inline size_t getIndexForTextureTarget(GLuint target) noexcept;
|
||||
constexpr inline size_t getIndexForCap(GLenum cap) noexcept;
|
||||
constexpr static inline size_t getIndexForBufferTarget(GLenum target) noexcept;
|
||||
|
||||
@@ -149,10 +149,10 @@ public:
|
||||
|
||||
void pixelStore(GLenum, GLint) noexcept;
|
||||
inline void activeTexture(GLuint unit) noexcept;
|
||||
inline void bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept;
|
||||
inline void bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept;
|
||||
|
||||
void unbindTexture(GLenum target, GLuint id) noexcept;
|
||||
void unbindTextureUnit(GLuint unit) noexcept;
|
||||
inline void bindVertexArray(RenderPrimitive const* p) noexcept;
|
||||
inline void bindSampler(GLuint unit, GLuint sampler) noexcept;
|
||||
void unbindSampler(GLuint sampler) noexcept;
|
||||
@@ -322,8 +322,14 @@ public:
|
||||
|
||||
// function to handle state changes we don't control
|
||||
void updateTexImage(GLenum target, GLuint id) noexcept {
|
||||
const size_t index = getIndexForTextureTarget(target);
|
||||
state.textures.units[state.textures.active].targets[index].texture_id = id;
|
||||
assert_invariant(target == GL_TEXTURE_EXTERNAL_OES);
|
||||
// if another target is bound to this texture unit, unbind that texture
|
||||
if (UTILS_UNLIKELY(state.textures.units[state.textures.active].target != target)) {
|
||||
glBindTexture(state.textures.units[state.textures.active].target, 0);
|
||||
state.textures.units[state.textures.active].target = GL_TEXTURE_EXTERNAL_OES;
|
||||
}
|
||||
// the texture is already bound to `target`, we just update our internal state
|
||||
state.textures.units[state.textures.active].id = id;
|
||||
}
|
||||
void resetProgram() noexcept { state.program.use = 0; }
|
||||
|
||||
@@ -426,9 +432,8 @@ public:
|
||||
GLuint active = 0; // zero-based
|
||||
struct {
|
||||
GLuint sampler = 0;
|
||||
struct {
|
||||
GLuint texture_id = 0;
|
||||
} targets[7]; // this must match getIndexForTextureTarget()
|
||||
GLuint target = 0;
|
||||
GLuint id = 0;
|
||||
} units[MAX_TEXTURE_UNIT_COUNT];
|
||||
} textures;
|
||||
|
||||
@@ -598,31 +603,10 @@ private:
|
||||
}
|
||||
|
||||
void setDefaultState() noexcept;
|
||||
|
||||
static constexpr const size_t TEXTURE_TARGET_COUNT =
|
||||
sizeof(state.textures.units[0].targets) / sizeof(state.textures.units[0].targets[0]);
|
||||
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
constexpr size_t OpenGLContext::getIndexForTextureTarget(GLuint target) noexcept {
|
||||
// this must match state.textures[].targets[]
|
||||
switch (target) {
|
||||
case GL_TEXTURE_2D: return 0;
|
||||
case GL_TEXTURE_2D_ARRAY: return 1;
|
||||
case GL_TEXTURE_CUBE_MAP: return 2;
|
||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||
case GL_TEXTURE_2D_MULTISAMPLE: return 3;
|
||||
#endif
|
||||
case GL_TEXTURE_EXTERNAL_OES: return 4;
|
||||
case GL_TEXTURE_3D: return 5;
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY: return 6;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
|
||||
size_t index = 0;
|
||||
switch (cap) {
|
||||
@@ -770,19 +754,17 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept {
|
||||
assert_invariant(targetIndex == getIndexForTextureTarget(target));
|
||||
assert_invariant(targetIndex < TEXTURE_TARGET_COUNT);
|
||||
update_state(state.textures.units[unit].targets[targetIndex].texture_id, texId, [&]() {
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept {
|
||||
update_state(state.textures.units[unit].target, target, [&]() {
|
||||
activeTexture(unit);
|
||||
glBindTexture(state.textures.units[unit].target, 0);
|
||||
});
|
||||
update_state(state.textures.units[unit].id, texId, [&]() {
|
||||
activeTexture(unit);
|
||||
glBindTexture(target, texId);
|
||||
}, target == GL_TEXTURE_EXTERNAL_OES);
|
||||
}
|
||||
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept {
|
||||
bindTexture(unit, target, texId, getIndexForTextureTarget(target));
|
||||
}
|
||||
|
||||
void OpenGLContext::useProgram(GLuint program) noexcept {
|
||||
update_state(state.program.use, program, [&]() {
|
||||
glUseProgram(program);
|
||||
|
||||
@@ -173,6 +173,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
PANIC_LOG("OpenGL ES 2.0 minimum needed (current %d.%d)", major, minor);
|
||||
goto cleanup;
|
||||
}
|
||||
if (UTILS_UNLIKELY(driverConfig.forceGLES2Context)) {
|
||||
major = 2;
|
||||
minor = 0;
|
||||
}
|
||||
#else
|
||||
// we require GL 4.1 headers and minimum version
|
||||
if (UTILS_UNLIKELY(!((major == 4 && minor >= 1) || major > 4))) {
|
||||
@@ -203,12 +207,13 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
|
||||
OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
mContext(mPlatform),
|
||||
mContext(mPlatform, driverConfig),
|
||||
mShaderCompilerService(*this),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mDriverConfig(driverConfig) {
|
||||
mDriverConfig(driverConfig),
|
||||
mCurrentPushConstants(new(std::nothrow) PushConstantBundle{}) {
|
||||
|
||||
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
|
||||
|
||||
@@ -265,6 +270,9 @@ void OpenGLDriver::terminate() {
|
||||
assert_invariant(mGpuCommandCompleteOps.empty());
|
||||
#endif
|
||||
|
||||
delete mCurrentPushConstants;
|
||||
mCurrentPushConstants = nullptr;
|
||||
|
||||
mContext.terminate();
|
||||
|
||||
mPlatform.terminate();
|
||||
@@ -285,9 +293,42 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
|
||||
mContext.bindSampler(unit, sampler);
|
||||
}
|
||||
|
||||
void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
|
||||
utils::Slice<std::pair<GLint, ConstantType>> constants;
|
||||
if (stage == ShaderStage::VERTEX) {
|
||||
constants = mCurrentPushConstants->vertexConstants;
|
||||
} else if (stage == ShaderStage::FRAGMENT) {
|
||||
constants = mCurrentPushConstants->fragmentConstants;
|
||||
}
|
||||
|
||||
assert_invariant(index < constants.size());
|
||||
auto const& [location, type] = constants[index];
|
||||
|
||||
// This push constant wasn't found in the shader. It's ok to return without error-ing here.
|
||||
if (location < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
assert_invariant(type == ConstantType::BOOL);
|
||||
bool const bval = std::get<bool>(value);
|
||||
glUniform1i(location, bval ? 1 : 0);
|
||||
} else if (std::holds_alternative<float>(value)) {
|
||||
assert_invariant(type == ConstantType::FLOAT);
|
||||
float const fval = std::get<float>(value);
|
||||
glUniform1f(location, fval);
|
||||
} else {
|
||||
assert_invariant(type == ConstantType::INT);
|
||||
int const ival = std::get<int>(value);
|
||||
glUniform1i(location, ival);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
|
||||
assert_invariant(t != nullptr);
|
||||
mContext.bindTexture(unit, t->gl.target, t->gl.id, t->gl.targetIndex);
|
||||
mContext.bindTexture(unit, t->gl.target, t->gl.id);
|
||||
}
|
||||
|
||||
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
@@ -749,7 +790,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
if (t->externalTexture) {
|
||||
t->gl.target = t->externalTexture->target;
|
||||
t->gl.id = t->externalTexture->id;
|
||||
t->gl.targetIndex = (uint8_t)OpenGLContext::getIndexForTextureTarget(t->gl.target);
|
||||
// internalFormat actually depends on the external image, but it doesn't matter
|
||||
// because it's not used anywhere for anything important.
|
||||
t->gl.internalFormat = internalFormat;
|
||||
@@ -761,30 +801,23 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
|
||||
t->gl.internalFormat = internalFormat;
|
||||
|
||||
// We DO NOT update targetIndex at function exit to take advantage of the fact that
|
||||
// getIndexForTextureTarget() is constexpr -- so all of this disappears at compile time.
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// we can't be here -- doesn't matter what we do
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = 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 = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -795,8 +828,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
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)
|
||||
OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -855,32 +886,24 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
t->gl.internalFormat = getInternalFormat(format);
|
||||
assert_invariant(t->gl.internalFormat);
|
||||
|
||||
// We DO NOT update targetIndex at function exit to take advantage of the fact that
|
||||
// getIndexForTextureTarget() is constexpr -- so all of this disappears at compile time.
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
t->gl.target = 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 = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = 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 = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -891,7 +914,6 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
if (gl.features.multisample_texture) {
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -1781,7 +1803,6 @@ void OpenGLDriver::updateStreams(DriverApi* driver) {
|
||||
// the target and id can be reset each time
|
||||
t->gl.target = t->externalTexture->target;
|
||||
t->gl.id = t->externalTexture->id;
|
||||
t->gl.targetIndex = (uint8_t)OpenGLContext::getIndexForTextureTarget(t->gl.target);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
}
|
||||
}
|
||||
@@ -2043,6 +2064,10 @@ bool OpenGLDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isDepthStencilBlitSupported(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isProtectedTexturesSupported() {
|
||||
return getContext().ext.EXT_protected_textures;
|
||||
}
|
||||
@@ -2667,7 +2692,6 @@ void OpenGLDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
// the target and id can be reset each time
|
||||
t->gl.target = t->externalTexture->target;
|
||||
t->gl.id = t->externalTexture->id;
|
||||
t->gl.targetIndex = (uint8_t)OpenGLContext::getIndexForTextureTarget(t->gl.target);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
}
|
||||
}
|
||||
@@ -3399,17 +3423,21 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
|
||||
|
||||
void OpenGLDriver::tick(int) {
|
||||
DEBUG_MARKER()
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
executeGpuCommandsCompleteOps();
|
||||
#endif
|
||||
executeEveryNowAndThenOps();
|
||||
getShaderCompilerService().tick();
|
||||
}
|
||||
|
||||
void OpenGLDriver::beginFrame(
|
||||
UTILS_UNUSED int64_t monotonic_clock_ns,
|
||||
UTILS_UNUSED int64_t refreshIntervalNs,
|
||||
UTILS_UNUSED uint32_t frameId) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
insertEventMarker("beginFrame");
|
||||
mPlatform.beginFrame(monotonic_clock_ns, refreshIntervalNs, frameId);
|
||||
if (UTILS_UNLIKELY(!mTexturesWithStreamsAttached.empty())) {
|
||||
OpenGLPlatform& platform = mPlatform;
|
||||
for (GLTexture const* t : mTexturesWithStreamsAttached) {
|
||||
@@ -3418,7 +3446,7 @@ void OpenGLDriver::beginFrame(
|
||||
assert_invariant(t->hwStream->stream);
|
||||
platform.updateTexImage(t->hwStream->stream,
|
||||
&static_cast<GLStream*>(t->hwStream)->user_thread.timestamp); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
// NOTE: We assume that updateTexImage() binds the texture on our behalf
|
||||
// NOTE: We assume that OpenGLPlatform::updateTexImage() binds the texture on our behalf
|
||||
gl.updateTexImage(GL_TEXTURE_EXTERNAL_OES, t->gl.id);
|
||||
}
|
||||
}
|
||||
@@ -3426,7 +3454,7 @@ void OpenGLDriver::beginFrame(
|
||||
}
|
||||
|
||||
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
FrameScheduledCallback callback, void* user) {
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
DEBUG_MARKER()
|
||||
}
|
||||
|
||||
@@ -3457,6 +3485,7 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
|
||||
#endif
|
||||
//SYSTRACE_NAME("glFinish");
|
||||
//glFinish();
|
||||
mPlatform.endFrame(frameId);
|
||||
insertEventMarker("endFrame");
|
||||
}
|
||||
|
||||
@@ -3668,7 +3697,7 @@ void OpenGLDriver::blit(
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
|
||||
switch (s->target) {
|
||||
@@ -3694,7 +3723,7 @@ void OpenGLDriver::blit(
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
@@ -3816,6 +3845,7 @@ void OpenGLDriver::bindPipeline(PipelineState state) {
|
||||
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
|
||||
mValidProgram = useProgram(p);
|
||||
(*mCurrentPushConstants) = p->getPushConstants();
|
||||
}
|
||||
|
||||
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
|
||||
@@ -66,9 +66,9 @@ namespace filament::backend {
|
||||
class OpenGLPlatform;
|
||||
class PixelBufferDescriptor;
|
||||
struct TargetBufferInfo;
|
||||
|
||||
class OpenGLProgram;
|
||||
class TimerQueryFactoryInterface;
|
||||
struct PushConstantBundle;
|
||||
|
||||
class OpenGLDriver final : public DriverBase {
|
||||
inline explicit OpenGLDriver(OpenGLPlatform* platform,
|
||||
@@ -375,6 +375,8 @@ private:
|
||||
// for ES2 sRGB support
|
||||
GLSwapChain* mCurrentDrawSwapChain = nullptr;
|
||||
bool mRec709OutputColorspace = false;
|
||||
|
||||
PushConstantBundle* mCurrentPushConstants = nullptr;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -55,6 +55,16 @@ uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OpenGLPlatform::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs,
|
||||
uint32_t frameId) noexcept {
|
||||
}
|
||||
|
||||
void OpenGLPlatform::endFrame(uint32_t frameId) noexcept {
|
||||
}
|
||||
|
||||
void OpenGLPlatform::preCommit() noexcept {
|
||||
}
|
||||
|
||||
OpenGLPlatform::ContextType OpenGLPlatform::getCurrentContextType() const noexcept {
|
||||
return ContextType::UNPROTECTED;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ struct OpenGLProgram::LazyInitializationData {
|
||||
Program::UniformBlockInfo uniformBlockInfo;
|
||||
Program::SamplerGroupInfo samplerGroupInfo;
|
||||
std::array<Program::UniformInfo, Program::UNIFORM_BINDING_COUNT> bindingUniformInfo;
|
||||
utils::FixedCapacityVector<Program::PushConstant> vertexPushConstants;
|
||||
utils::FixedCapacityVector<Program::PushConstant> fragmentPushConstants;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +55,6 @@ OpenGLProgram::OpenGLProgram() noexcept = default;
|
||||
|
||||
OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
|
||||
: HwProgram(std::move(program.getName())) {
|
||||
|
||||
auto* const lazyInitializationData = new(std::nothrow) LazyInitializationData();
|
||||
lazyInitializationData->samplerGroupInfo = std::move(program.getSamplerGroupInfo());
|
||||
if (UTILS_UNLIKELY(gld.getContext().isES2())) {
|
||||
@@ -61,6 +62,8 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
|
||||
} else {
|
||||
lazyInitializationData->uniformBlockInfo = std::move(program.getUniformBlockBindings());
|
||||
}
|
||||
lazyInitializationData->vertexPushConstants = std::move(program.getPushConstants(ShaderStage::VERTEX));
|
||||
lazyInitializationData->fragmentPushConstants = std::move(program.getPushConstants(ShaderStage::FRAGMENT));
|
||||
|
||||
ShaderCompilerService& compiler = gld.getShaderCompilerService();
|
||||
mToken = compiler.createProgram(name, std::move(program));
|
||||
@@ -203,6 +206,21 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
}
|
||||
}
|
||||
mUsedBindingsCount = usedBindingCount;
|
||||
|
||||
auto& vertexConstants = lazyInitializationData.vertexPushConstants;
|
||||
auto& fragmentConstants = lazyInitializationData.fragmentPushConstants;
|
||||
|
||||
size_t const totalConstantCount = vertexConstants.size() + fragmentConstants.size();
|
||||
if (totalConstantCount > 0) {
|
||||
mPushConstants.reserve(totalConstantCount);
|
||||
mPushConstantFragmentStageOffset = vertexConstants.size();
|
||||
auto const transformAndAdd = [&](Program::PushConstant const& constant) {
|
||||
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
|
||||
mPushConstants.push_back({loc, constant.type});
|
||||
};
|
||||
std::for_each(vertexConstants.cbegin(), vertexConstants.cend(), transformAndAdd);
|
||||
std::for_each(fragmentConstants.cbegin(), fragmentConstants.cend(), transformAndAdd);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
@@ -38,6 +39,11 @@ namespace filament::backend {
|
||||
|
||||
class OpenGLDriver;
|
||||
|
||||
struct PushConstantBundle {
|
||||
utils::Slice<std::pair<GLint, ConstantType>> vertexConstants;
|
||||
utils::Slice<std::pair<GLint, ConstantType>> fragmentConstants;
|
||||
};
|
||||
|
||||
class OpenGLProgram : public HwProgram {
|
||||
public:
|
||||
|
||||
@@ -78,6 +84,14 @@ public:
|
||||
GLuint program = 0;
|
||||
} gl; // 4 bytes
|
||||
|
||||
PushConstantBundle getPushConstants() {
|
||||
auto fragBegin = mPushConstants.begin() + mPushConstantFragmentStageOffset;
|
||||
return {
|
||||
.vertexConstants = utils::Slice(mPushConstants.begin(), fragBegin),
|
||||
.fragmentConstants = utils::Slice(fragBegin, mPushConstants.end()),
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
// keep these away from of other class attributes
|
||||
struct LazyInitializationData;
|
||||
@@ -95,11 +109,14 @@ private:
|
||||
ShaderCompilerService::program_token_t mToken{}; // 16 bytes
|
||||
|
||||
uint8_t mUsedBindingsCount = 0u; // 1 byte
|
||||
UTILS_UNUSED uint8_t padding[3] = {}; // 3 bytes
|
||||
UTILS_UNUSED uint8_t padding[2] = {}; // 2 byte
|
||||
|
||||
// Push constant array offset for fragment stage constants.
|
||||
uint8_t mPushConstantFragmentStageOffset = 0u; // 1 byte
|
||||
|
||||
// only needed for ES2
|
||||
GLint mRec709Location = -1; // 4 bytes
|
||||
GLint mRec709Location = -1; // 4 bytes
|
||||
|
||||
using LocationInfo = utils::FixedCapacityVector<GLint>;
|
||||
struct UniformsRecord {
|
||||
Program::UniformInfo uniforms;
|
||||
@@ -107,11 +124,15 @@ private:
|
||||
mutable GLuint id = 0;
|
||||
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
|
||||
};
|
||||
UniformsRecord const* mUniformsRecords = nullptr;
|
||||
UniformsRecord const* mUniformsRecords = nullptr; // 8 bytes
|
||||
|
||||
// Note that this can be replaced with a raw pointer and an uint8_t (for size) to reduce the
|
||||
// size of the container to 9 bytes if there is a need in the future.
|
||||
utils::FixedCapacityVector<std::pair<GLint, ConstantType>> mPushConstants;// 16 bytes
|
||||
};
|
||||
|
||||
// if OpenGLProgram is larger tha 64 bytes, it'll fall in a larger Handle bucket.
|
||||
static_assert(sizeof(OpenGLProgram) <= 64); // currently 48 bytes
|
||||
static_assert(sizeof(OpenGLProgram) <= 64); // currently 64 bytes
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool requestES2Context = false;
|
||||
bool requestES2Context = driverConfig.forceGLES2Context;
|
||||
char property[PROP_VALUE_MAX];
|
||||
int const length = __system_property_get("debug.filament.es2", property);
|
||||
if (length > 0) {
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <android/api-level.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#include <utils/android/PerformanceHintManager.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Log.h>
|
||||
@@ -36,8 +38,11 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <new>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -112,8 +117,38 @@ void PlatformEGLAndroid::terminate() noexcept {
|
||||
PlatformEGL::terminate();
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::beginFrame(
|
||||
int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs,
|
||||
uint32_t frameId) noexcept {
|
||||
if (mPerformanceHintSession.isValid()) {
|
||||
if (refreshIntervalNs <= 0) {
|
||||
// we're not provided with a target time, assume 16.67ms
|
||||
refreshIntervalNs = 16'666'667;
|
||||
}
|
||||
mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns));
|
||||
mPerformanceHintSession.updateTargetWorkDuration(refreshIntervalNs);
|
||||
}
|
||||
PlatformEGL::beginFrame(monotonic_clock_ns, refreshIntervalNs, frameId);
|
||||
}
|
||||
|
||||
void backend::PlatformEGLAndroid::preCommit() noexcept {
|
||||
if (mPerformanceHintSession.isValid()) {
|
||||
auto const actualWorkDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
clock::now() - mStartTimeOfActualWork);
|
||||
mPerformanceHintSession.reportActualWorkDuration(actualWorkDuration.count());
|
||||
}
|
||||
PlatformEGL::preCommit();
|
||||
}
|
||||
|
||||
Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
|
||||
// the refresh rate default value doesn't matter, we change it later
|
||||
int32_t const tid = gettid();
|
||||
mPerformanceHintSession = PerformanceHintManager::Session{
|
||||
mPerformanceHintManager, &tid, 1, 16'666'667 };
|
||||
|
||||
Driver* driver = PlatformEGL::createDriver(sharedContext, driverConfig);
|
||||
auto extensions = GLUtils::split(eglQueryString(mEGLDisplay, EGL_EXTENSIONS));
|
||||
|
||||
|
||||
@@ -410,6 +410,7 @@ io::ostream& operator<<(io::ostream& out, const RasterState& rs) {
|
||||
io::ostream& operator<<(io::ostream& out, const TargetBufferInfo& tbi) {
|
||||
return out << "TargetBufferInfo{"
|
||||
<< "handle=" << tbi.handle
|
||||
<< ", baseViewIndex=" << tbi.baseViewIndex
|
||||
<< ", level=" << tbi.level
|
||||
<< ", layer=" << tbi.layer << "}";
|
||||
}
|
||||
|
||||
@@ -26,21 +26,16 @@
|
||||
|
||||
#include <smolv.h>
|
||||
|
||||
#include "generated/vkshaders/vkshaders.h"
|
||||
|
||||
using namespace bluevk;
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
namespace {
|
||||
|
||||
inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter,
|
||||
VulkanAttachment src, VulkanAttachment dst,
|
||||
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
|
||||
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
@@ -48,21 +43,8 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
}
|
||||
|
||||
const VkImageSubresourceRange srcRange = {
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = src.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = src.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
const VkImageSubresourceRange dstRange = {
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = dst.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = dst.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
|
||||
VkImageSubresourceRange const dstRange = dst.getSubresourceRange();
|
||||
|
||||
VulkanLayout oldSrcLayout = src.getLayout();
|
||||
VulkanLayout oldDstLayout = dst.getLayout();
|
||||
@@ -77,15 +59,15 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
.dstOffsets = { dstRect[0], dstRect[1] },
|
||||
}};
|
||||
vkCmdBlitImage(cmdbuffer,
|
||||
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
src.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
1, blitRegions, filter);
|
||||
|
||||
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
|
||||
oldSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage);
|
||||
oldSrcLayout = imgutil::getDefaultLayout(src.texture->usage);
|
||||
}
|
||||
if (oldDstLayout == VulkanLayout::UNDEFINED) {
|
||||
oldDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage);
|
||||
oldDstLayout = imgutil::getDefaultLayout(dst.texture->usage);
|
||||
}
|
||||
src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout);
|
||||
dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout);
|
||||
@@ -93,7 +75,6 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
|
||||
inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
VulkanAttachment src, VulkanAttachment dst) {
|
||||
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
@@ -101,21 +82,8 @@ inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspe
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
}
|
||||
|
||||
const VkImageSubresourceRange srcRange = {
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = src.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = src.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
const VkImageSubresourceRange dstRange = {
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = dst.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = dst.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
|
||||
VkImageSubresourceRange const dstRange = dst.getSubresourceRange();
|
||||
|
||||
VulkanLayout oldSrcLayout = src.getLayout();
|
||||
VulkanLayout oldDstLayout = dst.getLayout();
|
||||
@@ -133,15 +101,15 @@ inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspe
|
||||
.extent = { src.getExtent2D().width, src.getExtent2D().height, 1 },
|
||||
}};
|
||||
vkCmdResolveImage(cmdbuffer,
|
||||
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
src.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
1, resolveRegions);
|
||||
|
||||
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
|
||||
oldSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage);
|
||||
oldSrcLayout = imgutil::getDefaultLayout(src.texture->usage);
|
||||
}
|
||||
if (oldDstLayout == VulkanLayout::UNDEFINED) {
|
||||
oldDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage);
|
||||
oldDstLayout = imgutil::getDefaultLayout(dst.texture->usage);
|
||||
}
|
||||
src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout);
|
||||
dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout);
|
||||
|
||||
@@ -27,8 +27,8 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VkBufferUsageFlags usage, uint32_t numBytes)
|
||||
: mAllocator(allocator),
|
||||
mStagePool(stagePool),
|
||||
mUsage(usage) {
|
||||
|
||||
mUsage(usage),
|
||||
mUpdatedBytes(0) {
|
||||
// for now make sure that only 1 bit is set in usage
|
||||
// (because loadFromCpu() assumes that somewhat)
|
||||
assert_invariant(usage && !(usage & (usage - 1)));
|
||||
@@ -49,8 +49,7 @@ VulkanBuffer::~VulkanBuffer() {
|
||||
}
|
||||
|
||||
void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) const {
|
||||
assert_invariant(byteOffset == 0);
|
||||
uint32_t numBytes) {
|
||||
VulkanStage const* stage = mStagePool.acquireStage(numBytes);
|
||||
void* mapped;
|
||||
vmaMapMemory(mAllocator, stage->memory, &mapped);
|
||||
@@ -58,15 +57,51 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
vmaUnmapMemory(mAllocator, stage->memory);
|
||||
vmaFlushAllocation(mAllocator, stage->memory, byteOffset, numBytes);
|
||||
|
||||
VkBufferCopy region{ .size = numBytes };
|
||||
// If there was a previous update, then we need to make sure the following write is properly
|
||||
// synced with the previous read.
|
||||
if (mUpdatedBytes > 0) {
|
||||
VkAccessFlags srcAccess = 0;
|
||||
VkPipelineStageFlags srcStage = 0;
|
||||
if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
|
||||
srcAccess = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
} else if (mUsage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) {
|
||||
srcAccess = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
} else if (mUsage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) {
|
||||
srcAccess = VK_ACCESS_INDEX_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = srcAccess,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.size = mUpdatedBytes,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1,
|
||||
&barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
VkBufferCopy region {
|
||||
.srcOffset = 0,
|
||||
.dstOffset = byteOffset,
|
||||
.size = numBytes,
|
||||
};
|
||||
vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
// Secondly, in case the user decides to upload another chunk (without ever using the first one)
|
||||
// we need to ensure that this upload completes first (hence
|
||||
// dstStageMask=VK_PIPELINE_STAGE_TRANSFER_BIT).
|
||||
VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
|
||||
if (mUsage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) {
|
||||
dstAccessMask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
||||
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
@@ -75,26 +110,24 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
} else if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
|
||||
dstAccessMask |= VK_ACCESS_UNIFORM_READ_BIT;
|
||||
// NOTE: ideally dstStageMask would include VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, but
|
||||
// this seems to be insufficient on Mali devices. To work around this we are using a more
|
||||
// aggressive ALL_GRAPHICS_BIT barrier.
|
||||
dstStageMask |= VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
dstStageMask |=
|
||||
(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
} else if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
|
||||
// TODO: implement me
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = dstAccessMask,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = dstAccessMask,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 0, nullptr, 1,
|
||||
&barrier, 0, nullptr);
|
||||
&barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
uint32_t numBytes);
|
||||
~VulkanBuffer();
|
||||
void loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) const;
|
||||
uint32_t numBytes);
|
||||
VkBuffer getGpuBuffer() const {
|
||||
return mGpuBuffer;
|
||||
}
|
||||
@@ -42,6 +42,7 @@ private:
|
||||
VmaAllocation mGpuMemory = VK_NULL_HANDLE;
|
||||
VkBuffer mGpuBuffer = VK_NULL_HANDLE;
|
||||
VkBufferUsageFlags mUsage = {};
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
// 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_GROUP_MARKERS 0x00000002
|
||||
|
||||
#define FVK_DEBUG_TEXTURE 0x00000004
|
||||
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
|
||||
@@ -75,18 +75,14 @@
|
||||
#define FVK_DEBUG_PERFORMANCE \
|
||||
FVK_DEBUG_SYSTRACE
|
||||
|
||||
#define FVK_DEBUG_CORRECTNESS \
|
||||
FVK_DEBUG_VALIDATION | \
|
||||
FVK_DEBUG_SHADER_MODULE | \
|
||||
FVK_DEBUG_TEXTURE | \
|
||||
FVK_DEBUG_LAYOUT_TRANSITION
|
||||
|
||||
#define FVK_DEBUG_RENDER_PASSES \
|
||||
FVK_DEBUG_GROUP_MARKERS | \
|
||||
FVK_DEBUG_PRINT_GROUP_MARKERS
|
||||
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
#define FVK_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FVK_DEBUG_EVERYTHING)
|
||||
#else
|
||||
#define FVK_DEBUG_FORWARDED_FLAG 0
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
|
||||
#else
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
@@ -112,7 +108,7 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
// end dependcy checks
|
||||
|
||||
// Shorthand for combination of enabled debug flags
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) && FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
|
||||
#else
|
||||
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
|
||||
|
||||
@@ -57,15 +57,19 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
|
||||
return { std::max(1u, texture->width >> level), std::max(1u, texture->height >> level) };
|
||||
}
|
||||
|
||||
VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) {
|
||||
VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
return texture->getAttachmentView(getSubresourceRange(aspect));
|
||||
return texture->getAttachmentView(getSubresourceRange());
|
||||
}
|
||||
|
||||
VkImageSubresourceRange VulkanAttachment::getSubresourceRange(VkImageAspectFlags aspect) const {
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
return texture->getImageAspect() & VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
|
||||
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
assert_invariant(texture);
|
||||
return {
|
||||
.aspectMask = aspect,
|
||||
.aspectMask = texture->getImageAspect(),
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
|
||||
@@ -44,13 +44,15 @@ struct VulkanAttachment {
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
|
||||
bool isDepth() const;
|
||||
VkImage getImage() const;
|
||||
VkFormat getFormat() const;
|
||||
VulkanLayout getLayout() const;
|
||||
VkExtent2D getExtent2D() const;
|
||||
VkImageView getImageView(VkImageAspectFlags aspect);
|
||||
VkImageView getImageView();
|
||||
// TODO: maybe embed aspect into the attachment or texture itself.
|
||||
VkImageSubresourceRange getSubresourceRange(VkImageAspectFlags aspect) const;
|
||||
VkImageSubresourceRange getSubresourceRange() const;
|
||||
};
|
||||
|
||||
class VulkanTimestamps {
|
||||
@@ -101,8 +103,12 @@ public:
|
||||
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
||||
}
|
||||
|
||||
inline VkFormatList const& getAttachmentDepthFormats() const {
|
||||
return mDepthFormats;
|
||||
inline VkFormatList const& getAttachmentDepthStencilFormats() const {
|
||||
return mDepthStencilFormats;
|
||||
}
|
||||
|
||||
inline VkFormatList const& getBlittableDepthStencilFormats() const {
|
||||
return mBlittableDepthStencilFormats;
|
||||
}
|
||||
|
||||
inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept {
|
||||
@@ -131,7 +137,8 @@ private:
|
||||
bool mDebugMarkersSupported = false;
|
||||
bool mDebugUtilsSupported = false;
|
||||
|
||||
VkFormatList mDepthFormats;
|
||||
VkFormatList mDepthStencilFormats;
|
||||
VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
// For convenience so that VulkanPlatform can initialize the private fields.
|
||||
friend class VulkanPlatform;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanDriverFactory.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
@@ -206,8 +205,6 @@ void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
return ConcreteDispatcher<VulkanDriver>::make();
|
||||
}
|
||||
@@ -262,8 +259,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mDescriptorSetManager.setPlaceHolders(mSamplerCache.getSampler({}), mEmptyTexture,
|
||||
mEmptyBufferObject);
|
||||
|
||||
mGetPipelineFunction = [this](VulkanDescriptorSetLayoutList const& layouts) {
|
||||
return mPipelineLayoutCache.getLayout(layouts);
|
||||
mGetPipelineFunction = [this](VulkanDescriptorSetLayoutList const& layouts, VulkanProgram* program) {
|
||||
return mPipelineLayoutCache.getLayout(layouts, program);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -391,12 +388,13 @@ void VulkanDriver::collectGarbage() {
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
FrameScheduledCallback callback, void* user) {
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
|
||||
@@ -920,6 +918,11 @@ bool VulkanDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthStencilBlitSupported(TextureFormat format) {
|
||||
auto const& formats = mContext.getBlittableDepthStencilFormats();
|
||||
return std::find(formats.begin(), formats.end(), getVkFormat(format)) != formats.end();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
@@ -1233,12 +1236,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLayout const currentDepthLayout = depth.getLayout();
|
||||
VulkanLayout const renderPassDepthLayout = VulkanLayout::DEPTH_ATTACHMENT;
|
||||
// We need to keep the final layout as an attachment because the implicit transition does not
|
||||
// have any barrier guarrantees, meaning that if we want to sample from the output in the next
|
||||
// pass, then we'd have a race-condition/validation error.
|
||||
VulkanLayout const finalDepthLayout = renderPassDepthLayout;
|
||||
VulkanLayout currentDepthLayout = depth.getLayout();
|
||||
|
||||
TargetBufferFlags clearVal = params.flags.clear;
|
||||
TargetBufferFlags discardEndVal = params.flags.discardEnd;
|
||||
@@ -1247,16 +1245,20 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
discardEndVal &= ~TargetBufferFlags::DEPTH;
|
||||
clearVal &= ~TargetBufferFlags::DEPTH;
|
||||
}
|
||||
auto const attachmentSubresourceRange = depth.getSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
depth.texture->setLayout(attachmentSubresourceRange, VulkanLayout::DEPTH_ATTACHMENT);
|
||||
// If the depth attachment texture was previously sampled, then we need to manually
|
||||
// transition it to an attachment. This is necessary to also set up a barrier between the
|
||||
// previous read and the potentially coming write.
|
||||
if (currentDepthLayout == VulkanLayout::DEPTH_SAMPLER) {
|
||||
depth.texture->transitionLayout(cmdbuffer, depth.getSubresourceRange(),
|
||||
VulkanLayout::DEPTH_ATTACHMENT);
|
||||
currentDepthLayout = VulkanLayout::DEPTH_ATTACHMENT;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the VkRenderPass or fetch it from cache.
|
||||
VulkanFboCache::RenderPassKey rpkey = {
|
||||
.initialColorLayoutMask = 0,
|
||||
.initialDepthLayout = currentDepthLayout,
|
||||
.renderPassDepthLayout = renderPassDepthLayout,
|
||||
.finalDepthLayout = finalDepthLayout,
|
||||
.depthFormat = depth.getFormat(),
|
||||
.clear = clearVal,
|
||||
.discardStart = discardStart,
|
||||
@@ -1273,9 +1275,9 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
rpkey.needsResolveMask |= (1 << i);
|
||||
}
|
||||
if (info.texture->getPrimaryImageLayout() != VulkanLayout::COLOR_ATTACHMENT) {
|
||||
((VulkanTexture*) info.texture)->transitionLayout(cmdbuffer,
|
||||
info.getSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT),
|
||||
VulkanLayout::COLOR_ATTACHMENT);
|
||||
((VulkanTexture*) info.texture)
|
||||
->transitionLayout(cmdbuffer, info.getSubresourceRange(),
|
||||
VulkanLayout::COLOR_ATTACHMENT);
|
||||
}
|
||||
} else {
|
||||
rpkey.colorFormat[i] = VK_FORMAT_UNDEFINED;
|
||||
@@ -1293,27 +1295,42 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
.layers = 1,
|
||||
.samples = rpkey.samples,
|
||||
};
|
||||
auto& renderPassAttachments = mRenderPassFboInfo.attachments;
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
if (!rt->getColor(i).texture) {
|
||||
fbkey.color[i] = VK_NULL_HANDLE;
|
||||
fbkey.resolve[i] = VK_NULL_HANDLE;
|
||||
} else if (fbkey.samples == 1) {
|
||||
fbkey.color[i] = rt->getColor(i).getImageView(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
auto& colorAttachment = rt->getColor(i);
|
||||
renderPassAttachments.insert(colorAttachment);
|
||||
fbkey.color[i] = colorAttachment.getImageView();
|
||||
fbkey.resolve[i] = VK_NULL_HANDLE;
|
||||
assert_invariant(fbkey.color[i]);
|
||||
} else {
|
||||
fbkey.color[i] = rt->getMsaaColor(i).getImageView(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
VulkanTexture* texture = (VulkanTexture*) rt->getColor(i).texture;
|
||||
auto& msaaColorAttachment = rt->getMsaaColor(i);
|
||||
renderPassAttachments.insert(msaaColorAttachment);
|
||||
|
||||
auto& colorAttachment = rt->getColor(i);
|
||||
fbkey.color[i] = msaaColorAttachment.getImageView();
|
||||
|
||||
VulkanTexture* texture = colorAttachment.texture;
|
||||
if (texture->samples == 1) {
|
||||
fbkey.resolve[i] = rt->getColor(i).getImageView(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
mRenderPassFboInfo.hasColorResolve = true;
|
||||
|
||||
renderPassAttachments.insert(colorAttachment);
|
||||
fbkey.resolve[i] = colorAttachment.getImageView();
|
||||
assert_invariant(fbkey.resolve[i]);
|
||||
}
|
||||
assert_invariant(fbkey.color[i]);
|
||||
}
|
||||
}
|
||||
if (depth.texture) {
|
||||
fbkey.depth = depth.getImageView(VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
fbkey.depth = depth.getImageView();
|
||||
assert_invariant(fbkey.depth);
|
||||
renderPassAttachments.insert(depth);
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const depthDiscardEnd =
|
||||
any(rpkey.discardEnd & TargetBufferFlags::DEPTH);
|
||||
|
||||
// Vulkan 1.1 does not support multisampled depth resolve, so let's check here
|
||||
// and assert if this is requested. (c.f. isAutoDepthResolveSupported)
|
||||
@@ -1322,7 +1339,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// - If the RT is MS then all SS attachments are auto resolved if not discarded.
|
||||
assert_invariant(!(rt->getSamples() > 1 &&
|
||||
rt->getDepth().texture->samples == 1 &&
|
||||
!any(rpkey.discardEnd & TargetBufferFlags::DEPTH)));
|
||||
!depthDiscardEnd));
|
||||
}
|
||||
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
|
||||
|
||||
@@ -1335,16 +1352,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
#endif
|
||||
|
||||
// The current command buffer now owns a reference to the render target and its attachments.
|
||||
// Note that we must acquire parent textures, not sidecars.
|
||||
// The current command buffer now has references to the render target and its attachments.
|
||||
commands.acquire(rt);
|
||||
if (depth.texture) {
|
||||
commands.acquire((VulkanTexture*) depth.texture);
|
||||
}
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
if (rt->getColor(i).texture) {
|
||||
commands.acquire(rt->getColor(i).texture);
|
||||
}
|
||||
for (auto const& attachment: renderPassAttachments) {
|
||||
commands.acquire(attachment.texture);
|
||||
}
|
||||
|
||||
// Populate the structures required for vkCmdBeginRenderPass.
|
||||
@@ -1430,27 +1441,51 @@ void VulkanDriver::endRenderPass(int) {
|
||||
// issue several of them when considering MRT. This would be very complex to set up and would
|
||||
// require more state tracking, so we've chosen to use a memory barrier for simplicity and
|
||||
// correctness.
|
||||
|
||||
// NOTE: ideally dstStageMask would merely be VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, but this
|
||||
// seems to be insufficient on Mali devices. To work around this we are adding a more aggressive
|
||||
// TOP_OF_PIPE barrier.
|
||||
if (!rt->isSwapChain()) {
|
||||
VkMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
};
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
if (rt->hasDepth()) {
|
||||
barrier.srcAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
srcStageMask |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
for (auto const& attachment: mRenderPassFboInfo.attachments) {
|
||||
bool const isDepth = attachment.isDepth();
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
// This is a workaround around a validation issue (might not be an actual driver issue).
|
||||
if (mRenderPassFboInfo.hasColorResolve && !isDepth) {
|
||||
srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags dstStageMask =
|
||||
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
VkAccessFlags srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
VkAccessFlags dstAccess = VK_ACCESS_SHADER_READ_BIT;
|
||||
VulkanLayout layout = VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT;
|
||||
if (isDepth) {
|
||||
srcAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dstAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
layout = VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT;
|
||||
}
|
||||
|
||||
auto const vkLayout = imgutil::getVkLayout(layout);
|
||||
auto const& range = attachment.getSubresourceRange();
|
||||
VkImageMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = srcAccess,
|
||||
.dstAccessMask = dstAccess,
|
||||
.oldLayout = vkLayout,
|
||||
.newLayout = vkLayout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = attachment.getImage(),
|
||||
.subresourceRange = range,
|
||||
};
|
||||
|
||||
attachment.texture->setLayout(range, layout);
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr,
|
||||
1, &barrier);
|
||||
}
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStageMask,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | // <== For Mali
|
||||
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
mRenderPassFboInfo.clear();
|
||||
mDescriptorSetManager.clearState();
|
||||
mCurrentRenderPass.renderTarget = nullptr;
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
@@ -1537,6 +1572,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
mSamplerBindings[index] = hwsb;
|
||||
}
|
||||
|
||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||
VulkanCommands* commands = &mCommands;
|
||||
mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index,
|
||||
value);
|
||||
}
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.insertEventMarker(string, len);
|
||||
@@ -1806,7 +1849,7 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
// This fallback path is very flaky because the dummy texture might not have
|
||||
// matching characteristics. (e.g. if the missing texture is a 3D texture)
|
||||
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) && FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
|
||||
utils::slog.w << " in material '" << program->name.c_str() << "'";
|
||||
utils::slog.w << " at binding point " << +binding << utils::io::endl;
|
||||
@@ -1814,19 +1857,21 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
texture = mEmptyTexture;
|
||||
}
|
||||
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(boundSampler->s);
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
|
||||
reinterpret_cast<uint64_t>(vksampler), bindingToName[binding].c_str());
|
||||
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
|
||||
reinterpret_cast<uint64_t>(samplerInfo.sampler), bindingToName[binding].c_str());
|
||||
#endif
|
||||
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(boundSampler->s);
|
||||
|
||||
mDescriptorSetManager.updateSampler({}, binding, texture, vksampler);
|
||||
}
|
||||
|
||||
mPipelineCache.bindLayout(mDescriptorSetManager.bind(commands, program, mGetPipelineFunction));
|
||||
auto const pipelineLayout = mDescriptorSetManager.bind(commands, program, mGetPipelineFunction);
|
||||
mBoundPipeline = {
|
||||
.program = program,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
};
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
mPipelineCache.bindPipeline(commands);
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,25 @@ namespace filament::backend {
|
||||
class VulkanPlatform;
|
||||
struct VulkanSamplerGroup;
|
||||
|
||||
// The maximum number of attachments for any renderpass (color + resolve + depth)
|
||||
constexpr uint8_t MAX_RENDERTARGET_ATTACHMENT_TEXTURES =
|
||||
MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT * 2 + 1;
|
||||
|
||||
// We need to store information about a render pass to enable better barriers at the end of a
|
||||
// renderpass.
|
||||
struct RenderPassFboBundle {
|
||||
using AttachmentArray =
|
||||
CappedArray<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
|
||||
|
||||
AttachmentArray attachments;
|
||||
bool hasColorResolve = false;
|
||||
|
||||
void clear() {
|
||||
attachments.clear();
|
||||
hasColorResolve = false;
|
||||
}
|
||||
};
|
||||
|
||||
class VulkanDriver final : public DriverBase {
|
||||
public:
|
||||
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
|
||||
@@ -141,6 +160,15 @@ private:
|
||||
|
||||
VulkanDescriptorSetManager::GetPipelineLayoutFunction mGetPipelineFunction;
|
||||
|
||||
// This is necessary for us to write to push constants after binding a pipeline.
|
||||
struct BoundPipeline {
|
||||
VulkanProgram* program;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
};
|
||||
BoundPipeline mBoundPipeline = {};
|
||||
|
||||
RenderPassFboBundle mRenderPassFboInfo;
|
||||
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,14 +29,10 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
|
||||
const RenderPassKey& k2) const {
|
||||
if (k1.initialColorLayoutMask != k2.initialColorLayoutMask) return false;
|
||||
if (k1.initialDepthLayout != k2.initialDepthLayout) return false;
|
||||
if (k1.renderPassDepthLayout != k2.renderPassDepthLayout) return false;
|
||||
if (k1.finalDepthLayout != k2.finalDepthLayout) return false;
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
if (k1.colorFormat[i] != k2.colorFormat[i]) return false;
|
||||
}
|
||||
@@ -197,7 +193,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) {
|
||||
continue;
|
||||
}
|
||||
const VkImageLayout subpassLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT);
|
||||
const VkImageLayout subpassLayout = imgutil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT);
|
||||
uint32_t index;
|
||||
|
||||
if (!hasSubpasses) {
|
||||
@@ -243,9 +239,9 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.stencilLoadOp = kDontCare,
|
||||
.stencilStoreOp = kDisableStore,
|
||||
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
|
||||
? ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT)
|
||||
: ImgUtil::getVkLayout(VulkanLayout::UNDEFINED),
|
||||
.finalLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
|
||||
? imgutil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT)
|
||||
: imgutil::getVkLayout(VulkanLayout::UNDEFINED),
|
||||
.finalLayout = imgutil::getVkLayout(FINAL_COLOR_ATTACHMENT_LAYOUT),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -272,7 +268,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
|
||||
pResolveAttachment->attachment = attachmentIndex;
|
||||
pResolveAttachment->layout
|
||||
= ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT_RESOLVE);
|
||||
= imgutil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT_RESOLVE);
|
||||
++pResolveAttachment;
|
||||
|
||||
attachments[attachmentIndex++] = {
|
||||
@@ -282,8 +278,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.storeOp = kEnableStore,
|
||||
.stencilLoadOp = kDontCare,
|
||||
.stencilStoreOp = kDisableStore,
|
||||
.initialLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
|
||||
.finalLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
|
||||
.initialLayout = imgutil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
|
||||
.finalLayout = imgutil::getVkLayout(FINAL_COLOR_ATTACHMENT_LAYOUT),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -292,7 +288,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
const bool clear = any(config.clear & TargetBufferFlags::DEPTH);
|
||||
const bool discardStart = any(config.discardStart & TargetBufferFlags::DEPTH);
|
||||
const bool discardEnd = any(config.discardEnd & TargetBufferFlags::DEPTH);
|
||||
depthAttachmentRef.layout = ImgUtil::getVkLayout(config.renderPassDepthLayout);
|
||||
depthAttachmentRef.layout = imgutil::getVkLayout(VulkanLayout::DEPTH_ATTACHMENT);
|
||||
depthAttachmentRef.attachment = attachmentIndex;
|
||||
attachments[attachmentIndex++] = {
|
||||
.format = config.depthFormat,
|
||||
@@ -301,8 +297,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.storeOp = discardEnd ? kDisableStore : kEnableStore,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = ImgUtil::getVkLayout(config.initialDepthLayout),
|
||||
.finalLayout = ImgUtil::getVkLayout(config.finalDepthLayout),
|
||||
.initialLayout = imgutil::getVkLayout(config.initialDepthLayout),
|
||||
.finalLayout = imgutil::getVkLayout(FINAL_DEPTH_ATTACHMENT_LAYOUT),
|
||||
};
|
||||
}
|
||||
renderPassInfo.attachmentCount = attachmentIndex;
|
||||
|
||||
@@ -35,24 +35,25 @@ namespace filament::backend {
|
||||
//
|
||||
class VulkanFboCache {
|
||||
public:
|
||||
constexpr static VulkanLayout FINAL_COLOR_ATTACHMENT_LAYOUT = VulkanLayout::COLOR_ATTACHMENT;
|
||||
constexpr static VulkanLayout FINAL_RESOLVE_ATTACHMENT_LAYOUT = VulkanLayout::COLOR_ATTACHMENT;
|
||||
constexpr static VulkanLayout FINAL_DEPTH_ATTACHMENT_LAYOUT = VulkanLayout::DEPTH_ATTACHMENT;
|
||||
|
||||
// RenderPassKey is a small POD representing the immutable state that is used to construct
|
||||
// a VkRenderPass. It is hashed and used as a lookup key.
|
||||
// TODO: This struct can be reduced in size by using a subset of formats instead of VkFormat
|
||||
// and removing the "finalDepthLayout" field.
|
||||
struct alignas(8) RenderPassKey {
|
||||
// For each target, we need to know three image layouts: the layout BEFORE the pass, the
|
||||
// layout DURING the pass, and the layout AFTER the pass. Here are the rules:
|
||||
// - For depth, we explicitly specify all three layouts.
|
||||
// - Color targets have their initial image layout specified with a bitmask.
|
||||
// - For each color target, the pre-existing layout is either UNDEFINED (0) or GENERAL (1).
|
||||
// - The render pass and final images layout for color buffers is always GENERAL.
|
||||
// - The render pass and final images layout for color buffers is always
|
||||
// VulkanLayout::COLOR_ATTACHMENT.
|
||||
uint8_t initialColorLayoutMask;
|
||||
|
||||
// Note that if VulkanLayout grows beyond 16, we'd need to up this.
|
||||
VulkanLayout initialDepthLayout : 4;
|
||||
VulkanLayout renderPassDepthLayout : 4;
|
||||
VulkanLayout finalDepthLayout : 4;
|
||||
uint8_t padding0 : 4;
|
||||
VulkanLayout initialDepthLayout : 8;
|
||||
uint8_t padding0;
|
||||
uint8_t padding1;
|
||||
|
||||
VkFormat colorFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 32 bytes
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
uint8_t samples; // 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
uint8_t subpassMask; // 1 byte
|
||||
bool padding2; // 1 byte
|
||||
uint8_t padding2; // 1 byte
|
||||
};
|
||||
struct RenderPassVal {
|
||||
VkRenderPass handle;
|
||||
|
||||
@@ -65,21 +65,6 @@ static constexpr Bitmask fromStageFlags(ShaderStageFlags2 flags, uint8_t binding
|
||||
return ret;
|
||||
}
|
||||
|
||||
UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags flags, UsageFlags src) {
|
||||
// NOTE: if you modify this function, you also need to modify getShaderStageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
src.set(binding);
|
||||
}
|
||||
if (any(flags & ShaderStageFlags::FRAGMENT)) {
|
||||
src.set(MAX_SAMPLER_COUNT + binding);
|
||||
}
|
||||
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
|
||||
// has 186 bits (MAX_SAMPLER_COUNT * 3)
|
||||
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
|
||||
return src;
|
||||
}
|
||||
|
||||
constexpr decltype(VulkanProgram::MAX_SHADER_MODULES) MAX_SHADER_MODULES =
|
||||
VulkanProgram::MAX_SHADER_MODULES;
|
||||
|
||||
@@ -127,27 +112,82 @@ inline VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device,
|
||||
return layout;
|
||||
}
|
||||
|
||||
inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) {
|
||||
switch(stage) {
|
||||
case backend::ShaderStage::VERTEX:
|
||||
return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
case backend::ShaderStage::FRAGMENT:
|
||||
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
case backend::ShaderStage::COMPUTE:
|
||||
PANIC_POSTCONDITION("Unsupported stage");
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo const& info,
|
||||
Bitmask const& bitmask)
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device,
|
||||
VkDescriptorSetLayoutCreateInfo const& info, Bitmask const& bitmask)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
|
||||
mDevice(device),
|
||||
vklayout(createDescriptorSetLayout(device, info)),
|
||||
bitmask(bitmask),
|
||||
bindings(getBindings(bitmask)),
|
||||
count(Count::fromLayoutBitmask(bitmask)) {
|
||||
}
|
||||
count(Count::fromLayoutBitmask(bitmask)) {}
|
||||
|
||||
VulkanDescriptorSetLayout::~VulkanDescriptorSetLayout() {
|
||||
vkDestroyDescriptorSetLayout(mDevice, vklayout, VKALLOC);
|
||||
}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
|
||||
mRangeCount = 0;
|
||||
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
|
||||
auto const& constants = program.getPushConstants(stage);
|
||||
if (constants.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We store the type of the constant for type-checking when writing.
|
||||
auto& types = mTypes[(uint8_t) stage];
|
||||
types.reserve(constants.size());
|
||||
std::for_each(constants.cbegin(), constants.cend(), [&types] (Program::PushConstant t) {
|
||||
types.push_back(t.type);
|
||||
});
|
||||
|
||||
mRanges[mRangeCount++] = {
|
||||
.stageFlags = getVkStage(stage),
|
||||
.offset = 0,
|
||||
.size = (uint32_t) constants.size() * ENTRY_SIZE,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
VulkanCommandBuffer* cmdbuf = &(commands->get());
|
||||
uint32_t binaryValue = 0;
|
||||
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
assert_invariant(types[index] == ConstantType::BOOL);
|
||||
bool const bval = std::get<bool>(value);
|
||||
binaryValue = static_cast<uint32_t const>(bval ? VK_TRUE : VK_FALSE);
|
||||
} else if (std::holds_alternative<float>(value)) {
|
||||
assert_invariant(types[index] == ConstantType::FLOAT);
|
||||
float const fval = std::get<float>(value);
|
||||
binaryValue = *reinterpret_cast<uint32_t const*>(&fval);
|
||||
} else {
|
||||
assert_invariant(types[index] == ConstantType::INT);
|
||||
int const ival = std::get<int>(value);
|
||||
binaryValue = *reinterpret_cast<uint32_t const*>(&ival);
|
||||
}
|
||||
vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
|
||||
&binaryValue);
|
||||
}
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
: HwProgram(builder.getName()),
|
||||
VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new PipelineInfo()),
|
||||
mInfo(new(std::nothrow) PipelineInfo(builder)),
|
||||
mDevice(device) {
|
||||
|
||||
constexpr uint8_t UBO_MODULE_OFFSET = (sizeof(UniformBufferBitmask) * 8) / MAX_SHADER_MODULES;
|
||||
@@ -236,7 +276,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
auto& groupInfo = builder.getSamplerGroupInfo();
|
||||
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
|
||||
auto& bindings = mInfo->bindings;
|
||||
auto& usage = mInfo->usage;
|
||||
for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) {
|
||||
auto const& group = groupInfo[groupInd];
|
||||
auto const& samplers = group.samplers;
|
||||
@@ -245,7 +284,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
|
||||
assert_invariant(bindings.find(binding) == bindings.end());
|
||||
bindings.insert(binding);
|
||||
usage = getUsageFlags(binding, group.stageFlags, usage);
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
bindingToName[binding] = samplers[i].name.c_str();
|
||||
@@ -302,13 +340,13 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
|
||||
// Constrain the sample count according to both kinds of sample count masks obtained from
|
||||
// VkPhysicalDeviceProperties. This is consistent with the VulkanTexture constructor.
|
||||
const auto& limits = context.getPhysicalDeviceLimits();
|
||||
auto const& limits = context.getPhysicalDeviceLimits();
|
||||
mSamples = samples = reduceSampleCount(samples, limits.framebufferDepthSampleCounts &
|
||||
limits.framebufferColorSampleCounts);
|
||||
|
||||
// Create sidecar MSAA textures for color attachments if they don't already exist.
|
||||
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
||||
const VulkanAttachment& spec = color[index];
|
||||
VulkanAttachment const& spec = color[index];
|
||||
VulkanTexture* texture = (VulkanTexture*) spec.texture;
|
||||
if (texture && texture->samples == 1) {
|
||||
auto msTexture = texture->getSidecar();
|
||||
@@ -371,19 +409,19 @@ VkExtent2D VulkanRenderTarget::getExtent() const {
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getColor(int target) const {
|
||||
VulkanAttachment& VulkanRenderTarget::getColor(int target) {
|
||||
return mColor[target];
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getMsaaColor(int target) const {
|
||||
VulkanAttachment& VulkanRenderTarget::getMsaaColor(int target) {
|
||||
return mMsaaAttachments[target];
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getDepth() const {
|
||||
VulkanAttachment& VulkanRenderTarget::getDepth() {
|
||||
return mDepth;
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getMsaaDepth() const {
|
||||
VulkanAttachment& VulkanRenderTarget::getMsaaDepth() {
|
||||
return mMsaaDepthAttachment;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
#include "utils/FixedCapacityVector.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include <private/backend/SamplerGroup.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/StructureOfArrays.h>
|
||||
|
||||
@@ -180,6 +180,28 @@ private:
|
||||
using VulkanDescriptorSetList = std::array<Handle<VulkanDescriptorSet>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
struct PushConstantDescription {
|
||||
|
||||
explicit PushConstantDescription(backend::Program const& program) noexcept;
|
||||
|
||||
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
|
||||
|
||||
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
||||
|
||||
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||
uint8_t index, backend::PushConstantVariant const& value);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t ENTRY_SIZE = sizeof(uint32_t);
|
||||
|
||||
utils::FixedCapacityVector<backend::ConstantType> mTypes[Program::SHADER_TYPE_COUNT];
|
||||
VkPushConstantRange mRanges[Program::SHADER_TYPE_COUNT];
|
||||
uint32_t mRangeCount;
|
||||
};
|
||||
|
||||
struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
|
||||
@@ -198,10 +220,6 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
return mInfo->bindingToSamplerIndex;
|
||||
}
|
||||
|
||||
inline UsageFlags getUsage() const {
|
||||
return mInfo->usage;
|
||||
}
|
||||
|
||||
// Get a list of the sampler binding indices so that we don't have to loop through all possible
|
||||
// samplers.
|
||||
inline BindingList const& getBindings() const { return mInfo->bindings; }
|
||||
@@ -216,6 +234,19 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
inline LayoutDescriptionList const& getLayoutDescriptionList() const { return mInfo->layouts; }
|
||||
|
||||
inline uint32_t getPushConstantRangeCount() const {
|
||||
return mInfo->pushConstantDescription.getVkRangeCount();
|
||||
}
|
||||
|
||||
inline VkPushConstantRange const* getPushConstantRanges() const {
|
||||
return mInfo->pushConstantDescription.getVkRanges();
|
||||
}
|
||||
|
||||
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
|
||||
return mInfo->bindingToName;
|
||||
@@ -228,17 +259,14 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
private:
|
||||
struct PipelineInfo {
|
||||
PipelineInfo()
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
|
||||
explicit PipelineInfo(backend::Program const& program) noexcept
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
|
||||
pushConstantDescription(program)
|
||||
#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).
|
||||
UsageFlags usage;
|
||||
|
||||
BindingList bindings;
|
||||
|
||||
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
|
||||
@@ -249,6 +277,8 @@ private:
|
||||
// descset::DescriptorSetLayout layout;
|
||||
LayoutDescriptionList layouts;
|
||||
|
||||
PushConstantDescription pushConstantDescription;
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
// We store the sampler name mapped from binding index (only for debug purposes).
|
||||
utils::FixedCapacityVector<std::string> bindingToName;
|
||||
@@ -282,10 +312,11 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
void transformClientRectToPlatform(VkRect2D* bounds) const;
|
||||
void transformClientRectToPlatform(VkViewport* bounds) const;
|
||||
VkExtent2D getExtent() const;
|
||||
VulkanAttachment getColor(int target) const;
|
||||
VulkanAttachment getMsaaColor(int target) const;
|
||||
VulkanAttachment getDepth() const;
|
||||
VulkanAttachment getMsaaDepth() const;
|
||||
// We return references in the following methods to avoid a copy.
|
||||
VulkanAttachment& getColor(int target);
|
||||
VulkanAttachment& getMsaaColor(int target);
|
||||
VulkanAttachment& getDepth();
|
||||
VulkanAttachment& getMsaaDepth();
|
||||
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
|
||||
uint8_t getSamples() const { return mSamples; }
|
||||
bool hasDepth() const { return mDepth.texture; }
|
||||
|
||||
@@ -26,38 +26,10 @@
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
namespace filament::backend::imgutil {
|
||||
|
||||
namespace {
|
||||
|
||||
inline VkImageLayout getVkImageLayout(VulkanLayout layout) {
|
||||
switch (layout) {
|
||||
case VulkanLayout::UNDEFINED:
|
||||
return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::READ_ONLY:
|
||||
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
case VulkanLayout::TRANSFER_SRC:
|
||||
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
case VulkanLayout::TRANSFER_DST:
|
||||
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
case VulkanLayout::DEPTH_ATTACHMENT:
|
||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
case VulkanLayout::DEPTH_SAMPLER:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::PRESENT:
|
||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
// Filament sometimes samples from one miplevel while writing to another level in the same
|
||||
// texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive layout
|
||||
// transitions. So, keep it simple and use GENERAL for all color-attachable textures.
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
|
||||
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::tuple<VkAccessFlags, VkAccessFlags, VkPipelineStageFlags, VkPipelineStageFlags,
|
||||
VkImageLayout, VkImageLayout>
|
||||
getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
@@ -70,11 +42,11 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
break;
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
@@ -154,27 +126,12 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
}
|
||||
|
||||
return std::make_tuple(srcAccessMask, dstAccessMask, srcStage, dstStage,
|
||||
getVkImageLayout(transition.oldLayout), getVkImageLayout(transition.newLayout));
|
||||
getVkLayout(transition.oldLayout), getVkLayout(transition.newLayout));
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
VkImageViewType VulkanImageUtility::getViewType(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
default:
|
||||
return VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanImageUtility::transitionLayout(VkCommandBuffer cmdbuffer,
|
||||
void transitionLayout(VkCommandBuffer cmdbuffer,
|
||||
VulkanLayoutTransition transition) {
|
||||
if (transition.oldLayout == transition.newLayout) {
|
||||
return;
|
||||
@@ -197,10 +154,6 @@ void VulkanImageUtility::transitionLayout(VkCommandBuffer cmdbuffer,
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
VkImageLayout VulkanImageUtility::getVkLayout(VulkanLayout layout) {
|
||||
return getVkImageLayout(layout);
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||
@@ -217,12 +170,12 @@ bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange&
|
||||
return false;
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION | FVK_DEBUG_TEXTURE)
|
||||
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
#define CASE(VALUE) \
|
||||
case filament::backend::VulkanLayout::VALUE: { \
|
||||
out << #VALUE; \
|
||||
out << " [" \
|
||||
<< filament::backend::VulkanImageUtility::getVkLayout( \
|
||||
<< filament::backend::imgutil::getVkLayout( \
|
||||
filament::backend::VulkanLayout::VALUE) \
|
||||
<< "]"; \
|
||||
break; \
|
||||
|
||||
@@ -59,44 +59,85 @@ struct VulkanLayoutTransition {
|
||||
VkImageSubresourceRange subresources;
|
||||
};
|
||||
|
||||
class VulkanImageUtility {
|
||||
public:
|
||||
static VkImageViewType getViewType(SamplerType target);
|
||||
namespace imgutil {
|
||||
|
||||
inline static VulkanLayout getDefaultLayout(TextureUsage usage) {
|
||||
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
return VulkanLayout::DEPTH_SAMPLER;
|
||||
} else {
|
||||
return VulkanLayout::DEPTH_ATTACHMENT;
|
||||
}
|
||||
}
|
||||
inline VkImageViewType getViewType(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
default:
|
||||
return VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
}
|
||||
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
return VulkanLayout::COLOR_ATTACHMENT;
|
||||
inline VulkanLayout getDefaultLayout(TextureUsage usage) {
|
||||
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
return VulkanLayout::DEPTH_SAMPLER;
|
||||
} else {
|
||||
return VulkanLayout::DEPTH_ATTACHMENT;
|
||||
}
|
||||
// Finally, the layout for an immutable texture is optimal read-only.
|
||||
return VulkanLayout::READ_ONLY;
|
||||
}
|
||||
|
||||
inline static VulkanLayout getDefaultLayout(VkImageUsageFlags vkusage) {
|
||||
TextureUsage usage {};
|
||||
if (vkusage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
|
||||
usage = usage | TextureUsage::DEPTH_ATTACHMENT;
|
||||
}
|
||||
if (vkusage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
|
||||
usage = usage | TextureUsage::COLOR_ATTACHMENT;
|
||||
}
|
||||
if (vkusage & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
usage = usage | TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
return getDefaultLayout(usage);
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
return VulkanLayout::COLOR_ATTACHMENT;
|
||||
}
|
||||
// Finally, the layout for an immutable texture is optimal read-only.
|
||||
return VulkanLayout::READ_ONLY;
|
||||
}
|
||||
|
||||
static VkImageLayout getVkLayout(VulkanLayout layout);
|
||||
|
||||
static void transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition);
|
||||
};
|
||||
inline VulkanLayout getDefaultLayout(VkImageUsageFlags vkusage) {
|
||||
TextureUsage usage{};
|
||||
if (vkusage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
|
||||
usage = usage | TextureUsage::DEPTH_ATTACHMENT;
|
||||
}
|
||||
if (vkusage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
|
||||
usage = usage | TextureUsage::COLOR_ATTACHMENT;
|
||||
}
|
||||
if (vkusage & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
usage = usage | TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
return getDefaultLayout(usage);
|
||||
}
|
||||
|
||||
constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) {
|
||||
switch (layout) {
|
||||
case VulkanLayout::UNDEFINED:
|
||||
return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::READ_ONLY:
|
||||
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
case VulkanLayout::TRANSFER_SRC:
|
||||
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
case VulkanLayout::TRANSFER_DST:
|
||||
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
case VulkanLayout::DEPTH_ATTACHMENT:
|
||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
case VulkanLayout::DEPTH_SAMPLER:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::PRESENT:
|
||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
// Filament sometimes samples from one miplevel while writing to another level in the
|
||||
// same texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive
|
||||
// layout transitions. So, keep it simple and use GENERAL for all color-attachable
|
||||
// textures.
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
|
||||
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
void transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition);
|
||||
|
||||
} // namespace imgutil
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
using TaskHandler = VulkanReadPixels::TaskHandler;
|
||||
using WorkloadFunc = TaskHandler::WorkloadFunc;
|
||||
using OnCompleteFunc = TaskHandler::OnCompleteFunc;
|
||||
@@ -118,7 +117,7 @@ void VulkanReadPixels::terminate() noexcept {
|
||||
VulkanReadPixels::VulkanReadPixels(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x, uint32_t const y,
|
||||
void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint32_t const y,
|
||||
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
|
||||
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc) {
|
||||
@@ -218,7 +217,7 @@ void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x
|
||||
};
|
||||
vkBeginCommandBuffer(cmdbuffer, &binfo);
|
||||
|
||||
ImgUtil::transitionLayout(cmdbuffer, {
|
||||
imgutil::transitionLayout(cmdbuffer, {
|
||||
.image = stagingImage,
|
||||
.oldLayout = VulkanLayout::UNDEFINED,
|
||||
.newLayout = VulkanLayout::TRANSFER_DST,
|
||||
@@ -232,8 +231,7 @@ void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x
|
||||
});
|
||||
|
||||
VulkanAttachment const srcAttachment = srcTarget->getColor(0);
|
||||
const VkImageSubresourceRange srcRange
|
||||
= srcAttachment.getSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange();
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
|
||||
|
||||
VkImageCopy const imageCopyRegion = {
|
||||
@@ -266,8 +264,8 @@ void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x
|
||||
imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
|
||||
|
||||
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(),
|
||||
ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC), stagingImage,
|
||||
ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion);
|
||||
imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC), stagingImage,
|
||||
imgutil::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion);
|
||||
|
||||
// Restore the source image layout.
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::COLOR_ATTACHMENT);
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -74,8 +72,8 @@ public:
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void run(VulkanRenderTarget const* srcTarget, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
|
||||
void run(VulkanRenderTarget* srcTarget, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
|
||||
SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "VulkanStagePool.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
@@ -118,7 +119,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
// VK_IMAGE_LAYOUT_PREINITIALIZED or VK_IMAGE_LAYOUT_GENERAL layout. Calling
|
||||
// vkGetImageSubresourceLayout for a linear image returns a subresource layout mapping that is
|
||||
// valid for either of those image layouts."
|
||||
VulkanImageUtility::transitionLayout(cmdbuffer, {
|
||||
imgutil::transitionLayout(cmdbuffer, {
|
||||
.image = image->image,
|
||||
.oldLayout = VulkanLayout::UNDEFINED,
|
||||
.newLayout = VulkanLayout::READ_WRITE, // (= VK_IMAGE_LAYOUT_GENERAL)
|
||||
|
||||
@@ -28,7 +28,6 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
|
||||
@@ -37,7 +36,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanComm
|
||||
VulkanResource(
|
||||
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
|
||||
mVkFormat(format),
|
||||
mViewType(ImgUtil::getViewType(target)),
|
||||
mViewType(imgutil::getViewType(target)),
|
||||
mSwizzle({}),
|
||||
mTextureImage(image),
|
||||
mFullViewRange{
|
||||
@@ -62,7 +61,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanResource(
|
||||
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
|
||||
mVkFormat(backend::getVkFormat(tformat)),
|
||||
mViewType(ImgUtil::getViewType(target)),
|
||||
mViewType(imgutil::getViewType(target)),
|
||||
mSwizzle(swizzle),
|
||||
mStagePool(stagePool),
|
||||
mDevice(device),
|
||||
@@ -236,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
commands.acquire(this);
|
||||
transitionLayout(cmdbuf, mFullViewRange, ImgUtil::getDefaultLayout(imageInfo.usage));
|
||||
transitionLayout(cmdbuf, mFullViewRange, imgutil::getDefaultLayout(imageInfo.usage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,10 +326,10 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
|
||||
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
|
||||
VulkanLayout nextLayout = getLayout(transitionRange.baseArrayLayer, miplevel);
|
||||
VkImageLayout const newVkLayout = ImgUtil::getVkLayout(newLayout);
|
||||
VkImageLayout const newVkLayout = imgutil::getVkLayout(newLayout);
|
||||
|
||||
if (nextLayout == VulkanLayout::UNDEFINED) {
|
||||
nextLayout = ImgUtil::getDefaultLayout(this->usage);
|
||||
nextLayout = imgutil::getDefaultLayout(this->usage);
|
||||
}
|
||||
|
||||
transitionLayout(cmdbuf, transitionRange, newLayout);
|
||||
@@ -374,8 +373,8 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
|
||||
VulkanLayout const oldLayout = getLayout(layer, miplevel);
|
||||
transitionLayout(cmdbuf, range, newLayout);
|
||||
|
||||
vkCmdBlitImage(cmdbuf, stage->image, ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
mTextureImage, ImgUtil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
|
||||
vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
|
||||
|
||||
transitionLayout(cmdbuf, range, oldLayout);
|
||||
}
|
||||
@@ -467,7 +466,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres
|
||||
for (uint32_t i = firstLayer; i < lastLayer; ++i) {
|
||||
for (uint32_t j = firstLevel; j < lastLevel; ++j) {
|
||||
VulkanLayout const layout = getLayout(i, j);
|
||||
ImgUtil::transitionLayout(cmdbuf, {
|
||||
imgutil::transitionLayout(cmdbuf, {
|
||||
.image = mTextureImage,
|
||||
.oldLayout = layout,
|
||||
.newLayout = newLayout,
|
||||
@@ -482,7 +481,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImgUtil::transitionLayout(cmdbuf, {
|
||||
imgutil::transitionLayout(cmdbuf, {
|
||||
.image = mTextureImage,
|
||||
.oldLayout = oldLayout,
|
||||
.newLayout = newLayout,
|
||||
|
||||
@@ -655,6 +655,4 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
||||
return mostSignificantBit((sampleCount - 1) & mask);
|
||||
}
|
||||
|
||||
_BitCountHelper _BitCountHelper::BitCounter = {};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -90,12 +90,321 @@ utils::FixedCapacityVector<OutType> enumerate(
|
||||
#undef EXPAND_ENUM_NO_ARGS
|
||||
#undef EXPAND_ENUM_ARGS
|
||||
|
||||
// Used across pipeline related classes.
|
||||
using UsageFlags = utils::bitset128;
|
||||
|
||||
// Useful shorthands
|
||||
using VkFormatList = utils::FixedCapacityVector<VkFormat>;
|
||||
|
||||
// Copied from
|
||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html
|
||||
constexpr VkFormat ALL_VK_FORMATS[] = {
|
||||
VK_FORMAT_UNDEFINED,
|
||||
VK_FORMAT_R4G4_UNORM_PACK8,
|
||||
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
|
||||
VK_FORMAT_B4G4R4A4_UNORM_PACK16,
|
||||
VK_FORMAT_R5G6B5_UNORM_PACK16,
|
||||
VK_FORMAT_B5G6R5_UNORM_PACK16,
|
||||
VK_FORMAT_R5G5B5A1_UNORM_PACK16,
|
||||
VK_FORMAT_B5G5R5A1_UNORM_PACK16,
|
||||
VK_FORMAT_A1R5G5B5_UNORM_PACK16,
|
||||
VK_FORMAT_R8_UNORM,
|
||||
VK_FORMAT_R8_SNORM,
|
||||
VK_FORMAT_R8_USCALED,
|
||||
VK_FORMAT_R8_SSCALED,
|
||||
VK_FORMAT_R8_UINT,
|
||||
VK_FORMAT_R8_SINT,
|
||||
VK_FORMAT_R8_SRGB,
|
||||
VK_FORMAT_R8G8_UNORM,
|
||||
VK_FORMAT_R8G8_SNORM,
|
||||
VK_FORMAT_R8G8_USCALED,
|
||||
VK_FORMAT_R8G8_SSCALED,
|
||||
VK_FORMAT_R8G8_UINT,
|
||||
VK_FORMAT_R8G8_SINT,
|
||||
VK_FORMAT_R8G8_SRGB,
|
||||
VK_FORMAT_R8G8B8_UNORM,
|
||||
VK_FORMAT_R8G8B8_SNORM,
|
||||
VK_FORMAT_R8G8B8_USCALED,
|
||||
VK_FORMAT_R8G8B8_SSCALED,
|
||||
VK_FORMAT_R8G8B8_UINT,
|
||||
VK_FORMAT_R8G8B8_SINT,
|
||||
VK_FORMAT_R8G8B8_SRGB,
|
||||
VK_FORMAT_B8G8R8_UNORM,
|
||||
VK_FORMAT_B8G8R8_SNORM,
|
||||
VK_FORMAT_B8G8R8_USCALED,
|
||||
VK_FORMAT_B8G8R8_SSCALED,
|
||||
VK_FORMAT_B8G8R8_UINT,
|
||||
VK_FORMAT_B8G8R8_SINT,
|
||||
VK_FORMAT_B8G8R8_SRGB,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_FORMAT_R8G8B8A8_SNORM,
|
||||
VK_FORMAT_R8G8B8A8_USCALED,
|
||||
VK_FORMAT_R8G8B8A8_SSCALED,
|
||||
VK_FORMAT_R8G8B8A8_UINT,
|
||||
VK_FORMAT_R8G8B8A8_SINT,
|
||||
VK_FORMAT_R8G8B8A8_SRGB,
|
||||
VK_FORMAT_B8G8R8A8_UNORM,
|
||||
VK_FORMAT_B8G8R8A8_SNORM,
|
||||
VK_FORMAT_B8G8R8A8_USCALED,
|
||||
VK_FORMAT_B8G8R8A8_SSCALED,
|
||||
VK_FORMAT_B8G8R8A8_UINT,
|
||||
VK_FORMAT_B8G8R8A8_SINT,
|
||||
VK_FORMAT_B8G8R8A8_SRGB,
|
||||
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_SNORM_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_USCALED_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_UINT_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_SINT_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_SRGB_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_UNORM_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_SNORM_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_USCALED_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_UINT_PACK32,
|
||||
VK_FORMAT_A2R10G10B10_SINT_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_SNORM_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_USCALED_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_UINT_PACK32,
|
||||
VK_FORMAT_A2B10G10R10_SINT_PACK32,
|
||||
VK_FORMAT_R16_UNORM,
|
||||
VK_FORMAT_R16_SNORM,
|
||||
VK_FORMAT_R16_USCALED,
|
||||
VK_FORMAT_R16_SSCALED,
|
||||
VK_FORMAT_R16_UINT,
|
||||
VK_FORMAT_R16_SINT,
|
||||
VK_FORMAT_R16_SFLOAT,
|
||||
VK_FORMAT_R16G16_UNORM,
|
||||
VK_FORMAT_R16G16_SNORM,
|
||||
VK_FORMAT_R16G16_USCALED,
|
||||
VK_FORMAT_R16G16_SSCALED,
|
||||
VK_FORMAT_R16G16_UINT,
|
||||
VK_FORMAT_R16G16_SINT,
|
||||
VK_FORMAT_R16G16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16_UNORM,
|
||||
VK_FORMAT_R16G16B16_SNORM,
|
||||
VK_FORMAT_R16G16B16_USCALED,
|
||||
VK_FORMAT_R16G16B16_SSCALED,
|
||||
VK_FORMAT_R16G16B16_UINT,
|
||||
VK_FORMAT_R16G16B16_SINT,
|
||||
VK_FORMAT_R16G16B16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_UNORM,
|
||||
VK_FORMAT_R16G16B16A16_SNORM,
|
||||
VK_FORMAT_R16G16B16A16_USCALED,
|
||||
VK_FORMAT_R16G16B16A16_SSCALED,
|
||||
VK_FORMAT_R16G16B16A16_UINT,
|
||||
VK_FORMAT_R16G16B16A16_SINT,
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_R32_UINT,
|
||||
VK_FORMAT_R32_SINT,
|
||||
VK_FORMAT_R32_SFLOAT,
|
||||
VK_FORMAT_R32G32_UINT,
|
||||
VK_FORMAT_R32G32_SINT,
|
||||
VK_FORMAT_R32G32_SFLOAT,
|
||||
VK_FORMAT_R32G32B32_UINT,
|
||||
VK_FORMAT_R32G32B32_SINT,
|
||||
VK_FORMAT_R32G32B32_SFLOAT,
|
||||
VK_FORMAT_R32G32B32A32_UINT,
|
||||
VK_FORMAT_R32G32B32A32_SINT,
|
||||
VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
VK_FORMAT_R64_UINT,
|
||||
VK_FORMAT_R64_SINT,
|
||||
VK_FORMAT_R64_SFLOAT,
|
||||
VK_FORMAT_R64G64_UINT,
|
||||
VK_FORMAT_R64G64_SINT,
|
||||
VK_FORMAT_R64G64_SFLOAT,
|
||||
VK_FORMAT_R64G64B64_UINT,
|
||||
VK_FORMAT_R64G64B64_SINT,
|
||||
VK_FORMAT_R64G64B64_SFLOAT,
|
||||
VK_FORMAT_R64G64B64A64_UINT,
|
||||
VK_FORMAT_R64G64B64A64_SINT,
|
||||
VK_FORMAT_R64G64B64A64_SFLOAT,
|
||||
VK_FORMAT_B10G11R11_UFLOAT_PACK32,
|
||||
VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
|
||||
VK_FORMAT_D16_UNORM,
|
||||
VK_FORMAT_X8_D24_UNORM_PACK32,
|
||||
VK_FORMAT_D32_SFLOAT,
|
||||
VK_FORMAT_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM_S8_UINT,
|
||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_BC1_RGB_UNORM_BLOCK,
|
||||
VK_FORMAT_BC1_RGB_SRGB_BLOCK,
|
||||
VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
|
||||
VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
|
||||
VK_FORMAT_BC2_UNORM_BLOCK,
|
||||
VK_FORMAT_BC2_SRGB_BLOCK,
|
||||
VK_FORMAT_BC3_UNORM_BLOCK,
|
||||
VK_FORMAT_BC3_SRGB_BLOCK,
|
||||
VK_FORMAT_BC4_UNORM_BLOCK,
|
||||
VK_FORMAT_BC4_SNORM_BLOCK,
|
||||
VK_FORMAT_BC5_UNORM_BLOCK,
|
||||
VK_FORMAT_BC5_SNORM_BLOCK,
|
||||
VK_FORMAT_BC6H_UFLOAT_BLOCK,
|
||||
VK_FORMAT_BC6H_SFLOAT_BLOCK,
|
||||
VK_FORMAT_BC7_UNORM_BLOCK,
|
||||
VK_FORMAT_BC7_SRGB_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
|
||||
VK_FORMAT_EAC_R11_UNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11_SNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11G11_UNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11G11_SNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_5x4_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_8x5_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_8x8_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_10x5_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_10x6_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_12x10_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
|
||||
VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
|
||||
VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
|
||||
VK_FORMAT_G8B8G8R8_422_UNORM,
|
||||
VK_FORMAT_B8G8R8G8_422_UNORM,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
|
||||
VK_FORMAT_R10X6_UNORM_PACK16,
|
||||
VK_FORMAT_R10X6G10X6_UNORM_2PACK16,
|
||||
VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
|
||||
VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
|
||||
VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
|
||||
VK_FORMAT_R12X4_UNORM_PACK16,
|
||||
VK_FORMAT_R12X4G12X4_UNORM_2PACK16,
|
||||
VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16,
|
||||
VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
|
||||
VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16,
|
||||
VK_FORMAT_G16B16G16R16_422_UNORM,
|
||||
VK_FORMAT_B16G16R16G16_422_UNORM,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_444_UNORM,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_444_UNORM,
|
||||
VK_FORMAT_A4R4G4B4_UNORM_PACK16,
|
||||
VK_FORMAT_A4B4G4R4_UNORM_PACK16,
|
||||
VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK,
|
||||
VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK,
|
||||
VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG,
|
||||
VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG,
|
||||
// Not supported (yet) by bluevk
|
||||
// VK_FORMAT_R16G16_SFIXED5_NV,
|
||||
// VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,
|
||||
// VK_FORMAT_A8_UNORM_KHR,
|
||||
// VK_FORMAT_A8_UNORM,
|
||||
VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT,
|
||||
VK_FORMAT_G8B8G8R8_422_UNORM_KHR,
|
||||
VK_FORMAT_B8G8R8G8_422_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR,
|
||||
VK_FORMAT_R10X6_UNORM_PACK16_KHR,
|
||||
VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR,
|
||||
VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_R12X4_UNORM_PACK16_KHR,
|
||||
VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR,
|
||||
VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR,
|
||||
VK_FORMAT_G16B16G16R16_422_UNORM_KHR,
|
||||
VK_FORMAT_B16G16R16G16_422_UNORM_KHR,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR,
|
||||
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT,
|
||||
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT,
|
||||
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT,
|
||||
VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT,
|
||||
VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT,
|
||||
VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT,
|
||||
VK_FORMAT_R16G16_S10_5_NV,
|
||||
};
|
||||
|
||||
// An Array that will be fixed capacity, but the "size" (as in user added elements) is variable.
|
||||
// Note that this class is movable.
|
||||
template<typename T, uint16_t CAPACITY>
|
||||
@@ -281,6 +590,7 @@ struct DescriptorSetLayout {
|
||||
|
||||
} // namespace descset
|
||||
|
||||
namespace {
|
||||
// Use constexpr to statically generate a bit count table for 8-bit numbers.
|
||||
struct _BitCountHelper {
|
||||
constexpr _BitCountHelper() : data{} {
|
||||
@@ -303,14 +613,15 @@ struct _BitCountHelper {
|
||||
return count;
|
||||
}
|
||||
|
||||
static _BitCountHelper BitCounter;
|
||||
private:
|
||||
uint8_t data[256];
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
||||
template<typename MaskType>
|
||||
inline uint8_t countBits(MaskType num) {
|
||||
return _BitCountHelper::BitCounter.count(num);
|
||||
static _BitCountHelper BitCounter = {};
|
||||
return BitCounter.count(num);
|
||||
}
|
||||
|
||||
// This is useful for counting the total number of descriptors for both vertex and fragment stages.
|
||||
|
||||
@@ -42,8 +42,6 @@ constexpr uint8_t MAX_INPUT_ATTACHMENT_BINDING = 1;
|
||||
constexpr uint8_t MAX_BINDINGS =
|
||||
MAX_SAMPLER_BINDING + MAX_UBO_BINDING + MAX_INPUT_ATTACHMENT_BINDING;
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
using Bitmask = VulkanDescriptorSetLayout::Bitmask;
|
||||
using DescriptorCount = VulkanDescriptorSetLayout::Count;
|
||||
using UBOMap = std::array<std::pair<VkDescriptorBufferInfo, VulkanBufferObject*>, MAX_UBO_BINDING>;
|
||||
@@ -881,7 +879,7 @@ public:
|
||||
vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr);
|
||||
}
|
||||
|
||||
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(outLayouts);
|
||||
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(outLayouts, program);
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
|
||||
BoundState state{};
|
||||
@@ -1010,15 +1008,15 @@ public:
|
||||
} else {
|
||||
info.imageView = texture->getViewForType(range, expectedType);
|
||||
}
|
||||
info.imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout());
|
||||
info.imageLayout = imgutil::getVkLayout(texture->getPrimaryImageLayout());
|
||||
mSamplerMap[binding] = {info, texture};
|
||||
mResources.acquire(texture);
|
||||
}
|
||||
|
||||
void updateInputAttachment(Handle<VulkanDescriptorSet>, VulkanAttachment attachment) noexcept {
|
||||
VkDescriptorImageInfo info = {
|
||||
.imageView = attachment.getImageView(VK_IMAGE_ASPECT_COLOR_BIT),
|
||||
.imageLayout = ImgUtil::getVkLayout(attachment.getLayout()),
|
||||
.imageView = attachment.getImageView(),
|
||||
.imageLayout = imgutil::getVkLayout(attachment.getLayout()),
|
||||
};
|
||||
mInputAttachment = {attachment, info};
|
||||
mResources.acquire(attachment.texture);
|
||||
@@ -1042,7 +1040,7 @@ public:
|
||||
mPlaceHolderImageInfo = {
|
||||
.sampler = sampler,
|
||||
.imageView = texture->getPrimaryImageView(),
|
||||
.imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout()),
|
||||
.imageLayout = imgutil::getVkLayout(texture->getPrimaryImageLayout()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ class VulkanDescriptorSetManager {
|
||||
public:
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||
using GetPipelineLayoutFunction =
|
||||
std::function<VkPipelineLayout(VulkanDescriptorSetLayoutList const&)>;
|
||||
using GetPipelineLayoutFunction = std::function<VkPipelineLayout(
|
||||
VulkanDescriptorSetLayoutList const&, VulkanProgram* program)>;
|
||||
|
||||
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
|
||||
|
||||
@@ -108,3 +108,4 @@ private:
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
|
||||
|
||||
@@ -21,13 +21,34 @@
|
||||
namespace filament::backend {
|
||||
|
||||
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
VulkanDescriptorSetLayoutList const& descriptorSetLayouts) {
|
||||
PipelineLayoutKey key = {VK_NULL_HANDLE};
|
||||
VulkanDescriptorSetLayoutList const& descriptorSetLayouts, VulkanProgram* program) {
|
||||
PipelineLayoutKey key = {};
|
||||
uint8_t descSetLayoutCount = 0;
|
||||
for (auto layoutHandle: descriptorSetLayouts) {
|
||||
if (layoutHandle) {
|
||||
auto layout = mAllocator->handle_cast<VulkanDescriptorSetLayout*>(layoutHandle);
|
||||
key[descSetLayoutCount++] = layout->vklayout;
|
||||
key.descSetLayouts[descSetLayoutCount++] = layout->vklayout;
|
||||
}
|
||||
}
|
||||
|
||||
// build the push constant layout key
|
||||
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
|
||||
auto const& pushCostantRanges = program->getPushConstantRanges();
|
||||
if (pushConstantRangeCount > 0) {
|
||||
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
|
||||
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
|
||||
auto const& range = pushCostantRanges[i];
|
||||
auto& pushConstant = key.pushConstant[i];
|
||||
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
|
||||
}
|
||||
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::FRAGMENT);
|
||||
}
|
||||
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::COMPUTE);
|
||||
}
|
||||
pushConstant.size = range.size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +63,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.setLayoutCount = (uint32_t) descSetLayoutCount,
|
||||
.pSetLayouts = key.data(),
|
||||
.pushConstantRangeCount = 0,
|
||||
.pSetLayouts = key.descSetLayouts.data(),
|
||||
.pushConstantRangeCount = pushConstantRangeCount,
|
||||
.pPushConstantRanges = pushCostantRanges,
|
||||
};
|
||||
|
||||
VkPipelineLayout layout;
|
||||
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
|
||||
|
||||
|
||||
@@ -35,13 +35,29 @@ public:
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
using PipelineLayoutKey = std::array<VkDescriptorSetLayout,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
struct PushConstantKey {
|
||||
uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc).
|
||||
uint8_t size;
|
||||
// Note that there is also an offset parameter for push constants, but
|
||||
// we always assume our update range will have the offset 0.
|
||||
};
|
||||
|
||||
struct PipelineLayoutKey {
|
||||
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 3
|
||||
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
|
||||
uint16_t padding = 0;
|
||||
};
|
||||
static_assert(sizeof(PipelineLayoutKey) == 32);
|
||||
|
||||
VulkanPipelineLayoutCache(VulkanPipelineLayoutCache const&) = delete;
|
||||
VulkanPipelineLayoutCache& operator=(VulkanPipelineLayoutCache const&) = delete;
|
||||
|
||||
VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts);
|
||||
// A pipeline layout depends on the descriptor set layout and the push constant ranges, which
|
||||
// are described in the program.
|
||||
VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts,
|
||||
VulkanProgram* program);
|
||||
|
||||
private:
|
||||
using Timestamp = uint64_t;
|
||||
|
||||
@@ -137,18 +137,14 @@ void printDepthFormats(VkPhysicalDevice device) {
|
||||
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
|
||||
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
utils::slog.i << "Sampleable depth formats: ";
|
||||
for (VkFormat format = (VkFormat) 1;;) {
|
||||
for (VkFormat format : ALL_VK_FORMATS) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||
if ((props.optimalTilingFeatures & required) == required) {
|
||||
utils::slog.i << format << " ";
|
||||
}
|
||||
if (format == VK_FORMAT_ASTC_12x12_SRGB_BLOCK) {
|
||||
utils::slog.i << utils::io::endl;
|
||||
break;
|
||||
}
|
||||
format = (VkFormat) (1 + (int) format);
|
||||
}
|
||||
utils::slog.i << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -344,13 +340,13 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
|
||||
ExtensionSet newInstExts = instExts;
|
||||
ExtensionSet newDeviceExts = deviceExts;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
// debugUtils and debugMarkers extensions are used mutually exclusively.
|
||||
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()
|
||||
&& newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) {
|
||||
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
// debugMarker must also request debugReport the instance extension. So check if that's present.
|
||||
@@ -405,7 +401,7 @@ inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
|
||||
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
|
||||
return 1;
|
||||
default:
|
||||
utils::slog.w << "devcieTypeOrder: Unexpected deviceType: " << deviceType
|
||||
utils::slog.w << "deviceTypeOrder: Unexpected deviceType: " << deviceType
|
||||
<< utils::io::endl;
|
||||
return -1;
|
||||
}
|
||||
@@ -500,7 +496,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
|
||||
return device;
|
||||
}
|
||||
|
||||
VkFormatList findAttachmentDepthFormats(VkPhysicalDevice device) {
|
||||
VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device) {
|
||||
VkFormatFeatureFlags const features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
|
||||
// The ordering here indicates the preference of choosing depth+stencil format.
|
||||
@@ -524,6 +520,24 @@ VkFormatList findAttachmentDepthFormats(VkPhysicalDevice device) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
VkFormatList findBlittableDepthStencilFormats(VkPhysicalDevice device) {
|
||||
std::vector<VkFormat> selectedFormats;
|
||||
VkFormatFeatureFlags const required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
|
||||
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||
for (VkFormat format : ALL_VK_FORMATS) {
|
||||
if (isVkDepthFormat(format)) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||
if ((props.optimalTilingFeatures & required) == required) {
|
||||
selectedFormats.push_back(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
VkFormatList ret(selectedFormats.size());
|
||||
std::copy(selectedFormats.begin(), selectedFormats.end(), ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
using SwapChainPtr = VulkanPlatform::SwapChainPtr;
|
||||
@@ -669,9 +683,11 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
"Debug utils should not be enabled in release build.");
|
||||
#endif
|
||||
|
||||
context.mDepthFormats = findAttachmentDepthFormats(mImpl->mPhysicalDevice);
|
||||
context.mDepthStencilFormats = findAttachmentDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
context.mBlittableDepthStencilFormats =
|
||||
findBlittableDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
|
||||
assert_invariant(context.mDepthFormats.size() > 0);
|
||||
assert_invariant(context.mDepthStencilFormats.size() > 0);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
printDepthFormats(mImpl->mPhysicalDevice);
|
||||
|
||||
@@ -243,7 +243,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
mSwapChainBundle.colors = enumerate(vkGetSwapchainImagesKHR, mDevice, mSwapchain);
|
||||
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthFormats(), mHasStencil);
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
|
||||
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
|
||||
|
||||
slog.i << "vkCreateSwapchain"
|
||||
@@ -330,7 +330,7 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
|
||||
|
||||
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthFormats(), hasStencil);
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
|
||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
|
||||
}
|
||||
|
||||
|
||||
@@ -238,13 +238,13 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
@@ -402,7 +402,7 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
});
|
||||
|
||||
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.bindUniformBuffer(0, ubuffer);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
@@ -484,13 +484,13 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
@@ -574,13 +574,13 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
@@ -655,7 +655,7 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
|
||||
@@ -143,7 +143,7 @@ TEST_F(BackendTest, VertexBufferUpdate) {
|
||||
}
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Draw 10 triangles, updating the vertex buffer / index buffer each time.
|
||||
size_t triangleIndex = 0;
|
||||
|
||||
@@ -200,7 +200,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.bindSamplers(0, sgroup);
|
||||
api.bindUniformBuffer(0, ubuffer);
|
||||
|
||||
@@ -265,7 +265,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
for (auto rt : renderTargets) api.destroyRenderTarget(rt);
|
||||
}
|
||||
|
||||
const uint32_t expected = 0xe93a4a07;
|
||||
const uint32_t expected = 0x70695aa1;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sPixelHashResult, expected);
|
||||
EXPECT_TRUE(sPixelHashResult == expected);
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
|
||||
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
SamplerGroup samplers(1);
|
||||
@@ -486,7 +486,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
|
||||
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
SamplerGroup samplers(1);
|
||||
@@ -570,7 +570,7 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
|
||||
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 4, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
SamplerGroup samplers(1);
|
||||
|
||||
@@ -127,7 +127,7 @@ TEST_F(BackendTest, MRT) {
|
||||
getDriverApi().startCapture(0);
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Draw a triangle.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
|
||||
@@ -143,7 +143,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// We set the base mip to 1, and the max mip to 3
|
||||
// Level 0: 128x128 (red)
|
||||
|
||||
@@ -100,7 +100,7 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
getDriverApi().startCapture(0);
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render a triangle.
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
|
||||
@@ -295,7 +295,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
params.viewport.width = t.getRenderTargetSize();
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render a white triangle over blue.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
@@ -434,7 +434,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
}
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render some content, just so we don't read back uninitialized data.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
|
||||
@@ -109,7 +109,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
getDriverApi().startCapture(0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
SamplerGroup samplers(1);
|
||||
samplers.setSampler(0, { texture, {} });
|
||||
@@ -231,7 +231,7 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
getDriverApi().startCapture(0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
SamplerGroup samplers(1);
|
||||
samplers.setSampler(0, { texture, {} });
|
||||
|
||||
@@ -136,7 +136,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
ps.rasterState.depthWrite = false;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.scissor(scissor);
|
||||
@@ -227,7 +227,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
ps.rasterState.depthWrite = false;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.scissor(scissor);
|
||||
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
|
||||
@@ -237,7 +237,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
|
||||
|
||||
@@ -340,6 +340,35 @@ public:
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
bool disableHandleUseAfterFreeCheck = false;
|
||||
|
||||
/*
|
||||
* Sets a preferred shader language for Filament to use.
|
||||
*
|
||||
* The Metal backend supports two shader languages: MSL (Metal Shading Language) and
|
||||
* METAL_LIBRARY (precompiled .metallib). This option controls which shader language is
|
||||
* used when materials contain both.
|
||||
*
|
||||
* By default, when preferredShaderLanguage is unset, Filament will prefer METAL_LIBRARY
|
||||
* shaders if present within a material, falling back to MSL. Setting
|
||||
* preferredShaderLanguage to ShaderLanguage::MSL will instead instruct Filament to check
|
||||
* for the presence of MSL in a material first, falling back to METAL_LIBRARY if MSL is not
|
||||
* present.
|
||||
*
|
||||
* When using a non-Metal backend, setting this has no effect.
|
||||
*/
|
||||
enum class ShaderLanguage {
|
||||
DEFAULT = 0,
|
||||
MSL = 1,
|
||||
METAL_LIBRARY = 2,
|
||||
};
|
||||
ShaderLanguage preferredShaderLanguage = ShaderLanguage::DEFAULT;
|
||||
|
||||
/*
|
||||
* When the OpenGL ES backend is used, setting this value to true will force a GLES2.0
|
||||
* context if supported by the Platform, or if not, will have the backend pretend
|
||||
* it's a GLES2 context. Ignored on other backends.
|
||||
*/
|
||||
bool forceGLES2Context = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -867,6 +896,15 @@ public:
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Get paused state of rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API.
|
||||
*
|
||||
* @see setPaused
|
||||
*/
|
||||
bool isPaused() const noexcept;
|
||||
|
||||
/**
|
||||
* Pause or resume rendering thread.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PresentCallable.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
@@ -264,13 +265,22 @@ public:
|
||||
* backend.
|
||||
*
|
||||
* A FrameScheduledCallback can be set on an individual SwapChain through
|
||||
* SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not*
|
||||
* automatically schedule itself for presentation. Instead, the application must call the
|
||||
* PresentCallable passed to the FrameScheduledCallback.
|
||||
* SwapChain::setFrameScheduledCallback. If the callback is set for a given frame, then the
|
||||
* SwapChain will *not* automatically schedule itself for presentation. Instead, the application
|
||||
* must call the PresentCallable passed to the FrameScheduledCallback.
|
||||
*
|
||||
* There may be only one FrameScheduledCallback set per SwapChain. A call to
|
||||
* SwapChain::setFrameScheduledCallback will overwrite any previous FrameScheduledCallbacks set
|
||||
* on the same SwapChain.
|
||||
* Each SwapChain can have only one FrameScheduledCallback set per frame. If
|
||||
* setFrameScheduledCallback is called multiple times on the same SwapChain before
|
||||
* Renderer::endFrame(), the most recent call effectively overwrites any previously set
|
||||
* callback. This allows the callback to be updated as needed before the frame has finished
|
||||
* encoding.
|
||||
*
|
||||
* The "last" callback set by setFrameScheduledCallback gets "latched" when Renderer::endFrame()
|
||||
* is executed. At this point, the state of the callback is fixed and is the one used for the
|
||||
* frame that was just encoded. Subsequent changes to the callback using
|
||||
* setFrameScheduledCallback after endFrame() apply to the next frame.
|
||||
*
|
||||
* Use \c setFrameScheduledCallback() (with default arguments) to unset the callback.
|
||||
*
|
||||
* If your application delays the call to the PresentCallable by, for example, calling it on a
|
||||
* separate thread, you must ensure all PresentCallables have been called before shutting down
|
||||
@@ -278,28 +288,26 @@ public:
|
||||
* Engine::shutdown. This is necessary to ensure the Filament Engine has had a chance to clean
|
||||
* up all memory related to frame presentation.
|
||||
*
|
||||
* @param callback A callback, or nullptr to unset.
|
||||
* @param user An optional pointer to user data passed to the callback function.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler.
|
||||
* @param callback Callback called when the frame is scheduled.
|
||||
*
|
||||
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
|
||||
* backends ignore the callback (which will never be called) and proceed normally.
|
||||
*
|
||||
* @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread.
|
||||
*
|
||||
* @see CallbackHandler
|
||||
* @see PresentCallable
|
||||
*/
|
||||
void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback,
|
||||
void* UTILS_NULLABLE user = nullptr);
|
||||
void setFrameScheduledCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
FrameScheduledCallback&& callback = {});
|
||||
|
||||
/**
|
||||
* Returns the SwapChain::FrameScheduledCallback that was previously set with
|
||||
* SwapChain::setFrameScheduledCallback, or nullptr if one is not set.
|
||||
* Returns whether or not this SwapChain currently has a FrameScheduledCallback set.
|
||||
*
|
||||
* @return the previously-set FrameScheduledCallback, or nullptr
|
||||
* @return true, if the last call to setFrameScheduledCallback set a callback
|
||||
*
|
||||
* @see SwapChain::setFrameCompletedCallback
|
||||
*/
|
||||
UTILS_NULLABLE FrameScheduledCallback getFrameScheduledCallback() const noexcept;
|
||||
bool isFrameScheduledCallbackSet() const noexcept;
|
||||
|
||||
/**
|
||||
* FrameCompletedCallback is a callback function that notifies an application when a frame's
|
||||
|
||||
@@ -308,6 +308,11 @@ utils::JobSystem& Engine::getJobSystem() noexcept {
|
||||
return downcast(this)->getJobSystem();
|
||||
}
|
||||
|
||||
bool Engine::isPaused() const noexcept {
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
|
||||
return downcast(this)->isPaused();
|
||||
}
|
||||
|
||||
void Engine::setPaused(bool paused) {
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
|
||||
downcast(this)->setPaused(paused);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
#include <private/filament/Variant.h>
|
||||
#include <private/filament/ConstantInfo.h>
|
||||
#include <private/filament/PushConstantInfo.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
@@ -225,6 +226,14 @@ bool MaterialParser::getConstants(utils::FixedCapacityVector<MaterialConstant>*
|
||||
return ChunkMaterialConstants::unflatten(unflattener, value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getPushConstants(utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(filamat::MaterialPushConstants);
|
||||
if (start == end) return false;
|
||||
Unflattener unflattener(start, end);
|
||||
return ChunkMaterialPushConstants::unflatten(unflattener, structVarName, value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getDepthWriteSet(bool* value) const noexcept {
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialDepthWriteSet, value);
|
||||
}
|
||||
@@ -709,4 +718,46 @@ bool ChunkMaterialConstants::unflatten(filaflat::Unflattener& unflattener,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialPushConstants::unflatten(filaflat::Unflattener& unflattener,
|
||||
utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
|
||||
assert_invariant(materialPushConstants);
|
||||
|
||||
if (!unflattener.read(structVarName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read number of constants.
|
||||
uint64_t numConstants = 0;
|
||||
if (!unflattener.read(&numConstants)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
materialPushConstants->reserve(numConstants);
|
||||
materialPushConstants->resize(numConstants);
|
||||
|
||||
for (uint64_t i = 0; i < numConstants; i++) {
|
||||
CString constantName;
|
||||
uint8_t constantType = 0;
|
||||
uint8_t shaderStage = 0;
|
||||
|
||||
if (!unflattener.read(&constantName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unflattener.read(&constantType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unflattener.read(&shaderStage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(*materialPushConstants)[i].name = constantName;
|
||||
(*materialPushConstants)[i].type = static_cast<backend::ConstantType>(constantType);
|
||||
(*materialPushConstants)[i].stage = static_cast<backend::ShaderStage>(shaderStage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -47,6 +47,7 @@ class BufferInterfaceBlock;
|
||||
class SamplerInterfaceBlock;
|
||||
struct SubpassInfo;
|
||||
struct MaterialConstant;
|
||||
struct MaterialPushConstant;
|
||||
|
||||
class MaterialParser {
|
||||
public:
|
||||
@@ -79,6 +80,8 @@ public:
|
||||
bool getSamplerBlockBindings(SamplerGroupBindingInfoList* pSamplerGroupInfoList,
|
||||
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept;
|
||||
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
|
||||
bool getPushConstants(utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
|
||||
|
||||
using BindingUniformInfoContainer = utils::FixedCapacityVector<
|
||||
std::pair<filament::UniformBindingPoints, backend::Program::UniformInfo>>;
|
||||
@@ -214,6 +217,11 @@ struct ChunkMaterialConstants {
|
||||
utils::FixedCapacityVector<MaterialConstant>* materialConstants);
|
||||
};
|
||||
|
||||
struct ChunkMaterialPushConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_MATERIALPARSER_H
|
||||
|
||||
@@ -16,12 +16,19 @@
|
||||
|
||||
#include "PerShadowMapUniforms.h"
|
||||
|
||||
#include "ShadowMapManager.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -88,8 +95,8 @@ void PerShadowMapUniforms::prepareViewport(Transaction const& transaction,
|
||||
void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
|
||||
FEngine& engine, math::float4 const& userTime) noexcept {
|
||||
auto& s = edit(transaction);
|
||||
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
|
||||
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
|
||||
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000;
|
||||
const float fraction = float(double(oneSecondRemainder) / 1'000'000'000.0);
|
||||
s.time = fraction;
|
||||
s.userTime = userTime;
|
||||
}
|
||||
@@ -102,7 +109,6 @@ void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
|
||||
s.vsmExponent = highPrecision ? high : low;
|
||||
}
|
||||
|
||||
|
||||
PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi& driver) noexcept {
|
||||
Transaction transaction;
|
||||
// TODO: use out-of-line buffer if too large
|
||||
@@ -114,7 +120,7 @@ PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi&
|
||||
void PerShadowMapUniforms::commit(Transaction& transaction,
|
||||
backend::DriverApi& driver) noexcept {
|
||||
driver.updateBufferObject(mUniformBufferHandle, {
|
||||
transaction.uniforms, sizeof(PerViewUib) }, 0);
|
||||
transaction.uniforms, sizeof(PerViewUib) }, 0);
|
||||
transaction.uniforms = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,18 +17,13 @@
|
||||
#ifndef TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H
|
||||
#define TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
#include <private/backend/SamplerGroup.h>
|
||||
|
||||
#include "TypedUniformBuffer.h"
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/EntityInstance.h>
|
||||
|
||||
#include <random>
|
||||
#include <math/vec4.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -40,13 +35,11 @@ class LightManager;
|
||||
/*
|
||||
* PerShadowMapUniforms manages the UBO needed to generate our shadow maps. Internally it just
|
||||
* holds onto a `PerViewUniform` UBO handle, but doesn't keep any shadow copy of it, instead it
|
||||
* writes the data directly into the commandstream, for this reason partial update of the data
|
||||
* writes the data directly into the CommandStream, for this reason partial update of the data
|
||||
* is not possible.
|
||||
*/
|
||||
class PerShadowMapUniforms {
|
||||
|
||||
using LightManagerInstance = utils::EntityInstance<LightManager>;
|
||||
|
||||
public:
|
||||
class Transaction {
|
||||
friend PerShadowMapUniforms;
|
||||
|
||||
@@ -265,6 +265,7 @@ static const PostProcessManager::MaterialInfo sMaterialList[] = {
|
||||
{ "bilateralBlur", MATERIAL(BILATERALBLUR) },
|
||||
{ "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) },
|
||||
{ "blitArray", MATERIAL(BLITARRAY) },
|
||||
{ "blitDepth", MATERIAL(BLITDEPTH) },
|
||||
{ "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) },
|
||||
{ "bloomDownsample2x", MATERIAL(BLOOMDOWNSAMPLE2X) },
|
||||
{ "bloomDownsample9", MATERIAL(BLOOMDOWNSAMPLE9) },
|
||||
@@ -340,19 +341,22 @@ void PostProcessManager::init() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
mStarburstTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::R8, 1, 256, 1, 1, TextureUsage::DEFAULT);
|
||||
if (engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1)) {
|
||||
mStarburstTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::R8, 1, 256, 1, 1, TextureUsage::DEFAULT);
|
||||
|
||||
PixelBufferDescriptor dataStarburst(driver.allocate(256), 256, PixelDataFormat::R, PixelDataType::UBYTE);
|
||||
std::generate_n((uint8_t*)dataStarburst.buffer, 256,
|
||||
[&dist = mUniformDistribution, &gen = mEngine.getRandomEngine()]() {
|
||||
float const r = 0.5f + 0.5f * dist(gen);
|
||||
return uint8_t(r * 255.0f);
|
||||
});
|
||||
PixelBufferDescriptor dataStarburst(driver.allocate(256), 256,
|
||||
PixelDataFormat::R, PixelDataType::UBYTE);
|
||||
std::generate_n((uint8_t*)dataStarburst.buffer, 256,
|
||||
[&dist = mUniformDistribution, &gen = mEngine.getRandomEngine()]() {
|
||||
float const r = 0.5f + 0.5f * dist(gen);
|
||||
return uint8_t(r * 255.0f);
|
||||
});
|
||||
|
||||
driver.update3DImage(mStarburstTexture,
|
||||
0, 0, 0, 0, 256, 1, 1,
|
||||
std::move(dataStarburst));
|
||||
driver.update3DImage(mStarburstTexture,
|
||||
0, 0, 0, 0, 256, 1, 1,
|
||||
std::move(dataStarburst));
|
||||
}
|
||||
}
|
||||
|
||||
void PostProcessManager::terminate(DriverApi& driver) noexcept {
|
||||
@@ -720,46 +724,16 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
* attachment, even if writes are not enabled. This restriction is lifted on desktop GL and
|
||||
* Vulkan. The Metal situation is unclear.
|
||||
* In this case, we need to duplicate the depth texture to use it as an attachment.
|
||||
* The pass below that does this is automatically culled if not needed, which is decided by
|
||||
* each backend.
|
||||
*
|
||||
* This is also needed in Vulkan for a similar reason.
|
||||
*/
|
||||
FrameGraphId<FrameGraphTexture> duplicateDepthOutput = {};
|
||||
if (!mWorkaroundAllowReadOnlyAncillaryFeedbackLoop) {
|
||||
duplicateDepthOutput = blitDepth(fg, depth);
|
||||
}
|
||||
|
||||
struct DuplicateDepthPassData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
};
|
||||
|
||||
// Needed for Vulkan and GLES. Some GLES implementations don't need it. Never needed for Metal.
|
||||
auto& duplicateDepthPass = fg.addPass<DuplicateDepthPassData>("Duplicate Depth Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.input = builder.read(depth,
|
||||
FrameGraphTexture::Usage::BLIT_SRC);
|
||||
|
||||
auto desc = builder.getDescriptor(data.input);
|
||||
desc.levels = 1; // only copy the base level
|
||||
|
||||
// create a new buffer for the copy
|
||||
data.output = builder.createTexture("Depth Texture Copy", desc);
|
||||
|
||||
// output is an attachment
|
||||
data.output = builder.write(data.output,
|
||||
FrameGraphTexture::Usage::BLIT_DST);
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto const& src = resources.getTexture(data.input);
|
||||
auto const& dst = resources.getTexture(data.output);
|
||||
auto const& srcSubDesc = resources.getSubResourceDescriptor(data.input);
|
||||
auto const& dstSubDesc = resources.getSubResourceDescriptor(data.output);
|
||||
auto const& desc = resources.getDescriptor(data.output);
|
||||
assert_invariant(desc.samples == resources.getDescriptor(data.input).samples);
|
||||
// here we can guarantee that src and dst format and size match, by construction.
|
||||
driver.blit(
|
||||
dst, dstSubDesc.level, dstSubDesc.layer, { 0, 0 },
|
||||
src, srcSubDesc.level, srcSubDesc.layer, { 0, 0 },
|
||||
{ desc.width, desc.height });
|
||||
});
|
||||
|
||||
auto& SSAOPass = fg.addPass<SSAOPassData>("SSAO Pass",
|
||||
auto& SSAOPass = fg.addPass<SSAOPassData>(
|
||||
"SSAO Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto const& desc = builder.getDescriptor(depth);
|
||||
|
||||
@@ -788,10 +762,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
// reading into it even though they were not written in the depth buffer.
|
||||
// The bilateral filter in the blur pass will ignore pixels at infinity.
|
||||
|
||||
auto depthAttachment = data.depth;
|
||||
if (!mWorkaroundAllowReadOnlyAncillaryFeedbackLoop) {
|
||||
depthAttachment = duplicateDepthPass->output;
|
||||
}
|
||||
auto depthAttachment = duplicateDepthOutput ? duplicateDepthOutput : data.depth;
|
||||
|
||||
depthAttachment = builder.read(depthAttachment,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
@@ -801,8 +772,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto depth = resources.getTexture(data.depth);
|
||||
auto ssao = resources.getRenderPassInfo();
|
||||
auto const& desc = resources.getDescriptor(data.depth);
|
||||
@@ -2143,6 +2113,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
mi->setParameter("source", hwOut, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
@@ -3043,7 +3014,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
auto& ppQuadBlit = fg.addPass<QuadBlitData>("blitting",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.input = builder.sample(input);
|
||||
data.output = builder.createTexture("upscaled output", outDesc);
|
||||
data.output = builder.createTexture("blit output", outDesc);
|
||||
data.output = builder.write(data.output,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
builder.declareRenderPass(builder.getName(data.output), {
|
||||
@@ -3087,6 +3058,87 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
return ppQuadBlit->output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::blitDepth(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input) noexcept {
|
||||
auto const& inputDesc = fg.getDescriptor(input);
|
||||
filament::Viewport const vp = {0, 0, inputDesc.width, inputDesc.height};
|
||||
bool const hardwareBlitSupported =
|
||||
mEngine.getDriverApi().isDepthStencilBlitSupported(inputDesc.format);
|
||||
|
||||
struct BlitData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
};
|
||||
|
||||
if (hardwareBlitSupported) {
|
||||
auto& depthPass = fg.addPass<BlitData>(
|
||||
"Depth Blit",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.input = builder.read(input, FrameGraphTexture::Usage::BLIT_SRC);
|
||||
|
||||
auto desc = builder.getDescriptor(data.input);
|
||||
desc.levels = 1;// only copy the base level
|
||||
|
||||
// create a new buffer for the copy
|
||||
data.output = builder.createTexture("depth blit output", desc);
|
||||
|
||||
// output is an attachment
|
||||
data.output = builder.write(data.output, FrameGraphTexture::Usage::BLIT_DST);
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto const& src = resources.getTexture(data.input);
|
||||
auto const& dst = resources.getTexture(data.output);
|
||||
auto const& srcSubDesc = resources.getSubResourceDescriptor(data.input);
|
||||
auto const& dstSubDesc = resources.getSubResourceDescriptor(data.output);
|
||||
auto const& desc = resources.getDescriptor(data.output);
|
||||
assert_invariant(desc.samples == resources.getDescriptor(data.input).samples);
|
||||
// here we can guarantee that src and dst format and size match, by
|
||||
// construction.
|
||||
driver.blit(
|
||||
dst, dstSubDesc.level, dstSubDesc.layer, { 0, 0 },
|
||||
src, srcSubDesc.level, srcSubDesc.layer, { 0, 0 },
|
||||
{ desc.width, desc.height });
|
||||
});
|
||||
return depthPass->output;
|
||||
}
|
||||
// Otherwise, we would do a shader-based blit.
|
||||
|
||||
auto& ppQuadBlit = fg.addPass<BlitData>(
|
||||
"Depth Blit (Shader)",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.input = builder.sample(input);
|
||||
// Note that this is a same size/format blit.
|
||||
auto const& outputDesc = inputDesc;
|
||||
data.output = builder.createTexture("depth blit output", outputDesc);
|
||||
data.output =
|
||||
builder.write(data.output, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
builder.declareRenderPass(builder.getName(data.output),
|
||||
{.attachments = {.depth = {data.output}}});
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto depth = resources.getTexture(data.input);
|
||||
auto const& inputDesc = resources.getDescriptor(data.input);
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// set uniforms
|
||||
PostProcessMaterial const& material = getPostProcessMaterial("blitDepth");
|
||||
auto* mi = material.getMaterialInstance(mEngine);
|
||||
mi->setParameter("depth", depth,
|
||||
{
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
.filterMin = SamplerMinFilter::NEAREST,
|
||||
});
|
||||
mi->setParameter("viewport",
|
||||
float4{float(vp.left) / inputDesc.width,
|
||||
float(vp.bottom) / inputDesc.height, float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height});
|
||||
commitAndRender(out, material, driver);
|
||||
});
|
||||
|
||||
return ppQuadBlit->output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
|
||||
const char* outputBufferName, FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphTexture::Descriptor outDesc) noexcept {
|
||||
@@ -3283,7 +3335,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
|
||||
return debugShadowCascadePass->output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(FrameGraph& fg,
|
||||
bool translucent, FrameGraphId<FrameGraphTexture> input,
|
||||
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
|
||||
SamplerMagFilter filterMag,
|
||||
|
||||
@@ -250,13 +250,17 @@ public:
|
||||
FrameGraphTexture::Descriptor const& outDesc,
|
||||
bool translucent);
|
||||
|
||||
// upscale/downscale blitter using shaders
|
||||
// color blitter using shaders
|
||||
FrameGraphId<FrameGraphTexture> blit(FrameGraph& fg, bool translucent,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
|
||||
backend::SamplerMagFilter filterMag,
|
||||
backend::SamplerMinFilter filterMin) noexcept;
|
||||
|
||||
// depth blitter using shaders
|
||||
FrameGraphId<FrameGraphTexture> blitDepth(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input) noexcept;
|
||||
|
||||
// Resolves base level of input and outputs a texture from outDesc.
|
||||
// outDesc with, height, format and samples will be overridden.
|
||||
FrameGraphId<FrameGraphTexture> resolve(FrameGraph& fg,
|
||||
|
||||
+259
-257
@@ -18,8 +18,8 @@
|
||||
|
||||
#include "RenderPrimitive.h"
|
||||
#include "ShadowMap.h"
|
||||
#include "SharedHandle.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Material.h"
|
||||
#include "details/MaterialInstance.h"
|
||||
#include "details/View.h"
|
||||
@@ -88,24 +88,26 @@ RenderPass RenderPassBuilder::build(FEngine& engine) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void RenderPass::BufferObjectHandleDeleter::operator()(
|
||||
backend::BufferObjectHandle handle) noexcept {
|
||||
if (handle) {
|
||||
driver.get().destroyBufferObject(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept
|
||||
: mRenderableSoa(*builder.mRenderableSoa),
|
||||
mVisibleRenderables(builder.mVisibleRenderables),
|
||||
mUboHandle(builder.mUboHandle),
|
||||
mCameraPosition(builder.mCameraPosition),
|
||||
mCameraForwardVector(builder.mCameraForwardVector),
|
||||
mFlags(builder.mFlags),
|
||||
mVariant(builder.mVariant),
|
||||
mVisibilityMask(builder.mVisibilityMask),
|
||||
mScissorViewport(builder.mScissorViewport),
|
||||
mCustomCommands(engine.getPerRenderPassArena()) {
|
||||
|
||||
// compute the number of commands we need
|
||||
updateSummedPrimitiveCounts(
|
||||
const_cast<FScene::RenderableSoa&>(mRenderableSoa), mVisibleRenderables);
|
||||
const_cast<FScene::RenderableSoa&>(mRenderableSoa), builder.mVisibleRenderables);
|
||||
|
||||
uint32_t commandCount =
|
||||
FScene::getPrimitiveCount(mRenderableSoa, mVisibleRenderables.last);
|
||||
FScene::getPrimitiveCount(mRenderableSoa, builder.mVisibleRenderables.last);
|
||||
const bool colorPass = bool(builder.mCommandTypeFlags & CommandTypeFlags::COLOR);
|
||||
const bool depthPass = bool(builder.mCommandTypeFlags & CommandTypeFlags::DEPTH);
|
||||
commandCount *= uint32_t(colorPass * 2 + depthPass);
|
||||
@@ -129,7 +131,15 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
mCommandBegin = curr;
|
||||
mCommandEnd = curr + commandCount + customCommandCount;
|
||||
|
||||
appendCommands(engine, { curr, commandCount }, builder.mCommandTypeFlags);
|
||||
appendCommands(engine, { curr, commandCount },
|
||||
builder.mUboHandle,
|
||||
builder.mVisibleRenderables,
|
||||
builder.mCommandTypeFlags,
|
||||
builder.mFlags,
|
||||
builder.mVisibilityMask,
|
||||
builder.mVariant,
|
||||
builder.mCameraPosition,
|
||||
builder.mCameraForwardVector);
|
||||
|
||||
if (builder.mCustomCommands.has_value()) {
|
||||
Command* p = curr + commandCount;
|
||||
@@ -142,12 +152,17 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
sortCommands(builder.mArena);
|
||||
|
||||
if (engine.isAutomaticInstancingEnabled()) {
|
||||
instanceify(engine, builder.mArena);
|
||||
uint32_t stereoscopicEyeCount = 1;
|
||||
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
|
||||
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
instanceify(engine, builder.mArena, stereoscopicEyeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// this destructor is actually heavy because it inlines ~vector<>
|
||||
RenderPass::~RenderPass() noexcept = default;
|
||||
RenderPass::~RenderPass() noexcept {
|
||||
}
|
||||
|
||||
void RenderPass::resize(Arena& arena, size_t count) noexcept {
|
||||
if (mCommandBegin) {
|
||||
@@ -157,11 +172,18 @@ void RenderPass::resize(Arena& arena, size_t count) noexcept {
|
||||
}
|
||||
|
||||
void RenderPass::appendCommands(FEngine& engine,
|
||||
Slice<Command> commands, CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
Slice<Command> commands,
|
||||
backend::BufferObjectHandle const uboHandle,
|
||||
utils::Range<uint32_t> const vr,
|
||||
CommandTypeFlags const commandTypeFlags,
|
||||
RenderFlags const renderFlags,
|
||||
FScene::VisibleMaskType const visibilityMask,
|
||||
Variant const variant,
|
||||
float3 const cameraPosition,
|
||||
float3 const cameraForwardVector) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
utils::Range<uint32_t> const vr = mVisibleRenderables;
|
||||
// trace the number of visible renderables
|
||||
SYSTRACE_VALUE32("visibleRenderables", vr.size());
|
||||
if (UTILS_UNLIKELY(vr.empty())) {
|
||||
@@ -174,9 +196,6 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
}
|
||||
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
const RenderFlags renderFlags = mFlags;
|
||||
const Variant variant = mVariant;
|
||||
const FScene::VisibleMaskType visibilityMask = mVisibilityMask;
|
||||
|
||||
// up-to-date summed primitive counts needed for generateCommands()
|
||||
FScene::RenderableSoa const& soa = mRenderableSoa;
|
||||
@@ -186,13 +205,14 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
|
||||
auto stereoscopicEyeCount = engine.getConfig().stereoscopicEyeCount;
|
||||
|
||||
const float3 cameraPosition(mCameraPosition);
|
||||
const float3 cameraForwardVector(mCameraForwardVector);
|
||||
auto work = [commandTypeFlags, curr, &soa, variant, renderFlags, visibilityMask, cameraPosition,
|
||||
cameraForwardVector, stereoscopicEyeCount]
|
||||
auto work = [commandTypeFlags, curr, &soa,
|
||||
boh = uboHandle,
|
||||
variant, renderFlags, visibilityMask,
|
||||
cameraPosition, cameraForwardVector, stereoscopicEyeCount]
|
||||
(uint32_t startIndex, uint32_t indexCount) {
|
||||
RenderPass::generateCommands(commandTypeFlags, curr,
|
||||
soa, { startIndex, startIndex + indexCount }, variant, renderFlags, visibilityMask,
|
||||
soa, { startIndex, startIndex + indexCount }, boh,
|
||||
variant, renderFlags, visibilityMask,
|
||||
cameraPosition, cameraForwardVector, stereoscopicEyeCount);
|
||||
};
|
||||
|
||||
@@ -204,8 +224,8 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
js.runAndWait(jobCommandsParallel);
|
||||
}
|
||||
|
||||
// always add an "eof" command
|
||||
// "eof" command. these commands are guaranteed to be sorted last in the
|
||||
// Always add an "eof" command
|
||||
// "eof" command. These commands are guaranteed to be sorted last in the
|
||||
// command buffer.
|
||||
curr[commandCount - 1].key = uint64_t(Pass::SENTINEL);
|
||||
|
||||
@@ -213,8 +233,8 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
// This must be done from the main thread.
|
||||
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
|
||||
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
|
||||
auto ma = first->primitive.primitive->getMaterialInstance()->getMaterial();
|
||||
ma->prepareProgram(first->primitive.materialVariant);
|
||||
auto ma = first->info.mi->getMaterial();
|
||||
ma->prepareProgram(first->info.materialVariant);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,7 +283,7 @@ void RenderPass::execute(RenderPass const& pass,
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
|
||||
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
|
||||
SYSTRACE_NAME("instanceify");
|
||||
|
||||
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
|
||||
@@ -290,19 +310,26 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
|
||||
|
||||
while (curr != last) {
|
||||
|
||||
// we can't have nice things! No more than maxInstanceCount due to UBO size limits
|
||||
Command const* const e = std::find_if_not(curr, std::min(last, curr + maxInstanceCount),
|
||||
[lhs = *curr](Command const& rhs) {
|
||||
// primitives must be identical to be instanced. Currently, instancing doesn't support
|
||||
// skinning/morphing.
|
||||
return lhs.primitive.primitive == rhs.primitive.primitive &&
|
||||
lhs.primitive.rasterState == rhs.primitive.rasterState &&
|
||||
lhs.primitive.skinningHandle == rhs.primitive.skinningHandle &&
|
||||
lhs.primitive.skinningOffset == rhs.primitive.skinningOffset &&
|
||||
lhs.primitive.morphWeightBuffer == rhs.primitive.morphWeightBuffer &&
|
||||
lhs.primitive.morphTargetBuffer == rhs.primitive.morphTargetBuffer &&
|
||||
lhs.primitive.skinningTexture == rhs.primitive.skinningTexture ;
|
||||
});
|
||||
// Currently, if we have skinnning or morphing, we can't use auto instancing. This is
|
||||
// because the morphing/skinning data for comparison is not easily accessible.
|
||||
// Additionally, we can't have a different skinning/morphing per instance anyway.
|
||||
// And thirdly, the info.index meaning changes with instancing, it is the index into
|
||||
// the instancing buffer no longer the index into the soa.
|
||||
Command const* e = curr + 1;
|
||||
if (UTILS_LIKELY(!curr->info.hasSkinning && !curr->info.hasMorphing)) {
|
||||
// we can't have nice things! No more than maxInstanceCount due to UBO size limits
|
||||
e = std::find_if_not(curr, std::min(last, curr + maxInstanceCount),
|
||||
[lhs = *curr](Command const& rhs) {
|
||||
// primitives must be identical to be instanced.
|
||||
// Currently, instancing doesn't support skinning/morphing.
|
||||
return lhs.info.mi == rhs.info.mi &&
|
||||
lhs.info.rph == rhs.info.rph &&
|
||||
lhs.info.vbih == rhs.info.vbih &&
|
||||
lhs.info.indexOffset == rhs.info.indexOffset &&
|
||||
lhs.info.indexCount == rhs.info.indexCount &&
|
||||
lhs.info.rasterState == rhs.info.rasterState;
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t const instanceCount = e - curr;
|
||||
assert_invariant(instanceCount > 0);
|
||||
@@ -313,6 +340,15 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
|
||||
|
||||
// allocate our staging buffer only if needed
|
||||
if (UTILS_UNLIKELY(!stagingBuffer)) {
|
||||
|
||||
// create a temporary UBO for instancing
|
||||
size_t const count = mCommandEnd - mCommandBegin;
|
||||
mInstancedUboHandle = BufferObjectSharedHandle{
|
||||
engine.getDriverApi().createBufferObject(
|
||||
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC),
|
||||
engine.getDriverApi() };
|
||||
|
||||
// TODO: use stream inline buffer for small sizes
|
||||
// TODO: use a pool for larger heap buffers
|
||||
// buffer large enough for all instances data
|
||||
@@ -326,12 +362,14 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
|
||||
assert_invariant(instancedPrimitiveOffset + instanceCount
|
||||
<= stagingBufferSize / sizeof(PerRenderableData));
|
||||
for (uint32_t i = 0; i < instanceCount; i++) {
|
||||
stagingBuffer[instancedPrimitiveOffset + i] = uboData[curr[i].primitive.index];
|
||||
stagingBuffer[instancedPrimitiveOffset + i] = uboData[curr[i].info.index];
|
||||
}
|
||||
|
||||
// make the first command instanced
|
||||
curr[0].primitive.instanceCount = instanceCount;
|
||||
curr[0].primitive.index = instancedPrimitiveOffset;
|
||||
curr[0].info.instanceCount = instanceCount * eyeCount;
|
||||
curr[0].info.index = instancedPrimitiveOffset;
|
||||
curr[0].info.boh = mInstancedUboHandle;
|
||||
|
||||
instancedPrimitiveOffset += instanceCount;
|
||||
|
||||
// cancel commands that are now instances
|
||||
@@ -351,12 +389,6 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
|
||||
// we have instanced primitives
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// TODO: maybe use a pool? so we can reuse the buffer.
|
||||
// create a ubo to hold the instanced primitive data
|
||||
mInstancedUboHandle = driver.createBufferObject(
|
||||
sizeof(PerRenderableData) * instancedPrimitiveOffset + sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
|
||||
|
||||
// copy our instanced ubo data
|
||||
driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, {
|
||||
stagingBuffer, sizeof(PerRenderableData) * instancedPrimitiveOffset,
|
||||
@@ -409,22 +441,22 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
keyDraw |= makeField(ma->getRasterState().alphaToCoverage, BLENDING_MASK, BLENDING_SHIFT);
|
||||
|
||||
cmdDraw.key = isBlendingCommand ? keyBlending : keyDraw;
|
||||
cmdDraw.primitive.rasterState = ma->getRasterState();
|
||||
cmdDraw.info.rasterState = ma->getRasterState();
|
||||
|
||||
// for SSR pass, the blending mode of opaques (including MASKED) must be off
|
||||
// see Material.cpp.
|
||||
const bool blendingMustBeOff = !isBlendingCommand && Variant::isSSRVariant(variant);
|
||||
cmdDraw.primitive.rasterState.blendFunctionSrcAlpha = blendingMustBeOff ?
|
||||
BlendFunction::ONE : cmdDraw.primitive.rasterState.blendFunctionSrcAlpha;
|
||||
cmdDraw.primitive.rasterState.blendFunctionDstAlpha = blendingMustBeOff ?
|
||||
BlendFunction::ZERO : cmdDraw.primitive.rasterState.blendFunctionDstAlpha;
|
||||
cmdDraw.info.rasterState.blendFunctionSrcAlpha = blendingMustBeOff ?
|
||||
BlendFunction::ONE : cmdDraw.info.rasterState.blendFunctionSrcAlpha;
|
||||
cmdDraw.info.rasterState.blendFunctionDstAlpha = blendingMustBeOff ?
|
||||
BlendFunction::ZERO : cmdDraw.info.rasterState.blendFunctionDstAlpha;
|
||||
|
||||
cmdDraw.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
|
||||
cmdDraw.primitive.rasterState.culling = mi->getCullingMode();
|
||||
cmdDraw.primitive.rasterState.colorWrite = mi->isColorWriteEnabled();
|
||||
cmdDraw.primitive.rasterState.depthWrite = mi->isDepthWriteEnabled();
|
||||
cmdDraw.primitive.rasterState.depthFunc = mi->getDepthFunc();
|
||||
cmdDraw.primitive.materialVariant = variant;
|
||||
cmdDraw.info.rasterState.inverseFrontFaces = inverseFrontFaces;
|
||||
cmdDraw.info.rasterState.culling = mi->getCullingMode();
|
||||
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
|
||||
cmdDraw.info.materialVariant = variant;
|
||||
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
|
||||
}
|
||||
|
||||
@@ -432,6 +464,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
UTILS_NOINLINE
|
||||
void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* const commands,
|
||||
FScene::RenderableSoa const& soa, Range<uint32_t> range,
|
||||
backend::BufferObjectHandle renderablesUbo,
|
||||
Variant variant, RenderFlags renderFlags,
|
||||
FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward,
|
||||
uint8_t stereoEyeCount) noexcept {
|
||||
@@ -465,12 +498,14 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co
|
||||
switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) {
|
||||
case CommandTypeFlags::COLOR:
|
||||
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
soa, range, renderablesUbo,
|
||||
variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
stereoEyeCount);
|
||||
break;
|
||||
case CommandTypeFlags::DEPTH:
|
||||
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
soa, range, renderablesUbo,
|
||||
variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
stereoEyeCount);
|
||||
break;
|
||||
default:
|
||||
@@ -493,57 +528,59 @@ UTILS_NOINLINE
|
||||
RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags,
|
||||
Command* UTILS_RESTRICT curr,
|
||||
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
|
||||
backend::BufferObjectHandle renderablesUbo,
|
||||
Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
|
||||
float3 cameraPosition, float3 cameraForward, uint8_t stereoEyeCount) noexcept {
|
||||
|
||||
// generateCommands() writes both the draw and depth commands simultaneously such that
|
||||
// we go throw the list of renderables just once.
|
||||
// (in principle, we could have split this method into two, at the cost of going through
|
||||
// the list twice)
|
||||
|
||||
constexpr bool isColorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR);
|
||||
constexpr bool isDepthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH);
|
||||
|
||||
static_assert(isColorPass != isDepthPass, "only color or depth pass supported");
|
||||
|
||||
const bool depthContainsShadowCasters = bool(extraFlags & CommandTypeFlags::DEPTH_CONTAINS_SHADOW_CASTERS);
|
||||
const bool depthFilterAlphaMaskedObjects = bool(extraFlags & CommandTypeFlags::DEPTH_FILTER_ALPHA_MASKED_OBJECTS);
|
||||
const bool filterTranslucentObjects = bool(extraFlags & CommandTypeFlags::FILTER_TRANSLUCENT_OBJECTS);
|
||||
bool const depthContainsShadowCasters =
|
||||
bool(extraFlags & CommandTypeFlags::DEPTH_CONTAINS_SHADOW_CASTERS);
|
||||
|
||||
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
|
||||
auto const* const UTILS_RESTRICT soaVisibility = soa.data<FScene::VISIBILITY_STATE>();
|
||||
auto const* const UTILS_RESTRICT soaPrimitives = soa.data<FScene::PRIMITIVES>();
|
||||
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaVisibilityMask = soa.data<FScene::VISIBLE_MASK>();
|
||||
auto const* const UTILS_RESTRICT soaInstanceInfo = soa.data<FScene::INSTANCES>();
|
||||
bool const depthFilterAlphaMaskedObjects =
|
||||
bool(extraFlags & CommandTypeFlags::DEPTH_FILTER_ALPHA_MASKED_OBJECTS);
|
||||
|
||||
const bool hasShadowing = renderFlags & HAS_SHADOWING;
|
||||
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
|
||||
const bool hasInstancedStereo = renderFlags & IS_INSTANCED_STEREOSCOPIC;
|
||||
bool const filterTranslucentObjects =
|
||||
bool(extraFlags & CommandTypeFlags::FILTER_TRANSLUCENT_OBJECTS);
|
||||
|
||||
Command cmdColor;
|
||||
bool const hasShadowing =
|
||||
renderFlags & HAS_SHADOWING;
|
||||
|
||||
bool const viewInverseFrontFaces =
|
||||
renderFlags & HAS_INVERSE_FRONT_FACES;
|
||||
|
||||
bool const hasInstancedStereo =
|
||||
renderFlags & IS_INSTANCED_STEREOSCOPIC;
|
||||
|
||||
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
|
||||
|
||||
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
|
||||
auto const* const UTILS_RESTRICT soaVisibility = soa.data<FScene::VISIBILITY_STATE>();
|
||||
auto const* const UTILS_RESTRICT soaPrimitives = soa.data<FScene::PRIMITIVES>();
|
||||
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaVisibilityMask = soa.data<FScene::VISIBLE_MASK>();
|
||||
auto const* const UTILS_RESTRICT soaInstanceInfo = soa.data<FScene::INSTANCES>();
|
||||
|
||||
Command cmd;
|
||||
|
||||
Command cmdDepth;
|
||||
if constexpr (isDepthPass) {
|
||||
cmdDepth.primitive.materialVariant = variant;
|
||||
cmdDepth.primitive.rasterState = {};
|
||||
cmdDepth.primitive.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
|
||||
cmdDepth.primitive.rasterState.depthWrite = true;
|
||||
cmdDepth.primitive.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
||||
cmdDepth.primitive.rasterState.alphaToCoverage = false;
|
||||
cmd.info.materialVariant = variant;
|
||||
cmd.info.rasterState = {};
|
||||
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
|
||||
cmd.info.rasterState.depthWrite = true;
|
||||
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
||||
cmd.info.rasterState.alphaToCoverage = false;
|
||||
}
|
||||
|
||||
const float cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
|
||||
|
||||
for (uint32_t i = range.first; i < range.last; ++i) {
|
||||
// Check if this renderable passes the visibilityMask.
|
||||
if (UTILS_UNLIKELY(!(soaVisibilityMask[i] & visibilityMask))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variant renderableVariant = variant;
|
||||
|
||||
// Signed distance from camera plane to object's center. Positive distances are in front of
|
||||
// the camera. Some objects with a center behind the camera can still be visible
|
||||
// so their distance will be negative (this happens a lot for the shadow map).
|
||||
@@ -558,7 +595,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// float3 d = soaWorldAABBCenter[i] - cameraPosition;
|
||||
// float distance = dot(d, cameraForward);
|
||||
// but saves a couple of instruction, because part of the math is done outside the loop.
|
||||
float distance = dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
|
||||
|
||||
// We negate the distance to the camera in order to create a bit pattern that will
|
||||
// be sorted properly, this works because:
|
||||
@@ -570,72 +606,58 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// Here, objects close to the camera (but behind) will be drawn first.
|
||||
// An alternative that keeps the mathematical ordering is given here:
|
||||
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
|
||||
distance = -distance;
|
||||
const uint32_t distanceBits = reinterpret_cast<uint32_t&>(distance);
|
||||
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
|
||||
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
|
||||
|
||||
// calculate the per-primitive face winding order inversion
|
||||
const bool inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder;
|
||||
const bool hasMorphing = soaVisibility[i].morphing;
|
||||
const bool hasSkinning = soaVisibility[i].skinning;
|
||||
const bool hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
|
||||
cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
|
||||
cmdColor.primitive.index = i;
|
||||
cmdColor.primitive.instanceCount =
|
||||
soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
cmdColor.primitive.instanceBufferHandle = soaInstanceInfo[i].handle;
|
||||
|
||||
// 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(hasInstancedStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
}
|
||||
bool const inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder;
|
||||
bool const hasMorphing = soaVisibility[i].morphing;
|
||||
bool const hasSkinning = soaVisibility[i].skinning;
|
||||
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
// if we are already an SSR variant, the SRE bit is already set,
|
||||
// there is no harm setting it again
|
||||
static_assert(Variant::SPECIAL_SSR & Variant::SRE);
|
||||
Variant renderableVariant = variant;
|
||||
renderableVariant.setShadowReceiver(
|
||||
Variant::isSSRVariant(variant) || (soaVisibility[i].receiveShadows & hasShadowing));
|
||||
renderableVariant.setSkinning(hasSkinningOrMorphing);
|
||||
|
||||
const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i];
|
||||
const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i];
|
||||
FRenderableManager::SkinningBindingInfo const& skinning = soaSkinning[i];
|
||||
FRenderableManager::MorphingBindingInfo const& morphing = soaMorphing[i];
|
||||
|
||||
if constexpr (isDepthPass) {
|
||||
cmdDepth.key = uint64_t(Pass::DEPTH);
|
||||
cmdDepth.key |= uint64_t(CustomCommand::PASS);
|
||||
cmdDepth.key |= makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
|
||||
cmdDepth.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
|
||||
cmdDepth.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
|
||||
cmdDepth.primitive.index = i;
|
||||
cmdDepth.primitive.instanceCount =
|
||||
soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
cmdDepth.primitive.instanceBufferHandle = soaInstanceInfo[i].handle;
|
||||
cmdDepth.primitive.materialVariant.setSkinning(hasSkinningOrMorphing);
|
||||
cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
|
||||
|
||||
cmdDepth.primitive.skinningHandle = skinning.handle;
|
||||
cmdDepth.primitive.skinningOffset = skinning.offset;
|
||||
cmdDepth.primitive.skinningTexture = skinning.handleSampler;
|
||||
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
|
||||
|
||||
if (UTILS_UNLIKELY(hasInstancedStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
}
|
||||
}
|
||||
if constexpr (isColorPass) {
|
||||
renderableVariant.setFog(soaVisibility[i].fog && Variant::isFogVariant(variant));
|
||||
cmd.key = uint64_t(Pass::COLOR);
|
||||
} else if constexpr (isDepthPass) {
|
||||
cmd.key = uint64_t(Pass::DEPTH);
|
||||
cmd.key |= uint64_t(CustomCommand::PASS);
|
||||
cmd.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
|
||||
cmd.info.materialVariant.setSkinning(hasSkinningOrMorphing);
|
||||
cmd.info.rasterState.inverseFrontFaces = inverseFrontFaces;
|
||||
}
|
||||
|
||||
cmdColor.primitive.skinningHandle = skinning.handle;
|
||||
cmdColor.primitive.skinningOffset = skinning.offset;
|
||||
cmdColor.primitive.skinningTexture = skinning.handleSampler;
|
||||
cmdColor.primitive.morphWeightBuffer = morphing.handle;
|
||||
cmd.key |= makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
|
||||
cmd.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
|
||||
cmd.info.index = i;
|
||||
cmd.info.hasHybridInstancing = (bool)soaInstanceInfo[i].handle;
|
||||
cmd.info.instanceCount = soaInstanceInfo[i].count;
|
||||
cmd.info.hasMorphing = (bool)morphing.handle;
|
||||
cmd.info.hasSkinning = (bool)skinning.handle;
|
||||
|
||||
// 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 (hasInstancedStereo) {
|
||||
cmd.info.instanceCount *= stereoEyeCount;
|
||||
}
|
||||
|
||||
if (cmd.info.hasHybridInstancing) {
|
||||
// with hybrid instancing, we already know which UBO to use
|
||||
cmd.info.boh = soaInstanceInfo[i].handle;
|
||||
} else {
|
||||
// with no- or user- instancing, we can only know after instanceify()
|
||||
cmd.info.boh = renderablesUbo;
|
||||
}
|
||||
|
||||
const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing;
|
||||
@@ -655,13 +677,17 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// TODO: we should disable the SKN variant if this primitive doesn't have either
|
||||
// skinning or morphing.
|
||||
|
||||
cmd.info.mi = mi;
|
||||
cmd.info.rph = primitive.getHwHandle();
|
||||
cmd.info.vbih = primitive.getVertexBufferInfoHandle();
|
||||
cmd.info.indexOffset = primitive.getIndexOffset();
|
||||
cmd.info.indexCount = primitive.getIndexCount();
|
||||
cmd.info.type = primitive.getPrimitiveType();
|
||||
cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle();
|
||||
|
||||
if constexpr (isColorPass) {
|
||||
cmdColor.primitive.primitive = &primitive;
|
||||
RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces);
|
||||
|
||||
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
|
||||
|
||||
const bool blendPass = Pass(cmdColor.key & PASS_MASK) == Pass::BLENDED;
|
||||
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
|
||||
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
|
||||
if (blendPass) {
|
||||
// TODO: at least for transparent objects, AABB should be per primitive
|
||||
// but that would break the "local" blend-order, which relies on
|
||||
@@ -669,16 +695,16 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// blend pass:
|
||||
// This will sort back-to-front for blended, and honor explicit ordering
|
||||
// for a given Z value, or globally.
|
||||
cmdColor.key &= ~BLEND_ORDER_MASK;
|
||||
cmdColor.key &= ~BLEND_DISTANCE_MASK;
|
||||
cmd.key &= ~BLEND_ORDER_MASK;
|
||||
cmd.key &= ~BLEND_DISTANCE_MASK;
|
||||
// write the distance
|
||||
cmdColor.key |= makeField(~distanceBits,
|
||||
cmd.key |= makeField(~distanceBits,
|
||||
BLEND_DISTANCE_MASK, BLEND_DISTANCE_SHIFT);
|
||||
// clear the distance if global ordering is enabled
|
||||
cmdColor.key &= ~select(primitive.isGlobalBlendOrderEnabled(),
|
||||
cmd.key &= ~select(primitive.isGlobalBlendOrderEnabled(),
|
||||
BLEND_DISTANCE_MASK);
|
||||
// write blend order
|
||||
cmdColor.key |= makeField(primitive.getBlendOrder(),
|
||||
cmd.key |= makeField(primitive.getBlendOrder(),
|
||||
BLEND_ORDER_MASK, BLEND_ORDER_SHIFT);
|
||||
|
||||
|
||||
@@ -695,11 +721,11 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// In this mode, we override the user's culling mode.
|
||||
|
||||
// TWO_PASSES_TWO_SIDES: this command will be issued 2nd, draw front faces
|
||||
cmdColor.primitive.rasterState.culling =
|
||||
cmd.info.rasterState.culling =
|
||||
(mode == TransparencyMode::TWO_PASSES_TWO_SIDES) ?
|
||||
CullingMode::BACK : cmdColor.primitive.rasterState.culling;
|
||||
CullingMode::BACK : cmd.info.rasterState.culling;
|
||||
|
||||
uint64_t key = cmdColor.key;
|
||||
uint64_t key = cmd.key;
|
||||
|
||||
// draw this command AFTER THE NEXT ONE
|
||||
key |= makeField(1, BLEND_TWO_PASS_MASK, BLEND_TWO_PASS_SHIFT);
|
||||
@@ -713,70 +739,51 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// cancel command if both front and back faces are culled
|
||||
key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
|
||||
|
||||
*curr = cmdColor;
|
||||
*curr = cmd;
|
||||
curr->key = key;
|
||||
++curr;
|
||||
|
||||
// TWO_PASSES_TWO_SIDES: this command will be issued first, draw back sides (i.e. cull front)
|
||||
cmdColor.primitive.rasterState.culling =
|
||||
cmd.info.rasterState.culling =
|
||||
(mode == TransparencyMode::TWO_PASSES_TWO_SIDES) ?
|
||||
CullingMode::FRONT : cmdColor.primitive.rasterState.culling;
|
||||
CullingMode::FRONT : cmd.info.rasterState.culling;
|
||||
|
||||
// TWO_PASSES_ONE_SIDE: this command will be issued first, draw (back side) in depth buffer only
|
||||
cmdColor.primitive.rasterState.depthWrite |= select(mode == TransparencyMode::TWO_PASSES_ONE_SIDE);
|
||||
cmdColor.primitive.rasterState.colorWrite &= ~select(mode == TransparencyMode::TWO_PASSES_ONE_SIDE);
|
||||
cmdColor.primitive.rasterState.depthFunc =
|
||||
cmd.info.rasterState.depthWrite |= select(mode == TransparencyMode::TWO_PASSES_ONE_SIDE);
|
||||
cmd.info.rasterState.colorWrite &= ~select(mode == TransparencyMode::TWO_PASSES_ONE_SIDE);
|
||||
cmd.info.rasterState.depthFunc =
|
||||
(mode == TransparencyMode::TWO_PASSES_ONE_SIDE) ?
|
||||
SamplerCompareFunc::GE : cmdColor.primitive.rasterState.depthFunc;
|
||||
|
||||
SamplerCompareFunc::GE : cmd.info.rasterState.depthFunc;
|
||||
} else {
|
||||
// color pass:
|
||||
// This will bucket objects by Z, front-to-back and then sort by material
|
||||
// in each buckets. We use the top 10 bits of the distance, which
|
||||
// bucketizes the depth by its log2 and in 4 linear chunks in each bucket.
|
||||
cmdColor.key &= ~Z_BUCKET_MASK;
|
||||
cmdColor.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
|
||||
cmd.key &= ~Z_BUCKET_MASK;
|
||||
cmd.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
|
||||
}
|
||||
|
||||
*curr = cmdColor;
|
||||
|
||||
// cancel command if both front and back faces are culled
|
||||
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
|
||||
|
||||
++curr;
|
||||
}
|
||||
|
||||
if constexpr (isDepthPass) {
|
||||
} else if constexpr (isDepthPass) {
|
||||
const RasterState rs = ma->getRasterState();
|
||||
const TransparencyMode mode = mi->getTransparencyMode();
|
||||
const BlendingMode blendingMode = ma->getBlendingMode();
|
||||
const bool translucent = (blendingMode != BlendingMode::OPAQUE
|
||||
&& blendingMode != BlendingMode::MASKED);
|
||||
|
||||
// TODO: we should disable the SKN variant if this primitive doesn't have either
|
||||
// skinning or morphing.
|
||||
|
||||
cmdDepth.key |= mi->getSortingKey(); // already all set-up for direct or'ing
|
||||
|
||||
// unconditionally write the command
|
||||
cmdDepth.primitive.primitive = &primitive;
|
||||
cmdDepth.primitive.rasterState.culling = mi->getCullingMode();
|
||||
cmdDepth.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
|
||||
cmd.key |= mi->getSortingKey(); // already all set-up for direct or'ing
|
||||
cmd.info.rasterState.culling = mi->getCullingMode();
|
||||
|
||||
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
|
||||
cmdDepth.primitive.rasterState.depthWrite = (1 // only keep bit 0
|
||||
cmd.info.rasterState.depthWrite = (1 // only keep bit 0
|
||||
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE))
|
||||
& !(filterTranslucentObjects & translucent)
|
||||
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
|
||||
| writeDepthForShadowCasters;
|
||||
|
||||
*curr = cmdDepth;
|
||||
|
||||
// cancel command if both front and back faces are culled
|
||||
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
|
||||
|
||||
++curr;
|
||||
& !(filterTranslucentObjects & translucent)
|
||||
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
|
||||
| writeDepthForShadowCasters;
|
||||
}
|
||||
|
||||
*curr = cmd;
|
||||
// cancel command if both front and back faces are culled
|
||||
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
return curr;
|
||||
@@ -915,20 +922,20 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
}
|
||||
|
||||
// primitiveHandle may be invalid if no geometry was set on the renderable.
|
||||
if (UTILS_UNLIKELY(!first->primitive.primitive->getHwHandle())) {
|
||||
if (UTILS_UNLIKELY(!first->info.rph)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// per-renderable uniform
|
||||
PrimitiveInfo const info = first->primitive;
|
||||
PrimitiveInfo const info = first->info;
|
||||
pipeline.rasterState = info.rasterState;
|
||||
pipeline.vertexBufferInfo = info.primitive->getVertexBufferInfoHandle();
|
||||
pipeline.primitiveType = info.primitive->getPrimitiveType();
|
||||
pipeline.vertexBufferInfo = info.vbih;
|
||||
pipeline.primitiveType = info.type;
|
||||
assert_invariant(pipeline.vertexBufferInfo);
|
||||
|
||||
if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) {
|
||||
if (UTILS_UNLIKELY(mi != info.mi)) {
|
||||
// this is always taken the first time
|
||||
mi = info.primitive->getMaterialInstance();
|
||||
mi = info.mi;
|
||||
assert_invariant(mi);
|
||||
|
||||
ma = mi->getMaterial();
|
||||
@@ -956,47 +963,33 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
assert_invariant(ma);
|
||||
pipeline.program = ma->getProgram(info.materialVariant);
|
||||
|
||||
uint16_t const instanceCount =
|
||||
info.instanceCount & PrimitiveInfo::INSTANCE_COUNT_MASK;
|
||||
auto getPerObjectUboHandle =
|
||||
[this, &info, &instanceCount]() -> std::pair<Handle<backend::HwBufferObject>, uint32_t> {
|
||||
if (info.instanceBufferHandle) {
|
||||
// "hybrid" instancing -- instanceBufferHandle takes the place of the UBO
|
||||
return { info.instanceBufferHandle, 0 };
|
||||
}
|
||||
bool const userInstancing =
|
||||
(info.instanceCount & PrimitiveInfo::USER_INSTANCE_MASK) != 0u;
|
||||
if (!userInstancing && instanceCount > 1) {
|
||||
// automatic instancing
|
||||
return {
|
||||
mInstancedUboHandle,
|
||||
info.index * sizeof(PerRenderableData) };
|
||||
} else {
|
||||
// manual instancing
|
||||
return { mUboHandle, info.index * sizeof(PerRenderableData) };
|
||||
}
|
||||
};
|
||||
|
||||
// Bind per-renderable uniform block. There is no need to attempt to skip this command
|
||||
// because the backends already do this.
|
||||
auto const [perObjectUboHandle, offset] = getPerObjectUboHandle();
|
||||
assert_invariant(perObjectUboHandle);
|
||||
size_t const offset = info.hasHybridInstancing ?
|
||||
0 : info.index * sizeof(PerRenderableData);
|
||||
|
||||
assert_invariant(info.boh);
|
||||
|
||||
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
|
||||
+UniformBindingPoints::PER_RENDERABLE,
|
||||
perObjectUboHandle,
|
||||
offset,
|
||||
sizeof(PerRenderableUib));
|
||||
info.boh, offset, sizeof(PerRenderableUib));
|
||||
|
||||
if (UTILS_UNLIKELY(info.hasSkinning)) {
|
||||
|
||||
FScene::RenderableSoa const& soa = *mRenderableSoa;
|
||||
|
||||
const FRenderableManager::SkinningBindingInfo& skinning =
|
||||
soa.elementAt<FScene::SKINNING_BUFFER>(info.index);
|
||||
|
||||
if (UTILS_UNLIKELY(info.skinningHandle)) {
|
||||
// note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations
|
||||
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
|
||||
+UniformBindingPoints::PER_RENDERABLE_BONES,
|
||||
info.skinningHandle,
|
||||
info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData),
|
||||
skinning.handle,
|
||||
skinning.offset * sizeof(PerRenderableBoneUib::BoneData),
|
||||
sizeof(PerRenderableBoneUib));
|
||||
// note: always bind the skinningTexture because the shader needs it.
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
|
||||
info.skinningTexture);
|
||||
skinning.handleSampler);
|
||||
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphTargetBuffer);
|
||||
@@ -1006,16 +999,25 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
rebindPipeline = true;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
|
||||
if (UTILS_UNLIKELY(info.hasMorphing)) {
|
||||
|
||||
FScene::RenderableSoa const& soa = *mRenderableSoa;
|
||||
|
||||
const FRenderableManager::SkinningBindingInfo& skinning =
|
||||
soa.elementAt<FScene::SKINNING_BUFFER>(info.index);
|
||||
|
||||
const FRenderableManager::MorphingBindingInfo& morphing =
|
||||
soa.elementAt<FScene::MORPHING_BUFFER>(info.index);
|
||||
|
||||
// Instead of using a UBO per primitive, we could also have a single UBO for all
|
||||
// primitives and use bindUniformBufferRange which might be more efficient.
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphWeightBuffer);
|
||||
morphing.handle);
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphTargetBuffer);
|
||||
// note: even if only morphing is enabled, binding skinningTexture is needed.
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
|
||||
info.skinningTexture);
|
||||
skinning.handleSampler);
|
||||
|
||||
// FIXME: Currently we need to rebind the PipelineState when texture or
|
||||
// UBO binding change.
|
||||
@@ -1029,15 +1031,12 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
driver.bindPipeline(pipeline);
|
||||
}
|
||||
|
||||
if (info.primitive->getHwHandle() != currentPrimitiveHandle) {
|
||||
currentPrimitiveHandle = info.primitive->getHwHandle();
|
||||
driver.bindRenderPrimitive(info.primitive->getHwHandle());
|
||||
if (info.rph != currentPrimitiveHandle) {
|
||||
currentPrimitiveHandle = info.rph;
|
||||
driver.bindRenderPrimitive(info.rph);
|
||||
}
|
||||
|
||||
driver.draw2(
|
||||
info.primitive->getIndexOffset(),
|
||||
info.primitive->getIndexCount(),
|
||||
instanceCount);
|
||||
driver.draw2(info.indexOffset, info.indexCount, info.instanceCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1047,20 +1046,16 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
engine.flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (mInstancedUboHandle) {
|
||||
driver.destroyBufferObject(mInstancedUboHandle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept
|
||||
: mCommands(b, e),
|
||||
RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e,
|
||||
BufferObjectSharedHandle instancedUbo) noexcept
|
||||
: mRenderableSoa(&pass->mRenderableSoa),
|
||||
mCommands(b, e),
|
||||
mCustomCommands(pass->mCustomCommands.data(), pass->mCustomCommands.size()),
|
||||
mUboHandle(pass->mUboHandle),
|
||||
mInstancedUboHandle(pass->mInstancedUboHandle),
|
||||
mInstancedUboHandle(std::move(instancedUbo)),
|
||||
mScissorViewport(pass->mScissorViewport),
|
||||
mPolygonOffsetOverride(false),
|
||||
mScissorOverride(false) {
|
||||
@@ -1068,7 +1063,14 @@ RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command
|
||||
assert_invariant(e <= pass->end());
|
||||
}
|
||||
|
||||
RenderPass::Executor::Executor(Executor const& rhs) = default;
|
||||
RenderPass::Executor::Executor() noexcept
|
||||
: mPolygonOffsetOverride(false),
|
||||
mScissorOverride(false) {
|
||||
}
|
||||
|
||||
RenderPass::Executor::Executor(Executor&& rhs) noexcept = default;
|
||||
|
||||
RenderPass::Executor& RenderPass::Executor::operator=(Executor&& rhs) noexcept = default;
|
||||
|
||||
// this destructor is actually heavy because it inlines ~vector<>
|
||||
RenderPass::Executor::~Executor() noexcept = default;
|
||||
|
||||
+65
-42
@@ -19,16 +19,19 @@
|
||||
|
||||
#include "Allocators.h"
|
||||
|
||||
#include "SharedHandle.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Scene.h"
|
||||
|
||||
#include "private/filament/Variant.h"
|
||||
#include "utils/BitmaskEnum.h"
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/architecture.h>
|
||||
@@ -238,31 +241,33 @@ public:
|
||||
|
||||
struct PrimitiveInfo { // 56 bytes
|
||||
union {
|
||||
FRenderPrimitive const* primitive; // 8 bytes;
|
||||
uint64_t padding = {}; // ensures primitive is 8 bytes on all archs
|
||||
}; // 8 bytes
|
||||
uint64_t rfu0; // 8 bytes
|
||||
backend::RasterState rasterState; // 4 bytes
|
||||
backend::Handle<backend::HwBufferObject> skinningHandle; // 4 bytes
|
||||
backend::Handle<backend::HwSamplerGroup> skinningTexture; // 4 bytes
|
||||
backend::Handle<backend::HwBufferObject> morphWeightBuffer; // 4 bytes
|
||||
backend::Handle<backend::HwSamplerGroup> morphTargetBuffer; // 4 bytes
|
||||
backend::Handle<backend::HwBufferObject> instanceBufferHandle; // 4 bytes
|
||||
FMaterialInstance const* mi;
|
||||
uint64_t padding; // make this field 64 bits on all platforms
|
||||
};
|
||||
backend::RenderPrimitiveHandle rph; // 4 bytes
|
||||
backend::VertexBufferInfoHandle vbih; // 4 bytes
|
||||
backend::BufferObjectHandle boh; // 4 bytes
|
||||
uint32_t indexOffset; // 4 bytes
|
||||
uint32_t indexCount; // 4 bytes
|
||||
uint32_t index = 0; // 4 bytes
|
||||
uint32_t skinningOffset = 0; // 4 bytes
|
||||
backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes
|
||||
|
||||
backend::RasterState rasterState; // 4 bytes
|
||||
|
||||
uint16_t instanceCount; // 2 bytes [MSb: user]
|
||||
Variant materialVariant; // 1 byte
|
||||
uint8_t rfu1; // 1 byte
|
||||
uint32_t rfu2; // 4 byte
|
||||
backend::PrimitiveType type : 3; // 1 byte 3 bits
|
||||
bool hasSkinning : 1; // 1 bit
|
||||
bool hasMorphing : 1; // 1 bit
|
||||
bool hasHybridInstancing : 1; // 1 bit
|
||||
|
||||
static const uint16_t USER_INSTANCE_MASK = 0x8000u;
|
||||
static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu;
|
||||
uint32_t rfu[3]; // 16 bytes
|
||||
};
|
||||
static_assert(sizeof(PrimitiveInfo) == 56);
|
||||
|
||||
struct alignas(8) Command { // 64 bytes
|
||||
CommandKey key = 0; // 8 bytes
|
||||
PrimitiveInfo primitive; // 56 bytes
|
||||
PrimitiveInfo info; // 56 bytes
|
||||
bool operator < (Command const& rhs) const noexcept { return key < rhs.key; }
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new (size_t, void* ptr) {
|
||||
@@ -288,11 +293,11 @@ public:
|
||||
|
||||
// RenderPass can only be moved
|
||||
RenderPass(RenderPass&& rhs) = default;
|
||||
RenderPass& operator=(RenderPass&& rhs) = delete; // could be supported if needed
|
||||
|
||||
// RenderPass can't be copied
|
||||
RenderPass(RenderPass const& rhs) = delete;
|
||||
RenderPass& operator=(RenderPass const& rhs) = delete;
|
||||
RenderPass& operator=(RenderPass&& rhs) = delete;
|
||||
|
||||
// allocated commands ARE NOT freed, they're owned by the Arena
|
||||
~RenderPass() noexcept;
|
||||
@@ -307,6 +312,17 @@ public:
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) noexcept;
|
||||
|
||||
|
||||
class BufferObjectHandleDeleter {
|
||||
std::reference_wrapper<backend::DriverApi> driver;
|
||||
public:
|
||||
explicit BufferObjectHandleDeleter(backend::DriverApi& driver) noexcept : driver(driver) { }
|
||||
void operator()(backend::BufferObjectHandle handle) noexcept;
|
||||
};
|
||||
|
||||
using BufferObjectSharedHandle = SharedHandle<
|
||||
backend::HwBufferObject, BufferObjectHandleDeleter>;
|
||||
|
||||
/*
|
||||
* Executor holds the range of commands to execute for a given pass
|
||||
*/
|
||||
@@ -316,10 +332,10 @@ public:
|
||||
friend class RenderPassBuilder;
|
||||
|
||||
// these fields are constant after creation
|
||||
FScene::RenderableSoa const* mRenderableSoa = nullptr;
|
||||
utils::Slice<Command> mCommands;
|
||||
utils::Slice<CustomCommandFn> mCustomCommands;
|
||||
backend::Handle<backend::HwBufferObject> mUboHandle;
|
||||
backend::Handle<backend::HwBufferObject> mInstancedUboHandle;
|
||||
BufferObjectSharedHandle mInstancedUboHandle;
|
||||
backend::Viewport mScissorViewport;
|
||||
|
||||
backend::Viewport mScissor{}; // value of scissor override
|
||||
@@ -327,7 +343,8 @@ public:
|
||||
bool mPolygonOffsetOverride : 1; // whether to override the polygon offset setting
|
||||
bool mScissorOverride : 1; // whether to override the polygon offset setting
|
||||
|
||||
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept;
|
||||
Executor(RenderPass const* pass, Command const* b, Command const* e,
|
||||
BufferObjectSharedHandle instancedUbo) noexcept;
|
||||
|
||||
void execute(FEngine& engine, const Command* first, const Command* last) const noexcept;
|
||||
|
||||
@@ -336,9 +353,16 @@ public:
|
||||
backend::Viewport const& scissor) noexcept;
|
||||
|
||||
public:
|
||||
Executor() = default;
|
||||
Executor(Executor const& rhs);
|
||||
Executor& operator=(Executor const& rhs) = default;
|
||||
Executor() noexcept;
|
||||
|
||||
// can't be copied
|
||||
Executor(Executor const& rhs) noexcept = delete;
|
||||
Executor& operator=(Executor const& rhs) noexcept = delete;
|
||||
|
||||
// can be moved
|
||||
Executor(Executor&& rhs) noexcept;
|
||||
Executor& operator=(Executor&& rhs) noexcept;
|
||||
|
||||
~Executor() noexcept;
|
||||
|
||||
// if non-null, overrides the material's polygon offset
|
||||
@@ -353,11 +377,11 @@ public:
|
||||
|
||||
// returns a new executor for this pass
|
||||
Executor getExecutor() const {
|
||||
return { this, mCommandBegin, mCommandEnd };
|
||||
return getExecutor(mCommandBegin, mCommandEnd);
|
||||
}
|
||||
|
||||
Executor getExecutor(Command const* b, Command const* e) const {
|
||||
return { this, b, e };
|
||||
return { this, b, e, mInstancedUboHandle };
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -368,7 +392,15 @@ private:
|
||||
// This is the main function of this class, this appends commands to the pass using
|
||||
// the current camera, geometry and flags set. This can be called multiple times if needed.
|
||||
void appendCommands(FEngine& engine,
|
||||
utils::Slice<Command> commands, CommandTypeFlags commandTypeFlags) noexcept;
|
||||
utils::Slice<Command> commands,
|
||||
backend::BufferObjectHandle uboHandle,
|
||||
utils::Range<uint32_t> const visibleRenderables,
|
||||
CommandTypeFlags commandTypeFlags,
|
||||
RenderFlags renderFlags,
|
||||
FScene::VisibleMaskType visibilityMask,
|
||||
Variant variant,
|
||||
math::float3 cameraPosition,
|
||||
math::float3 cameraForwardVector) noexcept;
|
||||
|
||||
// Appends a custom command.
|
||||
void appendCustomCommand(Command* commands,
|
||||
@@ -381,7 +413,7 @@ private:
|
||||
void sortCommands(Arena& arena) noexcept;
|
||||
|
||||
// instanceify commands then trims sentinels
|
||||
void instanceify(FEngine& engine, Arena& arena) noexcept;
|
||||
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
|
||||
|
||||
// We choose the command count per job to minimize JobSystem overhead.
|
||||
// On a Pixel 4, 2048 commands is about half a millisecond of processing.
|
||||
@@ -394,6 +426,7 @@ private:
|
||||
|
||||
static inline void generateCommands(CommandTypeFlags commandTypeFlags, Command* commands,
|
||||
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
|
||||
backend::BufferObjectHandle renderablesUbo,
|
||||
Variant variant, RenderFlags renderFlags,
|
||||
FScene::VisibleMaskType visibilityMask,
|
||||
math::float3 cameraPosition, math::float3 cameraForward,
|
||||
@@ -402,6 +435,7 @@ private:
|
||||
template<RenderPass::CommandTypeFlags commandTypeFlags>
|
||||
static inline Command* generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags, Command* curr,
|
||||
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
|
||||
backend::BufferObjectHandle renderablesUbo,
|
||||
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
|
||||
math::float3 cameraPosition, math::float3 cameraForward,
|
||||
uint8_t instancedStereoEyeCount) noexcept;
|
||||
@@ -412,23 +446,12 @@ private:
|
||||
static void updateSummedPrimitiveCounts(
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
|
||||
|
||||
|
||||
FScene::RenderableSoa const& mRenderableSoa;
|
||||
utils::Range<uint32_t> const mVisibleRenderables;
|
||||
backend::Handle<backend::HwBufferObject> const mUboHandle;
|
||||
math::float3 const mCameraPosition;
|
||||
math::float3 const mCameraForwardVector;
|
||||
RenderFlags const mFlags;
|
||||
Variant const mVariant;
|
||||
FScene::VisibleMaskType const mVisibilityMask;
|
||||
backend::Viewport const mScissorViewport;
|
||||
|
||||
// Pointer to the first command
|
||||
Command* mCommandBegin = nullptr;
|
||||
// Pointer to one past the last command
|
||||
Command* mCommandEnd = nullptr;
|
||||
Command* mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command* mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
// a UBO for instanced primitives
|
||||
backend::Handle<backend::HwBufferObject> mInstancedUboHandle;
|
||||
BufferObjectSharedHandle mInstancedUboHandle;
|
||||
// a vector for our custom commands
|
||||
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
|
||||
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
|
||||
|
||||
@@ -54,7 +54,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor) noexcept {
|
||||
RenderPass::Executor passExecutor) noexcept {
|
||||
|
||||
struct ColorPassData {
|
||||
FrameGraphId<FrameGraphTexture> shadows;
|
||||
@@ -200,7 +200,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
[=, &view, &engine](FrameGraphResources const& resources,
|
||||
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor) noexcept;
|
||||
RenderPass::Executor passExecutor) noexcept;
|
||||
|
||||
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
|
||||
@@ -365,7 +365,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
.camera(cameraInfo)
|
||||
.visibilityMask(entry.visibilityMask)
|
||||
.geometry(scene->getRenderableData(),
|
||||
entry.range, scene->getRenderableUBO())
|
||||
entry.range,
|
||||
view.getRenderableUBO())
|
||||
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
|
||||
.build(engine);
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_SHARED_HANDLE_H
|
||||
#define TNT_FILAMENT_SHARED_HANDLE_H
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
* SharedHandle is a bit like shared_ptr<> but for Handle<>, the destruction is
|
||||
* performed by a Deleter functor that needs to be provided. We only support strong
|
||||
* references for now.
|
||||
*
|
||||
* caveat: The current implementation is not thread-safe.
|
||||
*/
|
||||
template<typename T, typename Deleter>
|
||||
struct SharedHandle {
|
||||
SharedHandle() noexcept = default;
|
||||
|
||||
~SharedHandle() noexcept {
|
||||
dec(mControlBlockPtr);
|
||||
}
|
||||
|
||||
SharedHandle(SharedHandle const& rhs) noexcept
|
||||
: mControlBlockPtr(inc(rhs.mControlBlockPtr)) {
|
||||
}
|
||||
|
||||
SharedHandle(SharedHandle&& rhs) noexcept {
|
||||
std::swap(mControlBlockPtr, rhs.mControlBlockPtr);
|
||||
}
|
||||
|
||||
SharedHandle& operator=(SharedHandle const& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
inc(rhs.mControlBlockPtr); // add a reference to other control block
|
||||
dec(mControlBlockPtr); // drop a reference from ours (possibly destroying it)
|
||||
mControlBlockPtr = rhs.mControlBlockPtr; // adopt the new control block
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SharedHandle& operator=(SharedHandle&& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
std::swap(mControlBlockPtr, rhs.mControlBlockPtr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// initialize the SharedHandle and provide a Deleter
|
||||
template<typename ... ARGS>
|
||||
explicit SharedHandle(backend::Handle<T> const& rhs, ARGS&& ... args) noexcept
|
||||
: mControlBlockPtr(new ControlBlock(rhs, std::forward<ARGS>(args)...)) {
|
||||
}
|
||||
|
||||
// initialize the SharedHandle and provide a Deleter
|
||||
template<typename ... ARGS>
|
||||
explicit SharedHandle(backend::Handle<T>&& rhs, ARGS&& ... args) noexcept
|
||||
: mControlBlockPtr(new ControlBlock(rhs, std::forward<ARGS>(args)...)) {
|
||||
}
|
||||
|
||||
// automatically converts to Handle<T>
|
||||
operator backend::Handle<T>() const noexcept { // NOLINT(*-explicit-constructor)
|
||||
return mControlBlockPtr ? mControlBlockPtr->handle : backend::Handle<T>{};
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return mControlBlockPtr ? (bool)mControlBlockPtr->handle : false;
|
||||
}
|
||||
|
||||
void clear() noexcept { dec(mControlBlockPtr); }
|
||||
|
||||
private:
|
||||
struct ControlBlock {
|
||||
template<typename ... ARGS>
|
||||
explicit ControlBlock(backend::Handle<T> handle, ARGS&& ... args) noexcept
|
||||
: deleter(std::forward<ARGS>(args)...), handle(std::move(handle)) {
|
||||
}
|
||||
void inc() noexcept {
|
||||
++count;
|
||||
}
|
||||
void dec() noexcept {
|
||||
if (--count == 0) {
|
||||
deleter(handle);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
Deleter deleter;
|
||||
int32_t count = 1;
|
||||
backend::Handle<T> handle;
|
||||
};
|
||||
|
||||
ControlBlock* inc(ControlBlock* const ctrlBlk) noexcept {
|
||||
if (ctrlBlk) {
|
||||
ctrlBlk->inc();
|
||||
}
|
||||
return ctrlBlk;
|
||||
}
|
||||
|
||||
void dec(ControlBlock* const ctrlBlk) noexcept {
|
||||
if (ctrlBlk) {
|
||||
ctrlBlk->dec();
|
||||
}
|
||||
}
|
||||
|
||||
ControlBlock* mControlBlockPtr = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_SHARED_HANDLE_H
|
||||
@@ -28,12 +28,13 @@ void* SwapChain::getNativeWindow() const noexcept {
|
||||
return downcast(this)->getNativeWindow();
|
||||
}
|
||||
|
||||
void SwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) {
|
||||
downcast(this)->setFrameScheduledCallback(callback, user);
|
||||
void SwapChain::setFrameScheduledCallback(
|
||||
backend::CallbackHandler* handler, FrameScheduledCallback&& callback) {
|
||||
downcast(this)->setFrameScheduledCallback(handler, std::move(callback));
|
||||
}
|
||||
|
||||
SwapChain::FrameScheduledCallback SwapChain::getFrameScheduledCallback() const noexcept {
|
||||
return downcast(this)->getFrameScheduledCallback();
|
||||
bool SwapChain::isFrameScheduledCallbackSet() const noexcept {
|
||||
return downcast(this)->isFrameScheduledCallbackSet();
|
||||
}
|
||||
|
||||
void SwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user