Compare commits

..
Author SHA1 Message Date
Powei Feng 910c70f21b vk: record timestmps on command buffers
Timer queries are inserted into the beginning and the end of each
command buffer. Refactor vk's implementation of Filament's timer
queries so that they are now computed as a sum of the time taken
by one or more command buffers (and not direct insertion of queries
into the command stream).

This is to address a potential issue where the timestamps returned
between command buffers are not consistent.
2025-11-12 12:18:30 -08:00
34 changed files with 313 additions and 1333 deletions
-10
View File
@@ -214,18 +214,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanMemory.h
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPipelineStateSerializer.cpp
src/vulkan/VulkanPipelineStateSerializer.h
src/vulkan/VulkanSamplerStateSerializer.h
src/vulkan/VulkanSamplerStateSerializer.cpp
src/vulkan/VulkanRenderPassStateSerializer.h
src/vulkan/VulkanRenderPassStateSerializer.cpp
src/vulkan/VulkanDescriptorSetLayoutSerializer.h
src/vulkan/VulkanDescriptorSetLayoutSerializer.cpp
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/VulkanQueryManager.cpp
src/vulkan/VulkanQueryManager.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
@@ -48,7 +48,7 @@ struct VulkanPlatformPrivate;
// Forward declare the fence status that will be maintained by the command
// buffer manager.
struct VulkanCmdFence;
struct VulkanCmdBufferState;
/**
* A Platform interface that creates a Vulkan backend.
@@ -242,7 +242,7 @@ public:
* @return A Platform::Sync object tracking the provided fence.
*/
virtual Platform::Sync* createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
std::shared_ptr<VulkanCmdBufferState> fenceStatus) noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
@@ -434,7 +434,7 @@ public:
protected:
struct VulkanSync : public Platform::Sync {
VkFence fence;
std::shared_ptr<VulkanCmdFence> fenceStatus;
std::shared_ptr<VulkanCmdBufferState> fenceStatus;
};
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
@@ -29,9 +29,8 @@ using namespace bluevk;
namespace filament::backend {
FenceStatus VulkanCmdFence::wait(VkDevice device, uint64_t const timeout,
std::chrono::steady_clock::time_point const until) {
FenceStatus VulkanCmdBufferState::waitOnFence(VkDevice device, uint64_t const timeout,
std::chrono::steady_clock::time_point const until) {
// this lock MUST be held for READ when calling vkWaitForFences()
std::shared_lock rl(mLock);
@@ -81,7 +80,7 @@ FenceStatus VulkanCmdFence::wait(VkDevice device, uint64_t const timeout,
return FenceStatus::ERROR; // not supported
}
void VulkanCmdFence::resetFence(VkDevice device) {
void VulkanCmdBufferState::resetFence(VkDevice device) {
// This lock prevents vkResetFences() from being called simultaneously with vkWaitForFences(),
// but by construction, when we're here, we know that the fence has signaled and
// vkWaitForFences() will return shortly.
@@ -90,4 +89,35 @@ void VulkanCmdFence::resetFence(VkDevice device) {
vkResetFences(device, 1, &mFence);
}
uint64_t VulkanTimerQuery::getResult() {
{
std::lock_guard<std::mutex> lock(mDurationLock);
if (mDuration != UNKNOWN_QUERY_RESULT) {
return mDuration;
}
}
VulkanCmdBufferState* startState = nullptr;
VulkanCmdBufferState* endState = nullptr;
{
std::shared_lock const l(mMutex);
startState = mBeginState.get();
endState = mEndState.get();
}
// Here we sum up the time taken by each command buffer that were marked by this timer query.
uint64_t duration = 0;
do {
uint64_t const stDuration = startState->getBufferDuration();
assert_invariant(stDuration != VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION);
duration += stDuration;
startState = startState->getNextState().get();
} while (startState != endState);
{
std::lock_guard<std::mutex> lock(mDurationLock);
mDuration = duration;
}
return duration;
}
} // namespace filament::backend
@@ -27,6 +27,7 @@
#include <chrono>
#include <cstdint>
#include <limits>
#include <memory>
#include <mutex>
#include <shared_mutex>
@@ -35,25 +36,36 @@
namespace filament::backend {
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
explicit VulkanCmdFence(VkFence fence) : mFence(fence) { }
~VulkanCmdFence() = default;
// Wrapper to enable use of shared_ptr for implementing shared ownership of Vulkan fences
// and timer query results.
struct VulkanCmdBufferState {
static uint64_t const UNKNOWN_BUFFER_DURATION = std::numeric_limits<uint64_t>::max();
void setStatus(VkResult const value) {
explicit VulkanCmdBufferState(VkFence fence)
: mFence(fence) {
}
~VulkanCmdBufferState() = default;
void setStatus(VkResult const value, uint64_t duration) {
std::lock_guard const l(mLock);
mStatus = value;
mDuration = duration;
mCond.notify_all();
}
VkResult getStatus() {
uint64_t getBufferDuration() {
std::shared_lock const l(mLock);
return mDuration;
}
VkResult getFenceStatus() {
std::shared_lock const l(mLock);
return mStatus;
}
void resetFence(VkDevice device);
FenceStatus wait(VkDevice device, uint64_t timeout,
FenceStatus waitOnFence(VkDevice device, uint64_t timeout,
std::chrono::steady_clock::time_point until);
void cancel() {
@@ -62,6 +74,18 @@ struct VulkanCmdFence {
mCond.notify_all();
}
void setNextState(std::shared_ptr<VulkanCmdBufferState> next) {
mNextState = std::move(next);
}
std::shared_ptr<VulkanCmdBufferState>& getNextState() {
return mNextState;
}
VkFence getVkFence() const {
return mFence;
}
private:
std::shared_mutex mLock; // NOLINT(*-include-cleaner)
std::condition_variable_any mCond;
@@ -70,38 +94,42 @@ private:
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
VkResult mStatus{ VK_INCOMPLETE };
VkFence mFence;
VkFence const mFence;
uint64_t mDuration = UNKNOWN_BUFFER_DURATION;
// We assume that command buffers are serialized. This will point to the state of the next
// buffer. This is necessary so that we can return total duration of multiple command buffers
// when a TimerQuery is requested by the front-end.
std::shared_ptr<VulkanCmdBufferState> mNextState;
};
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
VulkanFence() {}
void setFence(std::shared_ptr<VulkanCmdFence> fence) {
std::lock_guard const l(lock);
sharedFence = std::move(fence);
void setCmdBufferState(std::shared_ptr<VulkanCmdBufferState> state) {
std::lock_guard l(lock);
cmdbufState = std::move(state);
cond.notify_all();
}
std::shared_ptr<VulkanCmdFence>& getSharedFence() {
std::lock_guard const l(lock);
return sharedFence;
std::shared_ptr<VulkanCmdBufferState>& getCmdBufferState() {
std::lock_guard l(lock);
return cmdbufState;
}
std::pair<std::shared_ptr<VulkanCmdFence>, bool>
wait(std::chrono::steady_clock::time_point const until) {
// hold a reference so that our state doesn't disappear while we wait
std::pair<std::shared_ptr<VulkanCmdBufferState>, bool> wait(
std::chrono::steady_clock::time_point const until) {
std::unique_lock l(lock);
cond.wait_until(l, until, [this] {
return bool(sharedFence) || canceled;
});
// here mSharedFence will be null if we timed out
return { sharedFence, canceled };
cond.wait_until(l, until, [&] { return bool(cmdbufState) || canceled; });
// here cmdbufState will be null if we timed out
return { cmdbufState, canceled };
}
void cancel() const {
std::lock_guard const l(lock);
if (sharedFence) {
sharedFence->cancel();
if (cmdbufState) {
cmdbufState->cancel();
}
canceled = true;
cond.notify_all();
@@ -111,10 +139,10 @@ private:
mutable std::mutex lock;
mutable std::condition_variable cond;
mutable bool canceled = false;
std::shared_ptr<VulkanCmdFence> sharedFence;
std::shared_ptr<VulkanCmdBufferState> cmdbufState;
};
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
struct VulkanSync : public HwSync, fvkmemory::ThreadSafeResource {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
@@ -128,38 +156,50 @@ struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
: mStartingQueryIndex(startingIndex),
mStoppingQueryIndex(stoppingIndex) {}
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::lock_guard const lock(mFenceMutex);
mFence = std::move(fence);
static decltype(VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION) const
UNKNOWN_QUERY_RESULT = VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION;
VulkanTimerQuery() {}
void setBeginState(std::shared_ptr<VulkanCmdBufferState> state) {
{
std::lock_guard const lock(mMutex);
mBeginState = std::move(state);
}
{
std::lock_guard<std::mutex> lock(mDurationLock);
mDuration = UNKNOWN_QUERY_RESULT;
}
}
bool isCompleted() noexcept {
std::lock_guard const lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written
// anything into the command buffer, which is an error according to the validation layer
// that ships in the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to
// require that the timestamp has at least been written into a processed command buffer.
// This fence indicates that the corresponding buffer has been completed.
return mFence && mFence->getStatus() == VK_SUCCESS;
void setEndState(std::shared_ptr<VulkanCmdBufferState> state) {
{
std::lock_guard const lock(mMutex);
mEndState = std::move(state);
}
{
std::lock_guard<std::mutex> lock(mDurationLock);
mDuration = UNKNOWN_QUERY_RESULT;
}
}
uint32_t getStartingQueryIndex() const { return mStartingQueryIndex; }
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
bool isComplete() {
std::shared_lock const lock(mMutex);
return mBeginState && mEndState &&
mBeginState->getBufferDuration() != UNKNOWN_QUERY_RESULT &&
mEndState->getBufferDuration() != UNKNOWN_QUERY_RESULT;
}
uint64_t getResult();
private:
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
std::shared_mutex mMutex; // NOLINT(*-include-cleaner)
std::mutex mDurationLock;
std::shared_ptr<VulkanCmdBufferState> mBeginState;
std::shared_ptr<VulkanCmdBufferState> mEndState;
uint64_t mDuration = UNKNOWN_QUERY_RESULT;
};
} // namespace filament::backend
+107 -37
View File
@@ -55,6 +55,22 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
return cmdbuffer;
}
VkFence createFence(VkDevice device, VulkanContext const& context, VkCommandPool pool) {
VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
VkExportFenceCreateInfo exportFenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
};
// Necessary to guard this. Otherwise, swiftshader would throw an error.
if (context.getFenceExportFlags()) {
exportFenceCreateInfo.handleTypes = context.getFenceExportFlags(),
fenceCreateInfo.pNext = &exportFenceCreateInfo;
}
VkFence fence;
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
return fence;
}
} // anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
@@ -91,30 +107,21 @@ uint32_t VulkanCommandBuffer::sAgeCounter = 0;
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
VkQueue queue, VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager,
bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mSemaphoreManager(semaphoreManager),
mBuffer(createCommandBuffer(device, pool)),
mSubmission(semaphoreManager->acquire()),
mAge(++sAgeCounter) {
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkExportFenceCreateInfo exportFenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
.handleTypes = context.getFenceExportFlags()
};
// Necessary to guard this. Otherwise, swiftshader would throw an error.
if (context.getFenceExportFlags()) {
fenceCreateInfo.pNext = &exportFenceCreateInfo;
}
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
}
bool isProtected, uint32_t timerQueryIndex, VkQueryPool queryPool)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mSemaphoreManager(semaphoreManager),
mBuffer(createCommandBuffer(device, pool)),
mSubmission(semaphoreManager->acquire()),
mFence(createFence(device, context, pool)),
mCmdbufState(std::make_shared<VulkanCmdBufferState>(mFence)),
mAge(++sAgeCounter),
mQueryBeginIndex(timerQueryIndex),
mQueryEndIndex(timerQueryIndex + 1),
mQueryPool(queryPool) {}
VulkanCommandBuffer::~VulkanCommandBuffer() {
vkDestroyFence(mDevice, mFence, VKALLOC);
@@ -129,12 +136,12 @@ void VulkanCommandBuffer::reset() noexcept {
mSubmission = mSemaphoreManager->acquire();
// reset the fence with proper host synchronization
mFenceStatus->resetFence(mDevice);
mCmdbufState->resetFence(mDevice);
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
mCmdbufState = std::make_shared<VulkanCmdBufferState>(mFence);
}
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
@@ -191,6 +198,12 @@ void VulkanCommandBuffer::begin() noexcept {
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(mBuffer, &binfo);
// We need to reset the queries before writing to it. (This must happen within a command buffer,
// but not before the queries have occurred).
vkCmdResetQueryPool(mBuffer, mQueryPool, mQueryBeginIndex, 2);
vkCmdWriteTimestamp(mBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, mQueryPool,
mQueryBeginIndex);
}
fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
@@ -198,6 +211,9 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
popMarker();
}
vkCmdWriteTimestamp(mBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, mQueryPool,
mQueryEndIndex);
vkEndCommandBuffer(mBuffer);
VkSemaphore submissionSemaphore = mSubmission->getVkSemaphore();
@@ -236,7 +252,7 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
UTILS_UNUSED_IN_RELEASE VkResult result =
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
mFenceStatus->setStatus(VK_NOT_READY);
mCmdbufState->setStatus(VK_NOT_READY, VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION);
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -248,6 +264,45 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
return mSubmission;
}
void VulkanCommandBuffer::setComplete() {
if (mCmdbufState->getBufferDuration() != VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION) {
return;
}
// We always query the duration of this buffer on the GPU. This is then used to inform timer
// queries from the Filament-side. The reason for doing it this way is because the timestamp is
// consistent within a command buffer, but not guarranteed to be consistent outside of a command
// buffer. So we can only infer duration by looking at execution duration with respect to a
// buffer.
struct QueryResult {
uint64_t beginTime = 0;
uint64_t beginAvailable = 0;
uint64_t endTime = 0;
uint64_t endAvailable = 0;
} result;
constexpr size_t dataSize = sizeof(result);
constexpr VkDeviceSize stride = sizeof(uint64_t) * 2;
VkResult const vkresult =
vkGetQueryPoolResults(mDevice, mQueryPool, mQueryBeginIndex, 2, dataSize,
(void*) &result, stride,
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
<< "vkGetQueryPoolResults error=" << static_cast<int32_t>(vkresult);
uint64_t const begin = result.beginTime;
uint64_t const end = result.endTime;
uint64_t duration = end - begin;
assert_invariant(result.beginAvailable != 0 && result.endAvailable != 0);
if (begin > end) {
FVK_LOGW << "Timestamps are not monotonically increasing. begin=" << begin <<
" end=" << end;
duration = begin - end;
}
mCmdbufState->setStatus(VK_SUCCESS, duration);
}
CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected)
: mDevice(device),
@@ -259,11 +314,24 @@ CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice devi
(isProtected ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0u),
.queueFamilyIndex = queueFamilyIndex,
};
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
VkResult result = vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateCommandPool failed."
<< " error=" << static_cast<int32_t>(result);
// Create a timestamp pool large enough to hold a pair of queries for each command buffer.
VkQueryPoolCreateInfo tqpCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.queryType = VK_QUERY_TYPE_TIMESTAMP,
.queryCount = CAPACITY *2,
};
result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mQueryPool);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool failed."
<< " error=" << static_cast<int32_t>(result);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(
context, device, queue, mPool, semaphoreManager, isProtected));
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool,
semaphoreManager, isProtected, i * 2, mQueryPool));
}
}
@@ -271,6 +339,7 @@ CommandBufferPool::~CommandBufferPool() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
vkDestroyQueryPool(mDevice, mQueryPool, VKALLOC);
}
VulkanCommandBuffer& CommandBufferPool::getRecording() {
@@ -414,7 +483,7 @@ void VulkanCommands::terminate() {
mPool.reset();
mProtectedPool.reset();
mLastSubmit = {};
mLastFenceStatus = {};
mLastBufferState = {};
}
VulkanCommandBuffer& VulkanCommands::get() {
@@ -443,8 +512,7 @@ bool VulkanCommands::flush() {
fvkmemory::resource_ptr<VulkanSemaphore> dependency;
bool hasFlushed = false;
VkFence flushedFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
std::shared_ptr<VulkanCmdBufferState> flushedBufferState;
// Note that we've ordered it so that the non-protected commands are followed by the protected
// commands. This assumes that the protected commands will be that one doing the rendering into
@@ -467,8 +535,7 @@ bool VulkanCommands::flush() {
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
mLastSubmit = {};
}
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
flushedBufferState = pool->getRecording().getState();
dependency = pool->flush();
hasFlushed = true;
}
@@ -476,8 +543,11 @@ bool VulkanCommands::flush() {
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
mLastFence = flushedFence;
mLastFenceStatus = flushedFenceStatus;
if (mLastBufferState) {
mLastBufferState->setNextState(flushedBufferState);
}
mLastBufferState = flushedBufferState;
}
return true;
+27 -25
View File
@@ -42,6 +42,7 @@
namespace filament::backend {
using namespace fvkmemory;
struct CommandBufferPool;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
class VulkanGroupMarkers {
@@ -65,7 +66,8 @@ private:
// we're done waiting on the most recent submission of the given command buffer.
struct VulkanCommandBuffer {
VulkanCommandBuffer(VulkanContext const& mContext, VkDevice device, VkQueue queue,
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected);
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected,
uint32_t timerQueryIndex, VkQueryPool queryPool);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
@@ -90,20 +92,14 @@ struct VulkanCommandBuffer {
void begin() noexcept;
fvkmemory::resource_ptr<VulkanSemaphore> submit();
inline void setComplete() {
mFenceStatus->setStatus(VK_SUCCESS);
}
void setComplete();
VkResult getStatus() {
return mFenceStatus->getStatus();
return mCmdbufState->getFenceStatus();
}
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
return mFenceStatus;
}
VkFence getVkFence() const {
return mFence;
std::shared_ptr<VulkanCmdBufferState> getState() const {
return mCmdbufState;
}
VkCommandBuffer buffer() const {
@@ -115,22 +111,31 @@ struct VulkanCommandBuffer {
}
private:
VkFence getVkFence() const {
return mFence;
}
static uint32_t sAgeCounter;
VulkanContext const& mContext;
uint8_t mMarkerCount;
bool const isProtected;
VkDevice mDevice;
VkQueue mQueue;
VkDevice const mDevice;
VkQueue const mQueue;
VulkanSemaphoreManager* mSemaphoreManager;
fvkutils::StaticVector<VkSemaphore, 2> mWaitSemaphores;
fvkutils::StaticVector<VkPipelineStageFlags, 2> mWaitSemaphoreStages;
VkCommandBuffer mBuffer;
VkCommandBuffer const mBuffer;
fvkmemory::resource_ptr<VulkanSemaphore> mSubmission;
VkFence mFence;
std::shared_ptr<VulkanCmdFence> mFenceStatus;
VkFence const mFence;
std::shared_ptr<VulkanCmdBufferState> mCmdbufState;
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
uint32_t mAge;
uint32_t const mQueryBeginIndex;
uint32_t const mQueryEndIndex;
VkQueryPool const mQueryPool;
friend struct CommandBufferPool;
};
struct CommandBufferPool {
@@ -174,6 +179,8 @@ private:
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
int8_t mRecording;
VkQueryPool mQueryPool;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
#endif
@@ -199,7 +206,7 @@ private:
//
// - Allows off-thread queries of command buffer status.
// - Exposes an "updateFences" method that transfers current fence status into atomics.
// - Users can examine these atomic variables (see VulkanCmdFence) to determine status.
// - Users can examine these atomic variables (see VulkanCmdBufferState) to determine status.
// - We do this because vkGetFenceStatus must be called from the rendering thread.
//
class VulkanCommands {
@@ -230,12 +237,8 @@ public:
return sem;
}
VkFence getMostRecentFence() {
return mLastFence;
}
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
return mLastFenceStatus;
std::shared_ptr<VulkanCmdBufferState> getMostRecentBufferState() {
return mLastBufferState;
}
// Takes a semaphore that signals when the next flush can occur. Only one injected
@@ -276,8 +279,7 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
fvkmemory::resource_ptr<VulkanSemaphore> mLastSubmit;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
std::shared_ptr<VulkanCmdBufferState> mLastBufferState;
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
};
@@ -15,7 +15,6 @@
*/
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
#include "VulkanHandles.h"
@@ -101,32 +100,22 @@ uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
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();
if (size == 0) {
return 0;
} else if (size == 1) {
return (uint64_t) samplers[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);
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager, VulkanSamplerCache* cache)
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager), mSamplerCache(cache) {}
mResourceManager(resourceManager) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
@@ -142,7 +131,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers, mSamplerCache),
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
};
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second;
@@ -164,20 +153,9 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
.bindingCount = count,
.pBindings = toBind,
};
LayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
utils::FixedCapacityVector<uint32_t> immutableSamplersHashes;
immutableSamplersHashes.reserve(MAX_SAMPLER_COUNT);
for (auto sampler: immutableSamplers) {
immutableSamplersHashes.push_back(mSamplerCache->getKey(sampler));
}
VulkanDescriptorSetLayoutSerializer setSer(dlinfo, immutableSamplersHashes, hashedKey);
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
mLayoutToKey[vklayout] = hashedKey;
return vklayout;
}
@@ -19,8 +19,6 @@
#include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
@@ -37,8 +35,7 @@ namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager,
VulkanSamplerCache* cache);
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
@@ -51,19 +48,10 @@ public:
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
uint32_t getKey(VkDescriptorSetLayout layout) {
uint32_t key = 0;
auto iter = mLayoutToKey.find(layout);
if (iter != mLayoutToKey.end()) {
key = iter->second;
}
return key;
}
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
VulkanSamplerCache* mSamplerCache;
struct LayoutKey {
// this describes the layout using bitset.
@@ -81,7 +69,6 @@ private:
};
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
std::map<VkDescriptorSetLayout, uint32_t> mLayoutToKey;
};
} // 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
+29 -44
View File
@@ -230,18 +230,17 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
mPipelineLayoutCache(mPlatform->getDevice()),
mPipelineCache(mPlatform->getDevice()),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice(), &mYcbcrConversionCache),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager, &mSamplerCache),
mPipelineLayoutCache(mPlatform->getDevice(), &mDescriptorSetLayoutCache),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
@@ -338,8 +337,6 @@ void VulkanDriver::terminate() {
mDefaultRenderTarget = {};
mPipelineState = {};
mQueryManager.terminate();
mBlitter.terminate();
mReadPixels.terminate();
@@ -860,32 +857,33 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& ta
cmdbuf = &mCommands.get();
}
// Note at this point, the fence has already been constructed via createFenceS, so we just tag
// it with appropriate VulkanCmdFence, which is associated with the current, recording command
// it with appropriate VulkanCmdBufferState, which is associated with the current, recording command
// buffer.
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
fence->setFence(cmdbuf->getFenceStatus());
fence->setCmdBufferState(cmdbuf->getState());
mResourceManager.associateHandle(fh.getId(), std::move(tag));
}
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::ImmutableCString&& tag) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
VkFence fence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fenceStatus;
std::shared_ptr<VulkanCmdBufferState> cmdbufState;
if (mCurrentRenderPass.commandBuffer) {
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
fence = cmdBuff->getVkFence();
fenceStatus = cmdBuff->getFenceStatus();
cmdbufState = cmdBuff->getState();
fence = cmdbufState->getVkFence();
// If we're currently recording, flush so that the fence only applies
// to commands already issued.
mCommands.flush();
} else {
fence = mCommands.getMostRecentFence();
fenceStatus = mCommands.getMostRecentFenceStatus();
cmdbufState = mCommands.getMostRecentBufferState();
fence = cmdbufState->getVkFence();
}
{
std::lock_guard<std::mutex> guard(sync->lock);
sync->sync = mPlatform->createSync(fence, fenceStatus);
sync->sync = mPlatform->createSync(fence, cmdbufState);
}
for (auto& cbData : sync->conversionCallbacks) {
@@ -1075,9 +1073,10 @@ Handle<HwSwapChain> VulkanDriver::createSwapChainHeadlessS() noexcept {
Handle<HwTimerQuery> VulkanDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
auto query = mQueryManager.getNextQuery(&mResourceManager);
auto handle = mResourceManager.allocHandle<VulkanTimerQuery>();
auto query = resource_ptr<VulkanTimerQuery>::make(&mResourceManager, handle);
query.inc();
return Handle<VulkanTimerQuery>(query.id());
return handle;
}
Handle<HwDescriptorSetLayout> VulkanDriver::createDescriptorSetLayoutS() noexcept {
@@ -1134,7 +1133,6 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
return;
}
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
mQueryManager.clearQuery(vtq);
vtq.dec();
}
@@ -1197,7 +1195,6 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> const fh) {
FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout) {
// Even though this is a synchronous call, the fence handle must be (and stay) valid
assert_invariant(fh);
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
// we have to take into account that the STL's wait_for() actually works with
@@ -1214,14 +1211,14 @@ FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout
until = now + nanoseconds(timeout);
}
auto const [cmdfence, canceled] = fence->wait(until);
if (!cmdfence || canceled) {
auto const [cmdbufState, canceled] = fence->wait(until);
if (!cmdbufState || canceled) {
return canceled ? FenceStatus::ERROR : FenceStatus::TIMEOUT_EXPIRED;
}
// now we are holding a reference to our VulkanCmdFence, so we know it can't
// now we are holding a reference to our VulkanCmdBufferState, so we know it can't
// disappear while we wait
return cmdfence->wait(mPlatform->getDevice(), timeout, until);
return cmdbufState->waitOnFence(mPlatform->getDevice(), timeout, until);
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
@@ -1518,29 +1515,17 @@ void VulkanDriver::setupExternalImage(void* image) {
TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
if (!vtq->isCompleted()) {
if (!vtq->isComplete()) {
return TimerQueryResult::NOT_READY;
}
auto results = mQueryManager.getResult(vtq);
if (results.beginAvailable == 0 || results.endAvailable == 0) {
return TimerQueryResult::NOT_READY;
}
uint64_t const begin = results.beginTime;
uint64_t const end = results.endTime;
if (begin >= end) {
// TODO: queries might have ran on different command buffers.
FVK_LOGW << "Timestamps are not monotonically increasing. ";
*elapsedTime = 0;
return TimerQueryResult::ERROR;
}
uint64_t result = vtq->getResult();
assert_invariant(result != VulkanTimerQuery::UNKNOWN_QUERY_RESULT);
// NOTE: MoltenVK currently writes system time so the following delta will always be zero.
// However there are plans for implementing this properly. See the following GitHub ticket.
// https://github.com/KhronosGroup/MoltenVK/issues/773
float const period = mContext.getPhysicalDeviceLimits().timestampPeriod;
*elapsedTime = uint64_t(float(end - begin) * period);
*elapsedTime = uint64_t(float(result) * period);
return TimerQueryResult::AVAILABLE;
}
@@ -1666,7 +1651,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.subpassMask = uint8_t(params.subpassMask);
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, mFramebufferCache.getRenderPassKey(renderPass), 0);
mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
@@ -1785,7 +1770,6 @@ void VulkanDriver::nextSubpass(int) {
VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
mFramebufferCache.getRenderPassKey(mCurrentRenderPass.renderPass),
++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) {
@@ -2161,8 +2145,7 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask;
auto key = mPipelineLayoutCache.getKey(pipelineLayout);
mPipelineCache.bindLayout(pipelineLayout, key);
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
}
@@ -2301,12 +2284,14 @@ void VulkanDriver::scissor(Viewport scissorBox) {
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
mQueryManager.beginQuery(&(mCommands.get()), vtq);
auto cmdbuf = &mCommands.get();
vtq->setBeginState(cmdbuf->getState());
}
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
mQueryManager.endQuery(&(mCommands.get()), vtq);
auto cmdbuf = &mCommands.get();
vtq->setEndState(cmdbuf->getState());
}
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
+1 -3
View File
@@ -25,7 +25,6 @@
#include "VulkanHandles.h"
#include "VulkanMemory.h"
#include "VulkanPipelineCache.h"
#include "VulkanQueryManager.h"
#include "VulkanReadPixels.h"
#include "VulkanSamplerCache.h"
#include "VulkanSemaphoreManager.h"
@@ -148,6 +147,7 @@ private:
VulkanSemaphoreManager mSemaphoreManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanBufferCache mBufferCache;
@@ -157,9 +157,7 @@ private:
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager;
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
@@ -15,7 +15,6 @@
*/
#include "VulkanFboCache.h"
#include "VulkanRenderPassStateSerializer.h"
#include "VulkanConstants.h"
#include "vulkan/utils/Image.h"
@@ -70,15 +69,6 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
uint32_t VulkanFboCache::getRenderPassKey(VkRenderPass pass) noexcept {
uint32_t key = 0;
auto iter = mRenderPassToKey.find(pass);
if (iter != mRenderPassToKey.end()) {
key = iter->second;
}
return key;
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
@@ -127,7 +117,6 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
mFramebufferCache[config] = {framebuffer, mCurrentTime};
return framebuffer;
}
@@ -337,12 +326,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error;
RenderPassHash hashFunc;
uint32_t key = hashFunc(config);
VulkanRenderPassStateSerializer passSer(renderPassInfo, key);
mRenderPassToKey[renderPass] = key;
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
@@ -24,7 +24,6 @@
#include <backend/TargetBufferInfo.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -98,8 +97,6 @@ public:
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
uint32_t getRenderPassKey(VkRenderPass pass) noexcept;
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
@@ -121,7 +118,6 @@ private:
FboMap mFramebufferCache;
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
std::map<VkRenderPass, uint32_t> mRenderPassToKey;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
};
@@ -33,7 +33,6 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h>
#include <fstream>
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
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
@@ -298,12 +280,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
.codeSize = dataSize,
.pCode = data,
};
if (i == 0) {
mVertexShaderHash = serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_VERTEX_BIT);
} else if (i == 1) {
mFragmentShaderHash =
serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_FRAGMENT_BIT);
}
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create shader module."
@@ -65,8 +65,6 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorSetLayoutHashArray = std::array<uint64_t,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
// The bitmask representation of a set layout.
struct Bitmask {
@@ -282,10 +280,6 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
inline uint64_t getVertexShaderHash() { return mVertexShaderHash;
}
inline uint64_t getFragmentShaderHash() { return mFragmentShaderHash; }
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
@@ -301,8 +295,6 @@ private:
};
PipelineInfo* mInfo;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
VkDevice mDevice = VK_NULL_HANDLE;
};
@@ -15,7 +15,6 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
@@ -43,9 +42,8 @@ VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
}
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout, uint32_t key) noexcept {
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
mPipelineRequirements.layout = layout;
mPipelineLayoutKey = key;
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept {
@@ -79,7 +77,6 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
VulkanPipelineStateSerializer serializer(mProgramName);
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
shaderStages[0] = {
@@ -92,12 +89,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = mPipelineRequirements.shaders[1];
VulkanPipelineStateSerializer::ShaderStageInfo vtx = { shaderStages[0].stage, mVertexShaderHash,
shaderStages[0].pName };
VulkanPipelineStateSerializer::ShaderStageInfo frg = { shaderStages[1].stage, mFragmentShaderHash,
shaderStages[1].pName };
serializer << vtx << frg;
bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
@@ -116,11 +107,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
if (mPipelineRequirements.vertexAttributes[i].format > 0) {
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
serializer << mPipelineRequirements.vertexAttributes[i];
}
if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
serializer << mPipelineRequirements.vertexBuffers[i];
}
}
VkPipelineVertexInputStateCreateInfo vertexInputState = {
@@ -134,15 +123,11 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
};
serializer << inputAssemblyState;
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
serializer << viewportState;
VkDynamicState dynamicStateEnables[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
@@ -152,8 +137,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.dynamicStateCount = 2,
.pDynamicStates = dynamicStateEnables,
};
serializer << dynamicState;
auto const& raster = mPipelineRequirements.rasterState;
VkPipelineRasterizationStateCreateInfo vkRaster = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@@ -167,8 +150,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.depthBiasSlopeFactor = raster.depthBiasSlopeFactor,
.lineWidth = 1.0f,
};
serializer << vkRaster;
VkPipelineMultisampleStateCreateInfo vkMs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples,
@@ -177,8 +158,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
.alphaToOneEnable = VK_FALSE,
};
serializer << vkMs;
VkPipelineDepthStencilStateCreateInfo vkDs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
@@ -198,12 +177,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.writeMask = 0u,
.reference = 0u,
};
serializer << vkDs;
//layout
serializer.setPipelineLayoutKey(mPipelineLayoutKey);
// renderPass
serializer.setRenderPassKey(mRenderPassKey);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
@@ -242,7 +215,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
colorBlendAttachments[i] = colorBlendAttachments[0];
}
}
serializer << colorBlendState;
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
@@ -258,20 +230,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
return nullptr;
}
PipelineHashFn hashFunc;
serializer.setID(hashFunc(mPipelineRequirements));
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
mProgramName = program->name;
mVertexShaderHash = program->getVertexShaderHash();
mFragmentShaderHash= program->getFragmentShaderHash();
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
@@ -286,10 +250,9 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
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.subpassIndex = subpassIndex;
mRenderPassKey = passKey;
}
void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept {
@@ -88,7 +88,7 @@ public:
VulkanPipelineCache(VkDevice device);
void bindLayout(VkPipelineLayout layout, uint32_t key) noexcept;
void bindLayout(VkPipelineLayout layout) noexcept;
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
void bindPipeline(VulkanCommandBuffer* commands);
@@ -96,7 +96,7 @@ public:
// Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
@@ -207,11 +207,6 @@ private:
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineKey mPipelineRequirements = {};
utils::CString mProgramName;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
uint32_t mPipelineLayoutKey;
uint32_t mRenderPassKey;
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
@@ -15,7 +15,6 @@
*/
#include "VulkanPipelineLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
namespace filament::backend {
@@ -24,11 +23,7 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
fvkmemory::resource_ptr<VulkanProgram> program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
DescriptorSetLayoutHashArray hashArray;
for (uint32_t i = 0; i < descriptorSetLayouts.size(); ++i) {
hashArray[i] = mDescriptorSetCache->getKey(descriptorSetLayouts[i]);
}
key.descSetLayouts = hashArray;
key.descSetLayouts = descriptorSetLayouts;
for (auto layoutHandle: descriptorSetLayouts) {
if (layoutHandle == VK_NULL_HANDLE) {
break;
@@ -67,14 +62,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = descriptorSetLayouts.data(),
.pSetLayouts = key.descSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges,
};
PipelineLayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
VulkanPipelineLayoutSerializer pipelineSer(info, mDescriptorSetCache, hashedKey);
VkPipelineLayout layout;
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
@@ -82,8 +74,6 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.handle = layout,
.lastUsed = mTimestamp++,
};
mPipelineToKey[layout] = hashedKey;
return layout;
}
@@ -18,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include <bluevk/BlueVK.h>
@@ -31,11 +30,10 @@ namespace filament::backend {
class VulkanPipelineLayoutCache {
public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetLayoutHashArray = VulkanDescriptorSetLayout::DescriptorSetLayoutHashArray;
VulkanPipelineLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache* cache)
VulkanPipelineLayoutCache(VkDevice device)
: mDevice(device),
mTimestamp(0), mDescriptorSetCache(cache) {}
mTimestamp(0) {}
void terminate() noexcept;
@@ -47,7 +45,7 @@ public:
};
struct PipelineLayoutKey {
DescriptorSetLayoutHashArray descSetLayouts = {}; // 8 * 4
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
uint16_t padding = 0;
};
@@ -60,14 +58,6 @@ public:
// are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program);
uint32_t getKey(VkPipelineLayout layout) {
uint32_t key = 0;
auto iter = mPipelineToKey.find(layout);
if (iter != mPipelineToKey.end()) {
key = iter->second;
}
return key;
}
private:
using Timestamp = uint64_t;
@@ -89,8 +79,6 @@ private:
VkDevice mDevice;
Timestamp mTimestamp;
PipelineLayoutMap mPipelineLayouts;
std::map<VkPipelineLayout, uint32_t> mPipelineToKey;
VulkanDescriptorSetLayoutCache* mDescriptorSetCache;
};
} // 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,106 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanQueryManager.h"
#include "VulkanAsyncHandles.h"
#include "VulkanCommands.h"
#include "VulkanConstants.h"
#include <bluevk/BlueVK.h>
namespace filament::backend {
using namespace bluevk;
VulkanQueryManager::VulkanQueryManager(VkDevice device) : mDevice(device) {
// Create a timestamp pool large enough to hold a pair of queries for each timer.
VkQueryPoolCreateInfo tqpCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.queryType = VK_QUERY_TYPE_TIMESTAMP,
.queryCount = (uint32_t) mUsed.size() * 2,
};
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool failed."
<< " error=" << static_cast<int32_t>(result);
}
fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
fvkmemory::ResourceManager* resourceManager) {
auto unused = ~mUsed;
if (unused.empty()) {
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported.";
return {};
}
std::pair<uint32_t, uint32_t> queryIndices;
size_t const firstUnused = unused.firstSetBit();
{
std::unique_lock<utils::Mutex> lock(mMutex);
mUsed.set(firstUnused);
queryIndices = { firstUnused * 2, firstUnused * 2 + 1 };
}
return fvkmemory::resource_ptr<VulkanTimerQuery>::construct(resourceManager, queryIndices.first,
queryIndices.second);
}
void VulkanQueryManager::clearQuery(fvkmemory::resource_ptr<VulkanTimerQuery> query) {
std::unique_lock<utils::Mutex> lock(mMutex);
uint32_t const startingIndex = query->getStartingQueryIndex();
mUsed.unset(startingIndex / 2);
}
void VulkanQueryManager::beginQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStartingQueryIndex();
auto const cmdbuffer = commands->buffer();
vkCmdResetQueryPool(cmdbuffer, mPool, index, 2);
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
// We stash this because getResult might come before the query is actually processed.
query->setFence(commands->getFenceStatus());
}
void VulkanQueryManager::endQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStoppingQueryIndex();
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
}
VulkanQueryManager::QueryResult VulkanQueryManager::getResult(
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStartingQueryIndex();
QueryResult result;
size_t const dataSize = sizeof(result);
VkDeviceSize const stride = sizeof(uint64_t) * 2;
VkResult vkresult =
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) &result, stride,
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
<< "vkGetQueryPoolResults error=" << static_cast<int32_t>(vkresult);
if (vkresult == VK_NOT_READY) {
return {};
}
return result;
}
void VulkanQueryManager::terminate() noexcept {
vkDestroyQueryPool(mDevice, mPool, VKALLOC);
mPool = VK_NULL_HANDLE;
}
} // filament::backend
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_H
#define TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_H
#include "vulkan/memory/ResourcePointer.h"
namespace filament::backend {
struct VulkanCommandBuffer;
struct VulkanTimerQuery;
class VulkanQueryManager {
public:
struct QueryResult {
uint64_t beginTime = 0;
uint64_t beginAvailable = 0;
uint64_t endTime = 0;
uint64_t endAvailable = 0;
};
VulkanQueryManager(VkDevice device);
~VulkanQueryManager() = default;
// Not copy-able.
VulkanQueryManager(VulkanQueryManager const&) = delete;
VulkanQueryManager& operator=(VulkanQueryManager const&) = delete;
fvkmemory::resource_ptr<VulkanTimerQuery>
getNextQuery(fvkmemory::ResourceManager* mResourceManager);
void clearQuery(fvkmemory::resource_ptr<VulkanTimerQuery> query);
void beginQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
void endQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
QueryResult getResult(fvkmemory::resource_ptr<VulkanTimerQuery> query);
void terminate() noexcept;
private:
VkDevice mDevice;
VkQueryPool mPool;
utils::bitset32 mUsed;
utils::Mutex mMutex;
};
} // filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_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 "VulkanSamplerStateSerializer.h"
using namespace bluevk;
namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache)
: mDevice(device), mConversionCache(conversionCache) {}
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) {
auto iter = mCache.find(params);
@@ -59,16 +57,11 @@ VkSampler VulkanSamplerCache::getSampler(Params params) {
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VulkanSamplerStateSerializer samplerSer(params,
params.conversion != VK_NULL_HANDLE ? mConversionCache->getKey(params.conversion) : 0);
VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, sampler });
SamplerHashFn hashFn;
mSamplerToKey[sampler] = hashFn(params);
return sampler;
}
@@ -17,11 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
#include <map>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
@@ -39,20 +37,11 @@ public:
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache);
explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params);
uint32_t getKey(VkSampler sampler) {
uint32_t key = 0;
auto iter = mSamplerToKey.find(sampler);
if (iter != mSamplerToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
VkDevice mDevice;
VulkanYcbcrConversionCache* mConversionCache;
struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
@@ -62,7 +51,6 @@ private:
};
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
std::map<VkSampler, uint32_t> mSamplerToKey;
};
} // 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 "VulkanSamplerStateSerializer.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
@@ -54,7 +53,6 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VulkanYcbcrConversionSerializer ycbcr_ser(params);
// We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__)
@@ -75,8 +73,6 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion });
ConversionHashFn hashFn;
mConversionToKey[conversion] = hashFn(params);
return conversion;
}
@@ -25,7 +25,6 @@
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -41,14 +40,6 @@ public:
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params);
uint32_t getKey(VkSamplerYcbcrConversion conv) {
uint32_t key = 0;
auto iter = mConversionToKey.find(conv);
if (iter != mConversionToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
@@ -63,7 +54,6 @@ private:
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
std::map<VkSamplerYcbcrConversion, uint32_t> mConversionToKey;
};
} // namespace filament::backend
@@ -971,7 +971,7 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
}
Platform::Sync* VulkanPlatform::createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept {
std::shared_ptr<VulkanCmdBufferState> fenceStatus) noexcept {
return new VulkanSync{.fence = fence, .fenceStatus = fenceStatus};
}
@@ -407,7 +407,7 @@ bool VulkanPlatformAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) const
VulkanSync& vulkanSync = static_cast<VulkanSync&>(*sync);
assert_invariant(vulkanSync.fenceStatus);
if (vulkanSync.fenceStatus->getStatus() == VK_SUCCESS) {
if (vulkanSync.fenceStatus->getFenceStatus() == VK_SUCCESS) {
// We've already signaled; return -1 so that operations will proceed
// immediately. Also, signal that fence conversion was successful.
*fd = -1;