Commit ed876984 by Courtney Goeltzenleuchter Committed by Commit Bot

Vulkan: functionally complete worker thread

Working on enhancing worker thread to completely own primary command buffers. This will include not only processing SCBs from main thread into a primary, but also submitting those command buffers to the queue. The CommandProcessor is a vk::Context so it can handle errors in the worker thread. When the main thread submits tasks to the worker thread it also syncs any outstanding errors from the worker. Include asynchronousCommandProcessing feature that will control whether the worker thread task does it's work in parallel or not. If false, we wait for the thread to complete it's work before letting the main thread continue. If true, the thread can execute in parallel with the main thread. Bug: b/154030730 Bug: b/161912801 Change-Id: I00f8f013d6cbb2af12a172c4f7927855db2f0ebf Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2328992 Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 2882e1af
...@@ -343,6 +343,13 @@ struct FeaturesVk : FeatureSetBase ...@@ -343,6 +343,13 @@ struct FeaturesVk : FeatureSetBase
"Enable parallel processing and submission of Vulkan commands in worker thread", &members, "Enable parallel processing and submission of Vulkan commands in worker thread", &members,
"http://anglebug.com/4324"}; "http://anglebug.com/4324"};
// Enable parallel thread execution when enableCommandProcessingThread is enabled.
// Currently off by default.
Feature asynchronousCommandProcessing = {"asynchronous_command_processing",
FeatureCategory::VulkanFeatures,
"Enable/Disable parallel processing of worker thread",
&members, "http://anglebug.com/4324"};
// Whether the VkDevice supports the VK_KHR_shader_float16_int8 extension and has the // Whether the VkDevice supports the VK_KHR_shader_float16_int8 extension and has the
// shaderFloat16 feature. // shaderFloat16 feature.
Feature supportsShaderFloat16 = {"supports_shader_float16", FeatureCategory::VulkanFeatures, Feature supportsShaderFloat16 = {"supports_shader_float16", FeatureCategory::VulkanFeatures,
......
...@@ -47,7 +47,6 @@ class CommandQueue final : angle::NonCopyable ...@@ -47,7 +47,6 @@ class CommandQueue final : angle::NonCopyable
bool hasInFlightCommands() const; bool hasInFlightCommands() const;
angle::Result allocatePrimaryCommandBuffer(vk::Context *context, angle::Result allocatePrimaryCommandBuffer(vk::Context *context,
const vk::CommandPool &commandPool,
vk::PrimaryCommandBuffer *commandBufferOut); vk::PrimaryCommandBuffer *commandBufferOut);
angle::Result releasePrimaryCommandBuffer(vk::Context *context, angle::Result releasePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer); vk::PrimaryCommandBuffer &&commandBuffer);
...@@ -653,11 +652,10 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -653,11 +652,10 @@ class ContextVk : public ContextImpl, public vk::Context
void updateOverlayOnPresent(); void updateOverlayOnPresent();
void addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer); void addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer);
// Submit commands to worker thread for processing // Sync any errors from the command processor
ANGLE_INLINE void queueCommandsToWorker(const vk::CommandProcessorTask &commands) void commandProcessorSyncErrors();
{ // Sync any error from worker thread and queue up next command for processing
mRenderer->queueCommands(commands); void commandProcessorSyncErrorsAndQueueCommand(vk::CommandProcessorTask *command);
}
// When worker thread completes, it releases command buffers back to context queue // When worker thread completes, it releases command buffers back to context queue
void recycleCommandBuffer(vk::CommandBufferHelper *commandBuffer); void recycleCommandBuffer(vk::CommandBufferHelper *commandBuffer);
...@@ -1085,6 +1083,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -1085,6 +1083,7 @@ class ContextVk : public ContextImpl, public vk::Context
// We use a single pool for recording commands. We also keep a free list for pool recycling. // We use a single pool for recording commands. We also keep a free list for pool recycling.
vk::CommandPool mCommandPool; vk::CommandPool mCommandPool;
// TODO: This can be killed once threading is enabled https://issuetracker.google.com/153666475
CommandQueue mCommandQueue; CommandQueue mCommandQueue;
vk::GarbageList mCurrentGarbage; vk::GarbageList mCurrentGarbage;
......
...@@ -180,18 +180,30 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -180,18 +180,30 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// finite time. // finite time.
// Note regarding time-elapsed: end should have been called after begin, so flushing when end // Note regarding time-elapsed: end should have been called after begin, so flushing when end
// has pending work should flush begin too. // has pending work should flush begin too.
if (mQueryHelper.hasPendingWork(contextVk)) // TODO: https://issuetracker.google.com/169788986 - can't guarantee hasPendingWork() works when
// using threaded worker
if (mQueryHelper.hasPendingWork(contextVk) ||
contextVk->getRenderer()->getFeatures().enableCommandProcessingThread.enabled)
{ {
ANGLE_TRY(contextVk->flushImpl(nullptr)); ANGLE_TRY(contextVk->flushImpl(nullptr));
if (contextVk->getRenderer()->getFeatures().enableCommandProcessingThread.enabled)
{
// TODO: https://issuetracker.google.com/170312581 - For now just stalling here
contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk);
}
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk)); ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk));
ASSERT(!mQueryHelper.hasPendingWork(contextVk)); ASSERT(!mQueryHelper.hasPendingWork(contextVk));
} }
// If the command buffer this query is being written to is still in flight, its reset command if (!contextVk->getRenderer()->getFeatures().enableCommandProcessingThread.enabled)
// may not have been performed by the GPU yet. To avoid a race condition in this case, wait {
// for the batch to finish first before querying (or return not-ready if not waiting). // If the command buffer this query is being written to is still in flight, its reset
ANGLE_TRY(contextVk->checkCompletedCommands()); // command may not have been performed by the GPU yet. To avoid a race condition in this
// case, wait for the batch to finish first before querying (or return not-ready if not
// waiting).
ANGLE_TRY(contextVk->checkCompletedCommands());
}
if (contextVk->isSerialInUse(mQueryHelper.getStoredQueueSerial())) if (contextVk->isSerialInUse(mQueryHelper.getStoredQueueSerial()))
{ {
if (!wait) if (!wait)
......
...@@ -467,6 +467,7 @@ RendererVk::RendererVk() ...@@ -467,6 +467,7 @@ RendererVk::RendererVk()
mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod), mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod),
mPipelineCacheDirty(false), mPipelineCacheDirty(false),
mPipelineCacheInitialized(false), mPipelineCacheInitialized(false),
mCommandProcessor(this),
mGlslangInitialized(false) mGlslangInitialized(false)
{ {
VkFormatProperties invalid = {0, 0, kInvalidFormatFeatureFlags}; VkFormatProperties invalid = {0, 0, kInvalidFormatFeatureFlags};
...@@ -536,6 +537,10 @@ void RendererVk::onDestroy() ...@@ -536,6 +537,10 @@ void RendererVk::onDestroy()
std::lock_guard<std::mutex> lock(mFenceRecyclerMutex); std::lock_guard<std::mutex> lock(mFenceRecyclerMutex);
mFenceRecycler.destroy(mDevice); mFenceRecycler.destroy(mDevice);
} }
{
std::lock_guard<decltype(mNextSubmitFenceMutex)> lock(mNextSubmitFenceMutex);
mNextSubmitFence.reset(mDevice);
}
mPipelineCache.destroy(mDevice); mPipelineCache.destroy(mDevice);
mSamplerCache.destroy(this); mSamplerCache.destroy(this);
...@@ -580,7 +585,7 @@ void RendererVk::notifyDeviceLost() ...@@ -580,7 +585,7 @@ void RendererVk::notifyDeviceLost()
{ {
{ {
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
mLastCompletedQueueSerial = mLastSubmittedQueueSerial; mLastCompletedQueueSerial = getLastSubmittedQueueSerial();
} }
mDeviceLost = true; mDeviceLost = true;
mDisplay->notifyDeviceLost(); mDisplay->notifyDeviceLost();
...@@ -909,7 +914,8 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk, ...@@ -909,7 +914,8 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk,
if (getFeatures().enableCommandProcessingThread.enabled) if (getFeatures().enableCommandProcessingThread.enabled)
{ {
mCommandProcessorThread = mCommandProcessorThread =
std::thread(&CommandProcessor::processCommandProcessorTasks, &mCommandProcessor); std::thread(&vk::CommandProcessor::processTasks, &mCommandProcessor);
mCommandProcessor.waitForWorkComplete(nullptr);
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1907,6 +1913,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1907,6 +1913,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
// Currently disabled by default: http://anglebug.com/4324 // Currently disabled by default: http://anglebug.com/4324
ANGLE_FEATURE_CONDITION(&mFeatures, enableCommandProcessingThread, false); ANGLE_FEATURE_CONDITION(&mFeatures, enableCommandProcessingThread, false);
// Currently disabled by default: http://anglebug.com/4324
ANGLE_FEATURE_CONDITION(&mFeatures, asynchronousCommandProcessing, false);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion, ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion,
mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE); mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE);
...@@ -2183,6 +2192,17 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, ...@@ -2183,6 +2192,17 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format,
return hasFormatFeatureBits<&VkFormatProperties::bufferFeatures>(format, featureBits); return hasFormatFeatureBits<&VkFormatProperties::bufferFeatures>(format, featureBits);
} }
void RendererVk::outputVmaStatString()
{
// Output the VMA stats string
// This JSON string can be passed to VmaDumpVis.py to generate a visualization of the
// allocations the VMA has performed.
char *statsString;
mAllocator.buildStatsString(&statsString, true);
INFO() << std::endl << statsString << std::endl;
mAllocator.freeStatsString(statsString);
}
angle::Result RendererVk::queueSubmit(vk::Context *context, angle::Result RendererVk::queueSubmit(vk::Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
...@@ -2192,23 +2212,11 @@ angle::Result RendererVk::queueSubmit(vk::Context *context, ...@@ -2192,23 +2212,11 @@ angle::Result RendererVk::queueSubmit(vk::Context *context,
{ {
if (kOutputVmaStatsString) if (kOutputVmaStatsString)
{ {
// Output the VMA stats string outputVmaStatString();
// This JSON string can be passed to VmaDumpVis.py to generate a visualization of the
// allocations the VMA has performed.
char *statsString;
mAllocator.buildStatsString(&statsString, true);
INFO() << std::endl << statsString << std::endl;
mAllocator.freeStatsString(statsString);
} }
if (getFeatures().enableCommandProcessingThread.enabled) ASSERT(!getFeatures().enableCommandProcessingThread.enabled);
{
// For initial threading phase 1 code make sure any outstanding command processing
// is complete.
// TODO: b/153666475 For phase2 investigate if this is required as most submits will take
// place through worker thread except for one-off submits below.
mCommandProcessor.waitForWorkComplete();
}
{ {
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
std::lock_guard<std::mutex> serialLock(mQueueSerialMutex); std::lock_guard<std::mutex> serialLock(mQueueSerialMutex);
...@@ -2235,12 +2243,26 @@ angle::Result RendererVk::queueSubmitOneOff(vk::Context *context, ...@@ -2235,12 +2243,26 @@ angle::Result RendererVk::queueSubmitOneOff(vk::Context *context,
const vk::Fence *fence, const vk::Fence *fence,
Serial *serialOut) Serial *serialOut)
{ {
VkSubmitInfo submitInfo = {}; if (getFeatures().enableCommandProcessingThread.enabled)
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; {
submitInfo.commandBufferCount = 1; vk::CommandProcessorTask oneOffQueueSubmit;
submitInfo.pCommandBuffers = primary.ptr(); oneOffQueueSubmit.initOneOffQueueSubmit(primary.getHandle(), priority, fence);
queueCommand(context, &oneOffQueueSubmit);
waitForCommandProcessorIdle(context);
*serialOut = getLastSubmittedQueueSerial();
ANGLE_TRY(cleanupGarbage(false));
}
else
{
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = primary.ptr();
ANGLE_TRY(queueSubmit(context, priority, submitInfo, nullptr, fence, serialOut)); ANGLE_TRY(queueSubmit(context, priority, submitInfo, nullptr, fence, serialOut));
}
mPendingOneOffCommands.push_back({*serialOut, std::move(primary)}); mPendingOneOffCommands.push_back({*serialOut, std::move(primary)});
...@@ -2251,8 +2273,8 @@ angle::Result RendererVk::queueWaitIdle(vk::Context *context, egl::ContextPriori ...@@ -2251,8 +2273,8 @@ angle::Result RendererVk::queueWaitIdle(vk::Context *context, egl::ContextPriori
{ {
if (getFeatures().enableCommandProcessingThread.enabled) if (getFeatures().enableCommandProcessingThread.enabled)
{ {
// First make sure command processor is complete when waiting for queue idle. // Wait for all pending commands to get sent before issuing vkQueueWaitIdle
mCommandProcessor.waitForWorkComplete(); waitForCommandProcessorIdle(context);
} }
{ {
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
...@@ -2268,8 +2290,8 @@ angle::Result RendererVk::deviceWaitIdle(vk::Context *context) ...@@ -2268,8 +2290,8 @@ angle::Result RendererVk::deviceWaitIdle(vk::Context *context)
{ {
if (getFeatures().enableCommandProcessingThread.enabled) if (getFeatures().enableCommandProcessingThread.enabled)
{ {
// First make sure command processor is complete when waiting for device idle. // Wait for all pending commands to get sent before issuing vkQueueWaitIdle
mCommandProcessor.waitForWorkComplete(); waitForCommandProcessorIdle(context);
} }
{ {
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
...@@ -2286,12 +2308,7 @@ VkResult RendererVk::queuePresent(egl::ContextPriority priority, ...@@ -2286,12 +2308,7 @@ VkResult RendererVk::queuePresent(egl::ContextPriority priority,
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queuePresent"); ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queuePresent");
if (getFeatures().enableCommandProcessingThread.enabled) ASSERT(!getFeatures().enableCommandProcessingThread.enabled);
{
// First make sure command processor is complete before queue present as
// present may have dependencies on that thread.
mCommandProcessor.waitForWorkComplete();
}
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
...@@ -2329,6 +2346,30 @@ angle::Result RendererVk::newSharedFence(vk::Context *context, ...@@ -2329,6 +2346,30 @@ angle::Result RendererVk::newSharedFence(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
// Return a shared fence to be used for the next submit
// Fence may be shared with a Sync object.
// reset indicates that nextSubmitFence should be reset before returning. This ensures that the next
// request for a submit fence gets a fresh fence.
// TODO: https://issuetracker.google.com/issues/170312581 - move to CommandProcessor as part of
// fence ownership follow-up task.
angle::Result RendererVk::getNextSubmitFence(vk::Shared<vk::Fence> *sharedFenceOut, bool reset)
{
std::lock_guard<decltype(mNextSubmitFenceMutex)> lock(mNextSubmitFenceMutex);
if (!mNextSubmitFence.isReferenced())
{
ANGLE_TRY(newSharedFence(&mCommandProcessor, &mNextSubmitFence));
}
ASSERT(!sharedFenceOut->isReferenced());
sharedFenceOut->copy(getDevice(), mNextSubmitFence);
if (reset)
{
resetSharedFence(&mNextSubmitFence);
}
return angle::Result::Continue;
}
template <VkFormatFeatureFlags VkFormatProperties::*features> template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format, VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const const VkFormatFeatureFlags featureBits) const
......
...@@ -168,6 +168,7 @@ class RendererVk : angle::NonCopyable ...@@ -168,6 +168,7 @@ class RendererVk : angle::NonCopyable
return mPriorities[priority]; return mPriorities[priority];
} }
// Queue submit that originates from the main thread
angle::Result queueSubmit(vk::Context *context, angle::Result queueSubmit(vk::Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
...@@ -197,6 +198,8 @@ class RendererVk : angle::NonCopyable ...@@ -197,6 +198,8 @@ class RendererVk : angle::NonCopyable
sharedFenceIn->resetAndRecycle(&mFenceRecycler); sharedFenceIn->resetAndRecycle(&mFenceRecycler);
} }
angle::Result getNextSubmitFence(vk::Shared<vk::Fence> *sharedFenceOut, bool reset);
template <typename... ArgsT> template <typename... ArgsT>
void collectGarbageAndReinit(vk::SharedResourceUse *use, ArgsT... garbageIn) void collectGarbageAndReinit(vk::SharedResourceUse *use, ArgsT... garbageIn)
{ {
...@@ -224,6 +227,12 @@ class RendererVk : angle::NonCopyable ...@@ -224,6 +227,12 @@ class RendererVk : angle::NonCopyable
} }
} }
vk::Shared<vk::Fence> getLastSubmittedFence() const
{
return mCommandProcessor.getLastSubmittedFence();
}
void handleDeviceLost() { mCommandProcessor.handleDeviceLost(); }
static constexpr size_t kMaxExtensionNames = 200; static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>; using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
...@@ -241,11 +250,19 @@ class RendererVk : angle::NonCopyable ...@@ -241,11 +250,19 @@ class RendererVk : angle::NonCopyable
ANGLE_INLINE Serial getCurrentQueueSerial() ANGLE_INLINE Serial getCurrentQueueSerial()
{ {
if (getFeatures().enableCommandProcessingThread.enabled)
{
return mCommandProcessor.getCurrentQueueSerial();
}
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mCurrentQueueSerial; return mCurrentQueueSerial;
} }
ANGLE_INLINE Serial getLastSubmittedQueueSerial() ANGLE_INLINE Serial getLastSubmittedQueueSerial()
{ {
if (getFeatures().enableCommandProcessingThread.enabled)
{
return mCommandProcessor.getLastSubmittedSerial();
}
std::lock_guard<std::mutex> lock(mQueueSerialMutex); std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mLastSubmittedQueueSerial; return mLastSubmittedQueueSerial;
} }
...@@ -264,11 +281,24 @@ class RendererVk : angle::NonCopyable ...@@ -264,11 +281,24 @@ class RendererVk : angle::NonCopyable
vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; } vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; }
// Queue commands to worker thread for processing // Queue commands to worker thread for processing
void queueCommands(const vk::CommandProcessorTask &commands) void queueCommand(vk::Context *context, vk::CommandProcessorTask *command)
{ {
mCommandProcessor.queueCommands(commands); mCommandProcessor.queueCommand(context, command);
}
bool hasPendingError() const { return mCommandProcessor.hasPendingError(); }
vk::Error getAndClearPendingError() { return mCommandProcessor.getAndClearPendingError(); }
void waitForCommandProcessorIdle(vk::Context *context)
{
mCommandProcessor.waitForWorkComplete(context);
} }
void waitForWorkerThreadIdle() { mCommandProcessor.waitForWorkComplete(); }
void finishToSerial(vk::Context *context, Serial serial)
{
mCommandProcessor.finishToSerial(context, serial);
}
void finishAllWork(vk::Context *context) { mCommandProcessor.finishAllWork(context); }
VkQueue getVkQueue(egl::ContextPriority priority) const { return mQueues[priority]; }
bool getEnableValidationLayers() const { return mEnableValidationLayers; } bool getEnableValidationLayers() const { return mEnableValidationLayers; }
...@@ -276,6 +306,8 @@ class RendererVk : angle::NonCopyable ...@@ -276,6 +306,8 @@ class RendererVk : angle::NonCopyable
void setGlobalDebugAnnotator(); void setGlobalDebugAnnotator();
void outputVmaStatString();
private: private:
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex); angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
...@@ -390,9 +422,13 @@ class RendererVk : angle::NonCopyable ...@@ -390,9 +422,13 @@ class RendererVk : angle::NonCopyable
}; };
std::deque<PendingOneOffCommands> mPendingOneOffCommands; std::deque<PendingOneOffCommands> mPendingOneOffCommands;
// Worker Thread // Command Processor Thread
CommandProcessor mCommandProcessor; vk::CommandProcessor mCommandProcessor;
std::thread mCommandProcessorThread; std::thread mCommandProcessorThread;
// 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).
vk::Shared<vk::Fence> mNextSubmitFence;
std::mutex mNextSubmitFenceMutex;
// track whether we initialized (or released) glslang // track whether we initialized (or released) glslang
bool mGlslangInitialized; bool mGlslangInitialized;
......
...@@ -102,11 +102,22 @@ ResourceUseList::ResourceUseList() ...@@ -102,11 +102,22 @@ ResourceUseList::ResourceUseList()
mResourceUses.reserve(kDefaultResourceUseCount); mResourceUses.reserve(kDefaultResourceUseCount);
} }
ResourceUseList::ResourceUseList(ResourceUseList &&other)
{
*this = std::move(other);
}
ResourceUseList::~ResourceUseList() ResourceUseList::~ResourceUseList()
{ {
ASSERT(mResourceUses.empty()); ASSERT(mResourceUses.empty());
} }
ResourceUseList &ResourceUseList::operator=(ResourceUseList &&rhs)
{
std::swap(mResourceUses, rhs.mResourceUses);
return *this;
}
void ResourceUseList::releaseResourceUses() void ResourceUseList::releaseResourceUses()
{ {
for (SharedResourceUse &use : mResourceUses) for (SharedResourceUse &use : mResourceUses)
......
...@@ -136,7 +136,9 @@ class ResourceUseList final : angle::NonCopyable ...@@ -136,7 +136,9 @@ class ResourceUseList final : angle::NonCopyable
{ {
public: public:
ResourceUseList(); ResourceUseList();
ResourceUseList(ResourceUseList &&other);
virtual ~ResourceUseList(); virtual ~ResourceUseList();
ResourceUseList &operator=(ResourceUseList &&rhs);
void add(const SharedResourceUse &resourceUse); void add(const SharedResourceUse &resourceUse);
......
...@@ -1186,6 +1186,7 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk, ...@@ -1186,6 +1186,7 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
bool *presentOutOfDate) bool *presentOutOfDate)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present"); ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
RendererVk *renderer = contextVk->getRenderer();
// Throttle the submissions to avoid getting too far ahead of the GPU. // Throttle the submissions to avoid getting too far ahead of the GPU.
SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex]; SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex];
...@@ -1194,7 +1195,7 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk, ...@@ -1194,7 +1195,7 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
if (swap.sharedFence.isReferenced()) if (swap.sharedFence.isReferenced())
{ {
ANGLE_TRY(swap.waitFence(contextVk)); ANGLE_TRY(swap.waitFence(contextVk));
swap.destroy(contextVk->getRenderer()); swap.destroy(renderer);
} }
} }
...@@ -1316,6 +1317,8 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk, ...@@ -1316,6 +1317,8 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
} }
// Update the swap history for this presentation // Update the swap history for this presentation
// TODO: https://issuetracker.google.com/issues/170312581 - this will force us to flush worker
// queue to get the fence.
swap.sharedFence = contextVk->getLastSubmittedFence(); swap.sharedFence = contextVk->getLastSubmittedFence();
ASSERT(!mAcquireImageSemaphore.valid()); ASSERT(!mAcquireImageSemaphore.valid());
...@@ -1323,13 +1326,47 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk, ...@@ -1323,13 +1326,47 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
mCurrentSwapHistoryIndex = mCurrentSwapHistoryIndex =
mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex; mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
VkResult result = contextVk->getRenderer()->queuePresent(contextVk->getPriority(), presentInfo); VkResult result;
if (renderer->getFeatures().enableCommandProcessingThread.enabled)
{
vk::CommandProcessorTask present;
present.initPresent(contextVk->getPriority(), presentInfo);
// Make sure everything has been submitted (and errors handled)
renderer->waitForCommandProcessorIdle(contextVk);
// Submit queuePresent all by itself (ignoring interference from other threads for now)
renderer->queueCommand(contextVk, &present);
// TODO: https://issuetracker.google.com/issues/170329600 - Just stalling here for now, but
// really want to let main thread continue
// need to figure out how to handle work below off-thread and sync to main
// Also, need to fix lifetime of presentInfo data when main thread continues.
// There is a bunch of work happening after present to deal with swapchain recreation.
// Will that require moving a large chunk of swapImpl to the CommandProcessor?
// That will likely require serializing access to the WindowSurfaceVk object in order
// to have current content.
result = VK_SUCCESS;
// wait for the queuePresent to be submitted and intentionally set the context to nullptr so
// that we can catch any error. Note this doesn't prevent another context from grabbing the
// error. Will be fixed properly in a follow-up as part of present work.
renderer->waitForCommandProcessorIdle(nullptr);
if (renderer->hasPendingError())
{
vk::Error error = renderer->getAndClearPendingError();
result = error.mErrorCode;
}
}
else
{
result = renderer->queuePresent(contextVk->getPriority(), presentInfo);
}
// If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
// continuing. // continuing.
// If VK_SUBOPTIMAL_KHR is returned it's because the device orientation changed and we should // If VK_SUBOPTIMAL_KHR is returned it's because the device orientation changed and we should
// recreate the swapchain with a new window orientation. // recreate the swapchain with a new window orientation.
if (contextVk->getFeatures().enablePreRotateSurfaces.enabled) if (renderer->getFeatures().enablePreRotateSurfaces.enabled)
{ {
// Also check for VK_SUBOPTIMAL_KHR. // Also check for VK_SUBOPTIMAL_KHR.
*presentOutOfDate = ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)); *presentOutOfDate = ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR));
......
...@@ -32,6 +32,12 @@ SyncHelper::~SyncHelper() {} ...@@ -32,6 +32,12 @@ SyncHelper::~SyncHelper() {}
void SyncHelper::releaseToRenderer(RendererVk *renderer) void SyncHelper::releaseToRenderer(RendererVk *renderer)
{ {
renderer->collectGarbageAndReinit(&mUse, &mEvent); renderer->collectGarbageAndReinit(&mUse, &mEvent);
// TODO: https://issuetracker.google.com/170312581 - Currently just stalling on worker thread
// here to try and avoid race condition. If this works, need some alternate solution
if (renderer->getFeatures().enableCommandProcessingThread.enabled)
{
renderer->waitForCommandProcessorIdle(nullptr);
}
mFence.reset(renderer->getDevice()); mFence.reset(renderer->getDevice());
} }
...@@ -48,7 +54,17 @@ angle::Result SyncHelper::initialize(ContextVk *contextVk) ...@@ -48,7 +54,17 @@ angle::Result SyncHelper::initialize(ContextVk *contextVk)
DeviceScoped<Event> event(device); DeviceScoped<Event> event(device);
ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo)); ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
ANGLE_TRY(contextVk->getNextSubmitFence(&mFence)); // TODO: https://issuetracker.google.com/170312581 - For now wait for worker thread to finish
// then get next fence from renderer
if (contextVk->getRenderer()->getFeatures().enableCommandProcessingThread.enabled)
{
contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk);
ANGLE_TRY(contextVk->getRenderer()->getNextSubmitFence(&mFence, false));
}
else
{
ANGLE_TRY(contextVk->getNextSubmitFence(&mFence));
}
mEvent = event.release(); mEvent = event.release();
...@@ -90,9 +106,17 @@ angle::Result SyncHelper::clientWait(Context *context, ...@@ -90,9 +106,17 @@ angle::Result SyncHelper::clientWait(Context *context,
ANGLE_TRY(contextVk->flushImpl(nullptr)); ANGLE_TRY(contextVk->flushImpl(nullptr));
} }
// If we are using worker need to wait for the commands to be issued before waiting on the
// fence.
if (contextVk->getRenderer()->getFeatures().enableCommandProcessingThread.enabled)
{
contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk);
}
// Wait on the fence that's expected to be signaled on the first vkQueueSubmit after // Wait on the fence that's expected to be signaled on the first vkQueueSubmit after
// `initialize` was called. The first fence is the fence created to signal this sync. // `initialize` was called. The first fence is the fence created to signal this sync.
ASSERT(mFence.get().valid()); ASSERT(mFence.get().valid());
// TODO: https://issuetracker.google.com/170312581 - Wait could be command to worker
VkResult status = mFence.get().wait(renderer->getDevice(), timeout); VkResult status = mFence.get().wait(renderer->getDevice(), timeout);
// Check for errors, but don't consider timeout as such. // Check for errors, but don't consider timeout as such.
...@@ -189,11 +213,24 @@ angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int ...@@ -189,11 +213,24 @@ angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int
retain(&contextVk->getResourceUseList()); retain(&contextVk->getResourceUseList());
Serial serialOut; if (renderer->getFeatures().enableCommandProcessingThread.enabled)
VkSubmitInfo submitInfo = {}; {
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; CommandProcessorTask oneOffQueueSubmit;
ANGLE_TRY(renderer->queueSubmit(contextVk, contextVk->getPriority(), submitInfo, nullptr, oneOffQueueSubmit.initOneOffQueueSubmit(VK_NULL_HANDLE, contextVk->getPriority(),
&fence.get(), &serialOut)); &fence.get());
renderer->queueCommand(contextVk, &oneOffQueueSubmit);
// TODO: https://issuetracker.google.com/170312581 - wait for now
renderer->waitForCommandProcessorIdle(contextVk);
}
else
{
Serial serialOut;
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
ANGLE_TRY(renderer->queueSubmit(contextVk, contextVk->getPriority(), submitInfo,
nullptr, &fence.get(), &serialOut));
}
VkFenceGetFdInfoKHR fenceGetFdInfo = {}; VkFenceGetFdInfoKHR fenceGetFdInfo = {};
fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR; fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
...@@ -253,6 +290,14 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context, ...@@ -253,6 +290,14 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context,
{ {
ANGLE_TRY(contextVk->flushImpl(nullptr)); ANGLE_TRY(contextVk->flushImpl(nullptr));
} }
// If we are using worker need to wait for the commands to be issued before waiting on the
// fence.
if (contextVk->getRenderer()->getFeatures().asynchronousCommandProcessing.enabled)
{
contextVk->getRenderer()->waitForCommandProcessorIdle(contextVk);
}
// Wait for mFenceWithFd to be signaled. // Wait for mFenceWithFd to be signaled.
VkResult status = mFenceWithFd.wait(renderer->getDevice(), timeout); VkResult status = mFenceWithFd.wait(renderer->getDevice(), timeout);
......
...@@ -969,6 +969,7 @@ angle::Result UtilsVk::setupProgram(ContextVk *contextVk, ...@@ -969,6 +969,7 @@ angle::Result UtilsVk::setupProgram(ContextVk *contextVk,
vk::PipelineAndSerial *pipelineAndSerial; vk::PipelineAndSerial *pipelineAndSerial;
program->setShader(gl::ShaderType::Compute, fsCsShader); program->setShader(gl::ShaderType::Compute, fsCsShader);
ANGLE_TRY(program->getComputePipeline(contextVk, pipelineLayout.get(), &pipelineAndSerial)); ANGLE_TRY(program->getComputePipeline(contextVk, pipelineLayout.get(), &pipelineAndSerial));
// TODO: https://issuetracker.google.com/issues/169788986: Update serial handling.
pipelineAndSerial->updateSerial(serial); pipelineAndSerial->updateSerial(serial);
commandBuffer->bindComputePipeline(pipelineAndSerial->get()); commandBuffer->bindComputePipeline(pipelineAndSerial->get());
} }
......
...@@ -916,7 +916,8 @@ void CommandBufferHelper::restoreStencilContent() ...@@ -916,7 +916,8 @@ void CommandBufferHelper::restoreStencilContent()
} }
} }
void CommandBufferHelper::executeBarriers(ContextVk *contextVk, PrimaryCommandBuffer *primary) void CommandBufferHelper::executeBarriers(const angle::FeaturesVk &features,
PrimaryCommandBuffer *primary)
{ {
// make a local copy for faster access // make a local copy for faster access
PipelineStagesMask mask = mPipelineBarrierMask; PipelineStagesMask mask = mPipelineBarrierMask;
...@@ -925,7 +926,7 @@ void CommandBufferHelper::executeBarriers(ContextVk *contextVk, PrimaryCommandBu ...@@ -925,7 +926,7 @@ void CommandBufferHelper::executeBarriers(ContextVk *contextVk, PrimaryCommandBu
return; return;
} }
if (contextVk->getFeatures().preferAggregateBarrierCalls.enabled) if (features.preferAggregateBarrierCalls.enabled)
{ {
PipelineStagesMask::Iterator iter = mask.begin(); PipelineStagesMask::Iterator iter = mask.begin();
PipelineBarrier &barrier = mPipelineBarriers[*iter]; PipelineBarrier &barrier = mPipelineBarriers[*iter];
...@@ -1152,24 +1153,31 @@ void CommandBufferHelper::endTransformFeedback() ...@@ -1152,24 +1153,31 @@ void CommandBufferHelper::endTransformFeedback()
mValidTransformFeedbackBufferCount = 0; mValidTransformFeedbackBufferCount = 0;
} }
angle::Result CommandBufferHelper::flushToPrimary(ContextVk *contextVk, angle::Result CommandBufferHelper::getRenderPassWithOps(ContextVk *contextVk,
PrimaryCommandBuffer *primary) RenderPass **renderPass)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "CommandBufferHelper::flushToPrimary"); *renderPass = nullptr;
ASSERT(!empty()); if (mIsRenderPassCommandBuffer)
if (kEnableCommandStreamDiagnostics)
{ {
addCommandDiagnostics(contextVk); ANGLE_TRY(contextVk->getRenderPassWithOps(mRenderPassDesc, mAttachmentOps, renderPass));
} }
return angle::Result::Continue;
}
angle::Result CommandBufferHelper::flushToPrimary(const angle::FeaturesVk &features,
PrimaryCommandBuffer *primary,
RenderPass *renderPass)
{
ANGLE_TRACE_EVENT0("gpu.angle", "CommandBufferHelper::flushToPrimary");
ASSERT(!empty());
// Commands that are added to primary before beginRenderPass command // Commands that are added to primary before beginRenderPass command
executeBarriers(contextVk, primary); executeBarriers(features, primary);
if (mIsRenderPassCommandBuffer) if (mIsRenderPassCommandBuffer)
{ {
mCommandBuffer.executeQueuedResetQueryPoolCommands(primary->getHandle()); mCommandBuffer.executeQueuedResetQueryPoolCommands(primary->getHandle());
// Pull a RenderPass from the cache. ASSERT(renderPass != nullptr);
RenderPass *renderPass = nullptr;
ANGLE_TRY(contextVk->getRenderPassWithOps(mRenderPassDesc, mAttachmentOps, &renderPass));
VkRenderPassBeginInfo beginInfo = {}; VkRenderPassBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
......
...@@ -962,9 +962,12 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -962,9 +962,12 @@ class CommandBufferHelper : angle::NonCopyable
CommandBuffer &getCommandBuffer() { return mCommandBuffer; } CommandBuffer &getCommandBuffer() { return mCommandBuffer; }
angle::Result flushToPrimary(ContextVk *contextVk, PrimaryCommandBuffer *primary); angle::Result getRenderPassWithOps(ContextVk *contextVk, RenderPass **renderPass);
angle::Result flushToPrimary(const angle::FeaturesVk &features,
PrimaryCommandBuffer *primary,
RenderPass *renderPass);
void executeBarriers(ContextVk *contextVk, PrimaryCommandBuffer *primary); void executeBarriers(const angle::FeaturesVk &features, PrimaryCommandBuffer *primary);
void setHasRenderPass(bool hasRenderPass) { mIsRenderPassCommandBuffer = hasRenderPass; } void setHasRenderPass(bool hasRenderPass) { mIsRenderPassCommandBuffer = hasRenderPass; }
...@@ -1129,9 +1132,9 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -1129,9 +1132,9 @@ class CommandBufferHelper : angle::NonCopyable
bool isReadOnlyDepthMode() const { return mReadOnlyDepthStencilMode; } bool isReadOnlyDepthMode() const { return mReadOnlyDepthStencilMode; }
private:
void addCommandDiagnostics(ContextVk *contextVk); void addCommandDiagnostics(ContextVk *contextVk);
private:
bool onDepthStencilAccess(ResourceAccess access, bool onDepthStencilAccess(ResourceAccess access,
uint32_t *cmdCountInvalidated, uint32_t *cmdCountInvalidated,
uint32_t *cmdCountDisabled); uint32_t *cmdCountDisabled);
......
...@@ -943,15 +943,15 @@ gl::LevelIndex GetLevelIndex(vk::LevelIndex levelVk, gl::LevelIndex baseLevel); ...@@ -943,15 +943,15 @@ gl::LevelIndex GetLevelIndex(vk::LevelIndex levelVk, gl::LevelIndex baseLevel);
} // namespace rx } // namespace rx
#define ANGLE_VK_TRY(context, command) \ #define ANGLE_VK_TRY(context, command) \
do \ do \
{ \ { \
auto ANGLE_LOCAL_VAR = command; \ auto ANGLE_LOCAL_VAR = command; \
if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR != VK_SUCCESS)) \ if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR != VK_SUCCESS)) \
{ \ { \
context->handleError(ANGLE_LOCAL_VAR, __FILE__, ANGLE_FUNCTION, __LINE__); \ (context)->handleError(ANGLE_LOCAL_VAR, __FILE__, ANGLE_FUNCTION, __LINE__); \
return angle::Result::Stop; \ return angle::Result::Stop; \
} \ } \
} while (0) } while (0)
#define ANGLE_VK_CHECK(context, test, error) ANGLE_VK_TRY(context, test ? VK_SUCCESS : error) #define ANGLE_VK_CHECK(context, test, error) ANGLE_VK_TRY(context, test ? VK_SUCCESS : error)
......
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