Compare commits
1 Commits
MapAsyncEx
...
pf/vk-time
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
910c70f21b |
@@ -216,8 +216,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanPipelineCache.h
|
||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/VulkanQueryManager.cpp
|
||||
src/vulkan/VulkanQueryManager.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanSamplerCache.cpp
|
||||
|
||||
@@ -48,7 +48,7 @@ struct VulkanPlatformPrivate;
|
||||
|
||||
// Forward declare the fence status that will be maintained by the command
|
||||
// buffer manager.
|
||||
struct VulkanCmdFence;
|
||||
struct VulkanCmdBufferState;
|
||||
|
||||
/**
|
||||
* A Platform interface that creates a Vulkan backend.
|
||||
@@ -242,7 +242,7 @@ public:
|
||||
* @return A Platform::Sync object tracking the provided fence.
|
||||
*/
|
||||
virtual Platform::Sync* createSync(VkFence fence,
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
|
||||
std::shared_ptr<VulkanCmdBufferState> fenceStatus) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys a sync. If called with a sync not created by this platform
|
||||
@@ -434,7 +434,7 @@ public:
|
||||
protected:
|
||||
struct VulkanSync : public Platform::Sync {
|
||||
VkFence fence;
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus;
|
||||
std::shared_ptr<VulkanCmdBufferState> fenceStatus;
|
||||
};
|
||||
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
|
||||
@@ -29,9 +29,8 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
FenceStatus VulkanCmdFence::wait(VkDevice device, uint64_t const timeout,
|
||||
std::chrono::steady_clock::time_point const until) {
|
||||
|
||||
FenceStatus VulkanCmdBufferState::waitOnFence(VkDevice device, uint64_t const timeout,
|
||||
std::chrono::steady_clock::time_point const until) {
|
||||
// this lock MUST be held for READ when calling vkWaitForFences()
|
||||
std::shared_lock rl(mLock);
|
||||
|
||||
@@ -81,7 +80,7 @@ FenceStatus VulkanCmdFence::wait(VkDevice device, uint64_t const timeout,
|
||||
return FenceStatus::ERROR; // not supported
|
||||
}
|
||||
|
||||
void VulkanCmdFence::resetFence(VkDevice device) {
|
||||
void VulkanCmdBufferState::resetFence(VkDevice device) {
|
||||
// This lock prevents vkResetFences() from being called simultaneously with vkWaitForFences(),
|
||||
// but by construction, when we're here, we know that the fence has signaled and
|
||||
// vkWaitForFences() will return shortly.
|
||||
@@ -90,4 +89,35 @@ void VulkanCmdFence::resetFence(VkDevice device) {
|
||||
vkResetFences(device, 1, &mFence);
|
||||
}
|
||||
|
||||
uint64_t VulkanTimerQuery::getResult() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDurationLock);
|
||||
if (mDuration != UNKNOWN_QUERY_RESULT) {
|
||||
return mDuration;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanCmdBufferState* startState = nullptr;
|
||||
VulkanCmdBufferState* endState = nullptr;
|
||||
{
|
||||
std::shared_lock const l(mMutex);
|
||||
startState = mBeginState.get();
|
||||
endState = mEndState.get();
|
||||
}
|
||||
// Here we sum up the time taken by each command buffer that were marked by this timer query.
|
||||
uint64_t duration = 0;
|
||||
do {
|
||||
uint64_t const stDuration = startState->getBufferDuration();
|
||||
assert_invariant(stDuration != VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION);
|
||||
duration += stDuration;
|
||||
startState = startState->getNextState().get();
|
||||
} while (startState != endState);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDurationLock);
|
||||
mDuration = duration;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
@@ -35,25 +36,36 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
|
||||
struct VulkanCmdFence {
|
||||
explicit VulkanCmdFence(VkFence fence) : mFence(fence) { }
|
||||
~VulkanCmdFence() = default;
|
||||
// Wrapper to enable use of shared_ptr for implementing shared ownership of Vulkan fences
|
||||
// and timer query results.
|
||||
struct VulkanCmdBufferState {
|
||||
static uint64_t const UNKNOWN_BUFFER_DURATION = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
void setStatus(VkResult const value) {
|
||||
explicit VulkanCmdBufferState(VkFence fence)
|
||||
: mFence(fence) {
|
||||
}
|
||||
~VulkanCmdBufferState() = default;
|
||||
|
||||
void setStatus(VkResult const value, uint64_t duration) {
|
||||
std::lock_guard const l(mLock);
|
||||
mStatus = value;
|
||||
mDuration = duration;
|
||||
mCond.notify_all();
|
||||
}
|
||||
|
||||
VkResult getStatus() {
|
||||
uint64_t getBufferDuration() {
|
||||
std::shared_lock const l(mLock);
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
VkResult getFenceStatus() {
|
||||
std::shared_lock const l(mLock);
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void resetFence(VkDevice device);
|
||||
|
||||
FenceStatus wait(VkDevice device, uint64_t timeout,
|
||||
FenceStatus waitOnFence(VkDevice device, uint64_t timeout,
|
||||
std::chrono::steady_clock::time_point until);
|
||||
|
||||
void cancel() {
|
||||
@@ -62,6 +74,18 @@ struct VulkanCmdFence {
|
||||
mCond.notify_all();
|
||||
}
|
||||
|
||||
void setNextState(std::shared_ptr<VulkanCmdBufferState> next) {
|
||||
mNextState = std::move(next);
|
||||
}
|
||||
|
||||
std::shared_ptr<VulkanCmdBufferState>& getNextState() {
|
||||
return mNextState;
|
||||
}
|
||||
|
||||
VkFence getVkFence() const {
|
||||
return mFence;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_mutex mLock; // NOLINT(*-include-cleaner)
|
||||
std::condition_variable_any mCond;
|
||||
@@ -70,38 +94,42 @@ private:
|
||||
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||
// finishes executing the command buffer, the status changes to VK_SUCCESS.
|
||||
VkResult mStatus{ VK_INCOMPLETE };
|
||||
VkFence mFence;
|
||||
VkFence const mFence;
|
||||
|
||||
uint64_t mDuration = UNKNOWN_BUFFER_DURATION;
|
||||
|
||||
// We assume that command buffers are serialized. This will point to the state of the next
|
||||
// buffer. This is necessary so that we can return total duration of multiple command buffers
|
||||
// when a TimerQuery is requested by the front-end.
|
||||
std::shared_ptr<VulkanCmdBufferState> mNextState;
|
||||
};
|
||||
|
||||
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
|
||||
VulkanFence() {}
|
||||
|
||||
void setFence(std::shared_ptr<VulkanCmdFence> fence) {
|
||||
std::lock_guard const l(lock);
|
||||
sharedFence = std::move(fence);
|
||||
void setCmdBufferState(std::shared_ptr<VulkanCmdBufferState> state) {
|
||||
std::lock_guard l(lock);
|
||||
cmdbufState = std::move(state);
|
||||
cond.notify_all();
|
||||
}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence>& getSharedFence() {
|
||||
std::lock_guard const l(lock);
|
||||
return sharedFence;
|
||||
std::shared_ptr<VulkanCmdBufferState>& getCmdBufferState() {
|
||||
std::lock_guard l(lock);
|
||||
return cmdbufState;
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<VulkanCmdFence>, bool>
|
||||
wait(std::chrono::steady_clock::time_point const until) {
|
||||
// hold a reference so that our state doesn't disappear while we wait
|
||||
std::pair<std::shared_ptr<VulkanCmdBufferState>, bool> wait(
|
||||
std::chrono::steady_clock::time_point const until) {
|
||||
std::unique_lock l(lock);
|
||||
cond.wait_until(l, until, [this] {
|
||||
return bool(sharedFence) || canceled;
|
||||
});
|
||||
// here mSharedFence will be null if we timed out
|
||||
return { sharedFence, canceled };
|
||||
cond.wait_until(l, until, [&] { return bool(cmdbufState) || canceled; });
|
||||
// here cmdbufState will be null if we timed out
|
||||
return { cmdbufState, canceled };
|
||||
}
|
||||
|
||||
void cancel() const {
|
||||
std::lock_guard const l(lock);
|
||||
if (sharedFence) {
|
||||
sharedFence->cancel();
|
||||
if (cmdbufState) {
|
||||
cmdbufState->cancel();
|
||||
}
|
||||
canceled = true;
|
||||
cond.notify_all();
|
||||
@@ -111,10 +139,10 @@ private:
|
||||
mutable std::mutex lock;
|
||||
mutable std::condition_variable cond;
|
||||
mutable bool canceled = false;
|
||||
std::shared_ptr<VulkanCmdFence> sharedFence;
|
||||
std::shared_ptr<VulkanCmdBufferState> cmdbufState;
|
||||
};
|
||||
|
||||
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
|
||||
struct VulkanSync : public HwSync, fvkmemory::ThreadSafeResource {
|
||||
struct CallbackData {
|
||||
CallbackHandler* handler;
|
||||
Platform::SyncCallback cb;
|
||||
@@ -128,38 +156,50 @@ struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
|
||||
};
|
||||
|
||||
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
|
||||
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
|
||||
: mStartingQueryIndex(startingIndex),
|
||||
mStoppingQueryIndex(stoppingIndex) {}
|
||||
|
||||
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
|
||||
std::lock_guard const lock(mFenceMutex);
|
||||
mFence = std::move(fence);
|
||||
static decltype(VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION) const
|
||||
UNKNOWN_QUERY_RESULT = VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION;
|
||||
|
||||
VulkanTimerQuery() {}
|
||||
|
||||
void setBeginState(std::shared_ptr<VulkanCmdBufferState> state) {
|
||||
{
|
||||
std::lock_guard const lock(mMutex);
|
||||
mBeginState = std::move(state);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDurationLock);
|
||||
mDuration = UNKNOWN_QUERY_RESULT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool isCompleted() noexcept {
|
||||
std::lock_guard const lock(mFenceMutex);
|
||||
// QueryValue is a synchronous call and might occur before beginTimerQuery has written
|
||||
// anything into the command buffer, which is an error according to the validation layer
|
||||
// that ships in the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to
|
||||
// require that the timestamp has at least been written into a processed command buffer.
|
||||
|
||||
// This fence indicates that the corresponding buffer has been completed.
|
||||
return mFence && mFence->getStatus() == VK_SUCCESS;
|
||||
void setEndState(std::shared_ptr<VulkanCmdBufferState> state) {
|
||||
{
|
||||
std::lock_guard const lock(mMutex);
|
||||
mEndState = std::move(state);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDurationLock);
|
||||
mDuration = UNKNOWN_QUERY_RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getStartingQueryIndex() const { return mStartingQueryIndex; }
|
||||
|
||||
uint32_t getStoppingQueryIndex() const {
|
||||
return mStoppingQueryIndex;
|
||||
bool isComplete() {
|
||||
std::shared_lock const lock(mMutex);
|
||||
return mBeginState && mEndState &&
|
||||
mBeginState->getBufferDuration() != UNKNOWN_QUERY_RESULT &&
|
||||
mEndState->getBufferDuration() != UNKNOWN_QUERY_RESULT;
|
||||
}
|
||||
|
||||
uint64_t getResult();
|
||||
|
||||
private:
|
||||
uint32_t mStartingQueryIndex;
|
||||
uint32_t mStoppingQueryIndex;
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> mFence;
|
||||
utils::Mutex mFenceMutex;
|
||||
std::shared_mutex mMutex; // NOLINT(*-include-cleaner)
|
||||
std::mutex mDurationLock;
|
||||
std::shared_ptr<VulkanCmdBufferState> mBeginState;
|
||||
std::shared_ptr<VulkanCmdBufferState> mEndState;
|
||||
uint64_t mDuration = UNKNOWN_QUERY_RESULT;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -55,6 +55,22 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
|
||||
return cmdbuffer;
|
||||
}
|
||||
|
||||
VkFence createFence(VkDevice device, VulkanContext const& context, VkCommandPool pool) {
|
||||
VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
VkExportFenceCreateInfo exportFenceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
|
||||
};
|
||||
|
||||
// Necessary to guard this. Otherwise, swiftshader would throw an error.
|
||||
if (context.getFenceExportFlags()) {
|
||||
exportFenceCreateInfo.handleTypes = context.getFenceExportFlags(),
|
||||
fenceCreateInfo.pNext = &exportFenceCreateInfo;
|
||||
}
|
||||
VkFence fence;
|
||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
|
||||
return fence;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
@@ -91,30 +107,21 @@ uint32_t VulkanCommandBuffer::sAgeCounter = 0;
|
||||
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
|
||||
VkQueue queue, VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager,
|
||||
bool isProtected)
|
||||
: mContext(context),
|
||||
mMarkerCount(0),
|
||||
isProtected(isProtected),
|
||||
mDevice(device),
|
||||
mQueue(queue),
|
||||
mSemaphoreManager(semaphoreManager),
|
||||
mBuffer(createCommandBuffer(device, pool)),
|
||||
mSubmission(semaphoreManager->acquire()),
|
||||
mAge(++sAgeCounter) {
|
||||
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||
VkExportFenceCreateInfo exportFenceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
|
||||
.handleTypes = context.getFenceExportFlags()
|
||||
};
|
||||
|
||||
// Necessary to guard this. Otherwise, swiftshader would throw an error.
|
||||
if (context.getFenceExportFlags()) {
|
||||
fenceCreateInfo.pNext = &exportFenceCreateInfo;
|
||||
}
|
||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
|
||||
|
||||
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
|
||||
}
|
||||
bool isProtected, uint32_t timerQueryIndex, VkQueryPool queryPool)
|
||||
: mContext(context),
|
||||
mMarkerCount(0),
|
||||
isProtected(isProtected),
|
||||
mDevice(device),
|
||||
mQueue(queue),
|
||||
mSemaphoreManager(semaphoreManager),
|
||||
mBuffer(createCommandBuffer(device, pool)),
|
||||
mSubmission(semaphoreManager->acquire()),
|
||||
mFence(createFence(device, context, pool)),
|
||||
mCmdbufState(std::make_shared<VulkanCmdBufferState>(mFence)),
|
||||
mAge(++sAgeCounter),
|
||||
mQueryBeginIndex(timerQueryIndex),
|
||||
mQueryEndIndex(timerQueryIndex + 1),
|
||||
mQueryPool(queryPool) {}
|
||||
|
||||
VulkanCommandBuffer::~VulkanCommandBuffer() {
|
||||
vkDestroyFence(mDevice, mFence, VKALLOC);
|
||||
@@ -129,12 +136,12 @@ void VulkanCommandBuffer::reset() noexcept {
|
||||
mSubmission = mSemaphoreManager->acquire();
|
||||
|
||||
// reset the fence with proper host synchronization
|
||||
mFenceStatus->resetFence(mDevice);
|
||||
mCmdbufState->resetFence(mDevice);
|
||||
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||
// finishes executing the command buffer, the status changes to VK_SUCCESS.
|
||||
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
|
||||
mCmdbufState = std::make_shared<VulkanCmdBufferState>(mFence);
|
||||
}
|
||||
|
||||
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
|
||||
@@ -191,6 +198,12 @@ void VulkanCommandBuffer::begin() noexcept {
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
vkBeginCommandBuffer(mBuffer, &binfo);
|
||||
|
||||
// We need to reset the queries before writing to it. (This must happen within a command buffer,
|
||||
// but not before the queries have occurred).
|
||||
vkCmdResetQueryPool(mBuffer, mQueryPool, mQueryBeginIndex, 2);
|
||||
vkCmdWriteTimestamp(mBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, mQueryPool,
|
||||
mQueryBeginIndex);
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
|
||||
@@ -198,6 +211,9 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
|
||||
popMarker();
|
||||
}
|
||||
|
||||
vkCmdWriteTimestamp(mBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, mQueryPool,
|
||||
mQueryEndIndex);
|
||||
|
||||
vkEndCommandBuffer(mBuffer);
|
||||
|
||||
VkSemaphore submissionSemaphore = mSubmission->getVkSemaphore();
|
||||
@@ -236,7 +252,7 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE VkResult result =
|
||||
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
|
||||
mFenceStatus->setStatus(VK_NOT_READY);
|
||||
mCmdbufState->setStatus(VK_NOT_READY, VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
if (result != VK_SUCCESS) {
|
||||
@@ -248,6 +264,45 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
|
||||
return mSubmission;
|
||||
}
|
||||
|
||||
void VulkanCommandBuffer::setComplete() {
|
||||
if (mCmdbufState->getBufferDuration() != VulkanCmdBufferState::UNKNOWN_BUFFER_DURATION) {
|
||||
return;
|
||||
}
|
||||
// We always query the duration of this buffer on the GPU. This is then used to inform timer
|
||||
// queries from the Filament-side. The reason for doing it this way is because the timestamp is
|
||||
// consistent within a command buffer, but not guarranteed to be consistent outside of a command
|
||||
// buffer. So we can only infer duration by looking at execution duration with respect to a
|
||||
// buffer.
|
||||
struct QueryResult {
|
||||
uint64_t beginTime = 0;
|
||||
uint64_t beginAvailable = 0;
|
||||
uint64_t endTime = 0;
|
||||
uint64_t endAvailable = 0;
|
||||
} result;
|
||||
|
||||
constexpr size_t dataSize = sizeof(result);
|
||||
constexpr VkDeviceSize stride = sizeof(uint64_t) * 2;
|
||||
VkResult const vkresult =
|
||||
vkGetQueryPoolResults(mDevice, mQueryPool, mQueryBeginIndex, 2, dataSize,
|
||||
(void*) &result, stride,
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
|
||||
<< "vkGetQueryPoolResults error=" << static_cast<int32_t>(vkresult);
|
||||
|
||||
uint64_t const begin = result.beginTime;
|
||||
uint64_t const end = result.endTime;
|
||||
uint64_t duration = end - begin;
|
||||
|
||||
assert_invariant(result.beginAvailable != 0 && result.endAvailable != 0);
|
||||
if (begin > end) {
|
||||
FVK_LOGW << "Timestamps are not monotonically increasing. begin=" << begin <<
|
||||
" end=" << end;
|
||||
duration = begin - end;
|
||||
}
|
||||
|
||||
mCmdbufState->setStatus(VK_SUCCESS, duration);
|
||||
}
|
||||
|
||||
CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue,
|
||||
uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected)
|
||||
: mDevice(device),
|
||||
@@ -259,11 +314,24 @@ CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice devi
|
||||
(isProtected ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0u),
|
||||
.queueFamilyIndex = queueFamilyIndex,
|
||||
};
|
||||
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
|
||||
VkResult result = vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateCommandPool failed."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Create a timestamp pool large enough to hold a pair of queries for each command buffer.
|
||||
VkQueryPoolCreateInfo tqpCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.queryType = VK_QUERY_TYPE_TIMESTAMP,
|
||||
.queryCount = CAPACITY *2,
|
||||
};
|
||||
result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mQueryPool);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool failed."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
||||
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(
|
||||
context, device, queue, mPool, semaphoreManager, isProtected));
|
||||
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool,
|
||||
semaphoreManager, isProtected, i * 2, mQueryPool));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +339,7 @@ CommandBufferPool::~CommandBufferPool() {
|
||||
wait();
|
||||
gc();
|
||||
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
||||
vkDestroyQueryPool(mDevice, mQueryPool, VKALLOC);
|
||||
}
|
||||
|
||||
VulkanCommandBuffer& CommandBufferPool::getRecording() {
|
||||
@@ -414,7 +483,7 @@ void VulkanCommands::terminate() {
|
||||
mPool.reset();
|
||||
mProtectedPool.reset();
|
||||
mLastSubmit = {};
|
||||
mLastFenceStatus = {};
|
||||
mLastBufferState = {};
|
||||
}
|
||||
|
||||
VulkanCommandBuffer& VulkanCommands::get() {
|
||||
@@ -443,8 +512,7 @@ bool VulkanCommands::flush() {
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> dependency;
|
||||
bool hasFlushed = false;
|
||||
|
||||
VkFence flushedFence = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
|
||||
std::shared_ptr<VulkanCmdBufferState> flushedBufferState;
|
||||
|
||||
// Note that we've ordered it so that the non-protected commands are followed by the protected
|
||||
// commands. This assumes that the protected commands will be that one doing the rendering into
|
||||
@@ -467,8 +535,7 @@ bool VulkanCommands::flush() {
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
mLastSubmit = {};
|
||||
}
|
||||
flushedFence = pool->getRecording().getVkFence();
|
||||
flushedFenceStatus = pool->getRecording().getFenceStatus();
|
||||
flushedBufferState = pool->getRecording().getState();
|
||||
dependency = pool->flush();
|
||||
hasFlushed = true;
|
||||
}
|
||||
@@ -476,8 +543,11 @@ bool VulkanCommands::flush() {
|
||||
if (hasFlushed) {
|
||||
mInjectedDependency = VK_NULL_HANDLE;
|
||||
mLastSubmit = dependency;
|
||||
mLastFence = flushedFence;
|
||||
mLastFenceStatus = flushedFenceStatus;
|
||||
|
||||
if (mLastBufferState) {
|
||||
mLastBufferState->setNextState(flushedBufferState);
|
||||
}
|
||||
mLastBufferState = flushedBufferState;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace fvkmemory;
|
||||
struct CommandBufferPool;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
class VulkanGroupMarkers {
|
||||
@@ -65,7 +66,8 @@ private:
|
||||
// we're done waiting on the most recent submission of the given command buffer.
|
||||
struct VulkanCommandBuffer {
|
||||
VulkanCommandBuffer(VulkanContext const& mContext, VkDevice device, VkQueue queue,
|
||||
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected);
|
||||
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected,
|
||||
uint32_t timerQueryIndex, VkQueryPool queryPool);
|
||||
|
||||
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
|
||||
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
|
||||
@@ -90,20 +92,14 @@ struct VulkanCommandBuffer {
|
||||
void begin() noexcept;
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> submit();
|
||||
|
||||
inline void setComplete() {
|
||||
mFenceStatus->setStatus(VK_SUCCESS);
|
||||
}
|
||||
void setComplete();
|
||||
|
||||
VkResult getStatus() {
|
||||
return mFenceStatus->getStatus();
|
||||
return mCmdbufState->getFenceStatus();
|
||||
}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
|
||||
return mFenceStatus;
|
||||
}
|
||||
|
||||
VkFence getVkFence() const {
|
||||
return mFence;
|
||||
std::shared_ptr<VulkanCmdBufferState> getState() const {
|
||||
return mCmdbufState;
|
||||
}
|
||||
|
||||
VkCommandBuffer buffer() const {
|
||||
@@ -115,22 +111,31 @@ struct VulkanCommandBuffer {
|
||||
}
|
||||
|
||||
private:
|
||||
VkFence getVkFence() const {
|
||||
return mFence;
|
||||
}
|
||||
|
||||
static uint32_t sAgeCounter;
|
||||
|
||||
VulkanContext const& mContext;
|
||||
uint8_t mMarkerCount;
|
||||
bool const isProtected;
|
||||
VkDevice mDevice;
|
||||
VkQueue mQueue;
|
||||
VkDevice const mDevice;
|
||||
VkQueue const mQueue;
|
||||
VulkanSemaphoreManager* mSemaphoreManager;
|
||||
fvkutils::StaticVector<VkSemaphore, 2> mWaitSemaphores;
|
||||
fvkutils::StaticVector<VkPipelineStageFlags, 2> mWaitSemaphoreStages;
|
||||
VkCommandBuffer mBuffer;
|
||||
VkCommandBuffer const mBuffer;
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> mSubmission;
|
||||
VkFence mFence;
|
||||
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||
VkFence const mFence;
|
||||
std::shared_ptr<VulkanCmdBufferState> mCmdbufState;
|
||||
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
|
||||
uint32_t mAge;
|
||||
uint32_t const mQueryBeginIndex;
|
||||
uint32_t const mQueryEndIndex;
|
||||
VkQueryPool const mQueryPool;
|
||||
|
||||
friend struct CommandBufferPool;
|
||||
};
|
||||
|
||||
struct CommandBufferPool {
|
||||
@@ -174,6 +179,8 @@ private:
|
||||
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
|
||||
int8_t mRecording;
|
||||
|
||||
VkQueryPool mQueryPool;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
#endif
|
||||
@@ -199,7 +206,7 @@ private:
|
||||
//
|
||||
// - Allows off-thread queries of command buffer status.
|
||||
// - Exposes an "updateFences" method that transfers current fence status into atomics.
|
||||
// - Users can examine these atomic variables (see VulkanCmdFence) to determine status.
|
||||
// - Users can examine these atomic variables (see VulkanCmdBufferState) to determine status.
|
||||
// - We do this because vkGetFenceStatus must be called from the rendering thread.
|
||||
//
|
||||
class VulkanCommands {
|
||||
@@ -230,12 +237,8 @@ public:
|
||||
return sem;
|
||||
}
|
||||
|
||||
VkFence getMostRecentFence() {
|
||||
return mLastFence;
|
||||
}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
|
||||
return mLastFenceStatus;
|
||||
std::shared_ptr<VulkanCmdBufferState> getMostRecentBufferState() {
|
||||
return mLastBufferState;
|
||||
}
|
||||
|
||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||
@@ -276,8 +279,7 @@ private:
|
||||
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> mLastSubmit;
|
||||
|
||||
VkFence mLastFence = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
|
||||
std::shared_ptr<VulkanCmdBufferState> mLastBufferState;
|
||||
|
||||
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
|
||||
};
|
||||
|
||||
@@ -241,7 +241,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mQueryManager(mPlatform->getDevice()),
|
||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||
&mDescriptorSetLayoutCache),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
@@ -338,8 +337,6 @@ void VulkanDriver::terminate() {
|
||||
mDefaultRenderTarget = {};
|
||||
mPipelineState = {};
|
||||
|
||||
mQueryManager.terminate();
|
||||
|
||||
mBlitter.terminate();
|
||||
mReadPixels.terminate();
|
||||
|
||||
@@ -860,32 +857,33 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& ta
|
||||
cmdbuf = &mCommands.get();
|
||||
}
|
||||
// Note at this point, the fence has already been constructed via createFenceS, so we just tag
|
||||
// it with appropriate VulkanCmdFence, which is associated with the current, recording command
|
||||
// it with appropriate VulkanCmdBufferState, which is associated with the current, recording command
|
||||
// buffer.
|
||||
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
|
||||
fence->setFence(cmdbuf->getFenceStatus());
|
||||
fence->setCmdBufferState(cmdbuf->getState());
|
||||
mResourceManager.associateHandle(fh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::ImmutableCString&& tag) {
|
||||
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus;
|
||||
std::shared_ptr<VulkanCmdBufferState> cmdbufState;
|
||||
if (mCurrentRenderPass.commandBuffer) {
|
||||
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
|
||||
fence = cmdBuff->getVkFence();
|
||||
fenceStatus = cmdBuff->getFenceStatus();
|
||||
cmdbufState = cmdBuff->getState();
|
||||
fence = cmdbufState->getVkFence();
|
||||
|
||||
// If we're currently recording, flush so that the fence only applies
|
||||
// to commands already issued.
|
||||
mCommands.flush();
|
||||
} else {
|
||||
fence = mCommands.getMostRecentFence();
|
||||
fenceStatus = mCommands.getMostRecentFenceStatus();
|
||||
cmdbufState = mCommands.getMostRecentBufferState();
|
||||
fence = cmdbufState->getVkFence();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(sync->lock);
|
||||
sync->sync = mPlatform->createSync(fence, fenceStatus);
|
||||
sync->sync = mPlatform->createSync(fence, cmdbufState);
|
||||
}
|
||||
|
||||
for (auto& cbData : sync->conversionCallbacks) {
|
||||
@@ -1075,9 +1073,10 @@ Handle<HwSwapChain> VulkanDriver::createSwapChainHeadlessS() noexcept {
|
||||
Handle<HwTimerQuery> VulkanDriver::createTimerQueryS() noexcept {
|
||||
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
|
||||
// before createTimerQueryR is executed.
|
||||
auto query = mQueryManager.getNextQuery(&mResourceManager);
|
||||
auto handle = mResourceManager.allocHandle<VulkanTimerQuery>();
|
||||
auto query = resource_ptr<VulkanTimerQuery>::make(&mResourceManager, handle);
|
||||
query.inc();
|
||||
return Handle<VulkanTimerQuery>(query.id());
|
||||
return handle;
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSetLayout> VulkanDriver::createDescriptorSetLayoutS() noexcept {
|
||||
@@ -1134,7 +1133,6 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
return;
|
||||
}
|
||||
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
|
||||
mQueryManager.clearQuery(vtq);
|
||||
vtq.dec();
|
||||
}
|
||||
|
||||
@@ -1197,7 +1195,6 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> const fh) {
|
||||
FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout) {
|
||||
// Even though this is a synchronous call, the fence handle must be (and stay) valid
|
||||
assert_invariant(fh);
|
||||
|
||||
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
|
||||
|
||||
// we have to take into account that the STL's wait_for() actually works with
|
||||
@@ -1214,14 +1211,14 @@ FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout
|
||||
until = now + nanoseconds(timeout);
|
||||
}
|
||||
|
||||
auto const [cmdfence, canceled] = fence->wait(until);
|
||||
if (!cmdfence || canceled) {
|
||||
auto const [cmdbufState, canceled] = fence->wait(until);
|
||||
if (!cmdbufState || canceled) {
|
||||
return canceled ? FenceStatus::ERROR : FenceStatus::TIMEOUT_EXPIRED;
|
||||
}
|
||||
|
||||
// now we are holding a reference to our VulkanCmdFence, so we know it can't
|
||||
// now we are holding a reference to our VulkanCmdBufferState, so we know it can't
|
||||
// disappear while we wait
|
||||
return cmdfence->wait(mPlatform->getDevice(), timeout, until);
|
||||
return cmdbufState->waitOnFence(mPlatform->getDevice(), timeout, until);
|
||||
}
|
||||
|
||||
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
|
||||
@@ -1518,29 +1515,17 @@ void VulkanDriver::setupExternalImage(void* image) {
|
||||
|
||||
TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
|
||||
if (!vtq->isCompleted()) {
|
||||
if (!vtq->isComplete()) {
|
||||
return TimerQueryResult::NOT_READY;
|
||||
}
|
||||
|
||||
auto results = mQueryManager.getResult(vtq);
|
||||
if (results.beginAvailable == 0 || results.endAvailable == 0) {
|
||||
return TimerQueryResult::NOT_READY;
|
||||
}
|
||||
|
||||
uint64_t const begin = results.beginTime;
|
||||
uint64_t const end = results.endTime;
|
||||
if (begin >= end) {
|
||||
// TODO: queries might have ran on different command buffers.
|
||||
FVK_LOGW << "Timestamps are not monotonically increasing. ";
|
||||
*elapsedTime = 0;
|
||||
return TimerQueryResult::ERROR;
|
||||
}
|
||||
uint64_t result = vtq->getResult();
|
||||
assert_invariant(result != VulkanTimerQuery::UNKNOWN_QUERY_RESULT);
|
||||
|
||||
// NOTE: MoltenVK currently writes system time so the following delta will always be zero.
|
||||
// However there are plans for implementing this properly. See the following GitHub ticket.
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/773
|
||||
float const period = mContext.getPhysicalDeviceLimits().timestampPeriod;
|
||||
*elapsedTime = uint64_t(float(end - begin) * period);
|
||||
*elapsedTime = uint64_t(float(result) * period);
|
||||
return TimerQueryResult::AVAILABLE;
|
||||
}
|
||||
|
||||
@@ -2299,12 +2284,14 @@ void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
|
||||
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
|
||||
mQueryManager.beginQuery(&(mCommands.get()), vtq);
|
||||
auto cmdbuf = &mCommands.get();
|
||||
vtq->setBeginState(cmdbuf->getState());
|
||||
}
|
||||
|
||||
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
auto vtq = resource_ptr<VulkanTimerQuery>::cast(&mResourceManager, tqh);
|
||||
mQueryManager.endQuery(&(mCommands.get()), vtq);
|
||||
auto cmdbuf = &mCommands.get();
|
||||
vtq->setEndState(cmdbuf->getState());
|
||||
}
|
||||
|
||||
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanQueryManager.h"
|
||||
#include "VulkanReadPixels.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanSemaphoreManager.h"
|
||||
@@ -159,7 +158,6 @@ private:
|
||||
VulkanReadPixels mReadPixels;
|
||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||
VulkanQueryManager mQueryManager;
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
|
||||
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanQueryManager.h"
|
||||
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanConstants.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
VulkanQueryManager::VulkanQueryManager(VkDevice device) : mDevice(device) {
|
||||
// Create a timestamp pool large enough to hold a pair of queries for each timer.
|
||||
VkQueryPoolCreateInfo tqpCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.queryType = VK_QUERY_TYPE_TIMESTAMP,
|
||||
.queryCount = (uint32_t) mUsed.size() * 2,
|
||||
};
|
||||
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool failed."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
|
||||
fvkmemory::ResourceManager* resourceManager) {
|
||||
auto unused = ~mUsed;
|
||||
if (unused.empty()) {
|
||||
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported.";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> queryIndices;
|
||||
size_t const firstUnused = unused.firstSetBit();
|
||||
{
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mUsed.set(firstUnused);
|
||||
queryIndices = { firstUnused * 2, firstUnused * 2 + 1 };
|
||||
}
|
||||
return fvkmemory::resource_ptr<VulkanTimerQuery>::construct(resourceManager, queryIndices.first,
|
||||
queryIndices.second);
|
||||
}
|
||||
|
||||
void VulkanQueryManager::clearQuery(fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
uint32_t const startingIndex = query->getStartingQueryIndex();
|
||||
mUsed.unset(startingIndex / 2);
|
||||
}
|
||||
|
||||
void VulkanQueryManager::beginQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStartingQueryIndex();
|
||||
|
||||
auto const cmdbuffer = commands->buffer();
|
||||
vkCmdResetQueryPool(cmdbuffer, mPool, index, 2);
|
||||
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||
|
||||
// We stash this because getResult might come before the query is actually processed.
|
||||
query->setFence(commands->getFenceStatus());
|
||||
}
|
||||
|
||||
void VulkanQueryManager::endQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStoppingQueryIndex();
|
||||
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||
}
|
||||
|
||||
VulkanQueryManager::QueryResult VulkanQueryManager::getResult(
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStartingQueryIndex();
|
||||
QueryResult result;
|
||||
size_t const dataSize = sizeof(result);
|
||||
VkDeviceSize const stride = sizeof(uint64_t) * 2;
|
||||
VkResult vkresult =
|
||||
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) &result, stride,
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
|
||||
<< "vkGetQueryPoolResults error=" << static_cast<int32_t>(vkresult);
|
||||
if (vkresult == VK_NOT_READY) {
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void VulkanQueryManager::terminate() noexcept {
|
||||
vkDestroyQueryPool(mDevice, mPool, VKALLOC);
|
||||
mPool = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
} // filament::backend
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_H
|
||||
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanCommandBuffer;
|
||||
struct VulkanTimerQuery;
|
||||
|
||||
class VulkanQueryManager {
|
||||
public:
|
||||
struct QueryResult {
|
||||
uint64_t beginTime = 0;
|
||||
uint64_t beginAvailable = 0;
|
||||
uint64_t endTime = 0;
|
||||
uint64_t endAvailable = 0;
|
||||
};
|
||||
|
||||
VulkanQueryManager(VkDevice device);
|
||||
~VulkanQueryManager() = default;
|
||||
|
||||
// Not copy-able.
|
||||
VulkanQueryManager(VulkanQueryManager const&) = delete;
|
||||
VulkanQueryManager& operator=(VulkanQueryManager const&) = delete;
|
||||
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery>
|
||||
getNextQuery(fvkmemory::ResourceManager* mResourceManager);
|
||||
void clearQuery(fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
void beginQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
void endQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
QueryResult getResult(fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
VkQueryPool mPool;
|
||||
utils::bitset32 mUsed;
|
||||
utils::Mutex mMutex;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANQUERYMANAGER_H
|
||||
@@ -971,7 +971,7 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
|
||||
}
|
||||
|
||||
Platform::Sync* VulkanPlatform::createSync(VkFence fence,
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept {
|
||||
std::shared_ptr<VulkanCmdBufferState> fenceStatus) noexcept {
|
||||
return new VulkanSync{.fence = fence, .fenceStatus = fenceStatus};
|
||||
}
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ bool VulkanPlatformAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) const
|
||||
VulkanSync& vulkanSync = static_cast<VulkanSync&>(*sync);
|
||||
assert_invariant(vulkanSync.fenceStatus);
|
||||
|
||||
if (vulkanSync.fenceStatus->getStatus() == VK_SUCCESS) {
|
||||
if (vulkanSync.fenceStatus->getFenceStatus() == VK_SUCCESS) {
|
||||
// We've already signaled; return -1 so that operations will proceed
|
||||
// immediately. Also, signal that fence conversion was successful.
|
||||
*fd = -1;
|
||||
|
||||
Reference in New Issue
Block a user