diff options
Diffstat (limited to '')
-rw-r--r-- | engine/xe_image.cpp | 166 |
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, ®ion); + + xeDevice.endSingleTimeCommands(commandBuffer); + } + +}
\ No newline at end of file |