diff --git a/external/Vulkan/examples/renderheadless/renderheadless.cpp b/external/Vulkan/examples/renderheadless/renderheadless.cpp index aeaffd20df51c2dce021d643d09c028117b0a148..912d07c5d06d9c7199a5b6970bd6a0092c3076ba 100644 --- a/external/Vulkan/examples/renderheadless/renderheadless.cpp +++ b/external/Vulkan/examples/renderheadless/renderheadless.cpp @@ -6,6 +6,8 @@ * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ +#include "renderheadless.h" + #if defined(_WIN32) #pragma comment(linker, "/subsystem:console") #elif defined(VK_USE_PLATFORM_ANDROID_KHR) @@ -93,6 +95,17 @@ public: VkDebugReportCallbackEXT debugReportCallback{}; + static constexpr uint32_t kMaxTargets{3}; + struct TargetImage { + VkImage image{VK_NULL_HANDLE}; + VkDeviceMemory mem{VK_NULL_HANDLE}; + VkSemaphore renderDone{VK_NULL_HANDLE}; + VkSemaphore presentDone{VK_NULL_HANDLE}; + }; + + TargetImage targets_[kMaxTargets]; + uint32_t currentTarget = 0; + uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties deviceMemoryProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); @@ -137,11 +150,15 @@ public: /* Submit command buffer to a queue and wait for fence until queue operations have been finished */ - void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue) + void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue, VkSemaphore wait = NULL, VkSemaphore signal = NULL) { VkSubmitInfo submitInfo = vks::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; + submitInfo.waitSemaphoreCount = wait == NULL ? 0 : 1; + submitInfo.pWaitSemaphores = wait == NULL ? NULL : &wait; + submitInfo.signalSemaphoreCount = signal == NULL ? 0 : 1; + submitInfo.pSignalSemaphores = signal == NULL ? NULL : &signal; VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(); VkFence fence; VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); @@ -150,7 +167,153 @@ public: vkDestroyFence(device, fence, nullptr); } - VulkanExample() + std::tuple<VkSemaphore, VkSemaphore, VkImage> render() { + /* + Command buffer creation + */ + { + VkCommandBuffer commandBuffer; + VkCommandBufferAllocateInfo cmdBufAllocateInfo = + vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &commandBuffer)); + + VkCommandBufferBeginInfo cmdBufInfo = + vks::initializers::commandBufferBeginInfo(); + + VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo)); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 1.0f } }; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = {}; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.framebuffer = framebuffer; + + vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = {}; + viewport.height = (float)height; + viewport.width = (float)width; + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + // Update dynamic scissor state + VkRect2D scissor = {}; + scissor.extent.width = width; + scissor.extent.height = height; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + // Render scene + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + std::vector<glm::vec3> pos = { + glm::vec3(-1.5f, 0.0f, -4.0f), + glm::vec3( 0.0f, 0.0f, -2.5f), + glm::vec3( 1.5f, 0.0f, -4.0f), + }; + + for (auto v : pos) { + glm::mat4 mvpMatrix = glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.1f, 256.0f) * glm::translate(glm::mat4(1.0f), v); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(mvpMatrix), &mvpMatrix); + vkCmdDrawIndexed(commandBuffer, 3, 1, 0, 0, 0); + } + + vkCmdEndRenderPass(commandBuffer); + + VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + submitWork(commandBuffer, queue); + vkDeviceWaitIdle(device); + } + + VkImage dstImage = targets_[currentTarget].image; + VkSemaphore presentDone = targets_[currentTarget].presentDone; + VkSemaphore renderDone = targets_[currentTarget].renderDone; + currentTarget = (currentTarget + 1) % kMaxTargets; + + { + // Do the actual blit from the offscreen image to our host visible destination image + VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); + VkCommandBuffer copyCmd; + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, ©Cmd)); + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo)); + + // Transition destination image to transfer destination layout + vks::tools::insertImageMemoryBarrier( + copyCmd, + dstImage, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + + // colorAttachment.image is already in VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, and does not need to be transitioned + + VkImageCopy imageCopyRegion{}; + imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.srcSubresource.layerCount = 1; + imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.dstSubresource.layerCount = 1; + imageCopyRegion.extent.width = width; + imageCopyRegion.extent.height = height; + imageCopyRegion.extent.depth = 1; + + vkCmdCopyImage( + copyCmd, + colorAttachment.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopyRegion); + + // Transition destination image to general layout, which is the required layout for mapping the image memory later on + vks::tools::insertImageMemoryBarrier( + copyCmd, + dstImage, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + + VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd)); + + // Wait on presentDone and signal renderDone when finished + submitWork(copyCmd, queue, presentDone, renderDone); + + vkFreeCommandBuffers(device, commandPool, 1, ©Cmd); + + // Get layout of the image (including row pitch) + //VkImageSubresource subResource{}; + //subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + //VkSubresourceLayout subResourceLayout; + + //vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); + //vkDestroyImage(device, dstImage, nullptr); + } + + return std::make_tuple(renderDone, presentDone, dstImage); + } + + VulkanExample(int32_t width, int32_t height) + : width(width) + , height(height) { LOG("Running headless rendering example\n"); @@ -358,13 +521,13 @@ public: vkDestroyBuffer(device, stagingBuffer, nullptr); vkFreeMemory(device, stagingMemory, nullptr); } + + vkFreeCommandBuffers(device, commandPool, 1, ©Cmd); } /* Create framebuffer attachments */ - width = 1024; - height = 1024; VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; VkFormat depthFormat; vks::tools::getSupportedDepthFormat(physicalDevice, &depthFormat); @@ -605,81 +768,34 @@ public: VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); } - /* - Command buffer creation - */ - { - VkCommandBuffer commandBuffer; - VkCommandBufferAllocateInfo cmdBufAllocateInfo = - vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); - VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &commandBuffer)); - - VkCommandBufferBeginInfo cmdBufInfo = - vks::initializers::commandBufferBeginInfo(); - - VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo)); - - VkClearValue clearValues[2]; - clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 1.0f } }; - clearValues[1].depthStencil = { 1.0f, 0 }; - - VkRenderPassBeginInfo renderPassBeginInfo = {}; - renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBeginInfo.renderArea.extent.width = width; - renderPassBeginInfo.renderArea.extent.height = height; - renderPassBeginInfo.clearValueCount = 2; - renderPassBeginInfo.pClearValues = clearValues; - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.framebuffer = framebuffer; - - vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport viewport = {}; - viewport.height = (float)height; - viewport.width = (float)width; - viewport.minDepth = (float)0.0f; - viewport.maxDepth = (float)1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - - // Update dynamic scissor state - VkRect2D scissor = {}; - scissor.extent.width = width; - scissor.extent.height = height; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + vkQueueWaitIdle(queue); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + // Initialize external targets + for (uint32_t i = 0; i < kMaxTargets; i++) { + VkSemaphoreTypeCreateInfo binaryCreateInfo; + binaryCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + binaryCreateInfo.pNext = NULL; + binaryCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_BINARY; + binaryCreateInfo.initialValue = 0; - // Render scene - VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets); - vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32); + VkSemaphoreCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + createInfo.pNext = &binaryCreateInfo; + createInfo.flags = 0; - std::vector<glm::vec3> pos = { - glm::vec3(-1.5f, 0.0f, -4.0f), - glm::vec3( 0.0f, 0.0f, -2.5f), - glm::vec3( 1.5f, 0.0f, -4.0f), - }; + VkSemaphore renderDone; + vkCreateSemaphore(device, &createInfo, NULL, &renderDone); - for (auto v : pos) { - glm::mat4 mvpMatrix = glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.1f, 256.0f) * glm::translate(glm::mat4(1.0f), v); - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(mvpMatrix), &mvpMatrix); - vkCmdDrawIndexed(commandBuffer, 3, 1, 0, 0, 0); - } + VkSemaphore presentDone; + vkCreateSemaphore(device, &createInfo, NULL, &presentDone); - vkCmdEndRenderPass(commandBuffer); + // Signal Semaphore by default to avoid being stuck + VkSubmitInfo submitInfo; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &presentDone; + vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); - VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); - - submitWork(commandBuffer, queue); - - vkDeviceWaitIdle(device); - } - - /* - Copy framebuffer image to host visible image - */ - const char* imagedata; - { // Create the linear tiled destination image to copy to and to read the memory from VkImageCreateInfo imgCreateInfo(vks::initializers::imageCreateInfo()); imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; @@ -692,135 +808,48 @@ public: imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; - imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imgCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + // Create the image VkImage dstImage; VK_CHECK_RESULT(vkCreateImage(device, &imgCreateInfo, nullptr, &dstImage)); + // Create memory to back up the image VkMemoryRequirements memRequirements; VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); VkDeviceMemory dstImageMemory; vkGetImageMemoryRequirements(device, dstImage, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; - // Memory must be host visible to copy from - memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); - // Do the actual blit from the offscreen image to our host visible destination image - VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); - VkCommandBuffer copyCmd; - VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, ©Cmd)); - VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); - VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo)); - - // Transition destination image to transfer destination layout - vks::tools::insertImageMemoryBarrier( - copyCmd, - dstImage, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - - // colorAttachment.image is already in VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, and does not need to be transitioned - - VkImageCopy imageCopyRegion{}; - imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopyRegion.srcSubresource.layerCount = 1; - imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopyRegion.dstSubresource.layerCount = 1; - imageCopyRegion.extent.width = width; - imageCopyRegion.extent.height = height; - imageCopyRegion.extent.depth = 1; - - vkCmdCopyImage( - copyCmd, - colorAttachment.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &imageCopyRegion); - - // Transition destination image to general layout, which is the required layout for mapping the image memory later on - vks::tools::insertImageMemoryBarrier( - copyCmd, - dstImage, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_GENERAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - - VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd)); - - submitWork(copyCmd, queue); - - // Get layout of the image (including row pitch) - VkImageSubresource subResource{}; - subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VkSubresourceLayout subResourceLayout; - - vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); - - // Map image memory so we can start copying from it - vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&imagedata); - imagedata += subResourceLayout.offset; - - /* - Save host visible framebuffer image to disk (ppm format) - */ - -#if defined (VK_USE_PLATFORM_ANDROID_KHR) - const char* filename = strcat(getenv("EXTERNAL_STORAGE"), "/headless.ppm"); -#else - const char* filename = "headless.ppm"; -#endif - std::ofstream file(filename, std::ios::out | std::ios::binary); - - // ppm header - file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; - - // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components - // Check if source is BGR and needs swizzle - std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; - const bool colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), VK_FORMAT_R8G8B8A8_UNORM) != formatsBGR.end()); - - // ppm binary pixel data - for (int32_t y = 0; y < height; y++) { - unsigned int *row = (unsigned int*)imagedata; - for (int32_t x = 0; x < width; x++) { - if (colorSwizzle) { - file.write((char*)row + 2, 1); - file.write((char*)row + 1, 1); - file.write((char*)row, 1); - } - else { - file.write((char*)row, 3); - } - row++; - } - imagedata += subResourceLayout.rowPitch; - } - file.close(); - - LOG("Framebuffer image saved to %s\n", filename); - - // Clean up resources - vkUnmapMemory(device, dstImageMemory); - vkFreeMemory(device, dstImageMemory, nullptr); - vkDestroyImage(device, dstImage, nullptr); + targets_[i].image = dstImage; + targets_[i].mem = dstImageMemory; + targets_[i].presentDone = presentDone; + targets_[i].renderDone = renderDone; } - - vkQueueWaitIdle(queue); } ~VulkanExample() { + for (uint32_t i = 0; i < kMaxTargets; i++) { + const uint64_t waitValue = 1; + VkSemaphoreWaitInfo waitInfo; + waitInfo.pNext = NULL; + waitInfo.flags = 0; + waitInfo.semaphoreCount = 1; + waitInfo.pSemaphores = &targets_[i].presentDone; + waitInfo.pValues = &waitValue; + vkWaitSemaphores(device, &waitInfo, UINT64_MAX); + + vkDestroyImage(device, targets_[i].image, nullptr); + vkFreeMemory(device, targets_[i].mem, nullptr); + vkDestroySemaphore(device, targets_[i].renderDone, nullptr); + vkDestroySemaphore(device, targets_[i].presentDone, nullptr); + } + vkDestroyBuffer(device, vertexBuffer, nullptr); vkFreeMemory(device, vertexMemory, nullptr); vkDestroyBuffer(device, indexBuffer, nullptr); @@ -879,11 +908,19 @@ void android_main(android_app* state) { } } #else -int main() { - VulkanExample *vulkanExample = new VulkanExample(); - std::cout << "Finished. Press enter to terminate..."; - getchar(); - delete(vulkanExample); - return 0; +VulkanExample* vulkan_example_init(int32_t width, int32_t height) { + return new VulkanExample(width, height); +} + +std::tuple<VkSemaphore, VkSemaphore, VkImage> vulkan_example_render(VulkanExample* vkExInstace) { + return vkExInstace->render(); +} + +VkDevice vulkan_example_get_device(VulkanExample* vkExInstance) { + return vkExInstance->device; +} + +void vulkan_example_deinit(VulkanExample* vkExInstance) { + delete(vkExInstance); } -#endif \ No newline at end of file +#endif diff --git a/external/Vulkan/examples/renderheadless/renderheadless.h b/external/Vulkan/examples/renderheadless/renderheadless.h new file mode 100644 index 0000000000000000000000000000000000000000..47202678e8ef230a42342bc4a36bbc1b5a73cddf --- /dev/null +++ b/external/Vulkan/examples/renderheadless/renderheadless.h @@ -0,0 +1,16 @@ +#pragma once + +#include <vulkan/vulkan.h> + +#include <tuple> + +class VulkanExample; + +VulkanExample* vulkan_example_init(int32_t width, int32_t height); + +/* the first semaphore is renderDone, the second semaphore is presentDone */ +std::tuple<VkSemaphore, VkSemaphore, VkImage> vulkan_example_render(VulkanExample* vkExInstance); + +VkDevice vulkan_example_get_device(VulkanExample* vkExInstance); + +void vulkan_example_deinit(VulkanExample* vkExInstance);