|
|
|
@@ -7,8 +7,8 @@ |
|
|
|
#include <string> |
|
|
|
#include <vector> |
|
|
|
|
|
|
|
const uint32_t kWidth = 16; |
|
|
|
const uint32_t kHeight = 16; |
|
|
|
const uint32_t kWidth = 128; |
|
|
|
const uint32_t kHeight = 128; |
|
|
|
const VkFormat kVulkanFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
|
|
|
const std::string kVertexShaderPath = "triangle.vert.spv"; |
|
|
|
const std::string kFragmentShaderPath = "triangle.frag.spv"; |
|
|
|
@@ -319,40 +319,232 @@ VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) { |
|
|
|
return pipeline; |
|
|
|
} |
|
|
|
|
|
|
|
int main() { |
|
|
|
VkInstance instance = CreateVkInstance(); |
|
|
|
VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance); |
|
|
|
uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device); |
|
|
|
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); |
|
|
|
VkRenderPass render_pass = CreateVkRenderPass(device); |
|
|
|
VkPipeline pipeline = CreateVkPipeline(device, render_pass); |
|
|
|
|
|
|
|
// Example code for exporting a PNG. |
|
|
|
FILE *f = fopen("triangle.png", "wb"); |
|
|
|
VkImage CreateVkImage(VkDevice device) { |
|
|
|
VkImageCreateInfo create_info = {}; |
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; |
|
|
|
create_info.pNext = nullptr; |
|
|
|
create_info.imageType = VK_IMAGE_TYPE_2D; |
|
|
|
create_info.format = kVulkanFormat; |
|
|
|
VkExtent3D extent = {}; |
|
|
|
extent.width = kWidth; |
|
|
|
extent.height = kHeight; |
|
|
|
extent.depth = 1; |
|
|
|
create_info.extent = extent; |
|
|
|
create_info.mipLevels = 1; |
|
|
|
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.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
|
|
|
VkImage image; |
|
|
|
VkResult result = vkCreateImage(device, &create_info, nullptr, &image); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create VkImage: " << result); |
|
|
|
} |
|
|
|
return image; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<uint8_t> pixels(kWidth * kHeight * 4); |
|
|
|
for (size_t i = 0; i < kHeight; i++) { |
|
|
|
for (size_t j = 0; j < kWidth; j++) { |
|
|
|
uint32_t base_index = (i * kWidth * 4) + (j * 4); |
|
|
|
pixels[base_index] = 255; |
|
|
|
pixels[base_index + 1] = 255; |
|
|
|
pixels[base_index + 2] = 0; |
|
|
|
pixels[base_index + 3] = 255; |
|
|
|
uint32_t FindMemoryType(uint32_t valid_image_memory_types, |
|
|
|
VkPhysicalDeviceMemoryProperties device_memory_properties) { |
|
|
|
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)) { |
|
|
|
return i; |
|
|
|
} |
|
|
|
} |
|
|
|
ERROR("Unable to find suitable memory type index"); |
|
|
|
} |
|
|
|
|
|
|
|
VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice device, |
|
|
|
VkImage image) { |
|
|
|
VkMemoryRequirements image_memory_requirements; |
|
|
|
vkGetImageMemoryRequirements(device, image, &image_memory_requirements); |
|
|
|
|
|
|
|
VkPhysicalDeviceMemoryProperties device_memory_properties; |
|
|
|
vkGetPhysicalDeviceMemoryProperties(physical_device, &device_memory_properties); |
|
|
|
|
|
|
|
VkMemoryAllocateInfo memory_allocate_info = {}; |
|
|
|
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); |
|
|
|
|
|
|
|
VkDeviceMemory image_memory; |
|
|
|
VkResult result = vkAllocateMemory(device, &memory_allocate_info, nullptr, &image_memory); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to allocate image memory: " << result); |
|
|
|
} |
|
|
|
|
|
|
|
result = vkBindImageMemory(device, image, image_memory, 0); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to bind image memory: " << result); |
|
|
|
} |
|
|
|
return image_memory; |
|
|
|
} |
|
|
|
|
|
|
|
VkImageView CreateVkImageView(VkDevice device, VkImage image) { |
|
|
|
VkImageViewCreateInfo create_info = {}; |
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
|
|
create_info.image = image; |
|
|
|
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
|
|
create_info.format = kVulkanFormat; |
|
|
|
|
|
|
|
VkComponentMapping component_mapping = {}; |
|
|
|
component_mapping.r = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
component_mapping.b = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
component_mapping.g = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
component_mapping.a = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
create_info.components = component_mapping; |
|
|
|
|
|
|
|
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; |
|
|
|
create_info.subresourceRange = subresource_range; |
|
|
|
|
|
|
|
VkImageView image_view; |
|
|
|
VkResult result = vkCreateImageView(device, &create_info, nullptr, &image_view); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create VkImageView: " << result); |
|
|
|
} |
|
|
|
return image_view; |
|
|
|
} |
|
|
|
|
|
|
|
VkFramebuffer CreateVkFramebuffer(VkDevice device, VkRenderPass render_pass, |
|
|
|
VkImageView image_view) { |
|
|
|
VkFramebufferCreateInfo create_info = {}; |
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; |
|
|
|
create_info.renderPass = render_pass; |
|
|
|
create_info.attachmentCount = 1; |
|
|
|
create_info.pAttachments = &image_view; |
|
|
|
create_info.width = kWidth; |
|
|
|
create_info.height = kHeight; |
|
|
|
create_info.layers = 1; |
|
|
|
|
|
|
|
VkFramebuffer framebuffer; |
|
|
|
VkResult result = vkCreateFramebuffer(device, &create_info, nullptr, &framebuffer); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create VkFramebuffer: " << result); |
|
|
|
} |
|
|
|
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) { |
|
|
|
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; |
|
|
|
VkResult result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to begin command buffer recording: " << result); |
|
|
|
} |
|
|
|
|
|
|
|
VkRenderPassBeginInfo render_pass_begin_info = {}; |
|
|
|
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
|
|
|
render_pass_begin_info.renderPass = render_pass; |
|
|
|
render_pass_begin_info.framebuffer = framebuffer; |
|
|
|
VkRect2D render_area = {}; |
|
|
|
render_area.offset = { 0, 0 }; |
|
|
|
render_area.extent = { kWidth, kHeight }; |
|
|
|
render_pass_begin_info.renderArea = render_area; |
|
|
|
render_pass_begin_info.clearValueCount = 1; |
|
|
|
VkClearValue clear_value = {}; |
|
|
|
clear_value.color.float32[0] = 1.0f; |
|
|
|
clear_value.color.float32[1] = 1.0f; |
|
|
|
clear_value.color.float32[2] = 0.0f; |
|
|
|
clear_value.color.float32[3] = 1.0f; |
|
|
|
render_pass_begin_info.pClearValues = &clear_value; |
|
|
|
vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t* MapVkDeviceMemory(VkDevice device, VkDeviceMemory image_memory) { |
|
|
|
void* mapped_memory = nullptr; |
|
|
|
VkResult result = vkMapMemory(device, image_memory, 0, kWidth * kHeight * 4, 0, &mapped_memory); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to map device memory: " << result); |
|
|
|
} |
|
|
|
return static_cast<uint8_t*>(mapped_memory); |
|
|
|
} |
|
|
|
|
|
|
|
void WriteImage(uint8_t* pixels) { |
|
|
|
FILE *f = fopen("triangle.png", "wb"); |
|
|
|
|
|
|
|
png_image image_info = {}; |
|
|
|
image_info.version = PNG_IMAGE_VERSION; |
|
|
|
image_info.width = kWidth; |
|
|
|
image_info.height = kHeight; |
|
|
|
image_info.format = PNG_FORMAT_RGBA; |
|
|
|
if (png_image_write_to_stdio(&image_info, f, 0, pixels.data(), kWidth * 4, nullptr) == 0) { |
|
|
|
if (png_image_write_to_stdio(&image_info, f, 0, pixels, kWidth * 4, nullptr) == 0) { |
|
|
|
ERROR("Error writing PNG: " << image_info.message); |
|
|
|
} |
|
|
|
|
|
|
|
fclose(f); |
|
|
|
} |
|
|
|
|
|
|
|
int main() { |
|
|
|
VkInstance instance = CreateVkInstance(); |
|
|
|
VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance); |
|
|
|
uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device); |
|
|
|
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); |
|
|
|
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); |
|
|
|
|
|
|
|
Draw(device, queue, render_pass, pipeline, framebuffer, command_buffer); |
|
|
|
|
|
|
|
uint8_t* pixels = MapVkDeviceMemory(device, image_memory); |
|
|
|
WriteImage(pixels); |
|
|
|
|
|
|
|
return EXIT_SUCCESS; |
|
|
|
} |