Browse Source

Render to optimally tiled images and blit to linear before scanout

master
Brian Ho 6 years ago
parent
commit
793c8bbb58
1 changed files with 146 additions and 53 deletions
  1. 146
    53
      triangle.cc

+ 146
- 53
triangle.cc View File

@@ -7,8 +7,8 @@
#include <string>
#include <vector>

const uint32_t kWidth = 128;
const uint32_t kHeight = 128;
const uint32_t kWidth = 256;
const uint32_t kHeight = 256;
const VkFormat kVulkanFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
const std::string kVertexShaderPath = "triangle.vert.spv";
const std::string kFragmentShaderPath = "triangle.frag.spv";
@@ -135,6 +135,21 @@ VkCommandPool CreateVkCommandPool(VkDevice device, uint32_t device_queue_family_
return command_pool;
}

VkCommandBuffer CreateVkCommandBuffer(VkDevice device, VkCommandPool command_pool) {
VkCommandBufferAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;

VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers( device, &allocate_info, &command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkCommandBuffer: " << result);
}
return command_buffer;
}

VkRenderPass CreateVkRenderPass(VkDevice device) {
VkAttachmentDescription attachment_description = {};
attachment_description.format = kVulkanFormat;
@@ -144,7 +159,7 @@ VkRenderPass CreateVkRenderPass(VkDevice device) {
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
attachment_description.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;

VkAttachmentReference attachment_reference = {};
attachment_reference.attachment = 0;
@@ -319,7 +334,7 @@ VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) {
return pipeline;
}

VkImage CreateVkImage(VkDevice device) {
VkImage CreateVkImage(VkDevice device, VkImageTiling image_tiling, VkImageUsageFlags usage) {
VkImageCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
create_info.pNext = nullptr;
@@ -334,9 +349,8 @@ VkImage CreateVkImage(VkDevice device) {
create_info.arrayLayers = 1;
create_info.samples = VK_SAMPLE_COUNT_1_BIT;
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
// create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
create_info.tiling = VK_IMAGE_TILING_LINEAR;
create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
create_info.tiling = image_tiling;
create_info.usage = usage;
create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;

VkImage image;
@@ -347,13 +361,24 @@ VkImage CreateVkImage(VkDevice device) {
return image;
}

inline VkImage CreateRenderVkImage(VkDevice device) {
return CreateVkImage(device, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
}

inline VkImage CreateScanoutVkImage(VkDevice device) {
return CreateVkImage(device, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
}

uint32_t FindMemoryType(uint32_t valid_image_memory_types,
VkPhysicalDeviceMemoryProperties device_memory_properties) {
VkPhysicalDeviceMemoryProperties device_memory_properties,
VkMemoryPropertyFlags memory_property_flags) {
for (uint32_t i = 0; i < device_memory_properties.memoryTypeCount; i++) {
// We don't care about performance, so just choose the first mappable memory type.
if ((valid_image_memory_types % (1 << i)) &&
(device_memory_properties.memoryTypes[i].propertyFlags &
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
((device_memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) ==
memory_property_flags)) {
return i;
}
}
@@ -361,7 +386,7 @@ uint32_t FindMemoryType(uint32_t valid_image_memory_types,
}

VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice device,
VkImage image) {
VkImage image, VkMemoryPropertyFlags memory_property_flags) {
VkMemoryRequirements image_memory_requirements;
vkGetImageMemoryRequirements(device, image, &image_memory_requirements);

@@ -372,7 +397,8 @@ VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_allocate_info.allocationSize = image_memory_requirements.size;
memory_allocate_info.memoryTypeIndex = FindMemoryType(
image_memory_requirements.memoryTypeBits, device_memory_properties);
image_memory_requirements.memoryTypeBits, device_memory_properties,
memory_property_flags);

VkDeviceMemory image_memory;
VkResult result = vkAllocateMemory(device, &memory_allocate_info, nullptr, &image_memory);
@@ -387,6 +413,16 @@ VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice
return image_memory;
}

inline VkDeviceMemory AllocateAndBindRenderMemory(VkPhysicalDevice physical_device,
VkDevice device, VkImage image) {
return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
}

inline VkDeviceMemory AllocateAndBindScanoutMemory(VkPhysicalDevice physical_device,
VkDevice device, VkImage image) {
return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
}

VkImageView CreateVkImageView(VkDevice device, VkImage image) {
VkImageViewCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -436,23 +472,7 @@ VkFramebuffer CreateVkFramebuffer(VkDevice device, VkRenderPass render_pass,
return framebuffer;
}

VkCommandBuffer CreateVkCommandBuffer(VkDevice device, VkCommandPool command_pool) {
VkCommandBufferAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;

VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers( device, &allocate_info, &command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkCommandBuffer: " << result);
}
return command_buffer;
}

void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline pipeline,
VkFramebuffer framebuffer, VkCommandBuffer command_buffer) {
void BeginCommandBuffer(VkCommandBuffer command_buffer) {
VkCommandBufferBeginInfo command_buffer_begin_info = {};
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
@@ -460,7 +480,37 @@ void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline p
if (result != VK_SUCCESS) {
ERROR("Unable to begin command buffer recording: " << result);
}
}

void EndCommandBufferAndSubmit(VkCommandBuffer command_buffer, VkQueue queue) {
VkResult result = vkEndCommandBuffer(command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to end command buffer recording: " << result);
}

VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
ERROR("Error in submitting command buffer to queue: " << result);
}
}

void WaitForIdle(VkQueue queue) {
// In a real application, we should use real synchronization primitives to figure out when the
// command buffers have been executed. Waiting on an idle queue is simple, but it can cause
// deadlock if we continue to submit to the queue while we wait for idle.
VkResult result = vkQueueWaitIdle(queue);
if (result != VK_SUCCESS) {
ERROR("Error in waiting for graphics queue to reach idle state: " << result);
}
}

void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline pipeline,
VkFramebuffer framebuffer, VkCommandBuffer command_buffer) {
BeginCommandBuffer(command_buffer);
VkRenderPassBeginInfo render_pass_begin_info = {};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = render_pass;
@@ -481,25 +531,58 @@ void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline p
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdDraw(command_buffer, 3, 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
}

result = vkEndCommandBuffer(command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to end command buffer recording: " << result);
}

VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
ERROR("Error in submitting command buffer to queue: " << result);
}

result = vkQueueWaitIdle(queue);
if (result != VK_SUCCESS) {
ERROR("Error in waiting for graphics queue to reach idle state: " << result);
}
void BlitToScanoutImage(VkQueue queue, VkCommandBuffer command_buffer, VkImage source_image,
VkImage dest_image) {
// We need to make sure the writes of the render pass have executed before actually scanning out
// the rendered image. Also, we need to transition the layout of the scanout image to
// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
VkImageMemoryBarrier image_memory_barrier = {};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
image_memory_barrier.pNext = nullptr;
image_memory_barrier.srcAccessMask = 0;
image_memory_barrier.dstAccessMask = 0;
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = dest_image;
VkImageSubresourceRange subresource_range = {};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
subresource_range.layerCount = 1;
image_memory_barrier.subresourceRange = subresource_range;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

VkOffset3D blit_start = {};
blit_start.x = 0;
blit_start.y = 0;
blit_start.z = 0;
VkOffset3D blit_end = {};
blit_end.x = kWidth;
blit_end.y = kHeight;
blit_end.z = 1;
VkImageSubresourceLayers subresource_layers = {};
subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_layers.mipLevel = 0;
subresource_layers.baseArrayLayer = 0;
subresource_layers.layerCount = 1;
VkImageBlit image_blit = {};
image_blit.srcSubresource = subresource_layers;
image_blit.srcOffsets[0] = blit_start;
image_blit.srcOffsets[1] = blit_end;
image_blit.dstSubresource = subresource_layers;
image_blit.dstOffsets[0] = blit_start;
image_blit.dstOffsets[1] = blit_end;
// TODO(brkho): Support multi-planar formats.
vkCmdBlitImage(command_buffer, source_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dest_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_blit, VK_FILTER_NEAREST);

EndCommandBufferAndSubmit(command_buffer, queue);
}

uint8_t* MapVkDeviceMemory(VkDevice device, VkDeviceMemory image_memory) {
@@ -533,17 +616,27 @@ int main() {
VkDevice device = CreateVkDevice(physical_device, device_queue_family_index);
VkQueue queue = GetVkQueue(device, device_queue_family_index);
VkCommandPool command_pool = CreateVkCommandPool(device, device_queue_family_index);
VkCommandBuffer command_buffer = CreateVkCommandBuffer(device, command_pool);

VkRenderPass render_pass = CreateVkRenderPass(device);
VkPipeline pipeline = CreateVkPipeline(device, render_pass);
VkImage image = CreateVkImage(device);
VkDeviceMemory image_memory = AllocateAndBindMemory(physical_device, device, image);
VkImageView image_view = CreateVkImageView(device, image);
VkFramebuffer framebuffer = CreateVkFramebuffer(device, render_pass, image_view);
VkCommandBuffer command_buffer = CreateVkCommandBuffer(device, command_pool);

VkImage render_image = CreateRenderVkImage(device);
AllocateAndBindRenderMemory(physical_device, device, render_image);
VkImageView render_image_view = CreateVkImageView(device, render_image);
VkFramebuffer framebuffer = CreateVkFramebuffer(device, render_pass, render_image_view);

VkImage scanout_image = CreateScanoutVkImage(device);
VkDeviceMemory scanout_image_memory =
AllocateAndBindScanoutMemory(physical_device, device, scanout_image);

Draw(device, queue, render_pass, pipeline, framebuffer, command_buffer);
// Since the render target is created with VK_IMAGE_TILING_OPTIMAL, we need to copy it to a linear
// format before scanning out.
BlitToScanoutImage(queue, command_buffer, render_image, scanout_image);
WaitForIdle(queue);

uint8_t* pixels = MapVkDeviceMemory(device, image_memory);
uint8_t* pixels = MapVkDeviceMemory(device, scanout_image_memory);
WriteImage(pixels);

return EXIT_SUCCESS;

Loading…
Cancel
Save