|
|
|
@ -11,6 +11,8 @@
@@ -11,6 +11,8 @@
|
|
|
|
|
#include <cglm/cam.h> |
|
|
|
|
#include <time.h> |
|
|
|
|
#include <cglm/util.h> |
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION |
|
|
|
|
#include "stb_image.h" |
|
|
|
|
|
|
|
|
|
#define WIDTH 2560 |
|
|
|
|
#define HEIGHT 1440 |
|
|
|
@ -88,6 +90,9 @@ typedef struct ApplicationState {
@@ -88,6 +90,9 @@ typedef struct ApplicationState {
|
|
|
|
|
|
|
|
|
|
VkDescriptorPool descriptor_pool; |
|
|
|
|
VkDescriptorSet descriptor_sets[2]; |
|
|
|
|
|
|
|
|
|
VkImage texture_image; |
|
|
|
|
VkDeviceMemory texture_image_memory; |
|
|
|
|
} ApplicationState; |
|
|
|
|
|
|
|
|
|
typedef struct QueueFamilyIndices { |
|
|
|
@ -348,7 +353,7 @@ int cmp(const void* a, const void* b) {
@@ -348,7 +353,7 @@ int cmp(const void* a, const void* b) {
|
|
|
|
|
return (*(int*)a - *(int*)b); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void create_logical_device(ApplicationState* state) { |
|
|
|
|
void create_logical_device(ApplicationState* state) {; |
|
|
|
|
//we already ensured that all of the required queues exist
|
|
|
|
|
QueueFamilyIndices indices = find_queue_families(state, state->physical_device); |
|
|
|
|
|
|
|
|
@ -985,7 +990,7 @@ void create_buffer(ApplicationState* state, VkDeviceSize size, VkBufferUsageFlag
@@ -985,7 +990,7 @@ void create_buffer(ApplicationState* state, VkDeviceSize size, VkBufferUsageFlag
|
|
|
|
|
vkBindBufferMemory(state->device, *buffer, *memory, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSize size) { |
|
|
|
|
VkCommandBuffer begin_single_time_commands(ApplicationState* state) { |
|
|
|
|
VkCommandBufferAllocateInfo command_buf_allocate_info = {0}; |
|
|
|
|
command_buf_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
|
command_buf_allocate_info.commandPool = state->commandPool; |
|
|
|
@ -1005,10 +1010,10 @@ void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSi
@@ -1005,10 +1010,10 @@ void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSi
|
|
|
|
|
|
|
|
|
|
vkBeginCommandBuffer(buffer, &begin_info); |
|
|
|
|
|
|
|
|
|
VkBufferCopy copy = {0}; |
|
|
|
|
copy.size = size; |
|
|
|
|
vkCmdCopyBuffer(buffer, src, dst, 1, ©); |
|
|
|
|
return buffer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void end_single_time_commands(ApplicationState* state, VkCommandBuffer buffer) { |
|
|
|
|
vkEndCommandBuffer(buffer); |
|
|
|
|
|
|
|
|
|
VkSubmitInfo submit_info = {0}; |
|
|
|
@ -1022,6 +1027,16 @@ void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSi
@@ -1022,6 +1027,16 @@ void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSi
|
|
|
|
|
vkFreeCommandBuffers(state->device, state->commandPool, 1, &buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void copy_buffer(ApplicationState* state, VkBuffer src, VkBuffer dst, VkDeviceSize size) { |
|
|
|
|
VkCommandBuffer buffer = begin_single_time_commands(state); |
|
|
|
|
|
|
|
|
|
VkBufferCopy copy = {0}; |
|
|
|
|
copy.size = size; |
|
|
|
|
vkCmdCopyBuffer(buffer, src, dst, 1, ©); |
|
|
|
|
|
|
|
|
|
end_single_time_commands(state, buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void create_vertex_buffer(ApplicationState* state) { |
|
|
|
|
VkDeviceSize buffer_size = sizeof(Vertex) * ARRSIZE(vertices); |
|
|
|
|
|
|
|
|
@ -1077,6 +1092,153 @@ void create_uniform_buffers(ApplicationState* state) {
@@ -1077,6 +1092,153 @@ void create_uniform_buffers(ApplicationState* state) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void create_image(ApplicationState* state, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, |
|
|
|
|
VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage* image, VkDeviceMemory* memory) { |
|
|
|
|
VkImageCreateInfo image_info = {0}; |
|
|
|
|
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; |
|
|
|
|
image_info.imageType = VK_IMAGE_TYPE_2D; |
|
|
|
|
image_info.extent.width = width; |
|
|
|
|
image_info.extent.height = height; |
|
|
|
|
image_info.extent.depth = 1; |
|
|
|
|
image_info.mipLevels = 1; |
|
|
|
|
image_info.arrayLayers = 1; |
|
|
|
|
image_info.format = format; |
|
|
|
|
image_info.tiling = tiling; |
|
|
|
|
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
image_info.usage = usage; |
|
|
|
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
|
|
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
|
|
|
|
if (vkCreateImage(state->device, &image_info, NULL, image) != VK_SUCCESS) { |
|
|
|
|
printf("failed to create image\n"); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VkMemoryRequirements mem_req; |
|
|
|
|
vkGetImageMemoryRequirements(state->device, state->texture_image, &mem_req); |
|
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo alloc_info = {0}; |
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
|
|
|
|
alloc_info.allocationSize = mem_req.size; |
|
|
|
|
alloc_info.memoryTypeIndex = find_memory_type(state, mem_req.memoryTypeBits, properties); |
|
|
|
|
|
|
|
|
|
if (vkAllocateMemory(state->device, &alloc_info, NULL, memory) != VK_SUCCESS) { |
|
|
|
|
printf("failed to allocate memory for buffer\n"); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vkBindImageMemory(state->device, *image, *memory, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void transition_image_layout(ApplicationState* state, VkImage image, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) { |
|
|
|
|
VkCommandBuffer buffer = begin_single_time_commands(state); |
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier barrier = {0}; |
|
|
|
|
VkPipelineStageFlags source_stage; |
|
|
|
|
VkPipelineStageFlags destination_stage; |
|
|
|
|
|
|
|
|
|
//specify barrier masks
|
|
|
|
|
if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { |
|
|
|
|
barrier.srcAccessMask = 0; |
|
|
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
|
|
|
|
|
|
|
|
|
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; |
|
|
|
|
destination_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; |
|
|
|
|
} else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { |
|
|
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
|
|
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
|
|
|
|
|
|
|
|
|
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; |
|
|
|
|
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; |
|
|
|
|
} else { |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; |
|
|
|
|
barrier.oldLayout = old_layout; |
|
|
|
|
barrier.newLayout = new_layout; |
|
|
|
|
//these are necessary if the barrier is used to transfer ownership
|
|
|
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
|
|
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
|
|
|
|
barrier.image = image; |
|
|
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
|
|
|
|
barrier.subresourceRange.baseMipLevel = 0; |
|
|
|
|
barrier.subresourceRange.levelCount = 1; |
|
|
|
|
barrier.subresourceRange.baseArrayLayer = 0; |
|
|
|
|
barrier.subresourceRange.layerCount = 1; |
|
|
|
|
|
|
|
|
|
vkCmdPipelineBarrier(buffer, |
|
|
|
|
source_stage, destination_stage, |
|
|
|
|
0, |
|
|
|
|
0, NULL, |
|
|
|
|
0, NULL, |
|
|
|
|
1, &barrier); |
|
|
|
|
|
|
|
|
|
end_single_time_commands(state, buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void copy_buffer_to_image(ApplicationState* state, VkBuffer to_copy, VkImage image, uint32_t width, uint32_t height) { |
|
|
|
|
VkCommandBuffer buffer = begin_single_time_commands(state); |
|
|
|
|
|
|
|
|
|
VkBufferImageCopy region = {0}; |
|
|
|
|
region.bufferOffset = 0; |
|
|
|
|
region.bufferRowLength = 0; |
|
|
|
|
region.bufferImageHeight = 0; |
|
|
|
|
|
|
|
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
|
|
|
|
region.imageSubresource.mipLevel = 0; |
|
|
|
|
region.imageSubresource.baseArrayLayer = 0; |
|
|
|
|
region.imageSubresource.layerCount = 1; |
|
|
|
|
|
|
|
|
|
region.imageOffset.x = 0; |
|
|
|
|
region.imageOffset.y = 0; |
|
|
|
|
region.imageOffset.z = 0; |
|
|
|
|
|
|
|
|
|
region.imageExtent.width = width; |
|
|
|
|
region.imageExtent.height = height; |
|
|
|
|
region.imageExtent.depth = 1; |
|
|
|
|
|
|
|
|
|
vkCmdCopyBufferToImage(buffer, to_copy, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); |
|
|
|
|
|
|
|
|
|
end_single_time_commands(state, buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void create_texture_image(ApplicationState* state) { |
|
|
|
|
int width, height, channels; |
|
|
|
|
stbi_uc* pixels = stbi_load("../src/res/texture.jpg", &width, &height, &channels, STBI_rgb_alpha); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!pixels) { |
|
|
|
|
printf("failed to load texture\n"); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VkDeviceSize image_size = width * height * 4; |
|
|
|
|
|
|
|
|
|
VkBuffer staging_buffer; |
|
|
|
|
VkDeviceMemory staging_buffer_memory; |
|
|
|
|
|
|
|
|
|
create_buffer(state, image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
|
|
|
|
&staging_buffer, &staging_buffer_memory); |
|
|
|
|
void* data; |
|
|
|
|
vkMapMemory(state->device, staging_buffer_memory, 0, image_size, 0, &data); |
|
|
|
|
memcpy(data, pixels, image_size); |
|
|
|
|
vkUnmapMemory(state->device, staging_buffer_memory); |
|
|
|
|
stbi_image_free(pixels); |
|
|
|
|
|
|
|
|
|
create_image(state, width, height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, |
|
|
|
|
&state->texture_image, &state->texture_image_memory); |
|
|
|
|
|
|
|
|
|
printf("created image texture\n"); |
|
|
|
|
|
|
|
|
|
transition_image_layout(state, state->texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); |
|
|
|
|
copy_buffer_to_image(state, staging_buffer, state->texture_image, width, height); |
|
|
|
|
transition_image_layout(state, state->texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
|
|
|
|
|
|
|
|
|
vkDestroyBuffer(state->device, staging_buffer, NULL); |
|
|
|
|
vkFreeMemory(state->device, staging_buffer_memory, NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void create_descriptor_pool(ApplicationState* state) { |
|
|
|
|
VkDescriptorPoolSize pool_size = {0}; |
|
|
|
|
pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
|
|
|
@ -1250,6 +1412,7 @@ void init_vulkan(ApplicationState* state) {
@@ -1250,6 +1412,7 @@ void init_vulkan(ApplicationState* state) {
|
|
|
|
|
create_graphics_pipeline(state); |
|
|
|
|
create_framebuffers(state); |
|
|
|
|
create_command_pool(state); |
|
|
|
|
create_texture_image(state); |
|
|
|
|
create_vertex_buffer(state); |
|
|
|
|
create_index_buffer(state); |
|
|
|
|
create_uniform_buffers(state); |
|
|
|
@ -1371,6 +1534,8 @@ void terminate(ApplicationState* state) {
@@ -1371,6 +1534,8 @@ void terminate(ApplicationState* state) {
|
|
|
|
|
vkFreeMemory(state->device, state->vertex_buffer_memory, NULL); |
|
|
|
|
vkDestroyBuffer(state->device, state->index_buffer, NULL); |
|
|
|
|
vkFreeMemory(state->device, state->index_buffer_memory, NULL); |
|
|
|
|
vkDestroyImage(state->device, state->texture_image, NULL); |
|
|
|
|
vkFreeMemory(state->device, state->texture_image_memory, NULL); |
|
|
|
|
|
|
|
|
|
vkDestroySwapchainKHR(state->device, state->swapchain, NULL); |
|
|
|
|
vkDestroyDevice(state->device, NULL); |
|
|
|
|