Commit 84fce18c by Jamie Madill Committed by Commit Bot

Vulkan: Give CommandQueue an abstract interface.

This gives CommandQueue and CommandProcessor the exact same interface. This also moves the worker thread to be owned by CommandProcessor. Bug: b/172704839 Change-Id: Ife439bcf52d923e01a6a2166e0caaffce14fd086 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2537235 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 59bddcfb
...@@ -59,6 +59,7 @@ bool CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> &commands) ...@@ -59,6 +59,7 @@ bool CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> &commands)
} }
} // namespace } // namespace
// CommandProcessorTask implementation
void CommandProcessorTask::initTask() void CommandProcessorTask::initTask()
{ {
mTask = CustomTask::Invalid; mTask = CustomTask::Invalid;
...@@ -75,7 +76,6 @@ void CommandProcessorTask::initTask() ...@@ -75,7 +76,6 @@ void CommandProcessorTask::initTask()
mOneOffCommandBufferVk = VK_NULL_HANDLE; mOneOffCommandBufferVk = VK_NULL_HANDLE;
} }
// CommandProcessorTask implementation
void CommandProcessorTask::initProcessCommands(CommandBufferHelper *commandBuffer, void CommandProcessorTask::initProcessCommands(CommandBufferHelper *commandBuffer,
const RenderPass *renderPass) const RenderPass *renderPass)
{ {
...@@ -165,29 +165,29 @@ void CommandProcessorTask::initFinishToSerial(Serial serial) ...@@ -165,29 +165,29 @@ void CommandProcessorTask::initFinishToSerial(Serial serial)
} }
void CommandProcessorTask::initFlushAndQueueSubmit( void CommandProcessorTask::initFlushAndQueueSubmit(
std::vector<VkSemaphore> &&waitSemaphores, const std::vector<VkSemaphore> &waitSemaphores,
std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks, const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *semaphore, const Semaphore *semaphore,
egl::ContextPriority priority, egl::ContextPriority priority,
GarbageList &&currentGarbage, GarbageList &&currentGarbage,
Serial submitQueueSerial) Serial submitQueueSerial)
{ {
mTask = CustomTask::FlushAndQueueSubmit; mTask = CustomTask::FlushAndQueueSubmit;
mWaitSemaphores = std::move(waitSemaphores); mWaitSemaphores = waitSemaphores;
mWaitSemaphoreStageMasks = std::move(waitSemaphoreStageMasks); mWaitSemaphoreStageMasks = waitSemaphoreStageMasks;
mSemaphore = semaphore; mSemaphore = semaphore;
mGarbage = std::move(currentGarbage); mGarbage = std::move(currentGarbage);
mPriority = priority; mPriority = priority;
mSerial = submitQueueSerial; mSerial = submitQueueSerial;
} }
void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk, void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,
egl::ContextPriority priority, egl::ContextPriority priority,
const Fence *fence, const Fence *fence,
Serial submitQueueSerial) Serial submitQueueSerial)
{ {
mTask = CustomTask::OneOffQueueSubmit; mTask = CustomTask::OneOffQueueSubmit;
mOneOffCommandBufferVk = oneOffCommandBufferVk; mOneOffCommandBufferVk = commandBufferHandle;
mOneOffFence = fence; mOneOffFence = fence;
mPriority = priority; mPriority = priority;
mSerial = submitQueueSerial; mSerial = submitQueueSerial;
...@@ -200,17 +200,17 @@ CommandProcessorTask &CommandProcessorTask::operator=(CommandProcessorTask &&rhs ...@@ -200,17 +200,17 @@ CommandProcessorTask &CommandProcessorTask::operator=(CommandProcessorTask &&rhs
return *this; return *this;
} }
mRenderPass = rhs.mRenderPass; std::swap(mRenderPass, rhs.mRenderPass);
mCommandBuffer = rhs.mCommandBuffer; std::swap(mCommandBuffer, rhs.mCommandBuffer);
std::swap(mTask, rhs.mTask); std::swap(mTask, rhs.mTask);
std::swap(mWaitSemaphores, rhs.mWaitSemaphores); std::swap(mWaitSemaphores, rhs.mWaitSemaphores);
std::swap(mWaitSemaphoreStageMasks, rhs.mWaitSemaphoreStageMasks); std::swap(mWaitSemaphoreStageMasks, rhs.mWaitSemaphoreStageMasks);
mSemaphore = rhs.mSemaphore; std::swap(mSemaphore, rhs.mSemaphore);
mOneOffFence = rhs.mOneOffFence; std::swap(mOneOffFence, rhs.mOneOffFence);
std::swap(mGarbage, rhs.mGarbage); std::swap(mGarbage, rhs.mGarbage);
std::swap(mSerial, rhs.mSerial); std::swap(mSerial, rhs.mSerial);
std::swap(mPriority, rhs.mPriority); std::swap(mPriority, rhs.mPriority);
mOneOffCommandBufferVk = rhs.mOneOffCommandBufferVk; std::swap(mOneOffCommandBufferVk, rhs.mOneOffCommandBufferVk);
copyPresentInfo(rhs.mPresentInfo); copyPresentInfo(rhs.mPresentInfo);
...@@ -261,7 +261,7 @@ void CommandProcessor::handleError(VkResult errorCode, ...@@ -261,7 +261,7 @@ void CommandProcessor::handleError(VkResult errorCode,
if (errorCode == VK_ERROR_DEVICE_LOST) if (errorCode == VK_ERROR_DEVICE_LOST)
{ {
WARN() << errorStream.str(); WARN() << errorStream.str();
handleDeviceLost(); handleDeviceLost(mRenderer);
} }
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
...@@ -281,39 +281,35 @@ CommandProcessor::CommandProcessor(RendererVk *renderer) ...@@ -281,39 +281,35 @@ CommandProcessor::CommandProcessor(RendererVk *renderer)
CommandProcessor::~CommandProcessor() = default; CommandProcessor::~CommandProcessor() = default;
Error CommandProcessor::getAndClearPendingError() angle::Result CommandProcessor::checkAndPopPendingError(Context *errorHandlingContext)
{ {
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
Error tmpError({VK_SUCCESS, nullptr, nullptr, 0}); if (mErrors.empty())
if (!mErrors.empty()) {
return angle::Result::Continue;
}
else
{ {
tmpError = mErrors.front(); Error err = mErrors.front();
mErrors.pop(); mErrors.pop();
errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
return angle::Result::Stop;
} }
return tmpError;
} }
void CommandProcessor::queueCommand(Context *context, CommandProcessorTask *task) void CommandProcessor::queueCommand(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);
mTasks.emplace(std::move(*task)); mTasks.emplace(std::move(task));
mWorkAvailableCondition.notify_one(); mWorkAvailableCondition.notify_one();
} }
void CommandProcessor::processTasks(const DeviceQueueMap &queueMap) void CommandProcessor::processTasks(const DeviceQueueMap &queueMap)
{ {
angle::Result initResult = mCommandQueue.init(this, queueMap);
if (initResult == angle::Result::Stop)
{
// TODO: https://issuetracker.google.com/issues/170311829 - error handling
UNREACHABLE();
return;
}
while (true) while (true)
{ {
bool exitThread = false; bool exitThread = false;
...@@ -376,7 +372,7 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task) ...@@ -376,7 +372,7 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
ANGLE_TRY(mCommandQueue.finishToSerial(this, Serial::Infinite(), ANGLE_TRY(mCommandQueue.finishToSerial(this, Serial::Infinite(),
mRenderer->getMaxFenceWaitTimeNs())); mRenderer->getMaxFenceWaitTimeNs()));
// Shutting down so cleanup // Shutting down so cleanup
mCommandQueue.destroy(mRenderer); mCommandQueue.destroy(this);
mCommandPool.destroy(mRenderer->getDevice()); mCommandPool.destroy(mRenderer->getDevice());
break; break;
} }
...@@ -403,18 +399,10 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task) ...@@ -403,18 +399,10 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
case CustomTask::OneOffQueueSubmit: case CustomTask::OneOffQueueSubmit:
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit"); ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit");
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
if (task->getOneOffCommandBufferVk() != VK_NULL_HANDLE)
{
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &task->getOneOffCommandBufferVk();
}
// TODO: https://issuetracker.google.com/issues/170328907 - vkQueueSubmit should be ANGLE_TRY(mCommandQueue.queueSubmitOneOff(
// owned by TaskProcessor to ensure proper synchronization this, task->getPriority(), task->getOneOffCommandBufferVk(), task->getOneOffFence(),
ANGLE_TRY(mCommandQueue.queueSubmit(this, task->getPriority(), submitInfo, task->getQueueSerial()));
task->getOneOffFence(), task->getQueueSerial()));
ANGLE_TRY(mCommandQueue.checkCompletedCommands(this)); ANGLE_TRY(mCommandQueue.checkCompletedCommands(this));
break; break;
} }
...@@ -444,14 +432,16 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task) ...@@ -444,14 +432,16 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
case CustomTask::ProcessCommands: case CustomTask::ProcessCommands:
{ {
ASSERT(!task->getCommandBuffer()->empty()); ASSERT(!task->getCommandBuffer()->empty());
CommandBufferHelper *commandBuffer = task->getCommandBuffer();
if (task->getRenderPass()) if (task->getRenderPass())
{ {
ANGLE_TRY(mCommandQueue.flushRenderPassCommands(this, *task->getRenderPass(), ANGLE_TRY(mCommandQueue.flushRenderPassCommands(this, *task->getRenderPass(),
task->getCommandBuffer())); &commandBuffer));
} }
else else
{ {
ANGLE_TRY(mCommandQueue.flushOutsideRPCommands(this, task->getCommandBuffer())); ANGLE_TRY(mCommandQueue.flushOutsideRPCommands(this, &commandBuffer));
} }
ASSERT(task->getCommandBuffer()->empty()); ASSERT(task->getCommandBuffer()->empty());
mRenderer->recycleCommandBufferHelper(task->getCommandBuffer()); mRenderer->recycleCommandBufferHelper(task->getCommandBuffer());
...@@ -470,64 +460,67 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task) ...@@ -470,64 +460,67 @@ angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
return angle::Result::Continue; return angle::Result::Continue;
} }
void CommandProcessor::checkCompletedCommands(Context *context) angle::Result CommandProcessor::checkCompletedCommands(Context *context)
{ {
ANGLE_TRY(checkAndPopPendingError(context));
CommandProcessorTask checkCompletedTask; CommandProcessorTask checkCompletedTask;
checkCompletedTask.initTask(CustomTask::CheckCompletedCommands); checkCompletedTask.initTask(CustomTask::CheckCompletedCommands);
queueCommand(this, &checkCompletedTask); queueCommand(std::move(checkCompletedTask));
return angle::Result::Continue;
} }
void CommandProcessor::waitForWorkComplete(Context *context) angle::Result CommandProcessor::waitForWorkComplete(Context *context)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForWorkComplete"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForWorkComplete");
std::unique_lock<std::mutex> lock(mWorkerMutex); std::unique_lock<std::mutex> lock(mWorkerMutex);
mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); }); mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); });
// Worker thread is idle and command queue is empty so good to continue // Worker thread is idle and command queue is empty so good to continue
if (!context)
{
return;
}
// Sync any errors to the context // Sync any errors to the context
bool shouldStop = hasPendingError();
while (hasPendingError()) while (hasPendingError())
{ {
Error workerError = getAndClearPendingError(); (void)checkAndPopPendingError(context);
if (workerError.mErrorCode != VK_SUCCESS)
{
context->handleError(workerError.mErrorCode, workerError.mFile, workerError.mFunction,
workerError.mLine);
}
} }
return shouldStop ? angle::Result::Stop : angle::Result::Continue;
} }
// TODO: https://issuetracker.google.com/170311829 - Add vk::Context so that queueCommand has angle::Result CommandProcessor::init(Context *context, const DeviceQueueMap &queueMap)
// someplace to send errors. {
void CommandProcessor::shutdown(std::thread *commandProcessorThread) ANGLE_TRY(mCommandQueue.init(context, queueMap));
mTaskThread = std::thread(&CommandProcessor::processTasks, this, queueMap);
return angle::Result::Continue;
}
void CommandProcessor::destroy(Context *context)
{ {
CommandProcessorTask endTask; CommandProcessorTask endTask;
endTask.initTask(CustomTask::Exit); endTask.initTask(CustomTask::Exit);
queueCommand(this, &endTask); queueCommand(std::move(endTask));
waitForWorkComplete(nullptr); (void)waitForWorkComplete(context);
if (commandProcessorThread->joinable()) if (mTaskThread.joinable())
{ {
commandProcessorThread->join(); mTaskThread.join();
} }
} }
Serial CommandProcessor::getLastCompletedQueueSerial() Serial CommandProcessor::getLastCompletedQueueSerial() const
{ {
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mCommandQueue.getLastCompletedQueueSerial(); return mCommandQueue.getLastCompletedQueueSerial();
} }
Serial CommandProcessor::getLastSubmittedQueueSerial() Serial CommandProcessor::getLastSubmittedQueueSerial() const
{ {
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mCommandQueue.getLastSubmittedQueueSerial(); return mCommandQueue.getLastSubmittedQueueSerial();
} }
Serial CommandProcessor::getCurrentQueueSerial() Serial CommandProcessor::getCurrentQueueSerial() const
{ {
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mCommandQueue.getCurrentQueueSerial(); return mCommandQueue.getCurrentQueueSerial();
...@@ -540,33 +533,36 @@ Serial CommandProcessor::reserveSubmitSerial() ...@@ -540,33 +533,36 @@ Serial CommandProcessor::reserveSubmitSerial()
} }
// 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(Context *context, Serial serial) angle::Result CommandProcessor::finishToSerial(Context *context, Serial serial, uint64_t timeout)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishToSerial"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishToSerial");
CommandProcessorTask finishToSerial;
finishToSerial.initFinishToSerial(serial); ANGLE_TRY(checkAndPopPendingError(context));
queueCommand(context, &finishToSerial);
CommandProcessorTask task;
task.initFinishToSerial(serial);
queueCommand(std::move(task));
// Wait until the worker is idle. At that point we know that the finishToSerial command has // Wait until the worker is idle. At that point we know that the finishToSerial command has
// completed executing, including any associated state cleanup. // completed executing, including any associated state cleanup.
waitForWorkComplete(context); return waitForWorkComplete(context);
} }
void CommandProcessor::handleDeviceLost() void CommandProcessor::handleDeviceLost(RendererVk *renderer)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::handleDeviceLost"); ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::handleDeviceLost");
std::unique_lock<std::mutex> lock(mWorkerMutex); std::unique_lock<std::mutex> lock(mWorkerMutex);
mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); }); mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); });
// Worker thread is idle and command queue is empty so good to continue // Worker thread is idle and command queue is empty so good to continue
mCommandQueue.handleDeviceLost(mRenderer); mCommandQueue.handleDeviceLost(renderer);
} }
void CommandProcessor::finishAllWork(Context *context) angle::Result 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()); return finishToSerial(context, Serial::Infinite(), mRenderer->getMaxFenceWaitTimeNs());
} }
VkResult CommandProcessor::getLastAndClearPresentResult(VkSwapchainKHR swapchain) VkResult CommandProcessor::getLastAndClearPresentResult(VkSwapchainKHR swapchain)
...@@ -601,12 +597,103 @@ VkResult CommandProcessor::present(egl::ContextPriority priority, ...@@ -601,12 +597,103 @@ VkResult CommandProcessor::present(egl::ContextPriority priority,
return result; return result;
} }
angle::Result CommandProcessor::submitFrame(
Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *signalSemaphore,
Shared<Fence> &&sharedFence,
GarbageList &&currentGarbage,
CommandPool *commandPool,
Serial submitQueueSerial)
{
ANGLE_TRY(checkAndPopPendingError(context));
CommandProcessorTask task;
task.initFlushAndQueueSubmit(waitSemaphores, waitSemaphoreStageMasks, signalSemaphore, priority,
std::move(currentGarbage), submitQueueSerial);
queueCommand(std::move(task));
return angle::Result::Continue;
}
angle::Result CommandProcessor::queueSubmitOneOff(Context *context,
egl::ContextPriority contextPriority,
VkCommandBuffer commandBufferHandle,
const Fence *fence,
Serial submitQueueSerial)
{
ANGLE_TRY(checkAndPopPendingError(context));
CommandProcessorTask task;
task.initOneOffQueueSubmit(commandBufferHandle, contextPriority, fence, submitQueueSerial);
queueCommand(std::move(task));
return angle::Result::Continue;
}
VkResult CommandProcessor::queuePresent(egl::ContextPriority contextPriority,
const VkPresentInfoKHR &presentInfo)
{
CommandProcessorTask task;
task.initPresent(contextPriority, presentInfo);
ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queuePresent");
queueCommand(std::move(task));
// Always return success, when we call acquireNextImage we'll check the return code. This
// allows the app to continue working until we really need to know the return code from
// present.
return VK_SUCCESS;
}
angle::Result CommandProcessor::waitForSerialWithUserTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
VkResult *result)
{
// If finishToSerial times out we generate an error. Therefore we a large timeout.
// TODO: https://issuetracker.google.com/170312581 - Wait with timeout.
return finishToSerial(context, serial, mRenderer->getMaxFenceWaitTimeNs());
}
angle::Result CommandProcessor::flushOutsideRPCommands(Context *context,
CommandBufferHelper **outsideRPCommands)
{
ANGLE_TRY(checkAndPopPendingError(context));
(*outsideRPCommands)->markClosed();
CommandProcessorTask task;
task.initProcessCommands(*outsideRPCommands, nullptr);
queueCommand(std::move(task));
*outsideRPCommands = mRenderer->getCommandBufferHelper(false);
return angle::Result::Continue;
}
angle::Result CommandProcessor::flushRenderPassCommands(Context *context,
const RenderPass &renderPass,
CommandBufferHelper **renderPassCommands)
{
ANGLE_TRY(checkAndPopPendingError(context));
(*renderPassCommands)->markClosed();
CommandProcessorTask task;
task.initProcessCommands(*renderPassCommands, &renderPass);
queueCommand(std::move(task));
*renderPassCommands = mRenderer->getCommandBufferHelper(true);
return angle::Result::Continue;
}
// CommandQueue implementation. // CommandQueue implementation.
CommandQueue::CommandQueue() : mCurrentQueueSerial(mQueueSerialFactory.generate()) {} CommandQueue::CommandQueue() : mCurrentQueueSerial(mQueueSerialFactory.generate()) {}
CommandQueue::~CommandQueue() = default; CommandQueue::~CommandQueue() = default;
void CommandQueue::destroy(RendererVk *renderer) void CommandQueue::destroy(Context *context)
{ {
// Force all commands to finish by flushing all queues. // Force all commands to finish by flushing all queues.
for (VkQueue queue : mQueues) for (VkQueue queue : mQueues)
...@@ -617,8 +704,10 @@ void CommandQueue::destroy(RendererVk *renderer) ...@@ -617,8 +704,10 @@ void CommandQueue::destroy(RendererVk *renderer)
} }
} }
RendererVk *renderer = context->getRenderer();
mLastCompletedQueueSerial = Serial::Infinite(); mLastCompletedQueueSerial = Serial::Infinite();
clearAllGarbage(renderer); (void)clearAllGarbage(renderer);
mPrimaryCommands.destroy(renderer->getDevice()); mPrimaryCommands.destroy(renderer->getDevice());
mPrimaryCommandPool.destroy(renderer->getDevice()); mPrimaryCommandPool.destroy(renderer->getDevice());
...@@ -952,20 +1041,38 @@ angle::Result CommandQueue::ensurePrimaryCommandBufferValid(Context *context) ...@@ -952,20 +1041,38 @@ angle::Result CommandQueue::ensurePrimaryCommandBufferValid(Context *context)
} }
angle::Result CommandQueue::flushOutsideRPCommands(Context *context, angle::Result CommandQueue::flushOutsideRPCommands(Context *context,
CommandBufferHelper *outsideRPCommands) CommandBufferHelper **outsideRPCommands)
{ {
ANGLE_TRY(ensurePrimaryCommandBufferValid(context)); ANGLE_TRY(ensurePrimaryCommandBufferValid(context));
return outsideRPCommands->flushToPrimary(context->getRenderer()->getFeatures(), return (*outsideRPCommands)
&mPrimaryCommands, nullptr); ->flushToPrimary(context->getRenderer()->getFeatures(), &mPrimaryCommands, nullptr);
} }
angle::Result CommandQueue::flushRenderPassCommands(Context *context, angle::Result CommandQueue::flushRenderPassCommands(Context *context,
const RenderPass &renderPass, const RenderPass &renderPass,
CommandBufferHelper *renderPassCommands) CommandBufferHelper **renderPassCommands)
{ {
ANGLE_TRY(ensurePrimaryCommandBufferValid(context)); ANGLE_TRY(ensurePrimaryCommandBufferValid(context));
return renderPassCommands->flushToPrimary(context->getRenderer()->getFeatures(), return (*renderPassCommands)
&mPrimaryCommands, &renderPass); ->flushToPrimary(context->getRenderer()->getFeatures(), &mPrimaryCommands, &renderPass);
}
angle::Result CommandQueue::queueSubmitOneOff(Context *context,
egl::ContextPriority contextPriority,
VkCommandBuffer commandBufferHandle,
const Fence *fence,
Serial submitQueueSerial)
{
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
if (commandBufferHandle != VK_NULL_HANDLE)
{
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBufferHandle;
}
return queueSubmit(context, contextPriority, submitInfo, fence, submitQueueSerial);
} }
angle::Result CommandQueue::queueSubmit(Context *context, angle::Result CommandQueue::queueSubmit(Context *context,
...@@ -996,5 +1103,20 @@ VkResult CommandQueue::queuePresent(egl::ContextPriority contextPriority, ...@@ -996,5 +1103,20 @@ VkResult CommandQueue::queuePresent(egl::ContextPriority contextPriority,
{ {
return vkQueuePresentKHR(mQueues[contextPriority], &presentInfo); return vkQueuePresentKHR(mQueues[contextPriority], &presentInfo);
} }
Serial CommandQueue::getLastSubmittedQueueSerial() const
{
return mLastSubmittedQueueSerial;
}
Serial CommandQueue::getLastCompletedQueueSerial() const
{
return mLastCompletedQueueSerial;
}
Serial CommandQueue::getCurrentQueueSerial() const
{
return mCurrentQueueSerial;
}
} // namespace vk } // namespace vk
} // namespace rx } // namespace rx
...@@ -27,13 +27,7 @@ class CommandProcessor; ...@@ -27,13 +27,7 @@ class CommandProcessor;
namespace vk namespace vk
{ {
// CommandProcessor is used to dispatch work to the GPU when commandProcessor feature is true. enum class CustomTask
// If asynchronousCommandProcessing is enabled the work will be queued and handled by a worker
// thread asynchronous to the context. Issuing the CustomTask::Exit command will cause the worker
// thread to clean up it's resources and shut down. This command is sent when the renderer instance
// shuts down. Custom tasks are:
enum CustomTask
{ {
Invalid = 0, Invalid = 0,
// Process SecondaryCommandBuffer commands into the primary CommandBuffer. // Process SecondaryCommandBuffer commands into the primary CommandBuffer.
...@@ -54,6 +48,7 @@ enum CustomTask ...@@ -54,6 +48,7 @@ enum CustomTask
Exit, Exit,
}; };
// CommandProcessorTask interface
class CommandProcessorTask class CommandProcessorTask
{ {
public: public:
...@@ -69,14 +64,14 @@ class CommandProcessorTask ...@@ -69,14 +64,14 @@ class CommandProcessorTask
void initFinishToSerial(Serial serial); void initFinishToSerial(Serial serial);
void initFlushAndQueueSubmit(std::vector<VkSemaphore> &&waitSemaphores, void initFlushAndQueueSubmit(const std::vector<VkSemaphore> &waitSemaphores,
std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks, const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *semaphore, const Semaphore *semaphore,
egl::ContextPriority priority, egl::ContextPriority priority,
GarbageList &&currentGarbage, GarbageList &&currentGarbage,
Serial submitQueueSerial); Serial submitQueueSerial);
void initOneOffQueueSubmit(VkCommandBuffer oneOffCommandBufferVk, void initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,
egl::ContextPriority priority, egl::ContextPriority priority,
const Fence *fence, const Fence *fence,
Serial submitQueueSerial); Serial submitQueueSerial);
...@@ -99,7 +94,7 @@ class CommandProcessorTask ...@@ -99,7 +94,7 @@ class CommandProcessorTask
const Semaphore *getSemaphore() { return mSemaphore; } const Semaphore *getSemaphore() { return mSemaphore; }
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; } VkCommandBuffer getOneOffCommandBufferVk() const { return mOneOffCommandBufferVk; }
const Fence *getOneOffFence() { return mOneOffFence; } const Fence *getOneOffFence() { return mOneOffFence; }
const VkPresentInfoKHR &getPresentInfo() const { return mPresentInfo; } const VkPresentInfoKHR &getPresentInfo() const { return mPresentInfo; }
const RenderPass *getRenderPass() const { return mRenderPass; } const RenderPass *getRenderPass() const { return mRenderPass; }
...@@ -159,21 +154,75 @@ struct CommandBatch final : angle::NonCopyable ...@@ -159,21 +154,75 @@ struct CommandBatch final : angle::NonCopyable
using DeviceQueueMap = angle::PackedEnumMap<egl::ContextPriority, VkQueue>; using DeviceQueueMap = angle::PackedEnumMap<egl::ContextPriority, VkQueue>;
class CommandQueue final : angle::NonCopyable class CommandQueueInterface : angle::NonCopyable
{ {
public: public:
CommandQueue(); virtual ~CommandQueueInterface() {}
~CommandQueue();
virtual angle::Result init(Context *context, const DeviceQueueMap &queueMap) = 0;
virtual void destroy(Context *context) = 0;
virtual void handleDeviceLost(RendererVk *renderer) = 0;
// Wait until the desired serial has been completed.
virtual angle::Result finishToSerial(Context *context,
Serial finishSerial,
uint64_t timeout) = 0;
virtual Serial reserveSubmitSerial() = 0;
virtual angle::Result submitFrame(
Context *context,
egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *signalSemaphore,
Shared<Fence> &&sharedFence,
GarbageList &&currentGarbage,
CommandPool *commandPool,
Serial submitQueueSerial) = 0;
virtual angle::Result queueSubmitOneOff(Context *context,
egl::ContextPriority contextPriority,
VkCommandBuffer commandBufferHandle,
const Fence *fence,
Serial submitQueueSerial) = 0;
virtual VkResult queuePresent(egl::ContextPriority contextPriority,
const VkPresentInfoKHR &presentInfo) = 0;
virtual angle::Result waitForSerialWithUserTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
VkResult *result) = 0;
angle::Result init(Context *context, const DeviceQueueMap &queueMap); // Check to see which batches have finished completion (forward progress for
void destroy(RendererVk *renderer); // the last completed serial, for example for when the application busy waits on a query
void handleDeviceLost(RendererVk *renderer); // result). It would be nice if we didn't have to expose this for QueryVk::getResult.
virtual angle::Result checkCompletedCommands(Context *context) = 0;
virtual angle::Result flushOutsideRPCommands(Context *context,
CommandBufferHelper **outsideRPCommands) = 0;
virtual angle::Result flushRenderPassCommands(Context *context,
const RenderPass &renderPass,
CommandBufferHelper **renderPassCommands) = 0;
virtual Serial getLastSubmittedQueueSerial() const = 0;
virtual Serial getLastCompletedQueueSerial() const = 0;
virtual Serial getCurrentQueueSerial() const = 0;
};
class CommandQueue final : public CommandQueueInterface
{
public:
CommandQueue();
~CommandQueue() override;
angle::Result init(Context *context, const DeviceQueueMap &queueMap) override;
void destroy(Context *context) override;
void clearAllGarbage(RendererVk *renderer); void clearAllGarbage(RendererVk *renderer);
angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout); void handleDeviceLost(RendererVk *renderer) override;
angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout) override;
Serial reserveSubmitSerial(); Serial reserveSubmitSerial() override;
angle::Result submitFrame(Context *context, angle::Result submitFrame(Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
...@@ -183,34 +232,39 @@ class CommandQueue final : angle::NonCopyable ...@@ -183,34 +232,39 @@ class CommandQueue final : angle::NonCopyable
Shared<Fence> &&sharedFence, Shared<Fence> &&sharedFence,
GarbageList &&currentGarbage, GarbageList &&currentGarbage,
CommandPool *commandPool, CommandPool *commandPool,
Serial submitQueueSerial); Serial submitQueueSerial) override;
angle::Result queueSubmitOneOff(Context *context,
egl::ContextPriority contextPriority,
VkCommandBuffer commandBufferHandle,
const Fence *fence,
Serial submitQueueSerial) override;
angle::Result queueSubmit(Context *context,
egl::ContextPriority contextPriority,
const VkSubmitInfo &submitInfo,
const Fence *fence,
Serial submitQueueSerial);
VkResult queuePresent(egl::ContextPriority contextPriority, VkResult queuePresent(egl::ContextPriority contextPriority,
const VkPresentInfoKHR &presentInfo); const VkPresentInfoKHR &presentInfo) override;
angle::Result waitForSerialWithUserTimeout(vk::Context *context, angle::Result waitForSerialWithUserTimeout(vk::Context *context,
Serial serial, Serial serial,
uint64_t timeout, uint64_t timeout,
VkResult *result); VkResult *result) override;
// Check to see which batches have finished completion (forward progress for angle::Result checkCompletedCommands(Context *context) override;
// 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 flushOutsideRPCommands(Context *context,
CommandBufferHelper **outsideRPCommands) override;
angle::Result flushRenderPassCommands(Context *context, angle::Result flushRenderPassCommands(Context *context,
const RenderPass &renderPass, const RenderPass &renderPass,
CommandBufferHelper *renderPassCommands); CommandBufferHelper **renderPassCommands) override;
ANGLE_INLINE Serial getLastSubmittedQueueSerial() const { return mLastSubmittedQueueSerial; } Serial getLastSubmittedQueueSerial() const override;
ANGLE_INLINE Serial getLastCompletedQueueSerial() const { return mLastCompletedQueueSerial; } Serial getLastCompletedQueueSerial() const override;
ANGLE_INLINE Serial getCurrentQueueSerial() const { return mCurrentQueueSerial; } Serial getCurrentQueueSerial() const override;
angle::Result queueSubmit(Context *context,
egl::ContextPriority contextPriority,
const VkSubmitInfo &submitInfo,
const Fence *fence,
Serial submitQueueSerial);
private: private:
angle::Result releaseToCommandBatch(Context *context, angle::Result releaseToCommandBatch(Context *context,
...@@ -239,59 +293,95 @@ class CommandQueue final : angle::NonCopyable ...@@ -239,59 +293,95 @@ class CommandQueue final : angle::NonCopyable
DeviceQueueMap mQueues; DeviceQueueMap mQueues;
}; };
// TODO(jmadill): Give this the same API as CommandQueue. b/172704839 // CommandProcessor is used to dispatch work to the GPU when the asyncCommandQueue feature is
class CommandProcessor : public Context // enabled. Issuing the |destroy| command will cause the worker thread to clean up it's resources
// and shut down. This command is sent when the renderer instance shuts down. Tasks are defined by
// the CommandQueue interface.
class CommandProcessor : public Context, public CommandQueueInterface
{ {
public: public:
CommandProcessor(RendererVk *renderer); CommandProcessor(RendererVk *renderer);
~CommandProcessor() override; ~CommandProcessor() override;
// Used by main thread to wait for worker thread to complete all outstanding work.
// TODO(jmadill): Make private. b/172704839
angle::Result waitForWorkComplete(Context *context);
angle::Result finishAllWork(Context *context);
VkResult getLastPresentResult(VkSwapchainKHR swapchain)
{
return getLastAndClearPresentResult(swapchain);
}
// vk::Context
void handleError(VkResult result, void handleError(VkResult result,
const char *file, const char *file,
const char *function, const char *function,
unsigned int line) override; unsigned int line) override;
// Entry point for command processor thread, calls processTasksImpl to do the // CommandQueueInterface
// work. called by Rendererinitialization on main thread angle::Result init(Context *context, const DeviceQueueMap &queueMap) override;
void processTasks(const DeviceQueueMap &queueMap);
// Called asynchronously from main thread to queue work that is then processed by the worker void destroy(Context *context) override;
// thread
void queueCommand(Context *context, CommandProcessorTask *task);
void checkCompletedCommands(Context *context); void handleDeviceLost(RendererVk *renderer) override;
// Used by main thread to wait for worker thread to complete all outstanding work. angle::Result finishToSerial(Context *context, Serial finishSerial, uint64_t timeout) override;
void waitForWorkComplete(Context *context);
Serial getLastCompletedQueueSerial(); Serial reserveSubmitSerial() override;
Serial getLastSubmittedQueueSerial();
Serial getCurrentQueueSerial();
Serial reserveSubmitSerial();
// Wait until desired serial has been processed. angle::Result submitFrame(Context *context,
void finishToSerial(Context *context, Serial serial); egl::ContextPriority priority,
const std::vector<VkSemaphore> &waitSemaphores,
const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
const Semaphore *signalSemaphore,
Shared<Fence> &&sharedFence,
GarbageList &&currentGarbage,
CommandPool *commandPool,
Serial submitQueueSerial) override;
angle::Result queueSubmitOneOff(Context *context,
egl::ContextPriority contextPriority,
VkCommandBuffer commandBufferHandle,
const Fence *fence,
Serial submitQueueSerial) override;
VkResult queuePresent(egl::ContextPriority contextPriority,
const VkPresentInfoKHR &presentInfo) override;
angle::Result waitForSerialWithUserTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
VkResult *result) override;
void handleDeviceLost(); angle::Result checkCompletedCommands(Context *context) override;
angle::Result flushOutsideRPCommands(Context *context,
CommandBufferHelper **outsideRPCommands) override;
angle::Result flushRenderPassCommands(Context *context,
const RenderPass &renderPass,
CommandBufferHelper **renderPassCommands) override;
Serial getLastSubmittedQueueSerial() const override;
Serial getLastCompletedQueueSerial() const override;
Serial getCurrentQueueSerial() const override;
private:
bool hasPendingError() const bool hasPendingError() const
{ {
std::lock_guard<std::mutex> queueLock(mErrorMutex); std::lock_guard<std::mutex> queueLock(mErrorMutex);
return !mErrors.empty(); return !mErrors.empty();
} }
Error getAndClearPendingError(); angle::Result checkAndPopPendingError(Context *errorHandlingContext);
// Stop the command processor thread
void shutdown(std::thread *commandProcessorThread);
void finishAllWork(Context *context); // Entry point for command processor thread, calls processTasksImpl to do the
// work. called by RendererVk::initializeDevice on main thread
void processTasks(const DeviceQueueMap &queueMap);
VkResult getLastPresentResult(VkSwapchainKHR swapchain) // Called asynchronously from main thread to queue work that is then processed by the worker
{ // thread
return getLastAndClearPresentResult(swapchain); void queueCommand(CommandProcessorTask &&task);
}
private:
// Command processor thread, called by processTasks. The loop waits for work to // Command processor thread, called by processTasks. The loop waits for work to
// be submitted from a separate thread. // be submitted from a separate thread.
angle::Result processTasksImpl(bool *exitThread); angle::Result processTasksImpl(bool *exitThread);
...@@ -314,7 +404,7 @@ class CommandProcessor : public Context ...@@ -314,7 +404,7 @@ class CommandProcessor : public Context
CommandPool mCommandPool; CommandPool mCommandPool;
CommandQueue mCommandQueue; CommandQueue mCommandQueue;
std::mutex mQueueSerialMutex; mutable std::mutex mQueueSerialMutex;
mutable std::mutex mErrorMutex; mutable std::mutex mErrorMutex;
std::queue<Error> mErrors; std::queue<Error> mErrors;
...@@ -323,6 +413,9 @@ class CommandProcessor : public Context ...@@ -323,6 +413,9 @@ class CommandProcessor : public Context
std::mutex mSwapchainStatusMutex; std::mutex mSwapchainStatusMutex;
std::condition_variable mSwapchainStatusCondition; std::condition_variable mSwapchainStatusCondition;
std::map<VkSwapchainKHR, VkResult> mSwapchainStatus; std::map<VkSwapchainKHR, VkResult> mSwapchainStatus;
// Command queue worker thread.
std::thread mTaskThread;
}; };
} // namespace vk } // namespace vk
......
...@@ -527,9 +527,6 @@ void ContextVk::onDestroy(const gl::Context *context) ...@@ -527,9 +527,6 @@ void ContextVk::onDestroy(const gl::Context *context)
mShaderLibrary.destroy(device); mShaderLibrary.destroy(device);
mGpuEventQueryPool.destroy(device); mGpuEventQueryPool.destroy(device);
mCommandPool.destroy(device); mCommandPool.destroy(device);
// This will clean up any outstanding buffer allocations
(void)mRenderer->clearAllGarbage(this);
} }
angle::Result ContextVk::getIncompleteTexture(const gl::Context *context, angle::Result ContextVk::getIncompleteTexture(const gl::Context *context,
...@@ -1842,13 +1839,12 @@ void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuT ...@@ -1842,13 +1839,12 @@ void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuT
void ContextVk::clearAllGarbage() void ContextVk::clearAllGarbage()
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::finishAllWork"); ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::clearAllGarbage");
for (vk::GarbageObject &garbage : mCurrentGarbage) for (vk::GarbageObject &garbage : mCurrentGarbage)
{ {
garbage.destroy(mRenderer); garbage.destroy(mRenderer);
} }
mCurrentGarbage.clear(); mCurrentGarbage.clear();
mRenderer->clearAllGarbage(this);
} }
void ContextVk::handleDeviceLost() void ContextVk::handleDeviceLost()
...@@ -3035,7 +3031,7 @@ angle::Result ContextVk::onUnMakeCurrent(const gl::Context *context) ...@@ -3035,7 +3031,7 @@ angle::Result ContextVk::onUnMakeCurrent(const gl::Context *context)
if (mRenderer->getFeatures().asyncCommandQueue.enabled) if (mRenderer->getFeatures().asyncCommandQueue.enabled)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::onUnMakeCurrent"); ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::onUnMakeCurrent");
mRenderer->finishAllWork(this); ANGLE_TRY(mRenderer->finishAllWork(this));
} }
mCurrentWindowSurface = nullptr; mCurrentWindowSurface = nullptr;
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -260,10 +260,10 @@ void DisplayVk::handleError(VkResult result, ...@@ -260,10 +260,10 @@ void DisplayVk::handleError(VkResult result,
{ {
ASSERT(result != VK_SUCCESS); ASSERT(result != VK_SUCCESS);
mSavedError.mErrorCode = result; mSavedError.errorCode = result;
mSavedError.mFile = file; mSavedError.file = file;
mSavedError.mFunction = function; mSavedError.function = function;
mSavedError.mLine = line; mSavedError.line = line;
if (result == VK_ERROR_DEVICE_LOST) if (result == VK_ERROR_DEVICE_LOST)
{ {
...@@ -277,10 +277,9 @@ void DisplayVk::handleError(VkResult result, ...@@ -277,10 +277,9 @@ void DisplayVk::handleError(VkResult result,
egl::Error DisplayVk::getEGLError(EGLint errorCode) egl::Error DisplayVk::getEGLError(EGLint errorCode)
{ {
std::stringstream errorStream; std::stringstream errorStream;
errorStream << "Internal Vulkan error (" << mSavedError.mErrorCode errorStream << "Internal Vulkan error (" << mSavedError.errorCode
<< "): " << VulkanResultString(mSavedError.mErrorCode) << ", in " << "): " << VulkanResultString(mSavedError.errorCode) << ", in " << mSavedError.file
<< mSavedError.mFile << ", " << mSavedError.mFunction << ":" << mSavedError.mLine << ", " << mSavedError.function << ":" << mSavedError.line << ".";
<< ".";
std::string errorString = errorStream.str(); std::string errorString = errorStream.str();
return egl::Error(errorCode, 0, std::move(errorString)); return egl::Error(errorCode, 0, std::move(errorString));
......
...@@ -190,7 +190,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -190,7 +190,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
if (renderer->getFeatures().asyncCommandQueue.enabled) if (renderer->getFeatures().asyncCommandQueue.enabled)
{ {
// TODO: https://issuetracker.google.com/170312581 - For now just stalling here // TODO: https://issuetracker.google.com/170312581 - For now just stalling here
renderer->waitForCommandProcessorIdle(contextVk); ANGLE_TRY(renderer->waitForCommandProcessorIdle(contextVk));
} }
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk)); ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk));
......
...@@ -511,15 +511,16 @@ void RendererVk::releaseSharedResources(vk::ResourceUseList *resourceList) ...@@ -511,15 +511,16 @@ void RendererVk::releaseSharedResources(vk::ResourceUseList *resourceList)
void RendererVk::onDestroy(vk::Context *context) void RendererVk::onDestroy(vk::Context *context)
{ {
if (mFeatures.asyncCommandQueue.enabled)
{
// Shutdown worker thread
mCommandProcessor.shutdown(&mCommandProcessorThread);
}
else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex); std::lock_guard<std::mutex> lock(mCommandQueueMutex);
mCommandQueue.destroy(this); if (mFeatures.asyncCommandQueue.enabled)
{
mCommandProcessor.destroy(context);
}
else
{
mCommandQueue.destroy(context);
}
} }
// Assigns an infinite "last completed" serial to force garbage to delete. // Assigns an infinite "last completed" serial to force garbage to delete.
...@@ -1495,9 +1496,7 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1495,9 +1496,7 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
mCommandProcessorThread = ANGLE_TRY(mCommandProcessor.init(displayVk, queueMap));
std::thread(&vk::CommandProcessor::processTasks, &mCommandProcessor, queueMap);
waitForCommandProcessorIdle(displayVk);
} }
else else
{ {
...@@ -2237,37 +2236,21 @@ angle::Result RendererVk::queueSubmitOneOff(vk::Context *context, ...@@ -2237,37 +2236,21 @@ angle::Result RendererVk::queueSubmitOneOff(vk::Context *context,
Serial *serialOut) Serial *serialOut)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queueSubmitOneOff"); ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queueSubmitOneOff");
Serial submitQueueSerial;
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
Serial submitQueueSerial;
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
submitQueueSerial = mCommandProcessor.reserveSubmitSerial(); submitQueueSerial = mCommandProcessor.reserveSubmitSerial();
ANGLE_TRY(mCommandProcessor.queueSubmitOneOff(context, priority, primary.getHandle(), fence,
vk::CommandProcessorTask oneOffQueueSubmit; submitQueueSerial));
oneOffQueueSubmit.initOneOffQueueSubmit(primary.getHandle(), priority, fence,
submitQueueSerial);
queueCommand(context, &oneOffQueueSubmit);
// TODO: https://issuetracker.google.com/170312581 - should go away with improved fence
// management
waitForCommandProcessorIdle(context);
} }
else else
{ {
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
if (primary.valid())
{
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = primary.ptr();
}
submitQueueSerial = mCommandQueue.reserveSubmitSerial(); submitQueueSerial = mCommandQueue.reserveSubmitSerial();
ANGLE_TRY( ANGLE_TRY(mCommandQueue.queueSubmitOneOff(context, priority, primary.getHandle(), fence,
mCommandQueue.queueSubmit(context, priority, submitInfo, fence, submitQueueSerial)); submitQueueSerial));
} }
*serialOut = submitQueueSerial; *serialOut = submitQueueSerial;
...@@ -2497,25 +2480,6 @@ angle::Result RendererVk::getCommandBufferOneOff(vk::Context *context, ...@@ -2497,25 +2480,6 @@ angle::Result RendererVk::getCommandBufferOneOff(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
void RendererVk::commandProcessorSyncErrors(vk::Context *context)
{
while (hasPendingError())
{
vk::Error error = getAndClearPendingError();
if (error.mErrorCode != VK_SUCCESS)
{
context->handleError(error.mErrorCode, error.mFile, error.mFunction, error.mLine);
}
}
}
void RendererVk::commandProcessorSyncErrorsAndQueueCommand(vk::Context *context,
vk::CommandProcessorTask *command)
{
commandProcessorSyncErrors(context);
queueCommand(context, command);
}
angle::Result RendererVk::submitFrame(vk::Context *context, angle::Result RendererVk::submitFrame(vk::Context *context,
egl::ContextPriority contextPriority, egl::ContextPriority contextPriority,
std::vector<VkSemaphore> &&waitSemaphores, std::vector<VkSemaphore> &&waitSemaphores,
...@@ -2525,78 +2489,61 @@ angle::Result RendererVk::submitFrame(vk::Context *context, ...@@ -2525,78 +2489,61 @@ angle::Result RendererVk::submitFrame(vk::Context *context,
vk::GarbageList &&currentGarbage, vk::GarbageList &&currentGarbage,
vk::CommandPool *commandPool) vk::CommandPool *commandPool)
{ {
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
vk::Shared<vk::Fence> submitFence;
ANGLE_TRY(newSharedFence(context, &submitFence));
Serial submitQueueSerial; Serial submitQueueSerial;
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
submitQueueSerial = mCommandProcessor.reserveSubmitSerial(); submitQueueSerial = mCommandProcessor.reserveSubmitSerial();
vk::CommandProcessorTask flushAndQueueSubmit; ANGLE_TRY(mCommandProcessor.submitFrame(
flushAndQueueSubmit.initFlushAndQueueSubmit( context, contextPriority, waitSemaphores, waitSemaphoreStageMasks, signalSemaphore,
std::move(waitSemaphores), std::move(waitSemaphoreStageMasks), signalSemaphore, std::move(submitFence), std::move(currentGarbage), commandPool, submitQueueSerial));
contextPriority, std::move(currentGarbage), submitQueueSerial);
commandProcessorSyncErrorsAndQueueCommand(context, &flushAndQueueSubmit);
} }
else else
{ {
std::lock_guard<std::mutex> commandQueueLock(mCommandQueueMutex);
submitQueueSerial = mCommandQueue.reserveSubmitSerial(); submitQueueSerial = mCommandQueue.reserveSubmitSerial();
vk::Shared<vk::Fence> submitFence;
ANGLE_TRY(newSharedFence(context, &submitFence));
ANGLE_TRY(mCommandQueue.submitFrame( ANGLE_TRY(mCommandQueue.submitFrame(
context, contextPriority, waitSemaphores, waitSemaphoreStageMasks, signalSemaphore, context, contextPriority, waitSemaphores, waitSemaphoreStageMasks, signalSemaphore,
std::move(submitFence), std::move(currentGarbage), commandPool, submitQueueSerial)); std::move(submitFence), std::move(currentGarbage), commandPool, submitQueueSerial));
waitSemaphores.clear();
waitSemaphoreStageMasks.clear();
} }
waitSemaphores.clear();
waitSemaphoreStageMasks.clear();
resourceUseList.releaseResourceUsesAndUpdateSerials(submitQueueSerial); resourceUseList.releaseResourceUsesAndUpdateSerials(submitQueueSerial);
return angle::Result::Continue; return angle::Result::Continue;
} }
void RendererVk::clearAllGarbage(vk::Context *context)
{
if (mFeatures.asyncCommandQueue.enabled)
{
// Issue command to CommandProcessor to ensure all work is complete, which will return any
// garbage items as well.
finishAllWork(context);
}
else
{
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
mCommandQueue.clearAllGarbage(this);
}
}
void RendererVk::handleDeviceLost() void RendererVk::handleDeviceLost()
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
mCommandProcessor.handleDeviceLost(); mCommandProcessor.handleDeviceLost(this);
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
mCommandQueue.handleDeviceLost(this); mCommandQueue.handleDeviceLost(this);
} }
} }
angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial) angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial)
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
mCommandProcessor.finishToSerial(context, serial); ANGLE_TRY(mCommandProcessor.finishToSerial(context, serial, getMaxFenceWaitTimeNs()));
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
ANGLE_TRY(mCommandQueue.finishToSerial(context, serial, getMaxFenceWaitTimeNs())); ANGLE_TRY(mCommandQueue.finishToSerial(context, serial, getMaxFenceWaitTimeNs()));
} }
...@@ -2609,14 +2556,14 @@ angle::Result RendererVk::waitForSerialWithUserTimeout(vk::Context *context, ...@@ -2609,14 +2556,14 @@ angle::Result RendererVk::waitForSerialWithUserTimeout(vk::Context *context,
VkResult *result) VkResult *result)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::waitForSerialWithUserTimeout"); ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::waitForSerialWithUserTimeout");
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
// TODO: https://issuetracker.google.com/170312581 - Wait with timeout. ANGLE_TRY(mCommandProcessor.waitForSerialWithUserTimeout(context, serial, timeout, result));
mCommandProcessor.finishToSerial(context, serial);
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
ANGLE_TRY(mCommandQueue.waitForSerialWithUserTimeout(context, serial, timeout, result)); ANGLE_TRY(mCommandQueue.waitForSerialWithUserTimeout(context, serial, timeout, result));
} }
...@@ -2630,16 +2577,16 @@ angle::Result RendererVk::finish(vk::Context *context) ...@@ -2630,16 +2577,16 @@ angle::Result RendererVk::finish(vk::Context *context)
angle::Result RendererVk::checkCompletedCommands(vk::Context *context) angle::Result RendererVk::checkCompletedCommands(vk::Context *context)
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
// TODO: https://issuetracker.google.com/169788986 - would be better if we could just wait
// for the work we need but that requires QueryHelper to use the actual serial for the
// query.
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
// TODO: https://issuetracker.google.com/169788986 - would be better if we could just wait ANGLE_TRY(mCommandProcessor.checkCompletedCommands(context));
// for the work we need but that requires QueryHelper to use the actual serial for the
// query.
mCommandProcessor.checkCompletedCommands(context);
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
ANGLE_TRY(mCommandQueue.checkCompletedCommands(context)); ANGLE_TRY(mCommandQueue.checkCompletedCommands(context));
} }
...@@ -2651,18 +2598,16 @@ angle::Result RendererVk::flushRenderPassCommands(vk::Context *context, ...@@ -2651,18 +2598,16 @@ angle::Result RendererVk::flushRenderPassCommands(vk::Context *context,
vk::CommandBufferHelper **renderPassCommands) vk::CommandBufferHelper **renderPassCommands)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::flushRenderPassCommands"); ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::flushRenderPassCommands");
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
(*renderPassCommands)->markClosed(); ANGLE_TRY(
vk::CommandProcessorTask flushToPrimary; mCommandProcessor.flushRenderPassCommands(context, renderPass, renderPassCommands));
flushToPrimary.initProcessCommands(*renderPassCommands, &renderPass);
commandProcessorSyncErrorsAndQueueCommand(context, &flushToPrimary);
*renderPassCommands = getCommandBufferHelper(true);
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex); ANGLE_TRY(mCommandQueue.flushRenderPassCommands(context, renderPass, renderPassCommands));
ANGLE_TRY(mCommandQueue.flushRenderPassCommands(context, renderPass, *renderPassCommands));
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -2672,18 +2617,15 @@ angle::Result RendererVk::flushOutsideRPCommands(vk::Context *context, ...@@ -2672,18 +2617,15 @@ angle::Result RendererVk::flushOutsideRPCommands(vk::Context *context,
vk::CommandBufferHelper **outsideRPCommands) vk::CommandBufferHelper **outsideRPCommands)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::flushOutsideRPCommands"); ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::flushOutsideRPCommands");
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
(*outsideRPCommands)->markClosed(); ANGLE_TRY(mCommandProcessor.flushOutsideRPCommands(context, outsideRPCommands));
vk::CommandProcessorTask flushToPrimary;
flushToPrimary.initProcessCommands(*outsideRPCommands, nullptr);
commandProcessorSyncErrorsAndQueueCommand(context, &flushToPrimary);
*outsideRPCommands = getCommandBufferHelper(false);
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex); ANGLE_TRY(mCommandQueue.flushOutsideRPCommands(context, outsideRPCommands));
ANGLE_TRY(mCommandQueue.flushOutsideRPCommands(context, *outsideRPCommands));
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -2693,22 +2635,15 @@ VkResult RendererVk::queuePresent(vk::Context *context, ...@@ -2693,22 +2635,15 @@ VkResult RendererVk::queuePresent(vk::Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
const VkPresentInfoKHR &presentInfo) const VkPresentInfoKHR &presentInfo)
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
VkResult result = VK_SUCCESS; VkResult result = VK_SUCCESS;
if (mFeatures.asyncCommandQueue.enabled) if (mFeatures.asyncCommandQueue.enabled)
{ {
vk::CommandProcessorTask present; result = mCommandProcessor.queuePresent(priority, presentInfo);
present.initPresent(priority, presentInfo);
ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
queueCommand(context, &present);
// Always return success, when we call acquireNextImage we'll check the return code. This
// allows the app to continue working until we really need to know the return code from
// present.
} }
else else
{ {
std::lock_guard<std::mutex> lock(mCommandQueueMutex);
result = mCommandQueue.queuePresent(priority, presentInfo); result = mCommandQueue.queuePresent(priority, presentInfo);
} }
......
...@@ -281,21 +281,20 @@ class RendererVk : angle::NonCopyable ...@@ -281,21 +281,20 @@ class RendererVk : angle::NonCopyable
SamplerYcbcrConversionCache &getYuvConversionCache() { return mYuvConversionCache; } SamplerYcbcrConversionCache &getYuvConversionCache() { return mYuvConversionCache; }
vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; } vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; }
// Queue commands to worker thread for processing // TODO(jmadill): Remove. b/172704839
void queueCommand(vk::Context *context, vk::CommandProcessorTask *command) angle::Result waitForCommandProcessorIdle(vk::Context *context)
{ {
mCommandProcessor.queueCommand(context, command); ASSERT(getFeatures().asyncCommandQueue.enabled);
return mCommandProcessor.waitForWorkComplete(context);
} }
bool hasPendingError() const { return mCommandProcessor.hasPendingError(); }
vk::Error getAndClearPendingError() { return mCommandProcessor.getAndClearPendingError(); } // TODO(jmadill): Remove. b/172704839
void waitForCommandProcessorIdle(vk::Context *context) angle::Result finishAllWork(vk::Context *context)
{ {
ASSERT(getFeatures().asyncCommandQueue.enabled); ASSERT(getFeatures().asyncCommandQueue.enabled);
mCommandProcessor.waitForWorkComplete(context); return mCommandProcessor.finishAllWork(context);
} }
void finishAllWork(vk::Context *context) { mCommandProcessor.finishAllWork(context); }
bool getEnableValidationLayers() const { return mEnableValidationLayers; } bool getEnableValidationLayers() const { return mEnableValidationLayers; }
vk::ResourceSerialFactory &getResourceSerialFactory() { return mResourceSerialFactory; } vk::ResourceSerialFactory &getResourceSerialFactory() { return mResourceSerialFactory; }
...@@ -315,7 +314,6 @@ class RendererVk : angle::NonCopyable ...@@ -315,7 +314,6 @@ class RendererVk : angle::NonCopyable
vk::GarbageList &&currentGarbage, vk::GarbageList &&currentGarbage,
vk::CommandPool *commandPool); vk::CommandPool *commandPool);
void clearAllGarbage(vk::Context *context);
void handleDeviceLost(); void handleDeviceLost();
angle::Result finishToSerial(vk::Context *context, Serial serial); angle::Result finishToSerial(vk::Context *context, Serial serial);
angle::Result waitForSerialWithUserTimeout(vk::Context *context, angle::Result waitForSerialWithUserTimeout(vk::Context *context,
...@@ -357,12 +355,6 @@ class RendererVk : angle::NonCopyable ...@@ -357,12 +355,6 @@ class RendererVk : angle::NonCopyable
template <VkFormatFeatureFlags VkFormatProperties::*features> template <VkFormatFeatureFlags VkFormatProperties::*features>
bool hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const; bool hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
// Sync any errors from the command processor
void commandProcessorSyncErrors(vk::Context *context);
// Sync any error from worker thread and queue up next command for processing
void commandProcessorSyncErrorsAndQueueCommand(vk::Context *context,
vk::CommandProcessorTask *command);
egl::Display *mDisplay; egl::Display *mDisplay;
mutable bool mCapsInitialized; mutable bool mCapsInitialized;
...@@ -456,9 +448,8 @@ class RendererVk : angle::NonCopyable ...@@ -456,9 +448,8 @@ class RendererVk : angle::NonCopyable
std::mutex mCommandBufferHelperFreeListMutex; std::mutex mCommandBufferHelperFreeListMutex;
std::vector<vk::CommandBufferHelper *> mCommandBufferHelperFreeList; std::vector<vk::CommandBufferHelper *> mCommandBufferHelperFreeList;
// Command Processor Thread // Async Command Queue
vk::CommandProcessor mCommandProcessor; vk::CommandProcessor mCommandProcessor;
std::thread mCommandProcessorThread;
// mNextSubmitFence is the fence that's going to be signaled at the next submission. This is // mNextSubmitFence is the fence that's going to be signaled at the next submission. This is
// used to support SyncVk objects, which may outlive the context (as EGLSync objects). // used to support SyncVk objects, which may outlive the context (as EGLSync objects).
vk::Shared<vk::Fence> mNextSubmitFence; vk::Shared<vk::Fence> mNextSubmitFence;
......
...@@ -37,7 +37,7 @@ void SyncHelper::releaseToRenderer(RendererVk *renderer) ...@@ -37,7 +37,7 @@ void SyncHelper::releaseToRenderer(RendererVk *renderer)
if (renderer->getFeatures().asyncCommandQueue.enabled) if (renderer->getFeatures().asyncCommandQueue.enabled)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelper::releaseToRenderer"); ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelper::releaseToRenderer");
renderer->waitForCommandProcessorIdle(nullptr); (void)renderer->waitForCommandProcessorIdle(nullptr);
} }
} }
...@@ -272,7 +272,7 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context, ...@@ -272,7 +272,7 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context,
if (contextVk->getRenderer()->getFeatures().asyncCommandQueue.enabled) if (contextVk->getRenderer()->getFeatures().asyncCommandQueue.enabled)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelperNativeFence::clientWait"); ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelperNativeFence::clientWait");
contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk); ANGLE_TRY(contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk));
} }
// Wait for mFenceWithFd to be signaled. // Wait for mFenceWithFd to be signaled.
......
...@@ -156,10 +156,10 @@ void AddToPNextChain(VulkanStruct1 *chainStart, VulkanStruct2 *ptr) ...@@ -156,10 +156,10 @@ void AddToPNextChain(VulkanStruct1 *chainStart, VulkanStruct2 *ptr)
struct Error struct Error
{ {
VkResult mErrorCode; VkResult errorCode;
const char *mFile; const char *file;
const char *mFunction; const char *function;
unsigned int mLine; uint32_t line;
}; };
// Abstracts error handling. Implemented by both ContextVk for GL and DisplayVk for EGL errors. // Abstracts error handling. Implemented by both ContextVk for GL and DisplayVk for EGL errors.
......
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