Compare commits

..
24 Commits
Author SHA1 Message Date
Benjamin Doherty 7fc8e339e7 Merge branch 'rc/1.53.1' into release 2024-06-25 11:46:53 -07:00
Ryan 0395df3689 Fix use-after-free of a std::mutex in PresentCallable. (#7934)
If a user is using `setFrameScheduledCallback()`, managing the provided PresentCallable during engine shutdown is tricky -- we'll likely get a final frame scheduled when we flush the engine's work queue, but the PresentCallable will schedule the final CAMetalDrawable to be released on main thread afterwards, even if we call `present(false)` to skip it. If the swap chain is destroyed before that main queue block gets executed, the mutex presenting that drawable will no longer exist, causing a crash.

To make things easier, store the std::mutex in a shared_ptr, so that a PresentCallable can safely outlive the FSwapChain instance that created it and clean itself up afterwards.

Alternatives considered, all of which seem unfortunate:
* Require users to clear out the callback before shutting down the engine, so that any final drawables are immediately discarded instead of using PresentCallable
* Require users to split up the engine teardown across two main queue blocks, ensuring that the PresentCallable cleanup executes before swapchains are destroyed
* Drop the PresentCallable on the ground and leak the memory
2024-06-24 11:01:44 -07:00
Benjamin Doherty 64f03b3832 Merge branch 'rc/1.53.0' into release 2024-06-17 17:26:55 -07:00
Benjamin Doherty 982b159b3e Bump version to 1.53.1 2024-06-17 17:26:55 -07:00
Benjamin Doherty e95edef9d7 Release Filament 1.53.0 2024-06-17 17:26:47 -07:00
Grant Commodore 9cfef925bb Updates vulkan swapchain synchronization (#7915)
Abstracts the synchronization of the vulkan swapchain so that it is easier to override during the acquisition and presentation of images. A new structure (ImageSyncData) is created to hold swapchain synchronization data.

FIXES=[338303279]
2024-06-17 22:29:28 +00:00
Hanno J. Gödecke 3900fc6c37 feat: support getParameter from MaterialInstance (#7686) 2024-06-17 11:31:17 -07:00
Powei Feng b8ec1658f3 vk: single output stream debug logging (#7925)
In some debug instances, it's nice to have the logs all go to one
output stream so that logs are ordered. We add a FVK_DEBUG flag
for this use case.
2024-06-14 22:31:45 +00:00
Grant Commodore 371bd1f01f Checks/enable if the multiview extension is supported (#7923)
Checks if the vulkan driver supports multiview. The result is stored in the vulkan context.

BUG=[332425392]
FIXES=[346860138]
2024-06-13 10:20:24 -07:00
Benjamin Doherty 6e3cccf30c Bump version to 1.53.0 2024-06-11 16:42:17 -07:00
Benjamin Doherty df34f92f8f Release Filament 1.52.3 2024-06-11 16:42:10 -07:00
Powei Feng 43331d04e5 gltfio: [extended] Fix generated color default (#7917)
Fixes #7905
2024-06-11 12:21:14 -07:00
Powei Feng 9917d4b7d9 Null out pending edits once latched (#7916)
Otherwise, we'd keep swapping between the edited version and the
original, causing matdbg to not work.
2024-06-10 21:32:49 +00:00
Mathias Agopian 682150ceec add debug option to disable sub-passes 2024-06-10 13:18:59 -07:00
Powei Feng af2ecf201f vk: workaround extension name length = 0 (adreno) (#7910) 2024-06-07 10:29:38 -07:00
Serge Metral 020668733a Adding the begin frame message for later xtrace post processing. (#7903)
BUG=343930963
2024-06-05 21:58:31 +00:00
Powei Feng 4c6d653d60 samples: Enable backend selection for hellopbr (#7898) 2024-06-05 19:15:45 +00:00
Mathias Agopian d1022527ac export PanicStream since it's a public API 2024-06-05 11:00:06 -07:00
Romain Guy 68c7a6c7b6 Update Android dependencies to latest (#7902) 2024-06-04 22:33:02 +00:00
Romain Guy f077fff012 Update actions/checkout to a supported version of Node.js (#7904) 2024-06-04 15:03:43 -07:00
Mathias Agopian bea02427ed keep the normal transforms in float during skinning
FIXES=[342459864]
2024-06-04 13:37:47 -07:00
Mathias Agopian b5d7de06bc Engine::unprotected() drops the command queue back to unprotected mode
FIXES=[344021154]
2024-06-04 11:46:00 -07:00
Mathias Agopian 749b063af3 PerformanceHintManager needs a valid Java thread
FIXES=[343965368, 343550453]
2024-06-04 10:22:21 -07:00
Ryan 7aefa99e06 Acquire a mutex before releasing CAMetalDrawables on main thread. (#7888)
PresentDrawable was moved to main thread by default in google#7535 and stopped
most crashes when a drawable is released. But there still appears to be crashes
if a drawable is released on main thread at the same time that -nextDrawable is
called from the Filament render thread. (It's likely that the drawable pool in
CAMetalLayer is completely non-thread-safe.)

So, add a mutex to the swapchain and always acquire it before creating or
releasing a CAMetalDrawable.

Users can opt out of this behavior by passing
-DFILAMENT_LOCK_METAL_DRAWABLE_POOL=0.
2024-06-03 19:37:46 +00:00
64 changed files with 536 additions and 200 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh continuous
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh continuous
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh continuous
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
name: npm-deploy
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ github.event.inputs.release_tag }}
# Setup .npmrc file to publish to npm
+5 -5
View File
@@ -18,7 +18,7 @@ jobs:
os: [macos-14, ubuntu-22.04]
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
@@ -32,7 +32,7 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
build\windows\build-github.bat presubmit
@@ -43,7 +43,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
@@ -57,7 +57,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh presubmit
@@ -70,7 +70,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh presubmit
+5 -5
View File
@@ -41,7 +41,7 @@ jobs:
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
@@ -76,7 +76,7 @@ jobs:
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
@@ -109,7 +109,7 @@ jobs:
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: actions/setup-java@v3
@@ -163,7 +163,7 @@ jobs:
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
@@ -197,7 +197,7 @@ jobs:
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
shell: bash
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh continuous
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3.3.0
- uses: actions/checkout@v4.1.6
- name: Run build script
run: |
build\windows\build-github.bat continuous
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.52.3'
implementation 'com.google.android.filament:filament-android:1.53.1'
}
```
@@ -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.52.3'
pod 'Filament', '~> 1.53.1'
```
### Snapshots
+7
View File
@@ -7,6 +7,13 @@ 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.53.1
## v1.53.0
- engine: fix skinning normals with large transforms (b/342459864) [⚠️ **New Material Version**]
## v1.52.3
+6 -6
View File
@@ -83,12 +83,12 @@ buildscript {
'minSdk': 21,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
'kotlin_coroutines': '1.7.3',
'kotlin': '2.0.0',
'kotlin_coroutines': '1.9.0-RC',
'buildTools': '34.0.0',
'ndk': '26.1.10909125',
'androidx_core': '1.12.0',
'androidx_annotations': '1.7.0'
'ndk': '27.0.11718014',
'androidx_core': '1.13.1',
'androidx_annotations': '1.8.0'
]
ext.deps = [
@@ -104,7 +104,7 @@ buildscript {
]
dependencies {
classpath 'com.android.tools.build:gradle:8.2.0'
classpath 'com.android.tools.build:gradle:8.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
@@ -420,6 +420,13 @@ Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
engine->setPaused(paused);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nUnprotected(JNIEnv*, jclass,
jlong nativeEngine, jboolean paused) {
Engine* engine = (Engine*) nativeEngine;
engine->unprotected();
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -1289,6 +1289,18 @@ public class Engine {
nSetPaused(getNativeObject(), paused);
}
/**
* Switch the command queue to unprotected mode. Protected mode can be activated via
* Renderer::beginFrame() using a protected SwapChain.
* @see Renderer
* @see SwapChain
*/
public void unprotected() {
nUnprotected(getNativeObject());
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
if (mNativeObject == 0) {
@@ -1366,6 +1378,7 @@ public class Engine {
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 void nUnprotected(long nativeEngine);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.52.3
VERSION_NAME=1.53.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+2 -1
View File
@@ -57,7 +57,8 @@ FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/ndk.version)}
# Install the required NDK version specifically (if not present)
if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION" > /dev/null
yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION"
fi
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
+1 -1
View File
@@ -1 +1 @@
26.1.10909125
27.0.11718014
@@ -90,8 +90,13 @@ protected:
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
private:
struct InitializeJvmForPerformanceManagerIfNeeded {
InitializeJvmForPerformanceManagerIfNeeded();
};
int mOSVersion;
ExternalStreamManagerAndroid& mExternalStreamManager;
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
@@ -90,6 +90,20 @@ public:
VkExtent2D extent = {0, 0};
};
struct ImageSyncData {
static constexpr uint32_t INVALID_IMAGE_INDEX = UINT32_MAX;
// The index of the next image as returned by vkAcquireNextImage or equivalent.
uint32_t imageIndex = INVALID_IMAGE_INDEX;
// Semaphore to be signaled once the image is available.
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
// A function called right before vkQueueSubmit. After this call, the image must be
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
};
VulkanPlatform();
~VulkanPlatform() override;
@@ -127,6 +141,12 @@ public:
* before recreating the swapchain. Default is true.
*/
bool flushAndWaitOnWindowResize = true;
/**
* Whether the swapchain image should be transitioned to a layout suitable for
* presentation. Default is true.
*/
bool transitionSwapChainImageLayoutForPresent = true;
};
/**
@@ -155,13 +175,10 @@ public:
* corresponding VkImage will be used as the output color attachment. The client should signal
* the `clientSignal` semaphore when the image is ready to be used by the backend.
* @param handle The handle returned by createSwapChain()
* @param clientSignal The semaphore that the client will signal to indicate that the backend
* may render into the image.
* @param index Pointer to memory that will be filled with the index that corresponding
* to an image in the `SwapChainBundle.colors` array.
* @param outImageSyncData The synchronization data used for image readiness
* @return Result of acquire
*/
virtual VkResult acquire(SwapChainPtr handle, VkSemaphore clientSignal, uint32_t* index);
virtual VkResult acquire(SwapChainPtr handle, ImageSyncData* outImageSyncData);
/**
* Present the image corresponding to `index` to the display. The client should wait on
+11 -1
View File
@@ -16,13 +16,23 @@
#include "private/backend/VirtualMachineEnv.h"
#include <utils/compiler.h>
#include <utils/debug.h>
#include <jni.h>
namespace filament {
JavaVM* VirtualMachineEnv::sVirtualMachine = nullptr;
// This is called when the library is loaded. We need this to get a reference to the global VM
/*
* This is typically called by filament_jni.so when it is loaded. If filament_jni.so is not used,
* then this must be called manually -- however, this is a problem because VirtualMachineEnv.h
* is currently private and part of backend.
* For now, we authorize this usage, but we will need to fix it; by making a proper public
* API for this.
*/
UTILS_PUBLIC
UTILS_NOINLINE
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
JNIEnv* env = nullptr;
+1 -1
View File
@@ -109,7 +109,7 @@ private:
NSUInteger headlessWidth = 0;
NSUInteger headlessHeight = 0;
CAMetalLayer* layer = nullptr;
std::mutex layerDrawableMutex;
std::shared_ptr<std::mutex> layerDrawableMutex;
MetalExternalImage externalImage;
SwapChainType type;
+13 -10
View File
@@ -73,6 +73,7 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow
: context(context),
depthStencilFormat(decideDepthStencilFormat(flags)),
layer(nativeWindow),
layerDrawableMutex(std::make_shared<std::mutex>()),
externalImage(context),
type(SwapChainType::CAMETALLAYER) {
@@ -179,7 +180,7 @@ id<MTLTexture> MetalSwapChain::acquireDrawable() {
// calling -nextDrawable, or when releasing the last known reference
// to any CAMetalDrawable returned from a previous -nextDrawable.
{
std::lock_guard<std::mutex> lock(layerDrawableMutex);
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
drawable = [layer nextDrawable];
}
@@ -188,8 +189,10 @@ id<MTLTexture> MetalSwapChain::acquireDrawable() {
}
void MetalSwapChain::releaseDrawable() {
std::lock_guard<std::mutex> lock(layerDrawableMutex);
drawable = nil;
if (drawable) {
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
drawable = nil;
}
}
id<MTLTexture> MetalSwapChain::acquireDepthTexture() {
@@ -265,7 +268,7 @@ public:
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
static PresentDrawableData* create(id<CAMetalDrawable> drawable,
std::mutex* drawableMutex, MetalDriver* driver) {
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver) {
assert_invariant(drawableMutex);
assert_invariant(driver);
return new PresentDrawableData(drawable, drawableMutex, driver);
@@ -287,22 +290,22 @@ public:
}
private:
PresentDrawableData(id<CAMetalDrawable> drawable, std::mutex* drawableMutex,
PresentDrawableData(id<CAMetalDrawable> drawable, std::shared_ptr<std::mutex> drawableMutex,
MetalDriver* driver)
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver) {}
static void cleanupAndDestroy(PresentDrawableData *that) {
{
if (that->mDrawable) {
std::lock_guard<std::mutex> lock(*(that->mDrawableMutex));
that->mDrawable = nil;
}
that->mDrawableMutex = nullptr;
that->mDrawableMutex.reset();
that->mDriver = nullptr;
delete that;
}
id<CAMetalDrawable> mDrawable;
std::mutex* mDrawableMutex = nullptr;
std::shared_ptr<std::mutex> mDrawableMutex;
MetalDriver* mDriver = nullptr;
};
@@ -320,7 +323,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
struct Callback {
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
std::mutex* drawableMutex, MetalDriver* driver)
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver)
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver)) {}
std::shared_ptr<FrameScheduledCallback> f;
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
@@ -337,7 +340,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// 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>(
frameScheduled.callback, drawable, &layerDrawableMutex, context.driver);
frameScheduled.callback, drawable, layerDrawableMutex, context.driver);
backend::CallbackHandler* handler = frameScheduled.handler;
MetalDriver* driver = context.driver;
@@ -19,6 +19,8 @@
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include <private/backend/VirtualMachineEnv.h>
#include "opengl/GLUtils.h"
#include "ExternalStreamManagerAndroid.h"
@@ -82,9 +84,23 @@ using EGLStream = Platform::Stream;
// ---------------------------------------------------------------------------------------------
PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmForPerformanceManagerIfNeeded() {
// PerformanceHintManager() needs the calling thread to be a Java thread; so we need
// to attach this thread to the JVM before we initialize PerformanceHintManager.
// This should be done in PerformanceHintManager(), but libutils doesn't have access to
// VirtualMachineEnv.
if (PerformanceHintManager::isSupported()) {
(void)VirtualMachineEnv::get().getEnvironment();
}
}
// ---------------------------------------------------------------------------------------------
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
: PlatformEGL(),
mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
mExternalStreamManager(ExternalStreamManagerAndroid::create()),
mInitializeJvmForPerformanceManagerIfNeeded(),
mPerformanceHintManager() {
char scratch[PROP_VALUE_MAX + 1];
int length = __system_property_get("ro.build.version.release", scratch);
@@ -37,7 +37,7 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
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
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< " layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
<< " layout=" << dst.getLayout() << utils::io::endl;
@@ -76,7 +76,7 @@ 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
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< " layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
<< " layout=" << dst.getLayout() << utils::io::endl;
@@ -178,7 +178,7 @@ VulkanCommandBuffer& VulkanCommands::get() {
// presenting the swap chain or waiting on a fence.
while (mAvailableBufferCount == 0) {
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "VulkanCommands has stalled. "
FVK_LOGI << "VulkanCommands has stalled. "
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
<< io::endl;
#endif
@@ -289,7 +289,7 @@ bool VulkanCommands::flush() {
};
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "Submitting cmdbuffer=" << cmdbuffer
FVK_LOGI << "Submitting cmdbuffer=" << cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
<< " signal=" << renderingFinished
<< " fence=" << currentbuf->fence->fence
@@ -305,7 +305,7 @@ bool VulkanCommands::flush() {
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
utils::slog.d << "Failed command buffer submission result: " << result << utils::io::endl;
FVK_LOGD << "Failed command buffer submission result: " << result << utils::io::endl;
}
#endif
assert_invariant(result == VK_SUCCESS);
@@ -320,7 +320,7 @@ VkSemaphore VulkanCommands::acquireFinishedSignal() {
VkSemaphore semaphore = mSubmissionSignal;
mSubmissionSignal = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
FVK_LOGI << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
#endif
return semaphore;
}
@@ -329,7 +329,7 @@ void VulkanCommands::injectDependency(VkSemaphore next) {
assert_invariant(mInjectedSignal == VK_NULL_HANDLE);
mInjectedSignal = next;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
FVK_LOGI << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
#endif
}
@@ -398,7 +398,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
// If the timestamp is not 0, then we are carrying over a marker across buffer submits.
// If it is 0, then this is a normal marker push and we should just print debug line as usual.
if (timestamp.time_since_epoch().count() == 0.0) {
utils::slog.d << "----> " << str << utils::io::endl;
FVK_LOGD << "----> " << str << utils::io::endl;
}
#endif
@@ -436,7 +436,7 @@ void VulkanCommands::popGroupMarker() {
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mGroupMarkers->pop();
@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
#define TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
#include <utils/Log.h>
#include <stdint.h>
// In debug builds, we enable validation layers and set up a debug callback.
@@ -70,6 +72,11 @@
// the currently active resources.
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
// Useful default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
@@ -133,6 +140,18 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#define FVK_HANDLE_ARENA_SIZE_IN_MB 8
#endif
#if FVK_ENABLED(FVK_DEBUG_FORCE_LOG_TO_I)
#define FVK_LOGI (utils::slog.i)
#define FVK_LOGD FVK_LOGI
#define FVK_LOGE FVK_LOGI
#define FVK_LOGW FVK_LOGI
#else
#define FVK_LOGE (utils::slog.e)
#define FVK_LOGW (utils::slog.w)
#define FVK_LOGD (utils::slog.d)
#define FVK_LOGI (utils::slog.i)
#endif
// All vkCreate* functions take an optional allocator. For now we select the default allocator by
// passing in a null pointer, and we highlight the argument by using the VKALLOC constant.
constexpr struct VkAllocationCallbacks* VKALLOC = nullptr;
@@ -100,7 +100,7 @@ std::tuple<uint32_t, uint32_t> VulkanTimestamps::getNextQuery() {
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
}
}
utils::slog.e << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
FVK_LOGE << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
return std::make_tuple(0, 1);
}
@@ -131,6 +131,10 @@ public:
return mDebugUtilsSupported;
}
inline bool isMultiviewEnabled() const noexcept {
return mMultiviewEnabled;
}
inline bool isClipDistanceSupported() const noexcept {
return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE;
}
@@ -141,6 +145,7 @@ private:
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mMultiviewEnabled = false;
VkFormatList mDepthStencilFormats;
VkFormatList mBlittableDepthStencilFormats;
+20 -17
View File
@@ -115,15 +115,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
utils::slog.e << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
} else {
// TODO: emit best practices warnings about aggressive pipeline barriers.
if (strstr(pMessage, "ALL_GRAPHICS_BIT") || strstr(pMessage, "ALL_COMMANDS_BIT")) {
return VK_FALSE;
}
utils::slog.w << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
}
utils::slog.e << utils::io::endl;
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
@@ -133,18 +133,18 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
void* pUserData) {
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
utils::slog.e << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
} else {
// TODO: emit best practices warnings about aggressive pipeline barriers.
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT")
|| strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
return VK_FALSE;
}
utils::slog.w << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
}
utils::slog.e << utils::io::endl;
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
@@ -292,7 +292,7 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
// VulkanRenderTarget : 312 few
// -- less than or equal to 312 bytes
utils::slog.d
FVK_LOGD
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
@@ -397,7 +397,10 @@ void VulkanDriver::collectGarbage() {
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginFrame");
// Do nothing.
FVK_SYSTRACE_END();
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
@@ -657,8 +660,8 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
utils::slog.w << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl;
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
@@ -669,7 +672,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
utils::slog.w << "sRGB swapchain requested, but Platform does not support it"
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
@@ -911,7 +914,7 @@ bool VulkanDriver::isStereoSupported() {
case backend::StereoscopicType::INSTANCED:
return mContext.isClipDistanceSupported();
case backend::StereoscopicType::MULTIVIEW:
// TODO: implement multiview feature in Vulkan.
return mContext.isMultiviewEnabled();
case backend::StereoscopicType::NONE:
return false;
}
@@ -1859,9 +1862,9 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
// 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) && 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;
FVK_LOGW << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
FVK_LOGW << " in material '" << program->name.c_str() << "'";
FVK_LOGW << " at binding point " << +binding << utils::io::endl;
#endif
texture = mEmptyTexture;
}
@@ -2011,7 +2014,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
assert_invariant(inRenderPass);
inRenderPass = false;
} else if (inRenderPass && OUTSIDE_COMMANDS.find(command) != OUTSIDE_COMMANDS.end()) {
utils::slog.e << command.data() << " issued inside a render pass." << utils::io::endl;
FVK_LOGE << command.data() << " issued inside a render pass." << utils::io::endl;
}
#endif
}
@@ -95,7 +95,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
}
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
utils::slog.d << "Creating framebuffer " << config.width << "x" << config.height << " "
FVK_LOGD << "Creating framebuffer " << config.width << "x" << config.height << " "
<< "for render pass " << config.renderPass << ", "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (config.depth ? 1 : 0) << ", "
@@ -310,7 +310,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
utils::slog.d << "Created render pass " << renderPass << " with "
FVK_LOGD << "Created render pass " << renderPass << " with "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (hasDepth ? 1 : 0) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
@@ -292,8 +292,8 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")" << utils::io::endl;
FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")" << utils::io::endl;
#endif
}
@@ -230,14 +230,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
PipelineCacheEntry cacheEntry = {};
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "vkCreateGraphicsPipelines with shaders = ("
<< shaderStages[0].module << ", " << shaderStages[1].module << ")" << utils::io::endl;
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"
<< utils::io::endl;
#endif
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle);
assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) {
utils::slog.e << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
FVK_LOGE << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
return nullptr;
}
@@ -252,7 +253,7 @@ void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
FVK_LOGE << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
}
@@ -167,9 +167,9 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
#if FVK_ENABLED(FVK_DEBUG_READ_PIXELS)
utils::slog.d << "readPixels created image=" << stagingImage
<< " to copy from image=" << srcTexture->getVkImage()
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
FVK_LOGD << "readPixels created image=" << stagingImage
<< " to copy from image=" << srcTexture->getVkImage()
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
#endif
VkMemoryRequirements memReqs;
@@ -185,7 +185,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
if (memoryTypeIndex >= VK_MAX_MEMORY_TYPES) {
memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
utils::slog.w
FVK_LOGW
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available"
<< utils::io::endl;
}
@@ -303,7 +303,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
// Fence hasn't been reached. Try waiting again.
if (status != VK_SUCCESS) {
utils::slog.e << "Failed to wait for readPixels fence" << utils::io::endl;
FVK_LOGE << "Failed to wait for readPixels fence" << utils::io::endl;
return;
}
@@ -321,7 +321,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
getComponentCount(srcFormat), srcPixels,
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
static_cast<int>(height), swizzle)) {
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
}
vkUnmapMemory(device, stagingMemory);
@@ -111,11 +111,11 @@ private:
#if DEBUG_RESOURCE_LEAKS
public:
void print() {
utils::slog.d << "Resource Allocator state (debug only)" << utils::io::endl;
FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl;
for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) {
utils::slog.d << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
}
utils::slog.d << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
}
private:
utils::FixedCapacityVector<size_t> mDebugOnlyResourceCount;
@@ -61,7 +61,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
if (result != VK_SUCCESS) {
utils::slog.e << "Allocation error: " << result << utils::io::endl;
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
}
#endif
+19 -34
View File
@@ -35,29 +35,13 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
mStagePool(stagePool),
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mCurrentImageReadyIndex(0),
mTransitionSwapChainImageLayoutForPresent(
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
mAcquired(false),
mIsFirstRenderPass(true) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
VkSemaphoreCreateInfo const createInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
// No need to wait on this semaphore before drawing when in Headless mode.
if (mHeadless) {
// Set all sempahores to VK_NULL_HANDLE
memset(mImageReady, 0, sizeof(mImageReady[0]) * IMAGE_READY_SEMAPHORE_COUNT);
} else {
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
VkResult result =
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, mImageReady + i);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Failed to create semaphore";
}
}
update();
}
@@ -68,11 +52,6 @@ VulkanSwapChain::~VulkanSwapChain() {
mCommands->wait();
mPlatform->destroy(swapChain);
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
if (mImageReady[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(mPlatform->getDevice(), mImageReady[i], VKALLOC);
}
}
}
void VulkanSwapChain::update() {
@@ -95,7 +74,7 @@ void VulkanSwapChain::update() {
}
void VulkanSwapChain::present() {
if (!mHeadless) {
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VkCommandBuffer const cmdbuf = mCommands->get().buffer();
VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
@@ -106,10 +85,15 @@ void VulkanSwapChain::present() {
};
mColors[mCurrentSwapIndex]->transitionLayout(cmdbuf, subresources, VulkanLayout::PRESENT);
}
mCommands->flush();
// We only present if it is not headless. No-op for headless (but note that we still need the
// flush() in the above line).
mCommands->flush();
// call the image ready wait function
if (mExplicitImageReadyWait != nullptr) {
mExplicitImageReadyWait(swapChain);
}
// We only present if it is not headless. No-op for headless.
if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
@@ -139,13 +123,14 @@ void VulkanSwapChain::acquire(bool& resized) {
update();
}
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
const VkSemaphore imageReady = mImageReady[mCurrentImageReadyIndex];
VkResult const result = mPlatform->acquire(swapChain, imageReady, &mCurrentSwapIndex);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain.";
if (imageReady != VK_NULL_HANDLE) {
mCommands->injectDependency(imageReady);
VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain.";
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
mCommands->injectDependency(imageSyncData.imageReadySemaphore);
}
mAcquired = true;
}
@@ -50,7 +50,10 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
void acquire(bool& reized);
inline VulkanTexture* getCurrentColor() const noexcept {
return mColors[mCurrentSwapIndex].get();
uint32_t const imageIndex = mCurrentSwapIndex;
FILAMENT_CHECK_PRECONDITION(
imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX);
return mColors[imageIndex].get();
}
inline VulkanTexture* getDepth() const noexcept {
@@ -80,15 +83,15 @@ private:
VulkanStagePool& mStagePool;
bool const mHeadless;
bool const mFlushAndWaitOnResize;
bool const mTransitionSwapChainImageLayoutForPresent;
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here.
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
std::unique_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
VkSemaphore mImageReady[IMAGE_READY_SEMAPHORE_COUNT];
uint32_t mCurrentImageReadyIndex;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
bool mIsFirstRenderPass;
};
@@ -113,7 +113,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, mVkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
utils::slog.w << "Texture usage is SAMPLEABLE but format " << mVkFormat << " is not "
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
#endif
@@ -163,7 +163,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
utils::slog.d << "vkCreateImage: "
FVK_LOGD << "vkCreateImage: "
<< "image = " << mTextureImage << ", "
<< "result = " << error << ", "
<< "handle = " << utils::io::hex << mTextureImage << utils::io::dec << ", "
@@ -450,7 +450,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres
}
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
utils::slog.d << "transition texture=" << mTextureImage
FVK_LOGD << "transition texture=" << mTextureImage
<< " (" << range.baseArrayLayer
<< "," << range.baseMipLevel << ")"
<< " count=(" << range.layerCount
@@ -539,7 +539,7 @@ void VulkanTexture::print() const {
layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) &&
level >= mPrimaryViewRange.baseMipLevel &&
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
utils::slog.d << "[" << mTextureImage << "]: (" << layer << "," << level
FVK_LOGD << "[" << mTextureImage << "]: (" << layer << "," << level
<< ")=" << getLayout(layer, level)
<< " primary=" << primary
<< utils::io::endl;
@@ -548,7 +548,7 @@ void VulkanTexture::print() const {
for (auto view: mCachedImageViews) {
auto& range = view.first.range;
utils::slog.d << "[" << mTextureImage << ", imageView=" << view.second << "]=>"
FVK_LOGD << "[" << mTextureImage << ", imageView=" << view.second << "]=>"
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
@@ -95,7 +95,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
.pNext = &driverProperties,
};
vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2);
utils::slog.i << "Vulkan device driver: " << driverProperties.driverName << " "
FVK_LOGI << "Vulkan device driver: " << driverProperties.driverName << " "
<< driverProperties.driverInfo << utils::io::endl;
}
@@ -128,7 +128,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
const FixedCapacityVector<VkPhysicalDevice> physicalDevices
= filament::backend::enumerate(vkEnumeratePhysicalDevices, instance);
utils::slog.i << "Selected physical device '" << deviceProperties.deviceName << "' from "
FVK_LOGI << "Selected physical device '" << deviceProperties.deviceName << "' from "
<< physicalDevices.size() << " physical devices. "
<< "(vendor " << utils::io::hex << vendorID << ", "
<< "device " << deviceID << ", "
@@ -142,15 +142,15 @@ void printDepthFormats(VkPhysicalDevice device) {
// Note that Vulkan is more constrained than OpenGL ES 3.1 in this area.
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
utils::slog.i << "Sampleable depth formats: ";
FVK_LOGI << "Sampleable depth formats: ";
for (VkFormat format : ALL_VK_FORMATS) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device, format, &props);
if ((props.optimalTilingFeatures & required) == required) {
utils::slog.i << format << " ";
FVK_LOGI << format << " ";
}
}
utils::slog.i << utils::io::endl;
FVK_LOGI << utils::io::endl;
}
#endif
@@ -176,6 +176,12 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
static_cast<char const*>(nullptr) /* pLayerName */);
for (auto const& extProps: availableExts) {
utils::CString name { extProps.extensionName };
// To workaround an Adreno bug where the extension name could be of 0 length.
if (name.size() == 0) {
continue;
}
if (setContains(TARGET_EXTS, name) || setContains(externallyRequiredExts, name)) {
exts.insert(name);
}
@@ -192,6 +198,7 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
VK_KHR_MAINTENANCE3_EXTENSION_NAME,
VK_KHR_MULTIVIEW_EXTENSION_NAME,
};
ExtensionSet exts;
// Identify supported physical device extensions
@@ -200,6 +207,12 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
static_cast<const char*>(nullptr) /* pLayerName */);
for (auto const& extension: extensions) {
utils::CString name { extension.extensionName };
// To workaround an Adreno bug where the extension name could be of 0 length.
if (name.size() == 0) {
continue;
}
if (setContains(TARGET_EXTS, name)) {
exts.insert(name);
}
@@ -229,11 +242,11 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
instanceCreateInfo.ppEnabledLayerNames = enabledLayers.data();
} else {
#if defined(__ANDROID__)
utils::slog.d << "Validation layers are not available; did you set jniLibs in your "
<< "gradle file?" << utils::io::endl;
FVK_LOGD << "Validation layers are not available; did you set jniLibs in your "
<< "gradle file?" << utils::io::endl;
#else
utils::slog.d << "Validation layer not available; did you install the Vulkan SDK?\n"
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
FVK_LOGD << "Validation layer not available; did you install the Vulkan SDK?\n"
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
#endif // __ANDROID__
}
@@ -322,6 +335,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
void* pNext = nullptr;
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
.pNext = nullptr,
@@ -329,9 +343,24 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
.mutableComparisonSamplers = VK_TRUE,
};
if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
deviceCreateInfo.pNext = &portability;
portability.pNext = pNext;
pNext = &portability;
}
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
.pNext = nullptr,
.multiview = VK_TRUE,
.multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE
};
if (setContains(deviceExtensions, VK_KHR_MULTIVIEW_EXTENSION_NAME)) {
multiview.pNext = pNext;
pNext = &multiview;
}
deviceCreateInfo.pNext = pNext;
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
@@ -406,8 +435,8 @@ inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return 1;
default:
utils::slog.w << "deviceTypeOrder: Unexpected deviceType: " << deviceType
<< utils::io::endl;
FVK_LOGW << "deviceTypeOrder: Unexpected deviceType: " << deviceType
<< utils::io::endl;
return -1;
}
}
@@ -689,6 +718,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
= pruneExtensions(mImpl->mPhysicalDevice, instExts, deviceExts);
instExts = prunedInstExts;
deviceExts = prunedDeviceExts;
if (driverConfig.stereoscopicType != StereoscopicType::MULTIVIEW) {
deviceExts.erase(VK_KHR_MULTIVIEW_EXTENSION_NAME);
}
}
mImpl->mDevice
@@ -704,6 +737,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// Store the extension support in the context
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
#ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions
@@ -738,8 +772,8 @@ VulkanPlatform::SwapChainBundle VulkanPlatform::getSwapChainBundle(SwapChainPtr
SWAPCHAIN_RET_FUNC(getSwapChainBundle, handle, )
}
VkResult VulkanPlatform::acquire(SwapChainPtr handle, VkSemaphore clientSignal, uint32_t* index) {
SWAPCHAIN_RET_FUNC(acquire, handle, clientSignal, index)
VkResult VulkanPlatform::acquire(SwapChainPtr handle, ImageSyncData* outImageSyncData) {
SWAPCHAIN_RET_FUNC(acquire, handle, outImageSyncData)
}
VkResult VulkanPlatform::present(SwapChainPtr handle, uint32_t index,
@@ -133,6 +133,7 @@ VulkanPlatformSurfaceSwapChain::VulkanPlatformSurfaceSwapChain(VulkanContext con
VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
vkDestroySwapchainKHR(mDevice, mSwapchain, VKALLOC);
vkDestroySurfaceKHR(mInstance, mSurface, VKALLOC);
destroy();
}
VkResult VulkanPlatformSurfaceSwapChain::create() {
@@ -153,7 +154,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
// the number of images, though there may be limits related to the total amount of memory used
// by presentable images."
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
utils::slog.e << "Swap chain does not support " << desiredImageCount << " images."
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
<< utils::io::endl;
desiredImageCount = caps.minImageCount;
}
@@ -246,7 +247,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
slog.i << "vkCreateSwapchain"
FVK_LOGI << "vkCreateSwapchain"
<< ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", "
<< surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", "
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
@@ -254,19 +255,29 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
<< "depth=" << mSwapChainBundle.depthFormat
<< io::endl;
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
VkResult result = vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr,
mImageReady + i);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Failed to create semaphore";
}
return result;
}
VkResult VulkanPlatformSurfaceSwapChain::acquire(VkSemaphore clientSignal, uint32_t* index) {
// This immediately retrieves the index of the next available presentable image, and
// asynchronously requests the GPU to trigger the "imageAvailable" semaphore.
VkResult result = vkAcquireNextImageKHR(mDevice, mSwapchain, UINT64_MAX, clientSignal,
VK_NULL_HANDLE, index);
VkResult VulkanPlatformSurfaceSwapChain::acquire(VulkanPlatform::ImageSyncData* outImageSyncData) {
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
outImageSyncData->imageReadySemaphore = mImageReady[mCurrentImageReadyIndex];
VkResult result = vkAcquireNextImageKHR(mDevice, mSwapchain, UINT64_MAX,
outImageSyncData->imageReadySemaphore, VK_NULL_HANDLE, &outImageSyncData->imageIndex);
// Users should be notified of a suboptimal surface, but it should not cause a cascade of
// log messages or a loop of re-creations.
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
slog.w << "Vulkan Driver: Suboptimal swap chain." << io::endl;
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << io::endl;
mSuboptimal = true;
}
return result;
@@ -288,7 +299,7 @@ VkResult VulkanPlatformSurfaceSwapChain::present(uint32_t index, VkSemaphore fin
// On Android Q and above, a suboptimal surface is always reported after screen rotation:
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
utils::slog.w << "Vulkan Driver: Suboptimal swap chain." << utils::io::endl;
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << utils::io::endl;
mSuboptimal = true;
}
return result;
@@ -312,6 +323,17 @@ VkResult VulkanPlatformSurfaceSwapChain::recreate() {
return create();
}
void VulkanPlatformSurfaceSwapChain::destroy() {
VulkanPlatformSwapChainImpl::destroy();
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
if (mImageReady[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(mDevice, mImageReady[i], VKALLOC);
mImageReady[i] = VK_NULL_HANDLE;
}
}
}
VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext const& context,
VkDevice device, VkQueue queue, VkExtent2D extent, uint64_t flags)
: VulkanPlatformSwapChainImpl(context, device, queue),
@@ -343,8 +365,8 @@ VkResult VulkanPlatformHeadlessSwapChain::present(uint32_t index, VkSemaphore fi
return VK_SUCCESS;
}
VkResult VulkanPlatformHeadlessSwapChain::acquire(VkSemaphore clientSignal, uint32_t* index) {
*index = mCurrentIndex;
VkResult VulkanPlatformHeadlessSwapChain::acquire(VulkanPlatform::ImageSyncData* outImageSyncData) {
outImageSyncData->imageIndex = mCurrentIndex;
mCurrentIndex = (mCurrentIndex + 1) % HEADLESS_SWAPCHAIN_SIZE;
return VK_SUCCESS;
}
@@ -42,7 +42,7 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain {
~VulkanPlatformSwapChainImpl();
// Non-virtual override-able method
VkResult acquire(VkSemaphore clientSignal, uint32_t* index) {
VkResult acquire(VulkanPlatform::ImageSyncData* outImageSyncData) {
PANIC_PRECONDITION("Should not be called");
return VK_ERROR_UNKNOWN;
}
@@ -86,7 +86,7 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl {
~VulkanPlatformSurfaceSwapChain();
// Non-virtual override
VkResult acquire(VkSemaphore clientSignal, uint32_t* index);
VkResult acquire(VulkanPlatform::ImageSyncData* outImageSyncData);
// Non-virtual override
VkResult present(uint32_t index, VkSemaphore finished);
@@ -97,7 +97,13 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl {
// Non-virtual override-able method
bool hasResized();
protected:
// Non-virtual override-able method
void destroy();
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
VkResult create();
VkInstance mInstance;
@@ -106,6 +112,8 @@ private:
VkSurfaceKHR mSurface;
VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
VkExtent2D const mFallbackExtent;
VkSemaphore mImageReady[IMAGE_READY_SEMAPHORE_COUNT];
uint32_t mCurrentImageReadyIndex;
bool mUsesRGB = false;
bool mHasStencil = false;
@@ -121,7 +129,7 @@ struct VulkanPlatformHeadlessSwapChain : public VulkanPlatformSwapChainImpl {
~VulkanPlatformHeadlessSwapChain();
// Non-virtual override
VkResult acquire(VkSemaphore clientSignal, uint32_t* index);
VkResult acquire(VulkanPlatform::ImageSyncData* outImageSyncData);
// Non-virtual override
VkResult present(uint32_t index, VkSemaphore finished);
+8
View File
@@ -930,6 +930,14 @@ public:
*/
void pumpMessageQueues();
/**
* Switch the command queue to unprotected mode. Protected mode can be activated via
* Renderer::beginFrame() using a protected SwapChain.
* @see Renderer
* @see SwapChain
*/
void unprotected() noexcept;
/**
* Returns the default Material.
*
@@ -140,6 +140,7 @@ public:
* @param values Array of values to set to the named parameter array.
* @param count Size of the array to set.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
* @see Material::hasParameter
*/
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
@@ -234,6 +235,32 @@ public:
setParameter(name, strlen(name), type, color);
}
/**
* Gets the value of a parameter by name.
*
* Note: Only supports non-texture parameters such as numeric and math types.
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*
* @see Material::hasParameter
*/
template<typename T>
T getParameter(const char* UTILS_NONNULL name, size_t nameLength) const;
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
inline T getParameter(StringLiteral name) const {
return getParameter<T>(name.data, name.size);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
inline T getParameter(const char* UTILS_NONNULL name) const {
return getParameter<T>(name, strlen(name));
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
+4
View File
@@ -329,6 +329,10 @@ void Engine::pumpMessageQueues() {
downcast(this)->pumpMessageQueues();
}
void Engine::unprotected() noexcept {
downcast(this)->unprotected();
}
void Engine::setAutomaticInstancingEnabled(bool enable) noexcept {
downcast(this)->setAutomaticInstancingEnabled(enable);
}
+29
View File
@@ -183,6 +183,35 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
// ------------------------------------------------------------------------------------------------
template<typename T>
T FMaterialInstance::getParameterImpl(std::string_view name) const {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getFieldOffset(name, 0);
assert_invariant(offset>=0);
return downcast(this)->getUniformBuffer().getUniform<T>(offset);
}
template<typename T>
T MaterialInstance::getParameter(const char* name, size_t nameLength) const {
return downcast(this)->getParameterImpl<T>({ name, nameLength });
}
// explicit template instantiation of our supported types
template UTILS_PUBLIC float MaterialInstance::getParameter<float> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC int32_t MaterialInstance::getParameter<int32_t> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC uint32_t MaterialInstance::getParameter<uint32_t>(const char* name, size_t nameLength) const;
template UTILS_PUBLIC int2 MaterialInstance::getParameter<int2> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC int3 MaterialInstance::getParameter<int3> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC int4 MaterialInstance::getParameter<int4> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC uint2 MaterialInstance::getParameter<uint2> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC uint3 MaterialInstance::getParameter<uint3> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC uint4 MaterialInstance::getParameter<uint4> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC float2 MaterialInstance::getParameter<float2> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC float3 MaterialInstance::getParameter<float3> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC float4 MaterialInstance::getParameter<float4> (const char* name, size_t nameLength) const;
template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const char* name, size_t nameLength) const;
// ------------------------------------------------------------------------------------------------
Material const* MaterialInstance::getMaterial() const noexcept {
return downcast(this)->getMaterial();
}
+9
View File
@@ -476,6 +476,8 @@ void FEngine::shutdown() {
destroy(mDefaultMaterial);
destroy(mUnprotectedDummySwapchain);
/*
* clean-up after the user -- we call terminate on each "leaked" object and clear each list.
*
@@ -1253,6 +1255,13 @@ void FEngine::resetBackendState() noexcept {
}
#endif
void FEngine::unprotected() noexcept {
if (UTILS_UNLIKELY(!mUnprotectedDummySwapchain)) {
mUnprotectedDummySwapchain = createSwapChain(1, 1, 0);
}
mUnprotectedDummySwapchain->makeCurrent(getDriverApi());
}
// ------------------------------------------------------------------------------------------------
Engine::Builder::Builder() noexcept = default;
+4
View File
@@ -406,6 +406,8 @@ public:
getDriver().purge();
}
void unprotected() noexcept;
void setAutomaticInstancingEnabled(bool enable) noexcept {
// instancing is not allowed at feature level 0
if (hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1)) {
@@ -539,6 +541,7 @@ private:
mutable FMaterial const* mDefaultMaterial = nullptr;
mutable FMaterial const* mSkyboxMaterial = nullptr;
mutable FSwapChain* mUnprotectedDummySwapchain = nullptr;
mutable FTexture* mDefaultIblTexture = nullptr;
mutable FIndirectLight* mDefaultIbl = nullptr;
@@ -595,6 +598,7 @@ public:
// capture to file. At the moment, only supported by the Metal backend.
bool doFrameCapture = false;
bool disable_buffer_padding = false;
bool disable_subpasses = false;
} renderer;
struct {
bool debug_froxel_visualization = false;
+1 -1
View File
@@ -661,7 +661,7 @@ bool FMaterial::hasPendingEdits() noexcept {
void FMaterial::latchPendingEdits() noexcept {
std::lock_guard const lock(mPendingEditsLock);
std::swap(mPendingEdits, mMaterialParser);
mMaterialParser.reset(mPendingEdits.release());
}
/**
+3
View File
@@ -231,6 +231,9 @@ private:
void setParameterImpl(std::string_view name,
FTexture const* texture, TextureSampler const& sampler);
template<typename T>
T getParameterImpl(std::string_view name) const;
// initialize the default instance
FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept;
+6 -2
View File
@@ -95,6 +95,8 @@ FRenderer::FRenderer(FEngine& engine) :
&engine.debug.renderer.doFrameCapture);
debugRegistry.registerProperty("d.renderer.disable_buffer_padding",
&engine.debug.renderer.disable_buffer_padding);
debugRegistry.registerProperty("d.renderer.disable_subpasses",
&engine.debug.renderer.disable_subpasses);
debugRegistry.registerProperty("d.shadowmap.display_shadow_texture",
&engine.debug.shadowmap.display_shadow_texture);
debugRegistry.registerProperty("d.shadowmap.display_shadow_texture_scale",
@@ -596,12 +598,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
hasColorGrading &&
msaaSampleCount <= 1 &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
driver.isFrameBufferFetchSupported(),
driver.isFrameBufferFetchSupported() &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaOptions.customResolve &&
msaaSampleCount > 1 &&
hasColorGrading &&
driver.isFrameBufferFetchMultiSampleSupported(),
driver.isFrameBufferFetchMultiSampleSupported() &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
.dithering = hasDithering,
+3 -7
View File
@@ -114,6 +114,7 @@ void FSkinningBuffer::setBones(FEngine& engine,
setBones(engine, mHandle, transforms, count, offset);
}
UTILS_UNUSED
static uint32_t packHalf2x16(half2 v) noexcept {
uint32_t lo = getBits(v[0]);
uint32_t hi = getBits(v[1]);
@@ -144,13 +145,8 @@ PerRenderableBoneUib::BoneData FSkinningBuffer::makeBone(mat4f transform) noexce
transform[1],
transform[2]
},
.cof = {
packHalf2x16({ cofactors[0].x, cofactors[0].y }),
packHalf2x16({ cofactors[0].z, cofactors[1].x }),
packHalf2x16({ cofactors[1].y, cofactors[1].z }),
packHalf2x16({ cofactors[2].x, cofactors[2].y })
// cofactor[2][2] is not stored because we don't have space for it
}
.cof0 = cofactors[0],
.cof1x = cofactors[1].x
};
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.52.3"
spec.version = "1.53.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.52.3/filament-v1.52.3-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.1/filament-v1.53.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 52;
static constexpr size_t MATERIAL_VERSION = 53;
/**
* Supported shading models
@@ -327,8 +327,9 @@ struct PerRenderableBoneUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
struct alignas(16) BoneData {
// bone transform, last row assumed [0,0,0,1]
math::float4 transform[3];
// 8 first cofactor matrix of transform's upper left
math::uint4 cof;
// 4 first cofactor matrix of transform's upper left
math::float3 cof0;
float cof1x;
};
BoneData bones[CONFIG_MAX_BONE_COUNT];
};
@@ -207,7 +207,17 @@ std::vector<BufferSlot> computeGeometries(cgltf_primitive const* prim, uint8_t c
size_t const requiredSize = byteCount * vertexCount;
auto gendata = (uint8_t*) malloc(requiredSize);
memset(gendata, 0xff, requiredSize);
if (vattr == filament::VertexAttribute::COLOR) {
// Assume white as the default if colors need to be generated.
float4* dataf = (float4*) gendata;
for (size_t i = 0; i < vertexCount; ++i) {
dataf[i] = float4(1.0, 1.0, 1.0, 1.0f);
}
} else {
memset(gendata, 0xff, requiredSize);
}
vslots.push_back({
.slot = slot,
.sizeInBytes = requiredSize,
@@ -55,9 +55,15 @@ public:
int reportActualWorkDuration(int64_t actualDurationNanos) noexcept;
};
// caveat: This must be called on a Java thread
PerformanceHintManager() noexcept;
~PerformanceHintManager() noexcept;
// Whether PerformanceHintManager APIs are supported (which doesn't mean PerformanceHintManager
// itself is, use isValid()).
static bool isSupported() noexcept;
// Whether PerformanceHintManager can be used.
bool isValid() const;
int64_t getPreferredUpdateRateNanos() const noexcept;
@@ -47,6 +47,13 @@ PerformanceHintManager::PerformanceHintManager() noexcept {
PerformanceHintManager::~PerformanceHintManager() noexcept = default;
bool PerformanceHintManager::isSupported() noexcept {
if (__builtin_available(android __ANDROID_API_T__, *)) {
return true;
}
return false;
}
bool PerformanceHintManager::isValid() const {
return mImpl->mManager != nullptr;
}
+2
View File
@@ -856,6 +856,8 @@ int main(int argc, char** argv) {
}
ImGui::Checkbox("Disable buffer padding",
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
ImGui::Checkbox("Disable sub-passes",
debug.getPropertyAddress<bool>("d.renderer.disable_subpasses"));
ImGui::Checkbox("Camera at origin",
debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
ImGui::Checkbox("Far Origin", &app.originIsFarAway);
+63 -6
View File
@@ -29,6 +29,10 @@
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <getopt/getopt.h>
#include <iostream>
#include "generated/resources/resources.h"
#include "generated/resources/monkey.h"
@@ -39,6 +43,7 @@ using namespace filament::math;
using Backend = Engine::Backend;
struct App {
Config config;
utils::Entity light;
Material* material;
MaterialInstance* materialInstance;
@@ -48,13 +53,65 @@ struct App {
static const char* IBL_FOLDER = "assets/ibl/lightroom_14b";
int main(int argc, char** argv) {
Config config;
config.title = "hellopbr";
config.iblDirectory = FilamentApp::getRootAssetsPath() + IBL_FOLDER;
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage(
"EXEC renders a simple PBR example\n"
"Usage:\n"
" EXEC [options]\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, or metal\n"
);
const std::string from("EXEC");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
}
std::cout << usage;
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "ha:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
if (arg == "opengl") {
app->config.backend = Engine::Backend::OPENGL;
} else if (arg == "vulkan") {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else {
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
exit(1);
}
break;
}
}
return optind;
}
int main(int argc, char** argv) {
App app;
auto setup = [config, &app](Engine* engine, View* view, Scene* scene) {
app.config.title = "hellopbr";
app.config.iblDirectory = FilamentApp::getRootAssetsPath() + IBL_FOLDER;
handleCommandLineArguments(argc, argv, &app);
auto setup = [config=app.config, &app](Engine* engine, View* view, Scene* scene) {
auto& tcm = engine->getTransformManager();
auto& rcm = engine->getRenderableManager();
auto& em = utils::EntityManager::get();
@@ -100,7 +157,7 @@ int main(int argc, char** argv) {
tcm.setTransform(ti, app.transform * mat4f::rotation(now, float3{ 0, 1, 0 }));
});
FilamentApp::get().run(config, setup, cleanup);
FilamentApp::get().run(app.config, setup, cleanup);
return 0;
}
+2 -1
View File
@@ -19,7 +19,8 @@ struct ShadowData {
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
struct BoneData {
highp mat3x4 transform; // bone transform is mat4x3 stored in row-major (last row [0,0,0,1])
highp uvec4 cof; // 8 first cofactor matrix of transform's upper left
highp float3 cof0; // 3 first cofactor matrix of transform's upper left
highp float cof1x; // 4th cofactor
};
#endif
+21 -14
View File
@@ -34,25 +34,32 @@ int getVertexIndex() {
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
#define MAX_SKINNING_BUFFER_WIDTH 2048u
vec3 mulBoneNormal(vec3 n, uint i) {
vec3 mulBoneNormal(vec3 n, uint j) {
highp mat3 cof;
// the first 8 elements of the cofactor matrix are stored as fp16
highp vec2 x0y0 = unpackHalf2x16(bonesUniforms.bones[i].cof[0]);
highp vec2 z0x1 = unpackHalf2x16(bonesUniforms.bones[i].cof[1]);
highp vec2 y1z1 = unpackHalf2x16(bonesUniforms.bones[i].cof[2]);
highp vec2 x2y2 = unpackHalf2x16(bonesUniforms.bones[i].cof[3]);
// the last element must be computed by hand
highp float a = bonesUniforms.bones[i].transform[0][0];
highp float b = bonesUniforms.bones[i].transform[0][1];
highp float d = bonesUniforms.bones[i].transform[1][0];
highp float e = bonesUniforms.bones[i].transform[1][1];
highp float a = bonesUniforms.bones[j].transform[0][0];
highp float b = bonesUniforms.bones[j].transform[0][1];
highp float c = bonesUniforms.bones[j].transform[0][2];
highp float d = bonesUniforms.bones[j].transform[1][0];
highp float e = bonesUniforms.bones[j].transform[1][1];
highp float f = bonesUniforms.bones[j].transform[1][2];
highp float g = bonesUniforms.bones[j].transform[2][0];
highp float h = bonesUniforms.bones[j].transform[2][1];
highp float i = bonesUniforms.bones[j].transform[2][2];
cof[0].xyz = vec3(x0y0, z0x1.x);
cof[1].xyz = vec3(z0x1.y, y1z1);
cof[2].xyz = vec3(x2y2, a * e - b * d);
cof[0] = bonesUniforms.bones[j].cof0;
cof[1].xyz = vec3(
bonesUniforms.bones[j].cof1x,
a * i - c * g,
c * d - a * f);
cof[2].xyz = vec3(
d * h - e * g,
b * g - a * h,
a * e - b * d);
return normalize(cof * n);
}
+2
View File
@@ -388,6 +388,8 @@ class_<Engine>("Engine")
return Engine::create();
}, allow_raw_pointers())
.function("unprotected", &Engine::unprotected)
.function("enableAccurateTranslations", &Engine::enableAccurateTranslations)
.function("setAutomaticInstancingEnabled", &Engine::setAutomaticInstancingEnabled)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.52.3",
"version": "1.53.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",