Compare commits

..
Author SHA1 Message Date
Serge Metral 531bfd6f6e Fix Android build. 2025-11-19 17:41:17 +00:00
Serge Metral db272484f9 Merge branch 'main' into serge/vulkan_pipeline_cache_json 2025-11-19 09:05:45 -08:00
Serge Metral 9dadf400d2 Adding render pass key. 2025-11-19 08:47:03 -08:00
Serge Metral 16c7a3c968 Adding render pass serialization. 2025-11-18 16:00:15 -08:00
Serge Metral 626f338cba First commit. 2025-11-18 07:06:46 -08:00
Serge Metral 4e05cd73c6 Merge branch 'google:main' into main 2025-11-04 11:20:17 -08:00
Serge Metral 49f9708312 Merge branch 'google:main' into main 2025-10-29 17:00:00 +00:00
Serge Metral 2791195a92 Merge branch 'google:main' into main 2025-10-22 10:50:16 -07:00
Serge Metral 65f78c06bb Merge branch 'google:main' into main 2025-10-21 11:32:23 -07:00
Serge Metral 71175ee410 Merge branch 'google:main' into main 2025-10-13 10:06:53 -07:00
Serge Metral c9b310450c Merge branch 'google:main' into main 2025-09-19 14:47:15 +00:00
Serge Metral 54c636d54c Merge branch 'google:main' into main 2025-09-04 03:15:27 +00:00
Serge Metral f503205be2 Merge branch 'google:main' into main 2025-08-29 17:25:01 +00:00
Serge Metral 02106110fe Merge branch 'google:main' into main 2025-08-25 16:31:27 +00:00
Serge Metral 5a0f78d915 Merge branch 'google:main' into main 2025-08-18 11:23:24 -07:00
Serge Metral f548c588d9 Merge branch 'google:main' into main 2025-08-13 18:17:53 +00:00
Serge Metral 487fb273f1 Merge branch 'google:main' into main 2025-08-12 15:25:09 +00:00
Serge Metral 111ac62d2f Merge branch 'google:main' into main 2025-08-07 11:24:34 -07:00
Serge Metral 2fac13e154 Merge branch 'google:main' into main 2025-07-31 20:45:26 -07:00
Serge Metral bcae04c9ef Merge branch 'google:main' into main 2025-05-12 14:15:56 -07:00
Serge Metral 55fc91b35f Merge branch 'google:main' into main 2025-05-08 18:25:41 +00:00
Serge Metral ff2827b226 Merge branch 'google:main' into main 2025-03-28 14:40:50 +00:00
Serge Metral 13e1d2a042 Merge branch 'google:main' into main 2025-03-21 23:32:55 +00:00
Serge Metral b09cbaf0db Merge branch 'google:main' into main 2025-03-18 19:47:10 +00:00
Serge Metral b570d489e3 Merge branch 'google:main' into main 2025-03-17 16:09:21 +00:00
Serge Metral 7f7daddbfa Merge branch 'google:main' into main 2025-03-14 17:28:06 +00:00
Serge Metral cfe6a423d3 Merge branch 'google:main' into main 2025-03-06 10:48:37 -08:00
Serge Metral ba99844f22 Merge branch 'google:main' into main 2025-03-05 16:00:28 -08:00
Serge Metral ea4c20ec2c Merge branch 'google:main' into main 2025-01-29 20:49:02 -08:00
Serge Metral 0a661c1f3c Merge branch 'google:main' into main 2025-01-27 08:55:26 -08:00
Serge Metral 021c4f6691 Merge branch 'google:main' into main 2025-01-13 12:07:40 -08:00
Serge Metral be6b6ae7be Merge branch 'google:main' into main 2024-12-18 20:56:10 -08:00
Serge Metral ad3a5b70b6 Merge branch 'google:main' into main 2024-12-03 10:05:08 -08:00
Serge Metral 80803f9693 Merge branch 'google:main' into main 2024-11-06 02:54:52 +00:00
Serge Metral 3b0e44c9a3 Merge branch 'google:main' into main 2024-11-05 22:20:25 +00:00
Serge Metral c5c2d42467 Merge branch 'google:main' into main 2024-11-05 05:25:51 +00:00
Serge Metral ff145f6ccf Merge branch 'google:main' into main 2024-10-31 15:55:28 +00:00
Serge Metral 48bb10a4b5 Merge branch 'google:main' into main 2024-10-30 04:14:21 +00:00
Serge Metral 01a2480175 Merge branch 'google:main' into main 2024-10-26 05:37:19 +00:00
Serge Metral 9e921936c6 Merge branch 'google:main' into main 2024-10-18 13:29:27 +00:00
Serge Metral 8bc4d3a9df Merge branch 'google:main' into main 2024-10-09 17:16:55 +00:00
Serge Metral 0fdf1cac1d Merge branch 'google:main' into main 2024-10-03 09:10:04 -07:00
Serge Metral 387942cc39 Merge branch 'google:main' into main 2024-09-20 20:48:06 -07:00
Serge Metral c81cedd887 Merge branch 'google:main' into main 2024-09-04 08:36:42 -07:00
Serge Metral abe8a49d17 Merge branch 'google:main' into main 2024-06-26 17:26:00 -07:00
Serge Metral 96cba96058 Merge branch 'google:main' into main 2024-06-18 09:38:05 -07:00
Serge Metral 4fccd957db Merge branch 'google:main' into main 2024-06-11 16:54:22 -07:00
Powei Feng d49554d5ce Merge branch 'main' into main 2024-06-05 14:36:17 -07:00
Powei Feng 867161773e Merge branch 'main' into main 2024-06-05 11:53:35 -07:00
Serge Metral e2658506f6 Merge branch 'main' into main 2024-06-04 15:39:46 -07:00
Serge Metral 6218164f79 Adding the begin frame message for later xtrace post processing. 2024-06-04 12:35:17 -07:00
34 changed files with 1335 additions and 315 deletions
+10
View File
@@ -214,8 +214,18 @@ 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 VulkanCmdBufferState;
struct VulkanCmdFence;
/**
* 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<VulkanCmdBufferState> fenceStatus) noexcept;
std::shared_ptr<VulkanCmdFence> 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<VulkanCmdBufferState> fenceStatus;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
@@ -29,8 +29,9 @@ using namespace bluevk;
namespace filament::backend {
FenceStatus VulkanCmdBufferState::waitOnFence(VkDevice device, uint64_t const timeout,
std::chrono::steady_clock::time_point const until) {
FenceStatus VulkanCmdFence::wait(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);
@@ -80,7 +81,7 @@ FenceStatus VulkanCmdBufferState::waitOnFence(VkDevice device, uint64_t const ti
return FenceStatus::ERROR; // not supported
}
void VulkanCmdBufferState::resetFence(VkDevice device) {
void VulkanCmdFence::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.
@@ -89,35 +90,4 @@ void VulkanCmdBufferState::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,7 +27,6 @@
#include <chrono>
#include <cstdint>
#include <limits>
#include <memory>
#include <mutex>
#include <shared_mutex>
@@ -36,36 +35,25 @@
namespace filament::backend {
// 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();
// 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;
explicit VulkanCmdBufferState(VkFence fence)
: mFence(fence) {
}
~VulkanCmdBufferState() = default;
void setStatus(VkResult const value, uint64_t duration) {
void setStatus(VkResult const value) {
std::lock_guard const l(mLock);
mStatus = value;
mDuration = duration;
mCond.notify_all();
}
uint64_t getBufferDuration() {
std::shared_lock const l(mLock);
return mDuration;
}
VkResult getFenceStatus() {
VkResult getStatus() {
std::shared_lock const l(mLock);
return mStatus;
}
void resetFence(VkDevice device);
FenceStatus waitOnFence(VkDevice device, uint64_t timeout,
FenceStatus wait(VkDevice device, uint64_t timeout,
std::chrono::steady_clock::time_point until);
void cancel() {
@@ -74,18 +62,6 @@ struct VulkanCmdBufferState {
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;
@@ -94,42 +70,38 @@ 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 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;
VkFence mFence;
};
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
VulkanFence() {}
void setCmdBufferState(std::shared_ptr<VulkanCmdBufferState> state) {
std::lock_guard l(lock);
cmdbufState = std::move(state);
void setFence(std::shared_ptr<VulkanCmdFence> fence) {
std::lock_guard const l(lock);
sharedFence = std::move(fence);
cond.notify_all();
}
std::shared_ptr<VulkanCmdBufferState>& getCmdBufferState() {
std::lock_guard l(lock);
return cmdbufState;
std::shared_ptr<VulkanCmdFence>& getSharedFence() {
std::lock_guard const l(lock);
return sharedFence;
}
std::pair<std::shared_ptr<VulkanCmdBufferState>, bool> wait(
std::chrono::steady_clock::time_point const until) {
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::unique_lock l(lock);
cond.wait_until(l, until, [&] { return bool(cmdbufState) || canceled; });
// here cmdbufState will be null if we timed out
return { cmdbufState, canceled };
cond.wait_until(l, until, [this] {
return bool(sharedFence) || canceled;
});
// here mSharedFence will be null if we timed out
return { sharedFence, canceled };
}
void cancel() const {
std::lock_guard const l(lock);
if (cmdbufState) {
cmdbufState->cancel();
if (sharedFence) {
sharedFence->cancel();
}
canceled = true;
cond.notify_all();
@@ -139,10 +111,10 @@ private:
mutable std::mutex lock;
mutable std::condition_variable cond;
mutable bool canceled = false;
std::shared_ptr<VulkanCmdBufferState> cmdbufState;
std::shared_ptr<VulkanCmdFence> sharedFence;
};
struct VulkanSync : public HwSync, fvkmemory::ThreadSafeResource {
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
@@ -156,50 +128,38 @@ struct VulkanSync : public HwSync, fvkmemory::ThreadSafeResource {
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
: mStartingQueryIndex(startingIndex),
mStoppingQueryIndex(stoppingIndex) {}
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;
}
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::lock_guard const lock(mFenceMutex);
mFence = std::move(fence);
}
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;
}
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;
}
bool isComplete() {
std::shared_lock const lock(mMutex);
return mBeginState && mEndState &&
mBeginState->getBufferDuration() != UNKNOWN_QUERY_RESULT &&
mEndState->getBufferDuration() != UNKNOWN_QUERY_RESULT;
}
uint32_t getStartingQueryIndex() const { return mStartingQueryIndex; }
uint64_t getResult();
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
}
private:
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;
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
};
} // namespace filament::backend
+37 -107
View File
@@ -55,22 +55,6 @@ 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)
@@ -107,21 +91,30 @@ uint32_t VulkanCommandBuffer::sAgeCounter = 0;
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
VkQueue queue, VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager,
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) {}
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);
}
VulkanCommandBuffer::~VulkanCommandBuffer() {
vkDestroyFence(mDevice, mFence, VKALLOC);
@@ -136,12 +129,12 @@ void VulkanCommandBuffer::reset() noexcept {
mSubmission = mSemaphoreManager->acquire();
// reset the fence with proper host synchronization
mCmdbufState->resetFence(mDevice);
mFenceStatus->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.
mCmdbufState = std::make_shared<VulkanCmdBufferState>(mFence);
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
}
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
@@ -198,12 +191,6 @@ 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() {
@@ -211,9 +198,6 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
popMarker();
}
vkCmdWriteTimestamp(mBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, mQueryPool,
mQueryEndIndex);
vkEndCommandBuffer(mBuffer);
VkSemaphore submissionSemaphore = mSubmission->getVkSemaphore();
@@ -252,7 +236,7 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
UTILS_UNUSED_IN_RELEASE VkResult result =
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
mCmdbufState->setStatus(VK_NOT_READY, VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION);
mFenceStatus->setStatus(VK_NOT_READY);
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -264,45 +248,6 @@ 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),
@@ -314,24 +259,11 @@ CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice devi
(isProtected ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0u),
.queueFamilyIndex = queueFamilyIndex,
};
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);
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool,
semaphoreManager, isProtected, i * 2, mQueryPool));
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(
context, device, queue, mPool, semaphoreManager, isProtected));
}
}
@@ -339,7 +271,6 @@ CommandBufferPool::~CommandBufferPool() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
vkDestroyQueryPool(mDevice, mQueryPool, VKALLOC);
}
VulkanCommandBuffer& CommandBufferPool::getRecording() {
@@ -483,7 +414,7 @@ void VulkanCommands::terminate() {
mPool.reset();
mProtectedPool.reset();
mLastSubmit = {};
mLastBufferState = {};
mLastFenceStatus = {};
}
VulkanCommandBuffer& VulkanCommands::get() {
@@ -512,7 +443,8 @@ bool VulkanCommands::flush() {
fvkmemory::resource_ptr<VulkanSemaphore> dependency;
bool hasFlushed = false;
std::shared_ptr<VulkanCmdBufferState> flushedBufferState;
VkFence flushedFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
// 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
@@ -535,7 +467,8 @@ bool VulkanCommands::flush() {
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
mLastSubmit = {};
}
flushedBufferState = pool->getRecording().getState();
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
dependency = pool->flush();
hasFlushed = true;
}
@@ -543,11 +476,8 @@ bool VulkanCommands::flush() {
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
if (mLastBufferState) {
mLastBufferState->setNextState(flushedBufferState);
}
mLastBufferState = flushedBufferState;
mLastFence = flushedFence;
mLastFenceStatus = flushedFenceStatus;
}
return true;
+27 -29
View File
@@ -42,7 +42,6 @@
namespace filament::backend {
using namespace fvkmemory;
struct CommandBufferPool;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
class VulkanGroupMarkers {
@@ -66,8 +65,7 @@ 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,
uint32_t timerQueryIndex, VkQueryPool queryPool);
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
@@ -92,14 +90,20 @@ struct VulkanCommandBuffer {
void begin() noexcept;
fvkmemory::resource_ptr<VulkanSemaphore> submit();
void setComplete();
VkResult getStatus() {
return mCmdbufState->getFenceStatus();
inline void setComplete() {
mFenceStatus->setStatus(VK_SUCCESS);
}
std::shared_ptr<VulkanCmdBufferState> getState() const {
return mCmdbufState;
VkResult getStatus() {
return mFenceStatus->getStatus();
}
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
return mFenceStatus;
}
VkFence getVkFence() const {
return mFence;
}
VkCommandBuffer buffer() const {
@@ -111,31 +115,22 @@ struct VulkanCommandBuffer {
}
private:
VkFence getVkFence() const {
return mFence;
}
static uint32_t sAgeCounter;
VulkanContext const& mContext;
uint8_t mMarkerCount;
bool const isProtected;
VkDevice const mDevice;
VkQueue const mQueue;
VkDevice mDevice;
VkQueue mQueue;
VulkanSemaphoreManager* mSemaphoreManager;
fvkutils::StaticVector<VkSemaphore, 2> mWaitSemaphores;
fvkutils::StaticVector<VkPipelineStageFlags, 2> mWaitSemaphoreStages;
VkCommandBuffer const mBuffer;
VkCommandBuffer mBuffer;
fvkmemory::resource_ptr<VulkanSemaphore> mSubmission;
VkFence const mFence;
std::shared_ptr<VulkanCmdBufferState> mCmdbufState;
VkFence mFence;
std::shared_ptr<VulkanCmdFence> mFenceStatus;
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 {
@@ -179,8 +174,6 @@ 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
@@ -206,7 +199,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 VulkanCmdBufferState) to determine status.
// - Users can examine these atomic variables (see VulkanCmdFence) to determine status.
// - We do this because vkGetFenceStatus must be called from the rendering thread.
//
class VulkanCommands {
@@ -237,8 +230,12 @@ public:
return sem;
}
std::shared_ptr<VulkanCmdBufferState> getMostRecentBufferState() {
return mLastBufferState;
VkFence getMostRecentFence() {
return mLastFence;
}
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
return mLastFenceStatus;
}
// Takes a semaphore that signals when the next flush can occur. Only one injected
@@ -279,7 +276,8 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
fvkmemory::resource_ptr<VulkanSemaphore> mLastSubmit;
std::shared_ptr<VulkanCmdBufferState> mLastBufferState;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
};
@@ -15,6 +15,7 @@
*/
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
#include "VulkanHandles.h"
@@ -100,22 +101,32 @@ uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
return count;
}
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers, VulkanSamplerCache* cache) {
size_t const size = samplers.size();
if (size == 0) {
return 0;
} else if (size == 1) {
return (uint64_t) samplers[0];
}
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
utils::FixedCapacityVector<uint32_t> samplerHash;
if (size > 0) {
samplerHash.reserve(MAX_SAMPLER_COUNT);
for (auto sampler : samplers) {
samplerHash.push_back(cache->getKey(sampler));
}
}
if (size == 1) {
return (uint64_t) samplerHash[0];
}
return utils::hash::murmur3((uint32_t*) samplerHash.data(), samplerHash.size() * 2, 0);
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
fvkmemory::ResourceManager* resourceManager, VulkanSamplerCache* cache)
: mDevice(device),
mResourceManager(resourceManager) {}
mResourceManager(resourceManager), mSamplerCache(cache) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
@@ -131,7 +142,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers, mSamplerCache),
};
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second;
@@ -153,9 +164,20 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
.bindingCount = count,
.pBindings = toBind,
};
LayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
utils::FixedCapacityVector<uint32_t> immutableSamplersHashes;
immutableSamplersHashes.reserve(MAX_SAMPLER_COUNT);
for (auto sampler: immutableSamplers) {
immutableSamplersHashes.push_back(mSamplerCache->getKey(sampler));
}
VulkanDescriptorSetLayoutSerializer setSer(dlinfo, immutableSamplersHashes, hashedKey);
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
mLayoutToKey[vklayout] = hashedKey;
return vklayout;
}
@@ -19,6 +19,8 @@
#include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
@@ -35,7 +37,8 @@ namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager,
VulkanSamplerCache* cache);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
@@ -48,10 +51,19 @@ public:
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
uint32_t getKey(VkDescriptorSetLayout layout) {
uint32_t key = 0;
auto iter = mLayoutToKey.find(layout);
if (iter != mLayoutToKey.end()) {
key = iter->second;
}
return key;
}
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
VulkanSamplerCache* mSamplerCache;
struct LayoutKey {
// this describes the layout using bitset.
@@ -69,6 +81,7 @@ private:
};
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
std::map<VkDescriptorSetLayout, uint32_t> mLayoutToKey;
};
} // namespace filament::backend
@@ -0,0 +1,87 @@
#include "VulkanDescriptorSetLayoutSerializer.h"
#include <fstream>
#include <sstream>
#include <utils/Hash.h>
namespace filament::backend {
VulkanDescriptorSetLayoutSerializer::VulkanDescriptorSetLayoutSerializer(
const VkDescriptorSetLayoutCreateInfo& info,
utils::FixedCapacityVector<uint32_t> immutableSamplers, uint32_t hash) {
std::stringstream filename;
filename << "descriptor_set_layout_" << hash << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "[" << std::endl;
for (uint32_t i = 0; i < info.bindingCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"binding\":" << info.pBindings[i].binding << "," << std::endl;
buffer << "\"descriptor_type\":" << info.pBindings[i].descriptorType << "," << std::endl;
buffer << "\"stage_flags\":" << info.pBindings[i].stageFlags;
if (immutableSamplers.size()) {
buffer << "," << std::endl;
buffer << "\"immutable_samplers\":[" << std::endl;
for (uint32_t i = 0; i < immutableSamplers.size(); ++i) {
buffer << immutableSamplers[i];
if (i < immutableSamplers.size() - 1) {
buffer << ",";
}
}
buffer << "]";
}
buffer << std::endl;
buffer << "}";
if (i < info.bindingCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
file << buffer.str();
}
file.close();
}
VulkanPipelineLayoutSerializer::VulkanPipelineLayoutSerializer(
const VkPipelineLayoutCreateInfo& info, VulkanDescriptorSetLayoutCache* cache,
uint32_t hash) {
std::stringstream filename;
filename << "pipeline_layout_" << hash << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.flags << "," << std::endl;
buffer << "\"layouts\":[" << std::endl;
for (uint32_t i = 0; i < info.setLayoutCount; ++i) {
buffer << cache->getKey(info.pSetLayouts[i]);
if (i < info.setLayoutCount - 1) {
buffer << ",";
}
}
buffer << "]," << std::endl;
buffer << "\"push_constants\":[" << std::endl;
for (uint32_t i = 0; i < info.pushConstantRangeCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"stage_flags\":" << info.pPushConstantRanges[i].stageFlags << "," << std::endl;
buffer << "\"offset\":" << info.pPushConstantRanges[i].offset << ","
<< std::endl;
buffer << "\"size\":" << info.pPushConstantRanges[i].size
<< std::endl;
buffer << "}" << std::endl;
if (i < info.pushConstantRangeCount - 1) {
buffer << ",";
}
}
buffer << "]" << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
} // namespace filament::backend
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
#include "VulkanPipelineLayoutCache.h"
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <sstream>
#include <vector>
namespace filament::backend {
class VulkanDescriptorSetLayoutSerializer {
public:
VulkanDescriptorSetLayoutSerializer(VulkanDescriptorSetLayoutSerializer const&) = delete;
VulkanDescriptorSetLayoutSerializer& operator=(
VulkanDescriptorSetLayoutSerializer const&) = delete;
VulkanDescriptorSetLayoutSerializer(const VkDescriptorSetLayoutCreateInfo& info,
utils::FixedCapacityVector<uint32_t> immutableSamplers,
uint32_t hash);
};
class VulkanPipelineLayoutSerializer {
public:
VulkanPipelineLayoutSerializer(VulkanPipelineLayoutSerializer const&) = delete;
VulkanPipelineLayoutSerializer& operator=(VulkanPipelineLayoutSerializer const&) = delete;
VulkanPipelineLayoutSerializer(const VkPipelineLayoutCreateInfo& layout,
VulkanDescriptorSetLayoutCache* cache, uint32_t hash);
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
+44 -29
View File
@@ -230,17 +230,18 @@ 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()),
mSamplerCache(mPlatform->getDevice(), &mYcbcrConversionCache),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager, &mSamplerCache),
mPipelineLayoutCache(mPlatform->getDevice(), &mDescriptorSetLayoutCache),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
@@ -337,6 +338,8 @@ void VulkanDriver::terminate() {
mDefaultRenderTarget = {};
mPipelineState = {};
mQueryManager.terminate();
mBlitter.terminate();
mReadPixels.terminate();
@@ -857,33 +860,32 @@ 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 VulkanCmdBufferState, which is associated with the current, recording command
// it with appropriate VulkanCmdFence, which is associated with the current, recording command
// buffer.
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
fence->setCmdBufferState(cmdbuf->getState());
fence->setFence(cmdbuf->getFenceStatus());
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<VulkanCmdBufferState> cmdbufState;
std::shared_ptr<VulkanCmdFence> fenceStatus;
if (mCurrentRenderPass.commandBuffer) {
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
cmdbufState = cmdBuff->getState();
fence = cmdbufState->getVkFence();
fence = cmdBuff->getVkFence();
fenceStatus = cmdBuff->getFenceStatus();
// If we're currently recording, flush so that the fence only applies
// to commands already issued.
mCommands.flush();
} else {
cmdbufState = mCommands.getMostRecentBufferState();
fence = cmdbufState->getVkFence();
fence = mCommands.getMostRecentFence();
fenceStatus = mCommands.getMostRecentFenceStatus();
}
{
std::lock_guard<std::mutex> guard(sync->lock);
sync->sync = mPlatform->createSync(fence, cmdbufState);
sync->sync = mPlatform->createSync(fence, fenceStatus);
}
for (auto& cbData : sync->conversionCallbacks) {
@@ -1073,10 +1075,9 @@ 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 handle = mResourceManager.allocHandle<VulkanTimerQuery>();
auto query = resource_ptr<VulkanTimerQuery>::make(&mResourceManager, handle);
auto query = mQueryManager.getNextQuery(&mResourceManager);
query.inc();
return handle;
return Handle<VulkanTimerQuery>(query.id());
}
Handle<HwDescriptorSetLayout> VulkanDriver::createDescriptorSetLayoutS() noexcept {
@@ -1133,6 +1134,7 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
return;
}
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
mQueryManager.clearQuery(vtq);
vtq.dec();
}
@@ -1195,6 +1197,7 @@ 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
@@ -1211,14 +1214,14 @@ FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout
until = now + nanoseconds(timeout);
}
auto const [cmdbufState, canceled] = fence->wait(until);
if (!cmdbufState || canceled) {
auto const [cmdfence, canceled] = fence->wait(until);
if (!cmdfence || canceled) {
return canceled ? FenceStatus::ERROR : FenceStatus::TIMEOUT_EXPIRED;
}
// now we are holding a reference to our VulkanCmdBufferState, so we know it can't
// now we are holding a reference to our VulkanCmdFence, so we know it can't
// disappear while we wait
return cmdbufState->waitOnFence(mPlatform->getDevice(), timeout, until);
return cmdfence->wait(mPlatform->getDevice(), timeout, until);
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
@@ -1515,17 +1518,29 @@ void VulkanDriver::setupExternalImage(void* image) {
TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
if (!vtq->isComplete()) {
if (!vtq->isCompleted()) {
return TimerQueryResult::NOT_READY;
}
uint64_t result = vtq->getResult();
assert_invariant(result != VulkanTimerQuery::UNKNOWN_QUERY_RESULT);
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;
}
// 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(result) * period);
*elapsedTime = uint64_t(float(end - begin) * period);
return TimerQueryResult::AVAILABLE;
}
@@ -1651,7 +1666,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.subpassMask = uint8_t(params.subpassMask);
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, 0);
mPipelineCache.bindRenderPass(renderPass, mFramebufferCache.getRenderPassKey(renderPass), 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
@@ -1770,6 +1785,7 @@ void VulkanDriver::nextSubpass(int) {
VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
mFramebufferCache.getRenderPassKey(mCurrentRenderPass.renderPass),
++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) {
@@ -2145,7 +2161,8 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask;
mPipelineCache.bindLayout(pipelineLayout);
auto key = mPipelineLayoutCache.getKey(pipelineLayout);
mPipelineCache.bindLayout(pipelineLayout, key);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
}
@@ -2284,14 +2301,12 @@ void VulkanDriver::scissor(Viewport scissorBox) {
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
auto cmdbuf = &mCommands.get();
vtq->setBeginState(cmdbuf->getState());
mQueryManager.beginQuery(&(mCommands.get()), vtq);
}
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
auto cmdbuf = &mCommands.get();
vtq->setEndState(cmdbuf->getState());
mQueryManager.endQuery(&(mCommands.get()), vtq);
}
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
+3 -1
View File
@@ -25,6 +25,7 @@
#include "VulkanHandles.h"
#include "VulkanMemory.h"
#include "VulkanPipelineCache.h"
#include "VulkanQueryManager.h"
#include "VulkanReadPixels.h"
#include "VulkanSamplerCache.h"
#include "VulkanSemaphoreManager.h"
@@ -147,7 +148,6 @@ private:
VulkanSemaphoreManager mSemaphoreManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanBufferCache mBufferCache;
@@ -157,7 +157,9 @@ 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,6 +15,7 @@
*/
#include "VulkanFboCache.h"
#include "VulkanRenderPassStateSerializer.h"
#include "VulkanConstants.h"
#include "vulkan/utils/Image.h"
@@ -69,6 +70,15 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
uint32_t VulkanFboCache::getRenderPassKey(VkRenderPass pass) noexcept {
uint32_t key = 0;
auto iter = mRenderPassToKey.find(pass);
if (iter != mRenderPassToKey.end()) {
key = iter->second;
}
return key;
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
@@ -117,6 +127,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
mFramebufferCache[config] = {framebuffer, mCurrentTime};
return framebuffer;
}
@@ -326,6 +337,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error;
RenderPassHash hashFunc;
uint32_t key = hashFunc(config);
VulkanRenderPassStateSerializer passSer(renderPassInfo, key);
mRenderPassToKey[renderPass] = key;
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
@@ -24,6 +24,7 @@
#include <backend/TargetBufferInfo.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -97,6 +98,8 @@ public:
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
uint32_t getRenderPassKey(VkRenderPass pass) noexcept;
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
@@ -118,6 +121,7 @@ private:
FboMap mFramebufferCache;
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
std::map<VkRenderPass, uint32_t> mRenderPassToKey;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
};
@@ -33,6 +33,7 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h>
#include <fstream>
using namespace bluevk;
@@ -179,6 +180,23 @@ VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanT
};
}
static inline uint64_t serializeShaderModule(utils::CString progName, VkShaderModuleCreateInfo info,
VkShaderStageFlagBits stage) {
std::stringstream filename;
filename << progName.c_str() << "_" << stage << "_";
constexpr uint32_t wordReadCount = 8;
uint64_t key = utils::hash::murmur3(info.pCode, wordReadCount, 0);
key |= uint64_t(utils::hash::murmur3(info.pCode + (wordReadCount / 2), wordReadCount, 0)) << 32;
filename << key << ".bin";
std::ofstream file(filename.str());
if (file.is_open()) {
file.write(reinterpret_cast<const char*>(info.pCode), info.codeSize);
}
file.close();
return key;
}
} // anonymous namespace
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
@@ -280,6 +298,12 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
.codeSize = dataSize,
.pCode = data,
};
if (i == 0) {
mVertexShaderHash = serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_VERTEX_BIT);
} else if (i == 1) {
mFragmentShaderHash =
serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_FRAGMENT_BIT);
}
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create shader module."
@@ -65,6 +65,8 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorSetLayoutHashArray = std::array<uint64_t,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
// The bitmask representation of a set layout.
struct Bitmask {
@@ -280,6 +282,10 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
inline uint64_t getVertexShaderHash() { return mVertexShaderHash;
}
inline uint64_t getFragmentShaderHash() { return mFragmentShaderHash; }
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
@@ -295,6 +301,8 @@ private:
};
PipelineInfo* mInfo;
uint64_t mVertexShaderHash;
uint64_t mFragmentShaderHash;
VkDevice mDevice = VK_NULL_HANDLE;
};
@@ -15,6 +15,7 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
@@ -42,8 +43,9 @@ VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
}
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout, uint32_t key) noexcept {
mPipelineRequirements.layout = layout;
mPipelineLayoutKey = key;
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept {
@@ -77,6 +79,7 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
VulkanPipelineStateSerializer serializer(mProgramName);
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
shaderStages[0] = {
@@ -89,6 +92,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = mPipelineRequirements.shaders[1];
VulkanPipelineStateSerializer::ShaderStageInfo vtx = { shaderStages[0].stage, mVertexShaderHash,
shaderStages[0].pName };
VulkanPipelineStateSerializer::ShaderStageInfo frg = { shaderStages[1].stage, mFragmentShaderHash,
shaderStages[1].pName };
serializer << vtx << frg;
bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
@@ -107,9 +116,11 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
if (mPipelineRequirements.vertexAttributes[i].format > 0) {
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
serializer << mPipelineRequirements.vertexAttributes[i];
}
if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
serializer << mPipelineRequirements.vertexBuffers[i];
}
}
VkPipelineVertexInputStateCreateInfo vertexInputState = {
@@ -123,11 +134,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
};
serializer << inputAssemblyState;
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
serializer << viewportState;
VkDynamicState dynamicStateEnables[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
@@ -137,6 +152,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.dynamicStateCount = 2,
.pDynamicStates = dynamicStateEnables,
};
serializer << dynamicState;
auto const& raster = mPipelineRequirements.rasterState;
VkPipelineRasterizationStateCreateInfo vkRaster = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@@ -150,6 +167,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.depthBiasSlopeFactor = raster.depthBiasSlopeFactor,
.lineWidth = 1.0f,
};
serializer << vkRaster;
VkPipelineMultisampleStateCreateInfo vkMs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples,
@@ -158,6 +177,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
.alphaToOneEnable = VK_FALSE,
};
serializer << vkMs;
VkPipelineDepthStencilStateCreateInfo vkDs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
@@ -177,6 +198,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.writeMask = 0u,
.reference = 0u,
};
serializer << vkDs;
//layout
serializer.setPipelineLayoutKey(mPipelineLayoutKey);
// renderPass
serializer.setRenderPassKey(mRenderPassKey);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
@@ -215,6 +242,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
colorBlendAttachments[i] = colorBlendAttachments[0];
}
}
serializer << colorBlendState;
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
@@ -230,12 +258,20 @@ 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)
@@ -250,9 +286,10 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
mPipelineRequirements.rasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass;
mPipelineRequirements.subpassIndex = subpassIndex;
mRenderPassKey = passKey;
}
void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept {
@@ -88,7 +88,7 @@ public:
VulkanPipelineCache(VkDevice device);
void bindLayout(VkPipelineLayout layout) noexcept;
void bindLayout(VkPipelineLayout layout, uint32_t key) noexcept;
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
void bindPipeline(VulkanCommandBuffer* commands);
@@ -96,7 +96,7 @@ public:
// Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
@@ -207,6 +207,11 @@ 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,6 +15,7 @@
*/
#include "VulkanPipelineLayoutCache.h"
#include "VulkanDescriptorSetLayoutSerializer.h"
namespace filament::backend {
@@ -23,7 +24,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
fvkmemory::resource_ptr<VulkanProgram> program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
key.descSetLayouts = descriptorSetLayouts;
DescriptorSetLayoutHashArray hashArray;
for (uint32_t i = 0; i < descriptorSetLayouts.size(); ++i) {
hashArray[i] = mDescriptorSetCache->getKey(descriptorSetLayouts[i]);
}
key.descSetLayouts = hashArray;
for (auto layoutHandle: descriptorSetLayouts) {
if (layoutHandle == VK_NULL_HANDLE) {
break;
@@ -62,11 +67,14 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = key.descSetLayouts.data(),
.pSetLayouts = descriptorSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges,
};
PipelineLayoutKeyHashFn hashFunc;
uint32_t hashedKey = hashFunc(key);
VulkanPipelineLayoutSerializer pipelineSer(info, mDescriptorSetCache, hashedKey);
VkPipelineLayout layout;
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
@@ -74,6 +82,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.handle = layout,
.lastUsed = mTimestamp++,
};
mPipelineToKey[layout] = hashedKey;
return layout;
}
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include <bluevk/BlueVK.h>
@@ -30,10 +31,11 @@ namespace filament::backend {
class VulkanPipelineLayoutCache {
public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetLayoutHashArray = VulkanDescriptorSetLayout::DescriptorSetLayoutHashArray;
VulkanPipelineLayoutCache(VkDevice device)
VulkanPipelineLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache* cache)
: mDevice(device),
mTimestamp(0) {}
mTimestamp(0), mDescriptorSetCache(cache) {}
void terminate() noexcept;
@@ -45,7 +47,7 @@ public:
};
struct PipelineLayoutKey {
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4
DescriptorSetLayoutHashArray descSetLayouts = {}; // 8 * 4
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
uint16_t padding = 0;
};
@@ -58,6 +60,14 @@ public:
// are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program);
uint32_t getKey(VkPipelineLayout layout) {
uint32_t key = 0;
auto iter = mPipelineToKey.find(layout);
if (iter != mPipelineToKey.end()) {
key = iter->second;
}
return key;
}
private:
using Timestamp = uint64_t;
@@ -79,6 +89,8 @@ private:
VkDevice mDevice;
Timestamp mTimestamp;
PipelineLayoutMap mPipelineLayouts;
std::map<VkPipelineLayout, uint32_t> mPipelineToKey;
VulkanDescriptorSetLayoutCache* mDescriptorSetCache;
};
} // filament::backend
@@ -0,0 +1,251 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fstream>
#include "VulkanPipelineStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-stack-address"
#endif
using namespace bluevk;
namespace filament::backend {
VulkanPipelineStateSerializer::VulkanPipelineStateSerializer(const utils::CString& name) {
mBuffer << "{" << std::endl;
mBuffer << "\"program_name\":" << "\"" << name.c_str() << "\"" << "," << std::endl;
}
VulkanPipelineStateSerializer::~VulkanPipelineStateSerializer() {
//Shader Modules
mBuffer << "\"shader_modules\":[" << std::endl;
for (uint32_t i = 0; i < mShaderStages.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"stage\":" << mShaderStages[i].stage << "," << std::endl;
mBuffer << "\"key\":" << mShaderStages[i].module << "," << std::endl;
mBuffer << "\"name\":" << "\"" << mShaderStages[i].mName << "\"" << std::endl;
mBuffer << "}";
if (i < mShaderStages.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
//Vertex Attribute
mBuffer << "\"vertex_attributes\":[" << std::endl;
for (uint32_t i = 0; i < mVtxAttribs.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"location\":" << mVtxAttribs[i].location << "," << std::endl;
mBuffer << "\"binding\":" << mVtxAttribs[i].binding << "," << std::endl;
mBuffer << "\"format\":" << mVtxAttribs[i].format << "," << std::endl;
mBuffer << "\"offset\":" << mVtxAttribs[i].offset << std::endl;
mBuffer << "}";
if (i < mVtxAttribs.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
// Vertex Bindings
mBuffer << "\"vertex_bindings\":[" << std::endl;
for (uint32_t i = 0; i < mVtxBindings.size(); ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"binding\":" << mVtxBindings[i].binding << "," << std::endl;
mBuffer << "\"stride\":" << mVtxBindings[i].stride << "," << std::endl;
mBuffer << "\"input_rate\":" << mVtxBindings[i].inputRate << std::endl;
mBuffer << "}";
if (i < mVtxBindings.size() - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]" << std::endl;
mBuffer << "}" << std::endl;
std::ofstream file(mFileName);
if (file.is_open()) {
file << mBuffer.str();
}
file.close();
}
void VulkanPipelineStateSerializer::setPipelineLayoutKey(uint32_t pipelineLayoutKey) {
mBuffer << "\"layout\":" << pipelineLayoutKey << "," << std::endl;
}
void VulkanPipelineStateSerializer::setRenderPassKey(uint32_t renderPassKey) {
mBuffer << "\"render_pass\":" << renderPassKey << "," << std::endl;
}
void VulkanPipelineStateSerializer::setID(uint32_t key) {
std::stringstream temp;
temp << "pipeline_" << key << ".json";
temp >> mFileName;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineInputAssemblyStateCreateInfo& inputAsm) {
mBuffer << "\"topology\":" << inputAsm.topology << "," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineViewportStateCreateInfo& viewport) {
mBuffer << "\"viewport_count\":" << viewport.viewportCount << "," << std::endl;
mBuffer << "\"scissor_count\":" << viewport.scissorCount << "," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineRasterizationStateCreateInfo& rasterState) {
mBuffer << "\"raster_states\":{" << std::endl;
mBuffer << "\"depth_clamp_enable\":" << rasterState.depthClampEnable << "," << std::endl;
mBuffer << "\"polygon_mode\":" << rasterState.polygonMode << "," << std::endl;
mBuffer << "\"cull_mode\":" << rasterState.cullMode << "," << std::endl;
mBuffer << "\"front_face\":" << rasterState.frontFace << "," << std::endl;
mBuffer << "\"depth_bias_enable\":" << rasterState.depthBiasEnable << "," << std::endl;
mBuffer << "\"depth_bias_constant_factor\":" << rasterState.depthBiasConstantFactor << ","
<< std::endl;
mBuffer << "\"depth_bias_clamp\":" << rasterState.depthBiasClamp << "," << std::endl;
mBuffer << "\"depth_bias_slope_factor\":" << rasterState.depthBiasSlopeFactor << "," << std::endl;
mBuffer << "\"line_width\":" << rasterState.lineWidth << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineDepthStencilStateCreateInfo& depthStencil) {
mBuffer << "\"depth_stencil_states\":{" << std::endl;
mBuffer << "\"depth_test_enable\":" << depthStencil.depthTestEnable << "," << std::endl;
mBuffer << "\"depth_write_enable\":" << depthStencil.depthWriteEnable << "," << std::endl;
mBuffer << "\"depth_compare_op\":" << depthStencil.depthCompareOp << "," << std::endl;
mBuffer << "\"depth_bounds_test_enable\":" << depthStencil.depthBoundsTestEnable << ","
<< std::endl;
mBuffer << "\"min_depth_bounds\":" << depthStencil.minDepthBounds << "," << std::endl;
mBuffer << "\"max_depth_bounds\":" << depthStencil.maxDepthBounds << "," << std::endl;
mBuffer << "\"front\":{" << std::endl;
mBuffer << "\"fail_op\":" << depthStencil.front.failOp << "," << std::endl;
mBuffer << "\"pass_op\":" << depthStencil.front.passOp << "," << std::endl;
mBuffer << "\"depth_fail_op\":" << depthStencil.front.depthFailOp << "," << std::endl;
mBuffer << "\"compare_op\":" << depthStencil.front.compareOp << "," << std::endl;
mBuffer << "\"compare_mask\":" << depthStencil.front.compareMask << "," << std::endl;
mBuffer << "\"write_mask\":" << depthStencil.front.writeMask << "," << std::endl;
mBuffer << "\"reference\":" << depthStencil.front.reference << std::endl;
mBuffer << "}," << std::endl;
mBuffer << "\"back\":{" << std::endl;
mBuffer << "\"fail_op\":" << depthStencil.back.failOp << "," << std::endl;
mBuffer << "\"pass_op\":" << depthStencil.back.passOp << "," << std::endl;
mBuffer << "\"depth_fail_op\":" << depthStencil.back.depthFailOp << "," << std::endl;
mBuffer << "\"compare_op\":" << depthStencil.back.compareOp << "," << std::endl;
mBuffer << "\"compare_mask\":" << depthStencil.back.compareMask << "," << std::endl;
mBuffer << "\"write_mask\":" << depthStencil.back.writeMask << "," << std::endl;
mBuffer << "\"reference\":" << depthStencil.back.reference << std::endl;
mBuffer << "}" << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineMultisampleStateCreateInfo& multiSample) {
mBuffer << "\"multisample_states\":{" << std::endl;
mBuffer << "\"rasterization_samples\":" << multiSample.rasterizationSamples << "," << std::endl;
mBuffer << "\"sample_shading_enable\":" << multiSample.sampleShadingEnable << "," << std::endl;
mBuffer << "\"min_sample_shading\":" << multiSample.minSampleShading << "," << std::endl;
mBuffer << "\"alpha_to_coverage_enable\":" << multiSample.alphaToCoverageEnable << ","
<< std::endl;
mBuffer << "\"alpha_to_one_enable\":" << multiSample.alphaToOneEnable << std::endl;
mBuffer << "}," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineDynamicStateCreateInfo& dyStates) {
mBuffer << "\"dynamic_states\":[" << std::endl;
for (uint32_t i = 0; i < dyStates.dynamicStateCount; ++i) {
mBuffer << dyStates.pDynamicStates[i];
if (i < dyStates.dynamicStateCount - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkPipelineColorBlendStateCreateInfo& blendStateInfo) {
mBuffer << "\"color_blend_state_attachments\":[" << std::endl;
for (uint32_t i = 0; i < blendStateInfo.attachmentCount; ++i) {
mBuffer << "{" << std::endl;
mBuffer << "\"blend_enable\":" << blendStateInfo.pAttachments[i].blendEnable << ","
<< std::endl;
mBuffer << "\"src_color_blend_factor\":" << blendStateInfo.pAttachments[i].srcColorBlendFactor
<< ","
<< std::endl;
mBuffer << "\"dst_color_blend_factor\":" << blendStateInfo.pAttachments[i].dstColorBlendFactor
<< ","
<< std::endl;
mBuffer << "\"color_blend_op\":" << blendStateInfo.pAttachments[i].colorBlendOp << ","
<< std::endl;
mBuffer << "\"src_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].srcAlphaBlendFactor
<< ","
<< std::endl;
mBuffer << "\"dst_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].dstAlphaBlendFactor
<< ","
<< std::endl;
mBuffer << "\"alpha_blend_op\":" << blendStateInfo.pAttachments[i].alphaBlendOp << ","
<< std::endl;
mBuffer << "\"color_write_mask\":" << blendStateInfo.pAttachments[i].colorWriteMask
<< std::endl;
mBuffer << "}";
if (i < blendStateInfo.attachmentCount - 1) {
mBuffer << ",";
}
mBuffer << std::endl;
}
mBuffer << "]," << std::endl;
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const ShaderStageInfo& shaderStages) {
mShaderStages.push_back(shaderStages);
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkVertexInputAttributeDescription& attrib) {
mVtxAttribs.push_back(attrib);
return *this;
}
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
const VkVertexInputBindingDescription& binding) {
mVtxBindings.push_back(binding);
return *this;
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
#include "VulkanCommands.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <type_traits>
#include <sstream>
#include <vector>
namespace filament::backend {
// VulkanPipelineStateSerializer stores to a file the pipeline states.
class VulkanPipelineStateSerializer {
public:
VulkanPipelineStateSerializer(VulkanPipelineStateSerializer const&) = delete;
VulkanPipelineStateSerializer& operator=(VulkanPipelineStateSerializer const&) = delete;
VulkanPipelineStateSerializer(const utils::CString& name);
~VulkanPipelineStateSerializer();
void setID(uint32_t key);
void setPipelineLayoutKey(uint32_t pipelineLayoutKey);
void setRenderPassKey(uint32_t renderPassKey);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineInputAssemblyStateCreateInfo& inputAsm);
VulkanPipelineStateSerializer& operator<<(const VkPipelineViewportStateCreateInfo& viewport);
VulkanPipelineStateSerializer& operator<<(const VkPipelineDynamicStateCreateInfo& dyStates);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineRasterizationStateCreateInfo& rasterState);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineDepthStencilStateCreateInfo& depthStencil);
VulkanPipelineStateSerializer& operator<<(const VkPipelineMultisampleStateCreateInfo& multiSample);
VulkanPipelineStateSerializer& operator<<(
const VkPipelineColorBlendStateCreateInfo& blendStateInfo);
struct ShaderStageInfo {
VkShaderStageFlagBits stage;
uint64_t module;
std::string mName;
};
VulkanPipelineStateSerializer& operator<<(const ShaderStageInfo& shaderStages);
VulkanPipelineStateSerializer& operator<<(
const VkVertexInputAttributeDescription& attrib);
VulkanPipelineStateSerializer& operator<<(const VkVertexInputBindingDescription& binding);
private:
std::string mFileName;
std::stringstream mBuffer;
//temp data
utils::CString mProgramName;
std::vector<ShaderStageInfo> mShaderStages;
std::vector<VkVertexInputAttributeDescription> mVtxAttribs;
std::vector<VkVertexInputBindingDescription> mVtxBindings;
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
@@ -0,0 +1,106 @@
/*
* 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
@@ -0,0 +1,63 @@
/*
* 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
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fstream>
#include "VulkanRenderPassStateSerializer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-stack-address"
#endif
using namespace bluevk;
namespace filament::backend {
VulkanRenderPassStateSerializer::VulkanRenderPassStateSerializer(
const VkRenderPassCreateInfo& info, uint32_t key) {
std::stringstream filename;
filename << "render_pass_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
buffer << "{" << std::endl;
buffer << "\"attachments\":[" << std::endl;
for (uint32_t i = 0; i < info.attachmentCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.pAttachments[i].flags << "," << std::endl;
buffer << "\"format\":" << info.pAttachments[i].format << "," << std::endl;
buffer << "\"samples\":" << info.pAttachments[i].samples << "," << std::endl;
buffer << "\"load_op\":" << info.pAttachments[i].loadOp << "," << std::endl;
buffer << "\"store_op\":" << info.pAttachments[i].storeOp << "," << std::endl;
buffer << "\"stencil_load_op\":" << info.pAttachments[i].stencilLoadOp << "," << std::endl;
buffer << "\"stencil_store_op\":" << info.pAttachments[i].stencilStoreOp << "," << std::endl;
buffer << "\"initial_layout\":" << info.pAttachments[i].initialLayout << "," << std::endl;
buffer << "\"final_layout\":" << info.pAttachments[i].finalLayout << std::endl;
buffer << "}";
if (i < info.attachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]," << std::endl;
buffer << "\"subpasses\":[" << std::endl;
for (uint32_t i = 0; i < info.subpassCount; ++i) {
buffer << "{" << std::endl;
buffer << "\"flags\":" << info.pSubpasses[i].flags << "," << std::endl;
buffer << "\"pipeline_bind_point\":" << info.pSubpasses[i].pipelineBindPoint;
//------------depth stencil-------------------
if (info.pSubpasses[i].pDepthStencilAttachment) {
buffer << "," << std::endl;
buffer << "\"depth_stencil_attachment\":{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pDepthStencilAttachment->attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pDepthStencilAttachment->layout
<< std::endl;
buffer << "}";
}
//------------Resolve-------------------
if (info.pSubpasses[i].pResolveAttachments) {
buffer << "," << std::endl;
buffer << "\"resolve_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pResolveAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pResolveAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]";
}
//------------Color-------------------
if (info.pSubpasses[i].pColorAttachments) {
buffer << "," << std::endl;
buffer << "\"color_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pColorAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pColorAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]";
}
//------------Input-------------------
if (info.pSubpasses[i].pInputAttachments) {
buffer << "," << std::endl;
buffer << "\"input_attachments\":[" << std::endl;
for (uint32_t j = 0; j < info.pSubpasses[i].inputAttachmentCount; ++j) {
buffer << "{" << std::endl;
buffer << "\"attachement\":"
<< info.pSubpasses[i].pInputAttachments[j].attachment << ","
<< std::endl;
buffer << "\"layout\":" << info.pSubpasses[i].pInputAttachments[j].layout
<< std::endl;
buffer << "}" << std::endl;
if (j < info.pSubpasses[i].inputAttachmentCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
}
buffer << "}";
if (i < info.subpassCount - 1) {
buffer << ",";
}
buffer << std::endl;
}
buffer << "]" << std::endl;
buffer << "}";
file << buffer.str();
}
file.close();
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
#include "VulkanCommands.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <bluevk/BlueVK.h>
#include <iostream>
#include <type_traits>
#include <sstream>
#include <vector>
namespace filament::backend {
class VulkanRenderPassStateSerializer {
public:
VulkanRenderPassStateSerializer(VulkanRenderPassStateSerializer const&) = delete;
VulkanRenderPassStateSerializer& operator=(VulkanRenderPassStateSerializer const&) = delete;
VulkanRenderPassStateSerializer(const VkRenderPassCreateInfo& info, uint32_t key);
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
@@ -21,12 +21,14 @@
#include <utils/Panic.h>
#include "VulkanSamplerStateSerializer.h"
using namespace bluevk;
namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VulkanSamplerCache::VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache)
: mDevice(device), mConversionCache(conversionCache) {}
VkSampler VulkanSamplerCache::getSampler(Params params) {
auto iter = mCache.find(params);
@@ -57,11 +59,16 @@ VkSampler VulkanSamplerCache::getSampler(Params params) {
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VulkanSamplerStateSerializer samplerSer(params,
params.conversion != VK_NULL_HANDLE ? mConversionCache->getKey(params.conversion) : 0);
VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, sampler });
SamplerHashFn hashFn;
mSamplerToKey[sampler] = hashFn(params);
return sampler;
}
@@ -17,9 +17,11 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include "VulkanYcbcrConversionCache.h"
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
#include <map>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
@@ -37,11 +39,20 @@ public:
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device);
explicit VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache);
VkSampler getSampler(Params params);
uint32_t getKey(VkSampler sampler) {
uint32_t key = 0;
auto iter = mSamplerToKey.find(sampler);
if (iter != mSamplerToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
VkDevice mDevice;
VulkanYcbcrConversionCache* mConversionCache;
struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
@@ -51,6 +62,7 @@ private:
};
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
std::map<VkSampler, uint32_t> mSamplerToKey;
};
} // namespace filament::backend
@@ -0,0 +1,64 @@
#include "VulkanSamplerStateSerializer.h"
#include <fstream>
#include <sstream>
#include <utils/Hash.h>
namespace filament::backend {
VulkanYcbcrConversionSerializer::VulkanYcbcrConversionSerializer(
const VulkanYcbcrConversionCache::Params& params) {
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanYcbcrConversionCache::Params>;
ConversionHashFn hashFn;
uint32_t key = hashFn(params);
std::stringstream filename;
filename << "ycbcr_conv_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
auto const& chroma = params.conversion;
buffer << "{" << std::endl;
buffer << "\"format\":" << params.format << "," << std::endl;
buffer << "\"external_format\":" << params.externalFormat << "," << std::endl;
buffer << "\"ycbcr_model\":" << uint8_t(chroma.ycbcrModel) << "," << std::endl;
buffer << "\"ycbcr_range\":" << uint8_t(chroma.ycbcrRange) << "," << std::endl;
buffer << "\"swizzle_array\":[" << uint8_t(chroma.r) << "," << uint8_t(chroma.g) << ","
<< uint8_t(chroma.b) << "," << uint8_t(chroma.a) << "]," << std::endl;
buffer << "\"x_chroma_offset\":" << uint8_t(chroma.xChromaOffset) << "," << std::endl;
buffer << "\"y_chroma_offset\":" << uint8_t(chroma.yChromaOffset) << "," << std::endl;
buffer << "\"chroma_filter\":" << uint8_t(chroma.chromaFilter) << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
VulkanSamplerStateSerializer::VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key) {
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanSamplerCache::Params>;
ConversionHashFn hashFn;
uint32_t key = hashFn(params);
std::stringstream filename;
filename << "sampler_" << key << ".json";
std::ofstream file(filename.str());
if (file.is_open()) {
std::stringstream buffer;
auto const& samplerParams = params.sampler;
buffer << "{" << std::endl;
buffer << "\"filter_mag\":" << uint32_t(samplerParams.filterMag) << "," << std::endl;
buffer << "\"filter_min\":" << uint32_t(samplerParams.filterMin) << "," << std::endl;
buffer << "\"wrap_s\":" << uint32_t(samplerParams.wrapS) << "," << std::endl;
buffer << "\"wrap_t\":" << uint32_t(samplerParams.wrapT) << "," << std::endl;
buffer << "\"wrap_r\":" << uint32_t(samplerParams.wrapR) << "," << std::endl;
buffer << "\"anisotropy_log2\":" << uint32_t(samplerParams.anisotropyLog2) << "," << std::endl;
buffer << "\"compare_mode\":" << uint32_t(samplerParams.compareMode) << "," << std::endl;
buffer << "\"compare_func\":" << uint32_t(samplerParams.compareFunc) << "," << std::endl;
buffer << "\"ycbcr_conv_key\":" << ycbcr_conv_key << std::endl;
buffer << "}" << std::endl;
file << buffer.str();
}
file.close();
}
} // namespace filament::backend
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
#include <backend/DriverEnums.h>
#include "VulkanYcbcrConversionCache.h"
#include "VulkanSamplerCache.h"
#include <bluevk/BlueVK.h>
#include <vector>
#include <iostream>
#include <sstream>
namespace filament::backend {
class VulkanYcbcrConversionSerializer {
public:
VulkanYcbcrConversionSerializer(VulkanYcbcrConversionSerializer const&) = delete;
VulkanYcbcrConversionSerializer& operator=(VulkanYcbcrConversionSerializer const&) = delete;
VulkanYcbcrConversionSerializer(const VulkanYcbcrConversionCache::Params& params);
};
// VulkanPipelineStateSerializer stores to a file the pipeline states.
class VulkanSamplerStateSerializer {
public:
VulkanSamplerStateSerializer(VulkanSamplerStateSerializer const&) = delete;
VulkanSamplerStateSerializer& operator=(VulkanSamplerStateSerializer const&) = delete;
VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key);
};
}
#endif //TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
@@ -15,6 +15,7 @@
*/
#include "VulkanYcbcrConversionCache.h"
#include "VulkanSamplerStateSerializer.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
@@ -53,6 +54,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VulkanYcbcrConversionSerializer ycbcr_ser(params);
// We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__)
@@ -73,6 +75,8 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion });
ConversionHashFn hashFn;
mConversionToKey[conversion] = hashFn(params);
return conversion;
}
@@ -25,6 +25,7 @@
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <map>
namespace filament::backend {
@@ -40,6 +41,14 @@ public:
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params);
uint32_t getKey(VkSamplerYcbcrConversion conv) {
uint32_t key = 0;
auto iter = mConversionToKey.find(conv);
if (iter != mConversionToKey.end()) {
key = iter->second;
}
return key;
}
void terminate() noexcept;
private:
@@ -54,6 +63,7 @@ private:
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
std::map<VkSamplerYcbcrConversion, uint32_t> mConversionToKey;
};
} // namespace filament::backend
@@ -971,7 +971,7 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
}
Platform::Sync* VulkanPlatform::createSync(VkFence fence,
std::shared_ptr<VulkanCmdBufferState> fenceStatus) noexcept {
std::shared_ptr<VulkanCmdFence> 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->getFenceStatus() == VK_SUCCESS) {
if (vulkanSync.fenceStatus->getStatus() == VK_SUCCESS) {
// We've already signaled; return -1 so that operations will proceed
// immediately. Also, signal that fence conversion was successful.
*fd = -1;