Skip to content
Snippets Groups Projects
Commit a6cbaf15 authored by Antonio Ospite's avatar Antonio Ospite
Browse files

Make renderheadless like a library that returns rendered images

parent c90c7c45
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
#include "renderheadless.h"
#if defined(_WIN32) #if defined(_WIN32)
#pragma comment(linker, "/subsystem:console") #pragma comment(linker, "/subsystem:console")
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
...@@ -93,6 +95,17 @@ public: ...@@ -93,6 +95,17 @@ public:
VkDebugReportCallbackEXT debugReportCallback{}; 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) { uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties deviceMemoryProperties; VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
...@@ -137,11 +150,15 @@ public: ...@@ -137,11 +150,15 @@ public:
/* /*
Submit command buffer to a queue and wait for fence until queue operations have been finished 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(); VkSubmitInfo submitInfo = vks::initializers::submitInfo();
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuffer; 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(); VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo();
VkFence fence; VkFence fence;
VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence));
...@@ -150,7 +167,153 @@ public: ...@@ -150,7 +167,153 @@ public:
vkDestroyFence(device, fence, nullptr); 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, &copyCmd));
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, &copyCmd);
// 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"); LOG("Running headless rendering example\n");
...@@ -358,13 +521,13 @@ public: ...@@ -358,13 +521,13 @@ public:
vkDestroyBuffer(device, stagingBuffer, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr);
vkFreeMemory(device, stagingMemory, nullptr); vkFreeMemory(device, stagingMemory, nullptr);
} }
vkFreeCommandBuffers(device, commandPool, 1, &copyCmd);
} }
/* /*
Create framebuffer attachments Create framebuffer attachments
*/ */
width = 1024;
height = 1024;
VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkFormat depthFormat; VkFormat depthFormat;
vks::tools::getSupportedDepthFormat(physicalDevice, &depthFormat); vks::tools::getSupportedDepthFormat(physicalDevice, &depthFormat);
...@@ -605,81 +768,34 @@ public: ...@@ -605,81 +768,34 @@ public:
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
} }
/* vkQueueWaitIdle(queue);
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) { // Initialize external targets
glm::mat4 mvpMatrix = glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.1f, 256.0f) * glm::translate(glm::mat4(1.0f), v); for (uint32_t i = 0; i < kMaxTargets; i++) {
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(mvpMatrix), &mvpMatrix); VkSemaphoreTypeCreateInfo binaryCreateInfo;
vkCmdDrawIndexed(commandBuffer, 3, 1, 0, 0, 0); binaryCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
} binaryCreateInfo.pNext = NULL;
binaryCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_BINARY;
binaryCreateInfo.initialValue = 0;
vkCmdEndRenderPass(commandBuffer); VkSemaphoreCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
createInfo.pNext = &binaryCreateInfo;
createInfo.flags = 0;
VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); VkSemaphore renderDone;
vkCreateSemaphore(device, &createInfo, NULL, &renderDone);
submitWork(commandBuffer, queue); VkSemaphore presentDone;
vkCreateSemaphore(device, &createInfo, NULL, &presentDone);
vkDeviceWaitIdle(device); // 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);
/*
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 // Create the linear tiled destination image to copy to and to read the memory from
VkImageCreateInfo imgCreateInfo(vks::initializers::imageCreateInfo()); VkImageCreateInfo imgCreateInfo(vks::initializers::imageCreateInfo());
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
...@@ -692,135 +808,48 @@ public: ...@@ -692,135 +808,48 @@ public:
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; 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 // Create the image
VkImage dstImage; VkImage dstImage;
VK_CHECK_RESULT(vkCreateImage(device, &imgCreateInfo, nullptr, &dstImage)); VK_CHECK_RESULT(vkCreateImage(device, &imgCreateInfo, nullptr, &dstImage));
// Create memory to back up the image // Create memory to back up the image
VkMemoryRequirements memRequirements; VkMemoryRequirements memRequirements;
VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo());
VkDeviceMemory dstImageMemory; VkDeviceMemory dstImageMemory;
vkGetImageMemoryRequirements(device, dstImage, &memRequirements); vkGetImageMemoryRequirements(device, dstImage, &memRequirements);
memAllocInfo.allocationSize = memRequirements.size; memAllocInfo.allocationSize = memRequirements.size;
// Memory must be host visible to copy from memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory));
VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0));
// Do the actual blit from the offscreen image to our host visible destination image targets_[i].image = dstImage;
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); targets_[i].mem = dstImageMemory;
VkCommandBuffer copyCmd; targets_[i].presentDone = presentDone;
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &copyCmd)); targets_[i].renderDone = renderDone;
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);
}
vkQueueWaitIdle(queue);
} }
~VulkanExample() ~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); vkDestroyBuffer(device, vertexBuffer, nullptr);
vkFreeMemory(device, vertexMemory, nullptr); vkFreeMemory(device, vertexMemory, nullptr);
vkDestroyBuffer(device, indexBuffer, nullptr); vkDestroyBuffer(device, indexBuffer, nullptr);
...@@ -879,11 +908,19 @@ void android_main(android_app* state) { ...@@ -879,11 +908,19 @@ void android_main(android_app* state) {
} }
} }
#else #else
int main() { VulkanExample* vulkan_example_init(int32_t width, int32_t height) {
VulkanExample *vulkanExample = new VulkanExample(); return new VulkanExample(width, height);
std::cout << "Finished. Press enter to terminate..."; }
getchar();
delete(vulkanExample); std::tuple<VkSemaphore, VkSemaphore, VkImage> vulkan_example_render(VulkanExample* vkExInstace) {
return 0; return vkExInstace->render();
}
VkDevice vulkan_example_get_device(VulkanExample* vkExInstance) {
return vkExInstance->device;
}
void vulkan_example_deinit(VulkanExample* vkExInstance) {
delete(vkExInstance);
} }
#endif #endif
#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);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment