summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--stream-servers/CompositorVk.cpp306
-rw-r--r--stream-servers/CompositorVk.h26
-rw-r--r--stream-servers/tests/CompositorVk_unittest.cpp126
3 files changed, 432 insertions, 26 deletions
diff --git a/stream-servers/CompositorVk.cpp b/stream-servers/CompositorVk.cpp
index 1dc0604d..27197e57 100644
--- a/stream-servers/CompositorVk.cpp
+++ b/stream-servers/CompositorVk.cpp
@@ -1,5 +1,7 @@
#include "CompositorVk.h"
+#include <optional>
+
#include "vulkan/vk_util.h"
namespace CompositorVkShader {
@@ -7,14 +9,305 @@ namespace CompositorVkShader {
#include "vulkan/CompositorVertexShader.h"
} // namespace CompositorVkShader
+static VkShaderModule createShaderModule(const goldfish_vk::VulkanDispatch &vk,
+ VkDevice device,
+ const std::vector<uint32_t> &code) {
+ VkShaderModuleCreateInfo shaderModuleCi = {};
+ shaderModuleCi.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderModuleCi.codeSize =
+ static_cast<uint32_t>(code.size() * sizeof(uint32_t));
+ shaderModuleCi.pCode = code.data();
+ VkShaderModule res;
+ VK_CHECK(vk.vkCreateShaderModule(device, &shaderModuleCi, nullptr, &res));
+ return res;
+}
+
std::unique_ptr<CompositorVk> CompositorVk::create(
- const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice) {
- return std::unique_ptr<CompositorVk>(new CompositorVk(vk, vkDevice));
+ const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, VkFormat format,
+ VkImageLayout initialLayout, VkImageLayout finalLayout, uint32_t width,
+ uint32_t height, const std::vector<VkImageView> &renderTargets,
+ VkCommandPool commandPool) {
+ auto res = std::unique_ptr<CompositorVk>(
+ new CompositorVk(vk, vkDevice, commandPool));
+ res->setUpGraphicsPipeline(width, height, format, initialLayout,
+ finalLayout);
+ res->setUpFramebuffers(renderTargets, width, height);
+ res->setUpCommandBuffers(width, height);
+ return res;
}
CompositorVk::CompositorVk(const goldfish_vk::VulkanDispatch &vk,
- VkDevice vkDevice)
- : m_vk(vk), m_vkDevice(vkDevice) {}
+ VkDevice vkDevice, VkCommandPool vkCommandPool)
+ : m_vk(vk),
+ m_vkDevice(vkDevice),
+ m_vkPipelineLayout(VK_NULL_HANDLE),
+ m_vkRenderPass(VK_NULL_HANDLE),
+ m_graphicsVkPipeline(VK_NULL_HANDLE),
+ m_vkCommandBuffers(0),
+ m_renderTargetVkFrameBuffers(0),
+ m_vkCommandPool(vkCommandPool) {}
+
+CompositorVk::~CompositorVk() {
+ m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool,
+ static_cast<uint32_t>(m_vkCommandBuffers.size()),
+ m_vkCommandBuffers.data());
+ while (!m_renderTargetVkFrameBuffers.empty()) {
+ m_vk.vkDestroyFramebuffer(m_vkDevice,
+ m_renderTargetVkFrameBuffers.back(), nullptr);
+ m_renderTargetVkFrameBuffers.pop_back();
+ }
+ if (m_graphicsVkPipeline != VK_NULL_HANDLE) {
+ m_vk.vkDestroyPipeline(m_vkDevice, m_graphicsVkPipeline, nullptr);
+ }
+ if (m_vkRenderPass != VK_NULL_HANDLE) {
+ m_vk.vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr);
+ }
+ if (m_vkPipelineLayout != VK_NULL_HANDLE) {
+ m_vk.vkDestroyPipelineLayout(m_vkDevice, m_vkPipelineLayout, nullptr);
+ }
+}
+
+void CompositorVk::setUpGraphicsPipeline(uint32_t width, uint32_t height,
+ VkFormat renderTargetFormat,
+ VkImageLayout initialLayout,
+ VkImageLayout finalLayout) {
+ const std::vector<uint32_t> vertSpvBuff(
+ CompositorVkShader::compositorVertexShader,
+ std::end(CompositorVkShader::compositorVertexShader));
+ const std::vector<uint32_t> fragSpvBuff(
+ CompositorVkShader::compositorFragmentShader,
+ std::end(CompositorVkShader::compositorFragmentShader));
+ const auto vertShaderMod =
+ createShaderModule(m_vk, m_vkDevice, vertSpvBuff);
+ const auto fragShaderMod =
+ createShaderModule(m_vk, m_vkDevice, fragSpvBuff);
+
+ VkPipelineShaderStageCreateInfo shaderStageCis[2] = {};
+ shaderStageCis[0].sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderStageCis[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+ shaderStageCis[0].module = vertShaderMod;
+ shaderStageCis[0].pName = "main";
+ shaderStageCis[1].sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderStageCis[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ shaderStageCis[1].module = fragShaderMod;
+ shaderStageCis[1].pName = "main";
+
+ VkPipelineVertexInputStateCreateInfo vertexInputStateCi = {};
+ vertexInputStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputStateCi.vertexBindingDescriptionCount = 0;
+ vertexInputStateCi.vertexAttributeDescriptionCount = 0;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = {};
+ inputAssemblyStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssemblyStateCi.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ inputAssemblyStateCi.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = static_cast<float>(width);
+ viewport.height = static_cast<float>(height);
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {};
+ scissor.offset = {0, 0};
+ scissor.extent.width = width;
+ scissor.extent.height = height;
+
+ VkPipelineViewportStateCreateInfo viewportStateCi = {};
+ viewportStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportStateCi.viewportCount = 1;
+ viewportStateCi.pViewports = &viewport;
+ viewportStateCi.scissorCount = 1;
+ viewportStateCi.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizerStateCi = {};
+ rasterizerStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizerStateCi.depthClampEnable = VK_FALSE;
+ rasterizerStateCi.rasterizerDiscardEnable = VK_FALSE;
+ rasterizerStateCi.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizerStateCi.lineWidth = 1.0f;
+ rasterizerStateCi.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizerStateCi.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ rasterizerStateCi.depthBiasEnable = VK_FALSE;
+ rasterizerStateCi.depthBiasConstantFactor = 0.0f;
+ rasterizerStateCi.depthBiasClamp = 0.0f;
+ rasterizerStateCi.depthBiasSlopeFactor = 0.0f;
+
+ VkPipelineMultisampleStateCreateInfo multisampleStateCi = {};
+ multisampleStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampleStateCi.sampleShadingEnable = VK_FALSE;
+ multisampleStateCi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ multisampleStateCi.minSampleShading = 1.0f;
+ multisampleStateCi.pSampleMask = nullptr;
+ multisampleStateCi.alphaToCoverageEnable = VK_FALSE;
+ multisampleStateCi.alphaToOneEnable = VK_FALSE;
+
+ VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
+ colorBlendAttachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.blendEnable = VK_TRUE;
+ colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ colorBlendAttachment.dstColorBlendFactor =
+ VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
+ colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
+
+ VkPipelineColorBlendStateCreateInfo colorBlendStateCi = {};
+ colorBlendStateCi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlendStateCi.logicOpEnable = VK_FALSE;
+ colorBlendStateCi.attachmentCount = 1;
+ colorBlendStateCi.pAttachments = &colorBlendAttachment;
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCi = {};
+ pipelineLayoutCi.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutCi.setLayoutCount = 0;
+ pipelineLayoutCi.pushConstantRangeCount = 0;
+
+ VK_CHECK(m_vk.vkCreatePipelineLayout(m_vkDevice, &pipelineLayoutCi, nullptr,
+ &m_vkPipelineLayout));
+
+ VkAttachmentDescription colorAttachment = {};
+ colorAttachment.format = renderTargetFormat;
+ colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
+ colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ colorAttachment.initialLayout = initialLayout;
+ colorAttachment.finalLayout = finalLayout;
+
+ VkAttachmentReference colorAttachmentRef = {};
+ colorAttachmentRef.attachment = 0;
+ colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ VkSubpassDescription subpass = {};
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.colorAttachmentCount = 1;
+ subpass.pColorAttachments = &colorAttachmentRef;
+
+ VkSubpassDependency subpassDependency = {};
+ subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ subpassDependency.dstSubpass = 0;
+ subpassDependency.srcStageMask =
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDependency.srcAccessMask = 0;
+ subpassDependency.dstStageMask =
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkRenderPassCreateInfo renderPassCi = {};
+ renderPassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ renderPassCi.attachmentCount = 1;
+ renderPassCi.pAttachments = &colorAttachment;
+ renderPassCi.subpassCount = 1;
+ renderPassCi.pSubpasses = &subpass;
+ renderPassCi.dependencyCount = 1;
+ renderPassCi.pDependencies = &subpassDependency;
+
+ VK_CHECK(m_vk.vkCreateRenderPass(m_vkDevice, &renderPassCi, nullptr,
+ &m_vkRenderPass));
+
+ VkGraphicsPipelineCreateInfo graphicsPipelineCi = {};
+ graphicsPipelineCi.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ graphicsPipelineCi.stageCount =
+ static_cast<uint32_t>(std::size(shaderStageCis));
+ graphicsPipelineCi.pStages = shaderStageCis;
+ graphicsPipelineCi.pVertexInputState = &vertexInputStateCi;
+ graphicsPipelineCi.pInputAssemblyState = &inputAssemblyStateCi;
+ graphicsPipelineCi.pViewportState = &viewportStateCi;
+ graphicsPipelineCi.pRasterizationState = &rasterizerStateCi;
+ graphicsPipelineCi.pMultisampleState = &multisampleStateCi;
+ graphicsPipelineCi.pDepthStencilState = nullptr;
+ graphicsPipelineCi.pColorBlendState = &colorBlendStateCi;
+ graphicsPipelineCi.pDynamicState = nullptr;
+ graphicsPipelineCi.layout = m_vkPipelineLayout;
+ graphicsPipelineCi.renderPass = m_vkRenderPass;
+ graphicsPipelineCi.subpass = 0;
+ graphicsPipelineCi.basePipelineHandle = VK_NULL_HANDLE;
+ graphicsPipelineCi.basePipelineIndex = -1;
+
+ VK_CHECK(m_vk.vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1,
+ &graphicsPipelineCi, nullptr,
+ &m_graphicsVkPipeline));
+
+ m_vk.vkDestroyShaderModule(m_vkDevice, vertShaderMod, nullptr);
+ m_vk.vkDestroyShaderModule(m_vkDevice, fragShaderMod, nullptr);
+}
+
+void CompositorVk::setUpFramebuffers(
+ const std::vector<VkImageView> &renderTargets, uint32_t width,
+ uint32_t height) {
+ for (size_t i = 0; i < renderTargets.size(); i++) {
+ VkFramebufferCreateInfo fbCi = {};
+ fbCi.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbCi.renderPass = m_vkRenderPass;
+ fbCi.attachmentCount = 1;
+ fbCi.pAttachments = &renderTargets[i];
+ fbCi.width = width;
+ fbCi.height = height;
+ fbCi.layers = 1;
+ VkFramebuffer framebuffer;
+ VK_CHECK(
+ m_vk.vkCreateFramebuffer(m_vkDevice, &fbCi, nullptr, &framebuffer));
+ m_renderTargetVkFrameBuffers.push_back(framebuffer);
+ }
+}
+
+void CompositorVk::setUpCommandBuffers(uint32_t width, uint32_t height) {
+ VkCommandBufferAllocateInfo cmdBuffAllocInfo = {};
+ cmdBuffAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBuffAllocInfo.commandPool = m_vkCommandPool;
+ cmdBuffAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cmdBuffAllocInfo.commandBufferCount =
+ static_cast<uint32_t>(m_renderTargetVkFrameBuffers.size());
+ m_vkCommandBuffers.resize(m_renderTargetVkFrameBuffers.size());
+ VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &cmdBuffAllocInfo,
+ m_vkCommandBuffers.data()));
+
+ for (size_t i = 0; i < m_vkCommandBuffers.size(); i++) {
+ VkCommandBufferBeginInfo beginInfo = {};
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ const auto &cmdBuffer = m_vkCommandBuffers[i];
+ VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuffer, &beginInfo));
+
+ VkClearValue clearColor = {};
+ clearColor.color.float32[0] = 0.0f;
+ clearColor.color.float32[1] = 0.0f;
+ clearColor.color.float32[2] = 0.0f;
+ clearColor.color.float32[3] = 1.0f;
+
+ VkRenderPassBeginInfo renderPassBeginInfo = {};
+ renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ renderPassBeginInfo.renderPass = m_vkRenderPass;
+ renderPassBeginInfo.framebuffer = m_renderTargetVkFrameBuffers[i];
+ renderPassBeginInfo.clearValueCount = 1;
+ renderPassBeginInfo.pClearValues = &clearColor;
+ renderPassBeginInfo.renderArea.offset = {0, 0};
+ renderPassBeginInfo.renderArea.extent = {width, height};
+
+ m_vk.vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo,
+ VK_SUBPASS_CONTENTS_INLINE);
+ m_vk.vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ m_graphicsVkPipeline);
+ m_vk.vkCmdDraw(cmdBuffer, 0, 1, 0, 0);
+ m_vk.vkCmdEndRenderPass(cmdBuffer);
+
+ VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuffer));
+ }
+}
bool CompositorVk::validatePhysicalDeviceFeatures(
const VkPhysicalDeviceFeatures2 &features) {
@@ -45,6 +338,11 @@ bool CompositorVk::enablePhysicalDeviceFeatures(
VK_TRUE;
return true;
}
+
std::vector<const char *> CompositorVk::getRequiredDeviceExtensions() {
return {VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME};
+}
+
+VkCommandBuffer CompositorVk::getCommandBuffer(uint32_t i) const {
+ return m_vkCommandBuffers[i];
} \ No newline at end of file
diff --git a/stream-servers/CompositorVk.h b/stream-servers/CompositorVk.h
index 6a52dc2d..e79ff0b7 100644
--- a/stream-servers/CompositorVk.h
+++ b/stream-servers/CompositorVk.h
@@ -2,6 +2,7 @@
#define COMPOSITOR_VK_H
#include <memory>
+#include <variant>
#include <vector>
#include "vulkan/cereal/common/goldfish_vk_dispatch.h"
@@ -9,7 +10,10 @@
class CompositorVk {
public:
static std::unique_ptr<CompositorVk> create(
- const goldfish_vk::VulkanDispatch &vk, VkDevice);
+ const goldfish_vk::VulkanDispatch &vk, VkDevice, VkFormat,
+ VkImageLayout initialLayout, VkImageLayout finalLayout, uint32_t width,
+ uint32_t height, const std::vector<VkImageView> &renderTargets,
+ VkCommandPool);
static bool validatePhysicalDeviceFeatures(
const VkPhysicalDeviceFeatures2 &features);
static bool validateQueueFamilyProperties(
@@ -18,11 +22,29 @@ class CompositorVk {
VkPhysicalDeviceFeatures2 &features);
static std::vector<const char *> getRequiredDeviceExtensions();
+ ~CompositorVk();
+ VkCommandBuffer getCommandBuffer(uint32_t i) const;
+
private:
- explicit CompositorVk(const goldfish_vk::VulkanDispatch &, VkDevice);
+ explicit CompositorVk(const goldfish_vk::VulkanDispatch &, VkDevice,
+ VkCommandPool);
+ void setUpGraphicsPipeline(uint32_t width, uint32_t height,
+ VkFormat renderTargetFormat,
+ VkImageLayout initialLayout,
+ VkImageLayout finalLayout);
+ void setUpFramebuffers(const std::vector<VkImageView> &, uint32_t width,
+ uint32_t height);
+ void setUpCommandBuffers(uint32_t width, uint32_t height);
const goldfish_vk::VulkanDispatch &m_vk;
const VkDevice m_vkDevice;
+ VkPipelineLayout m_vkPipelineLayout;
+ VkRenderPass m_vkRenderPass;
+ VkPipeline m_graphicsVkPipeline;
+ std::vector<VkCommandBuffer> m_vkCommandBuffers;
+ std::vector<VkFramebuffer> m_renderTargetVkFrameBuffers;
+
+ VkCommandPool m_vkCommandPool;
};
#endif /* COMPOSITOR_VK_H */ \ No newline at end of file
diff --git a/stream-servers/tests/CompositorVk_unittest.cpp b/stream-servers/tests/CompositorVk_unittest.cpp
index ed05fd81..9536ae7b 100644
--- a/stream-servers/tests/CompositorVk_unittest.cpp
+++ b/stream-servers/tests/CompositorVk_unittest.cpp
@@ -2,6 +2,7 @@
#include <gtest/gtest.h>
+#include <algorithm>
#include <array>
#include <optional>
@@ -27,7 +28,10 @@ class CompositorVkTest : public ::testing::Test {
protected:
struct RenderTarget {
public:
- static constexpr uint32_t k_bpp = 4; // always R8G8B8A8
+ static constexpr VkFormat k_vkFormat = VK_FORMAT_R8G8B8A8_SRGB;
+ static constexpr uint32_t k_bpp = 4;
+ static constexpr VkImageLayout k_vkImageLayout =
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
const goldfish_vk::VulkanDispatch &m_vk;
VkDevice m_vkDevice;
@@ -38,6 +42,7 @@ class CompositorVkTest : public ::testing::Test {
VkImage m_vkImage;
VkDeviceMemory m_imageVkDeviceMemory;
+ VkImageView m_vkImageView;
VkBuffer m_vkBuffer;
VkDeviceMemory m_bufferVkDeviceMemory;
@@ -112,6 +117,9 @@ class CompositorVkTest : public ::testing::Test {
if (m_vkBuffer != VK_NULL_HANDLE) {
m_vk.vkDestroyBuffer(m_vkDevice, m_vkBuffer, nullptr);
}
+ if (m_vkImageView != VK_NULL_HANDLE) {
+ m_vk.vkDestroyImageView(m_vkDevice, m_vkImageView, nullptr);
+ }
if (m_imageVkDeviceMemory != VK_NULL_HANDLE) {
m_vk.vkFreeMemory(m_vkDevice, m_imageVkDeviceMemory, nullptr);
}
@@ -125,6 +133,7 @@ class CompositorVkTest : public ::testing::Test {
: m_vk(vk),
m_vkImage(VK_NULL_HANDLE),
m_imageVkDeviceMemory(VK_NULL_HANDLE),
+ m_vkImageView(VK_NULL_HANDLE),
m_vkBuffer(VK_NULL_HANDLE),
m_bufferVkDeviceMemory(VK_NULL_HANDLE),
m_memory(nullptr),
@@ -140,7 +149,7 @@ class CompositorVkTest : public ::testing::Test {
imageCi.extent.depth = 1;
imageCi.mipLevels = 1;
imageCi.arrayLayers = 1;
- imageCi.format = VK_FORMAT_R8G8B8A8_SRGB;
+ imageCi.format = k_vkFormat;
imageCi.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCi.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCi.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
@@ -197,15 +206,35 @@ class CompositorVkTest : public ::testing::Test {
m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo) == VK_SUCCESS;
if (res) {
recordImageLayoutTransformCommands(
- cmdBuff, VK_IMAGE_LAYOUT_UNDEFINED,
- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ cmdBuff, VK_IMAGE_LAYOUT_UNDEFINED, k_vkImageLayout);
res = m_vk.vkEndCommandBuffer(cmdBuff) == VK_SUCCESS;
}
if (res) {
res = submitCommandBufferAndWait(cmdBuff);
}
m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &cmdBuff);
- return res;
+ if (!res) {
+ return false;
+ }
+ VkImageViewCreateInfo imageViewCi = {};
+ imageViewCi.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imageViewCi.image = m_vkImage;
+ imageViewCi.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imageViewCi.format = k_vkFormat;
+ imageViewCi.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageViewCi.subresourceRange.baseMipLevel = 0;
+ imageViewCi.subresourceRange.levelCount = 1;
+ imageViewCi.subresourceRange.baseArrayLayer = 0;
+ imageViewCi.subresourceRange.layerCount = 1;
+ if (m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr,
+ &m_vkImageView) != VK_SUCCESS) {
+ return false;
+ }
+ return true;
}
void recordImageLayoutTransformCommands(VkCommandBuffer cmdBuff,
@@ -317,7 +346,7 @@ class CompositorVkTest : public ::testing::Test {
return false;
}
recordImageLayoutTransformCommands(
- m_readCommandBuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ m_readCommandBuffer, k_vkImageLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkBufferImageCopy region = {};
region.bufferOffset = 0;
@@ -334,7 +363,7 @@ class CompositorVkTest : public ::testing::Test {
m_vkBuffer, 1, &region);
recordImageLayoutTransformCommands(
m_readCommandBuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ k_vkImageLayout);
if (m_vk.vkEndCommandBuffer(m_readCommandBuffer) != VK_SUCCESS) {
return false;
}
@@ -344,14 +373,14 @@ class CompositorVkTest : public ::testing::Test {
return false;
}
recordImageLayoutTransformCommands(
- m_writeCommandBuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ m_writeCommandBuffer, k_vkImageLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
m_vk.vkCmdCopyBufferToImage(
m_writeCommandBuffer, m_vkBuffer, m_vkImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
recordImageLayoutTransformCommands(
m_writeCommandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ k_vkImageLayout);
if (m_vk.vkEndCommandBuffer(m_writeCommandBuffer) != VK_SUCCESS) {
return false;
}
@@ -359,9 +388,7 @@ class CompositorVkTest : public ::testing::Test {
}
};
- static void SetUpTestCase() {
- k_vk = emugl::vkDispatch(false);
- }
+ static void SetUpTestCase() { k_vk = emugl::vkDispatch(false); }
static constexpr uint32_t k_numOfRenderTargets = 10;
static constexpr uint32_t k_renderTargetWidth = 255;
@@ -392,6 +419,16 @@ class CompositorVkTest : public ::testing::Test {
ASSERT_NE(renderTarget, nullptr);
m_renderTargets.emplace_back(std::move(renderTarget));
}
+
+ m_renderTargetImageViews.resize(m_renderTargets.size());
+ ASSERT_EQ(
+ std::transform(
+ m_renderTargets.begin(), m_renderTargets.end(),
+ m_renderTargetImageViews.begin(),
+ [](const std::unique_ptr<const RenderTarget> &renderTarget) {
+ return renderTarget->m_vkImageView;
+ }),
+ m_renderTargetImageViews.end());
}
void TearDown() override {
@@ -403,12 +440,21 @@ class CompositorVkTest : public ::testing::Test {
m_vkInstance = VK_NULL_HANDLE;
}
+ std::unique_ptr<CompositorVk> createCompositor() {
+ return CompositorVk::create(*k_vk, m_vkDevice, RenderTarget::k_vkFormat,
+ RenderTarget::k_vkImageLayout,
+ RenderTarget::k_vkImageLayout,
+ k_renderTargetWidth, k_renderTargetHeight,
+ m_renderTargetImageViews, m_vkCommandPool);
+ }
+
static const goldfish_vk::VulkanDispatch *k_vk;
VkInstance m_vkInstance = VK_NULL_HANDLE;
VkPhysicalDevice m_vkPhysicalDevice = VK_NULL_HANDLE;
uint32_t m_compositorQueueFamilyIndex = 0;
VkDevice m_vkDevice = VK_NULL_HANDLE;
std::vector<std::unique_ptr<const RenderTarget>> m_renderTargets;
+ std::vector<VkImageView> m_renderTargetImageViews;
VkCommandPool m_vkCommandPool = VK_NULL_HANDLE;
VkQueue m_compositorVkQueue = VK_NULL_HANDLE;
@@ -517,7 +563,7 @@ class CompositorVkTest : public ::testing::Test {
const goldfish_vk::VulkanDispatch *CompositorVkTest::k_vk = nullptr;
TEST_F(CompositorVkTest, Init) {
- ASSERT_NE(CompositorVk::create(*k_vk, m_vkDevice), nullptr);
+ ASSERT_NE(createCompositor(), nullptr);
}
TEST_F(CompositorVkTest, ValidatePhysicalDeviceFeatures) {
@@ -554,20 +600,60 @@ TEST_F(CompositorVkTest, EnablePhysicalDeviceFeatures) {
VK_TRUE);
}
-TEST_F(CompositorVkTest, TestRenderTarget) {
+TEST_F(CompositorVkTest, EmptyCompositionShouldDrawABlackFrame) {
std::vector<uint32_t> pixels(k_renderTargetNumOfPixels);
for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
uint8_t v = static_cast<uint8_t>((i / 4) & 0xff);
- uint8_t *pixel = reinterpret_cast<uint8_t*>(&pixels[i]);
+ uint8_t *pixel = reinterpret_cast<uint8_t *>(&pixels[i]);
pixel[0] = v;
pixel[1] = v;
pixel[2] = v;
pixel[3] = 0xff;
}
- ASSERT_TRUE(m_renderTargets[0]->write(pixels));
- auto maybeImageBytes = m_renderTargets[0]->read();
- ASSERT_TRUE(maybeImageBytes.has_value());
- for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
- ASSERT_EQ(pixels[i], maybeImageBytes.value()[i]);
+ for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
+ ASSERT_TRUE(m_renderTargets[i]->write(pixels));
+ auto maybeImageBytes = m_renderTargets[i]->read();
+ ASSERT_TRUE(maybeImageBytes.has_value());
+ for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
+ ASSERT_EQ(pixels[i], maybeImageBytes.value()[i]);
+ }
+ }
+
+ auto compositor = createCompositor();
+ ASSERT_NE(compositor, nullptr);
+
+ // render to render targets with event index
+ std::vector<VkCommandBuffer> cmdBuffs = {};
+ for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
+ if (i % 2 == 0) {
+ cmdBuffs.emplace_back(compositor->getCommandBuffer(i));
+ }
+ }
+ VkSubmitInfo submitInfo = {};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = static_cast<uint32_t>(cmdBuffs.size());
+ submitInfo.pCommandBuffers = cmdBuffs.data();
+ ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo,
+ VK_NULL_HANDLE),
+ VK_SUCCESS);
+
+ ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
+ for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
+ auto maybeImagePixels = m_renderTargets[i]->read();
+ ASSERT_TRUE(maybeImagePixels.has_value());
+ auto imagePixels = maybeImagePixels.value();
+ for (uint32_t j = 0; j < k_renderTargetNumOfPixels; j++) {
+ const auto pixel =
+ reinterpret_cast<const uint8_t *>(&imagePixels[j]);
+ // should only render to render targets with even index
+ if (i % 2 == 0) {
+ ASSERT_EQ(pixel[0], 0);
+ ASSERT_EQ(pixel[1], 0);
+ ASSERT_EQ(pixel[2], 0);
+ ASSERT_EQ(pixel[3], 0xff);
+ } else {
+ ASSERT_EQ(pixels[j], imagePixels[j]);
+ }
+ }
}
} \ No newline at end of file