blob: 20f8f2e9e09969846ce8fe1bb636f4d9281f7bbe [file] [log] [blame]
//
// 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 &copyRegion,
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_