summaryrefslogtreecommitdiff
path: root/src/vk/texture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vk/texture.cpp')
-rw-r--r--src/vk/texture.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/vk/texture.cpp b/src/vk/texture.cpp
new file mode 100644
index 0000000..ae35035
--- /dev/null
+++ b/src/vk/texture.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "texture.hpp"
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+
+#include "../command.hpp"
+#include "../core.hpp"
+#include "image.hpp"
+#include "source_buffer.hpp"
+
+namespace
+{
+
+struct TextureBuilder
+{
+ VK::Texture *texture;
+ std::string texture_path;
+
+ TextureBuilder(VK::Texture *t, std::string tp);
+ TextureBuilder(VK::Texture *t, const char* tp);
+};
+
+TextureBuilder::TextureBuilder(VK::Texture *t, std::string tp):
+ texture{t},
+ texture_path{tp}
+{
+}
+
+TextureBuilder::TextureBuilder(VK::Texture *t, const char* tp):
+ TextureBuilder{t, std::string(tp)}
+{
+}
+
+void move_image_state(
+ VkCommandBuffer vk_command_buffer, VkImage vk_image, VkFormat format,
+ VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask,
+ VkImageLayout old_layout, VkImageLayout new_layout,
+ VkPipelineStageFlags source_stage, VkPipelineStageFlags destination_stage)
+{
+ VkImageMemoryBarrier barrier{};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.pNext = nullptr;
+ barrier.srcAccessMask = src_access_mask;
+ barrier.dstAccessMask = dst_access_mask;
+ barrier.oldLayout = old_layout;
+ barrier.newLayout = new_layout;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = vk_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(
+ vk_command_buffer, source_stage, destination_stage, 0, 0, nullptr,
+ 0, nullptr, 1, &barrier);
+}
+
+void
+load_image(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ SDL_Surface *image{nullptr};
+
+ // Load file image from file.
+ {
+ SDL_Surface *raw_surface{nullptr};
+ raw_surface = IMG_Load(self->texture_path.c_str());
+ if(raw_surface == nullptr)
+ {
+ std::string error{"Failed to load image. SDL2_image Error: "};
+ error += IMG_GetError();
+ throw CommandError{error};
+ }
+
+ image = SDL_ConvertSurfaceFormat(raw_surface, SDL_PIXELFORMAT_ARGB8888, 0);
+ if(image == nullptr)
+ {
+ std::string error{"Failed to convert image. SDL2 Error: "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+
+ self->texture->width = static_cast<uint32_t>(image->w);
+ self->texture->height = static_cast<uint32_t>(image->h);
+ self->texture->mip_levels = 1;
+
+ SDL_FreeSurface(raw_surface);
+ }
+
+ // Load file image into a vulkan buffer.
+ size_t image_size{static_cast<size_t>(
+ image->format->BytesPerPixel * image->w * image->h)};
+ VK::SourceBuffer source_image_buffer{
+ cg_core.vk_device_with_swapchain, image->pixels, image_size};
+
+ // Create vulkan image.
+ {
+ try
+ {
+ VkExtent3D vk_extent3d{};
+ vk_extent3d.width = self->texture->width;
+ vk_extent3d.height = self->texture->height;
+ vk_extent3d.depth = 1;
+
+ VK::Image::create(
+ cg_core.vk_device_with_swapchain,
+ &self->texture->image,
+ &self->texture->device_memory,
+ VK_FORMAT_R8G8B8A8_UNORM,
+ vk_extent3d,
+ self->texture->mip_levels,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
+ }
+ catch(VK::Image::Error error)
+ {
+ throw CommandError{error.what()};
+ }
+ }
+
+ // Copy image from vulkan buffer into vulkan image.
+ {
+ auto queue_family{
+ cg_core.vk_device_with_swapchain->get_queue_family_with_presentation()};
+ auto queue{queue_family->get_queue()};
+ VK::CommandPool command_pool{queue_family, 1};
+ VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]};
+
+ queue.submit_one_time_command(vk_command_buffer, [&](){
+ move_image_state(
+ vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM,
+ 0, VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ VkBufferImageCopy image_copy{};
+ image_copy.bufferOffset = 0;
+ image_copy.bufferRowLength = 0;
+ image_copy.bufferImageHeight = 0;
+ image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ image_copy.imageSubresource.mipLevel = 0;
+ image_copy.imageSubresource.baseArrayLayer = 0;
+ image_copy.imageSubresource.layerCount = 1;
+ image_copy.imageOffset = {0, 0, 0};
+ image_copy.imageExtent = {self->texture->width, self->texture->height, 1};
+
+ vkCmdCopyBufferToImage(
+ vk_command_buffer, source_image_buffer.buffer, self->texture->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
+
+ move_image_state(
+ vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+ });
+ }
+
+ // Free resources.
+ SDL_FreeSurface(image);
+}
+
+void
+unload_image(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ vkDestroyImage(
+ cg_core.vk_device_with_swapchain->device, self->texture->image, nullptr);
+ vkFreeMemory(
+ cg_core.vk_device_with_swapchain->device, self->texture->device_memory,
+ nullptr);
+}
+
+void
+load_sampler(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ VkSamplerCreateInfo sampler_info{};
+ sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ sampler_info.pNext = nullptr;
+ sampler_info.flags = 0;
+ sampler_info.magFilter = VK_FILTER_LINEAR;
+ sampler_info.minFilter = VK_FILTER_LINEAR;
+ sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.mipLodBias = 0.0f;
+ sampler_info.anisotropyEnable = VK_TRUE;
+ sampler_info.maxAnisotropy = 16;
+ sampler_info.compareEnable = VK_FALSE;
+ sampler_info.compareOp = VK_COMPARE_OP_NEVER;
+ sampler_info.minLod = 0.0f;
+ sampler_info.maxLod = 0.0f;
+ sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ sampler_info.unnormalizedCoordinates = VK_FALSE;
+
+ if(vkCreateSampler(
+ cg_core.vk_device_with_swapchain->device, &sampler_info, nullptr,
+ &self->texture->sampler) != VK_SUCCESS)
+ throw CommandError{"Failed to create texture sampler."};
+}
+
+void
+unload_sampler(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ vkDestroySampler(
+ cg_core.vk_device_with_swapchain->device, self->texture->sampler, nullptr);
+}
+
+void
+load_view(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ try
+ {
+ VK::Image::create_view(
+ cg_core.vk_device_with_swapchain, &self->texture->view,
+ self->texture->image,
+ VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
+ }
+ catch(VK::Image::Error error)
+ {
+ throw CommandError{error.what()};
+ }
+}
+
+void
+unload_view(void *obj)
+{
+ auto self = static_cast<TextureBuilder*>(obj);
+
+ vkDestroyImageView(
+ cg_core.vk_device_with_swapchain->device, self->texture->view, nullptr);
+}
+
+const CommandChain loader{
+ {&load_image, &unload_image},
+ {&load_sampler, &unload_sampler},
+ {&load_view, &unload_view}
+};
+
+}
+
+namespace VK
+{
+
+Texture::Texture(std::string texture_path)
+{
+ TextureBuilder texture_builder(this, texture_path);
+ loader.execute(&texture_builder);
+}
+
+Texture::Texture(const char* texture_path):
+ Texture{std::string(texture_path)}
+{
+}
+
+Texture::~Texture()
+{
+ TextureBuilder texture_builder(this, "");
+ loader.revert(&texture_builder);
+}
+
+}