vk: improve framebuffer change

This commit is contained in:
Powei Feng
2026-03-16 22:49:43 -07:00
parent 48ee727c8d
commit 36c5d57b5e
10 changed files with 231 additions and 285 deletions

View File

@@ -42,7 +42,7 @@
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 352> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
// TODO WebGPU examine right size of handles
#define HandleAllocatorWGPU HandleAllocator<64, 160, 552> // ~1820 / pool / MiB

View File

@@ -95,9 +95,9 @@
#endif
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_RESOURCE_LEAK)
#else
#define FVK_DEBUG_FLAGS 0
#define FVK_DEBUG_FLAGS (FVK_DEBUG_RESOURCE_LEAK)
#endif
// Override the debug flags if we are forcing profiling mode

View File

@@ -248,7 +248,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
// swap the content later when createDefaultRenderTarget() is called. This frees
// createDefaultRenderTarget() from being ordered with makeCurrent().
mDefaultRenderTarget(
fvkmemory::resource_ptr<VulkanRenderTarget>::construct(&mResourceManager)),
fvkmemory::resource_ptr<VulkanRenderTarget>::construct(&mResourceManager, mPlatform->getDevice())),
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
mContext(context),
@@ -484,6 +484,9 @@ void VulkanDriver::endFrame(uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
endCommandRecording();
collectGarbage();
//#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
// mResourceManager.print();
//#endif
}
void VulkanDriver::updateDescriptorSetBuffer(
@@ -1981,15 +1984,9 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
mFramebufferCache.getRenderPass(rpkey, &mResourceManager);
mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
fbkey.renderPass = renderPass->getVkRenderPass();
fbkey.layers = 1;
rt->emitBarriersBeginRenderPass(*commandBuffer);
fvkmemory::resource_ptr<VulkanFramebuffer> vkfb =
mFramebufferCache.getFramebuffer(fbkey, &mResourceManager, rt);
VkFramebuffer vkfb = rt->getFramebuffer(renderPass->getVkRenderPass(), commandBuffer);
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
@@ -2003,13 +2000,12 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// The current command buffer now has references to the render target and its attachments.
commandBuffer->acquire(rt);
commandBuffer->acquire(renderPass);
commandBuffer->acquire(vkfb);
// Populate the structures required for vkCmdBeginRenderPass.
VkRenderPassBeginInfo renderPassInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = renderPass->getVkRenderPass(),
.framebuffer = vkfb->getVkFramebuffer(),
.framebuffer = vkfb,
// The renderArea field constrains the LoadOp, but scissoring does not.
// Therefore, we do not set the scissor rect here, we only need it in draw().
@@ -2023,10 +2019,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
1] = {};
if (clearVal != TargetBufferFlags::NONE) {
// NOTE: clearValues must be populated in the same order as the attachments array in
// VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is
// VulkanRenderTarget::getFramebuffer. Values must be provided regardless of whether Vulkan is
// actually clearing that particular target.
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (fbkey.color[i]) {
if (rpkey.colorFormat[i] != VK_FORMAT_UNDEFINED) {
VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.color.float32[0] = params.clearColor.r;
clearValue.color.float32[1] = params.clearColor.g;
@@ -2040,7 +2036,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
renderPassInfo.clearValueCount++;
}
}
if (fbkey.depth) {
if (rpkey.depthFormat != VK_FORMAT_UNDEFINED) {
VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.depthStencil = {(float) params.clearDepth, 0};
}
@@ -2103,7 +2099,7 @@ void VulkanDriver::nextSubpass(int) {
++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment& subpassInput = renderTarget->getColor0();
VulkanAttachment const& subpassInput = renderTarget->getColor0();
mDescriptorSetCache.updateInputAttachment({}, subpassInput);
}
}
@@ -2710,9 +2706,6 @@ void VulkanDriver::acquireNextSwapchainImage() {
bool resized = false;
mCurrentSwapChain->acquire(resized);
if (resized) {
mFramebufferCache.resetFramebuffers();
}
// Note that ordering this after the above lines is necessary since we set the swapchain image
// to the render target in bindSwapChain().
mDefaultRenderTarget->bindSwapChain(mCurrentSwapChain);

View File

@@ -48,85 +48,14 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
return true;
}
bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k2) const {
if (k1.renderPass != k2.renderPass) return false;
if (k1.width != k2.width) return false;
if (k1.height != k2.height) return false;
if (k1.layers != k2.layers) return false;
if (k1.samples != k2.samples) return false;
if (k1.depth != k2.depth) return false;
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (k1.color[i] != k2.color[i]) return false;
if (k1.resolve[i] != k2.resolve[i]) return false;
}
return true;
}
VulkanFboCache::VulkanFboCache(VkDevice device)
: mDevice(device) {}
VulkanFboCache::~VulkanFboCache() {
FILAMENT_CHECK_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty())
FILAMENT_CHECK_POSTCONDITION(mRenderPassCache.empty())
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(FboKey const& config,
fvkmemory::ResourceManager* resManager,
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end())) {
iter.value().timestamp = mCurrentTime;
return iter->second.handle;
}
// The attachment list contains: Color Attachments, Resolve Attachments, and Depth Attachment.
// For simplicity, create an array that can hold the maximum possible number of attachments.
// Note that this needs to have the same ordering as the corollary array in getRenderPass.
VkImageView attachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1];
uint32_t attachmentCount = 0;
for (VkImageView attachment : config.color) {
if (attachment) {
attachments[attachmentCount++] = attachment;
}
}
for (VkImageView attachment : config.resolve) {
if (attachment) {
attachments[attachmentCount++] = attachment;
}
}
if (config.depth) {
attachments[attachmentCount++] = config.depth;
}
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
FVK_LOGD << "Creating framebuffer " << config.width << "x" << config.height << " "
<< "for render pass " << config.renderPass << ", "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (config.depth ? 1 : 0) << ", "
<< "attachmentCount = " << attachmentCount;
#endif
VkFramebufferCreateInfo info {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = config.renderPass,
.attachmentCount = attachmentCount,
.pAttachments = attachments,
.width = config.width,
.height = config.height,
.layers = config.layers,
};
mRenderPassRefCount[info.renderPass]++;
VkFramebuffer framebuffer;
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
fvkmemory::resource_ptr<VulkanFramebuffer> fbh =
fvkmemory::resource_ptr<VulkanFramebuffer>::construct(resManager, mDevice, framebuffer,
renderTarget);
mFramebufferCache[config] = { fbh, mCurrentTime };
return fbh;
}
fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
auto iter = mRenderPassCache.find(config);
@@ -355,16 +284,7 @@ fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
return rph;
}
void VulkanFboCache::resetFramebuffers() noexcept {
for (const auto& pair: mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--;
}
mFramebufferCache.clear();
}
void VulkanFboCache::terminate() noexcept {
resetFramebuffers();
mRenderPassRefCount.clear();
mRenderPassCache.clear();
}
@@ -381,18 +301,6 @@ void VulkanFboCache::gc() noexcept {
}
const uint32_t evictTime = mCurrentTime - TIME_BEFORE_EVICTION;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end(); ) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
} else {
++iter;
}
}
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {

View File

@@ -30,7 +30,6 @@
namespace filament::backend {
struct VulkanFramebuffer;
struct VulkanRenderPass;
// Simple manager for VkFramebuffer and VkRenderPass objects.
@@ -75,39 +74,12 @@ public:
bool operator()(const RenderPassKey& k1, const RenderPassKey& k2) const;
};
// FboKey is a small POD representing the immutable state that we wish to configure
// in VkFramebuffer. It is hashed and used as a lookup key. There are several attachments, but
// rather than storing a count, we simply zero out the unused slots.
struct alignas(8) FboKey {
VkRenderPass renderPass; // 8 bytes
uint16_t width; // 2 bytes
uint16_t height; // 2 bytes
uint16_t layers; // 2 bytes
uint16_t samples; // 2 bytes
VkImageView color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 64 bytes
VkImageView resolve[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 64 bytes
VkImageView depth; // 8 bytes
};
struct FboVal {
fvkmemory::resource_ptr<VulkanFramebuffer> handle;
uint32_t timestamp;
};
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
static_assert(sizeof(VkImageView) == 8, "VkImageView has unexpected size.");
static_assert(sizeof(FboKey) == 152, "FboKey has unexpected size.");
using FboKeyHashFn = utils::hash::MurmurHashFn<FboKey>;
struct FboKeyEqualFn {
bool operator()(const FboKey& k1, const FboKey& k2) const;
};
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle.
fvkmemory::resource_ptr<VulkanFramebuffer> getFramebuffer(FboKey const& config,
fvkmemory::ResourceManager* resManager,
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget) noexcept;
// Retrieves or creates a VkRenderPass handle.
fvkmemory::resource_ptr<VulkanRenderPass> getRenderPass(
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
@@ -115,17 +87,11 @@ public:
// Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept;
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
void resetFramebuffers() noexcept;
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
void terminate() noexcept;
private:
VkDevice mDevice;
using FboMap = tsl::robin_map<FboKey, FboVal, FboKeyHashFn, FboKeyEqualFn>;
FboMap mFramebufferCache;
using RenderPassMap = tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq>;
RenderPassMap mRenderPassCache;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;

View File

@@ -27,7 +27,9 @@
#include "vulkan/utils/Definitions.h"
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Spirv.h"
#include "vulkan/vulkan_core.h"
#include <algorithm>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h> // UTILS_FALLTHROUGH
@@ -398,19 +400,25 @@ void VulkanProgram::flushPushConstants(VkPipelineLayout layout) {
}
// Creates a special "default" render target (i.e. associated with the swap chain)
VulkanRenderTarget::VulkanRenderTarget()
VulkanRenderTarget::VulkanRenderTarget(VkDevice device)
: HwRenderTarget(0, 0),
mOffscreen(false),
mProtected(false),
mInfo(std::make_unique<Auxiliary>()) {
mInfo->rpkey.samples = mInfo->fbkey.samples = 1;
mDevice(device) {
assert_invariant(device != VK_NULL_HANDLE);
mRpKey.samples = 1;
}
VulkanRenderTarget::~VulkanRenderTarget() = default;
VulkanRenderTarget::~VulkanRenderTarget() {
if (mDevice == VK_NULL_HANDLE) {
return;
}
for (auto const& [renderpass, framebuffer, fence] : mVkFrameBuffers) {
vkDestroyFramebuffer(mDevice, framebuffer, VKALLOC);
}
}
void VulkanRenderTarget::bindSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
assert_invariant(!mOffscreen);
assert_invariant(!mInfo->colors[0]);
assert_invariant(!mColors[0]);
VkExtent2D const extent = swapchain->getExtent();
width = extent.width;
@@ -418,36 +426,50 @@ void VulkanRenderTarget::bindSwapChain(fvkmemory::resource_ptr<VulkanSwapChain>
mProtected = swapchain->isProtected();
VulkanAttachment color = createSwapchainAttachment(swapchain->getCurrentColor());
assert_invariant(mInfo->attachments.size() == 0);
mInfo->attachments.push_back(color);
assert_invariant(mAttachments.size() == 0);
mAttachments.push_back(color);
auto& fbkey = mInfo->fbkey;
auto& rpkey = mInfo->rpkey;
auto& rpkey = mRpKey;
rpkey.colorFormat[0] = color.getFormat();
rpkey.viewCount = color.layerCount;
fbkey.width = width;
fbkey.height = height;
fbkey.color[0] = color.getImageView();
fbkey.resolve[0] = VK_NULL_HANDLE;
mFboInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
mFboInfo.width = width;
mFboInfo.height = height;
mFboInfo.layers = 1;
mFboInfo.pAttachments = mFboAttachments;
uint32_t attachmentCount = 0;
mFboAttachments[attachmentCount++] = color.getImageView();
if (swapchain->getDepth()) {
VulkanAttachment depth = createSwapchainAttachment(swapchain->getDepth());
mInfo->attachments.push_back(depth);
mInfo->depthIndex = 1;
mAttachments.push_back(depth);
mDepthIndex = 1;
rpkey.depthFormat = depth.getFormat();
fbkey.depth = depth.getImageView();
mFboAttachments[attachmentCount++] = depth.getImageView();
} else {
rpkey.depthFormat = VK_FORMAT_UNDEFINED;
fbkey.depth = VK_NULL_HANDLE;
}
mInfo->colors.set(0);
mFboInfo.attachmentCount = attachmentCount;
mColors.set(0);
std::vector<FboData> nbuffers;
for (auto const& [renderpass, framebuffer, fence] : mVkFrameBuffers) {
if (fence->getStatus() == VK_SUCCESS) {
vkDestroyFramebuffer(mDevice, framebuffer, VKALLOC);
} else {
nbuffers.push_back({renderpass,framebuffer,fence});
}
}
std::swap(mVkFrameBuffers, nbuffers);
}
void VulkanRenderTarget::releaseSwapchain() {
mInfo->colors = {};
mInfo->attachments.clear();
mColors = {};
mAttachments.clear();
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
@@ -456,9 +478,10 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
: HwRenderTarget(width, height),
mDevice(device),
mOffscreen(true),
mProtected(false),
mInfo(std::make_unique<Auxiliary>()) {
mProtected(false) {
assert_invariant(device != VK_NULL_HANDLE);
auto& depth = depthStencil[0];
// Constrain the sample count according to both kinds of sample count masks obtained from
@@ -467,19 +490,25 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
samples = fvkutils::reduceSampleCount(samples,
limits.framebufferDepthSampleCounts & limits.framebufferColorSampleCounts);
auto& rpkey = mInfo->rpkey;
auto& rpkey = mRpKey;
rpkey.samples = samples;
rpkey.depthFormat = depth.getFormat();
rpkey.viewCount = layerCount;
auto& fbkey = mInfo->fbkey;
fbkey.width = width;
fbkey.height = height;
fbkey.samples = samples;
auto& fboInfo = mFboInfo;
fboInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fboInfo.width = width;
fboInfo.height = height;
fboInfo.layers = 1;
fboInfo.pAttachments = mFboAttachments;
std::vector<VulkanAttachment>& attachments = mInfo->attachments;
std::vector<VulkanAttachment>& attachments = mAttachments;
std::vector<VulkanAttachment> msaaAttachments;
VkImageView colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VkImageView resolveAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VkImageView depthAttachment = VK_NULL_HANDLE;
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
VulkanAttachment& attachment = color[index];
auto texture = attachment.texture;
@@ -491,11 +520,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
mProtected |= texture->getIsProtected();
attachments.push_back(attachment);
mInfo->colors.set(index);
mColors.set(index);
rpkey.colorFormat[index] = attachment.getFormat();
fbkey.color[index] = attachment.getImageView();
fbkey.resolve[index] = VK_NULL_HANDLE;
colorAttachments[index] = attachment.getImageView();
resolveAttachments[index] = VK_NULL_HANDLE;
if (samples > 1) {
VulkanAttachment msaaAttachment = {};
@@ -513,30 +542,30 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
.layerCount = layerCount,
};
fbkey.resolve[index] = attachment.getImageView();
resolveAttachments[index] = attachment.getImageView();
} else {
msaaAttachment = {
.texture = texture,
.layerCount = layerCount,
};
}
fbkey.color[index] = msaaAttachment.getImageView();
colorAttachments[index] = msaaAttachment.getImageView();
msaaAttachments.push_back(msaaAttachment);
}
}
if (attachments.size() > 0 && samples > 1 && msaaAttachments.size() > 0) {
mInfo->msaaIndex = (uint8_t) attachments.size();
mMsaaIndex = (uint8_t) attachments.size();
attachments.insert(attachments.end(), msaaAttachments.begin(), msaaAttachments.end());
}
if (depth.texture) {
auto depthTexture = depth.texture;
mInfo->depthIndex = (uint8_t) attachments.size();
mDepthIndex = (uint8_t) attachments.size();
attachments.push_back(depth);
fbkey.depth = depth.getImageView();
depthAttachment = depth.getImageView();
if (samples > 1) {
mInfo->msaaDepthIndex = mInfo->depthIndex;
mMsaaDepthIndex = mDepthIndex;
if (depthTexture->samples == 1) {
// MSAA depth texture must have the mipmap count of 1
uint8_t const msLevel = 1;
@@ -544,16 +573,54 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
// exist.
auto msaaTexture = initMsaaTexture(depthTexture, device, physicalDevice, context,
allocator, commands, resourceManager, msLevel, samples, stagePool);
mInfo->msaaDepthIndex = (uint8_t) attachments.size();
mMsaaDepthIndex = (uint8_t) attachments.size();
VulkanAttachment msaaAttachment = {
.texture = msaaTexture,
.layerCount = layerCount,
};
attachments.push_back(msaaAttachment);
fbkey.depth = msaaAttachment.getImageView();
depthAttachment = msaaAttachment.getImageView();
}
}
}
uint32_t attachmentCount = 0;
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
if (colorAttachments[index]) {
mFboAttachments[attachmentCount++] = colorAttachments[index];
}
}
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
if (resolveAttachments[index]) {
mFboAttachments[attachmentCount++] = resolveAttachments[index];
}
}
if (depthAttachment) {
mFboAttachments[attachmentCount++] = depthAttachment;
}
fboInfo.attachmentCount = attachmentCount;
}
VkFramebuffer VulkanRenderTarget::getFramebuffer(VkRenderPass renderPass, VulkanCommandBuffer* commands) {
if (mOffscreen) {
for (auto& [rp, fb, fence] : mVkFrameBuffers) {
if (rp == renderPass) {
fence = commands->getFenceStatus();
return fb;
}
}
}
VkDevice device = mDevice;
mFboInfo.renderPass = renderPass;
VkFramebuffer framebuffer;
VkResult error = vkCreateFramebuffer(device, &mFboInfo, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
mVkFrameBuffers.push_back({renderPass, framebuffer, commands->getFenceStatus()});
return framebuffer;
}
void VulkanRenderTarget::transformClientRectToPlatform(VkRect2D* bounds) const {
@@ -570,10 +637,10 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& p
return 1;
}
if (pass.currentSubpass == 1) {
return mInfo->colors.count();
return mColors.count();
}
uint8_t count = 0;
mInfo->colors.forEachSetBit([&count, &pass](size_t index) {
mColors.forEachSetBit([&count, &pass](size_t index) {
if (!(pass.params.subpassMask & (1 << index))) {
count++;
}
@@ -582,8 +649,7 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& p
}
void VulkanRenderTarget::emitBarriersBeginRenderPass(VulkanCommandBuffer& commands) {
auto& attachments = mInfo->attachments;
auto samples = mInfo->fbkey.samples;
auto samples = mRpKey.samples;
auto barrier = [&commands](VulkanAttachment& attachment, VulkanLayout const layout) {
auto tex = attachment.texture;
auto const& range = attachment.getSubresourceRange();
@@ -594,24 +660,24 @@ void VulkanRenderTarget::emitBarriersBeginRenderPass(VulkanCommandBuffer& comman
}
};
for (size_t i = 0, count = mInfo->colors.count(); i < count; ++i) {
auto& attachment = attachments[i];
for (size_t i = 0, count = mColors.count(); i < count; ++i) {
auto& attachment = mAttachments[i];
auto tex = attachment.texture;
if (samples == 1 || tex->samples == 1) {
barrier(attachment, VulkanLayout::COLOR_ATTACHMENT);
}
}
if (mInfo->msaaIndex != Auxiliary::UNDEFINED_INDEX) {
for (size_t i = mInfo->msaaIndex, count = mInfo->msaaIndex + mInfo->colors.count();
if (mMsaaIndex != UNDEFINED_INDEX) {
for (size_t i = mMsaaIndex, count = mMsaaIndex + mColors.count();
i < count; ++i) {
barrier(attachments[i], VulkanLayout::COLOR_ATTACHMENT);
barrier(mAttachments[i], VulkanLayout::COLOR_ATTACHMENT);
}
}
if (mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX) {
barrier(attachments[mInfo->depthIndex], VulkanLayout::DEPTH_ATTACHMENT);
if (mDepthIndex != UNDEFINED_INDEX) {
barrier(mAttachments[mDepthIndex], VulkanLayout::DEPTH_ATTACHMENT);
}
if (mInfo->msaaDepthIndex != Auxiliary::UNDEFINED_INDEX) {
barrier(attachments[mInfo->msaaDepthIndex], VulkanLayout::DEPTH_ATTACHMENT);
if (mMsaaDepthIndex != UNDEFINED_INDEX) {
barrier(mAttachments[mMsaaDepthIndex], VulkanLayout::DEPTH_ATTACHMENT);
}
}
@@ -620,7 +686,7 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
return;
}
for (auto& attachment: mInfo->attachments) {
for (auto& attachment: mAttachments) {
auto const& range = attachment.getSubresourceRange();
bool const isDepth = attachment.isDepth();
auto texture = attachment.texture;
@@ -715,16 +781,6 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
vertexBuffer(vb),
indexBuffer(ib) {}
VulkanFramebuffer::VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer,
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget)
: mDevice(device),
mFramebuffer(framebuffer),
mRenderTarget(renderTarget) {}
VulkanFramebuffer::~VulkanFramebuffer() {
vkDestroyFramebuffer(mDevice, mFramebuffer, VKALLOC);
}
VulkanRenderPass::VulkanRenderPass(VkDevice device, VkRenderPass renderPass)
: mDevice(device), mRenderPass(renderPass) {}

View File

@@ -26,6 +26,7 @@
#include "VulkanFboCache.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
#include "utils/compiler.h"
#include "vulkan/VulkanCommands.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h"
@@ -35,6 +36,7 @@
#include <backend/Program.h>
#include <utils/FixedCapacityVector.h>
#include <utils/LruCache.h>
#include <utils/Mutex.h>
#include <utils/StructureOfArrays.h>
#include <utils/bitset.h>
@@ -372,12 +374,10 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
~VulkanRenderTarget();
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
explicit VulkanRenderTarget(VkDevice device);
VulkanRenderTarget(VulkanRenderTarget&& target)
: HwRenderTarget(0, 0),
mOffscreen(false),
mProtected(false) {
: HwRenderTarget(0, 0) {
swap(std::move(target));
}
@@ -394,34 +394,30 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
return {width, height};
}
inline VulkanAttachment& getColor0() const {
assert_invariant(mInfo->colors[0]);
return mInfo->attachments[0];
inline VulkanAttachment const& getColor0() const {
assert_invariant(mColors[0]);
return mAttachments[0];
}
inline VulkanAttachment& getDepth() const {
inline VulkanAttachment const& getDepth() const {
assert_invariant(hasDepth());
if (mInfo->fbkey.samples == 1) {
return mInfo->attachments[mInfo->depthIndex];
if (mRpKey.samples == 1) {
return mAttachments[mDepthIndex];
}
return mInfo->attachments[mInfo->msaaDepthIndex];
return mAttachments[mMsaaDepthIndex];
}
inline VulkanFboCache::RenderPassKey const& getRenderPassKey() const {
return mInfo->rpkey;
}
inline VulkanFboCache::FboKey const& getFboKey() const {
return mInfo->fbkey;
return mRpKey;
}
inline uint8_t getSamples() const {
return mInfo->fbkey.samples;
return mRpKey.samples;
}
uint8_t getColorTargetCount(VulkanRenderPassContext const& pass) const;
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
inline bool hasDepth() const { return mDepthIndex != UNDEFINED_INDEX; }
inline bool isSwapChain() const { return !mOffscreen; }
inline bool isProtected() const { return mProtected; }
@@ -431,41 +427,65 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
void releaseSwapchain();
bool isSwapchainBound() const {
return isSwapChain() && mInfo->colors[0];
return isSwapChain() && mColors[0];
}
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
void emitBarriersEndRenderPass(VulkanCommandBuffer& commands);
VkFramebuffer getFramebuffer(VkRenderPass renderPass, VulkanCommandBuffer* commands);
private:
void swap(VulkanRenderTarget&& target) {
std::swap(width, target.width);
std::swap(height, target.height);
assert_invariant(target.mDevice != VK_NULL_HANDLE);
//assert_invariant(mDevice != VK_NULL_HANDLE);
std::swap(mDevice, target.mDevice);
std::swap(mColors, target.mColors);
std::swap(mDepthIndex, target.mDepthIndex);
std::swap(mMsaaDepthIndex, target.mMsaaDepthIndex);
std::swap(mMsaaIndex, target.mMsaaIndex);
std::swap(mOffscreen, target.mOffscreen);
std::swap(mProtected, target.mProtected);
std::swap(mInfo, target.mInfo);
std::swap(mAttachments, target.mAttachments);
std::swap(mRpKey, target.mRpKey);
std::swap(mFboInfo, target.mFboInfo);
std::swap(mFboAttachments, target.mFboAttachments);
}
struct Auxiliary {
static constexpr int8_t UNDEFINED_INDEX = -1;
static constexpr int8_t UNDEFINED_INDEX = -1;
explicit Auxiliary() noexcept = default;
VkDevice mDevice = VK_NULL_HANDLE; // 8 bytes
utils::bitset32 mColors; // 4
int8_t mDepthIndex = UNDEFINED_INDEX; // 1
int8_t mMsaaDepthIndex = UNDEFINED_INDEX; // 1
int8_t mMsaaIndex = UNDEFINED_INDEX; // 1
bool mOffscreen = false; // 1
VulkanFboCache::RenderPassKey rpkey = {};
VulkanFboCache::FboKey fbkey = {};
std::vector<VulkanAttachment> attachments;
utils::bitset32 colors;
int8_t depthIndex = UNDEFINED_INDEX;
int8_t msaaDepthIndex = UNDEFINED_INDEX;
int8_t msaaIndex = UNDEFINED_INDEX;
struct FboData {
VkRenderPass renderpass;
VkFramebuffer framebuffer;
std::shared_ptr<VulkanCmdFence> fence;
};
bool mOffscreen;
bool mProtected;
std::unique_ptr<Auxiliary> mInfo;
std::vector<FboData> mVkFrameBuffers;
std::vector<VulkanAttachment> mAttachments; // 24 bytes
VulkanFboCache::RenderPassKey mRpKey = {}; // 56 bytes
VkFramebufferCreateInfo mFboInfo = {}; // 64 bytes
VkImageView mFboAttachments[
MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {}; // 136
bool mProtected = false; // 1
UTILS_UNUSED bool padding[3] = {}; // 3
};
static_assert(sizeof(VulkanRenderTarget) == 352);
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, fvkmemory::Resource {
@@ -585,26 +605,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
};
struct VulkanFramebuffer : public fvkmemory::Resource {
VulkanFramebuffer(VkDevice device,
VkFramebuffer framebuffer,
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget);
~VulkanFramebuffer();
inline VkFramebuffer getVkFramebuffer() const noexcept {
return mFramebuffer;
}
private:
VkDevice mDevice;
VkFramebuffer mFramebuffer;
// We need to keep a reference to the renderTarget because the key of the framebuffer in the
// cache has references to the image views that are derived from the textures of the render
// target.
fvkmemory::resource_ptr<VulkanRenderTarget> mRenderTarget;
};
struct VulkanRenderPass : public fvkmemory::Resource {
VulkanRenderPass(VkDevice device, VkRenderPass renderPass);
~VulkanRenderPass();

View File

@@ -42,7 +42,6 @@ template ResourceType getTypeEnum<VulkanSync>() noexcept;
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
template ResourceType getTypeEnum<VulkanStream>() noexcept;
template ResourceType getTypeEnum<VulkanFramebuffer>() noexcept;
template ResourceType getTypeEnum<VulkanRenderPass>() noexcept;
template<typename D>
@@ -110,9 +109,6 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanStream>) {
return ResourceType::STREAM;
}
if constexpr (std::is_same_v<D, VulkanFramebuffer>) {
return ResourceType::FRAMEBUFFER;
}
if constexpr (std::is_same_v<D, VulkanRenderPass>) {
return ResourceType::RENDER_PASS;
}
@@ -163,8 +159,6 @@ std::string_view getTypeStr(ResourceType type) {
return "Semaphore";
case ResourceType::STREAM:
return "VulkanStream";
case ResourceType::FRAMEBUFFER:
return "Framebuffer";
case ResourceType::RENDER_PASS:
return "RenderPass";
case ResourceType::UNDEFINED_TYPE:

View File

@@ -56,9 +56,8 @@ enum class ResourceType : uint8_t {
MEMORY_MAPPED_BUFFER = 18,
SEMAPHORE = 19,
STREAM = 20,
FRAMEBUFFER = 21,
RENDER_PASS = 22,
UNDEFINED_TYPE = 23, // Must be the last enum because we use it for iterating over the enums.
RENDER_PASS = 21,
UNDEFINED_TYPE = 22, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>

View File

@@ -20,6 +20,8 @@
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <algorithm>
namespace filament::backend::fvkmemory {
namespace {
@@ -34,9 +36,24 @@ ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck
void ResourceManager::gc() noexcept {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("ResourceManager::gc");
auto destroyAll = [this](GcList& list) {
for (auto const& [type, id]: list) {
destroyWithType(type, id);
uint32_t counts[(size_t) ResourceType::UNDEFINED_TYPE] = {};
auto destroyAll = [this, &counts](GcList& list) {
FVK_SYSTRACE_CONTEXT();
if (list.empty()) {
return;
}
std::sort(list.begin(), list.end(), [](auto const& a, auto const& b) {
return a.first < b.first;
});
for (size_t i = 0; i < list.size(); ) {
ResourceType type = list[i].first;
FVK_SYSTRACE_START(getTypeStr(type).data());
while (i < list.size() && list[i].first == type) {
destroyWithType(type, list[i].second);
counts[(size_t) type]++;
i++;
}
FVK_SYSTRACE_END();
}
list.clear();
};
@@ -45,14 +62,30 @@ void ResourceManager::gc() noexcept {
// Note that we're not copying mThreadSafeGcList because the objects here do not have
// resource_ptrs to other handle objects, so their desctruction would not add more elements
// to mThreadSafeGcList.
FVK_SYSTRACE_START("gc::threadSafeList");
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
destroyAll(mThreadSafeGcList);
FVK_SYSTRACE_END();
}
FVK_SYSTRACE_START("gc::mainList");
GcList gcs;
std::swap(gcs, mGcList);
destroyAll(gcs);
FVK_SYSTRACE_END();
bool hasGarbage = false;
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
if (counts[i] > 0) {
if (!hasGarbage) {
LOG(INFO) << "ResourceManager GC freed:";
hasGarbage = true;
}
LOG(INFO) << " " << getTypeStr((ResourceType) i) << "=" << counts[i];
}
}
FVK_SYSTRACE_END();
}
void ResourceManager::terminate() noexcept {
@@ -126,9 +159,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::STREAM:
destruct<VulkanStream>(Handle<VulkanStream>(id));
break;
case ResourceType::FRAMEBUFFER:
destruct<VulkanFramebuffer>(Handle<VulkanFramebuffer>(id));
break;
case ResourceType::RENDER_PASS:
destruct<VulkanRenderPass>(Handle<VulkanRenderPass>(id));
break;
@@ -149,11 +179,11 @@ void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
LOG(ERROR) << "-------------------";
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
LOG(ERROR) << " " << getTypeStr((ResourceType) i) << "=" << COUNTER[i];
}
LOG(ERROR) << "+++++++++++++++++++";
LOG(ERROR) << "-------------------";
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
LOG(ERROR) << " " << getTypeStr((ResourceType) i) << "=" << COUNTER[i];
}
LOG(ERROR) << "+++++++++++++++++++";
#endif
}