Commit 9d65420c by Jamie Madill Committed by Commit Bot

Vulkan: Move CommandQueue to CommandProcessor.h.

This will facilitate moving this class from the ContextVk to RendererVk. Also cleans up some redundant vk:: prefixes. Bug: b/172704839 Change-Id: I789c9984c0df7fc376e2373530e48afde354d30b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2524546 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 8305ebd4
...@@ -13,16 +13,18 @@ ...@@ -13,16 +13,18 @@
namespace rx namespace rx
{ {
namespace vk
{
namespace namespace
{ {
constexpr size_t kInFlightCommandsLimit = 100u; constexpr size_t kInFlightCommandsLimit = 100u;
constexpr bool kOutputVmaStatsString = false; constexpr bool kOutputVmaStatsString = false;
void InitializeSubmitInfo(VkSubmitInfo *submitInfo, void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
const vk::PrimaryCommandBuffer &commandBuffer, const PrimaryCommandBuffer &commandBuffer,
const std::vector<VkSemaphore> &waitSemaphores, const std::vector<VkSemaphore> &waitSemaphores,
std::vector<VkPipelineStageFlags> *waitSemaphoreStageMasks, std::vector<VkPipelineStageFlags> *waitSemaphoreStageMasks,
const vk::Semaphore *signalSemaphore) const Semaphore *signalSemaphore)
{ {
// Verify that the submitInfo has been zero'd out. // Verify that the submitInfo has been zero'd out.
ASSERT(submitInfo->signalSemaphoreCount == 0); ASSERT(submitInfo->signalSemaphoreCount == 0);
...@@ -51,10 +53,47 @@ void InitializeSubmitInfo(VkSubmitInfo *submitInfo, ...@@ -51,10 +53,47 @@ void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
submitInfo->pSignalSemaphores = nullptr; submitInfo->pSignalSemaphores = nullptr;
} }
} }
} // namespace
namespace vk // TODO(jmadill): De-duplicate. b/172704839
void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
const vk::PrimaryCommandBuffer &commandBuffer,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const vk::Semaphore *signalSemaphore)
{
// Verify that the submitInfo has been zero'd out.
ASSERT(submitInfo->signalSemaphoreCount == 0);
ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
submitInfo->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
submitInfo->pCommandBuffers = commandBuffer.ptr();
submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
submitInfo->pWaitSemaphores = waitSemaphores.data();
submitInfo->pWaitDstStageMask = waitSemaphoreStageMasks.data();
if (signalSemaphore)
{
submitInfo->signalSemaphoreCount = 1;
submitInfo->pSignalSemaphores = signalSemaphore->ptr();
}
}
bool CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> &commands)
{ {
Serial currentSerial;
for (const vk::CommandBatch &commands : commands)
{
if (commands.serial <= currentSerial)
{
return false;
}
currentSerial = commands.serial;
}
return true;
}
} // namespace
void CommandProcessorTask::initTask() void CommandProcessorTask::initTask()
{ {
mTask = CustomTask::Invalid; mTask = CustomTask::Invalid;
...@@ -75,9 +114,9 @@ void CommandProcessorTask::initTask() ...@@ -75,9 +114,9 @@ void CommandProcessorTask::initTask()
// CommandProcessorTask implementation // CommandProcessorTask implementation
void CommandProcessorTask::initProcessCommands(ContextVk *contextVk, void CommandProcessorTask::initProcessCommands(ContextVk *contextVk,
CommandBufferHelper *commandBuffer, CommandBufferHelper *commandBuffer,
vk::RenderPass *renderPass) RenderPass *renderPass)
{ {
mTask = vk::CustomTask::ProcessCommands; mTask = CustomTask::ProcessCommands;
mContextVk = contextVk; mContextVk = contextVk;
mCommandBuffer = commandBuffer; mCommandBuffer = commandBuffer;
mRenderPass = renderPass; mRenderPass = renderPass;
...@@ -149,7 +188,7 @@ void CommandProcessorTask::copyPresentInfo(const VkPresentInfoKHR &other) ...@@ -149,7 +188,7 @@ void CommandProcessorTask::copyPresentInfo(const VkPresentInfoKHR &other)
void CommandProcessorTask::initPresent(egl::ContextPriority priority, VkPresentInfoKHR &presentInfo) void CommandProcessorTask::initPresent(egl::ContextPriority priority, VkPresentInfoKHR &presentInfo)
{ {
mTask = vk::CustomTask::Present; mTask = CustomTask::Present;
mPriority = priority; mPriority = priority;
copyPresentInfo(presentInfo); copyPresentInfo(presentInfo);
} }
...@@ -158,19 +197,19 @@ void CommandProcessorTask::initFinishToSerial(Serial serial) ...@@ -158,19 +197,19 @@ void CommandProcessorTask::initFinishToSerial(Serial serial)
{ {
// Note: sometimes the serial is not valid and that's okay, the finish will early exit in the // Note: sometimes the serial is not valid and that's okay, the finish will early exit in the
// TaskProcessor::finishToSerial // TaskProcessor::finishToSerial
mTask = vk::CustomTask::FinishToSerial; mTask = CustomTask::FinishToSerial;
mSerial = serial; mSerial = serial;
} }
void CommandProcessorTask::initFlushAndQueueSubmit( void CommandProcessorTask::initFlushAndQueueSubmit(
std::vector<VkSemaphore> &&waitSemaphores, std::vector<VkSemaphore> &&waitSemaphores,
std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks, std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks,
const vk::Semaphore *semaphore, const Semaphore *semaphore,
egl::ContextPriority priority, egl::ContextPriority priority,
vk::GarbageList &&currentGarbage, GarbageList &&currentGarbage,
vk::ResourceUseList &&currentResources) ResourceUseList &&currentResources)
{ {
mTask = vk::CustomTask::FlushAndQueueSubmit; mTask = CustomTask::FlushAndQueueSubmit;
mWaitSemaphores = std::move(waitSemaphores); mWaitSemaphores = std::move(waitSemaphores);
mWaitSemaphoreStageMasks = std::move(waitSemaphoreStageMasks); mWaitSemaphoreStageMasks = std::move(waitSemaphoreStageMasks);
mSemaphore = semaphore; mSemaphore = semaphore;
...@@ -181,9 +220,9 @@ void CommandProcessorTask::initFlushAndQueueSubmit( ...@@ -181,9 +220,9 @@ void CommandProcessorTask::initFlushAndQueueSubmit(
void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk, void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk,
egl::ContextPriority priority, egl::ContextPriority priority,
const vk::Fence *fence) const Fence *fence)
{ {
mTask = vk::CustomTask::OneOffQueueSubmit; mTask = CustomTask::OneOffQueueSubmit;
mOneOffCommandBufferVk = oneOffCommandBufferVk; mOneOffCommandBufferVk = oneOffCommandBufferVk;
mOneOffFence = fence; mOneOffFence = fence;
mPriority = priority; mPriority = priority;
...@@ -255,7 +294,7 @@ void TaskProcessor::destroy(VkDevice device) ...@@ -255,7 +294,7 @@ void TaskProcessor::destroy(VkDevice device)
ASSERT(mInFlightCommands.empty() && mGarbageQueue.empty()); ASSERT(mInFlightCommands.empty() && mGarbageQueue.empty());
} }
angle::Result TaskProcessor::init(vk::Context *context, std::thread::id threadId) angle::Result TaskProcessor::init(Context *context, std::thread::id threadId)
{ {
mThreadId = threadId; mThreadId = threadId;
...@@ -265,7 +304,7 @@ angle::Result TaskProcessor::init(vk::Context *context, std::thread::id threadId ...@@ -265,7 +304,7 @@ angle::Result TaskProcessor::init(vk::Context *context, std::thread::id threadId
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TaskProcessor::lockAndCheckCompletedCommands(vk::Context *context) angle::Result TaskProcessor::lockAndCheckCompletedCommands(Context *context)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex); std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex);
...@@ -288,7 +327,7 @@ VkResult TaskProcessor::getLastAndClearPresentResult(VkSwapchainKHR swapchain) ...@@ -288,7 +327,7 @@ VkResult TaskProcessor::getLastAndClearPresentResult(VkSwapchainKHR swapchain)
return result; return result;
} }
angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context) angle::Result TaskProcessor::checkCompletedCommandsNoLock(Context *context)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::checkCompletedCommandsNoLock"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::checkCompletedCommandsNoLock");
VkDevice device = context->getDevice(); VkDevice device = context->getDevice();
...@@ -296,7 +335,7 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context) ...@@ -296,7 +335,7 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context)
int finishedCount = 0; int finishedCount = 0;
for (vk::CommandBatch &batch : mInFlightCommands) for (CommandBatch &batch : mInFlightCommands)
{ {
VkResult result = batch.fence.get().getStatus(device); VkResult result = batch.fence.get().getStatus(device);
if (result == VK_NOT_READY) if (result == VK_NOT_READY)
...@@ -326,10 +365,10 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context) ...@@ -326,10 +365,10 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context)
size_t freeIndex = 0; size_t freeIndex = 0;
for (; freeIndex < mGarbageQueue.size(); ++freeIndex) for (; freeIndex < mGarbageQueue.size(); ++freeIndex)
{ {
vk::GarbageAndSerial &garbageList = mGarbageQueue[freeIndex]; GarbageAndSerial &garbageList = mGarbageQueue[freeIndex];
if (garbageList.getSerial() <= lastCompleted) if (garbageList.getSerial() <= lastCompleted)
{ {
for (vk::GarbageObject &garbage : garbageList.get()) for (GarbageObject &garbage : garbageList.get())
{ {
garbage.destroy(rendererVk); garbage.destroy(rendererVk);
} }
...@@ -349,10 +388,10 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context) ...@@ -349,10 +388,10 @@ angle::Result TaskProcessor::checkCompletedCommandsNoLock(vk::Context *context)
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TaskProcessor::releaseToCommandBatch(vk::Context *context, angle::Result TaskProcessor::releaseToCommandBatch(Context *context,
vk::PrimaryCommandBuffer &&commandBuffer, PrimaryCommandBuffer &&commandBuffer,
vk::CommandPool *commandPool, CommandPool *commandPool,
vk::CommandBatch *batch) CommandBatch *batch)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::releaseToCommandBatch"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::releaseToCommandBatch");
...@@ -373,17 +412,16 @@ angle::Result TaskProcessor::releaseToCommandBatch(vk::Context *context, ...@@ -373,17 +412,16 @@ angle::Result TaskProcessor::releaseToCommandBatch(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TaskProcessor::allocatePrimaryCommandBuffer( angle::Result TaskProcessor::allocatePrimaryCommandBuffer(Context *context,
vk::Context *context, PrimaryCommandBuffer *commandBufferOut)
vk::PrimaryCommandBuffer *commandBufferOut)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::allocatePrimaryCommandBuffer"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::allocatePrimaryCommandBuffer");
return mPrimaryCommandPool.allocate(context, commandBufferOut); return mPrimaryCommandPool.allocate(context, commandBufferOut);
} }
angle::Result TaskProcessor::releasePrimaryCommandBuffer(vk::Context *context, angle::Result TaskProcessor::releasePrimaryCommandBuffer(Context *context,
vk::PrimaryCommandBuffer &&commandBuffer) PrimaryCommandBuffer &&commandBuffer)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::releasePrimaryCommandBuffer"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::releasePrimaryCommandBuffer");
...@@ -391,14 +429,14 @@ angle::Result TaskProcessor::releasePrimaryCommandBuffer(vk::Context *context, ...@@ -391,14 +429,14 @@ angle::Result TaskProcessor::releasePrimaryCommandBuffer(vk::Context *context,
return mPrimaryCommandPool.collect(context, std::move(commandBuffer)); return mPrimaryCommandPool.collect(context, std::move(commandBuffer));
} }
void TaskProcessor::handleDeviceLost(vk::Context *context) void TaskProcessor::handleDeviceLost(Context *context)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::handleDeviceLost"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::handleDeviceLost");
VkDevice device = context->getDevice(); VkDevice device = context->getDevice();
std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex); std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex);
for (vk::CommandBatch &batch : mInFlightCommands) for (CommandBatch &batch : mInFlightCommands)
{ {
// On device loss we need to wait for fence to be signaled before destroying it // On device loss we need to wait for fence to be signaled before destroying it
VkResult status = VkResult status =
...@@ -422,7 +460,7 @@ void TaskProcessor::handleDeviceLost(vk::Context *context) ...@@ -422,7 +460,7 @@ void TaskProcessor::handleDeviceLost(vk::Context *context)
// TODO: https://issuetracker.google.com/issues/170312581 - A more optimal solution might be to do // TODO: https://issuetracker.google.com/issues/170312581 - A more optimal solution might be to do
// the wait in CommandProcessor rather than the worker thread. That would require protecting access // the wait in CommandProcessor rather than the worker thread. That would require protecting access
// to mInFlightCommands // to mInFlightCommands
angle::Result TaskProcessor::finishToSerial(vk::Context *context, Serial serial) angle::Result TaskProcessor::finishToSerial(Context *context, Serial serial)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::finishToSerial"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::finishToSerial");
...@@ -447,7 +485,7 @@ angle::Result TaskProcessor::finishToSerial(vk::Context *context, Serial serial) ...@@ -447,7 +485,7 @@ angle::Result TaskProcessor::finishToSerial(vk::Context *context, Serial serial)
break; break;
} }
} }
const vk::CommandBatch &batch = mInFlightCommands[batchIndex]; const CommandBatch &batch = mInFlightCommands[batchIndex];
// Don't need to hold the lock while waiting for the fence // Don't need to hold the lock while waiting for the fence
inFlightLock.unlock(); inFlightLock.unlock();
...@@ -476,13 +514,13 @@ VkResult TaskProcessor::present(VkQueue queue, const VkPresentInfoKHR &presentIn ...@@ -476,13 +514,13 @@ VkResult TaskProcessor::present(VkQueue queue, const VkPresentInfoKHR &presentIn
return result; return result;
} }
angle::Result TaskProcessor::submitFrame(vk::Context *context, angle::Result TaskProcessor::submitFrame(Context *context,
VkQueue queue, VkQueue queue,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Shared<vk::Fence> &sharedFence, const Shared<Fence> &sharedFence,
vk::GarbageList *currentGarbage, GarbageList *currentGarbage,
vk::CommandPool *commandPool, CommandPool *commandPool,
vk::PrimaryCommandBuffer &&commandBuffer, PrimaryCommandBuffer &&commandBuffer,
const Serial &queueSerial) const Serial &queueSerial)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
...@@ -490,8 +528,8 @@ angle::Result TaskProcessor::submitFrame(vk::Context *context, ...@@ -490,8 +528,8 @@ angle::Result TaskProcessor::submitFrame(vk::Context *context,
VkDevice device = context->getDevice(); VkDevice device = context->getDevice();
vk::DeviceScoped<vk::CommandBatch> scopedBatch(device); DeviceScoped<CommandBatch> scopedBatch(device);
vk::CommandBatch &batch = scopedBatch.get(); CommandBatch &batch = scopedBatch.get();
batch.fence.copy(device, sharedFence); batch.fence.copy(device, sharedFence);
batch.serial = queueSerial; batch.serial = queueSerial;
...@@ -524,9 +562,9 @@ angle::Result TaskProcessor::submitFrame(vk::Context *context, ...@@ -524,9 +562,9 @@ angle::Result TaskProcessor::submitFrame(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
vk::Shared<vk::Fence> TaskProcessor::getLastSubmittedFenceWithLock(VkDevice device) const Shared<Fence> TaskProcessor::getLastSubmittedFenceWithLock(VkDevice device) const
{ {
vk::Shared<vk::Fence> fence; Shared<Fence> fence;
std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex); std::lock_guard<std::mutex> inFlightLock(mInFlightCommandsMutex);
if (!mInFlightCommands.empty()) if (!mInFlightCommands.empty())
...@@ -537,10 +575,10 @@ vk::Shared<vk::Fence> TaskProcessor::getLastSubmittedFenceWithLock(VkDevice devi ...@@ -537,10 +575,10 @@ vk::Shared<vk::Fence> TaskProcessor::getLastSubmittedFenceWithLock(VkDevice devi
return fence; return fence;
} }
angle::Result TaskProcessor::queueSubmit(vk::Context *context, angle::Result TaskProcessor::queueSubmit(Context *context,
VkQueue queue, VkQueue queue,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence *fence) const Fence *fence)
{ {
ASSERT(isValidWorkerThread(context)); ASSERT(isValidWorkerThread(context));
ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::queueSubmit"); ANGLE_TRACE_EVENT0("gpu.angle", "TaskProcessor::queueSubmit");
...@@ -559,7 +597,7 @@ angle::Result TaskProcessor::queueSubmit(vk::Context *context, ...@@ -559,7 +597,7 @@ angle::Result TaskProcessor::queueSubmit(vk::Context *context,
return context->getRenderer()->cleanupGarbage(false); return context->getRenderer()->cleanupGarbage(false);
} }
bool TaskProcessor::isValidWorkerThread(vk::Context *context) const bool TaskProcessor::isValidWorkerThread(Context *context) const
{ {
return (context->getRenderer()->getFeatures().asynchronousCommandProcessing.enabled == false) || return (context->getRenderer()->getFeatures().asynchronousCommandProcessing.enabled == false) ||
std::this_thread::get_id() == mThreadId; std::this_thread::get_id() == mThreadId;
...@@ -583,12 +621,12 @@ void CommandProcessor::handleError(VkResult errorCode, ...@@ -583,12 +621,12 @@ void CommandProcessor::handleError(VkResult errorCode,
} }
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
vk::Error error = {errorCode, file, function, line}; Error error = {errorCode, file, function, line};
mErrors.emplace(error); mErrors.emplace(error);
} }
CommandProcessor::CommandProcessor(RendererVk *renderer) CommandProcessor::CommandProcessor(RendererVk *renderer)
: vk::Context(renderer), : Context(renderer),
mWorkerThreadIdle(false), mWorkerThreadIdle(false),
mCommandProcessorLastSubmittedSerial(mQueueSerialFactory.generate()), mCommandProcessorLastSubmittedSerial(mQueueSerialFactory.generate()),
mCommandProcessorCurrentQueueSerial(mQueueSerialFactory.generate()) mCommandProcessorCurrentQueueSerial(mQueueSerialFactory.generate())
...@@ -602,10 +640,10 @@ CommandProcessor::CommandProcessor(RendererVk *renderer) ...@@ -602,10 +640,10 @@ CommandProcessor::CommandProcessor(RendererVk *renderer)
CommandProcessor::~CommandProcessor() = default; CommandProcessor::~CommandProcessor() = default;
vk::Error CommandProcessor::getAndClearPendingError() Error CommandProcessor::getAndClearPendingError()
{ {
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
vk::Error tmpError({VK_SUCCESS, nullptr, nullptr, 0}); Error tmpError({VK_SUCCESS, nullptr, nullptr, 0});
if (!mErrors.empty()) if (!mErrors.empty())
{ {
tmpError = mErrors.front(); tmpError = mErrors.front();
...@@ -614,15 +652,15 @@ vk::Error CommandProcessor::getAndClearPendingError() ...@@ -614,15 +652,15 @@ vk::Error CommandProcessor::getAndClearPendingError()
return tmpError; return tmpError;
} }
void CommandProcessor::queueCommand(vk::Context *context, vk::CommandProcessorTask *task) void CommandProcessor::queueCommand(Context *context, CommandProcessorTask *task)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queueCommand"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queueCommand");
// Grab the worker mutex so that we put things on the queue in the same order as we give out // Grab the worker mutex so that we put things on the queue in the same order as we give out
// serials. // serials.
std::lock_guard<std::mutex> queueLock(mWorkerMutex); std::lock_guard<std::mutex> queueLock(mWorkerMutex);
if (task->getTaskCommand() == vk::CustomTask::FlushAndQueueSubmit || if (task->getTaskCommand() == CustomTask::FlushAndQueueSubmit ||
task->getTaskCommand() == vk::CustomTask::OneOffQueueSubmit) task->getTaskCommand() == CustomTask::OneOffQueueSubmit)
{ {
std::lock_guard<std::mutex> lock(mCommandProcessorQueueSerialMutex); std::lock_guard<std::mutex> lock(mCommandProcessorQueueSerialMutex);
// Flush submits work, so give it the current serial and generate a new one. // Flush submits work, so give it the current serial and generate a new one.
...@@ -652,7 +690,7 @@ void CommandProcessor::queueCommand(vk::Context *context, vk::CommandProcessorTa ...@@ -652,7 +690,7 @@ void CommandProcessor::queueCommand(vk::Context *context, vk::CommandProcessorTa
} }
} }
angle::Result CommandProcessor::initTaskProcessor(vk::Context *context) angle::Result CommandProcessor::initTaskProcessor(Context *context)
{ {
// Initialization prior to work thread loop // Initialization prior to work thread loop
ANGLE_TRY(mTaskProcessor.init(context, std::this_thread::get_id())); ANGLE_TRY(mTaskProcessor.init(context, std::this_thread::get_id()));
...@@ -706,12 +744,12 @@ angle::Result CommandProcessor::processTasksImpl(bool *exitThread) ...@@ -706,12 +744,12 @@ angle::Result CommandProcessor::processTasksImpl(bool *exitThread)
mWorkAvailableCondition.wait(lock, [this] { return !mTasks.empty(); }); mWorkAvailableCondition.wait(lock, [this] { return !mTasks.empty(); });
} }
mWorkerThreadIdle = false; mWorkerThreadIdle = false;
vk::CommandProcessorTask task(std::move(mTasks.front())); CommandProcessorTask task(std::move(mTasks.front()));
mTasks.pop(); mTasks.pop();
lock.unlock(); lock.unlock();
ANGLE_TRY(processTask(this, &task)); ANGLE_TRY(processTask(this, &task));
if (task.getTaskCommand() == vk::CustomTask::Exit) if (task.getTaskCommand() == CustomTask::Exit)
{ {
*exitThread = true; *exitThread = true;
...@@ -726,11 +764,11 @@ angle::Result CommandProcessor::processTasksImpl(bool *exitThread) ...@@ -726,11 +764,11 @@ angle::Result CommandProcessor::processTasksImpl(bool *exitThread)
return angle::Result::Stop; return angle::Result::Stop;
} }
angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandProcessorTask *task) angle::Result CommandProcessor::processTask(Context *context, CommandProcessorTask *task)
{ {
switch (task->getTaskCommand()) switch (task->getTaskCommand())
{ {
case vk::CustomTask::Exit: case CustomTask::Exit:
{ {
ANGLE_TRY(mTaskProcessor.finishToSerial(context, Serial::Infinite())); ANGLE_TRY(mTaskProcessor.finishToSerial(context, Serial::Infinite()));
// Shutting down so cleanup // Shutting down so cleanup
...@@ -739,7 +777,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -739,7 +777,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
mPrimaryCommandBuffer.destroy(mRenderer->getDevice()); mPrimaryCommandBuffer.destroy(mRenderer->getDevice());
break; break;
} }
case vk::CustomTask::FlushAndQueueSubmit: case CustomTask::FlushAndQueueSubmit:
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "processTask::FlushAndQueueSubmit"); ANGLE_TRACE_EVENT0("gpu.angle", "processTask::FlushAndQueueSubmit");
// End command buffer // End command buffer
...@@ -752,7 +790,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -752,7 +790,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
// 2. Get shared submit fence. It's possible there are other users of this fence that // 2. Get shared submit fence. It's possible there are other users of this fence that
// must wait for the work to be submitted before waiting on the fence. Reset the fence // must wait for the work to be submitted before waiting on the fence. Reset the fence
// immediately so we are sure to get a fresh one next time. // immediately so we are sure to get a fresh one next time.
vk::Shared<vk::Fence> fence; Shared<Fence> fence;
ANGLE_TRY(mRenderer->getNextSubmitFence(&fence, true)); ANGLE_TRY(mRenderer->getNextSubmitFence(&fence, true));
// 3. Call submitFrame() // 3. Call submitFrame()
...@@ -775,7 +813,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -775,7 +813,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
ASSERT(task->getGarbage().empty()); ASSERT(task->getGarbage().empty());
break; break;
} }
case vk::CustomTask::OneOffQueueSubmit: case CustomTask::OneOffQueueSubmit:
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit"); ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit");
VkSubmitInfo submitInfo = {}; VkSubmitInfo submitInfo = {};
...@@ -794,12 +832,12 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -794,12 +832,12 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
ANGLE_TRY(mTaskProcessor.lockAndCheckCompletedCommands(context)); ANGLE_TRY(mTaskProcessor.lockAndCheckCompletedCommands(context));
break; break;
} }
case vk::CustomTask::FinishToSerial: case CustomTask::FinishToSerial:
{ {
ANGLE_TRY(mTaskProcessor.finishToSerial(context, task->getQueueSerial())); ANGLE_TRY(mTaskProcessor.finishToSerial(context, task->getQueueSerial()));
break; break;
} }
case vk::CustomTask::Present: case CustomTask::Present:
{ {
VkResult result = mTaskProcessor.present(getRenderer()->getVkQueue(task->getPriority()), VkResult result = mTaskProcessor.present(getRenderer()->getVkQueue(task->getPriority()),
task->getPresentInfo()); task->getPresentInfo());
...@@ -817,7 +855,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -817,7 +855,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
} }
break; break;
} }
case vk::CustomTask::ProcessCommands: case CustomTask::ProcessCommands:
{ {
ASSERT(!task->getCommandBuffer()->empty()); ASSERT(!task->getCommandBuffer()->empty());
ANGLE_TRY(task->getCommandBuffer()->flushToPrimary( ANGLE_TRY(task->getCommandBuffer()->flushToPrimary(
...@@ -826,7 +864,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -826,7 +864,7 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
task->getCommandBuffer()->releaseToContextQueue(task->getContextVk()); task->getCommandBuffer()->releaseToContextQueue(task->getContextVk());
break; break;
} }
case vk::CustomTask::CheckCompletedCommands: case CustomTask::CheckCompletedCommands:
{ {
ANGLE_TRY(mTaskProcessor.lockAndCheckCompletedCommands(this)); ANGLE_TRY(mTaskProcessor.lockAndCheckCompletedCommands(this));
break; break;
...@@ -839,14 +877,14 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro ...@@ -839,14 +877,14 @@ angle::Result CommandProcessor::processTask(vk::Context *context, vk::CommandPro
return angle::Result::Continue; return angle::Result::Continue;
} }
void CommandProcessor::checkCompletedCommands(vk::Context *context) void CommandProcessor::checkCompletedCommands(Context *context)
{ {
vk::CommandProcessorTask checkCompletedTask; CommandProcessorTask checkCompletedTask;
checkCompletedTask.initTask(vk::CustomTask::CheckCompletedCommands); checkCompletedTask.initTask(CustomTask::CheckCompletedCommands);
queueCommand(this, &checkCompletedTask); queueCommand(this, &checkCompletedTask);
} }
void CommandProcessor::waitForWorkComplete(vk::Context *context) void CommandProcessor::waitForWorkComplete(Context *context)
{ {
ASSERT(getRenderer()->getFeatures().asynchronousCommandProcessing.enabled); ASSERT(getRenderer()->getFeatures().asynchronousCommandProcessing.enabled);
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForWorkComplete"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForWorkComplete");
...@@ -862,7 +900,7 @@ void CommandProcessor::waitForWorkComplete(vk::Context *context) ...@@ -862,7 +900,7 @@ void CommandProcessor::waitForWorkComplete(vk::Context *context)
// Sync any errors to the context // Sync any errors to the context
while (hasPendingError()) while (hasPendingError())
{ {
vk::Error workerError = getAndClearPendingError(); Error workerError = getAndClearPendingError();
if (workerError.mErrorCode != VK_SUCCESS) if (workerError.mErrorCode != VK_SUCCESS)
{ {
context->handleError(workerError.mErrorCode, workerError.mFile, workerError.mFunction, context->handleError(workerError.mErrorCode, workerError.mFile, workerError.mFunction,
...@@ -875,8 +913,8 @@ void CommandProcessor::waitForWorkComplete(vk::Context *context) ...@@ -875,8 +913,8 @@ void CommandProcessor::waitForWorkComplete(vk::Context *context)
// someplace to send errors. // someplace to send errors.
void CommandProcessor::shutdown(std::thread *commandProcessorThread) void CommandProcessor::shutdown(std::thread *commandProcessorThread)
{ {
vk::CommandProcessorTask endTask; CommandProcessorTask endTask;
endTask.initTask(vk::CustomTask::Exit); endTask.initTask(CustomTask::Exit);
queueCommand(this, &endTask); queueCommand(this, &endTask);
if (this->getRenderer()->getFeatures().asynchronousCommandProcessing.enabled) if (this->getRenderer()->getFeatures().asynchronousCommandProcessing.enabled)
{ {
...@@ -890,7 +928,7 @@ void CommandProcessor::shutdown(std::thread *commandProcessorThread) ...@@ -890,7 +928,7 @@ void CommandProcessor::shutdown(std::thread *commandProcessorThread)
// Return the fence for the last submit. This may mean waiting on the worker to process tasks to // Return the fence for the last submit. This may mean waiting on the worker to process tasks to
// actually get to the last submit // actually get to the last submit
vk::Shared<vk::Fence> CommandProcessor::getLastSubmittedFence(const vk::Context *context) const Shared<Fence> CommandProcessor::getLastSubmittedFence(const Context *context) const
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::getLastSubmittedFence"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::getLastSubmittedFence");
std::unique_lock<std::mutex> lock(mWorkerMutex); std::unique_lock<std::mutex> lock(mWorkerMutex);
...@@ -916,10 +954,10 @@ Serial CommandProcessor::getCurrentQueueSerial() ...@@ -916,10 +954,10 @@ Serial CommandProcessor::getCurrentQueueSerial()
} }
// Wait until all commands up to and including serial have been processed // Wait until all commands up to and including serial have been processed
void CommandProcessor::finishToSerial(vk::Context *context, Serial serial) void CommandProcessor::finishToSerial(Context *context, Serial serial)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishToSerial"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishToSerial");
vk::CommandProcessorTask finishToSerial; CommandProcessorTask finishToSerial;
finishToSerial.initFinishToSerial(serial); finishToSerial.initFinishToSerial(serial);
queueCommand(context, &finishToSerial); queueCommand(context, &finishToSerial);
...@@ -944,13 +982,355 @@ void CommandProcessor::handleDeviceLost() ...@@ -944,13 +982,355 @@ void CommandProcessor::handleDeviceLost()
mTaskProcessor.handleDeviceLost(this); mTaskProcessor.handleDeviceLost(this);
} }
void CommandProcessor::finishAllWork(vk::Context *context) void CommandProcessor::finishAllWork(Context *context)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishAllWork"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishAllWork");
// Wait for GPU work to finish // Wait for GPU work to finish
finishToSerial(context, Serial::Infinite()); finishToSerial(context, Serial::Infinite());
} }
} // namespace vk // CommandQueue implementation.
CommandQueue::CommandQueue() = default;
CommandQueue::~CommandQueue() = default;
void CommandQueue::destroy(VkDevice device)
{
mPrimaryCommands.destroy(device);
mPrimaryCommandPool.destroy(device);
ASSERT(mInFlightCommands.empty() && mGarbageQueue.empty());
}
angle::Result CommandQueue::init(Context *context)
{
RendererVk *renderer = context->getRenderer();
// Initialize the command pool now that we know the queue family index.
uint32_t queueFamilyIndex = renderer->getQueueFamilyIndex();
ANGLE_TRY(mPrimaryCommandPool.init(context, queueFamilyIndex));
return angle::Result::Continue;
}
angle::Result CommandQueue::checkCompletedCommands(Context *context)
{
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::checkCompletedCommandsNoLock");
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
int finishedCount = 0;
for (CommandBatch &batch : mInFlightCommands)
{
VkResult result = batch.fence.get().getStatus(device);
if (result == VK_NOT_READY)
{
break;
}
ANGLE_VK_TRY(context, result);
++finishedCount;
}
if (finishedCount == 0)
{
return angle::Result::Continue;
}
return retireFinishedCommands(context, finishedCount);
}
angle::Result CommandQueue::retireFinishedCommands(Context *context, size_t finishedCount)
{
ASSERT(finishedCount > 0);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
for (size_t commandIndex = 0; commandIndex < finishedCount; ++commandIndex)
{
CommandBatch &batch = mInFlightCommands[commandIndex];
renderer->onCompletedSerial(batch.serial);
renderer->resetSharedFence(&batch.fence);
ANGLE_TRACE_EVENT0("gpu.angle", "command buffer recycling");
batch.commandPool.destroy(device);
ANGLE_TRY(releasePrimaryCommandBuffer(context, std::move(batch.primaryCommands)));
}
if (finishedCount > 0)
{
auto beginIter = mInFlightCommands.begin();
mInFlightCommands.erase(beginIter, beginIter + finishedCount);
}
Serial lastCompleted = renderer->getLastCompletedQueueSerial();
size_t freeIndex = 0;
for (; freeIndex < mGarbageQueue.size(); ++freeIndex)
{
GarbageAndSerial &garbageList = mGarbageQueue[freeIndex];
if (garbageList.getSerial() < lastCompleted)
{
for (GarbageObject &garbage : garbageList.get())
{
garbage.destroy(renderer);
}
}
else
{
break;
}
}
// Remove the entries from the garbage list - they should be ready to go.
if (freeIndex > 0)
{
mGarbageQueue.erase(mGarbageQueue.begin(), mGarbageQueue.begin() + freeIndex);
}
return angle::Result::Continue;
}
angle::Result CommandQueue::releaseToCommandBatch(Context *context,
PrimaryCommandBuffer &&commandBuffer,
CommandPool *commandPool,
CommandBatch *batch)
{
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
batch->primaryCommands = std::move(commandBuffer);
if (commandPool->valid())
{
batch->commandPool = std::move(*commandPool);
// Recreate CommandPool
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
poolInfo.queueFamilyIndex = renderer->getQueueFamilyIndex();
ANGLE_VK_TRY(context, commandPool->init(device, poolInfo));
}
return angle::Result::Continue;
}
void CommandQueue::clearAllGarbage(RendererVk *renderer)
{
for (GarbageAndSerial &garbageList : mGarbageQueue)
{
for (GarbageObject &garbage : garbageList.get())
{
garbage.destroy(renderer);
}
}
mGarbageQueue.clear();
}
angle::Result CommandQueue::allocatePrimaryCommandBuffer(Context *context,
PrimaryCommandBuffer *commandBufferOut)
{
return mPrimaryCommandPool.allocate(context, commandBufferOut);
}
angle::Result CommandQueue::releasePrimaryCommandBuffer(Context *context,
PrimaryCommandBuffer &&commandBuffer)
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
ASSERT(mPrimaryCommandPool.valid());
ANGLE_TRY(mPrimaryCommandPool.collect(context, std::move(commandBuffer)));
return angle::Result::Continue;
}
void CommandQueue::handleDeviceLost(RendererVk *renderer)
{
VkDevice device = renderer->getDevice();
for (CommandBatch &batch : mInFlightCommands)
{
// On device loss we need to wait for fence to be signaled before destroying it
VkResult status = batch.fence.get().wait(device, renderer->getMaxFenceWaitTimeNs());
// If the wait times out, it is probably not possible to recover from lost device
ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
// On device lost, here simply destroy the CommandBuffer, it will fully cleared later
// by CommandPool::destroy
batch.primaryCommands.destroy(device);
batch.commandPool.destroy(device);
batch.fence.reset(device);
}
mInFlightCommands.clear();
}
bool CommandQueue::hasInFlightCommands() const
{
return !mInFlightCommands.empty();
}
angle::Result CommandQueue::finishToSerial(Context *context, Serial finishSerial, uint64_t timeout)
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
if (mInFlightCommands.empty())
{
return angle::Result::Continue;
}
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::finishToSerial");
// Find the serial in the the list. The serials should be in order.
ASSERT(CommandsHaveValidOrdering(mInFlightCommands));
size_t finishedCount = 0;
while (finishedCount < mInFlightCommands.size() &&
mInFlightCommands[finishedCount].serial < finishSerial)
{
finishedCount++;
}
// This heuristic attempts to increase chances of success for shared resource scenarios.
// Ultimately as long as fences are managed by ContextVk there are edge case bugs here.
// TODO: http://anglebug.com/5217: fix this bug by moving it submit into RendererVk.
if (finishedCount < mInFlightCommands.size())
{
finishedCount++;
}
if (finishedCount == 0)
{
return angle::Result::Continue;
}
const CommandBatch &batch = mInFlightCommands[finishedCount - 1];
// Wait for it finish
VkDevice device = context->getDevice();
VkResult status = batch.fence.get().wait(device, timeout);
ANGLE_VK_TRY(context, status);
// Clean up finished batches.
return retireFinishedCommands(context, finishedCount);
}
angle::Result CommandQueue::submitFrame(
Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *signalSemaphore,
const Shared<Fence> &sharedFence,
ResourceUseList *resourceList,
GarbageList *currentGarbage,
CommandPool *commandPool)
{
// Start an empty primary buffer if we have an empty submit.
if (!hasPrimaryCommands())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_VK_TRY(context, mPrimaryCommands.end());
VkSubmitInfo submitInfo = {};
InitializeSubmitInfo(&submitInfo, mPrimaryCommands, waitSemaphores, waitSemaphoreStageMasks,
signalSemaphore);
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitFrame");
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
DeviceScoped<CommandBatch> scopedBatch(device);
CommandBatch &batch = scopedBatch.get();
batch.fence.copy(device, sharedFence);
ANGLE_TRY(renderer->queueSubmit(context, priority, submitInfo, resourceList, &batch.fence.get(),
&batch.serial));
if (!currentGarbage->empty())
{
mGarbageQueue.emplace_back(std::move(*currentGarbage), batch.serial);
}
// Store the primary CommandBuffer and command pool used for secondary CommandBuffers
// in the in-flight list.
ANGLE_TRY(releaseToCommandBatch(context, std::move(mPrimaryCommands), commandPool, &batch));
mInFlightCommands.emplace_back(scopedBatch.release());
ANGLE_TRY(checkCompletedCommands(context));
// CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
// off-screen scenarios.
while (mInFlightCommands.size() > kInFlightCommandsLimit)
{
ANGLE_TRY(finishToSerial(context, mInFlightCommands[0].serial,
renderer->getMaxFenceWaitTimeNs()));
}
return angle::Result::Continue;
}
Shared<Fence> CommandQueue::getLastSubmittedFence(const Context *context) const
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
Shared<Fence> fence;
if (!mInFlightCommands.empty())
{
fence.copy(context->getDevice(), mInFlightCommands.back().fence);
}
return fence;
}
angle::Result CommandQueue::startPrimaryCommandBuffer(Context *context)
{
ASSERT(!mPrimaryCommands.valid());
ANGLE_TRY(allocatePrimaryCommandBuffer(context, &mPrimaryCommands));
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr;
ANGLE_VK_TRY(context, mPrimaryCommands.begin(beginInfo));
return angle::Result::Continue;
}
angle::Result CommandQueue::flushOutsideRPCommands(Context *context,
CommandBufferHelper *outsideRPCommands)
{
if (!mPrimaryCommands.valid())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_TRY(outsideRPCommands->flushToPrimary(context->getRenderer()->getFeatures(),
&mPrimaryCommands, nullptr));
return angle::Result::Continue;
}
angle::Result CommandQueue::flushRenderPassCommands(Context *context,
const RenderPass &renderPass,
CommandBufferHelper *renderPassCommands)
{
if (!mPrimaryCommands.valid())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_TRY(renderPassCommands->flushToPrimary(context->getRenderer()->getFeatures(),
&mPrimaryCommands, &renderPass));
return angle::Result::Continue;
}
} // namespace vk
} // namespace rx } // namespace rx
...@@ -65,7 +65,7 @@ class CommandProcessorTask ...@@ -65,7 +65,7 @@ class CommandProcessorTask
void initProcessCommands(ContextVk *contextVk, void initProcessCommands(ContextVk *contextVk,
CommandBufferHelper *commandBuffer, CommandBufferHelper *commandBuffer,
vk::RenderPass *renderPass); RenderPass *renderPass);
void initPresent(egl::ContextPriority priority, VkPresentInfoKHR &presentInfo); void initPresent(egl::ContextPriority priority, VkPresentInfoKHR &presentInfo);
...@@ -73,14 +73,14 @@ class CommandProcessorTask ...@@ -73,14 +73,14 @@ class CommandProcessorTask
void initFlushAndQueueSubmit(std::vector<VkSemaphore> &&waitSemaphores, void initFlushAndQueueSubmit(std::vector<VkSemaphore> &&waitSemaphores,
std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks, std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks,
const vk::Semaphore *semaphore, const Semaphore *semaphore,
egl::ContextPriority priority, egl::ContextPriority priority,
vk::GarbageList &&currentGarbage, GarbageList &&currentGarbage,
vk::ResourceUseList &&currentResources); ResourceUseList &&currentResources);
void initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk, void initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk,
egl::ContextPriority priority, egl::ContextPriority priority,
const vk::Fence *fence); const Fence *fence);
CommandProcessorTask &operator=(CommandProcessorTask &&rhs); CommandProcessorTask &operator=(CommandProcessorTask &&rhs);
...@@ -91,20 +91,20 @@ class CommandProcessorTask ...@@ -91,20 +91,20 @@ class CommandProcessorTask
void setQueueSerial(Serial serial) { mSerial = serial; } void setQueueSerial(Serial serial) { mSerial = serial; }
Serial getQueueSerial() const { return mSerial; } Serial getQueueSerial() const { return mSerial; }
vk::ResourceUseList &getResourceUseList() { return mResourceUseList; } ResourceUseList &getResourceUseList() { return mResourceUseList; }
vk::CustomTask getTaskCommand() { return mTask; } CustomTask getTaskCommand() { return mTask; }
std::vector<VkSemaphore> &getWaitSemaphores() { return mWaitSemaphores; } std::vector<VkSemaphore> &getWaitSemaphores() { return mWaitSemaphores; }
std::vector<VkPipelineStageFlags> &getWaitSemaphoreStageMasks() std::vector<VkPipelineStageFlags> &getWaitSemaphoreStageMasks()
{ {
return mWaitSemaphoreStageMasks; return mWaitSemaphoreStageMasks;
} }
const vk::Semaphore *getSemaphore() { return mSemaphore; } const Semaphore *getSemaphore() { return mSemaphore; }
vk::GarbageList &getGarbage() { return mGarbage; } GarbageList &getGarbage() { return mGarbage; }
egl::ContextPriority getPriority() const { return mPriority; } egl::ContextPriority getPriority() const { return mPriority; }
const VkCommandBuffer &getOneOffCommandBufferVk() const { return mOneOffCommandBufferVk; } const VkCommandBuffer &getOneOffCommandBufferVk() const { return mOneOffCommandBufferVk; }
const vk::Fence *getOneOffFence() { return mOneOffFence; } const Fence *getOneOffFence() { return mOneOffFence; }
const VkPresentInfoKHR &getPresentInfo() const { return mPresentInfo; } const VkPresentInfoKHR &getPresentInfo() const { return mPresentInfo; }
vk::RenderPass *getRenderPass() const { return mRenderPass; } RenderPass *getRenderPass() const { return mRenderPass; }
CommandBufferHelper *getCommandBuffer() const { return mCommandBuffer; } CommandBufferHelper *getCommandBuffer() const { return mCommandBuffer; }
ContextVk *getContextVk() const { return mContextVk; } ContextVk *getContextVk() const { return mContextVk; }
...@@ -115,15 +115,15 @@ class CommandProcessorTask ...@@ -115,15 +115,15 @@ class CommandProcessorTask
// ProcessCommands // ProcessCommands
ContextVk *mContextVk; ContextVk *mContextVk;
vk::RenderPass *mRenderPass; RenderPass *mRenderPass;
CommandBufferHelper *mCommandBuffer; CommandBufferHelper *mCommandBuffer;
// Flush data // Flush data
std::vector<VkSemaphore> mWaitSemaphores; std::vector<VkSemaphore> mWaitSemaphores;
std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks; std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks;
const vk::Semaphore *mSemaphore; const Semaphore *mSemaphore;
vk::GarbageList mGarbage; GarbageList mGarbage;
vk::ResourceUseList mResourceUseList; ResourceUseList mResourceUseList;
// FinishToSerial & Flush command data // FinishToSerial & Flush command data
Serial mSerial; Serial mSerial;
...@@ -140,7 +140,7 @@ class CommandProcessorTask ...@@ -140,7 +140,7 @@ class CommandProcessorTask
// Used by OneOffQueueSubmit // Used by OneOffQueueSubmit
VkCommandBuffer mOneOffCommandBufferVk; VkCommandBuffer mOneOffCommandBufferVk;
const vk::Fence *mOneOffFence; const Fence *mOneOffFence;
// Flush, Present & QueueWaitIdle data // Flush, Present & QueueWaitIdle data
egl::ContextPriority mPriority; egl::ContextPriority mPriority;
...@@ -155,73 +155,135 @@ struct CommandBatch final : angle::NonCopyable ...@@ -155,73 +155,135 @@ struct CommandBatch final : angle::NonCopyable
void destroy(VkDevice device); void destroy(VkDevice device);
vk::PrimaryCommandBuffer primaryCommands; PrimaryCommandBuffer primaryCommands;
// commandPool is for secondary CommandBuffer allocation // commandPool is for secondary CommandBuffer allocation
vk::CommandPool commandPool; CommandPool commandPool;
vk::Shared<vk::Fence> fence; Shared<Fence> fence;
Serial serial; Serial serial;
}; };
class CommandQueue final : angle::NonCopyable
{
public:
CommandQueue();
~CommandQueue();
angle::Result init(Context *context);
void destroy(VkDevice device);
void handleDeviceLost(RendererVk *renderer);
bool hasInFlightCommands() const;
void clearAllGarbage(RendererVk *renderer);
angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout);
angle::Result submitFrame(Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *signalSemaphore,
const Shared<Fence> &sharedFence,
ResourceUseList *resourceList,
GarbageList *currentGarbage,
CommandPool *commandPool);
Shared<Fence> getLastSubmittedFence(const Context *context) const;
// Check to see which batches have finished completion (forward progress for
// mLastCompletedQueueSerial, for example for when the application busy waits on a query
// result). It would be nice if we didn't have to expose this for QuerygetResult.
angle::Result checkCompletedCommands(Context *context);
angle::Result flushOutsideRPCommands(Context *context, CommandBufferHelper *outsideRPCommands);
angle::Result flushRenderPassCommands(Context *context,
const RenderPass &renderPass,
CommandBufferHelper *renderPassCommands);
// TODO(jmadill): Remove this. b/172678125
bool hasPrimaryCommands() const { return mPrimaryCommands.valid(); }
private:
angle::Result releaseToCommandBatch(Context *context,
PrimaryCommandBuffer &&commandBuffer,
CommandPool *commandPool,
CommandBatch *batch);
angle::Result retireFinishedCommands(Context *context, size_t finishedCount);
angle::Result startPrimaryCommandBuffer(Context *context);
angle::Result allocatePrimaryCommandBuffer(Context *context,
PrimaryCommandBuffer *commandBufferOut);
angle::Result releasePrimaryCommandBuffer(Context *context,
PrimaryCommandBuffer &&commandBuffer);
GarbageQueue mGarbageQueue;
std::vector<CommandBatch> mInFlightCommands;
// Keeps a free list of reusable primary command buffers.
PrimaryCommandBuffer mPrimaryCommands;
PersistentCommandPool mPrimaryCommandPool;
};
class TaskProcessor : angle::NonCopyable class TaskProcessor : angle::NonCopyable
{ {
public: public:
TaskProcessor(); TaskProcessor();
~TaskProcessor(); ~TaskProcessor();
angle::Result init(vk::Context *context, std::thread::id threadId); angle::Result init(Context *context, std::thread::id threadId);
void destroy(VkDevice device); void destroy(VkDevice device);
angle::Result allocatePrimaryCommandBuffer(vk::Context *context, angle::Result allocatePrimaryCommandBuffer(Context *context,
vk::PrimaryCommandBuffer *commandBufferOut); PrimaryCommandBuffer *commandBufferOut);
angle::Result releasePrimaryCommandBuffer(vk::Context *context, angle::Result releasePrimaryCommandBuffer(Context *context,
vk::PrimaryCommandBuffer &&commandBuffer); PrimaryCommandBuffer &&commandBuffer);
angle::Result finishToSerial(vk::Context *context, Serial serial); angle::Result finishToSerial(Context *context, Serial serial);
VkResult present(VkQueue queue, const VkPresentInfoKHR &presentInfo); VkResult present(VkQueue queue, const VkPresentInfoKHR &presentInfo);
angle::Result submitFrame(vk::Context *context, angle::Result submitFrame(Context *context,
VkQueue queue, VkQueue queue,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Shared<vk::Fence> &sharedFence, const Shared<Fence> &sharedFence,
vk::GarbageList *currentGarbage, GarbageList *currentGarbage,
vk::CommandPool *commandPool, CommandPool *commandPool,
vk::PrimaryCommandBuffer &&commandBuffer, PrimaryCommandBuffer &&commandBuffer,
const Serial &queueSerial); const Serial &queueSerial);
angle::Result queueSubmit(vk::Context *context, angle::Result queueSubmit(Context *context,
VkQueue queue, VkQueue queue,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence *fence); const Fence *fence);
vk::Shared<vk::Fence> getLastSubmittedFenceWithLock(VkDevice device) const; Shared<Fence> getLastSubmittedFenceWithLock(VkDevice device) const;
void handleDeviceLost(vk::Context *context); void handleDeviceLost(Context *context);
// Called by CommandProcessor to process any completed work // Called by CommandProcessor to process any completed work
angle::Result lockAndCheckCompletedCommands(vk::Context *context); angle::Result lockAndCheckCompletedCommands(Context *context);
VkResult getLastAndClearPresentResult(VkSwapchainKHR swapchain); VkResult getLastAndClearPresentResult(VkSwapchainKHR swapchain);
private: private:
bool isValidWorkerThread(vk::Context *context) const; bool isValidWorkerThread(Context *context) const;
angle::Result releaseToCommandBatch(vk::Context *context, angle::Result releaseToCommandBatch(Context *context,
vk::PrimaryCommandBuffer &&commandBuffer, PrimaryCommandBuffer &&commandBuffer,
vk::CommandPool *commandPool, CommandPool *commandPool,
vk::CommandBatch *batch); CommandBatch *batch);
// Check to see which batches have finished completion (forward progress for // Check to see which batches have finished completion (forward progress for
// mLastCompletedQueueSerial, for example for when the application busy waits on a query // mLastCompletedQueueSerial, for example for when the application busy waits on a query
// result). It would be nice if we didn't have to expose this for QueryVk::getResult. // result). It would be nice if we didn't have to expose this for QuerygetResult.
angle::Result checkCompletedCommandsNoLock(vk::Context *context); angle::Result checkCompletedCommandsNoLock(Context *context);
vk::GarbageQueue mGarbageQueue; GarbageQueue mGarbageQueue;
mutable std::mutex mInFlightCommandsMutex; mutable std::mutex mInFlightCommandsMutex;
std::vector<vk::CommandBatch> mInFlightCommands; std::vector<CommandBatch> mInFlightCommands;
// Keeps a free list of reusable primary command buffers. // Keeps a free list of reusable primary command buffers.
vk::PersistentCommandPool mPrimaryCommandPool; PersistentCommandPool mPrimaryCommandPool;
std::thread::id mThreadId; std::thread::id mThreadId;
// Track present info // Track present info
...@@ -230,13 +292,13 @@ class TaskProcessor : angle::NonCopyable ...@@ -230,13 +292,13 @@ class TaskProcessor : angle::NonCopyable
std::map<VkSwapchainKHR, VkResult> mSwapchainStatus; std::map<VkSwapchainKHR, VkResult> mSwapchainStatus;
}; };
class CommandProcessor : public vk::Context class CommandProcessor : public Context
{ {
public: public:
CommandProcessor(RendererVk *renderer); CommandProcessor(RendererVk *renderer);
~CommandProcessor() override; ~CommandProcessor() override;
angle::Result initTaskProcessor(vk::Context *context); angle::Result initTaskProcessor(Context *context);
void handleError(VkResult result, void handleError(VkResult result,
const char *file, const char *file,
...@@ -244,24 +306,24 @@ class CommandProcessor : public vk::Context ...@@ -244,24 +306,24 @@ class CommandProcessor : public vk::Context
unsigned int line) override; unsigned int line) override;
// Entry point for command processor thread, calls processTasksImpl to do the // Entry point for command processor thread, calls processTasksImpl to do the
// work. called by RendererVk::initialization on main thread // work. called by Rendererinitialization on main thread
void processTasks(); void processTasks();
// Called asynchronously from main thread to queue work that is then processed by the worker // Called asynchronously from main thread to queue work that is then processed by the worker
// thread // thread
void queueCommand(vk::Context *context, vk::CommandProcessorTask *task); void queueCommand(Context *context, CommandProcessorTask *task);
void checkCompletedCommands(vk::Context *context); void checkCompletedCommands(Context *context);
// Used by main thread to wait for worker thread to complete all outstanding work. // Used by main thread to wait for worker thread to complete all outstanding work.
void waitForWorkComplete(vk::Context *context); void waitForWorkComplete(Context *context);
Serial getCurrentQueueSerial(); Serial getCurrentQueueSerial();
Serial getLastSubmittedSerial(); Serial getLastSubmittedSerial();
// Wait until desired serial has been processed. // Wait until desired serial has been processed.
void finishToSerial(vk::Context *context, Serial serial); void finishToSerial(Context *context, Serial serial);
vk::Shared<vk::Fence> getLastSubmittedFence(const vk::Context *context) const; Shared<Fence> getLastSubmittedFence(const Context *context) const;
void handleDeviceLost(); void handleDeviceLost();
bool hasPendingError() const bool hasPendingError() const
...@@ -269,12 +331,12 @@ class CommandProcessor : public vk::Context ...@@ -269,12 +331,12 @@ class CommandProcessor : public vk::Context
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
return !mErrors.empty(); return !mErrors.empty();
} }
vk::Error getAndClearPendingError(); Error getAndClearPendingError();
// Stop the command processor thread // Stop the command processor thread
void shutdown(std::thread *commandProcessorThread); void shutdown(std::thread *commandProcessorThread);
void finishAllWork(vk::Context *context); void finishAllWork(Context *context);
VkResult getLastPresentResult(VkSwapchainKHR swapchain) VkResult getLastPresentResult(VkSwapchainKHR swapchain)
{ {
...@@ -287,9 +349,9 @@ class CommandProcessor : public vk::Context ...@@ -287,9 +349,9 @@ class CommandProcessor : public vk::Context
angle::Result processTasksImpl(bool *exitThread); angle::Result processTasksImpl(bool *exitThread);
// Command processor thread, process a task // Command processor thread, process a task
angle::Result processTask(vk::Context *context, vk::CommandProcessorTask *task); angle::Result processTask(Context *context, CommandProcessorTask *task);
std::queue<vk::CommandProcessorTask> mTasks; std::queue<CommandProcessorTask> mTasks;
mutable std::mutex mWorkerMutex; mutable std::mutex mWorkerMutex;
// Signal worker thread when work is available // Signal worker thread when work is available
std::condition_variable mWorkAvailableCondition; std::condition_variable mWorkAvailableCondition;
...@@ -298,8 +360,8 @@ class CommandProcessor : public vk::Context ...@@ -298,8 +360,8 @@ class CommandProcessor : public vk::Context
// Track worker thread Idle state for assertion purposes // Track worker thread Idle state for assertion purposes
bool mWorkerThreadIdle; bool mWorkerThreadIdle;
// Command pool to allocate processor thread primary command buffers from // Command pool to allocate processor thread primary command buffers from
vk::CommandPool mCommandPool; CommandPool mCommandPool;
vk::PrimaryCommandBuffer mPrimaryCommandBuffer; PrimaryCommandBuffer mPrimaryCommandBuffer;
TaskProcessor mTaskProcessor; TaskProcessor mTaskProcessor;
AtomicSerialFactory mQueueSerialFactory; AtomicSerialFactory mQueueSerialFactory;
...@@ -308,7 +370,7 @@ class CommandProcessor : public vk::Context ...@@ -308,7 +370,7 @@ class CommandProcessor : public vk::Context
Serial mCommandProcessorCurrentQueueSerial; Serial mCommandProcessorCurrentQueueSerial;
mutable std::mutex mErrorMutex; mutable std::mutex mErrorMutex;
std::queue<vk::Error> mErrors; std::queue<Error> mErrors;
}; };
} // namespace vk } // namespace vk
......
...@@ -128,31 +128,6 @@ constexpr size_t kDefaultValueSize = sizeof(gl::VertexAttribCurrent ...@@ -128,31 +128,6 @@ constexpr size_t kDefaultValueSize = sizeof(gl::VertexAttribCurrent
constexpr size_t kDefaultBufferSize = kDefaultValueSize * 16; constexpr size_t kDefaultBufferSize = kDefaultValueSize * 16;
constexpr size_t kDriverUniformsAllocatorPageSize = 4 * 1024; constexpr size_t kDriverUniformsAllocatorPageSize = 4 * 1024;
constexpr size_t kInFlightCommandsLimit = 100u;
void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
const vk::PrimaryCommandBuffer &commandBuffer,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const vk::Semaphore *signalSemaphore)
{
// Verify that the submitInfo has been zero'd out.
ASSERT(submitInfo->signalSemaphoreCount == 0);
ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
submitInfo->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
submitInfo->pCommandBuffers = commandBuffer.ptr();
submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
submitInfo->pWaitSemaphores = waitSemaphores.data();
submitInfo->pWaitDstStageMask = waitSemaphoreStageMasks.data();
if (signalSemaphore)
{
submitInfo->signalSemaphoreCount = 1;
submitInfo->pSignalSemaphores = signalSemaphore->ptr();
}
}
uint32_t GetCoverageSampleCount(const gl::State &glState, FramebufferVk *drawFramebuffer) uint32_t GetCoverageSampleCount(const gl::State &glState, FramebufferVk *drawFramebuffer)
{ {
if (!glState.isSampleCoverageEnabled()) if (!glState.isSampleCoverageEnabled())
...@@ -310,19 +285,9 @@ vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState) ...@@ -310,19 +285,9 @@ vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState)
: vk::ResourceAccess::Write; : vk::ResourceAccess::Write;
} }
bool CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> &commands) egl::ContextPriority GetContextPriority(const gl::State &state)
{ {
Serial currentSerial; return egl::FromEGLenum<egl::ContextPriority>(state.getContextPriority());
for (const vk::CommandBatch &commands : commands)
{
if (commands.serial <= currentSerial)
{
return false;
}
currentSerial = commands.serial;
}
return true;
} }
} // anonymous namespace } // anonymous namespace
...@@ -376,357 +341,6 @@ void ContextVk::DriverUniformsDescriptorSet::destroy(RendererVk *renderer) ...@@ -376,357 +341,6 @@ void ContextVk::DriverUniformsDescriptorSet::destroy(RendererVk *renderer)
descriptorSetCache.clear(); descriptorSetCache.clear();
} }
// CommandQueue implementation.
CommandQueue::CommandQueue() = default;
CommandQueue::~CommandQueue() = default;
void CommandQueue::destroy(VkDevice device)
{
mPrimaryCommands.destroy(device);
mPrimaryCommandPool.destroy(device);
ASSERT(mInFlightCommands.empty() && mGarbageQueue.empty());
}
angle::Result CommandQueue::init(vk::Context *context)
{
RendererVk *renderer = context->getRenderer();
// Initialize the command pool now that we know the queue family index.
uint32_t queueFamilyIndex = renderer->getQueueFamilyIndex();
ANGLE_TRY(mPrimaryCommandPool.init(context, queueFamilyIndex));
return angle::Result::Continue;
}
angle::Result CommandQueue::checkCompletedCommands(vk::Context *context)
{
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::checkCompletedCommandsNoLock");
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
int finishedCount = 0;
for (vk::CommandBatch &batch : mInFlightCommands)
{
VkResult result = batch.fence.get().getStatus(device);
if (result == VK_NOT_READY)
{
break;
}
ANGLE_VK_TRY(context, result);
++finishedCount;
}
if (finishedCount == 0)
{
return angle::Result::Continue;
}
return retireFinishedCommands(context, finishedCount);
}
angle::Result CommandQueue::retireFinishedCommands(vk::Context *context, size_t finishedCount)
{
ASSERT(finishedCount > 0);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
for (size_t commandIndex = 0; commandIndex < finishedCount; ++commandIndex)
{
vk::CommandBatch &batch = mInFlightCommands[commandIndex];
renderer->onCompletedSerial(batch.serial);
renderer->resetSharedFence(&batch.fence);
ANGLE_TRACE_EVENT0("gpu.angle", "command buffer recycling");
batch.commandPool.destroy(device);
ANGLE_TRY(releasePrimaryCommandBuffer(context, std::move(batch.primaryCommands)));
}
if (finishedCount > 0)
{
auto beginIter = mInFlightCommands.begin();
mInFlightCommands.erase(beginIter, beginIter + finishedCount);
}
Serial lastCompleted = renderer->getLastCompletedQueueSerial();
size_t freeIndex = 0;
for (; freeIndex < mGarbageQueue.size(); ++freeIndex)
{
vk::GarbageAndSerial &garbageList = mGarbageQueue[freeIndex];
if (garbageList.getSerial() < lastCompleted)
{
for (vk::GarbageObject &garbage : garbageList.get())
{
garbage.destroy(renderer);
}
}
else
{
break;
}
}
// Remove the entries from the garbage list - they should be ready to go.
if (freeIndex > 0)
{
mGarbageQueue.erase(mGarbageQueue.begin(), mGarbageQueue.begin() + freeIndex);
}
return angle::Result::Continue;
}
angle::Result CommandQueue::releaseToCommandBatch(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer,
vk::CommandPool *commandPool,
vk::CommandBatch *batch)
{
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
batch->primaryCommands = std::move(commandBuffer);
if (commandPool->valid())
{
batch->commandPool = std::move(*commandPool);
// Recreate CommandPool
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
poolInfo.queueFamilyIndex = renderer->getQueueFamilyIndex();
ANGLE_VK_TRY(context, commandPool->init(device, poolInfo));
}
return angle::Result::Continue;
}
void CommandQueue::clearAllGarbage(RendererVk *renderer)
{
for (vk::GarbageAndSerial &garbageList : mGarbageQueue)
{
for (vk::GarbageObject &garbage : garbageList.get())
{
garbage.destroy(renderer);
}
}
mGarbageQueue.clear();
}
angle::Result CommandQueue::allocatePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut)
{
return mPrimaryCommandPool.allocate(context, commandBufferOut);
}
angle::Result CommandQueue::releasePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer)
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
ASSERT(mPrimaryCommandPool.valid());
ANGLE_TRY(mPrimaryCommandPool.collect(context, std::move(commandBuffer)));
return angle::Result::Continue;
}
void CommandQueue::handleDeviceLost(RendererVk *renderer)
{
VkDevice device = renderer->getDevice();
for (vk::CommandBatch &batch : mInFlightCommands)
{
// On device loss we need to wait for fence to be signaled before destroying it
VkResult status = batch.fence.get().wait(device, renderer->getMaxFenceWaitTimeNs());
// If the wait times out, it is probably not possible to recover from lost device
ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
// On device lost, here simply destroy the CommandBuffer, it will fully cleared later
// by CommandPool::destroy
batch.primaryCommands.destroy(device);
batch.commandPool.destroy(device);
batch.fence.reset(device);
}
mInFlightCommands.clear();
}
bool CommandQueue::hasInFlightCommands() const
{
return !mInFlightCommands.empty();
}
angle::Result CommandQueue::finishToSerial(vk::Context *context,
Serial finishSerial,
uint64_t timeout)
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
if (mInFlightCommands.empty())
{
return angle::Result::Continue;
}
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::finishToSerial");
// Find the serial in the the list. The serials should be in order.
ASSERT(CommandsHaveValidOrdering(mInFlightCommands));
size_t finishedCount = 0;
while (finishedCount < mInFlightCommands.size() &&
mInFlightCommands[finishedCount].serial < finishSerial)
{
finishedCount++;
}
// This heuristic attempts to increase chances of success for shared resource scenarios.
// Ultimately as long as fences are managed by ContextVk there are edge case bugs here.
// TODO: http://anglebug.com/5217: fix this bug by moving it submit into RendererVk.
if (finishedCount < mInFlightCommands.size())
{
finishedCount++;
}
if (finishedCount == 0)
{
return angle::Result::Continue;
}
const vk::CommandBatch &batch = mInFlightCommands[finishedCount - 1];
// Wait for it finish
VkDevice device = context->getDevice();
VkResult status = batch.fence.get().wait(device, timeout);
ANGLE_VK_TRY(context, status);
// Clean up finished batches.
return retireFinishedCommands(context, finishedCount);
}
angle::Result CommandQueue::submitFrame(
vk::Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const vk::Semaphore *signalSemaphore,
const vk::Shared<vk::Fence> &sharedFence,
vk::ResourceUseList *resourceList,
vk::GarbageList *currentGarbage,
vk::CommandPool *commandPool)
{
// Start an empty primary buffer if we have an empty submit.
if (!hasPrimaryCommands())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_VK_TRY(context, mPrimaryCommands.end());
VkSubmitInfo submitInfo = {};
InitializeSubmitInfo(&submitInfo, mPrimaryCommands, waitSemaphores, waitSemaphoreStageMasks,
signalSemaphore);
ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitFrame");
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
vk::DeviceScoped<vk::CommandBatch> scopedBatch(device);
vk::CommandBatch &batch = scopedBatch.get();
batch.fence.copy(device, sharedFence);
ANGLE_TRY(renderer->queueSubmit(context, priority, submitInfo, resourceList, &batch.fence.get(),
&batch.serial));
if (!currentGarbage->empty())
{
mGarbageQueue.emplace_back(std::move(*currentGarbage), batch.serial);
}
// Store the primary CommandBuffer and command pool used for secondary CommandBuffers
// in the in-flight list.
ANGLE_TRY(releaseToCommandBatch(context, std::move(mPrimaryCommands), commandPool, &batch));
mInFlightCommands.emplace_back(scopedBatch.release());
ANGLE_TRY(checkCompletedCommands(context));
// CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
// off-screen scenarios.
while (mInFlightCommands.size() > kInFlightCommandsLimit)
{
ANGLE_TRY(finishToSerial(context, mInFlightCommands[0].serial,
renderer->getMaxFenceWaitTimeNs()));
}
return angle::Result::Continue;
}
vk::Shared<vk::Fence> CommandQueue::getLastSubmittedFence(const vk::Context *context) const
{
ASSERT(!context->getRenderer()->getFeatures().commandProcessor.enabled);
vk::Shared<vk::Fence> fence;
if (!mInFlightCommands.empty())
{
fence.copy(context->getDevice(), mInFlightCommands.back().fence);
}
return fence;
}
angle::Result CommandQueue::startPrimaryCommandBuffer(vk::Context *context)
{
ASSERT(!mPrimaryCommands.valid());
ANGLE_TRY(allocatePrimaryCommandBuffer(context, &mPrimaryCommands));
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr;
ANGLE_VK_TRY(context, mPrimaryCommands.begin(beginInfo));
return angle::Result::Continue;
}
angle::Result CommandQueue::flushOutsideRPCommands(vk::Context *context,
vk::CommandBufferHelper *outsideRPCommands)
{
if (!mPrimaryCommands.valid())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_TRY(outsideRPCommands->flushToPrimary(context->getRenderer()->getFeatures(),
&mPrimaryCommands, nullptr));
return angle::Result::Continue;
}
angle::Result CommandQueue::flushRenderPassCommands(vk::Context *context,
const vk::RenderPass &renderPass,
vk::CommandBufferHelper *renderPassCommands)
{
if (!mPrimaryCommands.valid())
{
ANGLE_TRY(startPrimaryCommandBuffer(context));
}
ANGLE_TRY(renderPassCommands->flushToPrimary(context->getRenderer()->getFeatures(),
&mPrimaryCommands, &renderPass));
return angle::Result::Continue;
}
egl::ContextPriority GetContextPriority(const gl::State &state)
{
return egl::FromEGLenum<egl::ContextPriority>(state.getContextPriority());
}
// ContextVk implementation. // ContextVk implementation.
ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk *renderer) ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk *renderer)
: ContextImpl(state, errorSet), : ContextImpl(state, errorSet),
......
...@@ -34,69 +34,6 @@ class RendererVk; ...@@ -34,69 +34,6 @@ class RendererVk;
class WindowSurfaceVk; class WindowSurfaceVk;
class ShareGroupVk; class ShareGroupVk;
class CommandQueue final : angle::NonCopyable
{
public:
CommandQueue();
~CommandQueue();
angle::Result init(vk::Context *context);
void destroy(VkDevice device);
void handleDeviceLost(RendererVk *renderer);
bool hasInFlightCommands() const;
void clearAllGarbage(RendererVk *renderer);
angle::Result finishToSerial(vk::Context *context, Serial finishSerial, uint64_t timeout);
angle::Result submitFrame(vk::Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const vk::Semaphore *signalSemaphore,
const vk::Shared<vk::Fence> &sharedFence,
vk::ResourceUseList *resourceList,
vk::GarbageList *currentGarbage,
vk::CommandPool *commandPool);
vk::Shared<vk::Fence> getLastSubmittedFence(const vk::Context *context) const;
// Check to see which batches have finished completion (forward progress for
// mLastCompletedQueueSerial, for example for when the application busy waits on a query
// result). It would be nice if we didn't have to expose this for QueryVk::getResult.
angle::Result checkCompletedCommands(vk::Context *context);
angle::Result flushOutsideRPCommands(vk::Context *context,
vk::CommandBufferHelper *outsideRPCommands);
angle::Result flushRenderPassCommands(vk::Context *context,
const vk::RenderPass &renderPass,
vk::CommandBufferHelper *renderPassCommands);
// TODO(jmadill): Remove this. b/172704839
bool hasPrimaryCommands() const { return mPrimaryCommands.valid(); }
private:
angle::Result releaseToCommandBatch(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer,
vk::CommandPool *commandPool,
vk::CommandBatch *batch);
angle::Result retireFinishedCommands(vk::Context *context, size_t finishedCount);
angle::Result startPrimaryCommandBuffer(vk::Context *context);
angle::Result allocatePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut);
angle::Result releasePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer);
vk::GarbageQueue mGarbageQueue;
std::vector<vk::CommandBatch> mInFlightCommands;
// Keeps a free list of reusable primary command buffers.
vk::PrimaryCommandBuffer mPrimaryCommands;
vk::PersistentCommandPool mPrimaryCommandPool;
};
static constexpr uint32_t kMaxGpuEventNameLen = 32; static constexpr uint32_t kMaxGpuEventNameLen = 32;
using EventName = std::array<char, kMaxGpuEventNameLen>; using EventName = std::array<char, kMaxGpuEventNameLen>;
...@@ -1099,7 +1036,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -1099,7 +1036,7 @@ class ContextVk : public ContextImpl, public vk::Context
vk::CommandPool mCommandPool; vk::CommandPool mCommandPool;
// TODO: This can be killed once threading is enabled https://issuetracker.google.com/153666475 // TODO: This can be killed once threading is enabled https://issuetracker.google.com/153666475
CommandQueue mCommandQueue; vk::CommandQueue mCommandQueue;
vk::GarbageList mCurrentGarbage; vk::GarbageList mCurrentGarbage;
RenderPassCache mRenderPassCache; RenderPassCache mRenderPassCache;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment