Commit f10bf6bf by Jamie Madill Committed by Commit Bot

Vulkan: Implement multi-threaded GL.

The main component of this change is to make vk::BufferHelper, vk::ImageHelper and vk::SyncHelper use a common path. We introduce a new "vk::SharedGarbage" helper class that stores small lists of garbage from individual objects like an ImageHelper or BufferHelper. The SharedGarbage is stored in the RendererVk with the ResourceUse of the helper object. The ResourceUse tells RendererVk when it is safe to destroy the GarbageObjects. New "onGraphAccess" commands are added in a few places to enable the common garbage collection path. A couple Context-only resources like default attributes now are referenced where they were not before. Also reorganizes some functions so we can add a few helpful ASSERTs to our graph dependencies. Added "updateCurrentAccessNodes" for this. Also adds a "RendererScoped" helper to replace many uses of "ContextScoped". The multithreading EGL tests mostly pass but have some remaining flakiness so cannot yet be enabled. Bug: angleproject:2464 Change-Id: Ia3e3ae8848d731abf3f21ebe04c33e381e130be0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1808444 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 988f7170
......@@ -78,11 +78,12 @@ void BufferVk::destroy(const gl::Context *context)
void BufferVk::release(ContextVk *contextVk)
{
mBuffer.release(contextVk);
RendererVk *renderer = contextVk->getRenderer();
mBuffer.release(renderer);
for (ConversionBuffer &buffer : mVertexConversionBuffers)
{
buffer.data.release(contextVk);
buffer.data.release(renderer);
}
}
......
......@@ -255,29 +255,32 @@ bool CommandGraphResource::isResourceInUse(ContextVk *contextVk) const
return mUse.isCurrentlyInGraph() || contextVk->isSerialInUse(mUse.getSerial());
}
angle::Result CommandGraphResource::recordCommands(ContextVk *context,
angle::Result CommandGraphResource::recordCommands(ContextVk *contextVk,
CommandBuffer **commandBufferOut)
{
onGraphAccess(context->getCommandGraph());
updateCurrentAccessNodes();
if (!hasChildlessWritingNode() || hasStartedRenderPass())
{
startNewCommands(context);
startNewCommands(contextVk);
return mCurrentWritingNode->beginOutsideRenderPassRecording(
context, context->getCommandPool(), commandBufferOut);
contextVk, contextVk->getCommandPool(), commandBufferOut);
}
CommandBuffer *outsideRenderPassCommands = mCurrentWritingNode->getOutsideRenderPassCommands();
if (!outsideRenderPassCommands->valid())
{
ANGLE_TRY(mCurrentWritingNode->beginOutsideRenderPassRecording(
context, context->getCommandPool(), commandBufferOut));
contextVk, contextVk->getCommandPool(), commandBufferOut));
}
else
{
*commandBufferOut = outsideRenderPassCommands;
}
// Store reference to usage in graph.
contextVk->getCommandGraph()->onResourceUse(mUse);
return angle::Result::Continue;
}
......@@ -822,6 +825,42 @@ void CommandGraphNode::getMemoryUsageStatsForDiagnostics(size_t *usedMemoryOut,
*allocatedMemoryOut += commandBufferAllocated;
}
// SharedGarbage implementation.
SharedGarbage::SharedGarbage() = default;
SharedGarbage::SharedGarbage(SharedGarbage &&other)
{
*this = std::move(other);
}
SharedGarbage::SharedGarbage(SharedResourceUse &&use, std::vector<GarbageObject> &&garbage)
: mLifetime(std::move(use)), mGarbage(std::move(garbage))
{}
SharedGarbage::~SharedGarbage() = default;
SharedGarbage &SharedGarbage::operator=(SharedGarbage &&rhs)
{
std::swap(mLifetime, rhs.mLifetime);
std::swap(mGarbage, rhs.mGarbage);
return *this;
}
bool SharedGarbage::destroyIfComplete(VkDevice device, Serial completedSerial)
{
if (mLifetime.isCurrentlyInGraph() || mLifetime.getSerial() > completedSerial)
return false;
mLifetime.release();
for (GarbageObject &object : mGarbage)
{
object.destroy(device);
}
return true;
}
// CommandGraph implementation.
CommandGraph::CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator)
: mEnableGraphDiagnostics(enableGraphDiagnostics),
......@@ -835,6 +874,7 @@ CommandGraph::CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *po
CommandGraph::~CommandGraph()
{
ASSERT(empty());
ASSERT(mResourceUses.empty());
}
CommandGraphNode *CommandGraph::allocateNode(CommandGraphNodeFunction function)
......
......@@ -351,6 +351,24 @@ class SharedResourceUse final : angle::NonCopyable
ResourceUse *mUse;
};
class SharedGarbage
{
public:
SharedGarbage();
SharedGarbage(SharedGarbage &&other);
SharedGarbage(SharedResourceUse &&use, std::vector<GarbageObject> &&garbage);
~SharedGarbage();
SharedGarbage &operator=(SharedGarbage &&rhs);
bool destroyIfComplete(VkDevice device, Serial completedSerial);
private:
SharedResourceUse mLifetime;
std::vector<GarbageObject> mGarbage;
};
using SharedGarbageList = std::vector<SharedGarbage>;
// This is a helper class for back-end objects used in Vk command buffers. It records a serial
// at command recording times indicating an order in the queue. We use Fences to detect when
// commands finish, and then release any unreferenced and deleted resources based on the stored
......@@ -377,6 +395,7 @@ class CommandGraphResource : angle::NonCopyable
// Updates the in-use serial tracked for this resource. Will clear dependencies if the resource
// was not used in this set of command nodes.
void onGraphAccess(CommandGraph *commandGraph);
void updateCurrentAccessNodes();
// Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass.
......@@ -427,6 +446,9 @@ class CommandGraphResource : angle::NonCopyable
protected:
explicit CommandGraphResource(CommandGraphResourceType resourceType);
// Current resource lifetime.
SharedResourceUse mUse;
private:
// Returns true if this node has a current writing node with no children.
ANGLE_INLINE bool hasChildlessWritingNode() const;
......@@ -435,9 +457,6 @@ class CommandGraphResource : angle::NonCopyable
void onWriteImpl(ContextVk *contextVk, CommandGraphNode *writingNode);
// Current resource lifetime.
SharedResourceUse mUse;
std::vector<CommandGraphNode *> mCurrentReadingNodes;
// Current command graph writing node.
......@@ -583,7 +602,7 @@ ANGLE_INLINE bool CommandGraphResource::hasStartedRenderPass() const
return hasChildlessWritingNode() && mCurrentWritingNode->getInsideRenderPassCommands()->valid();
}
ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph)
ANGLE_INLINE void CommandGraphResource::updateCurrentAccessNodes()
{
// Clear dependencies if this is a new access.
if (!mUse.isCurrentlyInGraph())
......@@ -591,6 +610,11 @@ ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph
mCurrentWritingNode = nullptr;
mCurrentReadingNodes.clear();
}
}
ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph)
{
updateCurrentAccessNodes();
// Store reference to usage in graph.
commandGraph->onResourceUse(mUse);
......@@ -600,9 +624,13 @@ ANGLE_INLINE bool CommandGraphResource::appendToStartedRenderPass(CommandGraph *
const gl::Rectangle &renderArea,
CommandBuffer **commandBufferOut)
{
onGraphAccess(graph);
updateCurrentAccessNodes();
if (hasStartedRenderPass())
{
// Store reference to usage in graph.
graph->onResourceUse(mUse);
if (mCurrentWritingNode->getRenderPassRenderArea().encloses(renderArea))
{
*commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
......@@ -689,6 +717,7 @@ ANGLE_INLINE bool CommandGraphResource::hasChildlessWritingNode() const
// CommandGraph inlines.
ANGLE_INLINE void CommandGraph::onResourceUse(const SharedResourceUse &resourceUse)
{
ASSERT(!empty());
SharedResourceUse newUse;
newUse.set(resourceUse);
mResourceUses.emplace_back(std::move(newUse));
......
......@@ -1052,7 +1052,9 @@ angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(const gl::Context *con
mVertexArray->getCurrentArrayBuffers();
vk::FramebufferHelper *framebuffer = mDrawFramebuffer->getFramebuffer();
for (size_t attribIndex : context->getStateCache().getActiveBufferedAttribsMask())
// Mark all active vertex buffers as accessed by the graph.
gl::AttributesMask attribsMask = mProgram->getState().getActiveAttribLocationsMask();
for (size_t attribIndex : attribsMask)
{
vk::BufferHelper *arrayBuffer = arrayBufferResources[attribIndex];
if (arrayBuffer)
......@@ -3141,6 +3143,7 @@ angle::Result ContextVk::updateDefaultAttribute(size_t attribIndex)
ANGLE_TRY(defaultBuffer.flush(this));
mVertexArray->updateDefaultAttrib(this, attribIndex, bufferHandle,
defaultBuffer.getCurrentBuffer(),
static_cast<uint32_t>(offset));
return angle::Result::Continue;
}
......
......@@ -340,7 +340,10 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
template <typename T>
void addGarbage(T *object)
{
mCurrentGarbage.emplace_back(vk::GetGarbage(object));
if (object->valid())
{
mCurrentGarbage.emplace_back(vk::GetGarbage(object));
}
}
// It would be nice if we didn't have to expose this for QueryVk::getResult.
......
......@@ -181,7 +181,7 @@ void FramebufferVk::destroy(const gl::Context *context)
ContextVk *contextVk = vk::GetImpl(context);
mFramebuffer.release(contextVk);
mReadPixelBuffer.release(contextVk);
mReadPixelBuffer.release(contextVk->getRenderer());
}
angle::Result FramebufferVk::discard(const gl::Context *context,
......@@ -263,7 +263,7 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
return angle::Result::Continue;
}
mFramebuffer.onGraphAccess(contextVk->getCommandGraph());
mFramebuffer.updateCurrentAccessNodes();
// This function assumes that only enabled attachments are asked to be cleared.
ASSERT((clearColorBuffers & mState.getEnabledDrawBuffers()) == clearColorBuffers);
......@@ -1604,7 +1604,7 @@ void FramebufferVk::onScissorChange(ContextVk *contextVk)
// is too small, we need to start a new one. The latter can happen if a scissored clear starts
// a render pass, the scissor is disabled and a draw call is issued to affect the whole
// framebuffer.
mFramebuffer.onGraphAccess(contextVk->getCommandGraph());
mFramebuffer.updateCurrentAccessNodes();
if (mFramebuffer.hasStartedRenderPass() &&
!mFramebuffer.getRenderPassRenderArea().encloses(scissoredRenderArea))
{
......
......@@ -32,33 +32,19 @@ void ImageVk::onDestroy(const egl::Display *display)
DisplayVk *displayVk = vk::GetImpl(display);
RendererVk *renderer = displayVk->getRenderer();
std::vector<vk::GarbageObject> garbage;
if (mImage != nullptr && mOwnsImage)
{
mImage->releaseImage(displayVk, &garbage);
mImage->releaseStagingBuffer(displayVk, &garbage);
delete mImage;
mImage->releaseImage(renderer);
mImage->releaseStagingBuffer(renderer);
SafeDelete(mImage);
}
else if (egl::IsExternalImageTarget(mState.target))
{
ASSERT(mState.source != nullptr);
ExternalImageSiblingVk *externalImageSibling =
GetImplAs<ExternalImageSiblingVk>(GetAs<egl::ExternalImageSibling>(mState.source));
externalImageSibling->release(displayVk, &garbage);
}
mImage = nullptr;
if (!garbage.empty())
{
renderer->addGarbage(std::move(mImageLastUseFences), std::move(garbage));
}
else
{
for (vk::Shared<vk::Fence> &fence : mImageLastUseFences)
{
fence.reset(displayVk->getDevice());
}
externalImageSibling->release(renderer);
mImage = nullptr;
}
}
......@@ -160,12 +146,6 @@ angle::Result ImageVk::orphan(const gl::Context *context, egl::ImageSibling *sib
// Flush the context to make sure the fence has been submitted.
ANGLE_TRY(contextVk->flushImpl(nullptr));
vk::Shared<vk::Fence> fence = contextVk->getLastSubmittedFence();
if (fence.isReferenced())
{
mImageLastUseFences.push_back(std::move(fence));
}
return angle::Result::Continue;
}
......
......@@ -24,7 +24,7 @@ class ExternalImageSiblingVk : public ExternalImageSiblingImpl
virtual vk::ImageHelper *getImage() const = 0;
virtual void release(DisplayVk *display, std::vector<vk::GarbageObject> *garbageQueue) = 0;
virtual void release(RendererVk *renderer) = 0;
};
class ImageVk : public ImageImpl
......
......@@ -81,7 +81,7 @@ void OverlayVk::onDestroy(const gl::Context *context)
angle::Result OverlayVk::createFont(ContextVk *contextVk)
{
RendererVk *rendererVk = contextVk->getRenderer();
RendererVk *renderer = contextVk->getRenderer();
// Create a buffer to stage font data upload.
VkBufferCreateInfo bufferCreateInfo = {};
......@@ -91,7 +91,7 @@ angle::Result OverlayVk::createFont(ContextVk *contextVk)
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vk::ContextScoped<vk::BufferHelper> fontDataBuffer(contextVk);
vk::RendererScoped<vk::BufferHelper> fontDataBuffer(renderer);
ANGLE_TRY(fontDataBuffer.get().init(contextVk, bufferCreateInfo,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
......@@ -110,10 +110,10 @@ angle::Result OverlayVk::createFont(ContextVk *contextVk)
ANGLE_TRY(
mFontImage.init(contextVk, gl::TextureType::_2D,
VkExtent3D{gl::overlay::kFontImageWidth, gl::overlay::kFontImageHeight, 1},
rendererVk->getFormat(angle::FormatID::R8_UNORM), 1,
renderer->getFormat(angle::FormatID::R8_UNORM), 1,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0, 0, 1,
gl::overlay::kFontCount));
ANGLE_TRY(mFontImage.initMemory(contextVk, rendererVk->getMemoryProperties(),
ANGLE_TRY(mFontImage.initMemory(contextVk, renderer->getMemoryProperties(),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
ANGLE_TRY(mFontImage.initImageView(contextVk, gl::TextureType::_2DArray,
VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
......@@ -149,10 +149,10 @@ angle::Result OverlayVk::createFont(ContextVk *contextVk)
angle::Result OverlayVk::cullWidgets(ContextVk *contextVk)
{
RendererVk *rendererVk = contextVk->getRenderer();
RendererVk *renderer = contextVk->getRenderer();
// Release old culledWidgets image
mCulledWidgets.releaseImage(contextVk);
mCulledWidgets.releaseImage(renderer);
contextVk->addGarbage(&mCulledWidgetsView);
// Create a buffer to contain coordinates of enabled text and graph widgets. This buffer will
......@@ -163,7 +163,7 @@ angle::Result OverlayVk::cullWidgets(ContextVk *contextVk)
bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vk::ContextScoped<vk::BufferHelper> enabledWidgetsBuffer(contextVk);
vk::RendererScoped<vk::BufferHelper> enabledWidgetsBuffer(renderer);
ANGLE_TRY(enabledWidgetsBuffer.get().init(contextVk, bufferCreateInfo,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
......@@ -186,10 +186,10 @@ angle::Result OverlayVk::cullWidgets(ContextVk *contextVk)
UnsignedCeilDivide(mPresentImageExtent.height, mSubgroupSize[1]), 1};
ANGLE_TRY(mCulledWidgets.init(contextVk, gl::TextureType::_2D, culledWidgetsExtent,
rendererVk->getFormat(angle::FormatID::R32G32_UINT), 1,
renderer->getFormat(angle::FormatID::R32G32_UINT), 1,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0, 0, 1,
1));
ANGLE_TRY(mCulledWidgets.initMemory(contextVk, rendererVk->getMemoryProperties(),
ANGLE_TRY(mCulledWidgets.initMemory(contextVk, renderer->getMemoryProperties(),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
ANGLE_TRY(mCulledWidgets.initImageView(contextVk, gl::TextureType::_2D,
VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
......@@ -214,10 +214,10 @@ angle::Result OverlayVk::onPresent(ContextVk *contextVk,
return angle::Result::Continue;
}
RendererVk *rendererVk = contextVk->getRenderer();
RendererVk *renderer = contextVk->getRenderer();
// If the swapchain image doesn't support storage image, we can't output to it.
VkFormatFeatureFlags featureBits = rendererVk->getImageFormatFeatureBits(
VkFormatFeatureFlags featureBits = renderer->getImageFormatFeatureBits(
imageToPresent->getFormat().vkImageFormat, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
if ((featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
{
......@@ -240,8 +240,8 @@ angle::Result OverlayVk::onPresent(ContextVk *contextVk,
mRefreshCulledWidgets = false;
}
vk::ContextScoped<vk::BufferHelper> textDataBuffer(contextVk);
vk::ContextScoped<vk::BufferHelper> graphDataBuffer(contextVk);
vk::RendererScoped<vk::BufferHelper> textDataBuffer(renderer);
vk::RendererScoped<vk::BufferHelper> graphDataBuffer(renderer);
VkBufferCreateInfo textBufferCreateInfo = {};
textBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
......
......@@ -446,15 +446,17 @@ void ProgramVk::reset(ContextVk *contextVk)
}
mPipelineLayout.reset();
RendererVk *renderer = contextVk->getRenderer();
for (auto &uniformBlock : mDefaultUniformBlocks)
{
uniformBlock.storage.release(contextVk);
uniformBlock.storage.release(renderer);
}
mDefaultShaderInfo.release(contextVk);
mLineRasterShaderInfo.release(contextVk);
mEmptyBuffer.release(contextVk);
mEmptyBuffer.release(renderer);
mDescriptorSets.clear();
mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
......@@ -470,6 +472,7 @@ void ProgramVk::reset(ContextVk *contextVk)
}
mTextureDescriptorsCache.clear();
mDescriptorBuffersCache.clear();
}
std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
......@@ -1219,6 +1222,8 @@ void ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
uint32_t bindingIndex = 0;
mDescriptorBuffersCache.clear();
// Write default uniforms for each shader type.
for (const gl::ShaderType shaderType : mState.getLinkedShaderStages())
{
......@@ -1228,13 +1233,15 @@ void ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
if (!uniformBlock.uniformData.empty())
{
const vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer();
bufferInfo.buffer = bufferHelper->getBuffer().getHandle();
vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer();
bufferInfo.buffer = bufferHelper->getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(bufferHelper);
}
else
{
mEmptyBuffer.onGraphAccess(contextVk->getCommandGraph());
bufferInfo.buffer = mEmptyBuffer.getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(&mEmptyBuffer);
}
bufferInfo.offset = 0;
......@@ -1751,6 +1758,11 @@ angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
mDynamicBufferOffsets.data());
}
for (vk::BufferHelper *buffer : mDescriptorBuffersCache)
{
buffer->onGraphAccess(contextVk->getCommandGraph());
}
return angle::Result::Continue;
}
} // namespace rx
......@@ -141,7 +141,7 @@ class ProgramVk : public ProgramImpl
vk::ShaderProgramHelper *shaderProgram;
ANGLE_TRY(initGraphicsShaders(contextVk, mode, &shaderProgram));
ASSERT(shaderProgram->isGraphicsProgram());
RendererVk *renderer = contextVk->getRenderer();
RendererVk *renderer = contextVk->getRenderer();
vk::PipelineCache *pipelineCache = nullptr;
ANGLE_TRY(renderer->getPipelineCache(&pipelineCache));
return shaderProgram->getGraphicsPipeline(
......@@ -282,6 +282,7 @@ class ProgramVk : public ProgramImpl
// Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::vector<vk::BufferHelper *> mDescriptorBuffersCache;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
......
......@@ -225,8 +225,10 @@ void RenderbufferVk::releaseImage(ContextVk *contextVk)
{
if (mImage && mOwnsImage)
{
mImage->releaseImage(contextVk);
mImage->releaseStagingBuffer(contextVk);
RendererVk *renderer = contextVk->getRenderer();
mImage->releaseImage(renderer);
mImage->releaseStagingBuffer(renderer);
}
else
{
......
......@@ -505,31 +505,6 @@ void ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> &physicalDevices,
*physicalDeviceOut = physicalDevices[0];
vkGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
}
angle::Result WaitFences(vk::Context *context,
std::vector<vk::Shared<vk::Fence>> *fences,
bool block)
{
uint64_t timeout = block ? context->getRenderer()->getMaxFenceWaitTimeNs() : 0;
// Iterate backwards over the fences, removing them from the list in constant time when they
// are complete.
while (!fences->empty())
{
VkResult result = fences->back().get().wait(context->getDevice(), timeout);
if (result == VK_TIMEOUT)
{
return angle::Result::Continue;
}
ANGLE_VK_TRY(context, result);
context->getRenderer()->resetSharedFence(&fences->back());
fences->pop_back();
}
return angle::Result::Continue;
}
} // namespace
// RendererVk implementation.
......@@ -561,12 +536,13 @@ RendererVk::RendererVk()
RendererVk::~RendererVk()
{
ASSERT(mFencedGarbage.empty());
ASSERT(mSharedGarbage.empty());
}
void RendererVk::onDestroy(vk::Context *context)
{
(void)cleanupGarbage(context, true);
ASSERT(mSharedGarbage.empty());
mFenceRecycler.destroy(mDevice);
......@@ -1621,20 +1597,6 @@ angle::Result RendererVk::newSharedFence(vk::Context *context,
return angle::Result::Continue;
}
void RendererVk::addGarbage(vk::Shared<vk::Fence> &&fence, std::vector<vk::GarbageObject> &&garbage)
{
std::vector<vk::Shared<vk::Fence>> fences;
fences.push_back(std::move(fence));
addGarbage(std::move(fences), std::move(garbage));
}
void RendererVk::addGarbage(std::vector<vk::Shared<vk::Fence>> &&fences,
std::vector<vk::GarbageObject> &&garbage)
{
std::lock_guard<decltype(mGarbageMutex)> lock(mGarbageMutex);
mFencedGarbage.emplace_back(std::move(fences), std::move(garbage));
}
template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits)
......@@ -1675,17 +1637,13 @@ angle::Result RendererVk::cleanupGarbage(vk::Context *context, bool block)
{
std::lock_guard<decltype(mGarbageMutex)> lock(mGarbageMutex);
auto garbageIter = mFencedGarbage.begin();
while (garbageIter != mFencedGarbage.end())
for (auto garbageIter = mSharedGarbage.begin(); garbageIter != mSharedGarbage.end();)
{
ANGLE_TRY(WaitFences(context, &garbageIter->first, block));
if (garbageIter->first.empty())
// Possibly 'counter' should be always zero when we add the object to garbage.
vk::SharedGarbage &garbage = *garbageIter;
if (garbage.destroyIfComplete(mDevice, mLastCompletedQueueSerial))
{
for (vk::GarbageObject &garbageObject : garbageIter->second)
{
garbageObject.destroy(mDevice);
}
garbageIter = mFencedGarbage.erase(garbageIter);
garbageIter = mSharedGarbage.erase(garbageIter);
}
else
{
......
......@@ -45,6 +45,18 @@ struct Format;
// glSignalSemaphoreEXT.
using SignalSemaphoreVector = angle::FixedVector<VkSemaphore, 2>;
inline void CollectGarbage(std::vector<vk::GarbageObject> *garbageOut) {}
template <typename ArgT, typename... ArgsT>
void CollectGarbage(std::vector<vk::GarbageObject> *garbageOut, ArgT object, ArgsT... objectsIn)
{
if (object->valid())
{
garbageOut->emplace_back(vk::GarbageObject::Get(object));
}
CollectGarbage(garbageOut, objectsIn...);
}
class RendererVk : angle::NonCopyable
{
public:
......@@ -151,9 +163,23 @@ class RendererVk : angle::NonCopyable
sharedFenceIn->resetAndRecycle(&mFenceRecycler);
}
void addGarbage(vk::Shared<vk::Fence> &&fence, std::vector<vk::GarbageObject> &&garbage);
void addGarbage(std::vector<vk::Shared<vk::Fence>> &&fences,
std::vector<vk::GarbageObject> &&garbage);
template <typename... ArgsT>
void collectGarbageAndReinit(vk::SharedResourceUse *use, ArgsT... garbageIn)
{
std::vector<vk::GarbageObject> sharedGarbage;
CollectGarbage(&sharedGarbage, garbageIn...);
if (!sharedGarbage.empty())
{
mSharedGarbage.emplace_back(std::move(*use), std::move(sharedGarbage));
}
else
{
// Force releasing "use" even if no garbage was created.
use->release();
}
// Keep "use" valid.
use->init();
}
static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
......@@ -230,9 +256,7 @@ class RendererVk : angle::NonCopyable
vk::Recycler<vk::Fence> mFenceRecycler;
std::mutex mGarbageMutex;
using FencedGarbage =
std::pair<std::vector<vk::Shared<vk::Fence>>, std::vector<vk::GarbageObject>>;
std::vector<FencedGarbage> mFencedGarbage;
vk::SharedGarbageList mSharedGarbage;
vk::MemoryProperties mMemoryProperties;
vk::FormatTable mFormatTable;
......
......@@ -887,10 +887,12 @@ angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
if (mDepthStencilImage.valid())
{
mDepthStencilImage.releaseImage(contextVk);
mDepthStencilImage.releaseStagingBuffer(contextVk);
mDepthStencilImage.releaseImage(renderer);
mDepthStencilImage.releaseStagingBuffer(renderer);
if (mDepthStencilImageView.valid())
{
......@@ -900,8 +902,8 @@ void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
if (mColorImageMS.valid())
{
mColorImageMS.releaseImage(contextVk);
mColorImageMS.releaseStagingBuffer(contextVk);
mColorImageMS.releaseImage(renderer);
mColorImageMS.releaseStagingBuffer(renderer);
contextVk->addGarbage(&mColorImageViewMS);
contextVk->addGarbage(&mFramebufferMS);
......
......@@ -17,32 +17,25 @@
namespace rx
{
FenceSyncVk::FenceSyncVk() {}
FenceSyncVk::~FenceSyncVk() {}
void FenceSyncVk::onDestroy(ContextVk *contextVk)
namespace vk
{
if (mEvent.valid())
{
contextVk->addGarbage(&mEvent);
}
for (vk::Shared<vk::Fence> &fence : mFences)
{
fence.reset(contextVk->getDevice());
}
SyncHelper::SyncHelper()
{
mUse.init();
}
void FenceSyncVk::onDestroy(DisplayVk *display)
SyncHelper::~SyncHelper()
{
std::vector<vk::GarbageObject> garbage;
garbage.emplace_back(vk::GetGarbage(&mEvent));
mUse.release();
}
display->getRenderer()->addGarbage(std::move(mFences), std::move(garbage));
void SyncHelper::releaseToRenderer(RendererVk *renderer)
{
renderer->collectGarbageAndReinit(&mUse, &mEvent);
mFence.reset(renderer->getDevice());
}
angle::Result FenceSyncVk::initialize(ContextVk *contextVk)
angle::Result SyncHelper::initialize(ContextVk *contextVk)
{
ASSERT(!mEvent.valid());
......@@ -53,24 +46,24 @@ angle::Result FenceSyncVk::initialize(ContextVk *contextVk)
eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
eventCreateInfo.flags = 0;
vk::DeviceScoped<vk::Event> event(device);
DeviceScoped<Event> event(device);
ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
vk::Shared<vk::Fence> fence;
ANGLE_TRY(contextVk->getNextSubmitFence(&fence));
ANGLE_TRY(contextVk->getNextSubmitFence(&mFence));
mEvent = event.release();
mFences.emplace_back(std::move(fence));
contextVk->getCommandGraph()->setFenceSync(mEvent);
CommandGraph *commandGraph = contextVk->getCommandGraph();
commandGraph->setFenceSync(mEvent);
commandGraph->onResourceUse(mUse);
return angle::Result::Continue;
}
angle::Result FenceSyncVk::clientWait(vk::Context *context,
ContextVk *contextVk,
bool flushCommands,
uint64_t timeout,
VkResult *outResult)
angle::Result SyncHelper::clientWait(Context *context,
ContextVk *contextVk,
bool flushCommands,
uint64_t timeout,
VkResult *outResult)
{
RendererVk *renderer = context->getRenderer();
......@@ -97,8 +90,8 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context,
// 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.
ASSERT(!mFences.empty());
VkResult status = mFences[0].get().wait(renderer->getDevice(), timeout);
ASSERT(mFence.get().valid());
VkResult status = mFence.get().wait(renderer->getDevice(), timeout);
// Check for errors, but don't consider timeout as such.
if (status != VK_TIMEOUT)
......@@ -110,21 +103,14 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context,
return angle::Result::Continue;
}
angle::Result FenceSyncVk::serverWait(vk::Context *context, ContextVk *contextVk)
void SyncHelper::serverWait(ContextVk *contextVk)
{
if (contextVk)
{
contextVk->getCommandGraph()->waitFenceSync(mEvent);
// Track fences from Contexts that use this sync for garbage collection.
vk::Shared<vk::Fence> nextSubmitFence;
ANGLE_TRY(contextVk->getNextSubmitFence(&nextSubmitFence));
mFences.emplace_back(std::move(nextSubmitFence));
}
return angle::Result::Continue;
CommandGraph *commandGraph = contextVk->getCommandGraph();
commandGraph->waitFenceSync(mEvent);
commandGraph->onResourceUse(mUse);
}
angle::Result FenceSyncVk::getStatus(vk::Context *context, bool *signaled)
angle::Result SyncHelper::getStatus(Context *context, bool *signaled)
{
VkResult result = mEvent.getStatus(context->getDevice());
if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
......@@ -134,6 +120,7 @@ angle::Result FenceSyncVk::getStatus(vk::Context *context, bool *signaled)
*signaled = result == VK_EVENT_SET;
return angle::Result::Continue;
}
} // namespace vk
SyncVk::SyncVk() : SyncImpl() {}
......@@ -141,7 +128,7 @@ SyncVk::~SyncVk() {}
void SyncVk::onDestroy(const gl::Context *context)
{
mFenceSync.onDestroy(vk::GetImpl(context));
mFenceSync.releaseToRenderer(vk::GetImpl(context)->getRenderer());
}
angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
......@@ -194,7 +181,8 @@ angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, G
ASSERT(timeout == GL_TIMEOUT_IGNORED);
ContextVk *contextVk = vk::GetImpl(context);
return mFenceSync.serverWait(contextVk, contextVk);
mFenceSync.serverWait(contextVk);
return angle::Result::Continue;
}
angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
......@@ -215,7 +203,7 @@ EGLSyncVk::~EGLSyncVk() {}
void EGLSyncVk::onDestroy(const egl::Display *display)
{
mFenceSync.onDestroy(vk::GetImpl(display));
mFenceSync.releaseToRenderer(vk::GetImpl(display)->getRenderer());
}
egl::Error EGLSyncVk::initialize(const egl::Display *display,
......@@ -275,12 +263,14 @@ egl::Error EGLSyncVk::serverWait(const egl::Display *display,
const gl::Context *context,
EGLint flags)
{
// Server wait requires a valid bound context.
ASSERT(context);
// No flags are currently implemented.
ASSERT(flags == 0);
ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
if (mFenceSync.serverWait(vk::GetImpl(display), contextVk) == angle::Result::Stop)
{
return egl::Error(EGL_BAD_ALLOC);
}
ContextVk *contextVk = vk::GetImpl(context);
mFenceSync.serverWait(contextVk);
return egl::NoError();
}
......
......@@ -12,8 +12,7 @@
#include "libANGLE/renderer/EGLSyncImpl.h"
#include "libANGLE/renderer/SyncImpl.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h"
namespace egl
{
......@@ -22,34 +21,38 @@ class AttributeMap;
namespace rx
{
namespace vk
{
// The behaviors of SyncImpl and EGLSyncImpl as fence syncs (only supported type) are currently
// identical for the Vulkan backend, and this class implements both interfaces.
class FenceSyncVk
class SyncHelper
{
public:
FenceSyncVk();
~FenceSyncVk();
SyncHelper();
~SyncHelper();
void onDestroy(ContextVk *contextVk);
void onDestroy(DisplayVk *display);
void releaseToRenderer(RendererVk *renderer);
angle::Result initialize(ContextVk *contextVk);
angle::Result clientWait(vk::Context *context,
angle::Result clientWait(Context *context,
ContextVk *contextVk,
bool flushCommands,
uint64_t timeout,
VkResult *outResult);
angle::Result serverWait(vk::Context *context, ContextVk *contextVk);
angle::Result getStatus(vk::Context *context, bool *signaled);
void serverWait(ContextVk *contextVk);
angle::Result getStatus(Context *context, bool *signaled);
private:
// The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with
// `getStatus`.
vk::Event mEvent;
// The first fence in the list is signaled once the CB including the `init` signal is executed.
// `clientWait` waits on this fence. The other fences are referenced to prevent deletion.
std::vector<vk::Shared<vk::Fence>> mFences;
Event mEvent;
// The fence is signaled once the CB including the `init` signal is executed.
// `clientWait` waits on this fence.
Shared<Fence> mFence;
SharedResourceUse mUse;
};
} // namespace vk
class SyncVk final : public SyncImpl
{
......@@ -70,7 +73,7 @@ class SyncVk final : public SyncImpl
angle::Result getStatus(const gl::Context *context, GLint *outResult) override;
private:
FenceSyncVk mFenceSync;
vk::SyncHelper mFenceSync;
};
class EGLSyncVk final : public EGLSyncImpl
......@@ -97,7 +100,7 @@ class EGLSyncVk final : public EGLSyncImpl
egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *result) const override;
private:
FenceSyncVk mFenceSync;
vk::SyncHelper mFenceSync;
};
} // namespace rx
......
......@@ -1023,10 +1023,10 @@ angle::Result TextureVk::copyImageDataToBufferAndGetData(ContextVk *contextVk,
gl::Box area(0, 0, 0, sourceArea.width, sourceArea.height, 1);
VkBuffer copyBufferHandle = VK_NULL_HANDLE;
vk::BufferHelper *copyBuffer = nullptr;
VkDeviceSize sourceCopyOffset = 0;
ANGLE_TRY(copyImageDataToBuffer(contextVk, sourceLevel, layerCount, 0, area, &copyBufferHandle,
ANGLE_TRY(copyImageDataToBuffer(contextVk, sourceLevel, layerCount, 0, area, &copyBuffer,
&sourceCopyOffset, outDataPtr));
// Explicitly finish. If new use cases arise where we don't want to block we can change this.
......@@ -1040,7 +1040,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
uint32_t layerCount,
uint32_t baseLayer,
const gl::Box &sourceArea,
VkBuffer *bufferHandleOut,
vk::BufferHelper **bufferOut,
VkDeviceSize *bufferOffsetOut,
uint8_t **outDataPtr)
{
......@@ -1058,7 +1058,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
// Allocate staging buffer data
ANGLE_TRY(mImage->allocateStagingMemory(contextVk, sourceCopyAllocationSize, outDataPtr,
bufferHandleOut, bufferOffsetOut, nullptr));
bufferOut, bufferOffsetOut, nullptr));
VkBufferImageCopy region = {};
region.bufferOffset = *bufferOffsetOut;
......@@ -1076,7 +1076,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
region.imageSubresource.mipLevel = static_cast<uint32_t>(sourceLevel);
commandBuffer->copyImageToBuffer(mImage->getImage(), mImage->getCurrentLayout(),
*bufferHandleOut, 1, &region);
(*bufferOut)->getBuffer().getHandle(), 1, &region);
return angle::Result::Continue;
}
......@@ -1244,17 +1244,17 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GL
}
// Now copy from the image to the staging buffer
VkBuffer stagingBufferHandle = VK_NULL_HANDLE;
vk::BufferHelper *stagingBuffer = nullptr;
VkDeviceSize stagingBufferOffset = 0;
ANGLE_TRY(copyImageDataToBuffer(contextVk, srcLevelVK, 1, layer, area,
&stagingBufferHandle, &stagingBufferOffset, nullptr));
ANGLE_TRY(copyImageDataToBuffer(contextVk, srcLevelVK, 1, layer, area, &stagingBuffer,
&stagingBufferOffset, nullptr));
// Stage an update to the new image that we will populate with existing mip levels
// We're providing the buffer handle and offset to use, since we *just* populated it
size_t bufferSize = extents.width * extents.height * extents.depth * info.pixelBytes;
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, level, layer, 1, extents, gl::Offset(), stagingBufferHandle,
stagingBufferOffset));
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(contextVk, bufferSize, level, layer,
1, extents, gl::Offset(),
stagingBuffer, stagingBufferOffset));
}
}
......@@ -1810,7 +1810,7 @@ void TextureVk::releaseImage(ContextVk *contextVk)
{
if (mOwnsImage)
{
mImage->releaseImage(contextVk);
mImage->releaseImage(contextVk->getRenderer());
}
else
{
......@@ -1862,7 +1862,7 @@ void TextureVk::releaseStagingBuffer(ContextVk *contextVk)
{
if (mImage)
{
mImage->releaseStagingBuffer(contextVk);
mImage->releaseStagingBuffer(contextVk->getRenderer());
}
}
......
......@@ -241,10 +241,10 @@ class TextureVk : public TextureImpl
angle::Result copyImageDataToBuffer(ContextVk *contextVk,
size_t sourceLevel,
uint32_t layerCount,
uint32_t layer,
uint32_t baseLayer,
const gl::Box &sourceArea,
VkBuffer *stagingBufferHandleOut,
VkDeviceSize *stagingOffsetOut,
vk::BufferHelper **bufferOut,
VkDeviceSize *bufferOffsetOut,
uint8_t **outDataPtr);
angle::Result generateMipmapsWithCPU(const gl::Context *context);
......
......@@ -237,7 +237,7 @@ void TransformFeedbackVk::onBeginOrEnd(const gl::Context *context)
FramebufferVk *framebufferVk = vk::GetImpl(context->getState().getDrawFramebuffer());
vk::FramebufferHelper *framebuffer = framebufferVk->getFramebuffer();
framebuffer->onGraphAccess(contextVk->getCommandGraph());
framebuffer->updateCurrentAccessNodes();
if (framebuffer->hasStartedRenderPass())
{
framebuffer->finishCurrentCommands(contextVk);
......
......@@ -1371,7 +1371,7 @@ angle::Result UtilsVk::stencilBlitResolveNoShaderExport(ContextVk *contextVk,
&descriptorPoolBinding, &descriptorSet));
// Create a temporary buffer to blit/resolve stencil into.
vk::ContextScoped<vk::BufferHelper> blitBuffer(contextVk);
vk::RendererScoped<vk::BufferHelper> blitBuffer(contextVk->getRenderer());
uint32_t bufferRowLengthInUints = UnsignedCeilDivide(params.blitArea.width, sizeof(uint32_t));
VkDeviceSize bufferSize = bufferRowLengthInUints * sizeof(uint32_t) * params.blitArea.height;
......
......@@ -148,11 +148,13 @@ void VertexArrayVk::destroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
mTheNullBuffer.release(contextVk);
RendererVk *renderer = contextVk->getRenderer();
mTheNullBuffer.release(renderer);
mDynamicVertexData.release(contextVk);
mDynamicIndexData.release(contextVk);
mTranslatedByteIndexData.release(contextVk);
mDynamicVertexData.release(renderer);
mDynamicIndexData.release(renderer);
mTranslatedByteIndexData.release(renderer);
mLineLoopHelper.release(contextVk);
}
......@@ -597,7 +599,7 @@ angle::Result VertexArrayVk::updateStreamedAttribs(const gl::Context *context,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices)
{
ContextVk *contextVk = vk::GetImpl(context);
ContextVk *contextVk = vk::GetImpl(context);
const gl::AttributesMask activeAttribs =
context->getStateCache().getActiveClientAttribsMask() |
context->getStateCache().getActiveBufferedAttribsMask();
......@@ -631,7 +633,7 @@ angle::Result VertexArrayVk::updateStreamedAttribs(const gl::Context *context,
ASSERT(GetVertexInputAlignment(vertexFormat) <= vk::kVertexBufferAlignment);
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
const uint32_t divisor = binding.getDivisor();
if (divisor > 0)
{
......@@ -760,13 +762,14 @@ angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
void VertexArrayVk::updateDefaultAttrib(ContextVk *contextVk,
size_t attribIndex,
VkBuffer bufferHandle,
vk::BufferHelper *buffer,
uint32_t offset)
{
if (!mState.getEnabledAttributesMask().test(attribIndex))
{
mCurrentArrayBufferHandles[attribIndex] = bufferHandle;
mCurrentArrayBufferOffsets[attribIndex] = offset;
mCurrentArrayBuffers[attribIndex] = nullptr;
mCurrentArrayBuffers[attribIndex] = buffer;
setDefaultPackedInput(contextVk, attribIndex);
}
......
......@@ -35,6 +35,7 @@ class VertexArrayVk : public VertexArrayImpl
void updateDefaultAttrib(ContextVk *contextVk,
size_t attribIndex,
VkBuffer bufferHandle,
vk::BufferHelper *buffer,
uint32_t offset);
angle::Result updateStreamedAttribs(const gl::Context *context,
......
......@@ -201,13 +201,12 @@ vk::ImageHelper *HardwareBufferImageSiblingVkAndroid::getImage() const
return mImage;
}
void HardwareBufferImageSiblingVkAndroid::release(DisplayVk *display,
std::vector<vk::GarbageObject> *garbageQueue)
void HardwareBufferImageSiblingVkAndroid::release(RendererVk *renderer)
{
if (mImage != nullptr)
{
mImage->releaseImage(display, garbageQueue);
mImage->releaseStagingBuffer(display, garbageQueue);
mImage->releaseImage(renderer);
mImage->releaseStagingBuffer(renderer);
SafeDelete(mImage);
}
}
......
......@@ -36,7 +36,7 @@ class HardwareBufferImageSiblingVkAndroid : public ExternalImageSiblingVk
// ExternalImageSiblingVk interface
vk::ImageHelper *getImage() const override;
void release(DisplayVk *display, std::vector<vk::GarbageObject> *garbageQueue) override;
void release(RendererVk *renderer) override;
private:
angle::Result initImpl(DisplayVk *displayVk);
......
......@@ -82,8 +82,7 @@ class DynamicBuffer : angle::NonCopyable
angle::Result invalidate(ContextVk *contextVk);
// This releases resources when they might currently be in use.
void release(ContextVk *contextVk);
void release(DisplayVk *display, std::vector<GarbageObject> *garbageQueue);
void release(RendererVk *renderer);
// This releases all the buffers that have been allocated since this was last called.
void releaseInFlightBuffers(ContextVk *contextVk);
......@@ -101,10 +100,7 @@ class DynamicBuffer : angle::NonCopyable
private:
void reset();
angle::Result allocateNewBuffer(ContextVk *contextVk);
void releaseBufferListToContext(ContextVk *contextVk, std::vector<BufferHelper *> *buffers);
void releaseBufferListToDisplay(DisplayVk *display,
std::vector<GarbageObject> *garbageQueue,
std::vector<BufferHelper *> *buffers);
void releaseBufferListToRenderer(RendererVk *renderer, std::vector<BufferHelper *> *buffers);
void destroyBufferList(VkDevice device, std::vector<BufferHelper *> *buffers);
VkBufferUsageFlags mUsage;
......@@ -448,8 +444,7 @@ class BufferHelper final : public CommandGraphResource
VkMemoryPropertyFlags memoryPropertyFlags);
void destroy(VkDevice device);
void release(ContextVk *contextVk);
void release(DisplayVk *display, std::vector<GarbageObject> *garbageQueue);
void release(RendererVk *renderer);
bool valid() const { return mBuffer.valid(); }
const Buffer &getBuffer() const { return mBuffer; }
......@@ -707,11 +702,8 @@ class ImageHelper final : public CommandGraphResource
VkImageUsageFlags usage,
uint32_t layerCount);
void releaseImage(ContextVk *contextVk);
void releaseImage(DisplayVk *display, std::vector<GarbageObject> *garbageQueue);
void releaseStagingBuffer(ContextVk *contextVk);
void releaseStagingBuffer(DisplayVk *display, std::vector<GarbageObject> *garbageQueue);
void releaseImage(RendererVk *rendererVk);
void releaseStagingBuffer(RendererVk *renderer);
bool valid() const { return mImage.valid(); }
......@@ -790,7 +782,7 @@ class ImageHelper final : public CommandGraphResource
uint32_t layerCount,
const gl::Extents &glExtents,
const gl::Offset &offset,
VkBuffer stagingBufferHandle,
BufferHelper *stagingBuffer,
VkDeviceSize stagingOffset);
angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context,
......@@ -823,7 +815,7 @@ class ImageHelper final : public CommandGraphResource
angle::Result allocateStagingMemory(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
BufferHelper **bufferOut,
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut);
......@@ -894,16 +886,39 @@ class ImageHelper final : public CommandGraphResource
uint32_t layerCount,
CommandBuffer *commandBuffer);
enum class UpdateSource
{
Clear,
Buffer,
Image,
};
struct ClearUpdate
{
VkClearValue value;
uint32_t levelIndex;
uint32_t layerIndex;
uint32_t layerCount;
};
struct BufferUpdate
{
BufferHelper *bufferHelper;
VkBufferImageCopy copyRegion;
};
struct ImageUpdate
{
ImageHelper *image;
VkImageCopy copyRegion;
};
struct SubresourceUpdate
{
SubresourceUpdate();
SubresourceUpdate(VkBuffer bufferHandle, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(BufferHelper *bufferHelperIn, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(ImageHelper *image, const VkImageCopy &copyRegion);
SubresourceUpdate(const VkClearValue &clearValue, const gl::ImageIndex &imageIndex);
SubresourceUpdate(const SubresourceUpdate &other);
void release(ContextVk *contextVk);
void release(DisplayVk *display, std::vector<GarbageObject> *garbageQueue);
void release(RendererVk *renderer);
const VkImageSubresourceLayers &dstSubresource() const
{
......@@ -913,30 +928,6 @@ class ImageHelper final : public CommandGraphResource
}
bool isUpdateToLayerLevel(uint32_t layerIndex, uint32_t levelIndex) const;
enum class UpdateSource
{
Clear,
Buffer,
Image,
};
struct ClearUpdate
{
VkClearValue value;
uint32_t levelIndex;
uint32_t layerIndex;
uint32_t layerCount;
};
struct BufferUpdate
{
VkBuffer bufferHandle;
VkBufferImageCopy copyRegion;
};
struct ImageUpdate
{
ImageHelper *image;
VkImageCopy copyRegion;
};
UpdateSource updateSource;
union
{
......
......@@ -233,6 +233,7 @@ class GarbageObject
GarbageObject(GarbageObject &&other);
GarbageObject &operator=(GarbageObject &&rhs);
bool valid() const { return mHandle != VK_NULL_HANDLE; }
void destroy(VkDevice device);
template <typename DerivedT, typename HandleT>
......@@ -377,6 +378,23 @@ class ContextScoped final : angle::NonCopyable
T mVar;
};
template <typename T>
class RendererScoped final : angle::NonCopyable
{
public:
RendererScoped(RendererVk *renderer) : mRenderer(renderer) {}
~RendererScoped() { mVar.release(mRenderer); }
const T &get() const { return mVar; }
T &get() { return mVar; }
T &&release() { return std::move(mVar); }
private:
RendererVk *mRenderer;
T mVar;
};
// This is a very simple RefCount class that has no autoreleasing. Used in the descriptor set and
// pipeline layout caches.
template <typename T>
......
......@@ -156,6 +156,44 @@ TEST_P(LinkAndRelinkTest, RenderingProgramFailsWithProgramInstalled)
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Tests uniform default values.
TEST_P(LinkAndRelinkTest, UniformDefaultValues)
{
constexpr char kFS[] = R"(precision mediump float;
uniform vec4 u_uniform;
bool isZero(vec4 value) {
return value == vec4(0,0,0,0);
}
void main()
{
gl_FragColor = isZero(u_uniform) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
glUseProgram(program);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
GLint loc = glGetUniformLocation(program, "u_uniform");
ASSERT_NE(-1, loc);
glUniform4f(loc, 0.1f, 0.2f, 0.3f, 0.4f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glLinkProgram(program);
ASSERT_GL_NO_ERROR();
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// When program link fails and no valid compute program is installed in the GL
// state before the link, it should report an error for UseProgram and
// DispatchCompute.
......
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