diff options
-rw-r--r-- | stream-servers/CompositorVk.cpp | 306 | ||||
-rw-r--r-- | stream-servers/CompositorVk.h | 26 | ||||
-rw-r--r-- | stream-servers/tests/CompositorVk_unittest.cpp | 126 |
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, ®ion); 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, ®ion); 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 |