Compare commits

...
Author SHA1 Message Date
Sungun Park 6fc16bdcda Release Filament 1.51.8 2024-05-13 20:53:36 +00:00
Mathias Agopian 4f021583f1 backend tests were broken by a change in TargetBufferInfo
- a field was added, which broke the layout of the structure. We fix it
by adding constructors which will handle the old and new way of
initializing this structure.

- one of the test needed a hash update

- OpenGLContext wrongly asserted when trying to unbind texture 0
2024-05-10 14:39:22 -07:00
Powei Feng ef15a29c0c vk: fix missing lib for backend test 2024-05-10 13:00:00 -07:00
Benjamin Doherty a0472d3c9f Rename Metal log message 2024-05-10 11:25:34 -07:00
Ben Doherty 54a800a25d Metal: implement more accurate buffer tracking (#7839) 2024-05-10 11:14:41 -07:00
Powei Feng 7f8fbe586c gl: push constant small clean-up (#7841) 2024-05-10 10:23:32 -07:00
Powei Feng 6f2c45c76d Add push constants (#7817)
- Push constants is a small set of bytes that can be recorded
   directly on the command buffer.
 - Implemented it for the vulkan/gl backend.
2024-05-09 16:14:03 -07:00
Ryan ad60008b6a ryanmyers: Improve logging for Metallib function lookup failures (#7836)
If a .metallib was compiled with a target iOS version that's newer than
the current device, loading the .metallib may succeed, but finding main0
(or any other function in it) will fail. Currently, this causes a crash
due to an assert. Logging the error and returning
MetalFunctionBundle::error() makes the crash slightly easier to
diagnose.

(Note that in practice, this will probably be a useless "Compiler
encountered an internal error" message -- the GPU backend is crashing,
and the Metal stub library sees XPC_ERROR_CONNECTION_INTERRUPTED. It
retries up to 3 times (crashing each time) and then gives up.)
2024-05-09 10:49:41 -07:00
Mathias Agopian d87f9b621b add a simple tranformmanager unit test
this was to test issue #7827
2024-05-09 09:00:41 -07:00
Ben Doherty 1b38cda0d5 Add preferredShaderLanguage Java bindings (#7835) 2024-05-08 14:42:40 -07:00
Ben Doherty a1dea7b1fa Metal: log slow buffer allocation times (#7834) 2024-05-08 14:39:53 -07:00
Mathias Agopian 744708b5ca remove the single <sstream> usage we have
the STL's stream headers can bring in a lot of code, we don't use them.
2024-05-07 16:23:31 -07:00
Benjamin Doherty e901837317 Log excess buffer allocations for Metal 2024-05-07 15:39:48 -07:00
Powei Feng 17f71c9b81 Release Filament 1.51.7 2024-05-07 14:35:42 -07:00
Mathias AgopianandPowei Feng b056c126fe Add an Engine debug setting to force GLES 2.0 (#7829)
* Add an Engine debug setting to force GLES 2.0

This setting is only meaningful on GLES backends, it's otherwise
ignored. When set to true, the backend will try to force a ES2 context
if supported. If not supported by the platform,
the backend will pretend it's a ES2 context.

This setting is currently only taken into account by the EGL platform.

* Update filament/backend/include/backend/Platform.h

Co-authored-by: Powei Feng <powei@google.com>

---------

Co-authored-by: Powei Feng <powei@google.com>
2024-05-07 09:53:31 -07:00
Sungun Park 6ac36d1aab Fix a shader error (#7828)
gl_ViewID_OVR is of uint type. Explicitly convert it to int to fix the
error.
2024-05-06 21:03:04 +00:00
Ben Doherty a1752b363c Add preferredShaderLanguage option to Engine::Config (#7816) 2024-05-06 12:12:38 -07:00
Levente Koncz e855f4aafb Fix mixed up FBO targets in status check code inside OpenGLDriver::blit() 2024-05-06 09:17:15 -07:00
Ben Doherty 1c693e24cf Metal: track types of buffers (#7796) 2024-05-03 14:32:01 -07:00
Ben Doherty af92a1f21b Add getEyeIndex vertex API (#7822) 2024-05-03 14:22:24 -07:00
Powei Feng 10ff1ee3e4 Update filament to 1.51.6 for public pages (#7821)
Updated both /viewer and /remote
2024-05-03 20:49:53 +00:00
Vladyslav PopovychandVladyslav 5449f46350 Remove bitcode from iOS builds (#7802)
Co-authored-by: Vladyslav <vlad@arthouse.ar>
2024-05-03 12:25:00 -07:00
Mathias Agopian 2967888d26 repair instanced stereo in debug builds (#7819)
- it was broken by a wrong assertion
- also auto instancing + stereo instancing was
  always broken, but this makes it work
2024-05-03 09:36:09 -07:00
Powei Feng afe81d6889 vk: Use offset when uploading buffer data (#7818) 2024-05-02 11:17:32 -07:00
Mathias AgopianandBen Doherty 95134883d6 add the -a option to skinning and morphing samples (#7815)
* add the `-a` option to skinning and morphing samples

* Update samples/skinningtest.cpp

Co-authored-by: Ben Doherty <bendoherty@google.com>

* Update samples/helloskinningbuffer_morebones.cpp

Co-authored-by: Ben Doherty <bendoherty@google.com>

* Update samples/hellomorphing.cpp

Co-authored-by: Ben Doherty <bendoherty@google.com>

* Update samples/helloskinningbuffer.cpp

Co-authored-by: Ben Doherty <bendoherty@google.com>

---------

Co-authored-by: Ben Doherty <bendoherty@google.com>
2024-05-01 15:24:01 -07:00
Mathias Agopian 600c122761 fix ES2 support
- make sure we can build with ES2 headers only
- make sure to not use ES3 features when in ES2 mode
  (post processing was accidentally creating an R8 texture, 
  which is not supported)
2024-05-01 13:29:58 -07:00
Mathias Agopian 1cf5335964 Skybox doesn't need a commit() method
The material parameter changes are committed automatically in
Engine::prepare(), no need to do it explicitly for SkyBox.
2024-05-01 10:11:39 -07:00
Eliza Velasquez 4ac4ffd8d6 filagui: Split uiBlit material into two
Unfortunately, the external uniform was not optimized out as expected. The only
option is to split it into its own independent material.
2024-04-30 14:14:23 -07:00
Eliza Velasquez 6c40721695 Fix broken ImGui on web
I was unfortunately naive about the way that Filament handled external textures
on non-GLES platforms. This fix restricts the changes to Android (which is the
only place this change is required in the first place). Long story short, the
change broke WebGL. Desktop seems to be unaffected.
2024-04-30 14:14:23 -07:00
Benjamin Doherty 56fa078376 Release Filament 1.51.6 2024-04-29 16:49:28 -07:00
Ben Doherty 883507a26f Remove spirv-opt size optimizations for MSL (#7495) 2024-04-29 16:47:05 -07:00
Powei Feng d697d42c4a vk: finer-grain barrier at renderpass end (#7808)
- At the end of a renderpass we use a more fine-grained barrier
   for each of the attachments in the render target
 - Make sure that buffer update are barrier'd from previous reads
 - Remove previous Mali workaround barriers.  Seems to be fine
   without them on pixels + Mali.
2024-04-29 11:32:04 -07:00
Ben Doherty e7feee7d5b Switch setFrameScheduledCallback to use utils::Invocable (#7792) 2024-04-29 10:18:28 -07:00
Powei Feng 4c75e6e9a2 gltfio: enable extended implementation (#7776)
This change will enable proper flat-shading and MikkTSpace.

Caveats:
 - Only for disk-local glTF resources
 - iOS, Web, Android do not work as of now

Fixes #6358, #7444
2024-04-27 00:28:08 +00:00
Mathias Agopian 8ab8525359 fix a missing ubo upload in bloom set-up 2024-04-26 16:49:39 -07:00
Powei Feng 65c794881f vk: various clean up (#7805)
- use namespace for ImageUtility
 - use FILAMENT_BACKEND_DEBUG_FLAG
 - remove unused usage flag type
 - add const list of VKFormat for iteration
 - fix sampler name debug
2024-04-26 22:26:16 +00:00
Powei Feng 08d6d57ad0 vk: simplify VulkanAttachment (#7801) 2024-04-26 21:48:15 +00:00
Mathias Agopian 254cf15f0e a couple of significant improvements to instancing
The main goal of this change is to avoid having to select the 
"per renderable" UBO at execution time. Instead we want the
PrimitiveInfo structure to already know which UBO will be used.
Concretely, this means that this determination must be done when
the RenderPass is created.

When automatic instancing is used, the RenderPass creates a temporary
UBO to store the instance info. This UBO's life-time is dictated by both
the life-time of the RenderPass and the Executors that where created
from it. For this reason we introduce SharedHandle<> to correctly 
account for the owner's lifetime. This fixes a potential bugs where
that handle could have been destroyed and used later; in practice this
bug didn't happen however.

A couple other changes:

- RenderPass has a bunch of fields that were actually temporary, so we
removed those.

- The canonical "per-renderable" UBO was owned by View but accessed 
through Scene. This was confusing, it's now accessed through View.
2024-04-26 14:15:00 -07:00
Mathias Agopian 22d99bac3d simplify generateCommands()
It's been a while that generateCommands() can only generate either 
the depth or color pass but not both at once; we can simplify/de-dup
the code by leveraging that.
2024-04-26 14:15:00 -07:00
Mathias Agopian 420f06bef3 implement 16 and 8 bits clz, ctz and popcount
This is needed for utils::bitset8 and bitset16.
2024-04-26 14:13:48 -07:00
Powei Feng bf8bcef35c Add option to define a backend debug flag (#7798)
To allow easy enabling/disabling of debug options like vulkan
validation, android systrace, debug printing, and others, we
introduce an option to add a preprocessor flag so that a
backend can (optionally) use it to manage debug options.
2024-04-25 23:42:55 +00:00
Eliza Velasquez bfab9f9c32 engine: Add isPaused() 2024-04-25 16:11:14 -07:00
Powei Feng c8335fade7 vk: Fix unsupported depth blitting (#7789)
* vk: Fix unsupported depth blitting

On certain hardware (pixel 4 for example), blitting of depth texture
is not supported as an "optimalTilingFeature".  In these cases, we'd
would need to do a shader-based blit. We
 - Add the shader blit in PostProcessingManager
 - Add a driver API to check for support for blitting depthStencil
   attachments.
 - Fix some debugging ifdefs in vk backend.

The validation fixed is:
`[ VUID-vkCmdBlitImage-dstImage-02000 ] Object 0: handle = 0xb400007c300701d0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0xf2039b0000000771, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x86bc2a78 | In vkCmdBlitImage, VkFormatFeatureFlags (0x1c601) does not support required feature VK_FORMAT_FEATURE_2_BLIT_DST_BIT for format 126 used by VkImage 0xf2039b0000000771[] with tiling VK_IMAGE_TILING_OPTIMAL. The Vulkan spec states: The format features of dstImage must contain VK_FORMAT_FEATURE_BLIT_DST_BIT`
2024-04-24 18:27:57 +00:00
Mathias Agopian 99eac62b4e give proper move semantics to Handle<> 2024-04-23 15:38:29 -07:00
Mathias Agopian 6146d071ba StructureOfArray::push_back(&&) didn't do a move 2024-04-23 15:38:29 -07:00
Mathias Agopian d26c972f5b add missing includes in RenerableManager 2024-04-23 15:38:29 -07:00
Mathias Agopian 93dc218b83 fix PerShadowMapUniform includes 2024-04-23 15:38:29 -07:00
Ben Doherty b56379dc0f Add license information to matedit (#7790) 2024-04-23 14:05:53 -06:00
Mathias Agopian e4442a5eb2 optimize the inner rendering loop
This largely undoes a change I did recently where PrimitiveInfo has
a FRenderPrimitive* to save some space and keep a command at 64 bytes.

This wasn't a good idea because the inner rendering loop shouldn't 
to any dereference in the common case.

This change reorganises PrimitiveInfo such that it stores all the data
necessary to render a primitive in the common case. The less common
cases are when hybrid instancing, morphing or skinning are used; in 
those cases, a dereference into the renderable SOA is needed.

PrimitiveInfo currently has 16 bytes free, which we keep for futur use.
2024-04-23 10:22:33 -07:00
Ben Doherty 490e8cf0d0 Add option to preserve text shaders (#7786) 2024-04-23 10:38:23 -06:00
Mathias Agopian 0d25f79421 gl: simplify how we track bound textures
In opengl it's possible to bind several textures to the same texture
unit as long as they're a different target. Until now we were tracking
that state. In practice it's not very useful to bind several textures
to the same unit (it is a little bit when updating texture data, but
not when rendering). With the coming change to a descriptor set API, it
is better to have a 1-to-1 mapping between bound textures and texture
units.

So with this change, only a single texture can be bound to a texture 
unit. If another texture in bound to the same unit with a different
target, we first unbind the texture from the current target.

There is less state to track, and it allows us to 
"unbind a texture unit" (whereas before we'd have to iterate through 
all the possible targets for that unit and unbind all of them).
2024-04-22 16:03:24 -07:00
Mathias Agopian d0586743f6 add ADPF support for backend thread 2024-04-22 15:09:08 -07:00
Mathias Agopian 14a99cbc63 add the target refresh interval to the backend beginFrame 2024-04-22 15:09:08 -07:00
Benjamin Doherty 808b51c1cf matedit: fix use-after-free 2024-04-22 16:06:39 -06:00
165 changed files with 3865 additions and 1294 deletions
+12 -10
View File
@@ -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
# ==================================================================================================
+2 -2
View File
@@ -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
+17
View File
@@ -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 -1
View File
@@ -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.
+22 -2
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -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.
+1 -1
View File
@@ -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>
+2
View File
@@ -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
+4
View File
@@ -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()
+10 -3
View File
@@ -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
+15
View File
@@ -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) {
+6
View File
@@ -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;
+121 -37
View File
@@ -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;
+16 -6
View File
@@ -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,
+1 -1
View File
@@ -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,
+28 -5
View File
@@ -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) {
}
+14 -11
View File
@@ -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;
+31 -13
View File
@@ -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());
}
+11 -2
View File
@@ -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) {
}
+3 -3
View File
@@ -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;
+28 -7
View File
@@ -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!!!
+19 -37
View File
@@ -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);
+58 -28
View File
@@ -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) {
+3 -1
View File
@@ -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;
}
+19 -1
View File
@@ -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 {
+25 -4
View File
@@ -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));
+1
View File
@@ -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 << "}";
}
+12 -44
View File
@@ -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);
+50 -17
View File
@@ -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, &region);
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
+2 -1
View File
@@ -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
+8 -12
View File
@@ -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),
+12 -5
View File
@@ -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;
+104 -59
View File
@@ -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;
};
+10 -14
View File
@@ -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;
+9 -8
View File
@@ -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;
+66 -28
View File
@@ -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;
}
+48 -17
View File
@@ -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)
+9 -10
View File
@@ -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
+316 -5
View File
@@ -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);
}
+8 -8
View File
@@ -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], &params);
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, &params);
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, &params);
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],
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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);
}
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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, {} });
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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);
+38
View File
@@ -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.
*
+24 -16
View File
@@ -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
+5
View File
@@ -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);
+51
View File
@@ -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
+8
View File
@@ -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
+12 -6
View File
@@ -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;
}
+4 -11
View File
@@ -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;
+109 -57
View File
@@ -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,
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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>>;
+2 -2
View File
@@ -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();
+1 -1
View File
@@ -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,
+2 -1
View File
@@ -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);
+126
View File
@@ -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
+5 -4
View File
@@ -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