From f88712a929ee3543f8e1d45c6071f676df339cdb Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Tue, 2 Aug 2022 16:52:33 -0300 Subject: refa Use Vulkan for graphics This is a partial refactory. Some functionalities implemented in SDL were removed and need reimplementation. --- src/vk/texture.cpp | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 src/vk/texture.cpp (limited to 'src/vk/texture.cpp') 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 +#include + +#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(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(image->w); + self->texture->height = static_cast(image->h); + self->texture->mip_levels = 1; + + SDL_FreeSurface(raw_surface); + } + + // Load file image into a vulkan buffer. + size_t image_size{static_cast( + 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(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(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(obj); + + vkDestroySampler( + cg_core.vk_device_with_swapchain->device, self->texture->sampler, nullptr); +} + +void +load_view(void *obj) +{ + auto self = static_cast(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(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); +} + +} -- cgit v1.2.3