summaryrefslogtreecommitdiff
path: root/engine/xe_image.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--engine/xe_image.cpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/engine/xe_image.cpp b/engine/xe_image.cpp
new file mode 100644
index 0000000..c7b1488
--- /dev/null
+++ b/engine/xe_image.cpp
@@ -0,0 +1,166 @@
+#include "xe_image.hpp"
+
+#include <vulkan/vulkan.h>
+#include <stdexcept>
+#include <memory>
+#include <cstring>
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+
+namespace xe {
+
+XeImage::XeImage(XeDevice &xeDevice, const std::string &filename) : xeDevice{xeDevice} {
+ createTextureImage(filename);
+}
+
+XeImage::~XeImage() {
+ vkDestroyImage(xeDevice.device(), textureImage, nullptr);
+ vkFreeMemory(xeDevice.device(), textureImageMemory, nullptr);
+}
+
+void XeImage::createTextureImage(const std::string &filename) {
+ int texWidth, texHeight, texChannels;
+ stbi_uc* pixels = stbi_load(filename.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
+ VkDeviceSize imageSize = texWidth * texHeight * 4;
+
+ if (!pixels) {
+ throw std::runtime_error("failed to load texture: " + filename);
+ }
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingBufferMemory;
+
+ xeDevice.createBuffer(
+ imageSize,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ stagingBuffer,
+ stagingBufferMemory
+ );
+
+ void* data;
+ vkMapMemory(xeDevice.device(), stagingBufferMemory, 0, imageSize, 0, &data);
+ memcpy(data, pixels, static_cast<size_t>(imageSize));
+ vkUnmapMemory(xeDevice.device(), stagingBufferMemory);
+
+ stbi_image_free(pixels);
+
+ createImage(texWidth, texHeight, 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, textureImage, textureImageMemory);
+
+ transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
+ transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vkDestroyBuffer(xeDevice.device(), stagingBuffer, nullptr);
+ vkFreeMemory(xeDevice.device(), stagingBufferMemory, nullptr);
+
+}
+
+void XeImage::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
+
+ VkImageCreateInfo imageInfo{};
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent.width = width;
+ imageInfo.extent.height = height;
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+ imageInfo.format = format;
+ imageInfo.tiling = tiling;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageInfo.usage = usage;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ if (vkCreateImage(xeDevice.device(), &imageInfo, nullptr, &image) != VK_SUCCESS) {
+ throw std::runtime_error("failed to create image!");
+ }
+
+ VkMemoryRequirements memRequirements;
+ vkGetImageMemoryRequirements(xeDevice.device(), image, &memRequirements);
+
+ VkMemoryAllocateInfo allocInfo{};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = xeDevice.findMemoryType(memRequirements.memoryTypeBits, properties);
+
+ if (vkAllocateMemory(xeDevice.device(), &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
+ throw std::runtime_error("failed to allocate image memory!");
+ }
+
+ vkBindImageMemory(xeDevice.device(), image, imageMemory, 0);
+}
+
+void XeImage::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
+ VkCommandBuffer commandBuffer = xeDevice.beginSingleTimeCommands();
+
+ VkImageMemoryBarrier barrier{};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ 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;
+
+ VkPipelineStageFlags sourceStage;
+ VkPipelineStageFlags destinationStage;
+
+ if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ throw std::invalid_argument("unsupported layout transition!");
+ }
+
+ vkCmdPipelineBarrier(
+ commandBuffer,
+ sourceStage, destinationStage,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &barrier
+ );
+
+ xeDevice.endSingleTimeCommands(commandBuffer);
+ }
+
+ void XeImage::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
+ VkCommandBuffer commandBuffer = xeDevice.beginSingleTimeCommands();
+
+ VkBufferImageCopy region{};
+ 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 = {0, 0, 0};
+ region.imageExtent = {
+ width,
+ height,
+ 1
+ };
+
+ vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ xeDevice.endSingleTimeCommands(commandBuffer);
+ }
+
+} \ No newline at end of file