Compare commits

..
Author SHA1 Message Date
Serge Metral 531bfd6f6e Fix Android build. 2025-11-19 17:41:17 +00:00
Serge Metral db272484f9 Merge branch 'main' into serge/vulkan_pipeline_cache_json 2025-11-19 09:05:45 -08:00
Serge Metral 9dadf400d2 Adding render pass key. 2025-11-19 08:47:03 -08:00
Serge Metral 16c7a3c968 Adding render pass serialization. 2025-11-18 16:00:15 -08:00
Serge Metral 626f338cba First commit. 2025-11-18 07:06:46 -08:00
Serge Metral 4e05cd73c6 Merge branch 'google:main' into main 2025-11-04 11:20:17 -08:00
Serge Metral 49f9708312 Merge branch 'google:main' into main 2025-10-29 17:00:00 +00:00
Serge Metral 2791195a92 Merge branch 'google:main' into main 2025-10-22 10:50:16 -07:00
Serge Metral 65f78c06bb Merge branch 'google:main' into main 2025-10-21 11:32:23 -07:00
Serge Metral 71175ee410 Merge branch 'google:main' into main 2025-10-13 10:06:53 -07:00
Serge Metral c9b310450c Merge branch 'google:main' into main 2025-09-19 14:47:15 +00:00
Serge Metral 54c636d54c Merge branch 'google:main' into main 2025-09-04 03:15:27 +00:00
Serge Metral f503205be2 Merge branch 'google:main' into main 2025-08-29 17:25:01 +00:00
Serge Metral 02106110fe Merge branch 'google:main' into main 2025-08-25 16:31:27 +00:00
Serge Metral 5a0f78d915 Merge branch 'google:main' into main 2025-08-18 11:23:24 -07:00
Serge Metral f548c588d9 Merge branch 'google:main' into main 2025-08-13 18:17:53 +00:00
Serge Metral 487fb273f1 Merge branch 'google:main' into main 2025-08-12 15:25:09 +00:00
Serge Metral 111ac62d2f Merge branch 'google:main' into main 2025-08-07 11:24:34 -07:00
Serge Metral 2fac13e154 Merge branch 'google:main' into main 2025-07-31 20:45:26 -07:00
Serge Metral bcae04c9ef Merge branch 'google:main' into main 2025-05-12 14:15:56 -07:00
Serge Metral 55fc91b35f Merge branch 'google:main' into main 2025-05-08 18:25:41 +00:00
Serge Metral ff2827b226 Merge branch 'google:main' into main 2025-03-28 14:40:50 +00:00
Serge Metral 13e1d2a042 Merge branch 'google:main' into main 2025-03-21 23:32:55 +00:00
Serge Metral b09cbaf0db Merge branch 'google:main' into main 2025-03-18 19:47:10 +00:00
Serge Metral b570d489e3 Merge branch 'google:main' into main 2025-03-17 16:09:21 +00:00
Serge Metral 7f7daddbfa Merge branch 'google:main' into main 2025-03-14 17:28:06 +00:00
Serge Metral cfe6a423d3 Merge branch 'google:main' into main 2025-03-06 10:48:37 -08:00
Serge Metral ba99844f22 Merge branch 'google:main' into main 2025-03-05 16:00:28 -08:00
Serge Metral ea4c20ec2c Merge branch 'google:main' into main 2025-01-29 20:49:02 -08:00
Serge Metral 0a661c1f3c Merge branch 'google:main' into main 2025-01-27 08:55:26 -08:00
Serge Metral 021c4f6691 Merge branch 'google:main' into main 2025-01-13 12:07:40 -08:00
Serge Metral be6b6ae7be Merge branch 'google:main' into main 2024-12-18 20:56:10 -08:00
Serge Metral ad3a5b70b6 Merge branch 'google:main' into main 2024-12-03 10:05:08 -08:00
Serge Metral 80803f9693 Merge branch 'google:main' into main 2024-11-06 02:54:52 +00:00
Serge Metral 3b0e44c9a3 Merge branch 'google:main' into main 2024-11-05 22:20:25 +00:00
Serge Metral c5c2d42467 Merge branch 'google:main' into main 2024-11-05 05:25:51 +00:00
Serge Metral ff145f6ccf Merge branch 'google:main' into main 2024-10-31 15:55:28 +00:00
Serge Metral 48bb10a4b5 Merge branch 'google:main' into main 2024-10-30 04:14:21 +00:00
Serge Metral 01a2480175 Merge branch 'google:main' into main 2024-10-26 05:37:19 +00:00
Serge Metral 9e921936c6 Merge branch 'google:main' into main 2024-10-18 13:29:27 +00:00
Serge Metral 8bc4d3a9df Merge branch 'google:main' into main 2024-10-09 17:16:55 +00:00
Serge Metral 0fdf1cac1d Merge branch 'google:main' into main 2024-10-03 09:10:04 -07:00
Serge Metral 387942cc39 Merge branch 'google:main' into main 2024-09-20 20:48:06 -07:00
Serge Metral c81cedd887 Merge branch 'google:main' into main 2024-09-04 08:36:42 -07:00
Serge Metral abe8a49d17 Merge branch 'google:main' into main 2024-06-26 17:26:00 -07:00
Serge Metral 96cba96058 Merge branch 'google:main' into main 2024-06-18 09:38:05 -07:00
Serge Metral 4fccd957db Merge branch 'google:main' into main 2024-06-11 16:54:22 -07:00
Powei Feng d49554d5ce Merge branch 'main' into main 2024-06-05 14:36:17 -07:00
Powei Feng 867161773e Merge branch 'main' into main 2024-06-05 11:53:35 -07:00
Serge Metral e2658506f6 Merge branch 'main' into main 2024-06-04 15:39:46 -07:00
Serge Metral 6218164f79 Adding the begin frame message for later xtrace post processing. 2024-06-04 12:35:17 -07:00
269 changed files with 9095 additions and 20764 deletions
+2
View File
@@ -6,3 +6,5 @@
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.67.1'
implementation 'com.google.android.filament:filament-android:1.67.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.67.1'
pod 'Filament', '~> 1.67.0'
```
## Documentation
-6
View File
@@ -7,12 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.68.0
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
- Metal: report GPU errors to the platform via `debugUpdateStat` (b/431665753).
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
## v1.67.1
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
@@ -22,7 +22,6 @@ object Utils {
/**
* Initializes the utils JNI layer. Must be called before using any utils functionality.
*/
@JvmStatic
fun init() {
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
Filament.init()
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.67.1
VERSION_NAME=1.67.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -17,20 +17,12 @@ filamentTools {
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyDamagedHelmetGltf', Copy) {
from file("../../../third_party/models/DamagedHelmet/DamagedHelmet.glb")
into file("src/main/assets/models")
rename {String fileName -> "helmet.glb"}
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyBusterGltf', Copy) {
tasks.register('copyMesh', Copy) {
from "../../../third_party/models/BusterDrone"
into "src/main/assets/models"
}
preBuild.dependsOn copyDamagedHelmetGltf
preBuild.dependsOn copyBusterGltf
preBuild.dependsOn copyMesh
clean.doFirst {
delete "src/main/assets"
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.67.1'
implementation 'com.google.android.filament:filament-android:1.66.2'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.67.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.66.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1788 -1819
View File
File diff suppressed because it is too large Load Diff
+1174 -1201
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+8 -2
View File
@@ -214,6 +214,14 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanMemory.h
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPipelineStateSerializer.cpp
src/vulkan/VulkanPipelineStateSerializer.h
src/vulkan/VulkanSamplerStateSerializer.h
src/vulkan/VulkanSamplerStateSerializer.cpp
src/vulkan/VulkanRenderPassStateSerializer.h
src/vulkan/VulkanRenderPassStateSerializer.cpp
src/vulkan/VulkanDescriptorSetLayoutSerializer.h
src/vulkan/VulkanDescriptorSetLayoutSerializer.cpp
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/VulkanQueryManager.cpp
@@ -316,8 +324,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPURenderPrimitive.h
src/webgpu/WebGPURenderTarget.cpp
src/webgpu/WebGPURenderTarget.h
src/webgpu/WebGPUStagePool.cpp
src/webgpu/WebGPUStagePool.h
src/webgpu/WebGPUStrings.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
+7 -37
View File
@@ -19,17 +19,13 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORM_H
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Mutex.h>
#include <stddef.h>
#include <stdint.h>
#include <atomic>
#include <memory>
#include <mutex>
namespace filament::backend {
@@ -512,22 +508,13 @@ public:
// --------------------------------------------------------------------------------------------
// Debugging APIs
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key,
uint64_t intValue, utils::CString stringValue)>;
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
/**
* Sets the callback function that the backend can use to update backend-specific statistics
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
* thread.
*
* The callback signature is (key, intValue, stringValue). Note that for any given call,
* only one of the value parameters (intValue or stringValue) will be meaningful, depending on
* the specific key.
*
* IMPORTANT_NOTE: because the callback is called on the driver thread, only quick, non-blocking
* work should be done inside it. Furthermore, no graphics API calls (such as GL calls) should
* be made, which could interfere with Filament's driver state.
*
* @param debugUpdateStat an Invocable that updates debug statistics
*/
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
@@ -546,32 +533,15 @@ public:
* This function is guaranteed to be called only on a single thread, the Filament driver
* thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param intValue the updated integer value of key (the string value passed to the
* callback will be empty)
* @param key a null-terminated C-string with the key of the debug statistic
* @param value the updated value of key
*/
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue);
/**
* To track backend-specific statistics, the backend implementation can call the
* application-provided callback function debugUpdateStatFunc to associate or update a value
* with a given key. It is possible for this function to be called multiple times with the
* same key, in which case newer values should overwrite older values.
*
* This function is guaranteed to be called only on a single thread, the Filament driver
* thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param stringValue the updated string value of key (the integer value passed to the
* callback will be 0)
*/
void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue);
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
private:
std::shared_ptr<InsertBlobFunc> mInsertBlob;
std::shared_ptr<RetrieveBlobFunc> mRetrieveBlob;
std::shared_ptr<DebugUpdateStatFunc> mDebugUpdateStat;
mutable utils::Mutex mMutex;
InsertBlobFunc mInsertBlob;
RetrieveBlobFunc mRetrieveBlob;
DebugUpdateStatFunc mDebugUpdateStat;
};
} // namespace filament
@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#include "AndroidSwapChainHelper.h"
#include "AndroidFrameCallback.h"
#include "AndroidNdk.h"
#include <backend/AcquiredImage.h>
@@ -169,10 +171,19 @@ protected:
SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
private:
struct SwapChainEGLAndroid;
struct AndroidDetails;
struct SwapChainEGLAndroid : public SwapChainEGL {
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
void* nativeWindow, uint64_t flags);
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGLAndroid& platform);
bool setPresentFrameId(uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
AndroidSwapChainHelper mImpl{};
};
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
@@ -192,14 +203,17 @@ private:
int mOSVersion;
ExternalStreamManagerAndroid& mExternalStreamManager;
AndroidDetails& mAndroidDetails;
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
using clock = std::chrono::high_resolution_clock;
clock::time_point mStartTimeOfActualWork;
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
AndroidProducerThrottling mProducerThrottling;
bool mAssertNativeWindowIsValid = false;
AndroidFrameCallback mAndroidFrameCallback;
};
} // namespace filament::backend
@@ -444,11 +444,6 @@ protected:
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
/**
* Query if transient attachments are supported by the backend.
*/
bool isTransientAttachmentSupported() const noexcept;
private:
friend struct VulkanPlatformPrivate;
};
@@ -17,13 +17,12 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include "AndroidFrameCallback.h"
#include "AndroidNdk.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h>
#include <android/hardware_buffer.h>
namespace filament::backend {
@@ -92,8 +91,6 @@ protected:
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct AndroidDetails;
struct ExternalImageVulkanAndroid : public ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
@@ -102,7 +99,7 @@ private:
~ExternalImageVulkanAndroid() override;
};
AndroidDetails& mAndroidDetails;
AndroidFrameCallback mAndroidFrameCallback;
int mOSVersion{};
};
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include <backend/platforms/AndroidNdk.h>
#include "AndroidNdk.h"
#include <android/hardware_buffer.h>
+11 -42
View File
@@ -139,73 +139,42 @@ bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*
}
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex);
mInsertBlob = std::make_shared<InsertBlobFunc>(std::move(insertBlob));
mRetrieveBlob = std::make_shared<RetrieveBlobFunc>(std::move(retrieveBlob));
mInsertBlob = std::move(insertBlob);
mRetrieveBlob = std::move(retrieveBlob);
}
bool Platform::hasInsertBlobFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex);
return bool(mInsertBlob);
}
bool Platform::hasRetrieveBlobFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex);
return bool(mRetrieveBlob);
}
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {
std::shared_ptr<InsertBlobFunc> callback;
{
std::unique_lock<decltype(mMutex)> lock(mMutex);
callback = mInsertBlob;
}
if (callback) {
(*callback)(key, keySize, value, valueSize);
if (mInsertBlob) {
mInsertBlob(key, keySize, value, valueSize);
}
}
size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size_t valueSize) {
std::shared_ptr<RetrieveBlobFunc> callback;
{
std::unique_lock<decltype(mMutex)> lock(mMutex);
callback = mRetrieveBlob;
}
if (callback) {
return (*callback)(key, keySize, value, valueSize);
if (mRetrieveBlob) {
return mRetrieveBlob(key, keySize, value, valueSize);
}
return 0;
}
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex);
mDebugUpdateStat = std::make_shared<DebugUpdateStatFunc>(std::move(debugUpdateStat));
mDebugUpdateStat = std::move(debugUpdateStat);
}
bool Platform::hasDebugUpdateStatFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex);
return mDebugUpdateStat != nullptr;
return bool(mDebugUpdateStat);
}
void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
std::shared_ptr<DebugUpdateStatFunc> callback;
{
std::unique_lock<decltype(mMutex)> lock(mMutex);
callback = mDebugUpdateStat;
}
if (callback) {
(*callback)(key, intValue, "");
}
}
void Platform::debugUpdateStat(const char* key, utils::CString stringValue) {
std::shared_ptr<DebugUpdateStatFunc> callback;
{
std::unique_lock<decltype(mMutex)> lock(mMutex);
callback = mDebugUpdateStat;
}
if (callback) {
(*callback)(key, 0, stringValue);
void Platform::debugUpdateStat(const char* key, uint64_t value) {
if (mDebugUpdateStat) {
mDebugUpdateStat(key, value);
}
}
@@ -17,7 +17,6 @@
#ifndef TNT_METALCONTEXT_H
#define TNT_METALCONTEXT_H
#include "MetalErrorQueue.h"
#include "MetalResourceTracker.h"
#include "MetalShaderCompiler.h"
#include "MetalState.h"
@@ -130,7 +129,6 @@ struct MetalContext {
id<MTLCommandBuffer> pendingCommandBuffer = nil;
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nil;
uint32_t currentFrame = 0;
MetalErrorQueue commandBufferErrors;
std::atomic<bool> memorylessLimitsReached = false;
@@ -153,7 +153,6 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
logMTLCommandBufferError(errorCode);
context->commandBufferErrors.push(buffer.error);
}
}];
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
-12
View File
@@ -254,18 +254,6 @@ MetalDriver::~MetalDriver() noexcept {
void MetalDriver::tick(int) {
executeTickOps();
executeDeferredOps();
// Notify platform of GPU errors.
auto& platform = mPlatform;
if (UTILS_UNLIKELY(!mContext->commandBufferErrors.isEmpty())) {
mContext->commandBufferErrors.flush([&platform](NSError* error) {
if (UTILS_VERY_UNLIKELY(!error)) {
return;
}
const utils::CString errorString(error.localizedDescription.UTF8String);
platform.debugUpdateStat("filament.metal.command_buffer_error", errorString);
});
}
}
void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2025 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_DRIVER_METALERRORQUEUE_H
#define TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
#import <Foundation/Foundation.h>
#include <utils/compiler.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <vector>
class MetalErrorQueue {
public:
bool isEmpty() const {
return !mHasErrors.load(std::memory_order_relaxed);
}
void push(NSError* error) {
std::lock_guard<std::mutex> lock(mMutex);
mErrors.push_back(error);
mHasErrors.store(true, std::memory_order_relaxed);
}
void flush(const std::function<void(NSError*)>& callback) {
if (UTILS_LIKELY(isEmpty())) {
return;
}
std::vector<NSError*> errors;
{
std::lock_guard<std::mutex> lock(mMutex);
std::swap(mErrors, errors);
mHasErrors.store(false, std::memory_order_relaxed);
}
for (const auto& error: errors) {
callback(error);
}
}
private:
std::vector<NSError*> mErrors;
std::mutex mMutex;
// Optimization to avoid locking the mutex at each call to flush.
std::atomic<bool> mHasErrors;
};
#endif // TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
@@ -28,13 +28,14 @@
#include <private/backend/VirtualMachineEnv.h>
#include "AndroidNativeWindow.h"
#include "AndroidFrameCallback.h"
#include "AndroidSwapChainHelper.h"
#include "ExternalStreamManagerAndroid.h"
#include <android/api-level.h>
#include <android/native_window.h>
#include <android/hardware_buffer.h>
#include <android/choreographer.h>
#include <android/looper.h>
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
@@ -98,25 +99,6 @@ using namespace glext;
// ---------------------------------------------------------------------------------------------
struct PlatformEGLAndroid::SwapChainEGLAndroid : public SwapChainEGL {
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
void* nativeWindow, uint64_t flags);
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGLAndroid& platform);
bool setPresentFrameId(uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
AndroidSwapChainHelper mImpl{};
};
struct PlatformEGLAndroid::AndroidDetails {
AndroidProducerThrottling producerThrottling;
AndroidFrameCallback androidFrameCallback;
};
// ---------------------------------------------------------------------------------------------
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.
@@ -130,20 +112,17 @@ PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmFor
// ---------------------------------------------------------------------------------------------
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
: mExternalStreamManager(ExternalStreamManagerAndroid::create()),
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
: mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
mOSVersion = android_get_device_api_level();
if (mOSVersion < 0) {
mOSVersion = __ANDROID_API_FUTURE__;
}
}
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept {
delete &mAndroidDetails;
}
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
void PlatformEGLAndroid::terminate() noexcept {
mAndroidDetails.androidFrameCallback.terminate();
mAndroidFrameCallback.terminate();
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
PlatformEGL::terminate();
}
@@ -261,7 +240,7 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
mAndroidDetails.androidFrameCallback.init();
mAndroidFrameCallback.init();
return driver;
}
@@ -285,7 +264,7 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
}
AndroidFrameCallback::Timeline const preferredTimeline{
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
mAndroidFrameCallback.getPreferredTimeline() };
outCompositorTiming->frameTime = preferredTimeline.frameTime;
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
@@ -696,12 +675,12 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage const sou
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
return mAndroidDetails.producerThrottling.isSupported();
return mProducerThrottling.isSupported();
}
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
EGLNativeWindowType const nativeWindow, bool const enabled) const {
return mAndroidDetails.producerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
return mProducerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
}
// ---------------------------------------------------------------------------------------------
@@ -214,10 +214,4 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
// destroying any unused pipeline object.
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
// Indicates if the backend must be setup to allow doing a RenderDoc capture.
//
// If this is true, the features not supported by RenderDoc must be disabled, otherwise
// when using RenderDoc the application will crash or will fail to do a capture.
constexpr static const int FVK_RENDERDOC_CAPTURE_MODE = false;
#endif
@@ -154,10 +154,6 @@ public:
return mStagingBufferBypassEnabled;
}
inline bool pipelineCreationFeedbackSupported() const noexcept {
return mPipelineCreationFeedbackSupported;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -185,7 +181,6 @@ private:
bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false;
bool mStagingBufferBypassEnabled = false;
bool mPipelineCreationFeedbackSupported = false;
fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats;
@@ -15,6 +15,7 @@
*/
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
#include "VulkanHandles.h"
@@ -100,22 +101,32 @@ uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
return count;
}
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers, VulkanSamplerCache* cache) {
size_t const size = samplers.size();
if (size == 0) {
return 0;
} else if (size == 1) {
return (uint64_t) samplers[0];
}
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
utils::FixedCapacityVector<uint32_t> samplerHash;
if (size > 0) {
samplerHash.reserve(MAX_SAMPLER_COUNT);
for (auto sampler : samplers) {
samplerHash.push_back(cache->getKey(sampler));
}
}
if (size == 1) {
return (uint64_t) samplerHash[0];
}
return utils::hash::murmur3((uint32_t*) samplerHash.data(), samplerHash.size() * 2, 0);
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
fvkmemory::ResourceManager* resourceManager, VulkanSamplerCache* cache)
: mDevice(device),
mResourceManager(resourceManager) {}
mResourceManager(resourceManager), mSamplerCache(cache) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
@@ -131,7 +142,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers, mSamplerCache),
};
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second;
@@ -153,9 +164,20 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
.bindingCount = count,
.pBindings = toBind,
};
LayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
utils::FixedCapacityVector<uint32_t> immutableSamplersHashes;
immutableSamplersHashes.reserve(MAX_SAMPLER_COUNT);
for (auto sampler: immutableSamplers) {
immutableSamplersHashes.push_back(mSamplerCache->getKey(sampler));
}
VulkanDescriptorSetLayoutSerializer setSer(dlinfo, immutableSamplersHashes, hashedKey);
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
mLayoutToKey[vklayout] = hashedKey;
return vklayout;
}
@@ -19,6 +19,8 @@
#include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
@@ -35,7 +37,8 @@ namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager,
VulkanSamplerCache* cache);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
@@ -48,10 +51,19 @@ public:
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
uint32_t getKey(VkDescriptorSetLayout layout) {
uint32_t key = 0;
auto iter = mLayoutToKey.find(layout);
if (iter != mLayoutToKey.end()) {
key = iter->second;
}
return key;
}
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
VulkanSamplerCache* mSamplerCache;
struct LayoutKey {
// this describes the layout using bitset.
@@ -69,6 +81,7 @@ private:
};
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
std::map<VkDescriptorSetLayout, uint32_t> mLayoutToKey;
};
} // namespace filament::backend
@@ -0,0 +1,87 @@
#include "VulkanDescriptorSetLayoutSerializer.h"
#include <fstream>
#include <sstream>
#include <utils/Hash.h>
namespace filament::backend {
VulkanDescriptorSetLayoutSerializer::VulkanDescriptorSetLayoutSerializer(
const VkDescriptorSetLayoutCreateInfo& info,
utils::FixedCapacityVector<uint32_t> immutableSamplers, uint32_t hash) {
std::stringstream filename;
filename << "descriptor_set_layout_" << hash << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "[" << std::endl;
for (uint32_t i = 0; i < info.bindingCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"binding\":" << info.pBindings[i].binding << "," << std::endl;
buffer << "\"descriptor_type\":" << info.pBindings[i].descriptorType << "," << std::endl;
buffer << "\"stage_flags\":" << info.pBindings[i].stageFlags;
if (immutableSamplers.size()) {
buffer << "," << std::endl;
buffer << "\"immutable_samplers\":[" << std::endl;
for (uint32_t i = 0; i < immutableSamplers.size(); ++i) {
buffer << immutableSamplers[i];
if (i < immutableSamplers.size() - 1) {
buffer << ",";
}
}
buffer << "]";
}
buffer << std::endl;
buffer << "}";
if (i < info.bindingCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
file << buffer.str();
}
file.close();
}
VulkanPipelineLayoutSerializer::VulkanPipelineLayoutSerializer(
const VkPipelineLayoutCreateInfo& info, VulkanDescriptorSetLayoutCache* cache,
uint32_t hash) {
std::stringstream filename;
filename << "pipeline_layout_" << hash << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.flags << "," << std::endl;
buffer << "\"layouts\":[" << std::endl;
for (uint32_t i = 0; i < info.setLayoutCount; ++i) {
buffer << cache->getKey(info.pSetLayouts[i]);
if (i < info.setLayoutCount - 1) {
buffer << ",";
}
}
buffer << "]," << std::endl;
buffer << "\"push_constants\":[" << std::endl;
for (uint32_t i = 0; i < info.pushConstantRangeCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"stage_flags\":" << info.pPushConstantRanges[i].stageFlags << "," << std::endl;
buffer << "\"offset\":" << info.pPushConstantRanges[i].offset << ","
<< std::endl;
buffer << "\"size\":" << info.pPushConstantRanges[i].size
<< std::endl;
buffer << "}" << std::endl;
if (i < info.pushConstantRangeCount - 1) {
buffer << ",";
}
}
buffer << "]" << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
} // namespace filament::backend
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2022 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_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
#include "VulkanPipelineLayoutCache.h"
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <sstream>
#include <vector>
namespace filament::backend {
class VulkanDescriptorSetLayoutSerializer {
public:
VulkanDescriptorSetLayoutSerializer(VulkanDescriptorSetLayoutSerializer const&) = delete;
VulkanDescriptorSetLayoutSerializer& operator=(
VulkanDescriptorSetLayoutSerializer const&) = delete;
VulkanDescriptorSetLayoutSerializer(const VkDescriptorSetLayoutCreateInfo& info,
utils::FixedCapacityVector<uint32_t> immutableSamplers,
uint32_t hash);
};
class VulkanPipelineLayoutSerializer {
public:
VulkanPipelineLayoutSerializer(VulkanPipelineLayoutSerializer const&) = delete;
VulkanPipelineLayoutSerializer& operator=(VulkanPipelineLayoutSerializer const&) = delete;
VulkanPipelineLayoutSerializer(const VkPipelineLayoutCreateInfo& layout,
VulkanDescriptorSetLayoutCache* cache, uint32_t hash);
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
+16 -6
View File
@@ -230,16 +230,16 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
mPipelineLayoutCache(mPlatform->getDevice()),
mPipelineCache(mPlatform->getDevice(), mContext),
mPipelineCache(mPlatform->getDevice()),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice(), &mYcbcrConversionCache),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager, &mSamplerCache),
mPipelineLayoutCache(mPlatform->getDevice(), &mDescriptorSetLayoutCache),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
@@ -921,6 +921,14 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
}
}
#if defined(__ANDROID__)
// on Android, disable producer throttling
if (mProducerThrottling.isSupported()) {
mProducerThrottling.setProducerThrottlingEnabled(
static_cast<ANativeWindow*>(nativeWindow), false);
}
#endif
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
swapChain.inc();
@@ -1658,7 +1666,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.subpassMask = uint8_t(params.subpassMask);
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, 0);
mPipelineCache.bindRenderPass(renderPass, mFramebufferCache.getRenderPassKey(renderPass), 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
@@ -1777,6 +1785,7 @@ void VulkanDriver::nextSubpass(int) {
VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
mFramebufferCache.getRenderPassKey(mCurrentRenderPass.renderPass),
++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) {
@@ -2152,7 +2161,8 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask;
mPipelineCache.bindLayout(pipelineLayout);
auto key = mPipelineLayoutCache.getKey(pipelineLayout);
mPipelineCache.bindLayout(pipelineLayout, key);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
}
+8 -1
View File
@@ -48,6 +48,10 @@
#include <utils/Allocator.h>
#include <utils/compiler.h>
#if defined(__ANDROID__)
#include "AndroidNativeWindow.h"
#endif
namespace filament::backend {
class VulkanPlatform;
@@ -144,7 +148,6 @@ private:
VulkanSemaphoreManager mSemaphoreManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanBufferCache mBufferCache;
@@ -154,6 +157,7 @@ private:
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager;
@@ -202,6 +206,9 @@ private:
bool const mIsSRGBSwapChainSupported;
bool const mIsMSAASwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
#if defined(__ANDROID__)
AndroidProducerThrottling mProducerThrottling;
#endif
};
} // namespace filament::backend
@@ -15,6 +15,7 @@
*/
#include "VulkanFboCache.h"
#include "VulkanRenderPassStateSerializer.h"
#include "VulkanConstants.h"
#include "vulkan/utils/Image.h"
@@ -69,6 +70,15 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
uint32_t VulkanFboCache::getRenderPassKey(VkRenderPass pass) noexcept {
uint32_t key = 0;
auto iter = mRenderPassToKey.find(pass);
if (iter != mRenderPassToKey.end()) {
key = iter->second;
}
return key;
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
@@ -117,6 +127,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
mFramebufferCache[config] = {framebuffer, mCurrentTime};
return framebuffer;
}
@@ -326,6 +337,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error;
RenderPassHash hashFunc;
uint32_t key = hashFunc(config);
VulkanRenderPassStateSerializer passSer(renderPassInfo, key);
mRenderPassToKey[renderPass] = key;
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
@@ -24,6 +24,7 @@
#include <backend/TargetBufferInfo.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -97,6 +98,8 @@ public:
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
uint32_t getRenderPassKey(VkRenderPass pass) noexcept;
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
@@ -118,6 +121,7 @@ private:
FboMap mFramebufferCache;
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
std::map<VkRenderPass, uint32_t> mRenderPassToKey;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
};
@@ -33,6 +33,7 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h>
#include <fstream>
using namespace bluevk;
@@ -179,6 +180,23 @@ VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanT
};
}
static inline uint64_t serializeShaderModule(utils::CString progName, VkShaderModuleCreateInfo info,
VkShaderStageFlagBits stage) {
std::stringstream filename;
filename << progName.c_str() << "_" << stage << "_";
constexpr uint32_t wordReadCount = 8;
uint64_t key = utils::hash::murmur3(info.pCode, wordReadCount, 0);
key |= uint64_t(utils::hash::murmur3(info.pCode + (wordReadCount / 2), wordReadCount, 0)) << 32;
filename << key << ".bin";
std::ofstream file(filename.str());
if (file.is_open()) {
file.write(reinterpret_cast<const char*>(info.pCode), info.codeSize);
}
file.close();
return key;
}
} // anonymous namespace
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
@@ -280,6 +298,12 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
.codeSize = dataSize,
.pCode = data,
};
if (i == 0) {
mVertexShaderHash = serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_VERTEX_BIT);
} else if (i == 1) {
mFragmentShaderHash =
serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_FRAGMENT_BIT);
}
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create shader module."
@@ -65,6 +65,8 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorSetLayoutHashArray = std::array<uint64_t,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
// The bitmask representation of a set layout.
struct Bitmask {
@@ -280,6 +282,10 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
inline uint64_t getVertexShaderHash() { return mVertexShaderHash;
}
inline uint64_t getFragmentShaderHash() { return mFragmentShaderHash; }
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
@@ -295,6 +301,8 @@ private:
};
PipelineInfo* mInfo;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
VkDevice mDevice = VK_NULL_HANDLE;
};
@@ -15,6 +15,7 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
@@ -34,49 +35,17 @@ using namespace bluevk;
namespace filament::backend {
namespace {
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedbackInfo) {
VkPipelineCreationFeedback const& pipelineInfo = *feedbackInfo.pPipelineCreationFeedback;
if (!(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
return;
}
bool const isCacheHit =
(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
FVK_LOGD << "Pipeline build stats - Cache hit: " << isCacheHit
<< ", Time: " << pipelineInfo.duration / 1000000.0 << "ms";
for (uint32_t i = 0; i < feedbackInfo.pipelineStageCreationFeedbackCount; ++i) {
VkPipelineCreationFeedback const& stageInfo = feedbackInfo.pPipelineStageCreationFeedbacks[i];
if (!(stageInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
continue;
}
bool const isVertexShader = (i == 0);
bool const isCacheHit = (stageInfo.flags &
VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
FVK_LOGD << (isVertexShader ? "Vertex" : "Fragment")
<< " shader build stats - Cache hit: " << isCacheHit
<< ", Time: " << stageInfo.duration / 1000000.0 << "ms";
}
}
#endif
} // namespace
VulkanPipelineCache::VulkanPipelineCache(VkDevice device, VulkanContext const& context)
: mDevice(device),
mContext(context) {
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
: mDevice(device) {
VkPipelineCacheCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
};
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
}
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout, uint32_t key) noexcept {
mPipelineRequirements.layout = layout;
mPipelineLayoutKey = key;
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept {
@@ -110,6 +79,7 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
VulkanPipelineStateSerializer serializer(mProgramName);
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
shaderStages[0] = {
@@ -122,6 +92,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = mPipelineRequirements.shaders[1];
VulkanPipelineStateSerializer::ShaderStageInfo vtx = { shaderStages[0].stage, mVertexShaderHash,
shaderStages[0].pName };
VulkanPipelineStateSerializer::ShaderStageInfo frg = { shaderStages[1].stage, mFragmentShaderHash,
shaderStages[1].pName };
serializer << vtx << frg;
bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
@@ -140,9 +116,11 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
if (mPipelineRequirements.vertexAttributes[i].format > 0) {
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
serializer << mPipelineRequirements.vertexAttributes[i];
}
if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
serializer << mPipelineRequirements.vertexBuffers[i];
}
}
VkPipelineVertexInputStateCreateInfo vertexInputState = {
@@ -156,11 +134,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
};
serializer << inputAssemblyState;
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
serializer << viewportState;
VkDynamicState dynamicStateEnables[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
@@ -170,6 +152,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.dynamicStateCount = 2,
.pDynamicStates = dynamicStateEnables,
};
serializer << dynamicState;
auto const& raster = mPipelineRequirements.rasterState;
VkPipelineRasterizationStateCreateInfo vkRaster = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@@ -183,6 +167,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.depthBiasSlopeFactor = raster.depthBiasSlopeFactor,
.lineWidth = 1.0f,
};
serializer << vkRaster;
VkPipelineMultisampleStateCreateInfo vkMs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples,
@@ -191,6 +177,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
.alphaToOneEnable = VK_FALSE,
};
serializer << vkMs;
VkPipelineDepthStencilStateCreateInfo vkDs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
@@ -210,6 +198,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.writeMask = 0u,
.reference = 0u,
};
serializer << vkDs;
//layout
serializer.setPipelineLayoutKey(mPipelineLayoutKey);
// renderPass
serializer.setRenderPassKey(mRenderPassKey);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
@@ -248,52 +242,36 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
colorBlendAttachments[i] = colorBlendAttachments[0];
}
}
serializer << colorBlendState;
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
<< shaderStages[1].module << ")";
VkPipelineCreationFeedback stageFeedbacks[SHADER_MODULE_COUNT] = {};
VkPipelineCreationFeedback pipelineFeedback = {};
VkPipelineCreationFeedbackCreateInfo feedbackInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT,
.pNext = nullptr,
.pPipelineCreationFeedback = &pipelineFeedback,
.pipelineStageCreationFeedbackCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1,
.pPipelineStageCreationFeedbacks = stageFeedbacks,
};
if (mContext.pipelineCreationFeedbackSupported()) {
feedbackInfo.pNext = pipelineCreateInfo.pNext;
pipelineCreateInfo.pNext = &feedbackInfo;
}
#endif
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
<< shaderStages[0].module << ", " << shaderStages[1].module << ")";
#endif
PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime,
};
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle);
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
<< shaderStages[1].module << ")";
if (mContext.pipelineCreationFeedbackSupported()) {
printPipelineFeedbackInfo(feedbackInfo);
}
#endif
assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) {
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
return nullptr;
}
PipelineHashFn hashFunc;
serializer.setID(hashFunc(mPipelineRequirements));
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
mProgramName = program->name;
mVertexShaderHash = program->getVertexShaderHash();
mFragmentShaderHash= program->getFragmentShaderHash();
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
@@ -308,9 +286,10 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
mPipelineRequirements.rasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass;
mPipelineRequirements.subpassIndex = subpassIndex;
mRenderPassKey = passKey;
}
void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept {
@@ -86,9 +86,9 @@ public:
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
VulkanPipelineCache(VkDevice device, VulkanContext const& context);
VulkanPipelineCache(VkDevice device);
void bindLayout(VkPipelineLayout layout) noexcept;
void bindLayout(VkPipelineLayout layout, uint32_t key) noexcept;
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
void bindPipeline(VulkanCommandBuffer* commands);
@@ -96,7 +96,7 @@ public:
// Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
@@ -207,11 +207,14 @@ private:
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineKey mPipelineRequirements = {};
utils::CString mProgramName;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
uint32_t mPipelineLayoutKey;
uint32_t mRenderPassKey;
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
[[maybe_unused]] VulkanContext const& mContext;
};
} // namespace filament::backend
@@ -15,6 +15,7 @@
*/
#include "VulkanPipelineLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
namespace filament::backend {
@@ -23,7 +24,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
fvkmemory::resource_ptr<VulkanProgram> program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
key.descSetLayouts = descriptorSetLayouts;
DescriptorSetLayoutHashArray hashArray;
for (uint32_t i = 0; i < descriptorSetLayouts.size(); ++i) {
hashArray[i] = mDescriptorSetCache->getKey(descriptorSetLayouts[i]);
}
key.descSetLayouts = hashArray;
for (auto layoutHandle: descriptorSetLayouts) {
if (layoutHandle == VK_NULL_HANDLE) {
break;
@@ -62,11 +67,14 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = key.descSetLayouts.data(),
.pSetLayouts = descriptorSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges,
};
PipelineLayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
VulkanPipelineLayoutSerializer pipelineSer(info, mDescriptorSetCache, hashedKey);
VkPipelineLayout layout;
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
@@ -74,6 +82,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.handle = layout,
.lastUsed = mTimestamp++,
};
mPipelineToKey[layout] = hashedKey;
return layout;
}
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include <bluevk/BlueVK.h>
@@ -30,10 +31,11 @@ namespace filament::backend {
class VulkanPipelineLayoutCache {
public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetLayoutHashArray = VulkanDescriptorSetLayout::DescriptorSetLayoutHashArray;
VulkanPipelineLayoutCache(VkDevice device)
VulkanPipelineLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache* cache)
: mDevice(device),
mTimestamp(0) {}
mTimestamp(0), mDescriptorSetCache(cache) {}
void terminate() noexcept;
@@ -45,7 +47,7 @@ public:
};
struct PipelineLayoutKey {
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4
DescriptorSetLayoutHashArray descSetLayouts = {}; // 8 * 4
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
uint16_t padding = 0;
};
@@ -58,6 +60,14 @@ public:
// are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program);
uint32_t getKey(VkPipelineLayout layout) {
uint32_t key = 0;
auto iter = mPipelineToKey.find(layout);
if (iter != mPipelineToKey.end()) {
key = iter->second;
}
return key;
}
private:
using Timestamp = uint64_t;
@@ -79,6 +89,8 @@ private:
VkDevice mDevice;
Timestamp mTimestamp;
PipelineLayoutMap mPipelineLayouts;
std::map<VkPipelineLayout, uint32_t> mPipelineToKey;
VulkanDescriptorSetLayoutCache* mDescriptorSetCache;
};
} // filament::backend
@@ -0,0 +1,251 @@
/*
* Copyright (C) 2022 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.
*/
#include <fstream>
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-stack-address"
#endif
using namespace bluevk;
namespace filament::backend {
VulkanPipelineStateSerializer::VulkanPipelineStateSerializer(const utils::CString& name) {
mBuffer << "{" << std::endl;
mBuffer << "\"program_name\":" << "\"" << name.c_str() << "\"" << "," << std::endl;
}
VulkanPipelineStateSerializer::~VulkanPipelineStateSerializer() {
//Shader Modules
mBuffer << "\"shader_modules\":[" << std::endl;
for (uint32_t i = 0; i < mShaderStages.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"stage\":" << mShaderStages[i].stage << "," << std::endl;
mBuffer << "\"key\":" << mShaderStages[i].module << "," << std::endl;
mBuffer << "\"name\":" << "\"" << mShaderStages[i].mName << "\"" << std::endl;
mBuffer << "}";
if (i < mShaderStages.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
//Vertex Attribute
mBuffer << "\"vertex_attributes\":[" << std::endl;
for (uint32_t i = 0; i < mVtxAttribs.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"location\":" << mVtxAttribs[i].location << "," << std::endl;
mBuffer << "\"binding\":" << mVtxAttribs[i].binding << "," << std::endl;
mBuffer << "\"format\":" << mVtxAttribs[i].format << "," << std::endl;
mBuffer << "\"offset\":" << mVtxAttribs[i].offset << std::endl;
mBuffer << "}";
if (i < mVtxAttribs.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
// Vertex Bindings
mBuffer << "\"vertex_bindings\":[" << std::endl;
for (uint32_t i = 0; i < mVtxBindings.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"binding\":" << mVtxBindings[i].binding << "," << std::endl;
mBuffer << "\"stride\":" << mVtxBindings[i].stride << "," << std::endl;
mBuffer << "\"input_rate\":" << mVtxBindings[i].inputRate << std::endl;
mBuffer << "}";
if (i < mVtxBindings.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]" << std::endl;
mBuffer << "}" << std::endl;
std::ofstream file(mFileName);
if (file.is_open()) {
file << mBuffer.str();
}
file.close();
}
void VulkanPipelineStateSerializer::setPipelineLayoutKey(uint32_t pipelineLayoutKey) {
mBuffer << "\"layout\":" << pipelineLayoutKey << "," << std::endl;
}
void VulkanPipelineStateSerializer::setRenderPassKey(uint32_t renderPassKey) {
mBuffer << "\"render_pass\":" << renderPassKey << "," << std::endl;
}
void VulkanPipelineStateSerializer::setID(uint32_t key) {
std::stringstream temp;
temp << "pipeline_" << key << ".json";
temp >> mFileName;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineInputAssemblyStateCreateInfo& inputAsm) {
mBuffer << "\"topology\":" << inputAsm.topology << "," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineViewportStateCreateInfo& viewport) {
mBuffer << "\"viewport_count\":" << viewport.viewportCount << "," << std::endl;
mBuffer << "\"scissor_count\":" << viewport.scissorCount << "," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineRasterizationStateCreateInfo& rasterState) {
mBuffer << "\"raster_states\":{" << std::endl;
mBuffer << "\"depth_clamp_enable\":" << rasterState.depthClampEnable << "," << std::endl;
mBuffer << "\"polygon_mode\":" << rasterState.polygonMode << "," << std::endl;
mBuffer << "\"cull_mode\":" << rasterState.cullMode << "," << std::endl;
mBuffer << "\"front_face\":" << rasterState.frontFace << "," << std::endl;
mBuffer << "\"depth_bias_enable\":" << rasterState.depthBiasEnable << "," << std::endl;
mBuffer << "\"depth_bias_constant_factor\":" << rasterState.depthBiasConstantFactor << ","
<< std::endl;
mBuffer << "\"depth_bias_clamp\":" << rasterState.depthBiasClamp << "," << std::endl;
mBuffer << "\"depth_bias_slope_factor\":" << rasterState.depthBiasSlopeFactor << "," << std::endl;
mBuffer << "\"line_width\":" << rasterState.lineWidth << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineDepthStencilStateCreateInfo& depthStencil) {
mBuffer << "\"depth_stencil_states\":{" << std::endl;
mBuffer << "\"depth_test_enable\":" << depthStencil.depthTestEnable << "," << std::endl;
mBuffer << "\"depth_write_enable\":" << depthStencil.depthWriteEnable << "," << std::endl;
mBuffer << "\"depth_compare_op\":" << depthStencil.depthCompareOp << "," << std::endl;
mBuffer << "\"depth_bounds_test_enable\":" << depthStencil.depthBoundsTestEnable << ","
<< std::endl;
mBuffer << "\"min_depth_bounds\":" << depthStencil.minDepthBounds << "," << std::endl;
mBuffer << "\"max_depth_bounds\":" << depthStencil.maxDepthBounds << "," << std::endl;
mBuffer << "\"front\":{" << std::endl;
mBuffer << "\"fail_op\":" << depthStencil.front.failOp << "," << std::endl;
mBuffer << "\"pass_op\":" << depthStencil.front.passOp << "," << std::endl;
mBuffer << "\"depth_fail_op\":" << depthStencil.front.depthFailOp << "," << std::endl;
mBuffer << "\"compare_op\":" << depthStencil.front.compareOp << "," << std::endl;
mBuffer << "\"compare_mask\":" << depthStencil.front.compareMask << "," << std::endl;
mBuffer << "\"write_mask\":" << depthStencil.front.writeMask << "," << std::endl;
mBuffer << "\"reference\":" << depthStencil.front.reference << std::endl;
mBuffer << "}," << std::endl;
mBuffer << "\"back\":{" << std::endl;
mBuffer << "\"fail_op\":" << depthStencil.back.failOp << "," << std::endl;
mBuffer << "\"pass_op\":" << depthStencil.back.passOp << "," << std::endl;
mBuffer << "\"depth_fail_op\":" << depthStencil.back.depthFailOp << "," << std::endl;
mBuffer << "\"compare_op\":" << depthStencil.back.compareOp << "," << std::endl;
mBuffer << "\"compare_mask\":" << depthStencil.back.compareMask << "," << std::endl;
mBuffer << "\"write_mask\":" << depthStencil.back.writeMask << "," << std::endl;
mBuffer << "\"reference\":" << depthStencil.back.reference << std::endl;
mBuffer << "}" << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineMultisampleStateCreateInfo& multiSample) {
mBuffer << "\"multisample_states\":{" << std::endl;
mBuffer << "\"rasterization_samples\":" << multiSample.rasterizationSamples << "," << std::endl;
mBuffer << "\"sample_shading_enable\":" << multiSample.sampleShadingEnable << "," << std::endl;
mBuffer << "\"min_sample_shading\":" << multiSample.minSampleShading << "," << std::endl;
mBuffer << "\"alpha_to_coverage_enable\":" << multiSample.alphaToCoverageEnable << ","
<< std::endl;
mBuffer << "\"alpha_to_one_enable\":" << multiSample.alphaToOneEnable << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineDynamicStateCreateInfo& dyStates) {
mBuffer << "\"dynamic_states\":[" << std::endl;
for (uint32_t i = 0; i < dyStates.dynamicStateCount; ++i) {
mBuffer << dyStates.pDynamicStates[i];
if (i < dyStates.dynamicStateCount - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineColorBlendStateCreateInfo& blendStateInfo) {
mBuffer << "\"color_blend_state_attachments\":[" << std::endl;
for (uint32_t i = 0; i < blendStateInfo.attachmentCount; ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"blend_enable\":" << blendStateInfo.pAttachments[i].blendEnable << ","
<< std::endl;
mBuffer << "\"src_color_blend_factor\":" << blendStateInfo.pAttachments[i].srcColorBlendFactor
<< ","
<< std::endl;
mBuffer << "\"dst_color_blend_factor\":" << blendStateInfo.pAttachments[i].dstColorBlendFactor
<< ","
<< std::endl;
mBuffer << "\"color_blend_op\":" << blendStateInfo.pAttachments[i].colorBlendOp << ","
<< std::endl;
mBuffer << "\"src_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].srcAlphaBlendFactor
<< ","
<< std::endl;
mBuffer << "\"dst_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].dstAlphaBlendFactor
<< ","
<< std::endl;
mBuffer << "\"alpha_blend_op\":" << blendStateInfo.pAttachments[i].alphaBlendOp << ","
<< std::endl;
mBuffer << "\"color_write_mask\":" << blendStateInfo.pAttachments[i].colorWriteMask
<< std::endl;
mBuffer << "}";
if (i < blendStateInfo.attachmentCount - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const ShaderStageInfo& shaderStages) {
mShaderStages.push_back(shaderStages);
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkVertexInputAttributeDescription& attrib) {
mVtxAttribs.push_back(attrib);
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkVertexInputBindingDescription& binding) {
mVtxBindings.push_back(binding);
return *this;
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 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_BACKEND_VULKANPIPELINESTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
#include "VulkanCommands.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <type_traits>
#include <sstream>
#include <vector>
namespace filament::backend {
// VulkanPipelineStateSerializer stores to a file the pipeline states.
class VulkanPipelineStateSerializer {
public:
VulkanPipelineStateSerializer(VulkanPipelineStateSerializer const&) = delete;
VulkanPipelineStateSerializer& operator=(VulkanPipelineStateSerializer const&) = delete;
VulkanPipelineStateSerializer(const utils::CString& name);
~VulkanPipelineStateSerializer();
void setID(uint32_t key);
void setPipelineLayoutKey(uint32_t pipelineLayoutKey);
void setRenderPassKey(uint32_t renderPassKey);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineInputAssemblyStateCreateInfo& inputAsm);
VulkanPipelineStateSerializer& operator<<(const VkPipelineViewportStateCreateInfo& viewport);
VulkanPipelineStateSerializer& operator<<(const VkPipelineDynamicStateCreateInfo& dyStates);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineRasterizationStateCreateInfo& rasterState);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineDepthStencilStateCreateInfo& depthStencil);
VulkanPipelineStateSerializer& operator<<(const VkPipelineMultisampleStateCreateInfo& multiSample);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineColorBlendStateCreateInfo& blendStateInfo);
struct ShaderStageInfo {
VkShaderStageFlagBits stage;
uint64_t module;
std::string mName;
};
VulkanPipelineStateSerializer& operator<<(const ShaderStageInfo& shaderStages);
VulkanPipelineStateSerializer& operator<<(
const VkVertexInputAttributeDescription& attrib);
VulkanPipelineStateSerializer& operator<<(const VkVertexInputBindingDescription& binding);
private:
std::string mFileName;
std::stringstream mBuffer;
//temp data
utils::CString mProgramName;
std::vector<ShaderStageInfo> mShaderStages;
std::vector<VkVertexInputAttributeDescription> mVtxAttribs;
std::vector<VkVertexInputBindingDescription> mVtxBindings;
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2022 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.
*/
#include <fstream>
#include "VulkanRenderPassStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-stack-address"
#endif
using namespace bluevk;
namespace filament::backend {
VulkanRenderPassStateSerializer::VulkanRenderPassStateSerializer(
const VkRenderPassCreateInfo& info, uint32_t key) {
std::stringstream filename;
filename << "render_pass_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "{" << std::endl;
buffer << "\"attachments\":[" << std::endl;
for (uint32_t i = 0; i < info.attachmentCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.pAttachments[i].flags << "," << std::endl;
buffer << "\"format\":" << info.pAttachments[i].format << "," << std::endl;
buffer << "\"samples\":" << info.pAttachments[i].samples << "," << std::endl;
buffer << "\"load_op\":" << info.pAttachments[i].loadOp << "," << std::endl;
buffer << "\"store_op\":" << info.pAttachments[i].storeOp << "," << std::endl;
buffer << "\"stencil_load_op\":" << info.pAttachments[i].stencilLoadOp << "," << std::endl;
buffer << "\"stencil_store_op\":" << info.pAttachments[i].stencilStoreOp << "," << std::endl;
buffer << "\"initial_layout\":" << info.pAttachments[i].initialLayout << "," << std::endl;
buffer << "\"final_layout\":" << info.pAttachments[i].finalLayout << std::endl;
buffer << "}";
if (i < info.attachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]," << std::endl;
buffer << "\"subpasses\":[" << std::endl;
for (uint32_t i = 0; i < info.subpassCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.pSubpasses[i].flags << "," << std::endl;
buffer << "\"pipeline_bind_point\":" << info.pSubpasses[i].pipelineBindPoint;
//------------depth stencil-------------------
if (info.pSubpasses[i].pDepthStencilAttachment) {
buffer << "," << std::endl;
buffer << "\"depth_stencil_attachment\":{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pDepthStencilAttachment->attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pDepthStencilAttachment->layout
<< std::endl;
buffer << "}";
}
//------------Resolve-------------------
if (info.pSubpasses[i].pResolveAttachments) {
buffer << "," << std::endl;
buffer << "\"resolve_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pResolveAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pResolveAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]";
}
//------------Color-------------------
if (info.pSubpasses[i].pColorAttachments) {
buffer << "," << std::endl;
buffer << "\"color_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pColorAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pColorAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]";
}
//------------Input-------------------
if (info.pSubpasses[i].pInputAttachments) {
buffer << "," << std::endl;
buffer << "\"input_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].inputAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pInputAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pInputAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].inputAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
}
buffer << "}";
if (i < info.subpassCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
buffer << "}";
file << buffer.str();
}
file.close();
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 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_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
#include "VulkanCommands.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <type_traits>
#include <sstream>
#include <vector>
namespace filament::backend {
class VulkanRenderPassStateSerializer {
public:
VulkanRenderPassStateSerializer(VulkanRenderPassStateSerializer const&) = delete;
VulkanRenderPassStateSerializer& operator=(VulkanRenderPassStateSerializer const&) = delete;
VulkanRenderPassStateSerializer(const VkRenderPassCreateInfo& info, uint32_t key);
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
@@ -21,12 +21,14 @@
#include <utils/Panic.h>
#include "VulkanSamplerStateSerializer.h"
using namespace bluevk;
namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VulkanSamplerCache::VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache)
: mDevice(device), mConversionCache(conversionCache) {}
VkSampler VulkanSamplerCache::getSampler(Params params) {
auto iter = mCache.find(params);
@@ -57,11 +59,16 @@ VkSampler VulkanSamplerCache::getSampler(Params params) {
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VulkanSamplerStateSerializer samplerSer(params,
params.conversion != VK_NULL_HANDLE ? mConversionCache->getKey(params.conversion) : 0);
VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, sampler });
SamplerHashFn hashFn;
mSamplerToKey[sampler] = hashFn(params);
return sampler;
}
@@ -17,9 +17,11 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
#include <map>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
@@ -37,11 +39,20 @@ public:
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device);
explicit VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache);
VkSampler getSampler(Params params);
uint32_t getKey(VkSampler sampler) {
uint32_t key = 0;
auto iter = mSamplerToKey.find(sampler);
if (iter != mSamplerToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
VkDevice mDevice;
VulkanYcbcrConversionCache* mConversionCache;
struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
@@ -51,6 +62,7 @@ private:
};
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
std::map<VkSampler, uint32_t> mSamplerToKey;
};
} // namespace filament::backend
@@ -0,0 +1,64 @@
#include "VulkanSamplerStateSerializer.h"
#include <fstream>
#include <sstream>
#include <utils/Hash.h>
namespace filament::backend {
VulkanYcbcrConversionSerializer::VulkanYcbcrConversionSerializer(
const VulkanYcbcrConversionCache::Params& params) {
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanYcbcrConversionCache::Params>;
ConversionHashFn hashFn;
uint32_t key = hashFn(params);
std::stringstream filename;
filename << "ycbcr_conv_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
auto const& chroma = params.conversion;
buffer << "{" << std::endl;
buffer << "\"format\":" << params.format << "," << std::endl;
buffer << "\"external_format\":" << params.externalFormat << "," << std::endl;
buffer << "\"ycbcr_model\":" << uint8_t(chroma.ycbcrModel) << "," << std::endl;
buffer << "\"ycbcr_range\":" << uint8_t(chroma.ycbcrRange) << "," << std::endl;
buffer << "\"swizzle_array\":[" << uint8_t(chroma.r) << "," << uint8_t(chroma.g) << ","
<< uint8_t(chroma.b) << "," << uint8_t(chroma.a) << "]," << std::endl;
buffer << "\"x_chroma_offset\":" << uint8_t(chroma.xChromaOffset) << "," << std::endl;
buffer << "\"y_chroma_offset\":" << uint8_t(chroma.yChromaOffset) << "," << std::endl;
buffer << "\"chroma_filter\":" << uint8_t(chroma.chromaFilter) << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
VulkanSamplerStateSerializer::VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key) {
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanSamplerCache::Params>;
ConversionHashFn hashFn;
uint32_t key = hashFn(params);
std::stringstream filename;
filename << "sampler_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
auto const& samplerParams = params.sampler;
buffer << "{" << std::endl;
buffer << "\"filter_mag\":" << uint32_t(samplerParams.filterMag) << "," << std::endl;
buffer << "\"filter_min\":" << uint32_t(samplerParams.filterMin) << "," << std::endl;
buffer << "\"wrap_s\":" << uint32_t(samplerParams.wrapS) << "," << std::endl;
buffer << "\"wrap_t\":" << uint32_t(samplerParams.wrapT) << "," << std::endl;
buffer << "\"wrap_r\":" << uint32_t(samplerParams.wrapR) << "," << std::endl;
buffer << "\"anisotropy_log2\":" << uint32_t(samplerParams.anisotropyLog2) << "," << std::endl;
buffer << "\"compare_mode\":" << uint32_t(samplerParams.compareMode) << "," << std::endl;
buffer << "\"compare_func\":" << uint32_t(samplerParams.compareFunc) << "," << std::endl;
buffer << "\"ycbcr_conv_key\":" << ycbcr_conv_key << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
} // namespace filament::backend
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 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_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
#include <backend/DriverEnums.h>
#include "VulkanYcbcrConversionCache.h"
#include "VulkanSamplerCache.h"
#include <bluevk/BlueVK.h>
#include <vector>
#include <iostream>
#include <sstream>
namespace filament::backend {
class VulkanYcbcrConversionSerializer {
public:
VulkanYcbcrConversionSerializer(VulkanYcbcrConversionSerializer const&) = delete;
VulkanYcbcrConversionSerializer& operator=(VulkanYcbcrConversionSerializer const&) = delete;
VulkanYcbcrConversionSerializer(const VulkanYcbcrConversionCache::Params& params);
};
// VulkanPipelineStateSerializer stores to a file the pipeline states.
class VulkanSamplerStateSerializer {
public:
VulkanSamplerStateSerializer(VulkanSamplerStateSerializer const&) = delete;
VulkanSamplerStateSerializer& operator=(VulkanSamplerStateSerializer const&) = delete;
VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key);
};
}
#endif //TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
@@ -15,6 +15,7 @@
*/
#include "VulkanYcbcrConversionCache.h"
#include "VulkanSamplerStateSerializer.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
@@ -53,6 +54,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VulkanYcbcrConversionSerializer ycbcr_ser(params);
// We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__)
@@ -73,6 +75,8 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion });
ConversionHashFn hashFn;
mConversionToKey[conversion] = hashFn(params);
return conversion;
}
@@ -25,6 +25,7 @@
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -40,6 +41,14 @@ public:
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params);
uint32_t getKey(VkSamplerYcbcrConversion conv) {
uint32_t key = 0;
auto iter = mConversionToKey.find(conv);
if (iter != mConversionToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
@@ -54,6 +63,7 @@ private:
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
std::map<VkSamplerYcbcrConversion, uint32_t> mConversionToKey;
};
} // namespace filament::backend
@@ -228,10 +228,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
#endif
VK_KHR_MULTIVIEW_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
#endif
};
ExtensionSet exts;
// Identify supported physical device extensions
@@ -869,8 +865,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mPipelineCreationFeedbackSupported =
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
} else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
@@ -879,16 +873,13 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// Check the availability of lazily allocated memory
context.mLazilyAllocatedMemorySupported = false;
// RenderDoc doesn't support lazy allocated memory
if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
++i) {
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
context.mLazilyAllocatedMemorySupported = true;
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
break;
}
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
++i) {
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
context.mLazilyAllocatedMemorySupported = true;
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
break;
}
}
@@ -1031,8 +1022,4 @@ VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const no
return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
}
bool VulkanPlatform::isTransientAttachmentSupported() const noexcept {
return mImpl->mContext.isLazilyAllocatedMemorySupported();
}
} // namespace filament::backend
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#include "vulkan/VulkanConstants.h"
@@ -21,8 +20,6 @@
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
#include "vulkan/utils/Image.h"
#include "AndroidFrameCallback.h"
#include <private/backend/BackendUtilsAndroid.h>
#include <backend/DriverEnums.h>
@@ -35,7 +32,6 @@
#include <android/hardware_buffer.h>
#include <android/native_window.h>
#include <new>
#include <utility>
using namespace bluevk;
@@ -162,55 +158,7 @@ std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardware
};
}
uint32_t selectMemoryTypeForExternalImage(VkPhysicalDevice physicalDevice, VkDevice device,
VkImage image, uint32_t types, VkFlags requiredMemoryFlags) {
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
uint32_t const memoryTypeIndex =
VulkanContext::selectMemoryType(memoryProperties, types, requiredMemoryFlags);
if constexpr (FVK_RENDERDOC_CAPTURE_MODE) {
// RenderDoc will replay external resources as non-external.
// Adjust properties that will trip it up when replaying, even though these are not valid.
// Update memory type index if necessary so that we can replay the capture.
VkMemoryRequirements imageMemoryRequirements;
vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements);
uint32_t const imageMemoryTypeBits = imageMemoryRequirements.memoryTypeBits;
bool const isMemoryTypeSupported = ((1 << memoryTypeIndex) & imageMemoryTypeBits) != 0;
if (isMemoryTypeSupported) {
return memoryTypeIndex;
}
// Current memory type will not be replayable by RenderDoc.
// Attempt to change the memory type index
VkMemoryPropertyFlags const kRenderDocFallBackReqs = 0;
uint32_t commonMemoryTypeBits = types & imageMemoryTypeBits;
uint32_t commonTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
commonMemoryTypeBits, kRenderDocFallBackReqs);
if (commonMemoryTypeBits && commonTypeIndex != VK_MAX_MEMORY_TYPES) {
return commonTypeIndex;
}
uint32_t imageTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
imageMemoryTypeBits, kRenderDocFallBackReqs);
if (imageTypeIndex != VK_MAX_MEMORY_TYPES) {
return imageTypeIndex;
}
}
return memoryTypeIndex;
}
} // namespace
// ---------------------------------------------------------------------------------------------
struct VulkanPlatformAndroid::AndroidDetails {
AndroidFrameCallback androidFrameCallback;
};
// ---------------------------------------------------------------------------------------------
}// namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
if (__builtin_available(android 26, *)) {
@@ -220,19 +168,14 @@ VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid()
}
}
// ---------------------------------------------------------------------------------------------
VulkanPlatformAndroid::VulkanPlatformAndroid() :
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
VulkanPlatformAndroid::VulkanPlatformAndroid() {
mOSVersion = android_get_device_api_level();
if (mOSVersion < 0) {
mOSVersion = __ANDROID_API_FUTURE__;
}
}
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept {
delete &mAndroidDetails;
}
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept = default;
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
@@ -411,6 +354,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
.image = image,
.buffer = VK_NULL_HANDLE,
};
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
VkMemoryPropertyFlags requiredMemoryFlags =
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
@@ -420,8 +365,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
}
uint32_t const memoryTypeIndex = selectMemoryTypeForExternalImage(physicalDevice, device,
image, metadata.memoryTypeBits, requiredMemoryFlags);
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
@@ -514,14 +459,14 @@ int VulkanPlatformAndroid::getOSVersion() const noexcept {
}
void VulkanPlatformAndroid::terminate() {
mAndroidDetails.androidFrameCallback.terminate();
mAndroidFrameCallback.terminate();
VulkanPlatform::terminate();
}
Driver* VulkanPlatformAndroid::createDriver(void* sharedContext, DriverConfig const& driverConfig) {
Driver* driver = VulkanPlatform::createDriver(sharedContext, driverConfig);
if (driver) {
mAndroidDetails.androidFrameCallback.init();
mAndroidFrameCallback.init();
}
return driver;
}
@@ -537,7 +482,7 @@ bool VulkanPlatformAndroid::queryCompositorTiming(SwapChain const* swapchain,
}
AndroidFrameCallback::Timeline const preferredTimeline{
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
mAndroidFrameCallback.getPreferredTimeline() };
outCompositorTiming->frameTime = preferredTimeline.frameTime;
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
@@ -165,11 +165,6 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
VkResult VulkanPlatformSurfaceSwapChain::create() {
#ifdef __ANDROID__
NativeWindow::enableFrameTimestamps(static_cast<ANativeWindow*>(mNativeWindow), true);
// on Android, disable producer throttling
if (mProducerThrottling.isSupported()) {
mProducerThrottling.setProducerThrottlingEnabled(
static_cast<ANativeWindow*>(mNativeWindow), false);
}
#endif
VkSurfaceFormatKHR surfaceFormat = {};
@@ -25,8 +25,7 @@
#include <bluevk/BlueVK.h>
#ifdef __ANDROID__
#include "AndroidSwapChainHelper.h"
#include "AndroidNativeWindow.h"
#include <AndroidSwapChainHelper.h>
#endif
#include <unordered_map>
@@ -123,7 +122,6 @@ private:
#ifdef __ANDROID__
AndroidSwapChainHelper mImpl{};
AndroidProducerThrottling mProducerThrottling;
#endif
};
@@ -18,7 +18,6 @@
#include "WebGPUConstants.h"
#include "WebGPUQueueManager.h"
#include "WebGPUStagePool.h"
#include "DriverBase.h"
#include <backend/BufferDescriptor.h>
@@ -30,7 +29,6 @@
#include <cstdint>
#include <cstring>
#include <iostream>
namespace filament::backend {
@@ -67,7 +65,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
// of 4 by padding with zeros.
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
const uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool) {
WebGPUQueueManager* const webGPUQueueManager) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
<< "updateGPUBuffer called with a null buffer";
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= mBuffer.GetSize())
@@ -81,54 +79,34 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
// This may have some performance implications. That should be investigated later.
assert_invariant(mBuffer.GetUsage() & wgpu::BufferUsage::CopyDst);
// // Calculate some alignment related sizes
// Calculate some alignment related sizes
const size_t remainder = bufferDescriptor.size % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
const size_t mainBulk = bufferDescriptor.size - remainder;
const size_t stagingBufferSize =
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
Stage stage = webGPUStagePool->acquireBuffer(stagingBufferSize);
// create a staging buffer
wgpu::BufferDescriptor descriptor{
.label = "Filament WebGPU Staging Buffer",
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
.size = stagingBufferSize,
.mappedAtCreation = true };
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
std::string mappedRangeIsNull = stage.mappedRange
? "no"
: "yes";
std::cout << "Run Yu: got mapped range on the staging buffer with size "
<< stage.buffer.GetSize() << " and it is null? " << mappedRangeIsNull << std::endl;
memcpy(stage.mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
void* mappedRange = stagingBuffer.GetMappedRange();
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
stage.buffer.Unmap();
// Make sure the padded memory is set to 0 to have deterministic behaviors
if (remainder != 0) {
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size;
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder);
}
stagingBuffer.Unmap();
std::cout << "Run Yu: about to issue copy command with actual staging buffer of size "
<< stage.buffer.GetSize() << ", and computed size of " << stagingBufferSize
<< ". The mBuffer size is " << mBuffer.GetSize() << std::endl;
// Copy the staging buffer contents to the destination buffer.
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stage.buffer, 0, mBuffer,
byteOffset,
remainder == 0 ? bufferDescriptor.size
: mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS);
webGPUQueueManager->flush();
struct UserData final {
wgpu::Buffer stagingBuffer;
WebGPUStagePool* webGPUStagePool;
};
auto userData = std::make_unique<UserData>(
UserData{ .stagingBuffer = stage.buffer, .webGPUStagePool = webGPUStagePool });
stage.buffer.MapAsync(wgpu::MapMode::Write, 0, stagingBufferSize,
wgpu::CallbackMode::AllowSpontaneous,
[data = std::move(userData)](wgpu::MapAsyncStatus status, const char* message) {
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
std::cout << "Run Yu: successfully mapped a buffer with size "
<< data->stagingBuffer.GetSize() << std::endl;
void* mappedRange = data->stagingBuffer.GetMappedRange();
if (!mappedRange) {
std::cout << "Run Yu: MAPPED RANGE IS NULL RIGHT AWAY!!\n";
}
data->webGPUStagePool->addBufferToPool(data->stagingBuffer, mappedRange);
} else {
std::cout << "Run Yu: MAPPING UNSUCCESSFUL!!\n";
}
});
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
byteOffset, stagingBufferSize);
}
} // namespace filament::backend
@@ -25,7 +25,6 @@ namespace filament::backend {
class BufferDescriptor;
class WebGPUQueueManager;
class WebGPUStagePool;
/**
* A base class for WebGPU buffer objects, providing common functionality for creating and
@@ -41,7 +40,7 @@ public:
* ensures the calls happen in the expected sequence.
*/
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool);
WebGPUQueueManager* const webGPUQueueManager);
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
+3 -4
View File
@@ -107,7 +107,6 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
mAdapter{ mPlatform.requestAdapter(nullptr) },
mDevice{ mPlatform.requestDevice(mAdapter) },
mQueueManager{ mDevice },
mStagePool{ mDevice },
mPipelineLayoutCache{ mDevice },
mPipelineCache{ mDevice },
mRenderPassMipmapGenerator{ mDevice, &mQueueManager },
@@ -857,7 +856,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
// draw calls are made.
flush();
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor));
}
@@ -868,14 +867,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
// draw calls are made.
flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor));
}
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor));
}
@@ -25,7 +25,6 @@
#include "webgpu/WebGPUPipelineLayoutCache.h"
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
#include "webgpu/WebGPUQueueManager.h"
#include "webgpu/WebGPUStagePool.h"
#include "webgpu/utils/AsyncTaskCounter.h"
#include <backend/platforms/WebGPUPlatform.h>
@@ -82,7 +81,6 @@ private:
wgpu::Device mDevice = nullptr;
wgpu::Limits mDeviceLimits = {};
WebGPUQueueManager mQueueManager;
WebGPUStagePool mStagePool;
void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
@@ -1,86 +0,0 @@
/*
* Copyright (C) 2025 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.
*/
#include "WebGPUStagePool.h"
#include "WebGPUConstants.h"
#include <iostream>
namespace filament::backend {
WebGPUStagePool::WebGPUStagePool(wgpu::Device const& device) : mDevice(device) {}
WebGPUStagePool::~WebGPUStagePool() = default;
Stage WebGPUStagePool::acquireBuffer(size_t requiredSize) {
std::cout << "Run Yu: required size in acquireBuffer: " << requiredSize << std::endl;
std::cout << "Run Yu: the pool size is " << mBuffers.size() << std::endl;
{
std::lock_guard<std::mutex> lock(mMutex);
auto iter = mBuffers.lower_bound(requiredSize);
if (iter != mBuffers.end()) {
const Stage& fromPool = iter->second;
std::cout << "Run Yu: found buffer in the pool with size " << fromPool.buffer.GetSize()
<< std::endl;
if (fromPool.buffer.GetMapState() != wgpu::BufferMapState::Mapped) {
std::cout << "Run Yu: buffer from pool is not mapped!!" << std::endl;
}
Stage result{ .buffer = fromPool.buffer, .mappedRange = fromPool.mappedRange };
mBuffers.erase(iter);
return result;
}
}
wgpu::Buffer newBuffer = createNewBuffer(requiredSize);
return { .buffer = newBuffer, .mappedRange = newBuffer.GetMappedRange() };
}
void WebGPUStagePool::addBufferToPool(wgpu::Buffer buffer, void* mappedRange) {
std::lock_guard<std::mutex> lock(mMutex);
std::cout << "Run Yu: adding buffer to the pool with size " << buffer.GetSize() << std::endl;
Stage stage {.buffer = buffer, .mappedRange = mappedRange};
mBuffers.emplace(buffer.GetSize(), stage);
std::cout << "Run Yu: added buffer to the pool with size " << buffer.GetSize() << std::endl;
bool allMapped = true;
for (const auto& pair : mBuffers) {
auto state = pair.second.buffer.GetMapState();
if (state != wgpu::BufferMapState::Mapped) {
allMapped = false;
std::cout << "Run Yu: the buffer with size " << pair.second.buffer.GetSize()
<< " is not mapped but somehow was added to the pool, its state is "
<< static_cast<int>(state) << std::endl;
}
}
if (!allMapped) {
std::cout << "Run Yu: found buffers that are not mapped\n";
} else {
std::cout << "Run Yu: all buffers are mapped\n";
}
}
wgpu::Buffer WebGPUStagePool::createNewBuffer(size_t bufferSize) {
std::cout << "Run Yu: creating new buffer with size " << bufferSize << std::endl;
wgpu::BufferDescriptor descriptor{
.label = "Filament WebGPU Staging Buffer",
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
.size = bufferSize,
.mappedAtCreation = true };
return mDevice.CreateBuffer(&descriptor);
}
} // namespace filament::backend
@@ -1,49 +0,0 @@
/*
* Copyright (C) 2025 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_BACKEND_WEBGPUSTAGEPOOL_H
#define TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
#include <webgpu/webgpu_cpp.h>
#include <map>
#include <mutex>
namespace filament::backend {
struct Stage {
wgpu::Buffer buffer;
void* mappedRange;
};
class WebGPUStagePool {
public:
WebGPUStagePool(wgpu::Device const& device);
~WebGPUStagePool();
Stage acquireBuffer(size_t requiredSize);
void addBufferToPool(wgpu::Buffer buffer, void* mappedRange);
private:
wgpu::Buffer createNewBuffer(size_t bufferSize);
std::multimap<uint32_t, Stage> mBuffers;
mutable std::mutex mMutex;
wgpu::Device mDevice;
};
}
#endif // TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
+19 -23
View File
@@ -258,10 +258,6 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
{ { 0, 0 }, driver });
}
UboManager* PostProcessManager::getUboManager() const noexcept {
return mEngine.getUboManager();
}
UTILS_NOINLINE
void PostProcessManager::registerPostProcessMaterial(std::string_view const name,
StaticMaterialInfo const& info) {
@@ -517,7 +513,7 @@ UTILS_NOINLINE
void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi,
PostProcessVariant const variant) const noexcept {
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
FMaterial const* const ma = mi->getMaterial();
PipelineState const pipeline = getPipelineState(ma, variant);
@@ -649,7 +645,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto th = driver.createTextureView(in, level, 1);
mi->setParameter("depth", th, SamplerParams{
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -1083,7 +1079,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->setParameter("ssctRayCount",
float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) });
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto pipeline = getPipelineState(ma);
@@ -1204,7 +1200,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
mi->setParameter("sampleCount", kGaussianCount);
mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold);
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto pipeline = getPipelineState(ma);
@@ -1899,7 +1895,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres?
mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h });
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
renderFullScreenQuad(out, pipeline, driver);
@@ -2429,7 +2425,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
mi->setParameter("source", hwOutView, SamplerParams{
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
renderFullScreenQuad(hwDstRT, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -2537,7 +2533,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
FMaterialInstance* const mi =
configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
vignetteOptions, width, height);
mi->commit(driver, getUboManager());
mi->commit(driver);
}
void PostProcessManager::colorGradingSubpass(DriverApi& driver,
@@ -2570,7 +2566,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.customResolve = fixedIndex;
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
mi->commit(driver, getUboManager());
mi->commit(driver);
material.getMaterial(mEngine);
}
@@ -2623,7 +2619,7 @@ void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver) noexcep
auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.clearDepth = fixedIndex;
mi->commit(driver, getUboManager());
mi->commit(driver);
material.getMaterial(mEngine);
}
@@ -3115,7 +3111,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
mat4f{ historyProjection * inverse(current.projection) } *
normalizedToClip);
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
if (colorGradingConfig.asSubpass) {
@@ -3200,7 +3196,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
mi->setParameter("resolution", float4{
outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto pipeline = getPipelineState(material.getMaterial(mEngine), variant);
@@ -3291,7 +3287,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto out = resources.getRenderPassInfo();
@@ -3378,7 +3374,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
float(inputDesc.height)
});
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto const out = resources.getRenderPassInfo();
@@ -3473,7 +3469,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
mi->setParameter("resolution",
float4{ outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
}
@@ -3500,7 +3496,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
}
@@ -3586,7 +3582,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
if (layer) {
mi->setParameter("layerIndex", layer);
}
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto pipeline = getPipelineState(ma);
@@ -3844,7 +3840,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
});
mi->setParameter("layer", uint32_t(layer));
mi->setParameter("uvscale", 1.0f / float(dim));
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
renderFullScreenQuadWithScissor(out, pipeline, scissor, driver);
@@ -3946,7 +3942,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->commit(driver, getUboManager());
mi->commit(driver);
mi->use(driver);
auto pipeline = getPipelineState(ma);
@@ -3963,7 +3959,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
// Render all layers of the texture to the screen side-by-side.
for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) {
mi->setParameter("layerIndex", i);
mi->commit(driver, getUboManager());
mi->commit(driver);
renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
// From the second draw, don't clear the targetbuffer.
-3
View File
@@ -65,7 +65,6 @@ class FMaterialInstance;
class FrameGraph;
class RenderPass;
class RenderPassBuilder;
class UboManager;
struct CameraInfo;
class PostProcessManager {
@@ -423,8 +422,6 @@ private:
return getMaterialInstance(ma);
}
UboManager* getUboManager() const noexcept;
backend::RenderPrimitiveHandle mFullScreenQuadRph;
backend::VertexBufferInfoHandle mFullScreenQuadVbih;
backend::DescriptorSetLayoutHandle mPerRenderableDslh;
+15 -25
View File
@@ -1012,8 +1012,6 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size());
pCustomCommands[index]();
currentPipeline = {};
currentPrimitiveHandle ={};
continue;
}
@@ -1071,34 +1069,26 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] =
ma->getDescriptorSetLayout(info.materialVariant).getHandle();
// If we have a ColorPassDescriptorSet we use it to bind the per-view
// descriptor-set (ideally only if it changed).
// If we don't, it means the descriptor-set is already bound and the layout we
// got from the material above should match. This is the case for situations
// where we have a known per-view descriptor-set layout,
// e.g.: postfx, shadow-maps, ssr and structure passes.
if (mColorPassDescriptorSet) {
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
// It is possible to get a post-process material here (even though it's
// not technically a public API yet, it is used by the IBLPrefilterLibrary.
// Ideally we would have a more formal compute API). In this case, we need
// to set the post-process descriptor-set.
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
} else {
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
// It is possible to get a post-process material here (even though it's
// not technically a public API yet, it is used by the IBLPrefilterLibrary.
// Ideally we would have a more formal compute API). In this case, we need
// to set the post-process descriptor-set.
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
} else {
// If we have a ColorPassDescriptorSet we use it to bind the per-view
// descriptor-set (ideally only if it changed). If we don't, it means
// the descriptor-set is already bound and the layout we got from the
// material above should match. This is the case for situations where we
// have a known per-view descriptor-set layout, e.g.: shadow-maps, ssr and
// structure passes.
if (mColorPassDescriptorSet) {
// We have a ColorPassDescriptorSet, we need to go through it for binding
// the per-view descriptor-set because its layout can change based on the
// material.
mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex());
}
} else {
// if we're here it means the per-view descriptor set is constant and
// already set. This will be the case for postfx, ssr, structure and
// shadow passes. All these passes use a static descriptor set layout
// (albeit potentially different for each pass). In particular the
// per-view UBO must be compatible for all material domains.
// This is the case by construction for postfx, ssr. However, shadows
// and structure have their own UBO, but it's content is (must be)
// compatible with POST_PROCESS and COMPUTE materials.
}
// Each MaterialInstance has its own descriptor set. This binds it.
+2 -17
View File
@@ -16,7 +16,6 @@
#include "details/BufferAllocator.h"
#include <private/utils/Tracing.h>
#include <utils/Panic.h>
#include <utils/debug.h>
@@ -143,11 +142,7 @@ void BufferAllocator::retire(AllocationId id) {
InternalSlotNode* targetNode = getNodeById(id);
assert_invariant(targetNode != nullptr);
Slot& slot = targetNode->slot;
slot.isAllocated = false;
if (slot.gpuUseCount == 0) {
mHasPendingFrees = true;
}
targetNode->slot.isAllocated = false;
}
void BufferAllocator::acquireGpu(AllocationId id) {
@@ -162,19 +157,10 @@ void BufferAllocator::releaseGpu(AllocationId id) {
assert_invariant(targetNode != nullptr);
assert_invariant(targetNode->slot.gpuUseCount > 0);
Slot& slot = targetNode->slot;
slot.gpuUseCount--;
if (slot.gpuUseCount == 0 && !slot.isAllocated) {
mHasPendingFrees = true;
}
targetNode->slot.gpuUseCount--;
}
void BufferAllocator::releaseFreeSlots() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
if (!mHasPendingFrees) {
return;
}
auto curr = mSlotPool.begin();
while (curr != mSlotPool.end()) {
if (!curr->slot.isFree()) {
@@ -213,7 +199,6 @@ void BufferAllocator::releaseFreeSlots() {
curr = next;
}
mHasPendingFrees = false;
}
BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept {
+1 -2
View File
@@ -113,9 +113,8 @@ private:
[[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept;
bool mHasPendingFrees = false;
allocation_size_t mTotalSize;
const allocation_size_t mSlotSize; // Size of a single slot in bytes
const allocation_size_t mSlotSize; // Size of a single slot in bytes.
std::list<InternalSlotNode> mSlotPool; // All slots, including both allocated and freed
std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList;
std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap;
+6 -47
View File
@@ -468,26 +468,11 @@ void FEngine::init() {
}
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
}
// We must commit the default material instance here. It may not be used in a scene, but its
// descriptor set may still be used for shared variants.
//
// Note that this material instance is instantiated before the creation of UboManager, so at
// this point `isUboBatchingEnabled` is `false`, and it will fall back to individual UBO
// automatically.
mDefaultMaterial->getDefaultInstance()->commit(driverApi, mUboManager);
mDefaultMaterial->getDefaultInstance()->commit(driverApi);
if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
// UBO batching is not supported in feature level 0
if (features.material.enable_material_instance_uniform_batching) {
// Ubo size of each material instance is at least 16 bytes.
constexpr BufferAllocator::allocation_size_t minSlotSize = 16;
auto const uboOffsetAlignment = static_cast<BufferAllocator::allocation_size_t>(
driverApi.getUniformBufferOffsetAlignment());
BufferAllocator::allocation_size_t slotSize = std::max(minSlotSize, uboOffsetAlignment);
mUboManager = new UboManager(getDriverApi(), slotSize, mConfig.sharedUboInitialSizeInBytes);
}
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
constexpr float3 dummyPositions[1] = {};
@@ -668,12 +653,6 @@ void FEngine::shutdown() {
driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
if (isUboBatchingEnabled()) {
mUboManager->terminate(driver);
delete mUboManager;
mUboManager = nullptr;
}
/*
* Shutdown the backend...
*/
@@ -714,30 +693,18 @@ void FEngine::prepare() {
// UBOs that are visible only. It's not such a big issue because the actual upload() is
// skipped if the UBO hasn't changed. Still we could have a lot of these.
DriverApi& driver = getDriverApi();
const bool useUboBatching = isUboBatchingEnabled();
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
mUboManager->beginFrame(driver);
}
UboManager* uboManager = mUboManager;
for (auto& materialInstanceList: mMaterialInstances) {
materialInstanceList.second.forEach([&driver, uboManager](FMaterialInstance* item) {
materialInstanceList.second.forEach([&driver](FMaterialInstance* item) {
// post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
item->commitStreamUniformAssociations(driver);
item->commit(driver, uboManager);
item->commit(driver);
}
});
}
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
getUboManager()->finishBeginFrame(getDriverApi());
}
mMaterials.forEach([](FMaterial* material) {
#if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner)
material->checkProgramEdits();
@@ -754,13 +721,6 @@ void FEngine::gc() {
mCameraManager.gc(*this, em);
}
void FEngine::submitFrame() {
if (isUboBatchingEnabled()) {
DriverApi& driver = getDriverApi();
getUboManager()->endFrame(driver);
}
}
void FEngine::flush() {
// flush the command buffer
flushCommandBuffer(mCommandBufferQueue);
@@ -991,10 +951,9 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
return p;
}
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
UboBatchingMode batchingMode) noexcept {
FMaterialInstance* p =
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
const char* name) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name);
if (UTILS_LIKELY(p)) {
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p);
+5 -26
View File
@@ -21,17 +21,16 @@
#include "Allocators.h"
#include "DFG.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "HwDescriptorSetLayoutFactory.h"
#include "HwVertexBufferInfoFactory.h"
#include "MaterialCache.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "UboManager.h"
#include "components/CameraManager.h"
#include "components/LightManager.h"
#include "components/RenderableManager.h"
#include "components/TransformManager.h"
#include "components/RenderableManager.h"
#include "ds/DescriptorSetLayout.h"
@@ -264,14 +263,6 @@ public:
return mBackend;
}
bool isUboBatchingEnabled() const noexcept {
return mUboManager != nullptr;
}
UboManager* getUboManager() noexcept {
return mUboManager;
}
Platform* getPlatform() const noexcept {
return mPlatform;
}
@@ -345,21 +336,11 @@ public:
FRenderer* createRenderer() noexcept;
// Defines whether a material instance should use UBO batching or not.
enum class UboBatchingMode {
// For default, it follows the engine settings.
// If UBO batching is enabled on the engine and the material domain is not SURFACE, it
// turns on the UBO batching. Otherwise, it turns off the UBO batching.
DEFAULT,
NO_UBO_BATCHING,
UBO_BATCHING
};
FMaterialInstance* createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
UboBatchingMode batchingMode) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material,
const char* name) noexcept;
FScene* createScene() noexcept;
FView* createView() noexcept;
@@ -465,7 +446,6 @@ public:
void prepare();
void gc();
void submitFrame();
using ShaderContent = utils::FixedCapacityVector<uint8_t>;
@@ -670,7 +650,6 @@ private:
uint32_t mFlushCounter = 0;
UboManager* mUboManager = nullptr;
RootArenaScope::Arena mPerRenderPassArena;
HeapAllocatorArena mHeapAllocator;
+11 -13
View File
@@ -73,7 +73,6 @@ namespace filament {
using namespace backend;
using namespace filaflat;
using namespace utils;
using UboBatchingMode = FEngine::UboBatchingMode;
struct Material::BuilderDetails {
const void* mPayload = nullptr;
@@ -218,17 +217,16 @@ void FMaterial::terminate(FEngine& engine) {
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
if (mDefinition.materialDomain == MaterialDomain::SURFACE) {
// `variant` is only sensical for MaterialDomain::SURFACE
if (Variant::isValidDepthVariant(variant)) {
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
}
if (Variant::isSSRVariant(variant)) {
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
}
if (Variant::isValidDepthVariant(variant)) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
}
if (Variant::isSSRVariant(variant)) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
}
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
if (useVsmDescriptorSetLayout) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mDefinition.perViewDescriptorSetLayoutVsm;
}
return mDefinition.perViewDescriptorSetLayout;
@@ -282,14 +280,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
} else {
// but if we don't, just create an instance with all the default parameters
return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
return mEngine.createMaterialInstance(this, name);
}
}
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
mDefaultMaterialInstance =
mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
mDefaultMaterialInstance = mEngine.createMaterialInstance(this,
mDefinition.name.c_str());
mDefaultMaterialInstance->setDefaultInstance(true);
}
return mDefaultMaterialInstance;
+1 -1
View File
@@ -108,7 +108,7 @@ public:
backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) noexcept;
// Creates an instance of this material, specifying the batching mode.
// Create an instance of this material
FMaterialInstance* createInstance(const char* name) const noexcept;
bool hasParameter(const char* name) const noexcept;
+16 -48
View File
@@ -58,19 +58,15 @@ using namespace utils;
namespace filament {
using namespace backend;
using UboBatchingMode = FEngine::UboBatchingMode;
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
if (batchingMode != UboBatchingMode::DEFAULT) {
return batchingMode == UboBatchingMode::UBO_BATCHING;
}
return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept
: FMaterialInstance(engine, material, name,
engine.features.material.enable_material_instance_uniform_batching) {
}
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
UboBatchingMode batchingMode) noexcept
: mMaterial(material),
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name, bool useUboBatching) noexcept : mMaterial(material),
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK),
mShadowCulling(CullingMode::BACK),
@@ -80,12 +76,10 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false),
mIsDoubleSided(false),
mIsDefaultInstance(false),
mUseUboBatching(
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
mUseUboBatching(useUboBatching),
mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) {
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
<< "UBO batching is not enabled.";
FEngine::DriverApi& driver = engine.getDriverApi();
// even if the material doesn't have any parameters, we allocate a small UBO because it's
@@ -95,7 +89,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
if (mUseUboBatching) {
mUboData = BufferAllocator::UNALLOCATED;
engine.getUboManager()->manageMaterialInstance(this);
} else {
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::STATIC, ImmutableCString{ material->getName().c_str_safe() });
@@ -160,7 +153,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUseUboBatching(other->mUseUboBatching),
mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) {
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
FEngine::DriverApi& driver = engine.getDriverApi();
FMaterial const* const material = other->getMaterial();
@@ -168,7 +161,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
if (mUseUboBatching) {
mUboData = BufferAllocator::UNALLOCATED;
engine.getUboManager()->manageMaterialInstance(this);
} else {
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() });
@@ -201,8 +193,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
}
}
FMaterialInstance* FMaterialInstance::duplicate(FMaterialInstance const* other,
const char* name) noexcept {
FMaterialInstance* FMaterialInstance::duplicate(
FMaterialInstance const* other, const char* name) noexcept {
FMaterial const* const material = other->getMaterial();
FEngine& engine = material->getEngine();
return engine.createMaterialInstance(material, other, name);
@@ -213,10 +205,6 @@ FMaterialInstance::~FMaterialInstance() noexcept = default;
void FMaterialInstance::terminate(FEngine& engine) {
FEngine::DriverApi& driver = engine.getDriverApi();
mDescriptorSet.terminate(driver);
if (mUseUboBatching) {
engine.getUboManager()->unmanageMaterialInstance(this);
}
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
if (ubHandle){
driver.destroyBufferObject(*ubHandle);
@@ -250,21 +238,15 @@ void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driv
void FMaterialInstance::commit(FEngine& engine) const {
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
commit(engine.getDriverApi(), engine.getUboManager());
commit(engine.getDriverApi());
}
}
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
void FMaterialInstance::commit(FEngine::DriverApi& driver) const {
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean();
if (mUseUboBatching) {
assert_invariant(uboManager != nullptr);
if (!BufferAllocator::isValid(getAllocationId())) {
// The allocation hasn't happened yet, return.
return;
}
uboManager->updateSlot(driver, getAllocationId(), mUniforms.toBufferDescriptor(driver));
// TODO: update the content by `copyToMemoryMappedBuffer`
}
else {
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
@@ -289,10 +271,6 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
// TODO: eventually we should remove this in RELEASE builds
fixMissingSamplers();
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
// Commit descriptors if needed (e.g. when textures are updated,or the first time)
mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver);
}
@@ -435,13 +413,6 @@ const char* FMaterialInstance::getName() const noexcept {
// ------------------------------------------------------------------------------------------------
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
if (!mDescriptorSet.getHandle()) {
return;
}
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) {
std::call_once(mMissingSamplersFlag, [this] {
@@ -468,8 +439,7 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
return;
}
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL,
{ { mUboOffset }, driver });
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
}
void FMaterialInstance::assignUboAllocation(
@@ -479,10 +449,8 @@ void FMaterialInstance::assignUboAllocation(
assert_invariant(mUseUboBatching);
mUboData = id;
mUboOffset = offset;
if (BufferAllocator::isValid(id)) {
// Use dynamic offset during binding, so the offset here is always zero.
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, 0,
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, offset,
mUniforms.getSize());
}
}
+7 -5
View File
@@ -56,8 +56,11 @@ class FTexture;
class FMaterialInstance : public MaterialInstance {
public:
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
FEngine::UboBatchingMode batchingMode) noexcept;
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept;
// Use this constructor when you need to override the ubo batching flag for an individual MI.
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name, bool useUboBatching) noexcept;
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
@@ -72,7 +75,7 @@ public:
void commit(FEngine& engine) const;
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
void commit(FEngine::DriverApi& driver) const;
void use(FEngine::DriverApi& driver, Variant variant = {}) const;
@@ -284,7 +287,6 @@ private:
};
std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData;
BufferAllocator::allocation_size_t mUboOffset = 0;
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms;
@@ -306,7 +308,7 @@ private:
bool mHasScissor : 1;
bool mIsDoubleSided : 1;
bool mIsDefaultInstance : 1;
const bool mUseUboBatching : 1;
bool mUseUboBatching : 1;
TransparencyMode mTransparencyMode : 2;
uint64_t mMaterialSortingKey = 0;
-4
View File
@@ -452,8 +452,6 @@ void FRenderer::endFrame() {
mFrameInfoManager.endFrame(driver);
mFrameSkipper.submitFrame(driver);
engine.submitFrame();
driver.endFrame(mFrameId);
// gives the backend a chance to execute periodic tasks
@@ -589,8 +587,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
// happen with Renderer::beginFrame/endFrame.
renderInternal(view, true);
engine.submitFrame();
driver.endFrame(mFrameId);
// engine.flush() has already been called by renderInternal(), but we need an extra one
+92 -83
View File
@@ -48,7 +48,6 @@ void UboManager::FenceManager::track(DriverApi& driver, std::unordered_set<Alloc
void UboManager::FenceManager::reclaimCompletedResources(DriverApi& driver,
std::function<void(AllocationId)> const& onReclaimed) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
uint32_t signaledCount = 0;
bool seenSignaledFence = false;
@@ -116,7 +115,8 @@ UboManager::UboManager(DriverApi& driver, allocation_size_t defaultSlotSizeInByt
reallocate(driver, defaultTotalSizeInBytes);
}
void UboManager::beginFrame(DriverApi& driver) {
void UboManager::beginFrame(DriverApi& driver,
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// Check finished frames and decrement GPU count accordingly.
mFenceManager.reclaimCompletedResources(driver,
@@ -126,7 +126,7 @@ void UboManager::beginFrame(DriverApi& driver) {
mAllocator.releaseFreeSlots();
// Traverse all MIs and see which of them need slot allocation.
if (allocateOnDemand() == SUCCESS) {
if (allocateOnDemand(materialInstances) == SUCCESS) {
// No need to grow the buffer, so we can just map the buffer for writing and return.
mMemoryMappedBufferHandle = driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT,
"UboManager");
@@ -135,19 +135,25 @@ void UboManager::beginFrame(DriverApi& driver) {
}
// Calculate the required size and grow the Ubo.
const allocation_size_t requiredSize = calculateRequiredSize();
const allocation_size_t requiredSize = calculateRequiredSize(materialInstances);
reallocate(driver, requiredSize);
// Allocate slots for each MI on the new Ubo.
allocateAllInstances();
allocateAllInstances(materialInstances);
// Map the buffer so that we can write to it
mMemoryMappedBufferHandle =
driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT, "UboManager");
// Invalidate the migrated MIs, so that next commit() call must be triggered.
for (const auto* mi : mManagedInstances) {
mi->getUniformBuffer().invalidate();
for (const auto& materialInstance : materialInstances) {
materialInstance.second.forEach([](const FMaterialInstance* mi) {
if (!mi->isUsingUboBatching()) {
return;
}
mi->getUniformBuffer().invalidate();
});
}
}
@@ -158,16 +164,24 @@ void UboManager::finishBeginFrame(DriverApi& driver) {
}
}
void UboManager::endFrame(DriverApi& driver) {
void UboManager::endFrame(DriverApi& driver,
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
BufferAllocator& allocator = mAllocator;
std::unordered_set<AllocationId> allocationIds;
for (const auto* mi : mManagedInstances) {
const AllocationId id = mi->getAllocationId();
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
continue;
}
for (const auto& materialInstance : materialInstances) {
materialInstance.second.forEach([&allocator, &allocationIds](const FMaterialInstance* mi) {
if (!mi->isUsingUboBatching()) {
return;
}
mAllocator.acquireGpu(id);
allocationIds.insert(id);
const AllocationId id = mi->getAllocationId();
if (!BufferAllocator::isValid(id)) {
return;
}
allocator.acquireGpu(id);
allocationIds.insert(id);
});
}
mFenceManager.track(driver, std::move(allocationIds));
@@ -180,90 +194,76 @@ void UboManager::terminate(DriverApi& driver) {
void UboManager::updateSlot(DriverApi& driver, AllocationId id,
BufferDescriptor bufferDescriptor) const {
if (!mMemoryMappedBufferHandle) {
if (!mMemoryMappedBufferHandle)
return;
}
const allocation_size_t offset = mAllocator.getAllocationOffset(id);
driver.copyToMemoryMappedBuffer(mMemoryMappedBufferHandle, offset, std::move(bufferDescriptor));
}
void UboManager::manageMaterialInstance(FMaterialInstance* instance) {
mPendingInstances.insert(instance);
void UboManager::retireSlot(BufferAllocator::AllocationId id) {
if (!BufferAllocator::isValid(id))
return;
mAllocator.retire(id);
}
void UboManager::unmanageMaterialInstance(FMaterialInstance* materialInstance) {
AllocationId id = materialInstance->getAllocationId();
mPendingInstances.erase(materialInstance);
mManagedInstances.erase(materialInstance);
if (!BufferAllocator::isValid(id)) {
return;
UboManager::AllocationResult UboManager::allocateOnDemand(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances) {
// Collect all MIs that need allocation into two groups.
std::vector<FMaterialInstance*> newInstances;
std::vector<FMaterialInstance*> existingInstances;
for (const auto& [_, miList] : materialInstances) {
miList.forEach([&](FMaterialInstance* mi) {
if (!mi->isUsingUboBatching()) {
return;
}
if (BufferAllocator::isValid(mi->getAllocationId())) {
existingInstances.push_back(mi);
} else {
newInstances.push_back(mi);
}
});
}
mAllocator.retire(id);
materialInstance->assignUboAllocation(mUbHandle, BufferAllocator::UNALLOCATED, 0);
}
UboManager::AllocationResult UboManager::allocateOnDemand() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
bool reallocationNeeded = false;
// Pass 1: Allocate slots for new material instances (that don't have a slot yet).
for (auto* mi : mPendingInstances) {
mManagedInstances.insert(mi);
for (FMaterialInstance* mi : newInstances) {
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
// Even if the newId is not valid, we assign it to the MI so that the following process knows
// this material instance was not allocated successfully. Then we can calculate the new
// required UBO size properly.
mi->assignUboAllocation(mUbHandle, newId, newOffset);
if (!BufferAllocator::isValid(newId)) {
reallocationNeeded = true;
}
}
mPendingInstances.clear();
// Pass 2: Allocate slots for existing material instances that need to be orphaned.
for (auto* mi: mManagedInstances) {
if (!BufferAllocator::isValid(mi->getAllocationId())) {
continue;
}
// This instance doesn't need orphaning.
if (!mi->getUniformBuffer().isDirty() || !mAllocator.isLockedByGpu(mi->getAllocationId())) {
continue;
}
mAllocator.retire(mi->getAllocationId());
// If the space is already not sufficient, we don't need to give another try on allocation.
if (reallocationNeeded) {
mi->assignUboAllocation(mUbHandle, REALLOCATION_REQUIRED, 0);
continue;
}
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
// Even if the newId is not valid, we assign it to the MI so that the following process knows
// this material instance was not allocated successfully. Then we can calculate the new
// required UBO size properly.
mi->assignUboAllocation(mUbHandle, newId, newOffset);
if (!BufferAllocator::isValid(newId)) {
reallocationNeeded = true;
for (FMaterialInstance* mi : existingInstances) {
if (mi->getUniformBuffer().isDirty() && mAllocator.isLockedByGpu(mi->getAllocationId())) {
mAllocator.retire(mi->getAllocationId());
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
mi->assignUboAllocation(mUbHandle, newId, newOffset);
if (!BufferAllocator::isValid(newId)) {
reallocationNeeded = true;
}
}
}
return reallocationNeeded ? REALLOCATION_REQUIRED : SUCCESS;
}
void UboManager::allocateAllInstances() {
for (auto* mi: mManagedInstances) {
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
assert_invariant(BufferAllocator::isValid(newId));
mi->assignUboAllocation(mUbHandle, newId, newOffset);
void UboManager::allocateAllInstances(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances) {
for (const auto& [_, miList] : materialInstances) {
miList.forEach([this](FMaterialInstance* mi) {
if (!mi->isUsingUboBatching()) {
return;
}
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
assert_invariant(BufferAllocator::isValid(newId));
mi->assignUboAllocation(mUbHandle, newId, newOffset);
});
}
}
@@ -288,19 +288,28 @@ void UboManager::reallocate(DriverApi& driver, allocation_size_t requiredSize) {
BufferUsage::DYNAMIC | BufferUsage::SHARED_WRITE_BIT);
}
allocation_size_t UboManager::calculateRequiredSize() {
allocation_size_t UboManager::calculateRequiredSize(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances) {
BufferAllocator& allocator = mAllocator;
allocation_size_t newBufferSize = 0;
for (const auto* mi: mManagedInstances) {
const AllocationId allocationId = mi->getAllocationId();
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
// For MIs whose parameters have been updated, aside from the slot it is being
// occupied by the GPU, we need to preserve an additional slot for it.
newBufferSize += 2 * mAllocator.alignUp(mi->getUniformBuffer().getSize());
} else {
newBufferSize += mAllocator.alignUp(mi->getUniformBuffer().getSize());
}
for (const auto& materialInstance: materialInstances) {
materialInstance.second.forEach([&newBufferSize, &allocator](const FMaterialInstance* mi) {
if (!mi->isUsingUboBatching()) {
return;
}
const AllocationId allocationId = mi->getAllocationId();
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
// For MIs whose parameters have been updated, aside from the slot it is being
// occupied by the GPU, we need to preserve an additional slot for it.
newBufferSize += 2 * allocator.alignUp(mi->getUniformBuffer().getSize());
} else {
newBufferSize += allocator.alignUp(mi->getUniformBuffer().getSize());
}
});
}
return mAllocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
return allocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
}
} // namespace filament
+18 -22
View File
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_DETAILS_UBOMANAGER_H
#define TNT_FILAMENT_DETAILS_UBOMANAGER_H
#include "ResourceList.h"
#include "backend/DriverApiForward.h"
#include "details/BufferAllocator.h"
@@ -28,8 +29,6 @@
#include <unordered_set>
#include <vector>
class UboManagerTest;
namespace filament {
class FMaterial;
@@ -97,8 +96,9 @@ public:
// instances with modified uniforms).
// 3. Reallocating a larger shared UBO if the current one is insufficient.
// 4. Mapping the shared UBO into CPU-accessible memory to prepare for uniform data writes.
// Note that it must happen before committing all MIs.
void beginFrame(backend::DriverApi& driver);
void beginFrame(backend::DriverApi& driver,
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances);
// Unmap the buffer here
void finishBeginFrame(backend::DriverApi& driver);
@@ -106,31 +106,23 @@ public:
// Create a fence and associate it with a set of allocation ids.
// The gpuUseCount of these allocations will be incremented, and they will be decremented
// After the corresponding frame has been done.
void endFrame(backend::DriverApi& driver);
void endFrame(backend::DriverApi& driver,
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances);
void terminate(backend::DriverApi& driver);
void updateSlot(backend::DriverApi& driver, BufferAllocator::AllocationId id,
backend::BufferDescriptor bufferDescriptor) const;
// Call this to register a new material instance to UboManager.
void manageMaterialInstance(FMaterialInstance* instance);
// Call this when a material instance is destroyed.
void unmanageMaterialInstance(FMaterialInstance* materialInstance);
// Call this when a material instance is no longer holding a slot. e.g. it is destroyed.
void retireSlot(BufferAllocator::AllocationId id);
// Returns the size of the actual UBO. Note that when there's allocation failed, it will be
// reallocated to a bigger size at the next frame.
[[nodiscard]] BufferAllocator::allocation_size_t getTotalSize() const noexcept;
// For testing
[[nodiscard]] backend::MemoryMappedBufferHandle getMemoryMappedBufferHandle() const noexcept {
return mMemoryMappedBufferHandle;
}
private:
friend class ::UboManagerTest;
constexpr static float BUFFER_SIZE_GROWTH_MULTIPLIER = 1.5f;
enum AllocationResult {
@@ -142,19 +134,23 @@ private:
[[nodiscard]] BufferAllocator::allocation_size_t getAllocationOffset(
BufferAllocator::AllocationId id) const;
AllocationResult allocateOnDemand();
AllocationResult allocateOnDemand(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances);
void allocateAllInstances();
void allocateAllInstances(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances);
void reallocate(backend::DriverApi& driver, BufferAllocator::allocation_size_t requiredSize);
BufferAllocator::allocation_size_t calculateRequiredSize();
BufferAllocator::allocation_size_t calculateRequiredSize(
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
materialInstances);
backend::Handle<backend::HwBufferObject> mUbHandle;
backend::MemoryMappedBufferHandle mMemoryMappedBufferHandle;
BufferAllocator::allocation_size_t mUboSize{};
std::unordered_set<FMaterialInstance*> mPendingInstances;
std::unordered_set<FMaterialInstance*> mManagedInstances;
FenceManager mFenceManager;
BufferAllocator mAllocator;
+42 -34
View File
@@ -285,45 +285,53 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
// calculate buffer sizes
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
auto shouldCreateBuffer = [this](size_t attributeIndex) {
const uint8_t slot = mAttributes[attributeIndex].buffer;
return mDeclaredAttributes[attributeIndex] && slot != Attribute::BUFFER_UNUSED &&
!mBufferObjects[slot];
};
auto updateBufferSize = [&bufferSizes, this](size_t attributeIndex) {
const uint32_t offset = mAttributes[attributeIndex].offset;
const uint8_t stride = mAttributes[attributeIndex].stride;
const uint8_t slot = mAttributes[attributeIndex].buffer;
const size_t end = offset + mVertexCount * stride;
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
bufferSizes[slot] = std::max(bufferSizes[slot], end);
};
if (!mBufferObjectsEnabled) {
#pragma nounroll
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
if (shouldCreateBuffer(i)) {
updateBufferSize(i);
#pragma nounroll
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
if (mDeclaredAttributes[i]) {
const uint32_t offset = mAttributes[i].offset;
const uint8_t stride = mAttributes[i].stride;
const uint8_t slot = mAttributes[i].buffer;
const size_t end = offset + mVertexCount * stride;
if (slot != Attribute::BUFFER_UNUSED) {
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
bufferSizes[slot] = std::max(bufferSizes[slot], end);
}
}
} else if (mAdvancedSkinningEnabled) {
// For advanced skinning mode, only relevant buffers (BONE_INDICES & BONE_WEIGHTS) are
// created. We manually populated the relevant attributes for those buffers above.
updateBufferSize(BONE_INDICES);
updateBufferSize(BONE_WEIGHTS);
}
// create buffers
for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) {
if (bufferSizes[i] == 0 || mBufferObjects[i]) {
continue;
if (!mBufferObjectsEnabled) {
// If buffer objects are not enabled at the API level, then we create them internally.
#pragma nounroll
for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) {
size_t const i = mAttributes[index].buffer;
if (i != Attribute::BUFFER_UNUSED) {
assert_invariant(bufferSizes[i] > 0);
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
utils::ImmutableCString{ builder.getName() });
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
}
}
} else {
// in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves,
// so we have to set the corresponding buffer objects.
if (mAdvancedSkinningEnabled) {
for (auto const index : { BONE_INDICES, BONE_WEIGHTS }) {
size_t const i = mAttributes[index].buffer;
assert_invariant(i != Attribute::BUFFER_UNUSED);
assert_invariant(bufferSizes[i] > 0);
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
utils::ImmutableCString{ builder.getName() });
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
}
}
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
utils::ImmutableCString{ builder.getName() });
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ void PostProcessDescriptorSet::init(FEngine& engine) noexcept {
// create the descriptor-set layout
mDescriptorSetLayout = filament::DescriptorSetLayout{
engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), descriptor_sets::getDepthVariantLayout() };
engine.getDriverApi(), descriptor_sets::getPostProcessLayout() };
// create the descriptor-set from the layout
mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout };
+1 -4
View File
@@ -50,17 +50,14 @@ if (TNT_DEV)
test_BufferAllocatorStress.cpp
test_CircularQueue.cpp
test_FenceManager.cpp
test_UboManager.cpp
filament_test_exposure.cpp
filament_rendering_test.cpp
filament_bimap_test.cpp
filament_framegraph_test.cpp
filament_test.cpp
${RESGEN_SOURCE})
filament_test.cpp)
target_link_libraries(test_${TARGET} PRIVATE filament gtest)
target_compile_options(test_${TARGET} PRIVATE ${COMPILER_FLAGS})
target_include_directories(test_${TARGET} PRIVATE ${RESOURCE_DIR})
set_target_properties(test_${TARGET} PROPERTIES FOLDER Tests)
add_executable(test_depth depth_test.cpp)
-366
View File
@@ -1,366 +0,0 @@
/*
* Copyright (C) 2025 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.
*/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "MockDriver.h"
#include "details/MaterialInstance.h"
#include "details/UboManager.h"
#include <private/backend/CommandBufferQueue.h>
#include <private/backend/CommandStream.h>
#include <private/backend/Driver.h>
#include "filament_test_resources.h"
namespace {
using namespace filament;
using namespace backend;
using ::testing::NiceMock;
using AllocationId = BufferAllocator::AllocationId;
using allocation_size_t = BufferAllocator::allocation_size_t;
} // anonymous namespace
class UboManagerTest : public ::testing::Test {
protected:
static constexpr size_t MIN_COMMAND_BUFFERS_SIZE = 1 * 1024 * 1024;
static constexpr size_t COMMAND_BUFFERS_SIZE = 3 * MIN_COMMAND_BUFFERS_SIZE;
static constexpr BufferAllocator::allocation_size_t DEFAULT_SLOT_SIZE = 64;
static constexpr BufferAllocator::allocation_size_t DEFAULT_TOTAL_SIZE = 1024;
UboManagerTest()
: mCommandBufferQueue(MIN_COMMAND_BUFFERS_SIZE, COMMAND_BUFFERS_SIZE, false),
mCommandStream(mMockDriver, mCommandBufferQueue.getCircularBuffer()),
mDriverApi(mCommandStream),
// The constructor will call reallocate, which calls createBufferObject.
// MockDriver's default ...S() implementation returns an incrementing handle.
// So, the first handle will be 1.
mUboManager(mDriverApi, DEFAULT_SLOT_SIZE, DEFAULT_TOTAL_SIZE),
mPendingInstances(mUboManager.mPendingInstances),
mManagedInstances(mUboManager.mManagedInstances),
mUbHandle(mUboManager.mUbHandle),
mAllocator(mUboManager.mAllocator) {
mEngine = Engine::Builder()
.feature("material.enable_material_instance_uniform_batching", true)
.backend(Backend::NOOP)
.build();
mMaterial = Material::Builder()
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_DATA,
FILAMENT_TEST_RESOURCES_TEST_MATERIAL_SIZE)
.build(*mEngine);
}
void TearDown() override {
mEngine->destroy(mMaterial);
Engine::destroy(&mEngine);
}
// The engine is only for creating materials/material instances, we're not using the UboManager
// inside for testing.
Engine* mEngine = nullptr;
NiceMock<MockDriver> mMockDriver;
CommandBufferQueue mCommandBufferQueue;
CommandStream mCommandStream;
DriverApi& mDriverApi;
UboManager mUboManager;
Material const* mMaterial;
std::unordered_set<FMaterialInstance*>& mPendingInstances;
std::unordered_set<FMaterialInstance*>& mManagedInstances;
Handle<HwBufferObject>& mUbHandle;
BufferAllocator& mAllocator;
};
TEST_F(UboManagerTest, InitialState) {
EXPECT_EQ(mUboManager.getTotalSize(), DEFAULT_TOTAL_SIZE);
EXPECT_EQ(mMockDriver.nextFakeHandle, 2);
EXPECT_NE(mUbHandle.getId(), HandleBase::nullid);
}
TEST_F(UboManagerTest, BeginFrameWithoutReallocate) {
BufferAllocator::allocation_size_t originalBufferSize = mUboManager.getTotalSize();
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
EXPECT_EQ(mi1->getAllocationId(), BufferAllocator::UNALLOCATED);
ASSERT_TRUE(mi1->isUsingUboBatching());
// The mi1 should be put in the pending list.
mUboManager.manageMaterialInstance(mi1);
EXPECT_TRUE(mPendingInstances.contains(mi1));
EXPECT_FALSE(mManagedInstances.contains(mi1));
mUboManager.beginFrame(mDriverApi);
// The mi1 should be moved to managed list after beginFrame.
EXPECT_FALSE(mPendingInstances.contains(mi1));
EXPECT_TRUE(mManagedInstances.contains(mi1));
// Should have allocation after beginFrame.
EXPECT_TRUE(BufferAllocator::isValid(mi1->getAllocationId()));
// Reallocation is not triggered under this case.
EXPECT_EQ(mUboManager.getTotalSize(), originalBufferSize);
EXPECT_NE(mUboManager.getMemoryMappedBufferHandle().getId(), HandleBase::nullid);
mUboManager.finishBeginFrame(mDriverApi);
EXPECT_EQ(mUboManager.getMemoryMappedBufferHandle().getId(), HandleBase::nullid);
mUboManager.endFrame(mDriverApi);
EXPECT_TRUE(mAllocator.isLockedByGpu(mi1->getAllocationId()));
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
// by ourselves.
mUboManager.unmanageMaterialInstance(mi1);
EXPECT_FALSE(mPendingInstances.contains(mi1));
EXPECT_FALSE(mManagedInstances.contains(mi1));
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
}
TEST_F(UboManagerTest, BeginFrameWithReallocate) {
const allocation_size_t originalBufferSize = mUboManager.getTotalSize();
const Handle<HwBufferObject> originalBufferHandle = mUbHandle;
// Create enough material instances to trigger a reallocation.
constexpr size_t numInstances = (DEFAULT_TOTAL_SIZE / DEFAULT_SLOT_SIZE) + 1;
std::vector<FMaterialInstance*> instances;
instances.reserve(numInstances);
for (size_t i = 0; i < numInstances; ++i) {
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
instances.push_back(mi);
mUboManager.manageMaterialInstance(mi);
}
// Before beginFrame, all instances should be pending.
EXPECT_EQ(mPendingInstances.size(), numInstances);
EXPECT_TRUE(mManagedInstances.empty());
mUboManager.beginFrame(mDriverApi);
// After beginFrame, reallocation should have occurred.
EXPECT_NE(mUbHandle.getId(), originalBufferHandle.getId());
EXPECT_GT(mUboManager.getTotalSize(), originalBufferSize);
// All instances should now be managed and have valid allocations.
EXPECT_TRUE(mPendingInstances.empty());
EXPECT_EQ(mManagedInstances.size(), numInstances);
for (const auto* mi: instances) {
EXPECT_TRUE(mManagedInstances.contains(const_cast<FMaterialInstance*>(mi)));
EXPECT_TRUE(BufferAllocator::isValid(mi->getAllocationId()));
}
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.terminate(mDriverApi);
for (auto* mi: instances) {
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
// by ourselves.
mUboManager.unmanageMaterialInstance(mi);
mEngine->destroy(mi);
}
}
TEST_F(UboManagerTest, RecycleSlot) {
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
mUboManager.manageMaterialInstance(mi1);
// Frame 1: mi1 gets an allocation.
mUboManager.beginFrame(mDriverApi);
const AllocationId mi1AllocationId = mi1->getAllocationId();
const allocation_size_t mi1AllocationOffset =
mAllocator.getAllocationOffset(mi1AllocationId);
EXPECT_TRUE(BufferAllocator::isValid(mi1AllocationId));
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.endFrame(mDriverApi); // Locks mi1's allocation.
// Now, unmanage mi1. The slot should be retired but not yet released.
mUboManager.unmanageMaterialInstance(mi1);
EXPECT_TRUE(mAllocator.isLockedByGpu(mi1AllocationId));
// Frame 2: The slot for mi1 is still locked by the GPU.
// We expect getFenceStatus to be called for the fence from frame 1.
// We'll mock it to return TIMEOUT_EXPIRED, so the resource is not reclaimed.
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED));
mUboManager.beginFrame(mDriverApi);
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.endFrame(mDriverApi);
// Frame 3: Now, we'll simulate that the fence from frame 1 has signaled.
// The resource for mi1 should be reclaimed.
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::CONDITION_SATISFIED));
auto mi2 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
mUboManager.manageMaterialInstance(mi2);
mUboManager.beginFrame(mDriverApi);
// mi2 should now have a valid allocation, and it should reuse the slot from mi1.
EXPECT_TRUE(BufferAllocator::isValid(mi2->getAllocationId()));
EXPECT_EQ(mAllocator.getAllocationOffset(mi2->getAllocationId()), mi1AllocationOffset);
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.unmanageMaterialInstance(mi2);
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
mEngine->destroy(mi2);
}
TEST_F(UboManagerTest, OrphanSlot) {
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
mUboManager.manageMaterialInstance(mi1);
// Frame 1: mi1 gets an allocation.
mUboManager.beginFrame(mDriverApi);
const AllocationId alloc1 = mi1->getAllocationId();
EXPECT_TRUE(BufferAllocator::isValid(alloc1));
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.endFrame(mDriverApi); // Locks alloc1.
// Frame 2: Mark the instance as dirty and begin a new frame.
// This should trigger orphaning.
mi1->getUniformBuffer().invalidate();
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED));
mUboManager.beginFrame(mDriverApi);
const AllocationId alloc2 = mi1->getAllocationId();
EXPECT_TRUE(BufferAllocator::isValid(alloc2));
EXPECT_NE(alloc1, alloc2); // Should have a new allocation.
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.endFrame(mDriverApi); // Locks alloc2.
// Frame 3: The fence for alloc1 should now be signaled.
EXPECT_CALL(mMockDriver, getFenceStatus(_))
.WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED)) // For alloc2's fence
.WillOnce(Return(FenceStatus::CONDITION_SATISFIED)); // For alloc1's fence
mUboManager.beginFrame(mDriverApi);
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.unmanageMaterialInstance(mi1);
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
}
TEST_F(UboManagerTest, DoubleManage) {
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
mUboManager.manageMaterialInstance(mi1);
EXPECT_EQ(mPendingInstances.size(), 1);
// Managing the same instance again should be a no-op.
mUboManager.manageMaterialInstance(mi1);
EXPECT_EQ(mPendingInstances.size(), 1);
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
}
TEST_F(UboManagerTest, ManageAndUnmanageBeforeBeginFrame) {
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
mUboManager.manageMaterialInstance(mi1);
EXPECT_TRUE(mPendingInstances.contains(mi1));
mUboManager.unmanageMaterialInstance(mi1);
EXPECT_FALSE(mPendingInstances.contains(mi1));
// After beginFrame, the instance should not be in any list.
mUboManager.beginFrame(mDriverApi);
EXPECT_FALSE(mPendingInstances.contains(mi1));
EXPECT_FALSE(mManagedInstances.contains(mi1));
EXPECT_EQ(mi1->getAllocationId(), BufferAllocator::UNALLOCATED);
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
}
TEST_F(UboManagerTest, UnmanageUnmanaged) {
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
// Unmanaging an instance that was never managed should not cause any issues.
mUboManager.unmanageMaterialInstance(mi1);
EXPECT_FALSE(mPendingInstances.contains(mi1));
EXPECT_FALSE(mManagedInstances.contains(mi1));
mUboManager.terminate(mDriverApi);
mEngine->destroy(mi1);
}
TEST_F(UboManagerTest, AllAllocationsLockedAfterEndFrame) {
constexpr size_t numInstances = 5;
std::vector<FMaterialInstance*> instances;
instances.reserve(numInstances);
for (size_t i = 0; i < numInstances; ++i) {
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
instances.push_back(mi);
mUboManager.manageMaterialInstance(mi);
}
mUboManager.beginFrame(mDriverApi);
mUboManager.finishBeginFrame(mDriverApi);
mUboManager.endFrame(mDriverApi);
for (const auto* mi: instances) {
EXPECT_TRUE(mAllocator.isLockedByGpu(mi->getAllocationId()));
}
mUboManager.terminate(mDriverApi);
for (auto* mi: instances) {
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
// by ourselves.
mUboManager.unmanageMaterialInstance(mi);
mEngine->destroy(mi);
}
}
TEST_F(UboManagerTest, AllAllocationsLockedAfterEndFrameWithInvalidIdInBetween) {
constexpr size_t numInstances = 5;
std::vector<FMaterialInstance*> instances;
instances.reserve(numInstances);
for (size_t i = 0; i < numInstances; ++i) {
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
instances.push_back(mi);
mUboManager.manageMaterialInstance(mi);
}
mUboManager.beginFrame(mDriverApi);
mUboManager.finishBeginFrame(mDriverApi);
// It should rarely happen, but we want to make sure all other instances are locked properly.
instances[2]->assignUboAllocation(mUbHandle, BufferAllocator::REALLOCATION_REQUIRED, 0);
mUboManager.endFrame(mDriverApi);
for (const auto* mi: instances) {
if (BufferAllocator::isValid(mi->getAllocationId())) {
EXPECT_TRUE(mAllocator.isLockedByGpu(mi->getAllocationId()));
}
}
mUboManager.terminate(mDriverApi);
for (auto* mi: instances) {
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
// by ourselves.
mUboManager.unmanageMaterialInstance(mi);
mEngine->destroy(mi);
}
}
// TODO: Add more tests for the beginFrame flow
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.67.1"
spec.version = "1.67.0"
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.67.1/filament-v1.67.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.0/filament-v1.67.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,9 +28,9 @@
namespace filament::descriptor_sets {
backend::DescriptorSetLayout const& getPostProcessLayout() noexcept;
backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept;
backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept;
backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
+16 -3
View File
@@ -40,7 +40,12 @@ namespace filament::descriptor_sets {
using namespace backend;
// used to generate shadow-maps, structure and postfx passes
// used for post-processing passes
static constexpr std::initializer_list<DescriptorSetLayoutBinding> postProcessDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
};
// used to generate shadow-maps
static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
};
@@ -137,6 +142,10 @@ static const std::unordered_map<
{{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL }
};
// used for post-processing passes
static DescriptorSetLayout const postProcessDescriptorSetLayout{ utils::StaticString("postProcess"),
postProcessDescriptorSetLayoutList };
// used to generate shadow-maps
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
@@ -154,6 +163,10 @@ static DescriptorSetLayout perRenderableDescriptorSetLayout = {
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
};
DescriptorSetLayout const& getPostProcessLayout() noexcept {
return postProcessDescriptorSetLayout;
}
DescriptorSetLayout const& getDepthVariantLayout() noexcept {
return depthVariantDescriptorSetLayout;
}
@@ -267,10 +280,10 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
return layout;
}
case MaterialDomain::POST_PROCESS:
return depthVariantDescriptorSetLayout;
return postProcessDescriptorSetLayout;
case MaterialDomain::COMPUTE:
// TODO: what's the layout for compute?
return depthVariantDescriptorSetLayout;
return postProcessDescriptorSetLayout;
}
}
+3 -3
View File
@@ -129,9 +129,9 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
DescriptorSetLayout layout;
layout.bindings.reserve(1 + samplers.size());
layout.bindings.push_back(DescriptorSetLayoutBinding{ DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::DYNAMIC_OFFSET, 0 });
layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
for (auto const& sampler: samplers) {
DescriptorSetLayoutBinding layoutBinding{
@@ -225,7 +225,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(0);
f.writeUint8(uint8_t(DescriptorFlags::DYNAMIC_OFFSET));
f.writeUint8(uint8_t(DescriptorFlags::NONE));
f.writeUint16(0);
// all the material's sampler descriptors
+3 -3
View File
@@ -281,7 +281,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
}
text << " "
<< setw(alignment) << fieldName.c_str_safe()
<< setw(alignment) << fieldName.c_str()
<< setw(shortAlignment) << toString(UniformType(fieldType))
<< arraySizeToString(fieldSize)
<< setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -325,7 +325,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
}
text << " "
<< setw(alignment) << fieldName.c_str_safe()
<< setw(alignment) << fieldName.c_str()
<< setw(shortAlignment) << +fieldBinding
<< setw(shortAlignment) << toString(SamplerType(fieldType))
<< setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -364,7 +364,7 @@ static bool printConstantInfo(ostream& text, const ChunkContainer& container) {
}
text << " "
<< setw(alignment) << fieldName.c_str_safe()
<< setw(alignment) << fieldName.c_str()
<< setw(shortAlignment) << toString(ConstantType(fieldType))
<< endl;
}
+4 -6
View File
@@ -302,16 +302,14 @@ vec4 evaluateLights(const MaterialInputs material) {
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
}
vec3 emissive(const MaterialInputs material, float alpha) {
void addEmissive(const MaterialInputs material, inout vec4 color) {
#if defined(MATERIAL_HAS_EMISSIVE)
highp vec4 emissive = material.emissive;
highp float attenuation = mix(1.0, getExposure(), emissive.w);
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
attenuation *= alpha;
attenuation *= color.a;
#endif
return emissive.rgb * attenuation;
#else
return vec3(0.0);
color.rgb += emissive.rgb * attenuation;
#endif
}
@@ -323,7 +321,7 @@ vec3 emissive(const MaterialInputs material, float alpha) {
*/
vec4 evaluateMaterial(const MaterialInputs material) {
vec4 color = evaluateLights(material);
color.rgb += emissive(material, color.a);
addEmissive(material, color);
// kill +inf generated by strong highlights
color.rgb = min(color.rgb, MEDIUMP_FLT_MAX);
return color;
+1 -1
View File
@@ -1,5 +1,5 @@
---
Language: Cpp
BasedOnStyle: Google
PointerAlignment: Left
...
-37
View File
@@ -1,37 +0,0 @@
---
Checks: >
abseil-*,
bugprone-*,
clang-analyzer-*,
cppcoreguidelines-*,
google-*,
misc-*,
performance-*,
readability-*,
-clang-analyzer-deadcode*,
-clang-analyzer-optin*,
-readability-identifier-length
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none
CheckOptions:
llvm-else-after-return.WarnOnConditionVariables: 'false'
modernize-loop-convert.MinConfidence: reasonable
modernize-replace-auto-ptr.IncludeStyle: llvm
cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false'
google-readability-namespace-comments.ShortNamespaceLines: '10'
cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false'
cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU'
google-readability-braces-around-statements.ShortStatementLines: '1'
cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true'
google-readability-namespace-comments.SpacesBeforeComments: '2'
modernize-loop-convert.MaxCopySize: '16'
modernize-pass-by-value.IncludeStyle: llvm
modernize-use-nullptr.NullMacros: 'NULL'
llvm-qualified-auto.AddConstToQualified: 'false'
modernize-loop-convert.NamingStyle: CamelCase
llvm-else-after-return.WarnOnUnfixable: 'false'
google-readability-function-size.StatementThreshold: '800'
...
-1
View File
@@ -1 +0,0 @@
.*third_party/.*
@@ -1,32 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**System**
Which OS, compiler, and compiler version are you using:
- OS:
- Compiler and version:
**To reproduce**
Steps to reproduce the behavior:
1. sync to commit ...
2. cmake/bazel...
3. make ...
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FR]"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-12
View File
@@ -1,12 +0,0 @@
if ! bazel version; then
arch=$(uname -m)
if [ "$arch" == "aarch64" ]; then
arch="arm64"
fi
echo "Downloading $arch Bazel binary from GitHub releases."
curl -L -o $HOME/bin/bazel --create-dirs "https://github.com/bazelbuild/bazel/releases/download/8.2.0/bazel-8.2.0-linux-$arch"
chmod +x $HOME/bin/bazel
else
# Bazel is installed for the correct architecture
exit 0
fi
-35
View File
@@ -1,35 +0,0 @@
#!/usr/bin/env bash
set -e
# Checkout LLVM sources
git clone --filter=blob:none --depth=1 --branch llvmorg-19.1.6 --no-checkout https://github.com/llvm/llvm-project.git llvm-project
cd llvm-project
git sparse-checkout set --cone
git checkout llvmorg-19.1.6
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
cd ..
## Setup libc++ options
if [ -z "$BUILD_32_BITS" ]; then
export BUILD_32_BITS=OFF && echo disabling 32 bit build
fi
## Build and install libc++ (Use unstable ABI for better sanitizer coverage)
mkdir llvm-build && cd llvm-build
cmake -GNinja \
-DCMAKE_C_COMPILER=${CC} \
-DCMAKE_CXX_COMPILER=${CXX} \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=/usr \
-DLIBCXX_ABI_UNSTABLE=OFF \
-DLLVM_USE_SANITIZER=${LIBCXX_SANITIZER} \
-DLLVM_BUILD_32_BITS=${BUILD_32_BITS} \
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLIBCXX_INCLUDE_TESTS=OFF \
-DLIBCXX_INCLUDE_BENCHMARKS=OFF \
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
../llvm-project/runtimes/
cmake --build . -- cxx cxxabi
cd ..
-37
View File
@@ -1,37 +0,0 @@
name: bazel
on:
push: {}
pull_request: {}
env:
CMAKE_GENERATOR: Ninja
jobs:
build_and_test_default:
name: bazel.${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: mount bazel cache
uses: actions/cache@v4
env:
cache-name: bazel-cache
with:
path: "~/.cache/bazel"
key: ${{ env.cache-name }}-${{ matrix.os }}-${{ github.ref }}
restore-keys: |
${{ env.cache-name }}-${{ matrix.os }}-main
- name: build
run: |
bazel build //:benchmark //:benchmark_main //test/...
- name: test
run: |
bazel test --test_output=all //test/...
@@ -1,49 +0,0 @@
name: build-and-test-min-cmake
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CMAKE_GENERATOR: Ninja
jobs:
job:
name: ${{ matrix.os }}.min-cmake
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: lukka/get-cmake@latest
with:
cmakeVersion: 3.13.0
- name: create build environment
run: cmake -E make_directory ${{ runner.workspace }}/_build
- name: setup cmake initial cache
run: touch compiler-cache.cmake
- name: configure cmake
env:
CXX: ${{ matrix.compiler }}
shell: bash
working-directory: ${{ runner.workspace }}/_build
run: >
cmake -C ${{ github.workspace }}/compiler-cache.cmake
$GITHUB_WORKSPACE
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
-DCMAKE_CXX_VISIBILITY_PRESET=hidden
-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON
- name: build
shell: bash
working-directory: ${{ runner.workspace }}/_build
run: cmake --build .
@@ -1,54 +0,0 @@
name: build-and-test-perfcounters
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CMAKE_GENERATOR: Ninja
jobs:
job:
# TODO(dominic): Extend this to include compiler and set through env: CC/CXX.
name: ${{ matrix.os }}.${{ matrix.build_type }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
build_type: ['Release', 'Debug']
steps:
- uses: actions/checkout@v4
- name: install libpfm
run: |
sudo apt update
sudo apt -y install libpfm4-dev
- name: create build environment
run: cmake -E make_directory ${{ runner.workspace }}/_build
- name: configure cmake
shell: bash
working-directory: ${{ runner.workspace }}/_build
run: >
cmake $GITHUB_WORKSPACE
-DBENCHMARK_ENABLE_LIBPFM=1
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
- name: build
shell: bash
working-directory: ${{ runner.workspace }}/_build
run: cmake --build . --config ${{ matrix.build_type }}
# Skip testing, for now. It seems perf_event_open does not succeed on the
# hosting machine, very likely a permissions issue.
# TODO(mtrofin): Enable test.
# - name: test
# shell: bash
# working-directory: ${{ runner.workspace }}/_build
# run: ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure
@@ -1,151 +0,0 @@
name: build-and-test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CMAKE_GENERATOR: Ninja
jobs:
# TODO: add 32-bit builds (g++ and clang++) for ubuntu
# (requires g++-multilib and libc6:i386)
# TODO: add coverage build (requires lcov)
# TODO: add clang + libc++ builds for ubuntu
job:
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.compiler }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, macos-latest]
build_type: ['Release', 'Debug']
compiler: ['g++', 'clang++']
lib: ['shared', 'static']
steps:
- name: Install dependencies (macos)
if: runner.os == 'macOS'
run: brew install ninja
- uses: actions/checkout@v4
- name: build
uses: threeal/cmake-action@v2.1.0
with:
build-dir: ${{ runner.workspace }}/_build
cxx-compiler: ${{ matrix.compiler }}
options: |
BENCHMARK_DOWNLOAD_DEPENDENCIES=ON
BUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
CMAKE_BUILD_TYPE=${{ matrix.build_type }}
CMAKE_CXX_COMPILER=${{ matrix.compiler }}
CMAKE_CXX_VISIBILITY_PRESET=hidden
CMAKE_VISIBILITY_INLINES_HIDDEN=ON
- name: test
shell: bash
working-directory: ${{ runner.workspace }}/_build
run: ctest -C ${{ matrix.build_type }} -VV
msvc:
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.msvc }}
runs-on: ${{ matrix.os }}
defaults:
run:
shell: powershell
strategy:
fail-fast: false
matrix:
msvc:
- VS-16-2019
- VS-17-2022
build_type:
- Debug
- Release
lib:
- shared
- static
include:
- msvc: VS-16-2019
os: windows-2019
generator: 'Visual Studio 16 2019'
- msvc: VS-17-2022
os: windows-2022
generator: 'Visual Studio 17 2022'
steps:
- uses: actions/checkout@v4
- uses: lukka/get-cmake@latest
- name: configure cmake
run: >
cmake -S . -B ${{ runner.workspace }}/_build/
-G "${{ matrix.generator }}"
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
-DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
- name: build
run: cmake --build ${{ runner.workspace }}/_build/ --config ${{ matrix.build_type }}
- name: test
run: ctest --test-dir ${{ runner.workspace }}/_build/ -C ${{ matrix.build_type }} -VV
msys2:
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.msys2.msystem }}
runs-on: ${{ matrix.os }}
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
os: [ windows-latest ]
msys2:
- { msystem: MINGW64, arch: x86_64, family: GNU, compiler: g++ }
- { msystem: CLANG64, arch: x86_64, family: LLVM, compiler: clang++ }
- { msystem: UCRT64, arch: x86_64, family: GNU, compiler: g++ }
build_type:
- Debug
- Release
lib:
- shared
- static
steps:
- name: setup msys2
uses: msys2/setup-msys2@v2
with:
cache: false
msystem: ${{ matrix.msys2.msystem }}
update: true
install: >-
git
base-devel
pacboy: >-
gcc:p
clang:p
cmake:p
ninja:p
- uses: actions/checkout@v4
# NOTE: we can't use cmake actions here as we need to do everything in msys2 shell.
- name: configure cmake
env:
CXX: ${{ matrix.msys2.compiler }}
run: >
cmake -S . -B _build/
-GNinja
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
-DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
- name: build
run: cmake --build _build/ --config ${{ matrix.build_type }}
- name: test
working-directory: _build
run: ctest -C ${{ matrix.build_type }} -VV
@@ -1,19 +0,0 @@
name: clang-format-lint
on:
push: {}
pull_request: {}
env:
CMAKE_GENERATOR: Ninja
jobs:
job:
name: check-clang-format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DoozyX/clang-format-lint-action@v0.18.2
with:
source: './include/benchmark ./src ./test'
clangFormatVersion: 18

Some files were not shown because too many files have changed in this diff Show More