aboutsummaryrefslogtreecommitdiff
path: root/src/engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine.cpp')
-rw-r--r--src/engine.cpp181
1 files changed, 164 insertions, 17 deletions
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