Commit e88ec8ee by Jamie Madill Committed by Commit Bot

Vulkan: Refactor GarbageObject.

Instead of allocating a small bundle for deferred deletion, store a small triple of {Serial,VkHandle,HandleType}. The HandleType can be used in a generic way to release the VkHandle, without needing to store a pointer and use a virtual call. BUG=angleproject:2200 Change-Id: I30925c2f50fd11dafb1b986ced7d279a7dde827b Reviewed-on: https://chromium-review.googlesource.com/741163 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent c1f14fbe
......@@ -31,8 +31,8 @@ void BufferVk::destroy(const gl::Context *context)
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mBuffer));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mBufferMemory));
renderer->releaseResource(*this, &mBuffer);
renderer->releaseResource(*this, &mBufferMemory);
}
gl::Error BufferVk::setData(const gl::Context *context,
......
......@@ -885,7 +885,7 @@ std::vector<PathImpl *> ContextVk::createPaths(GLsizei)
// TODO(jmadill): Use pipeline cache.
void ContextVk::invalidateCurrentPipeline()
{
mRenderer->enqueueGarbageOrDeleteNow(*this, mCurrentPipeline);
mRenderer->releaseResource(*this, &mCurrentPipeline);
}
gl::Error ContextVk::dispatchCompute(const gl::Context *context,
......
......@@ -342,7 +342,7 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
PackPixels(params, angleFormat, inputPitch, mapPointer, reinterpret_cast<uint8_t *>(pixels));
stagingImage.getDeviceMemory().unmap(device);
renderer->enqueueGarbage(renderer->getCurrentQueueSerial(), std::move(stagingImage));
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
return vk::NoError();
}
......
......@@ -655,7 +655,7 @@ void RendererVk::freeAllInFlightResources()
for (auto &garbage : mGarbage)
{
garbage->destroy(mDevice);
garbage.destroy(mDevice);
}
mGarbage.clear();
}
......@@ -706,7 +706,7 @@ vk::Error RendererVk::checkInFlightCommands()
size_t freeIndex = 0;
for (; freeIndex < mGarbage.size(); ++freeIndex)
{
if (!mGarbage[freeIndex]->destroyIfComplete(mDevice, finishedSerial))
if (!mGarbage[freeIndex].destroyIfComplete(mDevice, finishedSerial))
break;
}
......
......@@ -74,22 +74,22 @@ class RendererVk : angle::NonCopyable
Serial getCurrentQueueSerial() const;
template <typename T>
void enqueueGarbage(Serial serial, T &&object)
void releaseResource(const ResourceVk &resource, T *object)
{
mGarbage.emplace_back(std::unique_ptr<vk::GarbageObject<T>>(
new vk::GarbageObject<T>(serial, std::move(object))));
Serial resourceSerial = resource.getQueueSerial();
releaseObject(resourceSerial, object);
}
template <typename T>
void enqueueGarbageOrDeleteNow(const ResourceVk &resource, T &&object)
void releaseObject(Serial resourceSerial, T *object)
{
if (resource.getDeleteSchedule(mLastCompletedQueueSerial) == DeleteSchedule::NOW)
if (resourceSerial <= mLastCompletedQueueSerial)
{
object.destroy(mDevice);
object->destroy(mDevice);
}
else
{
enqueueGarbage(resource.getStoredQueueSerial(), std::move(object));
object->dumpResources(resourceSerial, &mGarbage);
}
}
......@@ -133,7 +133,7 @@ class RendererVk : angle::NonCopyable
Serial mCurrentQueueSerial;
std::vector<vk::CommandBufferAndSerial> mInFlightCommands;
std::vector<vk::FenceAndSerial> mInFlightFences;
std::vector<std::unique_ptr<vk::IGarbageObject>> mGarbage;
std::vector<vk::GarbageObject> mGarbage;
vk::MemoryProperties mMemoryProperties;
};
......
......@@ -31,10 +31,10 @@ gl::Error TextureVk::onDestroy(const gl::Context *context)
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImage));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mDeviceMemory));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImageView));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mSampler));
renderer->releaseResource(*this, &mImage);
renderer->releaseResource(*this, &mDeviceMemory);
renderer->releaseResource(*this, &mImageView);
renderer->releaseResource(*this, &mSampler);
return gl::NoError();
}
......@@ -222,7 +222,7 @@ gl::Error TextureVk::setImage(const gl::Context *context,
VK_IMAGE_ASPECT_COLOR_BIT);
// TODO(jmadill): Re-use staging images.
renderer->enqueueGarbage(renderer->getCurrentQueueSerial(), std::move(stagingImage));
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingImage);
}
return gl::NoError();
......
......@@ -808,6 +808,12 @@ Error StagingImage::init(VkDevice device,
return NoError();
}
void StagingImage::dumpResources(Serial serial, std::vector<vk::GarbageObject> *garbageQueue)
{
mImage.dumpResources(serial, garbageQueue);
mDeviceMemory.dumpResources(serial, garbageQueue);
}
// Buffer implementation.
Buffer::Buffer()
{
......@@ -1086,6 +1092,87 @@ gl::Error AllocateBufferMemory(ContextVk *contextVk,
return gl::NoError();
}
// GarbageObject implementation.
GarbageObject::GarbageObject()
: mSerial(), mHandleType(HandleType::Invalid), mHandle(VK_NULL_HANDLE)
{
}
GarbageObject::GarbageObject(const GarbageObject &other) = default;
GarbageObject &GarbageObject::operator=(const GarbageObject &other) = default;
bool GarbageObject::destroyIfComplete(VkDevice device, Serial completedSerial)
{
if (completedSerial >= mSerial)
{
destroy(device);
return true;
}
return false;
}
void GarbageObject::destroy(VkDevice device)
{
switch (mHandleType)
{
case HandleType::Semaphore:
vkDestroySemaphore(device, reinterpret_cast<VkSemaphore>(mHandle), nullptr);
break;
case HandleType::CommandBuffer:
// Command buffers are pool allocated.
UNREACHABLE();
break;
case HandleType::Fence:
vkDestroyFence(device, reinterpret_cast<VkFence>(mHandle), nullptr);
break;
case HandleType::DeviceMemory:
vkFreeMemory(device, reinterpret_cast<VkDeviceMemory>(mHandle), nullptr);
break;
case HandleType::Buffer:
vkDestroyBuffer(device, reinterpret_cast<VkBuffer>(mHandle), nullptr);
break;
case HandleType::Image:
vkDestroyImage(device, reinterpret_cast<VkImage>(mHandle), nullptr);
break;
case HandleType::ImageView:
vkDestroyImageView(device, reinterpret_cast<VkImageView>(mHandle), nullptr);
break;
case HandleType::ShaderModule:
vkDestroyShaderModule(device, reinterpret_cast<VkShaderModule>(mHandle), nullptr);
break;
case HandleType::PipelineLayout:
vkDestroyPipelineLayout(device, reinterpret_cast<VkPipelineLayout>(mHandle), nullptr);
break;
case HandleType::RenderPass:
vkDestroyRenderPass(device, reinterpret_cast<VkRenderPass>(mHandle), nullptr);
break;
case HandleType::Pipeline:
vkDestroyPipeline(device, reinterpret_cast<VkPipeline>(mHandle), nullptr);
break;
case HandleType::DescriptorSetLayout:
vkDestroyDescriptorSetLayout(device, reinterpret_cast<VkDescriptorSetLayout>(mHandle),
nullptr);
break;
case HandleType::Sampler:
vkDestroySampler(device, reinterpret_cast<VkSampler>(mHandle), nullptr);
break;
case HandleType::DescriptorPool:
vkDestroyDescriptorPool(device, reinterpret_cast<VkDescriptorPool>(mHandle), nullptr);
break;
case HandleType::Framebuffer:
vkDestroyFramebuffer(device, reinterpret_cast<VkFramebuffer>(mHandle), nullptr);
break;
case HandleType::CommandPool:
vkDestroyCommandPool(device, reinterpret_cast<VkCommandPool>(mHandle), nullptr);
break;
default:
UNREACHABLE();
break;
}
}
} // namespace vk
namespace gl_vk
......
......@@ -45,17 +45,10 @@ enum class TextureDimension
TEX_2D_ARRAY,
};
enum DeleteSchedule
{
NOW,
LATER,
};
// This is a small helper mixin for any GL object used in Vk command buffers. It records a serial
// at command submission times indicating it's order in the queue. We will use Fences to detect
// when commands are finished, and then handle lifetime management for the resources.
// Note that we use a queue order serial instead of a command buffer id serial since a queue can
// submit multiple command buffers in one API call.
// 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
// queue serial in a special 'garbage' queue.
class ResourceVk
{
public:
......@@ -65,19 +58,7 @@ class ResourceVk
mStoredQueueSerial = queueSerial;
}
DeleteSchedule getDeleteSchedule(Serial lastCompletedQueueSerial) const
{
if (lastCompletedQueueSerial >= mStoredQueueSerial)
{
return DeleteSchedule::NOW;
}
else
{
return DeleteSchedule::LATER;
}
}
Serial getStoredQueueSerial() const { return mStoredQueueSerial; }
Serial getQueueSerial() const { return mStoredQueueSerial; }
private:
Serial mStoredQueueSerial;
......@@ -85,14 +66,6 @@ class ResourceVk
namespace vk
{
class Buffer;
class DeviceMemory;
class Framebuffer;
class Image;
class Pipeline;
class PipelineLayout;
class RenderPass;
class MemoryProperties final : angle::NonCopyable
{
public:
......@@ -146,6 +119,92 @@ inline Error NoError()
return Error(VK_SUCCESS);
}
// Unimplemented handle types:
// Instance
// PhysicalDevice
// Device
// Queue
// Event
// QueryPool
// BufferView
// DescriptorSet
// PipelineCache
#define ANGLE_HANDLE_TYPES_X(FUNC) \
FUNC(Semaphore) \
FUNC(CommandBuffer) \
FUNC(Fence) \
FUNC(DeviceMemory) \
FUNC(Buffer) \
FUNC(Image) \
FUNC(ImageView) \
FUNC(ShaderModule) \
FUNC(PipelineLayout) \
FUNC(RenderPass) \
FUNC(Pipeline) \
FUNC(DescriptorSetLayout) \
FUNC(Sampler) \
FUNC(DescriptorPool) \
FUNC(Framebuffer) \
FUNC(CommandPool)
#define ANGLE_COMMA_SEP_FUNC(TYPE) TYPE,
enum class HandleType
{
Invalid,
ANGLE_HANDLE_TYPES_X(ANGLE_COMMA_SEP_FUNC)
};
#undef ANGLE_COMMA_SEP_FUNC
#define ANGLE_PRE_DECLARE_CLASS_FUNC(TYPE) class TYPE;
ANGLE_HANDLE_TYPES_X(ANGLE_PRE_DECLARE_CLASS_FUNC)
#undef ANGLE_PRE_DECLARE_CLASS_FUNC
// Returns the HandleType of a Vk Handle.
template <typename T>
struct HandleTypeHelper;
// clang-format off
#define ANGLE_HANDLE_TYPE_HELPER_FUNC(TYPE) \
template<> struct HandleTypeHelper<TYPE> \
{ \
constexpr static HandleType kHandleType = HandleType::TYPE; \
};
// clang-format on
ANGLE_HANDLE_TYPES_X(ANGLE_HANDLE_TYPE_HELPER_FUNC)
#undef ANGLE_HANDLE_TYPE_HELPER_FUNC
class GarbageObject final
{
public:
template <typename ObjectT>
GarbageObject(Serial serial, const ObjectT &object)
: mSerial(serial),
mHandleType(HandleTypeHelper<ObjectT>::kHandleType),
mHandle(reinterpret_cast<VkDevice>(object.getHandle()))
{
}
GarbageObject();
GarbageObject(const GarbageObject &other);
GarbageObject &operator=(const GarbageObject &other);
bool destroyIfComplete(VkDevice device, Serial completedSerial);
void destroy(VkDevice device);
private:
// TODO(jmadill): Since many objects will have the same serial, it might be more efficient to
// store the serial outside of the garbage object itself. We could index ranges of garbage
// objects in the Renderer, using a circular buffer.
Serial mSerial;
HandleType mHandleType;
VkDevice mHandle;
};
template <typename DerivedT, typename HandleT>
class WrappedObject : angle::NonCopyable
{
......@@ -155,6 +214,15 @@ class WrappedObject : angle::NonCopyable
const HandleT *ptr() const { return &mHandle; }
void dumpResources(Serial serial, std::vector<vk::GarbageObject> *garbageQueue)
{
if (valid())
{
garbageQueue->emplace_back(serial, *static_cast<DerivedT *>(this));
mHandle = VK_NULL_HANDLE;
}
}
protected:
WrappedObject() : mHandle(VK_NULL_HANDLE) {}
WrappedObject(HandleT handle) : mHandle(handle) {}
......@@ -386,6 +454,8 @@ class StagingImage final : angle::NonCopyable
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
VkDeviceSize getSize() const { return mSize; }
void dumpResources(Serial serial, std::vector<vk::GarbageObject> *mGarbage);
private:
Image mImage;
DeviceMemory mDeviceMemory;
......@@ -511,38 +581,6 @@ class ObjectAndSerial final : angle::NonCopyable
using CommandBufferAndSerial = ObjectAndSerial<CommandBuffer>;
using FenceAndSerial = ObjectAndSerial<Fence>;
class IGarbageObject : angle::NonCopyable
{
public:
virtual ~IGarbageObject() {}
virtual bool destroyIfComplete(VkDevice device, Serial completedSerial) = 0;
virtual void destroy(VkDevice device) = 0;
};
template <typename T>
class GarbageObject final : public IGarbageObject
{
public:
GarbageObject(Serial serial, T &&object) : mSerial(serial), mObject(std::move(object)) {}
bool destroyIfComplete(VkDevice device, Serial completedSerial) override
{
if (completedSerial >= mSerial)
{
mObject.destroy(device);
return true;
}
return false;
}
void destroy(VkDevice device) override { mObject.destroy(device); }
private:
Serial mSerial;
T mObject;
};
Optional<uint32_t> FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProps,
const VkMemoryRequirements &requirements,
uint32_t propertyFlagMask);
......
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