Compare commits

..
Author SHA1 Message Date
Run Yu 5f14ebba87 Use MapAsync and AllowProcessEvents for callback 2025-11-19 11:19:27 -05:00
Doris Wu aa4e2c56b5 buffer update opt: Skip releaseFreeSlots when no frees are pending (#9437) 2025-11-15 10:23:27 +08:00
Powei Feng aa4f1910b8 android: add static annotation for Utils.init() (#9435)
This allows for static call from java, e.g.:

```
static {
    Utils.init();
}
```
2025-11-13 22:01:49 +00:00
Mathias Agopian 223a4b18a8 Fix several problems with RenderPass descriptor sets (#9431)
The root of the problem is that in the main rendering loop we need
to set the correct per-view descriptor-set based on the material and
variants.

But we have two cases, either the descriptor set is always constant, 
which is the case with ssr, structure, shadows and postfx passes, or
it need to be dynamically changed based on the material & variant.

In the 2nd case, where was a problem where the postfx descriptor set
could be used, which is wrong (e.g. while rendering shadows we need
the shadow UBO, even with a postfx material), and would corrupt the
correct descriptor set, which would never be set back.

Another issue was related to running a custom command, it could 
change the state without updating the local copy, causing corruptions
or validation errors.

In this CL we:
- use the same descriptorsetlayout for both postfx and depth
- invalidate the state after a custom command
- only switch to postfx descriptor set in dynamic mode (color pass)


This fixes setChannelDepthClearEnabled() which could cause 
validation error, corruption or crashes.

FIXES=[459567258]
2025-11-13 12:07:16 -08:00
rafadevai 57ef534acd VK: Add support for getting pipeline creation stats (#9422)
* VK: Add support for getting pipeline creation stats

When debugging is pipeline compilation related hitches
is useful to know more information about what happened
when building a new pipeline.

By levering the VK_EXT_pipeline_creation_feedback
extension, we can know if the pipeline creation was
speed up because of a cache hit, how much time it spent
creating it and also the stats related to each
pipeline stage.

This is only available when using the debug flag
FVK_DEBUG_SHADER_MODULE.

* Fix the unused build error
2025-11-13 09:01:02 -08:00
rafadevai 719914fb84 VK: Better support for renderdoc captures (#9423)
Doing a renderdoc capture when using transient memory
or importing AHardwareBuffers into Vulkan, makes it fail,
crash or not replay properly.

To improve the capture support, added a new constant to let the
backend know when renderdoc is expected to be used and configure
things appropietly.

- In the case of transient attachments that uses lazy allocated
memory, it will be disabled.
- In the case of AHardwareBuffers, a more robust memory heap
selection algorithm will be used.

Also a new VulkanPlatform function will be added, to let other
subclasses know that the transient attachments are enabled or not.
2025-11-13 07:40:51 -08:00
Powei Feng 080f958da3 android: remove checked in asset (#9426)
For sample-gltf-viewer, an asset was checked into the source tree.
But other assets are generated or copied from asset directories.

We remove the checked in gltf and copy the asset over during
build (as with other existing files).
2025-11-13 06:29:45 +00:00
Doris Wu 7547aa3807 buffer update opt: Integrate UboManager into the engine (#9397) 2025-11-13 02:19:48 +00:00
Sungun Park 67a0c6e0e1 fix a typo in VertexBuffer (#9427) 2025-11-12 15:31:55 -08:00
Ben Doherty 37cb842993 Fix possible null string crash inside TextWriter (#9428) 2025-11-12 12:07:16 -08:00
58 changed files with 462 additions and 1154 deletions
+2
View File
@@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut ## Release notes for next branch cut
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622) - engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
@@ -22,6 +22,7 @@ object Utils {
/** /**
* Initializes the utils JNI layer. Must be called before using any utils functionality. * Initializes the utils JNI layer. Must be called before using any utils functionality.
*/ */
@JvmStatic
fun init() { fun init() {
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv. // Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
Filament.init() Filament.init()
@@ -17,12 +17,20 @@ filamentTools {
} }
// don't forget to update MainACtivity.kt when/if changing this. // don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyMesh', Copy) { 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) {
from "../../../third_party/models/BusterDrone" from "../../../third_party/models/BusterDrone"
into "src/main/assets/models" into "src/main/assets/models"
} }
preBuild.dependsOn copyMesh preBuild.dependsOn copyDamagedHelmetGltf
preBuild.dependsOn copyBusterGltf
clean.doFirst { clean.doFirst {
delete "src/main/assets" delete "src/main/assets"
-8
View File
@@ -214,14 +214,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanMemory.h src/vulkan/VulkanMemory.h
src/vulkan/VulkanPipelineCache.cpp src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h 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.cpp
src/vulkan/VulkanPipelineLayoutCache.h src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/VulkanQueryManager.cpp src/vulkan/VulkanQueryManager.cpp
@@ -444,6 +444,11 @@ protected:
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept; virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
/**
* Query if transient attachments are supported by the backend.
*/
bool isTransientAttachmentSupported() const noexcept;
private: private:
friend struct VulkanPlatformPrivate; friend struct VulkanPlatformPrivate;
}; };
@@ -214,4 +214,10 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
// destroying any unused pipeline object. // destroying any unused pipeline object.
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS); 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 #endif
@@ -154,6 +154,10 @@ public:
return mStagingBufferBypassEnabled; return mStagingBufferBypassEnabled;
} }
inline bool pipelineCreationFeedbackSupported() const noexcept {
return mPipelineCreationFeedbackSupported;
}
private: private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -181,6 +185,7 @@ private:
bool mProtectedMemorySupported = false; bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false; bool mIsUnifiedMemoryArchitecture = false;
bool mStagingBufferBypassEnabled = false; bool mStagingBufferBypassEnabled = false;
bool mPipelineCreationFeedbackSupported = false;
fvkutils::VkFormatList mDepthStencilFormats; fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats; fvkutils::VkFormatList mBlittableDepthStencilFormats;
@@ -15,7 +15,6 @@
*/ */
#include "VulkanDescriptorSetLayoutCache.h" #include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
#include "VulkanHandles.h" #include "VulkanHandles.h"
@@ -101,32 +100,22 @@ uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
return count; return count;
} }
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers, VulkanSamplerCache* cache) { uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
size_t const size = samplers.size(); size_t const size = samplers.size();
if (size == 0) { if (size == 0) {
return 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 } // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager, VulkanSamplerCache* cache) fvkmemory::ResourceManager* resourceManager)
: mDevice(device), : mDevice(device),
mResourceManager(resourceManager), mSamplerCache(cache) {} mResourceManager(resourceManager) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default; VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
@@ -142,7 +131,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
utils::FixedCapacityVector<VkSampler> immutableSamplers) { utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = { LayoutKey key = {
.bitmask = bitmasks, .bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers, mSamplerCache), .immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
}; };
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) { if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second; return itr->second;
@@ -164,20 +153,9 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
.bindingCount = count, .bindingCount = count,
.pBindings = toBind, .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; VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout); vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout; mVkLayouts[key] = vklayout;
mLayoutToKey[vklayout] = hashedKey;
return vklayout; return vklayout;
} }
@@ -19,8 +19,6 @@
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
@@ -37,8 +35,7 @@ namespace filament::backend {
class VulkanDescriptorSetLayoutCache { class VulkanDescriptorSetLayoutCache {
public: public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager, VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
VulkanSamplerCache* cache);
~VulkanDescriptorSetLayoutCache(); ~VulkanDescriptorSetLayoutCache();
void terminate() noexcept; void terminate() noexcept;
@@ -51,19 +48,10 @@ public:
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks, VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers, fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {}); 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: private:
VkDevice mDevice; VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager; fvkmemory::ResourceManager* mResourceManager;
VulkanSamplerCache* mSamplerCache;
struct LayoutKey { struct LayoutKey {
// this describes the layout using bitset. // this describes the layout using bitset.
@@ -81,7 +69,6 @@ private:
}; };
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts; tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
std::map<VkDescriptorSetLayout, uint32_t> mLayoutToKey;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -1,87 +0,0 @@
#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
@@ -1,53 +0,0 @@
/*
* 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
+6 -8
View File
@@ -230,16 +230,16 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager), mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
mPipelineCache(mPlatform->getDevice()), mPipelineLayoutCache(mPlatform->getDevice()),
mPipelineCache(mPlatform->getDevice(), mContext),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()), mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator), mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice()), mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()), mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice(), &mYcbcrConversionCache), mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands), mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()), mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager, &mSamplerCache), mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mPipelineLayoutCache(mPlatform->getDevice(), &mDescriptorSetLayoutCache),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager), mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()), mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache, mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
@@ -1666,7 +1666,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.subpassMask = uint8_t(params.subpassMask); rpkey.subpassMask = uint8_t(params.subpassMask);
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey); VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, mFramebufferCache.getRenderPassKey(renderPass), 0); mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache. // Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey(); VulkanFboCache::FboKey fbkey = rt->getFboKey();
@@ -1785,7 +1785,6 @@ void VulkanDriver::nextSubpass(int) {
VK_SUBPASS_CONTENTS_INLINE); VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
mFramebufferCache.getRenderPassKey(mCurrentRenderPass.renderPass),
++mCurrentRenderPass.currentSubpass); ++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) { if (mCurrentRenderPass.params.subpassMask & 0x1) {
@@ -2161,8 +2160,7 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
mPipelineState.pipelineLayout = pipelineLayout; mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask; mPipelineState.descriptorSetMask = descriptorSetMask;
auto key = mPipelineLayoutCache.getKey(pipelineLayout); mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindLayout(pipelineLayout, key);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer); mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
} }
+1 -1
View File
@@ -148,6 +148,7 @@ private:
VulkanSemaphoreManager mSemaphoreManager; VulkanSemaphoreManager mSemaphoreManager;
VulkanCommands mCommands; VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanPipelineCache mPipelineCache; VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool; VulkanStagePool mStagePool;
VulkanBufferCache mBufferCache; VulkanBufferCache mBufferCache;
@@ -157,7 +158,6 @@ private:
VulkanBlitter mBlitter; VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels; VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache; VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache; VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager; VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager; VulkanExternalImageManager mExternalImageManager;
@@ -15,7 +15,6 @@
*/ */
#include "VulkanFboCache.h" #include "VulkanFboCache.h"
#include "VulkanRenderPassStateSerializer.h"
#include "VulkanConstants.h" #include "VulkanConstants.h"
#include "vulkan/utils/Image.h" #include "vulkan/utils/Image.h"
@@ -70,15 +69,6 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive."; << "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 { VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config); FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) { if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
@@ -127,7 +117,6 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer); VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer." FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error); << " error=" << static_cast<int32_t>(error);
mFramebufferCache[config] = {framebuffer, mCurrentTime}; mFramebufferCache[config] = {framebuffer, mCurrentTime};
return framebuffer; return framebuffer;
} }
@@ -337,12 +326,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass); VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass." FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error; << " error=" << error;
RenderPassHash hashFunc;
uint32_t key = hashFunc(config);
VulkanRenderPassStateSerializer passSer(renderPassInfo, key);
mRenderPassToKey[renderPass] = key;
mRenderPassCache[config] = {renderPass, mCurrentTime}; mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE) #if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
@@ -24,7 +24,6 @@
#include <backend/TargetBufferInfo.h> #include <backend/TargetBufferInfo.h>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include <map>
namespace filament::backend { namespace filament::backend {
@@ -98,8 +97,6 @@ public:
explicit VulkanFboCache(VkDevice device); explicit VulkanFboCache(VkDevice device);
~VulkanFboCache(); ~VulkanFboCache();
uint32_t getRenderPassKey(VkRenderPass pass) noexcept;
// Retrieves or creates a VkFramebuffer handle. // Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept; VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
@@ -121,7 +118,6 @@ private:
FboMap mFramebufferCache; FboMap mFramebufferCache;
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache; tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
std::map<VkRenderPass, uint32_t> mRenderPassToKey;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount; tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0; uint32_t mCurrentTime = 0;
}; };
@@ -33,7 +33,6 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH #include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION #include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h> #include <utils/CString.h>
#include <fstream>
using namespace bluevk; using namespace bluevk;
@@ -180,23 +179,6 @@ 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 } // anonymous namespace
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout, VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
@@ -298,12 +280,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
.codeSize = dataSize, .codeSize = dataSize,
.pCode = data, .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); VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create shader module." << "Unable to create shader module."
@@ -65,8 +65,6 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout, using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>; VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorSetLayoutHashArray = std::array<uint64_t,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
// The bitmask representation of a set layout. // The bitmask representation of a set layout.
struct Bitmask { struct Bitmask {
@@ -282,10 +280,6 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value); mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
} }
inline uint64_t getVertexShaderHash() { return mVertexShaderHash;
}
inline uint64_t getFragmentShaderHash() { return mFragmentShaderHash; }
// TODO: handle compute shaders. // TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute. // The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2; static constexpr uint8_t const MAX_SHADER_MODULES = 2;
@@ -301,8 +295,6 @@ private:
}; };
PipelineInfo* mInfo; PipelineInfo* mInfo;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
VkDevice mDevice = VK_NULL_HANDLE; VkDevice mDevice = VK_NULL_HANDLE;
}; };
@@ -15,7 +15,6 @@
*/ */
#include "VulkanPipelineCache.h" #include "VulkanPipelineCache.h"
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -35,17 +34,49 @@ using namespace bluevk;
namespace filament::backend { namespace filament::backend {
VulkanPipelineCache::VulkanPipelineCache(VkDevice device) namespace {
: mDevice(device) {
#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) {
VkPipelineCacheCreateInfo createInfo = { VkPipelineCacheCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
}; };
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache); bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
} }
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout, uint32_t key) noexcept { void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
mPipelineRequirements.layout = layout; mPipelineRequirements.layout = layout;
mPipelineLayoutKey = key;
} }
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept { VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept {
@@ -79,7 +110,6 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept { VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound."); assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified"); assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
VulkanPipelineStateSerializer serializer(mProgramName);
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT]; VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
shaderStages[0] = { shaderStages[0] = {
@@ -92,12 +122,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = mPipelineRequirements.shaders[1]; 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; bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
@@ -116,11 +140,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) { for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
if (mPipelineRequirements.vertexAttributes[i].format > 0) { if (mPipelineRequirements.vertexAttributes[i].format > 0) {
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i]; vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
serializer << mPipelineRequirements.vertexAttributes[i];
} }
if (mPipelineRequirements.vertexBuffers[i].stride > 0) { if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i]; vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
serializer << mPipelineRequirements.vertexBuffers[i];
} }
} }
VkPipelineVertexInputStateCreateInfo vertexInputState = { VkPipelineVertexInputStateCreateInfo vertexInputState = {
@@ -134,15 +156,11 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology, .topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
}; };
serializer << inputAssemblyState;
VkPipelineViewportStateCreateInfo viewportState = { VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1, .viewportCount = 1,
.scissorCount = 1, .scissorCount = 1,
}; };
serializer << viewportState;
VkDynamicState dynamicStateEnables[] = { VkDynamicState dynamicStateEnables[] = {
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_SCISSOR,
@@ -152,8 +170,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.dynamicStateCount = 2, .dynamicStateCount = 2,
.pDynamicStates = dynamicStateEnables, .pDynamicStates = dynamicStateEnables,
}; };
serializer << dynamicState;
auto const& raster = mPipelineRequirements.rasterState; auto const& raster = mPipelineRequirements.rasterState;
VkPipelineRasterizationStateCreateInfo vkRaster = { VkPipelineRasterizationStateCreateInfo vkRaster = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@@ -167,8 +183,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.depthBiasSlopeFactor = raster.depthBiasSlopeFactor, .depthBiasSlopeFactor = raster.depthBiasSlopeFactor,
.lineWidth = 1.0f, .lineWidth = 1.0f,
}; };
serializer << vkRaster;
VkPipelineMultisampleStateCreateInfo vkMs = { VkPipelineMultisampleStateCreateInfo vkMs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples, .rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples,
@@ -177,8 +191,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.alphaToCoverageEnable = raster.alphaToCoverageEnable, .alphaToCoverageEnable = raster.alphaToCoverageEnable,
.alphaToOneEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE,
}; };
serializer << vkMs;
VkPipelineDepthStencilStateCreateInfo vkDs = { VkPipelineDepthStencilStateCreateInfo vkDs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE, .depthTestEnable = VK_TRUE,
@@ -198,12 +210,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.writeMask = 0u, .writeMask = 0u,
.reference = 0u, .reference = 0u,
}; };
serializer << vkDs;
//layout
serializer.setPipelineLayoutKey(mPipelineLayoutKey);
// renderPass
serializer.setRenderPassKey(mRenderPassKey);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = { VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
@@ -242,36 +248,52 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
colorBlendAttachments[i] = colorBlendAttachments[0]; colorBlendAttachments[i] = colorBlendAttachments[0];
} }
} }
serializer << colorBlendState;
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"; << shaderStages[1].module << ")";
#endif
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
PipelineCacheEntry cacheEntry = { PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime, .lastUsed = mCurrentTime,
}; };
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo, VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle); 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); assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) { if (error != VK_SUCCESS) {
FVK_LOGE << "vkCreateGraphicsPipelines error " << error; FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
return nullptr; return nullptr;
} }
PipelineHashFn hashFunc;
serializer.setID(hashFunc(mPipelineRequirements));
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value(); return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
} }
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept { void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader(); mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader(); 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 this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
@@ -286,10 +308,9 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
mPipelineRequirements.rasterState = rasterState; mPipelineRequirements.rasterState = rasterState;
} }
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept { void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass; mPipelineRequirements.renderPass = renderPass;
mPipelineRequirements.subpassIndex = subpassIndex; mPipelineRequirements.subpassIndex = subpassIndex;
mRenderPassKey = passKey;
} }
void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept { void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept {
@@ -86,9 +86,9 @@ public:
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding."); static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
VulkanPipelineCache(VkDevice device); VulkanPipelineCache(VkDevice device, VulkanContext const& context);
void bindLayout(VkPipelineLayout layout, uint32_t key) noexcept; void bindLayout(VkPipelineLayout layout) noexcept;
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline. // Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
void bindPipeline(VulkanCommandBuffer* commands); void bindPipeline(VulkanCommandBuffer* commands);
@@ -96,7 +96,7 @@ public:
// Each of the following methods are fast and do not make Vulkan calls. // Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept; void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept; void bindRasterState(RasterState const& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept; void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc, void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count); VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
@@ -207,14 +207,11 @@ private:
// Current requirements for the pipeline layout, pipeline, and descriptor sets. // Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineKey mPipelineRequirements = {}; 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. // Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {}; PipelineKey mBoundPipeline = {};
[[maybe_unused]] VulkanContext const& mContext;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -15,7 +15,6 @@
*/ */
#include "VulkanPipelineLayoutCache.h" #include "VulkanPipelineLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
namespace filament::backend { namespace filament::backend {
@@ -24,11 +23,7 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
fvkmemory::resource_ptr<VulkanProgram> program) { fvkmemory::resource_ptr<VulkanProgram> program) {
PipelineLayoutKey key = {}; PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0; uint8_t descSetLayoutCount = 0;
DescriptorSetLayoutHashArray hashArray; key.descSetLayouts = descriptorSetLayouts;
for (uint32_t i = 0; i < descriptorSetLayouts.size(); ++i) {
hashArray[i] = mDescriptorSetCache->getKey(descriptorSetLayouts[i]);
}
key.descSetLayouts = hashArray;
for (auto layoutHandle: descriptorSetLayouts) { for (auto layoutHandle: descriptorSetLayouts) {
if (layoutHandle == VK_NULL_HANDLE) { if (layoutHandle == VK_NULL_HANDLE) {
break; break;
@@ -67,14 +62,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.setLayoutCount = (uint32_t) descSetLayoutCount, .setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = descriptorSetLayouts.data(), .pSetLayouts = key.descSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount, .pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges, .pPushConstantRanges = pushConstantRanges,
}; };
PipelineLayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
VulkanPipelineLayoutSerializer pipelineSer(info, mDescriptorSetCache, hashedKey);
VkPipelineLayout layout; VkPipelineLayout layout;
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout); vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
@@ -82,8 +74,6 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.handle = layout, .handle = layout,
.lastUsed = mTimestamp++, .lastUsed = mTimestamp++,
}; };
mPipelineToKey[layout] = hashedKey;
return layout; return layout;
} }
@@ -18,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H #define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
@@ -31,11 +30,10 @@ namespace filament::backend {
class VulkanPipelineLayoutCache { class VulkanPipelineLayoutCache {
public: public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray; using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetLayoutHashArray = VulkanDescriptorSetLayout::DescriptorSetLayoutHashArray;
VulkanPipelineLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache* cache) VulkanPipelineLayoutCache(VkDevice device)
: mDevice(device), : mDevice(device),
mTimestamp(0), mDescriptorSetCache(cache) {} mTimestamp(0) {}
void terminate() noexcept; void terminate() noexcept;
@@ -47,7 +45,7 @@ public:
}; };
struct PipelineLayoutKey { struct PipelineLayoutKey {
DescriptorSetLayoutHashArray descSetLayouts = {}; // 8 * 4 DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3 PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
uint16_t padding = 0; uint16_t padding = 0;
}; };
@@ -60,14 +58,6 @@ public:
// are described in the program. // are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts, VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program); 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: private:
using Timestamp = uint64_t; using Timestamp = uint64_t;
@@ -89,8 +79,6 @@ private:
VkDevice mDevice; VkDevice mDevice;
Timestamp mTimestamp; Timestamp mTimestamp;
PipelineLayoutMap mPipelineLayouts; PipelineLayoutMap mPipelineLayouts;
std::map<VkPipelineLayout, uint32_t> mPipelineToKey;
VulkanDescriptorSetLayoutCache* mDescriptorSetCache;
}; };
} // filament::backend } // filament::backend
@@ -1,251 +0,0 @@
/*
* 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;
}
}
@@ -1,79 +0,0 @@
/*
* 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
@@ -1,157 +0,0 @@
/*
* 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();
}
}
@@ -1,42 +0,0 @@
/*
* 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,14 +21,12 @@
#include <utils/Panic.h> #include <utils/Panic.h>
#include "VulkanSamplerStateSerializer.h"
using namespace bluevk; using namespace bluevk;
namespace filament::backend { namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache) VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device), mConversionCache(conversionCache) {} : mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) { VkSampler VulkanSamplerCache::getSampler(Params params) {
auto iter = mCache.find(params); auto iter = mCache.find(params);
@@ -59,16 +57,11 @@ VkSampler VulkanSamplerCache::getSampler(Params params) {
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE, .unnormalizedCoordinates = VK_FALSE,
}; };
VulkanSamplerStateSerializer samplerSer(params,
params.conversion != VK_NULL_HANDLE ? mConversionCache->getKey(params.conversion) : 0);
VkSampler sampler; VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler); VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler." FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result); << " error=" << static_cast<int32_t>(result);
mCache.insert({ params, sampler }); mCache.insert({ params, sampler });
SamplerHashFn hashFn;
mSamplerToKey[sampler] = hashFn(params);
return sampler; return sampler;
} }
@@ -17,11 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H #ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H #define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <utils/Hash.h> #include <utils/Hash.h>
#include <map>
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
@@ -39,20 +37,11 @@ public:
static_assert(sizeof(Params) == 16); static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache); explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params); 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; void terminate() noexcept;
private: private:
VkDevice mDevice; VkDevice mDevice;
VulkanYcbcrConversionCache* mConversionCache;
struct SamplerEqualTo { struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept { bool operator()(Params lhs, Params rhs) const noexcept {
@@ -62,7 +51,6 @@ private:
}; };
using SamplerHashFn = utils::hash::MurmurHashFn<Params>; using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache; tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
std::map<VkSampler, uint32_t> mSamplerToKey;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -1,64 +0,0 @@
#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
@@ -1,48 +0,0 @@
/*
* 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,7 +15,6 @@
*/ */
#include "VulkanYcbcrConversionCache.h" #include "VulkanYcbcrConversionCache.h"
#include "VulkanSamplerStateSerializer.h"
#include "vulkan/VulkanConstants.h" #include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h" #include "vulkan/utils/Conversion.h"
@@ -54,7 +53,6 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset), .yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter), .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 // We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__) #if defined(__ANDROID__)
@@ -75,8 +73,6 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
<< " error=" << static_cast<int32_t>(result); << " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion }); mCache.insert({ params, conversion });
ConversionHashFn hashFn;
mConversionToKey[conversion] = hashFn(params);
return conversion; return conversion;
} }
@@ -25,7 +25,6 @@
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include <map>
namespace filament::backend { namespace filament::backend {
@@ -41,14 +40,6 @@ public:
explicit VulkanYcbcrConversionCache(VkDevice device); explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params); 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; void terminate() noexcept;
private: private:
@@ -63,7 +54,6 @@ private:
}; };
using ConversionHashFn = utils::hash::MurmurHashFn<Params>; using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache; tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
std::map<VkSamplerYcbcrConversion, uint32_t> mConversionToKey;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -228,6 +228,10 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
#endif #endif
VK_KHR_MULTIVIEW_EXTENSION_NAME, VK_KHR_MULTIVIEW_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
#endif
}; };
ExtensionSet exts; ExtensionSet exts;
// Identify supported physical device extensions // Identify supported physical device extensions
@@ -865,6 +869,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
if (!mImpl->mSharedContext) { if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mPipelineCreationFeedbackSupported =
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
} else { } else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext; VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported; context.mDebugUtilsSupported = scontext->debugUtilsSupported;
@@ -873,13 +879,16 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// Check the availability of lazily allocated memory // Check the availability of lazily allocated memory
context.mLazilyAllocatedMemorySupported = false; context.mLazilyAllocatedMemorySupported = false;
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount; // RenderDoc doesn't support lazy allocated memory
++i) { if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i]; for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { ++i) {
context.mLazilyAllocatedMemorySupported = true; VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
break; context.mLazilyAllocatedMemorySupported = true;
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
break;
}
} }
} }
@@ -1022,4 +1031,8 @@ VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const no
return static_cast<VkExternalFenceHandleTypeFlagBits>(0); return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
} }
bool VulkanPlatform::isTransientAttachmentSupported() const noexcept {
return mImpl->mContext.isLazilyAllocatedMemorySupported();
}
} // namespace filament::backend } // namespace filament::backend
@@ -158,7 +158,47 @@ std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardware
}; };
} }
}// namespace 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
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() { VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
if (__builtin_available(android 26, *)) { if (__builtin_available(android 26, *)) {
@@ -354,8 +394,6 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
.image = image, .image = image,
.buffer = VK_NULL_HANDLE, .buffer = VK_NULL_HANDLE,
}; };
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
VkMemoryPropertyFlags requiredMemoryFlags = VkMemoryPropertyFlags requiredMemoryFlags =
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE) !isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
@@ -365,8 +403,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT; requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
} }
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties, uint32_t const memoryTypeIndex = selectMemoryTypeForExternalImage(physicalDevice, device,
metadata.memoryTypeBits, requiredMemoryFlags); image, metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = { VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
@@ -63,7 +63,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
// WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a // WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a
// multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple // multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple
// of 4 by padding with zeros. // of 4 by padding with zeros.
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor, void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor&& bufferDescriptor,
const uint32_t byteOffset, wgpu::Device const& device, const uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager) { WebGPUQueueManager* const webGPUQueueManager) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer) FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
@@ -89,24 +89,49 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
wgpu::BufferDescriptor descriptor{ wgpu::BufferDescriptor descriptor{
.label = "Filament WebGPU Staging Buffer", .label = "Filament WebGPU Staging Buffer",
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, .usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
.size = stagingBufferSize, .size = stagingBufferSize};
.mappedAtCreation = true };
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor); wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
void* mappedRange = stagingBuffer.GetMappedRange(); struct UserData final {
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size); uint32_t byteOffset;
size_t stagingBufferSize;
// Make sure the padded memory is set to 0 to have deterministic behaviors size_t remainder;
if (remainder != 0) { BufferDescriptor srcBufferDescriptor;
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size; wgpu::Buffer stagingBuffer;
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder); WebGPUQueueManager* const webGPUQueueManager;
} wgpu::Buffer dstBuffer;
};
stagingBuffer.Unmap(); auto userData = std::make_unique<UserData>(UserData{
.byteOffset = byteOffset,
// Copy the staging buffer contents to the destination buffer. .stagingBufferSize = stagingBufferSize,
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer, .remainder = remainder,
byteOffset, stagingBufferSize); .srcBufferDescriptor = std::move(bufferDescriptor),
.stagingBuffer = stagingBuffer,
.webGPUQueueManager = webGPUQueueManager,
.dstBuffer = mBuffer});
stagingBuffer.MapAsync(
wgpu::MapMode::Write, 0, stagingBufferSize, wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
void* mappedRange = data->stagingBuffer.GetMappedRange();
memcpy(mappedRange, data->srcBufferDescriptor.buffer,
data->srcBufferDescriptor.size);
if (data->remainder != 0) {
uint8_t* paddingStart =
static_cast<uint8_t*>(mappedRange) + data->srcBufferDescriptor.size;
memset(paddingStart, 0,
FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - data->remainder);
}
data->stagingBuffer.Unmap();
data->webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(
data->stagingBuffer, 0, data->dstBuffer, data->byteOffset,
data->stagingBufferSize);
} else {
FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
}
},
userData.release());
} }
} // namespace filament::backend } // namespace filament::backend
@@ -39,7 +39,7 @@ public:
* happen after draw commands encoded in the encoder. Submitting any commands up to this point * happen after draw commands encoded in the encoder. Submitting any commands up to this point
* ensures the calls happen in the expected sequence. * ensures the calls happen in the expected sequence.
*/ */
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device, void updateGPUBuffer(BufferDescriptor&&, uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager); WebGPUQueueManager* const webGPUQueueManager);
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; } [[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
+3 -3
View File
@@ -856,7 +856,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
// draw calls are made. // draw calls are made.
flush(); flush();
handleCast<WebGPUIndexBuffer>(indexBufferHandle) handleCast<WebGPUIndexBuffer>(indexBufferHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
@@ -867,14 +867,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
// draw calls are made. // draw calls are made.
flush(); flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle) handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle, void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) { BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
handleCast<WebGPUBufferObject>(bufferObjectHandle) handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
@@ -21,6 +21,7 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <thread> #include <thread>
#include <iostream>
namespace filament::backend { namespace filament::backend {
@@ -58,6 +59,7 @@ WebGPUQueueManager::WebGPUQueueManager(wgpu::Device const& device)
WebGPUQueueManager::~WebGPUQueueManager() = default; WebGPUQueueManager::~WebGPUQueueManager() = default;
wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() { wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
// std::unique_lock<std::mutex> lock(mLock);
if (!mCommandEncoder) { if (!mCommandEncoder) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "Filament Command Encoder", .label = "Filament Command Encoder",
@@ -90,7 +92,9 @@ std::shared_ptr<WebGPUSubmissionState> WebGPUQueueManager::getLatestSubmissionSt
} }
void WebGPUQueueManager::submit() { void WebGPUQueueManager::submit() {
// std::unique_lock<std::mutex> lock(mLock);
if (!mCommandEncoder) { if (!mCommandEncoder) {
std::cout << "Run Yu: no mCommandEncoder found!\n";
return; return;
} }
@@ -74,6 +74,7 @@ private:
wgpu::Queue mQueue; wgpu::Queue mQueue;
wgpu::CommandEncoder mCommandEncoder; wgpu::CommandEncoder mCommandEncoder;
std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState; std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState;
std::mutex mLock;
}; };
} // namespace filament::backend } // namespace filament::backend
+23 -19
View File
@@ -258,6 +258,10 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
{ { 0, 0 }, driver }); { { 0, 0 }, driver });
} }
UboManager* PostProcessManager::getUboManager() const noexcept {
return mEngine.getUboManager();
}
UTILS_NOINLINE UTILS_NOINLINE
void PostProcessManager::registerPostProcessMaterial(std::string_view const name, void PostProcessManager::registerPostProcessMaterial(std::string_view const name,
StaticMaterialInfo const& info) { StaticMaterialInfo const& info) {
@@ -513,7 +517,7 @@ UTILS_NOINLINE
void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver, void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi, FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi,
PostProcessVariant const variant) const noexcept { PostProcessVariant const variant) const noexcept {
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
FMaterial const* const ma = mi->getMaterial(); FMaterial const* const ma = mi->getMaterial();
PipelineState const pipeline = getPipelineState(ma, variant); PipelineState const pipeline = getPipelineState(ma, variant);
@@ -645,7 +649,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto th = driver.createTextureView(in, level, 1); auto th = driver.createTextureView(in, level, 1);
mi->setParameter("depth", th, SamplerParams{ mi->setParameter("depth", th, SamplerParams{
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -1079,7 +1083,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->setParameter("ssctRayCount", mi->setParameter("ssctRayCount",
float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) }); float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -1200,7 +1204,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
mi->setParameter("sampleCount", kGaussianCount); mi->setParameter("sampleCount", kGaussianCount);
mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold); mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold);
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -1895,7 +1899,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres? mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres?
mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h }); mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
@@ -2425,7 +2429,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
mi->setParameter("source", hwOutView, SamplerParams{ mi->setParameter("source", hwOutView, SamplerParams{
.filterMag = SamplerMagFilter::LINEAR, .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(hwDstRT, pipeline, driver); renderFullScreenQuad(hwDstRT, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -2533,7 +2537,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
FMaterialInstance* const mi = FMaterialInstance* const mi =
configureColorGradingMaterial(material, colorGrading, colorGradingConfig, configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
vignetteOptions, width, height); vignetteOptions, width, height);
mi->commit(driver); mi->commit(driver, getUboManager());
} }
void PostProcessManager::colorGradingSubpass(DriverApi& driver, void PostProcessManager::colorGradingSubpass(DriverApi& driver,
@@ -2566,7 +2570,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma); auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.customResolve = fixedIndex; mFixedMaterialInstanceIndex.customResolve = fixedIndex;
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f), mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
mi->commit(driver); mi->commit(driver, getUboManager());
material.getMaterial(mEngine); material.getMaterial(mEngine);
} }
@@ -2619,7 +2623,7 @@ void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver) noexcep
auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE); auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma); auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.clearDepth = fixedIndex; mFixedMaterialInstanceIndex.clearDepth = fixedIndex;
mi->commit(driver); mi->commit(driver, getUboManager());
material.getMaterial(mEngine); material.getMaterial(mEngine);
} }
@@ -3111,7 +3115,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
mat4f{ historyProjection * inverse(current.projection) } * mat4f{ historyProjection * inverse(current.projection) } *
normalizedToClip); normalizedToClip);
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
if (colorGradingConfig.asSubpass) { if (colorGradingConfig.asSubpass) {
@@ -3196,7 +3200,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
mi->setParameter("resolution", float4{ mi->setParameter("resolution", float4{
outputDesc.width, outputDesc.height, outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height }); 1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(material.getMaterial(mEngine), variant); auto pipeline = getPipelineState(material.getMaterial(mEngine), variant);
@@ -3287,7 +3291,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto out = resources.getRenderPassInfo(); auto out = resources.getRenderPassInfo();
@@ -3374,7 +3378,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
float(inputDesc.height) float(inputDesc.height)
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto const out = resources.getRenderPassInfo(); auto const out = resources.getRenderPassInfo();
@@ -3469,7 +3473,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
mi->setParameter("resolution", mi->setParameter("resolution",
float4{ outputDesc.width, outputDesc.height, float4{ outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height }); 1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
} }
@@ -3496,7 +3500,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
} }
@@ -3582,7 +3586,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
if (layer) { if (layer) {
mi->setParameter("layerIndex", layer); mi->setParameter("layerIndex", layer);
} }
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -3840,7 +3844,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
}); });
mi->setParameter("layer", uint32_t(layer)); mi->setParameter("layer", uint32_t(layer));
mi->setParameter("uvscale", 1.0f / float(dim)); mi->setParameter("uvscale", 1.0f / float(dim));
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuadWithScissor(out, pipeline, scissor, driver); renderFullScreenQuadWithScissor(out, pipeline, scissor, driver);
@@ -3942,7 +3946,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -3959,7 +3963,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
// Render all layers of the texture to the screen side-by-side. // Render all layers of the texture to the screen side-by-side.
for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) { for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) {
mi->setParameter("layerIndex", i); mi->setParameter("layerIndex", i);
mi->commit(driver); mi->commit(driver, getUboManager());
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
// From the second draw, don't clear the targetbuffer. // From the second draw, don't clear the targetbuffer.
+3
View File
@@ -65,6 +65,7 @@ class FMaterialInstance;
class FrameGraph; class FrameGraph;
class RenderPass; class RenderPass;
class RenderPassBuilder; class RenderPassBuilder;
class UboManager;
struct CameraInfo; struct CameraInfo;
class PostProcessManager { class PostProcessManager {
@@ -422,6 +423,8 @@ private:
return getMaterialInstance(ma); return getMaterialInstance(ma);
} }
UboManager* getUboManager() const noexcept;
backend::RenderPrimitiveHandle mFullScreenQuadRph; backend::RenderPrimitiveHandle mFullScreenQuadRph;
backend::VertexBufferInfoHandle mFullScreenQuadVbih; backend::VertexBufferInfoHandle mFullScreenQuadVbih;
backend::DescriptorSetLayoutHandle mPerRenderableDslh; backend::DescriptorSetLayoutHandle mPerRenderableDslh;
+25 -15
View File
@@ -1012,6 +1012,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT; uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size()); assert_invariant(index < mCustomCommands.size());
pCustomCommands[index](); pCustomCommands[index]();
currentPipeline = {};
currentPrimitiveHandle ={};
continue; continue;
} }
@@ -1069,26 +1071,34 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] = pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] =
ma->getDescriptorSetLayout(info.materialVariant).getHandle(); ma->getDescriptorSetLayout(info.materialVariant).getHandle();
// If we have a ColorPassDescriptorSet we use it to bind the per-view
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) { // descriptor-set (ideally only if it changed).
// It is possible to get a post-process material here (even though it's // If we don't, it means the descriptor-set is already bound and the layout we
// not technically a public API yet, it is used by the IBLPrefilterLibrary. // got from the material above should match. This is the case for situations
// Ideally we would have a more formal compute API). In this case, we need // where we have a known per-view descriptor-set layout,
// to set the post-process descriptor-set. // e.g.: postfx, shadow-maps, ssr and structure passes.
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver); if (mColorPassDescriptorSet) {
} else { if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
// If we have a ColorPassDescriptorSet we use it to bind the per-view // It is possible to get a post-process material here (even though it's
// descriptor-set (ideally only if it changed). If we don't, it means // not technically a public API yet, it is used by the IBLPrefilterLibrary.
// the descriptor-set is already bound and the layout we got from the // Ideally we would have a more formal compute API). In this case, we need
// material above should match. This is the case for situations where we // to set the post-process descriptor-set.
// have a known per-view descriptor-set layout, e.g.: shadow-maps, ssr and engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
// structure passes. } else {
if (mColorPassDescriptorSet) {
// We have a ColorPassDescriptorSet, we need to go through it for binding // 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 // the per-view descriptor-set because its layout can change based on the
// material. // material.
mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex()); 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. // Each MaterialInstance has its own descriptor set. This binds it.
+15 -2
View File
@@ -142,7 +142,11 @@ void BufferAllocator::retire(AllocationId id) {
InternalSlotNode* targetNode = getNodeById(id); InternalSlotNode* targetNode = getNodeById(id);
assert_invariant(targetNode != nullptr); assert_invariant(targetNode != nullptr);
targetNode->slot.isAllocated = false; Slot& slot = targetNode->slot;
slot.isAllocated = false;
if (slot.gpuUseCount == 0) {
mHasPendingFrees = true;
}
} }
void BufferAllocator::acquireGpu(AllocationId id) { void BufferAllocator::acquireGpu(AllocationId id) {
@@ -157,10 +161,18 @@ void BufferAllocator::releaseGpu(AllocationId id) {
assert_invariant(targetNode != nullptr); assert_invariant(targetNode != nullptr);
assert_invariant(targetNode->slot.gpuUseCount > 0); assert_invariant(targetNode->slot.gpuUseCount > 0);
targetNode->slot.gpuUseCount--; Slot& slot = targetNode->slot;
slot.gpuUseCount--;
if (slot.gpuUseCount == 0 && !slot.isAllocated) {
mHasPendingFrees = true;
}
} }
void BufferAllocator::releaseFreeSlots() { void BufferAllocator::releaseFreeSlots() {
if (!mHasPendingFrees) {
return;
}
auto curr = mSlotPool.begin(); auto curr = mSlotPool.begin();
while (curr != mSlotPool.end()) { while (curr != mSlotPool.end()) {
if (!curr->slot.isFree()) { if (!curr->slot.isFree()) {
@@ -199,6 +211,7 @@ void BufferAllocator::releaseFreeSlots() {
curr = next; curr = next;
} }
mHasPendingFrees = false;
} }
BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept { BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept {
+2 -1
View File
@@ -113,8 +113,9 @@ private:
[[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept; [[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept;
bool mHasPendingFrees = false;
allocation_size_t mTotalSize; 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::list<InternalSlotNode> mSlotPool; // All slots, including both allocated and freed
std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList; std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList;
std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap; std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap;
+53 -6
View File
@@ -468,11 +468,26 @@ void FEngine::init() {
} }
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this)); mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
} }
// We must commit the default material instance here. It may not be used in a scene, but its // 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. // descriptor set may still be used for shared variants.
mDefaultMaterial->getDefaultInstance()->commit(driverApi); //
// 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);
if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) { 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)); mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
constexpr float3 dummyPositions[1] = {}; constexpr float3 dummyPositions[1] = {};
@@ -653,6 +668,12 @@ void FEngine::shutdown() {
driver.destroyRenderTarget(std::move(mDefaultRenderTarget)); driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
if (isUboBatchingEnabled()) {
mUboManager->terminate(driver);
delete mUboManager;
mUboManager = nullptr;
}
/* /*
* Shutdown the backend... * Shutdown the backend...
*/ */
@@ -693,18 +714,31 @@ void FEngine::prepare() {
// UBOs that are visible only. It's not such a big issue because the actual upload() is // 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. // skipped if the UBO hasn't changed. Still we could have a lot of these.
DriverApi& driver = getDriverApi(); DriverApi& driver = getDriverApi();
const bool useUboBatching = isUboBatchingEnabled();
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
mUboManager->beginFrame(driver, mMaterialInstances);
}
UboManager* uboManager = mUboManager;
for (auto& materialInstanceList: mMaterialInstances) { for (auto& materialInstanceList: mMaterialInstances) {
materialInstanceList.second.forEach([&driver](FMaterialInstance* item) { materialInstanceList.second.forEach([&driver, uboManager](FMaterialInstance* item) {
// post-process materials instances must be commited explicitly because their // post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time. // parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) { if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
item->commitStreamUniformAssociations(driver); item->commitStreamUniformAssociations(driver);
item->commit(driver); item->commit(driver, uboManager);
} }
}); });
} }
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
getUboManager()->finishBeginFrame(getDriverApi());
}
mMaterials.forEach([](FMaterial* material) { mMaterials.forEach([](FMaterial* material) {
#if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner) #if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner)
material->checkProgramEdits(); material->checkProgramEdits();
@@ -721,6 +755,13 @@ void FEngine::gc() {
mCameraManager.gc(*this, em); mCameraManager.gc(*this, em);
} }
void FEngine::submitFrame() {
if (isUboBatchingEnabled()) {
DriverApi& driver = getDriverApi();
getUboManager()->endFrame(driver, getMaterialInstanceResourceList());
}
}
void FEngine::flush() { void FEngine::flush() {
// flush the command buffer // flush the command buffer
flushCommandBuffer(mCommandBufferQueue); flushCommandBuffer(mCommandBufferQueue);
@@ -951,9 +992,10 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
return p; return p;
} }
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
const char* name) noexcept { UboBatchingMode batchingMode) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name); FMaterialInstance* p =
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
if (UTILS_LIKELY(p)) { if (UTILS_LIKELY(p)) {
auto pos = mMaterialInstances.emplace(material, "MaterialInstance"); auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p); pos.first->second.insert(p);
@@ -1243,6 +1285,11 @@ UTILS_NOINLINE
bool FEngine::destroy(const FMaterialInstance* p) { bool FEngine::destroy(const FMaterialInstance* p) {
if (p == nullptr) return true; if (p == nullptr) return true;
if (p->isUsingUboBatching()) {
assert_invariant(isUboBatchingEnabled());
mUboManager->retireSlot(p->getAllocationId());
}
// Check that the material instance we're destroying is not in use in the RenderableManager // Check that the material instance we're destroying is not in use in the RenderableManager
// To do this, we currently need to inspect all render primitives in the RenderableManager // To do this, we currently need to inspect all render primitives in the RenderableManager
EntityManager const& em = mEntityManager; EntityManager const& em = mEntityManager;
+26 -5
View File
@@ -21,16 +21,17 @@
#include "Allocators.h" #include "Allocators.h"
#include "DFG.h" #include "DFG.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "HwDescriptorSetLayoutFactory.h" #include "HwDescriptorSetLayoutFactory.h"
#include "HwVertexBufferInfoFactory.h" #include "HwVertexBufferInfoFactory.h"
#include "MaterialCache.h" #include "MaterialCache.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "UboManager.h"
#include "components/CameraManager.h" #include "components/CameraManager.h"
#include "components/LightManager.h" #include "components/LightManager.h"
#include "components/TransformManager.h"
#include "components/RenderableManager.h" #include "components/RenderableManager.h"
#include "components/TransformManager.h"
#include "ds/DescriptorSetLayout.h" #include "ds/DescriptorSetLayout.h"
@@ -263,6 +264,14 @@ public:
return mBackend; return mBackend;
} }
bool isUboBatchingEnabled() const noexcept {
return mUboManager != nullptr;
}
UboManager* getUboManager() noexcept {
return mUboManager;
}
Platform* getPlatform() const noexcept { Platform* getPlatform() const noexcept {
return mPlatform; return mPlatform;
} }
@@ -336,11 +345,21 @@ public:
FRenderer* createRenderer() noexcept; 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, FMaterialInstance* createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept; const FMaterialInstance* other, const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material, FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
const char* name) noexcept; UboBatchingMode batchingMode) noexcept;
FScene* createScene() noexcept; FScene* createScene() noexcept;
FView* createView() noexcept; FView* createView() noexcept;
@@ -446,6 +465,7 @@ public:
void prepare(); void prepare();
void gc(); void gc();
void submitFrame();
using ShaderContent = utils::FixedCapacityVector<uint8_t>; using ShaderContent = utils::FixedCapacityVector<uint8_t>;
@@ -650,6 +670,7 @@ private:
uint32_t mFlushCounter = 0; uint32_t mFlushCounter = 0;
UboManager* mUboManager = nullptr;
RootArenaScope::Arena mPerRenderPassArena; RootArenaScope::Arena mPerRenderPassArena;
HeapAllocatorArena mHeapAllocator; HeapAllocatorArena mHeapAllocator;
+13 -11
View File
@@ -73,6 +73,7 @@ namespace filament {
using namespace backend; using namespace backend;
using namespace filaflat; using namespace filaflat;
using namespace utils; using namespace utils;
using UboBatchingMode = FEngine::UboBatchingMode;
struct Material::BuilderDetails { struct Material::BuilderDetails {
const void* mPayload = nullptr; const void* mPayload = nullptr;
@@ -217,16 +218,17 @@ void FMaterial::terminate(FEngine& engine) {
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout( filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept { Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
if (Variant::isValidDepthVariant(variant)) { if (mDefinition.materialDomain == MaterialDomain::SURFACE) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE); // `variant` is only sensical for MaterialDomain::SURFACE
return mEngine.getPerViewDescriptorSetLayoutDepthVariant(); if (Variant::isValidDepthVariant(variant)) {
} return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
if (Variant::isSSRVariant(variant)) { }
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE); if (Variant::isSSRVariant(variant)) {
return mEngine.getPerViewDescriptorSetLayoutSsrVariant(); return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
}
} }
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
if (useVsmDescriptorSetLayout) { if (useVsmDescriptorSetLayout) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mDefinition.perViewDescriptorSetLayoutVsm; return mDefinition.perViewDescriptorSetLayoutVsm;
} }
return mDefinition.perViewDescriptorSetLayout; return mDefinition.perViewDescriptorSetLayout;
@@ -280,14 +282,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name); return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
} else { } else {
// but if we don't, just create an instance with all the default parameters // but if we don't, just create an instance with all the default parameters
return mEngine.createMaterialInstance(this, name); return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
} }
} }
FMaterialInstance* FMaterial::getDefaultInstance() noexcept { FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) { if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
mDefaultMaterialInstance = mEngine.createMaterialInstance(this, mDefaultMaterialInstance =
mDefinition.name.c_str()); mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
mDefaultMaterialInstance->setDefaultInstance(true); mDefaultMaterialInstance->setDefaultInstance(true);
} }
return mDefaultMaterialInstance; return mDefaultMaterialInstance;
+1 -1
View File
@@ -108,7 +108,7 @@ public:
backend::CallbackHandler* handler, backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) noexcept; utils::Invocable<void(Material*)>&& callback) noexcept;
// Create an instance of this material // Creates an instance of this material, specifying the batching mode.
FMaterialInstance* createInstance(const char* name) const noexcept; FMaterialInstance* createInstance(const char* name) const noexcept;
bool hasParameter(const char* name) const noexcept; bool hasParameter(const char* name) const noexcept;
+42 -16
View File
@@ -58,15 +58,19 @@ using namespace utils;
namespace filament { namespace filament {
using namespace backend; using namespace backend;
using UboBatchingMode = FEngine::UboBatchingMode;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
const char* name) noexcept if (batchingMode != UboBatchingMode::DEFAULT) {
: FMaterialInstance(engine, material, name, return batchingMode == UboBatchingMode::UBO_BATCHING;
engine.features.material.enable_material_instance_uniform_batching) { }
return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
} }
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
const char* name, bool useUboBatching) noexcept : mMaterial(material), UboBatchingMode batchingMode) noexcept
: mMaterial(material),
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()), mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK), mCulling(CullingMode::BACK),
mShadowCulling(CullingMode::BACK), mShadowCulling(CullingMode::BACK),
@@ -76,10 +80,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false), mHasScissor(false),
mIsDoubleSided(false), mIsDoubleSided(false),
mIsDefaultInstance(false), mIsDefaultInstance(false),
mUseUboBatching(useUboBatching), mUseUboBatching(
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
mTransparencyMode(TransparencyMode::DEFAULT), mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) { mName(name ? CString(name) : material->getName()) {
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
<< "UBO batching is not enabled.";
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
// even if the material doesn't have any parameters, we allocate a small UBO because it's // even if the material doesn't have any parameters, we allocate a small UBO because it's
@@ -153,7 +159,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUseUboBatching(other->mUseUboBatching), mUseUboBatching(other->mUseUboBatching),
mScissorRect(other->mScissorRect), mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) { mName(name ? CString(name) : other->mName) {
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
FMaterial const* const material = other->getMaterial(); FMaterial const* const material = other->getMaterial();
@@ -193,8 +199,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
} }
} }
FMaterialInstance* FMaterialInstance::duplicate( FMaterialInstance* FMaterialInstance::duplicate(FMaterialInstance const* other,
FMaterialInstance const* other, const char* name) noexcept { const char* name) noexcept {
FMaterial const* const material = other->getMaterial(); FMaterial const* const material = other->getMaterial();
FEngine& engine = material->getEngine(); FEngine& engine = material->getEngine();
return engine.createMaterialInstance(material, other, name); return engine.createMaterialInstance(material, other, name);
@@ -238,15 +244,21 @@ void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driv
void FMaterialInstance::commit(FEngine& engine) const { void FMaterialInstance::commit(FEngine& engine) const {
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) { if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
commit(engine.getDriverApi()); commit(engine.getDriverApi(), engine.getUboManager());
} }
} }
void FMaterialInstance::commit(FEngine::DriverApi& driver) const { void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
if (mUniforms.isDirty() || mHasStreamUniformAssociations) { if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean(); mUniforms.clean();
if (mUseUboBatching) { if (mUseUboBatching) {
// TODO: update the content by `copyToMemoryMappedBuffer` assert_invariant(uboManager != nullptr);
if (!BufferAllocator::isValid(getAllocationId())) {
// The allocation hasn't happened yet, return.
return;
}
uboManager->updateSlot(driver, getAllocationId(), mUniforms.toBufferDescriptor(driver));
} }
else { else {
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData); auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
@@ -271,6 +283,10 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver) const {
// TODO: eventually we should remove this in RELEASE builds // TODO: eventually we should remove this in RELEASE builds
fixMissingSamplers(); fixMissingSamplers();
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
// Commit descriptors if needed (e.g. when textures are updated,or the first time) // Commit descriptors if needed (e.g. when textures are updated,or the first time)
mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver); mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver);
} }
@@ -413,6 +429,13 @@ const char* FMaterialInstance::getName() const noexcept {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const { void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
if (!mDescriptorSet.getHandle()) {
return;
}
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) { if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) {
std::call_once(mMissingSamplersFlag, [this] { std::call_once(mMissingSamplersFlag, [this] {
@@ -439,7 +462,8 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
return; return;
} }
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL); mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL,
{ { mUboOffset }, driver });
} }
void FMaterialInstance::assignUboAllocation( void FMaterialInstance::assignUboAllocation(
@@ -449,8 +473,10 @@ void FMaterialInstance::assignUboAllocation(
assert_invariant(mUseUboBatching); assert_invariant(mUseUboBatching);
mUboData = id; mUboData = id;
mUboOffset = offset;
if (BufferAllocator::isValid(id)) { if (BufferAllocator::isValid(id)) {
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, offset, // Use dynamic offset during binding, so the offset here is always zero.
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, 0,
mUniforms.getSize()); mUniforms.getSize());
} }
} }
+5 -7
View File
@@ -56,11 +56,8 @@ class FTexture;
class FMaterialInstance : public MaterialInstance { class FMaterialInstance : public MaterialInstance {
public: public:
FMaterialInstance(FEngine& engine, FMaterial const* material, FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
const char* name) noexcept; FEngine::UboBatchingMode batchingMode) 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(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete; FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete; FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
@@ -75,7 +72,7 @@ public:
void commit(FEngine& engine) const; void commit(FEngine& engine) const;
void commit(FEngine::DriverApi& driver) const; void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
void use(FEngine::DriverApi& driver, Variant variant = {}) const; void use(FEngine::DriverApi& driver, Variant variant = {}) const;
@@ -287,6 +284,7 @@ private:
}; };
std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData; std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData;
BufferAllocator::allocation_size_t mUboOffset = 0;
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters; tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
mutable DescriptorSet mDescriptorSet; mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms; UniformBuffer mUniforms;
@@ -308,7 +306,7 @@ private:
bool mHasScissor : 1; bool mHasScissor : 1;
bool mIsDoubleSided : 1; bool mIsDoubleSided : 1;
bool mIsDefaultInstance : 1; bool mIsDefaultInstance : 1;
bool mUseUboBatching : 1; const bool mUseUboBatching : 1;
TransparencyMode mTransparencyMode : 2; TransparencyMode mTransparencyMode : 2;
uint64_t mMaterialSortingKey = 0; uint64_t mMaterialSortingKey = 0;
+4
View File
@@ -452,6 +452,8 @@ void FRenderer::endFrame() {
mFrameInfoManager.endFrame(driver); mFrameInfoManager.endFrame(driver);
mFrameSkipper.submitFrame(driver); mFrameSkipper.submitFrame(driver);
engine.submitFrame();
driver.endFrame(mFrameId); driver.endFrame(mFrameId);
// gives the backend a chance to execute periodic tasks // gives the backend a chance to execute periodic tasks
@@ -587,6 +589,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
// happen with Renderer::beginFrame/endFrame. // happen with Renderer::beginFrame/endFrame.
renderInternal(view, true); renderInternal(view, true);
engine.submitFrame();
driver.endFrame(mFrameId); driver.endFrame(mFrameId);
// engine.flush() has already been called by renderInternal(), but we need an extra one // engine.flush() has already been called by renderInternal(), but we need an extra one
+1 -1
View File
@@ -302,7 +302,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
if (!mBufferObjectsEnabled) { if (!mBufferObjectsEnabled) {
// If buffer objects are not enabled at the API level, then we create them internally. // If buffer objects are not enabled at the API level, then we create them internally.
#pragma nounroll #pragma nounroll
for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) { for (size_t index = 0; index < MAX_VERTEX_ATTRIBUTE_COUNT; ++index) {
size_t const i = mAttributes[index].buffer; size_t const i = mAttributes[index].buffer;
if (i != Attribute::BUFFER_UNUSED) { if (i != Attribute::BUFFER_UNUSED) {
assert_invariant(bufferSizes[i] > 0); assert_invariant(bufferSizes[i] > 0);
+1 -1
View File
@@ -39,7 +39,7 @@ void PostProcessDescriptorSet::init(FEngine& engine) noexcept {
// create the descriptor-set layout // create the descriptor-set layout
mDescriptorSetLayout = filament::DescriptorSetLayout{ mDescriptorSetLayout = filament::DescriptorSetLayout{
engine.getDescriptorSetLayoutFactory(), engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), descriptor_sets::getPostProcessLayout() }; engine.getDriverApi(), descriptor_sets::getDepthVariantLayout() };
// create the descriptor-set from the layout // create the descriptor-set from the layout
mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout }; mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout };
@@ -28,9 +28,9 @@
namespace filament::descriptor_sets { namespace filament::descriptor_sets {
backend::DescriptorSetLayout const& getPostProcessLayout() noexcept;
backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept; backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept;
backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept; backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept;
backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept; backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayout( backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
+3 -16
View File
@@ -40,12 +40,7 @@ namespace filament::descriptor_sets {
using namespace backend; using namespace backend;
// used for post-processing passes // used to generate shadow-maps, structure and postfx 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 = { static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS }, { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}; };
@@ -142,10 +137,6 @@ static const std::unordered_map<
{{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL } {{ 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 // used to generate shadow-maps
static DescriptorSetLayout const depthVariantDescriptorSetLayout{ static DescriptorSetLayout const depthVariantDescriptorSetLayout{
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
@@ -163,10 +154,6 @@ static DescriptorSetLayout perRenderableDescriptorSetLayout = {
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
}; };
DescriptorSetLayout const& getPostProcessLayout() noexcept {
return postProcessDescriptorSetLayout;
}
DescriptorSetLayout const& getDepthVariantLayout() noexcept { DescriptorSetLayout const& getDepthVariantLayout() noexcept {
return depthVariantDescriptorSetLayout; return depthVariantDescriptorSetLayout;
} }
@@ -280,10 +267,10 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
return layout; return layout;
} }
case MaterialDomain::POST_PROCESS: case MaterialDomain::POST_PROCESS:
return postProcessDescriptorSetLayout; return depthVariantDescriptorSetLayout;
case MaterialDomain::COMPUTE: case MaterialDomain::COMPUTE:
// TODO: what's the layout for compute? // TODO: what's the layout for compute?
return postProcessDescriptorSetLayout; return depthVariantDescriptorSetLayout;
} }
} }
+3 -3
View File
@@ -129,9 +129,9 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
DescriptorSetLayout layout; DescriptorSetLayout layout;
layout.bindings.reserve(1 + samplers.size()); layout.bindings.reserve(1 + samplers.size());
layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::UNIFORM_BUFFER, layout.bindings.push_back(DescriptorSetLayoutBinding{ DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 }); +PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::DYNAMIC_OFFSET, 0 });
for (auto const& sampler: samplers) { for (auto const& sampler: samplers) {
DescriptorSetLayoutBinding layoutBinding{ DescriptorSetLayoutBinding layoutBinding{
@@ -225,7 +225,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER)); f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)); f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(0); f.writeUint8(0);
f.writeUint8(uint8_t(DescriptorFlags::NONE)); f.writeUint8(uint8_t(DescriptorFlags::DYNAMIC_OFFSET));
f.writeUint16(0); f.writeUint16(0);
// all the material's sampler descriptors // all the material's sampler descriptors
+3 -3
View File
@@ -281,7 +281,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << toString(UniformType(fieldType)) << setw(shortAlignment) << toString(UniformType(fieldType))
<< arraySizeToString(fieldSize) << arraySizeToString(fieldSize)
<< setw(shortAlignment) << toString(Precision(fieldPrecision)) << setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -325,7 +325,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << +fieldBinding << setw(shortAlignment) << +fieldBinding
<< setw(shortAlignment) << toString(SamplerType(fieldType)) << setw(shortAlignment) << toString(SamplerType(fieldType))
<< setw(shortAlignment) << toString(Precision(fieldPrecision)) << setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -364,7 +364,7 @@ static bool printConstantInfo(ostream& text, const ChunkContainer& container) {
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << toString(ConstantType(fieldType)) << setw(shortAlignment) << toString(ConstantType(fieldType))
<< endl; << endl;
} }