diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/engine.cpp | 181 | ||||
| -rw-r--r-- | src/math/mat4.hpp | 3 | ||||
| -rw-r--r-- | src/stb_image.c | 1 |
4 files changed, 174 insertions, 19 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 88e4ee3..805db1f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,13 @@ engine_SOURCES = \ o3d/scene.hpp \ ctrl/keyboard.hpp \ ctrl/mouse.hpp -engine_CPPFLAGS = -std=gnu++23 -Wall -Wextra -DDATADIR='"$(datadir)"' -DSHADERSDIR='"$(shadersdir)"' $(GLFW3_CFLAGS) $(STB_CFLAGS) $(VULKAN_CFLAGS) + +# TODO: we should somehow pass assetsdir and texturesdir from ../Makefile.am to this Makefile.am. +# We assume that they will always be relative to datadir, which might not be true if they are +# configured differently +engine_CPPFLAGS = -std=gnu++23 -Wall -Wextra \ + -DDATADIR='"$(datadir)"' -DSHADERSDIR='"$(shadersdir)"' \ + $(GLFW3_CFLAGS) $(STB_CFLAGS) $(VULKAN_CFLAGS) engine_LDFLAGS = -std=gnu++23 -Wall -Wextra engine_LDADD = $(GLFW3_LIBS) $(STB_LIBS) $(VULKAN_LIBS) diff --git a/src/engine.cpp b/src/engine.cpp index 4ab8c60..7af909a 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -471,7 +471,7 @@ create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, return { buf, buf_device_mem }; } -static void copy_bufs(VkQueue graphics_queue, VkDevice device, VkCommandPool cmd_pool, VkBuffer src, VkBuffer dst, VkDeviceSize size) { +static VkCommandBuffer begin_single_time_cmds(VkDevice device, VkCommandPool cmd_pool) { auto cmd_buf = [&]() { VkCommandBufferAllocateInfo cmd_buf_ai { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -482,7 +482,7 @@ static void copy_bufs(VkQueue graphics_queue, VkDevice device, VkCommandPool cmd }; VkCommandBuffer cmd_buf; if (VkResult res = vkAllocateCommandBuffers(device, &cmd_buf_ai, &cmd_buf); res != VK_SUCCESS) { - std::cerr << "failed to allocate copy command buffer, error code: " << string_VkResult(res) << std::endl; + std::cerr << "failed to allocate command buffer, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } return cmd_buf; @@ -495,20 +495,16 @@ static void copy_bufs(VkQueue graphics_queue, VkDevice device, VkCommandPool cmd .pInheritanceInfo = {}, }; if (VkResult res = vkBeginCommandBuffer(cmd_buf, &cmd_buf_bi); res != VK_SUCCESS) { - std::cerr << "failed to begin copy command buffer, error code: " << string_VkResult(res) << std::endl; + std::cerr << "failed to begin command buffer, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } } - { - VkBufferCopy buf_copy { - .srcOffset = 0, - .dstOffset = 0, - .size = size, - }; - vkCmdCopyBuffer(cmd_buf, src, dst, 1, &buf_copy); - } + return cmd_buf; +} + +static void end_single_time_cmds(VkQueue queue, VkDevice device, VkCommandPool cmd_pool, VkCommandBuffer cmd_buf) { if (VkResult res = vkEndCommandBuffer(cmd_buf); res != VK_SUCCESS) { - std::cerr << "failed to end copy command buffer, error code: " << string_VkResult(res) << std::endl; + std::cerr << "failed to end command buffer, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } { @@ -523,18 +519,101 @@ static void copy_bufs(VkQueue graphics_queue, VkDevice device, VkCommandPool cmd .signalSemaphoreCount = {}, .pSignalSemaphores = {}, }; - if (VkResult res = vkQueueSubmit(graphics_queue, 1, &submit_info, nullptr); res != VK_SUCCESS) { - std::cerr << "failed to submit copy command buffer to queue, error code: " << string_VkResult(res) << std::endl; + // TODO: should probably submit every command buffers in a single function call, instead of + // submitting and waiting for them to finish separatly + if (VkResult res = vkQueueSubmit(queue, 1, &submit_info, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to submit command buffer to queue, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } } - if (VkResult res = vkQueueWaitIdle(graphics_queue); res != VK_SUCCESS) { + if (VkResult res = vkQueueWaitIdle(queue); res != VK_SUCCESS) { std::cerr << "failed to wait idle for graphics queue, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } vkFreeCommandBuffers(device, cmd_pool, 1, &cmd_buf); } +static void copy_buf_to_buf(VkCommandBuffer cmd_buf, VkBuffer src, VkBuffer dst, VkDeviceSize size) { + VkBufferCopy buf_copy { + .srcOffset = 0, + .dstOffset = 0, + .size = size, + }; + vkCmdCopyBuffer(cmd_buf, src, dst, 1, &buf_copy); +} + +static void copy_buf_to_img(VkCommandBuffer cmd_buf, VkBuffer src, VkImage dst, uint32_t width, uint32_t height) { + VkBufferImageCopy buf_img_copy { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { // VkImageSubresourceLayers + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = { .x = 0, .y = 0, .z = 0 }, + .imageExtent = { .width = width, .height = height, .depth = 1 }, + }; + vkCmdCopyBufferToImage(cmd_buf, src, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buf_img_copy); +} + +static std::tuple<VkImage, VkDeviceMemory> +create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, VkFormat format, + VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags mem_flags) { + auto texture_img = [&]() { + VkImageCreateInfo img_ci { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = {}, + .imageType = VK_IMAGE_TYPE_2D, + // TODO: check if this format is supported + .format = format, + .extent = { .width = width, .height = height, .depth = 1 }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = tiling, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = {}, + .pQueueFamilyIndices = {}, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + VkImage texture_img; + if (VkResult res = vkCreateImage(device, &img_ci, nullptr, &texture_img); res != VK_SUCCESS) { + std::cerr << "failed to create image, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + return texture_img; + }(); + + auto texture_img_device_mem = [&]() { + VkMemoryRequirements mem_requirements; + vkGetImageMemoryRequirements(device, texture_img, &mem_requirements); + VkMemoryAllocateInfo mem_ai { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = mem_requirements.size, + .memoryTypeIndex = find_mem_type(physical_device, mem_requirements.memoryTypeBits, mem_flags), + }; + VkDeviceMemory texture_img_device_mem; + if (VkResult res = vkAllocateMemory(device, &mem_ai, nullptr, &texture_img_device_mem); res != VK_SUCCESS) { + std::cerr << "failed to allocate texture image memory, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + return texture_img_device_mem; + }(); + + if (VkResult res = vkBindImageMemory(device, texture_img, texture_img_device_mem, 0); res != VK_SUCCESS) { + std::cerr << "failed to bind texture image to memory, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + return std::tuple { texture_img, texture_img_device_mem }; +} + static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img, VkImageLayout old_layout, VkImageLayout new_layout, VkAccessFlags2 src_access_mask, VkAccessFlags2 dst_access_mask, @@ -1089,6 +1168,8 @@ static int main_graphical() { if (graphics_queue_family_index == present_queue_family_index) { swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; } else { + // TODO: should use VK_SHARING_MODE_EXCLUSIVE too, and handle queue family ownership + // while transitionning swapchain image layout swapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; queue_family_indices[0] = graphics_queue_family_index; queue_family_indices[1] = present_queue_family_index; @@ -1400,6 +1481,62 @@ static int main_graphical() { return cmd_pool; }(); + // create texture image + auto [texture_img, texture_img_device_mem] = [&]() { + int w, h, channels; + stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/texture.jpg", &w, &h, &channels, STBI_rgb_alpha); + + // TODO: we're also using this size as the host pixels buffer size, I don't know if mixing + // the two will cause problems later. Right now they are the same, so it doesn't + VkDeviceSize img_size = w * h * 4; + + if (!pixels) { + std::cerr << "failed to load texture image, reason: " << stbi_failure_reason() << std::endl; + std::exit(EXIT_FAILURE); + } + + auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, img_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + { + void* staging_buf_mem; + + if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, img_size, 0, &staging_buf_mem); res != VK_SUCCESS) { + std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + memcpy(staging_buf_mem, pixels, img_size); + + vkUnmapMemory(device, staging_buf_device_mem); + } + + stbi_image_free(pixels); + + auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast<uint32_t>(w), static_cast<uint32_t>(h), 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); + + { + auto cmd_buf = begin_single_time_cmds(device, cmd_pool); + transition_image_layout(cmd_buf, texture_img, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + {}, VK_ACCESS_2_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_TRANSFER_BIT); + copy_buf_to_img(cmd_buf, staging_buf, texture_img, static_cast<uint32_t>(w), static_cast<uint32_t>(h)); + transition_image_layout(cmd_buf, texture_img, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT); + end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + } + + vkFreeMemory(device, staging_buf_device_mem, nullptr); + vkDestroyBuffer(device, staging_buf, nullptr); + + return std::tuple { texture_img, texture_img_device_mem }; + }(); + // create vertex buffer auto [vertex_buf, vertex_buf_device_mem] = [&]() { VkDeviceSize vertex_buf_size = sizeof(engine::vk::Vertex) * vertices.size(); @@ -1429,7 +1566,11 @@ static int main_graphical() { // VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, for optimization. For now we will stick to the one // which does the rendering // TODO: don't wait individually for every copy operations, use a fence or something similar - copy_bufs(graphics_queue, device, cmd_pool, staging_buf, vertex_buf, vertex_buf_size); + { + auto cmd_buf = begin_single_time_cmds(device, cmd_pool); + copy_buf_to_buf(cmd_buf, staging_buf, vertex_buf, vertex_buf_size); + end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + } vkFreeMemory(device, staging_buf_device_mem, nullptr); vkDestroyBuffer(device, staging_buf, nullptr); @@ -1465,7 +1606,11 @@ static int main_graphical() { VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - copy_bufs(graphics_queue, device, cmd_pool, staging_buf, index_buf, index_buf_size); + { + auto cmd_buf = begin_single_time_cmds(device, cmd_pool); + copy_buf_to_buf(cmd_buf, staging_buf, index_buf, index_buf_size); + end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + } vkFreeMemory(device, staging_buf_device_mem, nullptr); vkDestroyBuffer(device, staging_buf, nullptr); @@ -1879,6 +2024,8 @@ static int main_graphical() { vkDestroyBuffer(device, index_buf, nullptr); vkFreeMemory(device, vertex_buf_device_mem, nullptr); vkDestroyBuffer(device, vertex_buf, nullptr); + vkFreeMemory(device, texture_img_device_mem, nullptr); + vkDestroyImage(device, texture_img, nullptr); // no need to free cmd_buf because destroying the command pool will vkDestroyCommandPool(device, cmd_pool, nullptr); // no need to free descriptor_sets because destroying the descriptor pool will diff --git a/src/math/mat4.hpp b/src/math/mat4.hpp index cbbee9a..77c865a 100644 --- a/src/math/mat4.hpp +++ b/src/math/mat4.hpp @@ -7,7 +7,8 @@ namespace engine::math { -// values are represented in memory as column first, meaning values[1] is the first column, 2nd row +// values are represented in memory as column-major order, meaning values[1] is the first column, +// 2nd row struct Matrix4 { static constexpr Matrix4 idty() { return { diff --git a/src/stb_image.c b/src/stb_image.c index 9177288..cf4a748 100644 --- a/src/stb_image.c +++ b/src/stb_image.c @@ -1,2 +1,3 @@ #define STB_IMAGE_IMPLEMENTATION +#define STBI_FAILURE_USERMSG #include <stb_image.h> |
