240 lines
9.5 KiB
C++
240 lines
9.5 KiB
C++
#ifndef COMPOSITOR_VK_H
|
|
#define COMPOSITOR_VK_H
|
|
|
|
#include <array>
|
|
#include <deque>
|
|
#include <future>
|
|
#include <glm/glm.hpp>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "BorrowedImage.h"
|
|
#include "BorrowedImageVk.h"
|
|
#include "Compositor.h"
|
|
#include "Hwc2.h"
|
|
#include "aemu/base/synchronization/Lock.h"
|
|
#include "aemu/base/LruCache.h"
|
|
#include "vulkan/cereal/common/goldfish_vk_dispatch.h"
|
|
#include "vulkan/vk_util.h"
|
|
|
|
namespace gfxstream {
|
|
namespace vk {
|
|
|
|
// We do see a composition requests with 12 layers. (b/222700096)
|
|
// Inside hwc2, we will ask for surfaceflinger to
|
|
// do the composition, if the layers more than 16.
|
|
// If we see rendering error or significant time spent on updating
|
|
// descriptors in setComposition, we should tune this number.
|
|
static constexpr const uint32_t kMaxLayersPerFrame = 16;
|
|
static const uint64_t kVkWaitForFencesTimeoutNsecs = 5ULL * 1000ULL * 1000ULL * 1000ULL;
|
|
|
|
// Base used to grant visibility to members to the vk_util::* helper classes.
|
|
struct CompositorVkBase : public vk_util::MultiCrtp<CompositorVkBase, //
|
|
vk_util::FindMemoryType, //
|
|
vk_util::RunSingleTimeCommand> {
|
|
const VulkanDispatch& m_vk;
|
|
const VkDevice m_vkDevice;
|
|
const VkPhysicalDevice m_vkPhysicalDevice;
|
|
const VkQueue m_vkQueue;
|
|
const uint32_t m_queueFamilyIndex;
|
|
std::shared_ptr<android::base::Lock> m_vkQueueLock;
|
|
VkDescriptorSetLayout m_vkDescriptorSetLayout;
|
|
VkPipelineLayout m_vkPipelineLayout;
|
|
VkRenderPass m_vkRenderPass;
|
|
VkPipeline m_graphicsVkPipeline;
|
|
VkBuffer m_vertexVkBuffer;
|
|
VkDeviceMemory m_vertexVkDeviceMemory;
|
|
VkBuffer m_indexVkBuffer;
|
|
VkDeviceMemory m_indexVkDeviceMemory;
|
|
VkDescriptorPool m_vkDescriptorPool;
|
|
VkCommandPool m_vkCommandPool;
|
|
// TODO: create additional VkSampler-s for YCbCr layers.
|
|
VkSampler m_vkSampler;
|
|
|
|
// The underlying storage for all of the uniform buffer objects.
|
|
struct UniformBufferStorage {
|
|
VkBuffer m_vkBuffer = VK_NULL_HANDLE;
|
|
VkDeviceMemory m_vkDeviceMemory = VK_NULL_HANDLE;
|
|
VkDeviceSize m_stride = 0;
|
|
} m_uniformStorage;
|
|
|
|
// Keep in sync with vulkan/Compositor.frag.
|
|
struct SamplerBinding {
|
|
VkImageView sampledImageView = VK_NULL_HANDLE;
|
|
};
|
|
|
|
// Keep in sync with vulkan/Compositor.vert.
|
|
struct UniformBufferBinding {
|
|
alignas(16) glm::mat4 positionTransform;
|
|
alignas(16) glm::mat4 texCoordTransform;
|
|
};
|
|
|
|
// The cached contents of a given descriptor set.
|
|
struct DescriptorSetContents {
|
|
SamplerBinding binding0;
|
|
UniformBufferBinding binding1;
|
|
};
|
|
|
|
// The cached contents of all descriptors sets of a given frame.
|
|
struct FrameDescriptorSetsContents {
|
|
std::vector<DescriptorSetContents> descriptorSets;
|
|
};
|
|
|
|
friend bool operator==(const DescriptorSetContents& lhs, const DescriptorSetContents& rhs);
|
|
|
|
friend bool operator==(const FrameDescriptorSetsContents& lhs,
|
|
const FrameDescriptorSetsContents& rhs);
|
|
|
|
struct PerFrameResources {
|
|
VkFence m_vkFence = VK_NULL_HANDLE;
|
|
VkCommandBuffer m_vkCommandBuffer = VK_NULL_HANDLE;
|
|
std::vector<VkDescriptorSet> m_layerDescriptorSets;
|
|
// Pointers into the underlying uniform buffer storage for the uniform
|
|
// buffer of part of each descriptor set for each layer.
|
|
std::vector<UniformBufferBinding*> m_layerUboStorages;
|
|
std::optional<FrameDescriptorSetsContents> m_vkDescriptorSetsContents;
|
|
};
|
|
std::vector<PerFrameResources> m_frameResources;
|
|
std::deque<std::shared_future<PerFrameResources*>> m_availableFrameResources;
|
|
|
|
explicit CompositorVkBase(const VulkanDispatch& vk, VkDevice device,
|
|
VkPhysicalDevice physicalDevice, VkQueue queue,
|
|
std::shared_ptr<android::base::Lock> queueLock,
|
|
uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
|
|
: m_vk(vk),
|
|
m_vkDevice(device),
|
|
m_vkPhysicalDevice(physicalDevice),
|
|
m_vkQueue(queue),
|
|
m_queueFamilyIndex(queueFamilyIndex),
|
|
m_vkQueueLock(queueLock),
|
|
m_vkDescriptorSetLayout(VK_NULL_HANDLE),
|
|
m_vkPipelineLayout(VK_NULL_HANDLE),
|
|
m_vkRenderPass(VK_NULL_HANDLE),
|
|
m_graphicsVkPipeline(VK_NULL_HANDLE),
|
|
m_vertexVkBuffer(VK_NULL_HANDLE),
|
|
m_vertexVkDeviceMemory(VK_NULL_HANDLE),
|
|
m_indexVkBuffer(VK_NULL_HANDLE),
|
|
m_indexVkDeviceMemory(VK_NULL_HANDLE),
|
|
m_vkDescriptorPool(VK_NULL_HANDLE),
|
|
m_vkCommandPool(VK_NULL_HANDLE),
|
|
m_vkSampler(VK_NULL_HANDLE),
|
|
m_frameResources(maxFramesInFlight) {}
|
|
};
|
|
|
|
class CompositorVk : protected CompositorVkBase, public Compositor {
|
|
public:
|
|
static std::unique_ptr<CompositorVk> create(const VulkanDispatch& vk, VkDevice vkDevice,
|
|
VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
|
|
std::shared_ptr<android::base::Lock> queueLock,
|
|
uint32_t queueFamilyIndex,
|
|
uint32_t maxFramesInFlight);
|
|
|
|
~CompositorVk();
|
|
|
|
CompositionFinishedWaitable compose(const CompositionRequest& compositionRequest) override;
|
|
|
|
void onImageDestroyed(uint32_t imageId) override;
|
|
|
|
static bool queueSupportsComposition(const VkQueueFamilyProperties& properties) {
|
|
return properties.queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
|
}
|
|
|
|
private:
|
|
explicit CompositorVk(const VulkanDispatch&, VkDevice, VkPhysicalDevice, VkQueue,
|
|
std::shared_ptr<android::base::Lock> queueLock, uint32_t queueFamilyIndex,
|
|
uint32_t maxFramesInFlight);
|
|
|
|
void setUpGraphicsPipeline();
|
|
void setUpVertexBuffers();
|
|
void setUpSampler();
|
|
void setUpDescriptorSets();
|
|
void setUpUniformBuffers();
|
|
void setUpCommandPool();
|
|
void setUpFences();
|
|
void setUpFrameResourceFutures();
|
|
|
|
std::optional<std::tuple<VkBuffer, VkDeviceMemory>> createBuffer(VkDeviceSize,
|
|
VkBufferUsageFlags,
|
|
VkMemoryPropertyFlags) const;
|
|
std::tuple<VkBuffer, VkDeviceMemory> createStagingBufferWithData(const void* data,
|
|
VkDeviceSize size) const;
|
|
void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize) const;
|
|
|
|
VkFormatFeatureFlags getFormatFeatures(VkFormat format, VkImageTiling tiling);
|
|
|
|
// Check if the ColorBuffer can be used as a compose layer to be sampled from.
|
|
bool canCompositeFrom(const VkImageCreateInfo& info);
|
|
|
|
// Check if the ColorBuffer can be used as a render target of a composition.
|
|
bool canCompositeTo(const VkImageCreateInfo& info);
|
|
|
|
// A consolidated view of a `Compositor::CompositionRequestLayer` with only
|
|
// the Vulkan components needed for command recording and submission.
|
|
struct CompositionLayerVk {
|
|
VkImage image = VK_NULL_HANDLE;
|
|
VkImageView imageView = VK_NULL_HANDLE;
|
|
VkImageLayout preCompositionLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
uint32_t preCompositionQueueFamilyIndex = 0;
|
|
VkImageLayout postCompositionLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
uint32_t postCompositionQueueFamilyIndex = 0;
|
|
};
|
|
|
|
// A consolidated view of a `Compositor::CompositionRequest` with only
|
|
// the Vulkan components needed for command recording and submission.
|
|
struct CompositionVk {
|
|
const BorrowedImageInfoVk* targetImage = nullptr;
|
|
VkFramebuffer targetFramebuffer = VK_NULL_HANDLE;
|
|
std::vector<const BorrowedImageInfoVk*> layersSourceImages;
|
|
FrameDescriptorSetsContents layersDescriptorSets;
|
|
};
|
|
void buildCompositionVk(const CompositionRequest& compositionRequest,
|
|
CompositionVk* compositionVk);
|
|
|
|
void updateDescriptorSetsIfChanged(const FrameDescriptorSetsContents& contents,
|
|
PerFrameResources* frameResources);
|
|
|
|
class RenderTarget {
|
|
public:
|
|
~RenderTarget();
|
|
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(RenderTarget);
|
|
|
|
private:
|
|
friend class CompositorVk;
|
|
RenderTarget(const VulkanDispatch& vk, VkDevice vkDevice, VkImage vkImage,
|
|
VkImageView vkImageView, uint32_t width, uint32_t height,
|
|
VkRenderPass vkRenderPass);
|
|
|
|
const VulkanDispatch& m_vk;
|
|
VkDevice m_vkDevice;
|
|
VkImage m_vkImage;
|
|
VkFramebuffer m_vkFramebuffer;
|
|
uint32_t m_width;
|
|
uint32_t m_height;
|
|
};
|
|
|
|
// Gets the RenderTarget used for composing into the given image if it already exists,
|
|
// otherwise creates it.
|
|
RenderTarget* getOrCreateRenderTargetInfo(const BorrowedImageInfoVk& info);
|
|
|
|
// Cached format properties used for checking if composition is supported with a given
|
|
// format.
|
|
std::unordered_map<VkFormat, VkFormatProperties> m_vkFormatProperties;
|
|
|
|
uint32_t m_maxFramesInFlight = 0;
|
|
|
|
static constexpr const VkFormat k_renderTargetFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
|
static constexpr const uint32_t k_renderTargetCacheSize = 128;
|
|
// Maps from borrowed image ids to render target info.
|
|
android::base::LruCache<uint32_t, std::unique_ptr<RenderTarget>> m_renderTargetCache;
|
|
};
|
|
|
|
} // namespace vk
|
|
} // namespace gfxstream
|
|
|
|
#endif /* COMPOSITOR_VK_H */
|