| // |
| // Copyright 2016 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // renderervk_utils: |
| // Helper functions for the Vulkan Renderer. |
| // |
| |
| #ifndef LIBANGLE_RENDERER_VULKAN_RENDERERVK_UTILS_H_ |
| #define LIBANGLE_RENDERER_VULKAN_RENDERERVK_UTILS_H_ |
| |
| #include <limits> |
| |
| #include <vulkan/vulkan.h> |
| |
| #include "common/debug.h" |
| #include "common/Optional.h" |
| #include "libANGLE/Error.h" |
| |
| namespace gl |
| { |
| struct Box; |
| struct Extents; |
| struct RasterizerState; |
| struct Rectangle; |
| } |
| |
| namespace rx |
| { |
| const char *VulkanResultString(VkResult result); |
| bool HasStandardValidationLayer(const std::vector<VkLayerProperties> &layerProps); |
| |
| extern const char *g_VkStdValidationLayerName; |
| |
| enum class TextureDimension |
| { |
| TEX_2D, |
| TEX_CUBE, |
| TEX_3D, |
| TEX_2D_ARRAY, |
| }; |
| |
| enum DeleteSchedule |
| { |
| NOW, |
| LATER, |
| }; |
| |
| // A serial supports a few operations - comparison, increment, and assignment. |
| // TODO(jmadill): Verify it's not easy to overflow the queue serial. |
| class Serial final |
| { |
| public: |
| Serial() : mValue(0) {} |
| Serial(const Serial &other) : mValue(other.mValue) {} |
| Serial(Serial &&other) : mValue(other.mValue) { other.mValue = 0; } |
| Serial &operator=(const Serial &other) |
| { |
| mValue = other.mValue; |
| return *this; |
| } |
| bool operator>=(Serial other) const { return mValue >= other.mValue; } |
| bool operator>(Serial other) const { return mValue > other.mValue; } |
| |
| // This function fails if we're at the limits of our counting. |
| bool operator++() |
| { |
| if (mValue == std::numeric_limits<uint32_t>::max()) |
| return false; |
| mValue++; |
| return true; |
| } |
| |
| private: |
| uint32_t mValue; |
| }; |
| |
| // 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. |
| class ResourceVk |
| { |
| public: |
| void setQueueSerial(Serial queueSerial) |
| { |
| ASSERT(queueSerial >= mStoredQueueSerial); |
| mStoredQueueSerial = queueSerial; |
| } |
| |
| DeleteSchedule getDeleteSchedule(Serial lastCompletedQueueSerial) const |
| { |
| if (lastCompletedQueueSerial >= mStoredQueueSerial) |
| { |
| return DeleteSchedule::NOW; |
| } |
| else |
| { |
| return DeleteSchedule::LATER; |
| } |
| } |
| |
| Serial getStoredQueueSerial() const { return mStoredQueueSerial; } |
| |
| private: |
| Serial mStoredQueueSerial; |
| }; |
| |
| namespace vk |
| { |
| class DeviceMemory; |
| class Framebuffer; |
| class Image; |
| class Pipeline; |
| class RenderPass; |
| |
| class Error final |
| { |
| public: |
| Error(VkResult result); |
| Error(VkResult result, const char *file, unsigned int line); |
| ~Error(); |
| |
| Error(const Error &other); |
| Error &operator=(const Error &other); |
| |
| gl::Error toGL(GLenum glErrorCode) const; |
| egl::Error toEGL(EGLint eglErrorCode) const; |
| |
| operator gl::Error() const; |
| operator egl::Error() const; |
| template <typename T> |
| operator gl::ErrorOrResult<T>() const |
| { |
| return static_cast<gl::Error>(*this); |
| } |
| |
| bool isError() const; |
| |
| std::string toString() const; |
| |
| private: |
| VkResult mResult; |
| const char *mFile; |
| unsigned int mLine; |
| }; |
| |
| template <typename ResultT> |
| using ErrorOrResult = angle::ErrorOrResultBase<Error, ResultT, VkResult, VK_SUCCESS>; |
| |
| // Avoid conflicting with X headers which define "Success". |
| inline Error NoError() |
| { |
| return Error(VK_SUCCESS); |
| } |
| |
| template <typename DerivedT, typename HandleT> |
| class WrappedObject : angle::NonCopyable |
| { |
| public: |
| HandleT getHandle() const { return mHandle; } |
| bool valid() const { return (mHandle != VK_NULL_HANDLE); } |
| |
| const HandleT *ptr() const { return &mHandle; } |
| |
| protected: |
| WrappedObject() : mHandle(VK_NULL_HANDLE) {} |
| WrappedObject(HandleT handle) : mHandle(handle) {} |
| ~WrappedObject() { ASSERT(!valid()); } |
| |
| WrappedObject(WrappedObject &&other) : mHandle(other.mHandle) |
| { |
| other.mHandle = VK_NULL_HANDLE; |
| } |
| |
| // Only works to initialize empty objects, since we don't have the device handle. |
| WrappedObject &operator=(WrappedObject &&other) |
| { |
| ASSERT(!valid()); |
| std::swap(mHandle, other.mHandle); |
| return *this; |
| } |
| |
| void retain(VkDevice device, DerivedT &&other) |
| { |
| if (valid()) |
| { |
| static_cast<DerivedT *>(this)->destroy(device); |
| } |
| std::swap(mHandle, other.mHandle); |
| } |
| |
| HandleT mHandle; |
| }; |
| |
| class CommandPool final : public WrappedObject<CommandPool, VkCommandPool> |
| { |
| public: |
| CommandPool(); |
| |
| void destroy(VkDevice device); |
| |
| Error init(VkDevice device, const VkCommandPoolCreateInfo &createInfo); |
| }; |
| |
| // Helper class that wraps a Vulkan command buffer. |
| class CommandBuffer final : public WrappedObject<CommandBuffer, VkCommandBuffer> |
| { |
| public: |
| CommandBuffer(); |
| |
| bool started() const { return mStarted; } |
| |
| void destroy(VkDevice device); |
| using WrappedObject::operator=; |
| |
| void setCommandPool(CommandPool *commandPool); |
| Error begin(VkDevice device); |
| Error end(); |
| Error reset(); |
| |
| void singleImageBarrier(VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| VkDependencyFlags dependencyFlags, |
| const VkImageMemoryBarrier &imageMemoryBarrier); |
| |
| void clearSingleColorImage(const vk::Image &image, const VkClearColorValue &color); |
| |
| void copySingleImage(const vk::Image &srcImage, |
| const vk::Image &destImage, |
| const gl::Box ©Region, |
| VkImageAspectFlags aspectMask); |
| |
| void beginRenderPass(const RenderPass &renderPass, |
| const Framebuffer &framebuffer, |
| const gl::Rectangle &renderArea, |
| const std::vector<VkClearValue> &clearValues); |
| void endRenderPass(); |
| |
| void draw(uint32_t vertexCount, |
| uint32_t instanceCount, |
| uint32_t firstVertex, |
| uint32_t firstInstance); |
| |
| void bindPipeline(VkPipelineBindPoint pipelineBindPoint, const vk::Pipeline &pipeline); |
| void bindVertexBuffers(uint32_t firstBinding, |
| const std::vector<VkBuffer> &buffers, |
| const std::vector<VkDeviceSize> &offsets); |
| |
| private: |
| bool mStarted; |
| CommandPool *mCommandPool; |
| }; |
| |
| class Image final : public WrappedObject<Image, VkImage> |
| { |
| public: |
| // Use this constructor if the lifetime of the image is not controlled by ANGLE. (SwapChain) |
| Image(); |
| explicit Image(VkImage image); |
| |
| // Called on shutdown when the helper class *doesn't* own the handle to the image resource. |
| void reset(); |
| |
| // Called on shutdown when the helper class *does* own the handle to the image resource. |
| void destroy(VkDevice device); |
| |
| void retain(VkDevice device, Image &&other); |
| |
| Error init(VkDevice device, const VkImageCreateInfo &createInfo); |
| |
| void changeLayoutTop(VkImageAspectFlags aspectMask, |
| VkImageLayout newLayout, |
| CommandBuffer *commandBuffer); |
| |
| void changeLayoutWithStages(VkImageAspectFlags aspectMask, |
| VkImageLayout newLayout, |
| VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| CommandBuffer *commandBuffer); |
| |
| void getMemoryRequirements(VkDevice device, VkMemoryRequirements *requirementsOut) const; |
| Error bindMemory(VkDevice device, const vk::DeviceMemory &deviceMemory); |
| |
| VkImageLayout getCurrentLayout() const { return mCurrentLayout; } |
| void updateLayout(VkImageLayout layout) { mCurrentLayout = layout; } |
| |
| private: |
| VkImageLayout mCurrentLayout; |
| }; |
| |
| class ImageView final : public WrappedObject<ImageView, VkImageView> |
| { |
| public: |
| ImageView(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device, const VkImageViewCreateInfo &createInfo); |
| }; |
| |
| class Semaphore final : public WrappedObject<Semaphore, VkSemaphore> |
| { |
| public: |
| Semaphore(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device); |
| }; |
| |
| class Framebuffer final : public WrappedObject<Framebuffer, VkFramebuffer> |
| { |
| public: |
| Framebuffer(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device, const VkFramebufferCreateInfo &createInfo); |
| }; |
| |
| class DeviceMemory final : public WrappedObject<DeviceMemory, VkDeviceMemory> |
| { |
| public: |
| DeviceMemory(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error allocate(VkDevice device, const VkMemoryAllocateInfo &allocInfo); |
| Error map(VkDevice device, |
| VkDeviceSize offset, |
| VkDeviceSize size, |
| VkMemoryMapFlags flags, |
| uint8_t **mapPointer); |
| void unmap(VkDevice device); |
| }; |
| |
| class RenderPass final : public WrappedObject<RenderPass, VkRenderPass> |
| { |
| public: |
| RenderPass(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device, const VkRenderPassCreateInfo &createInfo); |
| }; |
| |
| class StagingImage final : angle::NonCopyable |
| { |
| public: |
| StagingImage(); |
| StagingImage(StagingImage &&other); |
| void destroy(VkDevice device); |
| void retain(VkDevice device, StagingImage &&other); |
| |
| vk::Error init(VkDevice device, |
| uint32_t queueFamilyIndex, |
| uint32_t hostVisibleMemoryIndex, |
| TextureDimension dimension, |
| VkFormat format, |
| const gl::Extents &extent); |
| |
| Image &getImage() { return mImage; } |
| const Image &getImage() const { return mImage; } |
| DeviceMemory &getDeviceMemory() { return mDeviceMemory; } |
| const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; } |
| VkDeviceSize getSize() const { return mSize; } |
| |
| private: |
| Image mImage; |
| DeviceMemory mDeviceMemory; |
| VkDeviceSize mSize; |
| }; |
| |
| class Buffer final : public WrappedObject<Buffer, VkBuffer> |
| { |
| public: |
| Buffer(); |
| void destroy(VkDevice device); |
| void retain(VkDevice device, Buffer &&other); |
| |
| Error init(VkDevice device, const VkBufferCreateInfo &createInfo); |
| Error bindMemory(VkDevice device); |
| |
| DeviceMemory &getMemory() { return mMemory; } |
| const DeviceMemory &getMemory() const { return mMemory; } |
| |
| private: |
| DeviceMemory mMemory; |
| }; |
| |
| class ShaderModule final : public WrappedObject<ShaderModule, VkShaderModule> |
| { |
| public: |
| ShaderModule(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device, const VkShaderModuleCreateInfo &createInfo); |
| }; |
| |
| class Pipeline final : public WrappedObject<Pipeline, VkPipeline> |
| { |
| public: |
| Pipeline(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error initGraphics(VkDevice device, const VkGraphicsPipelineCreateInfo &createInfo); |
| }; |
| |
| class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayout> |
| { |
| public: |
| PipelineLayout(); |
| void destroy(VkDevice device); |
| using WrappedObject::retain; |
| |
| Error init(VkDevice device, const VkPipelineLayoutCreateInfo &createInfo); |
| }; |
| |
| class Fence final : public WrappedObject<Fence, VkFence> |
| { |
| public: |
| Fence(); |
| void destroy(VkDevice fence); |
| using WrappedObject::retain; |
| using WrappedObject::operator=; |
| |
| Error init(VkDevice device, const VkFenceCreateInfo &createInfo); |
| VkResult getStatus(VkDevice device) const; |
| }; |
| |
| template <typename ObjT> |
| class ObjectAndSerial final : angle::NonCopyable |
| { |
| public: |
| ObjectAndSerial(ObjT &&object, Serial queueSerial) |
| : mObject(std::move(object)), mQueueSerial(queueSerial) |
| { |
| } |
| |
| ObjectAndSerial(ObjectAndSerial &&other) |
| : mObject(std::move(other.mObject)), mQueueSerial(std::move(other.mQueueSerial)) |
| { |
| } |
| ObjectAndSerial &operator=(ObjectAndSerial &&other) |
| { |
| mObject = std::move(other.mObject); |
| mQueueSerial = std::move(other.mQueueSerial); |
| return *this; |
| } |
| |
| void destroy(VkDevice device) { mObject.destroy(device); } |
| |
| Serial queueSerial() const { return mQueueSerial; } |
| |
| const ObjT &get() const { return mObject; } |
| |
| private: |
| ObjT mObject; |
| Serial mQueueSerial; |
| }; |
| |
| 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; |
| }; |
| |
| } // namespace vk |
| |
| Optional<uint32_t> FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProps, |
| const VkMemoryRequirements &requirements, |
| uint32_t propertyFlagMask); |
| |
| namespace gl_vk |
| { |
| VkPrimitiveTopology GetPrimitiveTopology(GLenum mode); |
| VkCullModeFlags GetCullMode(const gl::RasterizerState &rasterState); |
| VkFrontFace GetFrontFace(GLenum frontFace); |
| } // namespace gl_vk |
| |
| } // namespace rx |
| |
| #define ANGLE_VK_TRY(command) \ |
| { \ |
| auto ANGLE_LOCAL_VAR = command; \ |
| if (ANGLE_LOCAL_VAR != VK_SUCCESS) \ |
| { \ |
| return rx::vk::Error(ANGLE_LOCAL_VAR, __FILE__, __LINE__); \ |
| } \ |
| } \ |
| ANGLE_EMPTY_STATEMENT |
| |
| #define ANGLE_VK_CHECK(test, error) ANGLE_VK_TRY(test ? VK_SUCCESS : error) |
| |
| std::ostream &operator<<(std::ostream &stream, const rx::vk::Error &error); |
| |
| #endif // LIBANGLE_RENDERER_VULKAN_RENDERERVK_UTILS_H_ |