diff --git a/CMakeLists.txt b/CMakeLists.txt index 563e29a..e7b3836 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,12 +191,14 @@ public: if (type STREQUAL "image") cmake_path(GET file_path PARENT_PATH file_parent) cmake_path(GET file_path FILENAME file_name) - exec_program("${ImageMagick_identify_EXECUTABLE}" "${file_parent}" - ARGS -format "%[fx:w]" "${file_name}" - OUTPUT_VARIABLE image_width) - exec_program("${ImageMagick_identify_EXECUTABLE}" "${file_parent}" - ARGS -format "%[fx:h]" "${file_name}" - OUTPUT_VARIABLE image_height) + execute_process(COMMAND "${ImageMagick_identify_EXECUTABLE}" -format "%[fx:w]" "${file_name}" + WORKING_DIRECTORY "${file_parent}" + OUTPUT_VARIABLE image_width + COMMAND_ERROR_IS_FATAL ANY) + execute_process(COMMAND "${ImageMagick_identify_EXECUTABLE}" -format "%[fx:h]" "${file_name}" + WORKING_DIRECTORY "${file_parent}" + OUTPUT_VARIABLE image_height + COMMAND_ERROR_IS_FATAL ANY) file(APPEND "${EMBED_HEADER_DIR}/embed_resources.hpp" "inline EmbededImage embeded_${obj_cleaned_name}(${image_width}, ${image_height}, EmbededResource(&_binary_${obj_cleaned_name}_start, &_binary_${obj_cleaned_name}_end)); \n") elseif (type STREQUAL "embed") @@ -206,7 +208,7 @@ public: endforeach() set(EMBED_OBJ_RET "${EMBED_OBJ_RET}" PARENT_SCOPE) endfunction() -EmbedResources(${SHADILER_OBJ_RET}) +EmbedResources(${SHADILER_OBJ_RET} "${CMAKE_CURRENT_SOURCE_DIR}/res/debug_north.png") target_sources(${PROJECT_NAME} PRIVATE ${EMBED_OBJ_RET}) target_include_directories(${PROJECT_NAME} PUBLIC "${EMBED_HEADER_DIR}") diff --git a/res/debug_north.png b/res/debug_north.png new file mode 100644 index 0000000..5c0cedd Binary files /dev/null and b/res/debug_north.png differ diff --git a/shader/shader.frag.glsl b/shader/shader.frag.glsl index 187b463..9517576 100644 --- a/shader/shader.frag.glsl +++ b/shader/shader.frag.glsl @@ -19,9 +19,12 @@ #version 450 -layout(location = 0) out vec4 out_color; layout(location = 0) in vec3 frag_color; +layout(location = 1) in vec2 frag_texture_coordinate; +layout(binding = 1) uniform sampler2D texture_sampler; + +layout(location = 0) out vec4 out_color; void main() { - out_color = vec4(frag_color, 1.0); + out_color = texture(texture_sampler, frag_texture_coordinate); } diff --git a/shader/shader.vertex.glsl b/shader/shader.vertex.glsl index 77a22a0..b7637d9 100644 --- a/shader/shader.vertex.glsl +++ b/shader/shader.vertex.glsl @@ -27,11 +27,13 @@ layout(binding = 0) uniform UniformBufferObject { layout(location = 0) in vec2 in_position; layout(location = 2) in vec3 in_color; +layout(location = 4) in vec2 in_texture_coordinate; layout(location = 0) out vec3 frag_color; +layout(location = 1) out vec2 frag_texture_coordinate; void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(in_position, 0.0, 1.0); - //gl_Position = vec4(, 0.5, 1.0); frag_color = in_color; + frag_texture_coordinate = in_texture_coordinate; } diff --git a/src/main.cpp b/src/main.cpp index 8afaadd..64f5fc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,7 +68,8 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback( class VulkanExampleApplication { private: - using VulkanBuffers = std::pair, std::unique_ptr>; + using VulkanBuffer = std::pair, std::unique_ptr>; + using VulkanImage = std::pair, std::unique_ptr>; std::unique_ptr libsdl; std::unique_ptr window; @@ -119,12 +120,13 @@ private: } vk_physical_card_info; public: - VulkanBuffers vk_buffer_vertex; // in order to get a clean desctruction sequence, instantiate the DeviceMemory for the vertex buffer first // https://github.com/KhronosGroup/Vulkan-Hpp/blob/6f72ceca515d59f40d64b64cf2734f6261e1f9f2/RAII_Samples/13_InitVertexBuffer/13_InitVertexBuffer.cpp - VulkanBuffers vk_buffer_index; // used to make rendering models more efficent by sharing values + VulkanBuffer vk_buffer_vertex; // in order to get a clean desctruction sequence, instantiate the DeviceMemory for the vertex buffer first // https://github.com/KhronosGroup/Vulkan-Hpp/blob/6f72ceca515d59f40d64b64cf2734f6261e1f9f2/RAII_Samples/13_InitVertexBuffer/13_InitVertexBuffer.cpp + VulkanBuffer vk_buffer_index; // used to make rendering models more efficent by sharing values struct Vertex { glm::vec2 pos; glm::vec3 color; + glm::vec2 texture_coordinates; static constexpr vk::VertexInputBindingDescription GetBindingDescription() { constexpr vk::VertexInputBindingDescription binding_description { @@ -134,8 +136,8 @@ public: }; return binding_description; } - static constexpr std::array GetAttributeDescriptions() { - constexpr std::array attribute_descriptions { + static constexpr std::array GetAttributeDescriptions() { + constexpr std::array attribute_descriptions { vk::VertexInputAttributeDescription { // https://docs.vulkan.org/tutorial/latest/04_Vertex_buffers/00_Vertex_input_description.html .location = 0, .binding = 0, @@ -145,16 +147,22 @@ public: .location = 2, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, - .offset = offsetof(Vertex, color) } + .offset = offsetof(Vertex, color) }, + vk::VertexInputAttributeDescription { + .location = 4, + .binding = 0, + .format = vk::Format::eR32G32Sfloat, + .offset = offsetof(Vertex, texture_coordinates) } + }; return attribute_descriptions; } }; static inline const std::vector quad_vertices = { - { { -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f } }, - { { 0.5f, -0.5f }, { 0.0f, 1.0f, 0.0f } }, - { { 0.5f, 0.5f }, { 0.0f, 0.0f, 1.0f } }, - { { -0.5f, 0.5f }, { 1.0f, 1.0f, 1.0f } } + { { -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f } }, + { { 0.5f, -0.5f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, + { { 0.5f, 0.5f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f } }, + { { -0.5f, 0.5f }, { 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f } } }; static inline const std::vector quad_indexes = { 0, 1, 2, 2, 3, 0 // Wraps a quad-box's 2 triangles around itself. // https://docs.vulkan.org/tutorial/latest/04_Vertex_buffers/03_Index_buffer.html @@ -167,9 +175,14 @@ public: alignas(16) glm::mat4 view; alignas(16) glm::mat4 proj; }; - std::vector vk_buffers_uniform; + std::vector vk_buffers_uniform; std::vector vk_buffers_uniform_mapped; +public: + VulkanImage vk_texture_image; + std::unique_ptr vk_texture_view; + std::unique_ptr vk_texture_sampler; + public: VulkanExampleApplication() { try { // try me // all the code is in init :O boo hoo, ill shove all ur ram out of scope ASAP i can, dont seperate them, just scope them instead dumb dumb. @@ -304,6 +317,9 @@ public: if (!VulkanCheckDeviceExtensionSupport(gfx_card)) return ret; + if (!device_features.samplerAnisotropy) + return ret; + ret.surface_capabilities = gfx_card.getSurfaceCapabilitiesKHR(*found_screen_surface); const auto surface_present_modes = gfx_card.getSurfacePresentModesKHR(*found_screen_surface); const auto surface_formats = gfx_card.getSurfaceFormatsKHR(*found_screen_surface); @@ -352,7 +368,9 @@ public: return device_queues_we_need; }(this->vk_physical_card_info.graphics_family.value(), this->vk_physical_card_info.present_family.value()); - constexpr vk::PhysicalDeviceFeatures device_features {}; + constexpr vk::PhysicalDeviceFeatures device_features { + .samplerAnisotropy = vk::True + }; const vk::DeviceCreateInfo device_create_info { .queueCreateInfoCount = static_cast(device_queue_create_infos.size()), .pQueueCreateInfos = device_queue_create_infos.data(), @@ -552,16 +570,25 @@ public: }; // Descriptor Layouts, appears required. - constexpr auto descriptor_set_layout_binding_ubo = vk::DescriptorSetLayoutBinding { + constexpr vk::DescriptorSetLayoutBinding descriptor_set_layout_binding_ubo { .binding = 0, .descriptorType = vk::DescriptorType::eUniformBuffer, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eVertex, .pImmutableSamplers = nullptr // Optional }; - const auto descriptor_set_layout_create_info = vk::DescriptorSetLayoutCreateInfo { - .bindingCount = 1, - .pBindings = &descriptor_set_layout_binding_ubo + constexpr vk::DescriptorSetLayoutBinding descriptor_set_layout_binding_sampler { + .binding = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .pImmutableSamplers = nullptr // Optional + }; + + const std::array descriptor_set_layout_bindings = { descriptor_set_layout_binding_ubo, descriptor_set_layout_binding_sampler }; + const vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_create_info { + .bindingCount = static_cast(descriptor_set_layout_bindings.size()), + .pBindings = descriptor_set_layout_bindings.data() }; this->vk_descriptor_set_layout_ubo = std::make_unique(*this->vk_gpu, descriptor_set_layout_create_info); @@ -575,15 +602,20 @@ public: this->vk_pipeline_layout = std::make_unique(*this->vk_gpu, pipeline_layout_create_info); // descriptors - const vk::DescriptorPoolSize descriptor_pool_size { + const vk::DescriptorPoolSize descriptor_pool_size_ubo { .type = vk::DescriptorType::eUniformBuffer, .descriptorCount = static_cast(this->vk_max_frames_in_flight) }; + const vk::DescriptorPoolSize descriptor_pool_size_sampler { + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = static_cast(this->vk_max_frames_in_flight) + }; + const std::array descriptor_pool_sizes = { descriptor_pool_size_ubo, descriptor_pool_size_sampler }; const vk::DescriptorPoolCreateInfo descriptor_pool_create_info { .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, .maxSets = static_cast(this->vk_max_frames_in_flight), - .poolSizeCount = 1, - .pPoolSizes = &descriptor_pool_size, + .poolSizeCount = static_cast(descriptor_pool_sizes.size()), + .pPoolSizes = descriptor_pool_sizes.data(), }; assert(descriptor_pool_create_info.flags & vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); this->vk_descriptor_pool = std::make_unique(*this->vk_gpu, descriptor_pool_create_info); @@ -717,7 +749,7 @@ public: this->vk_fences_in_flight.emplace_back(*this->vk_gpu, vk::FenceCreateInfo { .flags = vk::FenceCreateFlagBits::eSignaled }); } } - { + { // uniform buffer object assert(this->vk_buffers_uniform.empty()); assert(this->vk_buffers_uniform_mapped.empty()); this->vk_buffers_uniform.reserve(this->vk_max_frames_in_flight); @@ -728,19 +760,71 @@ public: this->vk_buffers_uniform_mapped.emplace_back(static_cast(uniform_buffer.second->mapMemory(0, ubo_buffer_size))); this->vk_buffers_uniform.emplace_back(std::move(uniform_buffer)); } + } + { // texture + const auto texture_buffer_size = embeded_debug_north_png_rgba.data.size; + const vk::Extent2D texture_buffer_extent = { .width = static_cast(embeded_debug_north_png_rgba.width), .height = static_cast(embeded_debug_north_png_rgba.height) }; + const auto texture_buffer_staging = this->CreateBuffer(texture_buffer_size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); + + std::uint8_t* const texture_buffer_data = static_cast(texture_buffer_staging.second->mapMemory(0, texture_buffer_size)); + memcpy(texture_buffer_data, embeded_debug_north_png_rgba.data.begin, texture_buffer_size); + texture_buffer_staging.second->unmapMemory(); + + this->vk_texture_image = CreateImage(texture_buffer_extent, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal); + + this->SingleTimeSubmitCommand([&](const auto& command_buffer) { + this->TransitionImageLayout(command_buffer, *this->vk_texture_image.first, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); + this->CopyBuffer(command_buffer, *texture_buffer_staging.first, *this->vk_texture_image.first, texture_buffer_extent); + this->TransitionImageLayout(command_buffer, *this->vk_texture_image.first, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); + }); + const vk::ImageViewCreateInfo imageview_create_info { + .image = **this->vk_texture_image.first, + .viewType = vk::ImageViewType::e2D, + .format = vk::Format::eR8G8B8A8Srgb, + .components = { + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eIdentity }, + .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }, + }; + this->vk_texture_view = std::make_unique(*this->vk_gpu, imageview_create_info); + + const vk::SamplerCreateInfo sampler_create_info { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eLinear, + .addressModeU = vk::SamplerAddressMode::eRepeat, // https://docs.vulkan.org/tutorial/latest/06_Texture_mapping/01_Image_view_and_sampler.html#_samplers + .addressModeV = vk::SamplerAddressMode::eRepeat, + .addressModeW = vk::SamplerAddressMode::eRepeat, + .mipLodBias = 0.0f, + .anisotropyEnable = vk::True, + .maxAnisotropy = std::max(4.0f, this->vk_gfx_card->getProperties().limits.maxSamplerAnisotropy), + .compareEnable = vk::False, + .compareOp = vk::CompareOp::eAlways, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = vk::BorderColor::eIntOpaqueBlack, + .unnormalizedCoordinates = vk::False, + }; + this->vk_texture_sampler = std::make_unique(*this->vk_gpu, sampler_create_info); + } + { // update descriptors + std::vector write_descriptor_sets; + write_descriptor_sets.reserve(this->vk_max_frames_in_flight); std::vector buffer_infos; - std::vector write_descriptor_sets; buffer_infos.reserve(this->vk_max_frames_in_flight); - write_descriptor_sets.reserve(this->vk_max_frames_in_flight); for (std::size_t i = 0; i < this->vk_max_frames_in_flight; i++) { - const vk::DescriptorBufferInfo descriptor_buffer_info { + const vk::DescriptorBufferInfo descriptor_buffer_info_ubo { .buffer = **this->vk_buffers_uniform.at(i).first, .offset = 0, .range = sizeof(UniformBufferObject), }; - const vk::DescriptorBufferInfo& descriptor_buffer_info_handle = buffer_infos.emplace_back(descriptor_buffer_info); - const vk::WriteDescriptorSet descriptor_write { + + const vk::DescriptorBufferInfo& descriptor_buffer_info_handle = buffer_infos.emplace_back(descriptor_buffer_info_ubo); + + const vk::WriteDescriptorSet descriptor_write_ubo { .dstSet = *this->vk_descriptor_sets[i], .dstBinding = 0, .dstArrayElement = 0, @@ -750,8 +834,29 @@ public: .pBufferInfo = &descriptor_buffer_info_handle, .pTexelBufferView = nullptr, // Optional }; - write_descriptor_sets.emplace_back(descriptor_write); + write_descriptor_sets.emplace_back(descriptor_write_ubo); } + + std::vector image_infos; + image_infos.reserve(this->vk_max_frames_in_flight); + for (std::size_t i = 0; i < this->vk_max_frames_in_flight; i++) { + const vk::DescriptorImageInfo descriptor_image_info_texture { + .sampler = **this->vk_texture_sampler, + .imageView = **this->vk_texture_view, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + const vk::DescriptorImageInfo& descriptor_image_info_handle = image_infos.emplace_back(descriptor_image_info_texture); + const vk::WriteDescriptorSet descriptor_write_texture { + .dstSet = *this->vk_descriptor_sets[i], + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &descriptor_image_info_handle // Optional + }; + write_descriptor_sets.emplace_back(descriptor_write_texture); + } + this->vk_gpu->updateDescriptorSets(write_descriptor_sets, nullptr); } } catch (const vk::SystemError& error) { @@ -769,8 +874,7 @@ public: } protected: - VulkanBuffers - CreateBuffer(vk::DeviceSize wanted_size, vk::BufferUsageFlags wanted_usage, vk::MemoryPropertyFlags wanted_properties) const { + VulkanBuffer CreateBuffer(vk::DeviceSize wanted_size, vk::BufferUsageFlags wanted_usage, vk::MemoryPropertyFlags wanted_properties) const { const vk::BufferCreateInfo buffer_create_info { .size = wanted_size, .usage = wanted_usage, @@ -779,45 +883,107 @@ protected: const auto allocated_buffer = std::make_unique(*this->vk_gpu, buffer_create_info); const auto memory_requirements = allocated_buffer->getMemoryRequirements(); - const auto FindMemoryType = [this](const std::uint32_t& type_filter, const vk::MemoryPropertyFlags& wanted_properties) -> std::uint32_t { - const auto memory_properties = this->vk_gfx_card->getMemoryProperties(); - const std::uint32_t memory_type_count = memory_properties.memoryTypeCount; - std::uint32_t memory_type_to_test; - for (memory_type_to_test = 0; memory_type_to_test < memory_type_count; memory_type_to_test++) { - if ((*reinterpret_cast*>(&type_filter))[memory_type_to_test]) { - if ((memory_properties.memoryTypes[memory_type_to_test].propertyFlags & wanted_properties) == wanted_properties) { - break; - } - } - } - if (memory_type_to_test == memory_type_count) - throw std::runtime_error("failed to find suitable memory type!"); - return memory_type_to_test; - }; const vk::MemoryAllocateInfo vertex_allocate_info { .allocationSize = memory_requirements.size, - .memoryTypeIndex = FindMemoryType(memory_requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent) + .memoryTypeIndex = this->FindMemoryType(memory_requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent) }; const auto allocated_buffer_memory = std::make_unique(*this->vk_gpu, vertex_allocate_info); allocated_buffer->bindMemory(**allocated_buffer_memory, 0); return { std::make_unique(std::move(*allocated_buffer)), std::make_unique(std::move(*allocated_buffer_memory)) }; }; - void CopyBuffer(const vk::Buffer& src_buffer, const vk::Buffer& dest_buffer, const vk::DeviceSize& size) const { - const vk::CommandBufferAllocateInfo command_buffer_allocate_info { - .commandPool = **this->vk_command_pool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1 + VulkanImage CreateImage(vk::Extent2D image_size, vk::Format wanted_format, vk::ImageTiling wanted_tiling, vk::ImageUsageFlags wanted_usage, vk::MemoryPropertyFlags wanted_properties) const { + const vk::ImageCreateInfo image_create_info { + .imageType = vk::ImageType::e2D, + .format = wanted_format, + .extent { + .width = image_size.width, + .height = image_size.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .tiling = wanted_tiling, // https://docs.vulkan.org/tutorial/latest/06_Texture_mapping/00_Images.html#_texture_image + .usage = wanted_usage, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + //.flags = 0, // optional }; - const auto command_buffer = std::move(vk::raii::CommandBuffers(*this->vk_gpu, command_buffer_allocate_info).front()); - command_buffer.begin(vk::CommandBufferBeginInfo { .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit }); + const auto allocated_image_texture = std::make_unique(*this->vk_gpu, image_create_info); + const auto memory_requirements = allocated_image_texture->getMemoryRequirements(); + const vk::MemoryAllocateInfo image_allocate_info { + .allocationSize = memory_requirements.size, + .memoryTypeIndex = this->FindMemoryType(memory_requirements.memoryTypeBits, wanted_properties) + }; + const auto allocated_image_memory = std::make_unique(*this->vk_gpu, image_allocate_info); + allocated_image_texture->bindMemory(**allocated_image_memory, 0); + return { std::make_unique(std::move(*allocated_image_texture)), std::make_unique(std::move(*allocated_image_memory)) }; + } + const std::uint32_t FindMemoryType(const std::uint32_t& type_filter, const vk::MemoryPropertyFlags& wanted_properties) const { + const auto memory_properties = this->vk_gfx_card->getMemoryProperties(); + const std::uint32_t memory_type_count = memory_properties.memoryTypeCount; + + std::uint32_t memory_type_to_test; + for (memory_type_to_test = 0; memory_type_to_test < memory_type_count; memory_type_to_test++) { + if ((*reinterpret_cast*>(&type_filter))[memory_type_to_test]) { + if ((memory_properties.memoryTypes[memory_type_to_test].propertyFlags & wanted_properties) == wanted_properties) { + break; + } + } + } + if (memory_type_to_test == memory_type_count) + throw std::runtime_error("failed to find suitable memory type!"); + return memory_type_to_test; + } + void CopyBuffer(const vk::CommandBuffer& command_buffer, const vk::Buffer& src_buffer, const vk::Buffer& dest_buffer, const vk::DeviceSize& size) const { const vk::BufferCopy copy_region { //.srcOffset = 0, // Optional //.dstOffset = 0, // Optional .size = size }; command_buffer.copyBuffer(src_buffer, dest_buffer, { copy_region }); + } + void CopyBuffer(const vk::Buffer& src_buffer, const vk::Buffer& dest_buffer, const vk::DeviceSize& size) const { + this->SingleTimeSubmitCommand([&](const auto& command_buffer) { + this->CopyBuffer(command_buffer, src_buffer, dest_buffer, size); + }); + }; + void CopyBuffer(const vk::CommandBuffer& command_buffer, const vk::Buffer& src_buffer, const vk::Image& dest_buffer, const vk::Extent2D& size) const { + const vk::BufferImageCopy copy_region { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { size.width, size.height, 1 } + }; + command_buffer.copyBufferToImage(src_buffer, dest_buffer, vk::ImageLayout::eTransferDstOptimal, copy_region); + } + void CopyBuffer(const vk::Buffer& src_buffer, const vk::Image& dest_buffer, const vk::Extent2D& size) const { + this->SingleTimeSubmitCommand([&](const auto& command_buffer) { + this->CopyBuffer(command_buffer, src_buffer, dest_buffer, size); + }); + } + template + void SingleTimeSubmitCommand(const Func& single_time_function) const { + const vk::CommandBufferAllocateInfo command_buffer_allocate_info { + .commandPool = **this->vk_command_pool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1 + }; + auto command_buffer = std::move(vk::raii::CommandBuffers(*this->vk_gpu, command_buffer_allocate_info).front()); + + command_buffer.begin(vk::CommandBufferBeginInfo { .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit }); + + single_time_function(command_buffer); + command_buffer.end(); const vk::SubmitInfo submit_queue { @@ -826,7 +992,104 @@ protected: }; this->vk_queue_graphics->submit(submit_queue); this->vk_queue_graphics->waitIdle(); - }; + } + void TransitionImageLayout(const vk::CommandBuffer& command_buffer, const vk::Image& image, vk::Format format, const vk::ImageLayout& old_layout, const vk::ImageLayout& new_layout) const { + // https://github.com/KhronosGroup/Vulkan-Hpp/blob/main/RAII_Samples/utils/utils.hpp#L101 // credits to the vulkanhpp project for having fantastic usage examples + // https://docs.vulkan.org/tutorial/latest/06_Texture_mapping/00_Images.html#_transition_barrier_masks + const vk::AccessFlags source_access_mask = [](const vk::ImageLayout& old_layout) -> vk::AccessFlags { + switch (old_layout) { + case vk::ImageLayout::eTransferDstOptimal: + return vk::AccessFlagBits::eTransferWrite; + case vk::ImageLayout::ePreinitialized: + return vk::AccessFlagBits::eHostWrite; + case vk::ImageLayout::eGeneral: // sourceAccessMask is empty + case vk::ImageLayout::eUndefined: + break; + default: + assert(false); + break; + } + return {}; + }(old_layout); + + const vk::PipelineStageFlags source_stage = [](const vk::ImageLayout& old_layout) -> vk::PipelineStageFlags { + switch (old_layout) { + case vk::ImageLayout::eGeneral: + case vk::ImageLayout::ePreinitialized: + return vk::PipelineStageFlagBits::eHost; + case vk::ImageLayout::eTransferDstOptimal: + return vk::PipelineStageFlagBits::eTransfer; + case vk::ImageLayout::eUndefined: + return vk::PipelineStageFlagBits::eTopOfPipe; + default: + assert(false); + break; + } + return {}; + }(old_layout); + + const vk::AccessFlags destination_access_mask = [](const vk::ImageLayout& new_layout) -> vk::AccessFlags { + switch (new_layout) { + case vk::ImageLayout::eColorAttachmentOptimal: + return vk::AccessFlagBits::eColorAttachmentWrite; + case vk::ImageLayout::eDepthStencilAttachmentOptimal: + return vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite; + case vk::ImageLayout::eGeneral: // empty destinationAccessMask + case vk::ImageLayout::ePresentSrcKHR: + break; + case vk::ImageLayout::eShaderReadOnlyOptimal: + return vk::AccessFlagBits::eShaderRead; + case vk::ImageLayout::eTransferSrcOptimal: + return vk::AccessFlagBits::eTransferRead; + case vk::ImageLayout::eTransferDstOptimal: + return vk::AccessFlagBits::eTransferWrite; + default: + assert(false); + break; + } + return {}; + }(new_layout); + + const vk::PipelineStageFlags destination_stage = [](const vk::ImageLayout& new_layout) -> vk::PipelineStageFlags { + switch (new_layout) { + case vk::ImageLayout::eColorAttachmentOptimal: + return vk::PipelineStageFlagBits::eColorAttachmentOutput; + case vk::ImageLayout::eDepthStencilAttachmentOptimal: + return vk::PipelineStageFlagBits::eEarlyFragmentTests; + case vk::ImageLayout::eGeneral: + return vk::PipelineStageFlagBits::eHost; + case vk::ImageLayout::ePresentSrcKHR: + return vk::PipelineStageFlagBits::eBottomOfPipe; + case vk::ImageLayout::eShaderReadOnlyOptimal: + return vk::PipelineStageFlagBits::eFragmentShader; + case vk::ImageLayout::eTransferDstOptimal: + case vk::ImageLayout::eTransferSrcOptimal: + return vk::PipelineStageFlagBits::eTransfer; + default: + assert(false); + break; + } + return {}; + }(new_layout); + + const vk::ImageMemoryBarrier image_memory_barrier { + .srcAccessMask = source_access_mask, + .dstAccessMask = destination_access_mask, + .oldLayout = old_layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .image = image, + .subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + command_buffer.pipelineBarrier(source_stage, destination_stage, {}, nullptr, nullptr, image_memory_barrier); + } public: void Run() {