diff --git a/' b/' new file mode 100644 index 0000000..0cf3de9 --- /dev/null +++ b/' @@ -0,0 +1,1786 @@ + +/* + * VulkZample + * Copyright (C) 2024 Rebekah Rowe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#define VULKAN_HPP_NO_CONSTRUCTORS +#include +#include +#include + +#include + +#define VMA_IMPLEMENTATION +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 // Obviously its the C++ way, just initialize it since its ez // https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/debugging_memory_usage.html +#define VMA_DEBUG_DETECT_CORRUPTION 1 // heap overflow, enable! +#include + +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include + +static constexpr bool fatal_errors = true; // keep this true for tests + +#define ENABLE_VULKAN_VALIDATION true +static constexpr bool vulkan_enable_validation_layers = +#if defined(ENABLE_VULKAN_VALIDATION) + true; +#else + false; +#endif + +#if defined(ENABLE_VULKAN_VALIDATION) +static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanExampleApplication_VulkanDebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_type, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, + void* user_data); +/*static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanExampleApplication_VulkanReportCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT type, + uint64_t obj, + size_t location, + int32_t code, + const char* layer_prefix, + const char* msg, + void* user_data);*/ +#endif + +class VulkanExampleApplication { +private: + using VulkanBuffer = std::optional>; + using VulkanImage = std::optional>; + +public: // Window/Device setup + std::optional libsdl; + std::optional window; + std::optional vk_ctx; + std::optional vk_inst; + std::optional vk_debug_utils_messenger; + // std::optional vk_debug_report_callback; + std::optional vk_gfx_card; + std::optional vk_screen_surface; + std::optional vk_gpu; + + struct VulkanDeviceQueriedInfo { + std::optional screen_surface; + std::optional graphics_family; + std::optional present_family; + bool surface_has_present_modes = false; + bool surface_has_format_modes = false; + std::optional msaa_samples; + std::vector supported_features_vma_device; + std::vector supported_features_vma_allocator; + + bool IsGoodCard() const { + return this->graphics_family.has_value() && this->present_family.has_value() && this->screen_surface && surface_has_present_modes && surface_has_format_modes; + } + } vk_physical_card_info; + +private: + vma::UniqueAllocator vk_allocator; + +private: // Swapchain/Framebuffer + std::optional vk_render_pass; + + std::optional vk_queue_graphics; + std::optional vk_queue_present; + + std::optional vk_swapchain; + vk::Format vk_swapchain_image_format; + vk::Extent2D vk_swapchain_extent; + std::vector vk_swapchain_image_views; + std::vector vk_swapchain_framebuffers; + + uint vk_current_frame_index = 0; + std::optional vk_command_pool; + std::vector vk_command_buffers; + std::vector vk_fences_in_flight; + std::vector vk_semephores_image_available; + std::vector vk_semephores_render_finished; + +private: + enum DrawMode : std::uint32_t { + kPlain = 1, + kTextured = 2, + kFreetype = 3 + }; + static constexpr vk::ShaderModuleCreateInfo GetSharedShaderFragment() { + return { .codeSize = embeded_shader_frag_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_frag_glsl_spv.begin) }; + } + + struct Vertex2 { + glm::vec2 pos; + glm::vec4 color; + glm::vec2 texture_coordinates; + DrawMode draw_mode; + + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoFragment() { + return VulkanExampleApplication::GetSharedShaderFragment(); + }; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoVertex() { + return { .codeSize = embeded_shader_second_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_second_vertex_glsl_spv.begin) }; + }; + static constexpr vk::VertexInputBindingDescription GetBindingDescription() { + constexpr vk::VertexInputBindingDescription binding_description { + .binding = 0, + .stride = sizeof(Vertex2), + .inputRate = vk::VertexInputRate::eVertex + }; + return binding_description; + } + 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, + .format = vk::Format::eR32G32Sfloat, + .offset = offsetof(Vertex2, pos) }, + vk::VertexInputAttributeDescription { + .location = 2, + .binding = 0, + .format = vk::Format::eR32G32B32A32Sfloat, + .offset = offsetof(Vertex2, color) }, + vk::VertexInputAttributeDescription { + .location = 4, + .binding = 0, + .format = vk::Format::eR32G32Sfloat, + .offset = offsetof(Vertex2, texture_coordinates) }, + vk::VertexInputAttributeDescription { + .location = 6, + .binding = 0, + .format = vk::Format::eR32Uint, + .offset = offsetof(Vertex2, draw_mode) } + }; + return attribute_descriptions; + } + struct IndexMap { + static inline const std::vector triangle = { 0, 1, 2 }; + static inline const std::vector rectangle = { 0, 1, 2, 2, 3, 0 }; + }; + }; + + struct Vertex3 { + glm::vec3 pos; + glm::vec4 color; + glm::vec2 texture_coordinates; + DrawMode draw_mode; + + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoFragment() { + return VulkanExampleApplication::GetSharedShaderFragment(); + }; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoVertex() { + return { .codeSize = embeded_shader_third_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_third_vertex_glsl_spv.begin) }; + } + static constexpr vk::VertexInputBindingDescription GetBindingDescription() { + constexpr vk::VertexInputBindingDescription binding_description { + .binding = 0, + .stride = sizeof(Vertex3), + .inputRate = vk::VertexInputRate::eVertex + }; + return binding_description; + } + 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, + .format = vk::Format::eR32G32B32Sfloat, + .offset = offsetof(Vertex3, pos) }, + vk::VertexInputAttributeDescription { + .location = 2, + .binding = 0, + .format = vk::Format::eR32G32B32A32Sfloat, + .offset = offsetof(Vertex2, color) }, + vk::VertexInputAttributeDescription { + .location = 4, + .binding = 0, + .format = vk::Format::eR32G32Sfloat, + .offset = offsetof(Vertex3, texture_coordinates) }, + vk::VertexInputAttributeDescription { + .location = 6, + .binding = 0, + .format = vk::Format::eR32Uint, + .offset = offsetof(Vertex2, draw_mode) } + + }; + return attribute_descriptions; + } + struct IndexMap { + static inline const std::vector rectangle = { + 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 + 4, 5, 6, 6, 7, 4 + }; + }; + }; + + template + class VulkanRenderPipeline { + private: + const VulkanExampleApplication* parent; + std::optional vk_shader_vertex; + std::optional vk_shader_frag; + + std::optional vk_pipeline_layout; + std::optional vk_pipeline; + + public: + constexpr VulkanRenderPipeline(const VulkanExampleApplication* _parent) + : parent(_parent) { assert(_parent != nullptr); } + + public: + std::optional vk_descriptor_set_layout; + std::optional vk_descriptor_pool; + std::vector vk_descriptor_sets; + + VulkanBuffer vk_buffer_vertex; // in order to get a clean destruction 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 + std::size_t vk_buffer_index_size = 0; + + private: + static constexpr bool UsesProjection() { + return decltype(VertexT().pos)::length() == 3; + } + // https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ + struct UniformBufferObject_Projection { // https://docs.vulkan.org/tutorial/latest/05_Uniform_buffers/01_Descriptor_pool_and_sets.html#_alignment_requirements + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 proj; + }; + std::vector vk_buffers_uniform; + std::vector vk_buffers_uniform_mapped; + + public: + using IndexT = std::uint16_t; + + void CreatePipeline(const vk::raii::Device& gpu, const vk::RenderPass& render_pass, const vk::SampleCountFlagBits& msaa_samples = vk::SampleCountFlagBits::e1) { + { // Load Shaders + this->vk_shader_vertex.emplace(gpu, VertexT::GetShaderInfoVertex()); + this->vk_shader_frag.emplace(gpu, VertexT::GetShaderInfoFragment()); + } + { + constexpr auto descriptor_set_layout_bindings = [&]() { + constexpr vk::DescriptorSetLayoutBinding descriptor_set_layout_binding_sampler { + .binding = 0, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .pImmutableSamplers = nullptr // Optional + }; + if constexpr (UsesProjection()) { + constexpr vk::DescriptorSetLayoutBinding descriptor_set_layout_binding_ubo { + .binding = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eVertex, + .pImmutableSamplers = nullptr // Optional + }; + return std::array { descriptor_set_layout_binding_sampler, descriptor_set_layout_binding_ubo }; + } else + return std::array { 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.emplace(gpu, descriptor_set_layout_create_info); + } + { // Init graphics pipeline/renderpass + const vk::PipelineShaderStageCreateInfo shader_stage_create_info_vertex { + .stage = vk::ShaderStageFlagBits::eVertex, + .module = **this->vk_shader_vertex, + .pName = "main" + }; + const vk::PipelineShaderStageCreateInfo shader_stage_create_info_frag { + .stage = vk::ShaderStageFlagBits::eFragment, + .module = **this->vk_shader_frag, + .pName = "main" + }; + const vk::PipelineShaderStageCreateInfo shader_stages_create_info[] = { shader_stage_create_info_vertex, shader_stage_create_info_frag }; + + constexpr auto vertex_binding_description = VertexT::GetBindingDescription(); + constexpr auto vertex_attribute_descriptions = VertexT::GetAttributeDescriptions(); + const vk::PipelineVertexInputStateCreateInfo vertex_input_create_info { + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &vertex_binding_description, // Optional + .vertexAttributeDescriptionCount = vertex_attribute_descriptions.size(), + .pVertexAttributeDescriptions = vertex_attribute_descriptions.data() // Optional + }; + + constexpr vk::PipelineInputAssemblyStateCreateInfo input_assembly_create_info { + .topology = vk::PrimitiveTopology::eTriangleList, + .primitiveRestartEnable = vk::False + }; + + constexpr vk::PipelineViewportStateCreateInfo viewport_state { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.html + .viewportCount = 1, + .scissorCount = 1 + }; + + constexpr vk::PipelineRasterizationStateCreateInfo rasterizer_create_info { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.html // these require GPU features... + .depthClampEnable = vk::False, + .rasterizerDiscardEnable = vk::False, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = UsesProjection() ? vk::FrontFace::eCounterClockwise : vk::FrontFace::eClockwise, + .depthBiasEnable = vk::False, + .depthBiasConstantFactor = 0.0f, // Optional + .depthBiasClamp = 0.0f, // Optional + .depthBiasSlopeFactor = 0.0f, // Optional + .lineWidth = 1.0f + }; + + const vk::PipelineMultisampleStateCreateInfo multisampling_create_info { + .rasterizationSamples = msaa_samples, + .sampleShadingEnable = vk::False, + .minSampleShading = 1.0f, // Optional + .pSampleMask = nullptr, // Optional + .alphaToCoverageEnable = vk::False, // Optional + .alphaToOneEnable = vk::False // Optional + }; + + constexpr vk::PipelineDepthStencilStateCreateInfo depth_stencil_create_info { + .depthTestEnable = vk::True, + .depthWriteEnable = vk::True, + .depthCompareOp = vk::CompareOp::eLess, + .depthBoundsTestEnable = vk::False, + .minDepthBounds = 0.0f, // Optional + .maxDepthBounds = 1.0f, // Optional + }; + + constexpr vk::PipelineColorBlendAttachmentState color_blend_attachment { + .blendEnable = vk::False, + .srcColorBlendFactor = vk::BlendFactor::eOne, // Optional + .dstColorBlendFactor = vk::BlendFactor::eZero, // Optional + .colorBlendOp = vk::BlendOp::eAdd, // Optional + .srcAlphaBlendFactor = vk::BlendFactor::eOne, // Optional + .dstAlphaBlendFactor = vk::BlendFactor::eZero, // Optional + .alphaBlendOp = vk::BlendOp::eAdd, // Optional + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA + }; + const vk::PipelineColorBlendStateCreateInfo color_blending_create_info { + .logicOpEnable = vk::False, + .logicOp = vk::LogicOp::eCopy, // Optional + .attachmentCount = 1, + .pAttachments = &color_blend_attachment, + .blendConstants = std::array { + 0.0f, + 0.0f, // Optional + 0.0f, // Optional + 0.0f // Optional + } // Optional + }; + + constexpr std::array dynamic_states = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor + }; + const vk::PipelineDynamicStateCreateInfo dynamic_state_create_info { + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data() + }; + + const vk::PipelineLayoutCreateInfo pipeline_layout_create_info { + .setLayoutCount = 1, // Optional + .pSetLayouts = &**this->vk_descriptor_set_layout, // Optional + //.pushConstantRangeCount = 0, // Optional + //.pPushConstantRanges = nullptr // Optional + }; + this->vk_pipeline_layout.emplace(gpu, pipeline_layout_create_info); + + const vk::GraphicsPipelineCreateInfo pipeline_create_info { + .stageCount = 2, + .pStages = shader_stages_create_info, + .pVertexInputState = &vertex_input_create_info, + .pInputAssemblyState = &input_assembly_create_info, + .pViewportState = &viewport_state, + .pRasterizationState = &rasterizer_create_info, + .pMultisampleState = &multisampling_create_info, + .pDepthStencilState = &depth_stencil_create_info, // Optional + .pColorBlendState = &color_blending_create_info, + .pDynamicState = &dynamic_state_create_info, + .layout = **this->vk_pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, // Optional + .basePipelineIndex = -1 // Optional + }; + this->vk_pipeline.emplace(gpu, nullptr, pipeline_create_info); + } + if constexpr (UsesProjection()) { // uniform buffer object + assert(this->vk_buffers_uniform.empty()); + assert(this->vk_buffers_uniform_mapped.empty()); + this->vk_buffers_uniform.reserve(this->parent->vk_max_frames_in_flight); + this->vk_buffers_uniform_mapped.reserve(this->parent->vk_max_frames_in_flight); + const vk::DeviceSize ubo_buffer_size = sizeof(UniformBufferObject_Projection); + for (std::size_t i = 0; i < this->parent->vk_max_frames_in_flight; i++) { + vma::AllocationInfo alloc_info; + auto uniform_buffer = this->parent->CreateBuffer(ubo_buffer_size, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, &alloc_info); + this->vk_buffers_uniform_mapped.emplace_back(static_cast(alloc_info.pMappedData)); + this->vk_buffers_uniform.emplace_back(std::move(uniform_buffer)); + } + } + } + void UpdateDescriptors(const vk::ImageView& texture_view) { + { // update descriptors + const vk::DescriptorPoolSize descriptor_pool_size_ubo { + .type = vk::DescriptorType::eUniformBuffer, + .descriptorCount = static_cast(this->parent->vk_max_frames_in_flight) + }; + const vk::DescriptorPoolSize descriptor_pool_size_sampler { + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = static_cast(this->parent->vk_max_frames_in_flight) + }; + const auto descriptor_pool_sizes = [&]() { + if constexpr (UsesProjection()) + return std::array { descriptor_pool_size_sampler, descriptor_pool_size_ubo }; + else + return std::array { descriptor_pool_size_sampler }; + }(); + const vk::DescriptorPoolCreateInfo descriptor_pool_create_info { + .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + .maxSets = static_cast(this->parent->vk_max_frames_in_flight), + .poolSizeCount = static_cast(descriptor_pool_sizes.size()), + .pPoolSizes = descriptor_pool_sizes.data(), + }; + + assert(descriptor_pool_create_info.flags & vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); // requirement, to soothe validation layer complaints + this->vk_descriptor_pool.emplace(*this->parent->vk_gpu, descriptor_pool_create_info); + + const std::vector descriptor_set_layouts = std::vector(this->parent->vk_max_frames_in_flight, *this->vk_descriptor_set_layout); + const vk::DescriptorSetAllocateInfo descriptor_set_allocate_info { + .descriptorPool = **this->vk_descriptor_pool, + .descriptorSetCount = static_cast(descriptor_set_layouts.size()), + .pSetLayouts = descriptor_set_layouts.data() + }; + this->vk_descriptor_sets = vk::raii::DescriptorSets(*this->parent->vk_gpu, descriptor_set_allocate_info); + + std::vector write_descriptor_sets; + write_descriptor_sets.reserve(this->parent->vk_max_frames_in_flight); + + std::vector image_infos; + image_infos.reserve(this->parent->vk_max_frames_in_flight); + for (std::size_t i = 0; i < this->parent->vk_max_frames_in_flight; i++) { + const vk::DescriptorImageInfo descriptor_image_info_texture { + .sampler = **this->parent->vk_texture_sampler, + .imageView = 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 = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &descriptor_image_info_handle // Optional + }; + write_descriptor_sets.emplace_back(descriptor_write_texture); + } + + std::vector buffer_infos; + if constexpr (UsesProjection()) { + buffer_infos.reserve(this->parent->vk_max_frames_in_flight); + for (std::size_t i = 0; i < this->parent->vk_max_frames_in_flight; i++) { + const vk::DescriptorBufferInfo descriptor_buffer_info_ubo { + .buffer = *this->vk_buffers_uniform.at(i)->first, + .offset = 0, + .range = sizeof(UniformBufferObject_Projection), + }; + 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 = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pImageInfo = nullptr, // Optional + .pBufferInfo = &descriptor_buffer_info_handle, + .pTexelBufferView = nullptr, // Optional + }; + write_descriptor_sets.emplace_back(descriptor_write_ubo); + } + } + + this->parent->vk_gpu->updateDescriptorSets(write_descriptor_sets, nullptr); + } + } + void ReplaceModelInfo(const std::vector& vertices, const std::vector& indexes) { + const auto vertex_buffer_size = sizeof(VertexT) * vertices.size(); + const auto vertex_buffer_staging = this->parent->CreateBuffer(vertex_buffer_size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); + + this->parent->vk_allocator->copyMemoryToAllocation(vertices.data(), *vertex_buffer_staging->second, 0, vertex_buffer_size); + + this->vk_buffer_vertex = this->parent->CreateBuffer(vertex_buffer_size, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal); + this->parent->CopyBuffer(*vertex_buffer_staging->first, *this->vk_buffer_vertex->first, vertex_buffer_size); + + // index + const auto index_array_size = indexes.size(); + const auto index_buffer_size = sizeof(IndexT) * index_array_size; + const auto index_buffer_staging = this->parent->CreateBuffer(index_buffer_size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); + + this->parent->vk_allocator->copyMemoryToAllocation(indexes.data(), *index_buffer_staging->second, 0, index_buffer_size); + + this->vk_buffer_index = this->parent->CreateBuffer(index_buffer_size, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal); + this->parent->CopyBuffer(*index_buffer_staging->first, *this->vk_buffer_index->first, index_buffer_size); + + this->vk_buffer_index_size = index_array_size; + } + void UpdateUniformBuffer(const std::uint32_t current_image) const { + if constexpr (decltype(VertexT().pos)::length() == 3) { + static auto start_time = std::chrono::high_resolution_clock::now(); + + const auto current_time = std::chrono::high_resolution_clock::now(); + const float time = std::chrono::duration(current_time - start_time).count(); + + UniformBufferObject_Projection ubo; // https://docs.vulkan.org/tutorial/latest/05_Uniform_buffers/00_Descriptor_set_layout_and_buffer.html#_updating_uniform_data + { + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), this->parent->vk_swapchain_extent.width / (float)this->parent->vk_swapchain_extent.height, 0.1f, 10.0f); + ubo.proj[1][1] *= -1; // "GLM was originally designed for OpenGL, where the Y coordinate of the clip coordinates is inverted." - so it will flip the image to correct. + } + memcpy(this->vk_buffers_uniform_mapped[current_image], &ubo, sizeof(ubo)); + } + } + void Bind(const vk::raii::CommandBuffer& command_buffer, const std::size_t frame_index) const { + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, **this->vk_pipeline); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *this->vk_pipeline_layout, 0, *this->vk_descriptor_sets[frame_index], nullptr); + command_buffer.bindVertexBuffers(0, { *this->vk_buffer_vertex->first }, { 0 }); + command_buffer.bindIndexBuffer(*this->vk_buffer_index->first, 0, vk::IndexType::eUint16); + } + void Draw(const vk::raii::CommandBuffer& command_buffer) const { + command_buffer.drawIndexed(static_cast(this->vk_buffer_index_size), 1, 0, 0, 0); + } + }; + + VulkanRenderPipeline vk_pipeline_second; + VulkanRenderPipeline vk_pipeline_third; + + static inline const std::vector vertices_rect_sample = { + { { -0.2f, -0.2f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.2f, -0.2f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.2f, 0.2f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.2f, 0.2f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured } + }; + + static inline const std::vector vertices_cube_sample = { + { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.5f, 0.5f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured }, + + { { -0.5f, -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured } + }; + +private: + vk::SampleCountFlagBits vk_msaa_samples_wanted = vk::SampleCountFlagBits::e8; + vk::SampleCountFlagBits vk_msaa_samples; + VulkanImage vk_color_image; + std::optional vk_color_view; + +private: + vk::Format vk_depth_format; + VulkanImage vk_depth_image; + std::optional vk_depth_view; + +private: + std::uint32_t vk_texture_mip_levels; + VulkanImage vk_texture_image; + std::optional vk_texture_view; + std::optional vk_texture_sampler; + +public: + VulkanExampleApplication() + : vk_pipeline_second(this) + , vk_pipeline_third(this) { + 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. + { // Window init + this->libsdl.emplace(SDL_INIT_VIDEO); + SDL_Vulkan_LoadLibrary(nullptr); // optional + this->window.emplace(this->app_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 480, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + } + { // Vulkan Init + this->vk_ctx.emplace(); + +#if defined(ENABLE_VULKAN_VALIDATION) + const std::vector available_layers = this->vk_ctx->enumerateInstanceLayerProperties(); + for (const auto& required_layer_name : required_vulkan_validation_layers) { + const auto find_wanted_layer = std::find_if(available_layers.begin(), available_layers.end(), [&](const auto& layer_to_test) { return std::string_view(layer_to_test.layerName) == required_layer_name; }); + if (find_wanted_layer == available_layers.end()) + throw std::runtime_error("validation layers requested, but not available!"); + } +#endif + } + { // Setup vulkan + constexpr vk::ApplicationInfo app_info { + .pApplicationName = this->app_name, + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "No Engine", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_3 + }; + + const std::vector required_extensions = [this]() -> std::vector { + std::uint32_t extension_count = 0; + SDL_Vulkan_GetInstanceExtensions(this->window->Get(), &extension_count, nullptr); + std::vector ret_extensions(extension_count); + SDL_Vulkan_GetInstanceExtensions(this->window->Get(), &extension_count, ret_extensions.data()); + + if (vulkan_enable_validation_layers) + ret_extensions.emplace_back(vk::EXTDebugUtilsExtensionName); + + return ret_extensions; + }(); + + const vk::InstanceCreateInfo instance_create_info { +#if defined(ENABLE_VULKAN_VALIDATION) + .pNext = &debug_message_info, +#endif + .pApplicationInfo = &app_info, + .enabledLayerCount = required_vulkan_validation_layers.size(), + .ppEnabledLayerNames = required_vulkan_validation_layers.data(), + .enabledExtensionCount = static_cast(required_extensions.size()), + .ppEnabledExtensionNames = required_extensions.data() + }; + this->vk_inst.emplace(*this->vk_ctx, instance_create_info); + +#if defined(ENABLE_VULKAN_VALIDATION) + this->vk_debug_utils_messenger.emplace(*this->vk_inst, debug_message_info); + + // SDL specific, optional (does it even work?) + /*constexpr vk::DebugReportCallbackCreateInfoEXT debug_callback_create_info = { + .flags = vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning, + .pfnCallback = VulkanReportCallback + }; + static const auto SDL2_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)SDL_Vulkan_GetVkGetInstanceProcAddr(); // https://gist.github.com/YukiSnowy/dc31f47448ac61dd6aedee18b5d53858 + assert(SDL2_vkCreateDebugReportCallbackEXT); + + VkDebugReportCallbackEXT debug_report_callback; + SDL2_vkCreateDebugReportCallbackEXT(**this->vk_inst, &static_cast(debug_callback_create_info), 0, &debug_report_callback); + this->vk_debug_report_callback = std::make_unique(*this->vk_inst, debug_report_callback);*/ +#endif + } + { // Pick device + auto physical_cards = vk::raii::PhysicalDevices(*this->vk_inst); + if (physical_cards.empty()) + throw std::runtime_error("failed to find GFX CARD with Vulkan support!"); + + int gfx_card_score = 0; + std::optional found_search_info; + decltype(physical_cards.begin()) found_physical_card = physical_cards.end(); + for (auto i = physical_cards.begin(); i != physical_cards.end(); i++) { + const auto& gfx_card = *i; + int cur_score = 0; + + const auto device_properties = gfx_card.getProperties(); + const auto device_features = gfx_card.getFeatures(); + const auto queue_families = gfx_card.getQueueFamilyProperties(); + const bool is_swrast = device_properties.deviceType == vk::PhysicalDeviceType::eCpu; + const bool is_gpurast = device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu; + if (!(((allow_swrast && is_swrast) || is_gpurast) && device_features.geometryShader)) + continue; + if (is_swrast) + cur_score += 5; + if (is_gpurast) + cur_score += 15; + + const auto found_graphics_family = std::find_if(queue_families.begin(), queue_families.end(), [](const auto& queue_family_to_test) { + return queue_family_to_test.queueFlags & vk::QueueFlagBits::eGraphics; + }); + if (found_graphics_family == queue_families.end()) + continue; + + VulkanDeviceQueriedInfo gfx_card_info; + const auto ReturnScore = [&]() { + if (gfx_card_info.IsGoodCard()) { + if (cur_score > gfx_card_score) { + gfx_card_score = cur_score; + found_search_info = std::move(gfx_card_info); + found_physical_card = i; + } + } + }; + + const auto found_graphics_family_idx = std::distance(queue_families.begin(), found_graphics_family); + gfx_card_info.graphics_family = found_graphics_family_idx; + + auto found_screen_surface = [](const SDL2pp::Window& window, const vk::raii::Instance& vk_inst) -> vk::raii::SurfaceKHR { + VkSurfaceKHR _screen_surface; + SDL_Vulkan_CreateSurface(window.Get(), static_cast(static_cast(vk_inst)), &_screen_surface); + return vk::raii::SurfaceKHR(vk_inst, _screen_surface); + }(*this->window, *this->vk_inst); + + const auto found_present_family = gfx_card.getSurfaceSupportKHR(found_graphics_family_idx, *found_screen_surface) ? found_graphics_family : [&]() -> decltype(queue_families)::const_iterator { // https://github.com/KhronosGroup/Vulkan-Hpp/blob/6f72ceca515d59f40d64b64cf2734f6261e1f9f2/RAII_Samples/05_InitSwapchain/05_InitSwapchain.cpp#L50 + decltype(queue_families)::const_iterator queue_family_to_test; + for (queue_family_to_test = queue_families.begin(); queue_family_to_test != queue_families.end(); queue_family_to_test++) { + const auto queue_family_idx_to_test = std::distance(queue_families.begin(), queue_family_to_test); + if ((queue_family_to_test->queueFlags & vk::QueueFlagBits::eGraphics) && gfx_card.getSurfaceSupportKHR(queue_family_idx_to_test, *found_screen_surface)) + break; + } + if (queue_family_to_test == queue_families.end()) { + for (queue_family_to_test = queue_families.begin(); queue_family_to_test != queue_families.end(); queue_family_to_test++) { + const auto queue_family_idx_to_test = std::distance(queue_families.begin(), queue_family_to_test); + if (gfx_card.getSurfaceSupportKHR(queue_family_idx_to_test, *found_screen_surface)) + break; + } + } + return queue_family_to_test; + }(); + if (found_present_family == queue_families.end()) { + ReturnScore(); + continue; + } + + const auto found_present_family_idx = std::distance(queue_families.begin(), found_present_family); + gfx_card_info.present_family = found_present_family_idx; + cur_score += 5; + + const auto available_extensions = gfx_card.enumerateDeviceExtensionProperties(); + const auto DeviceSupportsExtension = [&available_extensions](const std::string_view required_ext_name) -> bool { + return available_extensions.end() != std::find_if(available_extensions.begin(), available_extensions.end(), [&](const auto& ext_to_test) { return std::string_view(ext_to_test.extensionName) == required_ext_name; }); + }; + { + bool has_all_required_extensions = true; + for (const std::string_view ext_name_required : required_vulkan_device_extensions) { + if (!DeviceSupportsExtension(ext_name_required)) { + has_all_required_extensions = false; + } + } + if (!has_all_required_extensions) { + ReturnScore(); + continue; + } + cur_score += 2; + } + + const auto vma_feature_sets = std::array, vma::AllocatorCreateFlagBits>, 5> { + std::make_pair(std::vector { "VK_KHR_dedicated_allocation", "VK_KHR_get_memory_requirements2" }, vma::AllocatorCreateFlagBits::eKhrDedicatedAllocation), + { { "VK_KHR_bind_memory2" }, vma::AllocatorCreateFlagBits::eKhrBindMemory2 }, + { { "VK_KHR_maintenance4" }, vma::AllocatorCreateFlagBits::eKhrMaintenance4 }, + { { "VK_KHR_maintenance5" }, vma::AllocatorCreateFlagBits::eKhrMaintenance5 }, + { { "VK_EXT_memory_budget", "VK_KHR_get_physical_device_properties2" }, vma::AllocatorCreateFlagBits::eExtMemoryBudget } + }; + for (const auto& wanted_vma_feature_set : vma_feature_sets) { + bool has_all_required_features = true; + for (const auto& required_device_feature : wanted_vma_feature_set.first) + if (!DeviceSupportsExtension(required_device_feature)) + has_all_required_features = false; + if (has_all_required_features) { + for (const auto& required_device_feature : wanted_vma_feature_set.first) + gfx_card_info.supported_features_vma_device.emplace_back(required_device_feature); + gfx_card_info.supported_features_vma_allocator.emplace_back(wanted_vma_feature_set.second); + cur_score += 2; + } + } + + if (!device_features.samplerAnisotropy) { + ReturnScore(); + continue; + } + cur_score += 1; + + const auto surface_present_modes = gfx_card.getSurfacePresentModesKHR(*found_screen_surface); + const auto surface_formats = gfx_card.getSurfaceFormatsKHR(*found_screen_surface); + if (surface_formats.empty() || surface_present_modes.empty()) { + ReturnScore(); + continue; + } + cur_score += 5; + + const auto GetMaxUseableSampleCount = [&device_properties]() -> vk::SampleCountFlagBits { + vk::SampleCountFlags counts = device_properties.limits.framebufferColorSampleCounts & device_properties.limits.framebufferDepthSampleCounts; + if (counts & vk::SampleCountFlagBits::e64) + return vk::SampleCountFlagBits::e64; + if (counts & vk::SampleCountFlagBits::e32) + return vk::SampleCountFlagBits::e32; + if (counts & vk::SampleCountFlagBits::e16) + return vk::SampleCountFlagBits::e16; + if (counts & vk::SampleCountFlagBits::e8) + return vk::SampleCountFlagBits::e8; + if (counts & vk::SampleCountFlagBits::e4) + return vk::SampleCountFlagBits::e4; + if (counts & vk::SampleCountFlagBits::e2) + return vk::SampleCountFlagBits::e2; + return vk::SampleCountFlagBits::e1; + }; + + gfx_card_info.screen_surface.emplace(std::move(found_screen_surface)); + gfx_card_info.surface_has_format_modes = !surface_formats.empty(); + gfx_card_info.surface_has_present_modes = !surface_present_modes.empty(); + gfx_card_info.msaa_samples = GetMaxUseableSampleCount(); + ReturnScore(); + } + + if (found_physical_card == physical_cards.end() || !found_search_info.has_value() || !found_search_info->IsGoodCard()) + throw std::runtime_error("Unable to choose a Suitable GRAPHICS DEVICE from the ones availiable!"); + + this->vk_gfx_card.emplace(std::move(*found_physical_card)); + this->vk_screen_surface.emplace(std::move(*found_search_info->screen_surface)); + this->vk_physical_card_info = std::move(*found_search_info); + } + { // Setup device + static constexpr float queue_priority = 1.0f; + const std::vector device_queue_create_infos = [](const std::uint32_t graphics_family, const std::uint32_t present_family) { + std::vector device_queues_we_need; + device_queues_we_need.reserve(2); + const auto GetQueuesToUse = [&graphics_family, &present_family]() { + std::vector wanted_queues = { graphics_family }; + wanted_queues.reserve(2); + if (graphics_family != present_family) + wanted_queues.emplace_back(present_family); + return wanted_queues; + }; + for (const std::uint32_t queue_family : GetQueuesToUse()) { + const vk::DeviceQueueCreateInfo device_queue_create_info { + .queueFamilyIndex = queue_family, + .queueCount = 1, + .pQueuePriorities = &queue_priority + }; + device_queues_we_need.emplace_back(device_queue_create_info); + } + return device_queues_we_need; + }(this->vk_physical_card_info.graphics_family.value(), this->vk_physical_card_info.present_family.value()); + + const std::vector enabled_extensions_device = [this]() -> std::vector { + std::vector enabled_extensions(required_vulkan_device_extensions.begin(), required_vulkan_device_extensions.end()); + + for (const auto& i : this->vk_physical_card_info.supported_features_vma_device) + enabled_extensions.emplace_back(i.data()); + + return enabled_extensions; + }(); + + 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(), + .enabledExtensionCount = static_cast(enabled_extensions_device.size()), + .ppEnabledExtensionNames = enabled_extensions_device.data(), + .pEnabledFeatures = &device_features, + }; + this->vk_gpu.emplace(*this->vk_gfx_card, device_create_info); + + this->vk_queue_graphics.emplace(*this->vk_gpu, this->vk_physical_card_info.graphics_family.value(), 0); + this->vk_queue_present.emplace(*this->vk_gpu, this->vk_physical_card_info.present_family.value(), 0); + } + { // initialize the vulkan memory allocator + // TODO: enable extensions... https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/quick_start.html#quick_start_initialization + const auto dispatcher_inst = this->vk_inst->getDispatcher(); + const auto dispatcher_gpu = this->vk_gpu->getDispatcher(); + const vma::VulkanFunctions vulkan_functions { + .vkGetInstanceProcAddr = dispatcher_inst->vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = dispatcher_gpu->vkGetDeviceProcAddr, + .vkGetPhysicalDeviceProperties = dispatcher_inst->vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = dispatcher_inst->vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = dispatcher_gpu->vkAllocateMemory, + .vkFreeMemory = dispatcher_gpu->vkFreeMemory, + .vkMapMemory = dispatcher_gpu->vkMapMemory, + .vkUnmapMemory = dispatcher_gpu->vkUnmapMemory, + .vkFlushMappedMemoryRanges = dispatcher_gpu->vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = dispatcher_gpu->vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = dispatcher_gpu->vkBindBufferMemory, + .vkBindImageMemory = dispatcher_gpu->vkBindImageMemory, + .vkGetBufferMemoryRequirements = dispatcher_gpu->vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = dispatcher_gpu->vkGetImageMemoryRequirements, + .vkCreateBuffer = dispatcher_gpu->vkCreateBuffer, + .vkDestroyBuffer = dispatcher_gpu->vkDestroyBuffer, + .vkCreateImage = dispatcher_gpu->vkCreateImage, + .vkDestroyImage = dispatcher_gpu->vkDestroyImage, + .vkCmdCopyBuffer = dispatcher_gpu->vkCmdCopyBuffer, + .vkGetBufferMemoryRequirements2KHR = dispatcher_gpu->vkGetBufferMemoryRequirements2KHR, + }; + const vma::AllocatorCreateInfo allocator_create_info = { + .flags = [&]() -> vma::AllocatorCreateFlags { + vma::AllocatorCreateFlags enabled_flags; + for (const auto& wanted_feature : this->vk_physical_card_info.supported_features_vma_allocator) + enabled_flags |= wanted_feature; + return enabled_flags; + }(), + .physicalDevice = **this->vk_gfx_card, + .device = **this->vk_gpu, + .pVulkanFunctions = &vulkan_functions, + .instance = **this->vk_inst + }; + this->vk_allocator = vma::createAllocatorUnique(allocator_create_info); + } + { + this->CreateSwapchain(); + } + { + this->CreateBufferColor(); + } + { // find depth format, needed before a few things + this->CreateBufferDepth(); + } + { // init renderpass + const vk::AttachmentDescription attachment_description_color { + .format = this->vk_swapchain_image_format, + .samples = this->vk_msaa_samples, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, + .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, + .initialLayout = vk::ImageLayout::eUndefined, + .finalLayout = vk::ImageLayout::eColorAttachmentOptimal + }; + const vk::AttachmentDescription attachment_description_color_resolve { + .format = this->vk_swapchain_image_format, + .samples = vk::SampleCountFlagBits::e1, + .loadOp = vk::AttachmentLoadOp::eDontCare, + .storeOp = vk::AttachmentStoreOp::eStore, + .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, + .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, + .initialLayout = vk::ImageLayout::eUndefined, + .finalLayout = vk::ImageLayout::ePresentSrcKHR + }; + const vk::AttachmentDescription attachment_description_depth { + .format = this->vk_depth_format, + .samples = this->vk_msaa_samples, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eDontCare, + .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, + .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, + .initialLayout = vk::ImageLayout::eUndefined, + .finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal + }; + constexpr vk::AttachmentReference attachment_ref_color { + .attachment = 0, + .layout = vk::ImageLayout::eColorAttachmentOptimal, + }; + constexpr vk::AttachmentReference attachment_ref_color_resolve { + .attachment = 1, + .layout = vk::ImageLayout::eColorAttachmentOptimal + }; + constexpr vk::AttachmentReference attachment_ref_depth { + .attachment = 2, + .layout = vk::ImageLayout::eDepthStencilAttachmentOptimal + }; + + const vk::SubpassDescription subpass_description { + .pipelineBindPoint = vk::PipelineBindPoint::eGraphics, + .colorAttachmentCount = 1, + .pColorAttachments = &attachment_ref_color, + .pResolveAttachments = &attachment_ref_color_resolve, + .pDepthStencilAttachment = &attachment_ref_depth + }; + constexpr vk::SubpassDependency subpass_dependency { + .srcSubpass = vk::SubpassExternal, + .dstSubpass = 0, + .srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eLateFragmentTests, + .dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests, + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite + }; + const std::array attachment_descriptions = { attachment_description_color, attachment_description_color_resolve, attachment_description_depth }; + const vk::RenderPassCreateInfo render_pass_create_info { + .attachmentCount = static_cast(attachment_descriptions.size()), + .pAttachments = attachment_descriptions.data(), + .subpassCount = 1, + .pSubpasses = &subpass_description, + .dependencyCount = 1, + .pDependencies = &subpass_dependency + }; + this->vk_render_pass.emplace(*this->vk_gpu, render_pass_create_info); + } + { + this->vk_pipeline_second.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, this->vk_msaa_samples); + this->vk_pipeline_third.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, this->vk_msaa_samples); + } + { + this->CreateBufferFrame(); + } + { // command pool setup + const vk::CommandPoolCreateInfo command_pool_create_info { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.html + .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, + .queueFamilyIndex = this->vk_physical_card_info.graphics_family.value() + }; + this->vk_command_pool.emplace(*this->vk_gpu, command_pool_create_info); + + const vk::CommandBufferAllocateInfo command_buffer_alloc_info { + .commandPool = **this->vk_command_pool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = static_cast(this->vk_max_frames_in_flight) + }; + this->vk_command_buffers = vk::raii::CommandBuffers(*this->vk_gpu, command_buffer_alloc_info); + } + { + this->TransitionImageLayout(*this->vk_depth_image->first, this->vk_depth_format, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal); + } + { // fill sample vertex data + this->vk_pipeline_second.ReplaceModelInfo(vertices_rect_sample, Vertex2::IndexMap::rectangle); + this->vk_pipeline_third.ReplaceModelInfo(vertices_cube_sample, Vertex3::IndexMap::rectangle); + } + { // syncronizing vars + assert(this->vk_semephores_image_available.empty()); + assert(this->vk_semephores_render_finished.empty()); + assert(this->vk_fences_in_flight.empty()); + this->vk_semephores_image_available.reserve(this->vk_max_frames_in_flight); + this->vk_semephores_render_finished.reserve(this->vk_max_frames_in_flight); + this->vk_fences_in_flight.reserve(this->vk_max_frames_in_flight); + for (size_t i = 0; i < this->vk_max_frames_in_flight; i++) { + this->vk_semephores_image_available.emplace_back(*this->vk_gpu, vk::SemaphoreCreateInfo {}); + this->vk_semephores_render_finished.emplace_back(*this->vk_gpu, vk::SemaphoreCreateInfo {}); + this->vk_fences_in_flight.emplace_back(*this->vk_gpu, vk::FenceCreateInfo { .flags = vk::FenceCreateFlagBits::eSignaled }); + } + } + + { // texture + const auto texture_format = vk::Format::eR8G8B8A8Srgb; + 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); + + this->vk_texture_mip_levels = static_cast(std::floor(std::log2(std::max(texture_buffer_extent.width, texture_buffer_extent.height)))) + 1; + + this->vk_allocator->copyMemoryToAllocation(embeded_debug_north_png_rgba.data.begin, *texture_buffer_staging->second, 0, texture_buffer_size); + + this->vk_texture_image = CreateImage(texture_buffer_extent, texture_format, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, this->vk_texture_mip_levels, vk::SampleCountFlagBits::e1); + + this->SingleTimeSubmitCommand([&](const auto& command_buffer) { + this->TransitionImageLayout(command_buffer, *this->vk_texture_image->first, texture_format, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, this->vk_texture_mip_levels); + this->CopyBuffer(command_buffer, *texture_buffer_staging->first, *this->vk_texture_image->first, texture_buffer_extent); + const auto GenerateMipmaps = [this](const vk::CommandBuffer& command_buffer, const vk::Image& src_image, vk::Format src_format, vk::Extent2D texture_size, std::uint32_t mip_levels) { + assert(mip_levels); + + if (!(this->vk_gfx_card->getFormatProperties(src_format).optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear)) + throw std::runtime_error("texture image format does not support linear blitting!"); + + vk::ImageMemoryBarrier image_memory_barrier { + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .image = src_image, + .subresourceRange { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + for (std::uint32_t i = 1; i < mip_levels; i++) { + + image_memory_barrier.subresourceRange.baseMipLevel = i - 1; + image_memory_barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite, + image_memory_barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead, + image_memory_barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; + image_memory_barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal, + command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, nullptr, nullptr, image_memory_barrier); + + const std::array image_offsets_src { + vk::Offset3D { 0, 0, 0 }, + vk::Offset3D { static_cast(texture_size.width), static_cast(texture_size.height), 1 } + }; + const vk::ImageSubresourceLayers image_subresource_layers_src { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = i - 1, + .baseArrayLayer = 0, + .layerCount = 1 + }; + const std::array image_offsets_dest { + vk::Offset3D { 0, 0, 0 }, + vk::Offset3D { static_cast(texture_size.width) > 1 ? static_cast(texture_size.width) / 2 : 1, static_cast(texture_size.height) > 1 ? static_cast(texture_size.height) / 2 : 1, 1 } + }; + const vk::ImageSubresourceLayers image_subresource_layers_dest { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = i, + .baseArrayLayer = 0, + .layerCount = 1 + }; + + const vk::ImageBlit image_blit { + .srcSubresource = image_subresource_layers_src, + .srcOffsets = image_offsets_src, + .dstSubresource = image_subresource_layers_dest, + .dstOffsets = image_offsets_dest + }; + command_buffer.blitImage(src_image, vk::ImageLayout::eTransferSrcOptimal, src_image, vk::ImageLayout::eTransferDstOptimal, image_blit, vk::Filter::eLinear); + + if (texture_size.width > 1) + texture_size.width /= 2; + if (texture_size.height > 1) + texture_size.height /= 2; + + image_memory_barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; + image_memory_barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + image_memory_barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal; + image_memory_barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, image_memory_barrier); + } + image_memory_barrier.subresourceRange.baseMipLevel = mip_levels - 1; + image_memory_barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + image_memory_barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + image_memory_barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; + image_memory_barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, image_memory_barrier); + }; + GenerateMipmaps(command_buffer, *this->vk_texture_image->first, texture_format, texture_buffer_extent, this->vk_texture_mip_levels); + }); + + this->vk_texture_view = this->CreateImageView(*this->vk_texture_image->first, texture_format, vk::ImageAspectFlagBits::eColor, this->vk_texture_mip_levels); + } + { // Sampler + const vk::SamplerCreateInfo sampler_create_info { + .magFilter = vk::Filter::eNearest, + .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 = vk::LodClampNone, + .borderColor = vk::BorderColor::eIntOpaqueBlack, + .unnormalizedCoordinates = vk::False, + }; + this->vk_texture_sampler.emplace(*this->vk_gpu, sampler_create_info); + } + { + this->vk_pipeline_second.UpdateDescriptors(*this->vk_texture_view); + this->vk_pipeline_third.UpdateDescriptors(*this->vk_texture_view); + } + } catch (const vk::SystemError& error) { + std::cout << "Received Vulkan-Specific Error during process init: " << error.what() << std::endl + << "Going to rethrow!" << std::endl; + throw error; + } catch (const std::exception& error) { + std::cout << "Received Error during process init: " << error.what() << std::endl + << "Going to rethrow!" << std::endl; + throw error; + } catch (...) { + std::cout << "Received Unknown Error during process init..." << std::endl; + throw "Unknown Exception, throwing..."; + } + } + +private: + void RecreateSwapchain() { + this->vk_gpu->waitIdle(); + this->CreateSwapchain(); + this->CreateBufferColor(); + this->CreateBufferDepth(); + this->CreateBufferFrame(); + } + void CreateSwapchain() { + assert(this->vk_gfx_card); + assert(this->vk_screen_surface); + assert(this->vk_gpu); + + const vk::SurfaceFormatKHR surface_format = [](const std::vector& available_formats) -> vk::SurfaceFormatKHR { + vk::SurfaceFormatKHR chosen_format = available_formats.at(0); + if (available_formats.size() == 1) { + if (available_formats[0].format == vk::Format::eUndefined) { + chosen_format.format = required_vulkan_default_format; + chosen_format.colorSpace = required_vulkan_default_screen_colorspace; + } + } else { + for (const auto& format_we_want : required_vulkan_screen_formats) { + const auto find = std::find_if(available_formats.begin(), available_formats.end(), [&](const auto& format_to_test) { return format_to_test.format == format_we_want && format_to_test.colorSpace == required_vulkan_screen_colorspace; }); + if (find != available_formats.end()) { + chosen_format = *find; + break; + } + } + } + return chosen_format; + }(this->vk_gfx_card->getSurfaceFormatsKHR(*this->vk_screen_surface)); + + const vk::PresentModeKHR present_mode = [](const std::vector& available_present_modes) -> vk::PresentModeKHR { + for (const auto& present_mode_to_test : available_present_modes) + if (present_mode_to_test == vk::PresentModeKHR::eMailbox) + return present_mode_to_test; + + return vk::PresentModeKHR::eFifo; + }(this->vk_gfx_card->getSurfacePresentModesKHR(*this->vk_screen_surface)); + + const auto surface_capabilities = this->vk_gfx_card->getSurfaceCapabilitiesKHR(*this->vk_screen_surface); + const vk::Extent2D extent = [this](const vk::SurfaceCapabilitiesKHR& capabilities) -> vk::Extent2D { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int sdl_width = 0, sdl_height = 0; + SDL_Vulkan_GetDrawableSize(this->window->Get(), &sdl_width, &sdl_height); // using equivalent of glfwGetFramebufferSize(window, &width, &height); // https://wiki.libsdl.org/SDL2/SDL_Vulkan_GetDrawableSize + return { + std::clamp(static_cast(sdl_width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width), + std::clamp(static_cast(sdl_height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height) + }; + } + }(surface_capabilities); + + const std::uint32_t image_count = [&surface_capabilities]() { + std::uint32_t image_count = surface_capabilities.minImageCount + 1; + if (surface_capabilities.maxImageCount > 0 && image_count > surface_capabilities.maxImageCount) + image_count = surface_capabilities.maxImageCount; + return image_count; + }(); + + const auto swapchain_create_info = [&]() -> vk::SwapchainCreateInfoKHR { + vk::SwapchainCreateInfoKHR swapchain_create_info { + .surface = **this->vk_screen_surface, + .minImageCount = image_count, + .imageFormat = surface_format.format, + .imageColorSpace = surface_format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, + .preTransform = surface_capabilities.currentTransform, + .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, // for window manager, we dont need transparency with windows... + .presentMode = present_mode, + .clipped = vk::True, + .oldSwapchain = this->vk_swapchain ? **this->vk_swapchain : nullptr + }; + + const std::array queue_family_indices = { this->vk_physical_card_info.graphics_family.value(), this->vk_physical_card_info.present_family.value() }; + if ((this->vk_physical_card_info.graphics_family.has_value() && this->vk_physical_card_info.present_family.has_value()) && this->vk_physical_card_info.graphics_family.value() != this->vk_physical_card_info.present_family.value()) { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.html#_creating_the_swap_chain + swapchain_create_info.imageSharingMode = vk::SharingMode::eConcurrent; + swapchain_create_info.queueFamilyIndexCount = queue_family_indices.size(); + swapchain_create_info.pQueueFamilyIndices = queue_family_indices.data(); + } else { + swapchain_create_info.imageSharingMode = vk::SharingMode::eExclusive; + swapchain_create_info.queueFamilyIndexCount = 0; // Optional + swapchain_create_info.pQueueFamilyIndices = nullptr; // Optional + } + return swapchain_create_info; + }(); + auto new_swapchain = vk::raii::SwapchainKHR(*this->vk_gpu, swapchain_create_info); + this->vk_swapchain.emplace(std::move(new_swapchain)); + + auto swapchain_images = this->vk_swapchain->getImages(); + this->vk_swapchain_image_format = surface_format.format; + this->vk_swapchain_extent = extent; + + this->vk_swapchain_image_views.clear(); + this->vk_swapchain_image_views.reserve(swapchain_images.size()); + for (const auto& image : swapchain_images) // requires images + this->vk_swapchain_image_views.emplace_back(this->CreateImageView(image, this->vk_swapchain_image_format, vk::ImageAspectFlagBits::eColor)); + } + void CreateBufferColor() { + assert(this->vk_swapchain); + const auto ClampToMaxSupportedMSAA = [this](vk::SampleCountFlagBits sample) -> vk::SampleCountFlagBits { + const auto max_clamp = this->vk_physical_card_info.msaa_samples; + if (sample > max_clamp) + sample = *max_clamp; + return sample; + }; + this->vk_msaa_samples = ClampToMaxSupportedMSAA(this->vk_msaa_samples_wanted); + + this->vk_color_image = this->CreateImage(this->vk_swapchain_extent, this->vk_swapchain_image_format, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransientAttachment | vk::ImageUsageFlagBits::eColorAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, 1, this->vk_msaa_samples); + this->vk_color_view = this->CreateImageView(*this->vk_color_image->first, this->vk_swapchain_image_format, vk::ImageAspectFlagBits::eColor); + } + void CreateBufferDepth() { + /*const auto HasStencilComponent = [](vk::Format format) { + return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint; + };*/ + const auto FindSupportedFormat = [&](const std::vector& wanted_candidates, vk::ImageTiling wanted_tiling, vk::FormatFeatureFlags wanted_features) -> vk::Format { + for (const auto& format : wanted_candidates) { + const vk::FormatProperties props = this->vk_gfx_card->getFormatProperties(format); + if (wanted_tiling == vk::ImageTiling::eLinear && (props.linearTilingFeatures & wanted_features) == wanted_features) + return format; + else if (wanted_tiling == vk::ImageTiling::eOptimal && (props.optimalTilingFeatures & wanted_features) == wanted_features) + return format; + } + throw std::runtime_error("failed to find supported format!"); + }; + this->vk_depth_format = FindSupportedFormat({ vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint }, vk::ImageTiling::eOptimal, vk::FormatFeatureFlagBits::eDepthStencilAttachment); + + this->vk_depth_image = this->CreateImage(this->vk_swapchain_extent, this->vk_depth_format, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, 1, this->vk_msaa_samples); + this->vk_depth_view.emplace(this->CreateImageView(*this->vk_depth_image->first, this->vk_depth_format, vk::ImageAspectFlagBits::eDepth)); + } + void CreateBufferFrame() { + assert(this->vk_depth_image); + assert(this->vk_render_pass); + this->vk_swapchain_framebuffers.clear(); + this->vk_swapchain_framebuffers.reserve(this->vk_swapchain_image_views.size()); + for (const auto& i : this->vk_swapchain_image_views) { + const std::array attachments = { **this->vk_color_view, *i, **this->vk_depth_view }; + const vk::FramebufferCreateInfo framebuffer_create_info { + .renderPass = **this->vk_render_pass, + .attachmentCount = attachments.size(), + .pAttachments = attachments.data(), + .width = this->vk_swapchain_extent.width, + .height = this->vk_swapchain_extent.height, + .layers = 1 + }; + this->vk_swapchain_framebuffers.emplace_back(*this->vk_gpu, framebuffer_create_info); + } + } + +protected: + vma::AllocationCreateFlags GetAllocationFlags(const vk::BufferUsageFlags& wanted_usage, const vk::MemoryPropertyFlags& wanted_properties) const { + vma::AllocationCreateFlags ret; + if (wanted_properties & vk::MemoryPropertyFlagBits::eHostVisible) + ret |= vma::AllocationCreateFlagBits::eHostAccessSequentialWrite; + else + ret |= vma::AllocationCreateFlagBits::eDedicatedMemory; + if (wanted_usage & vk::BufferUsageFlagBits::eUniformBuffer) + ret |= vma::AllocationCreateFlagBits::eMapped; + return ret; + } + VulkanBuffer CreateBuffer(const vk::DeviceSize& wanted_size, const vk::BufferUsageFlags& wanted_usage, const vk::MemoryPropertyFlags& wanted_properties, vma::AllocationInfo* allocation_info_return = nullptr) const { + const vk::BufferCreateInfo buffer_create_info { + .size = wanted_size, + .usage = wanted_usage, + .sharingMode = vk::SharingMode::eExclusive + }; + const vma::AllocationCreateInfo allocation_create_info = { + .flags = this->GetAllocationFlags(wanted_usage, wanted_properties), + .usage = vma::MemoryUsage::eAuto, + .requiredFlags = wanted_properties, + }; + return (allocation_info_return != nullptr) ? this->vk_allocator->createBufferUnique(buffer_create_info, allocation_create_info, allocation_info_return) : this->vk_allocator->createBufferUnique(buffer_create_info, allocation_create_info); + }; + VulkanImage CreateImage(const vk::Extent2D& image_size, const vk::Format& wanted_format, const vk::ImageTiling& wanted_tiling, const vk::ImageUsageFlags& wanted_usage, const vk::MemoryPropertyFlags& wanted_properties, const std::uint32_t mip_levels = 1, const vk::SampleCountFlagBits& msaa_samples = vk::SampleCountFlagBits::e1) 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 = mip_levels, + .arrayLayers = 1, + .samples = msaa_samples, + .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 vma::AllocationCreateInfo allocation_create_info = { + .usage = vma::MemoryUsage::eAuto, + .requiredFlags = wanted_properties + }; + return this->vk_allocator->createImageUnique(image_create_info, allocation_create_info); + } + vk::raii::ImageView CreateImageView(const vk::Image& image_src, const vk::Format& wanted_format, const vk::ImageAspectFlags& flag_mask, const std::uint32_t mip_levels = 1) const { + const vk::ImageViewCreateInfo imageview_create_info { + .image = image_src, + .viewType = vk::ImageViewType::e2D, + .format = wanted_format, + .components = { + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eIdentity }, + .subresourceRange = { .aspectMask = flag_mask, .baseMipLevel = 0, .levelCount = mip_levels, .baseArrayLayer = 0, .layerCount = 1 }, + }; + return vk::raii::ImageView(*this->vk_gpu, imageview_create_info); + } + 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 { + .commandBufferCount = 1, + .pCommandBuffers = &*command_buffer, + }; + this->vk_queue_graphics->submit(submit_queue); + this->vk_queue_graphics->waitIdle(); + } + void TransitionImageLayout(const vk::Image& image, vk::Format format, const vk::ImageLayout& old_layout, const vk::ImageLayout& new_layout) const { + this->SingleTimeSubmitCommand([&](const auto& command_buffer) { + this->TransitionImageLayout(command_buffer, image, format, old_layout, new_layout); + }); + } + 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 std::uint32_t mip_levels = 1) 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::ImageAspectFlags aspect_mask = [&format](const vk::ImageLayout& new_layout) -> vk::ImageAspectFlags { + vk::ImageAspectFlags aspect_mask; + if (new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) { + aspect_mask = vk::ImageAspectFlagBits::eDepth; + if (format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint) { + aspect_mask |= vk::ImageAspectFlagBits::eStencil; + } + } else + aspect_mask = vk::ImageAspectFlagBits::eColor; + return aspect_mask; + }(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 = aspect_mask, + .baseMipLevel = 0, + .levelCount = mip_levels, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + command_buffer.pipelineBarrier(source_stage, destination_stage, {}, nullptr, nullptr, image_memory_barrier); + } + +public: + void Run() { + bool running = true; + while (running) { + SDL_Event window_event; + while (SDL_PollEvent(&window_event)) { + if (window_event.type == SDL_QUIT) { + running = false; + break; + } + } + this->DrawFrame(); + } + } + void RunTests() { + constexpr auto amount_of_frames_needed = 15000; + std::cout << "Starting Render Tests with: " << std::to_string(amount_of_frames_needed) << " frames!" << std::endl; + std::uintptr_t posted_about = 0; + for (std::uintptr_t drawn_frames = 0; drawn_frames < amount_of_frames_needed; drawn_frames++) { + this->DrawFrame(); + const auto amount_of_thousands = drawn_frames / 1000; + if (amount_of_thousands > posted_about) { + std::cout << "DrawFrame Count: " << std::to_string(drawn_frames) << std::endl; + posted_about = amount_of_thousands; + } + } + std::cout << "Completed tests with: " << std::to_string(amount_of_frames_needed) << " frames!" << std::endl; + } + + void DrawFrame() { + const auto current_frame_index = this->vk_current_frame_index; + + const std::array wait_fences = { *this->vk_fences_in_flight[current_frame_index] }; + const vk::Result fence_result = this->vk_gpu->waitForFences(wait_fences, vk::True, std::numeric_limits::max()); + if (fence_result != vk::Result::eSuccess) + throw std::runtime_error("failed to wait for frame fence!"); + + const auto next_image = this->vk_swapchain->acquireNextImage(std::numeric_limits::max(), *this->vk_semephores_image_available[current_frame_index]); + if (next_image.first == vk::Result::eErrorOutOfDateKHR) { + this->RecreateSwapchain(); + return; + } else if (next_image.first != vk::Result::eSuccess && next_image.first != vk::Result::eSuboptimalKHR) + throw std::runtime_error("failed to acquire next swap chain image!"); + + this->vk_gpu->resetFences(wait_fences); + + this->vk_current_frame_index = (this->vk_current_frame_index + 1) % this->vk_max_frames_in_flight; + this->vk_allocator->setCurrentFrameIndex(current_frame_index); + + const auto& command_buffer = this->vk_command_buffers[current_frame_index]; + command_buffer.reset(); + + this->RecordCommandBuffer(command_buffer, next_image.second, current_frame_index); + + const std::array wait_semaphores = { *this->vk_semephores_image_available[current_frame_index] }; + constexpr auto wait_stages = vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput); + const std::array signal_semaphores = { *this->vk_semephores_render_finished[current_frame_index] }; + + this->vk_pipeline_third.UpdateUniformBuffer(current_frame_index); + + const vk::SubmitInfo submit_info { + .waitSemaphoreCount = wait_semaphores.size(), + .pWaitSemaphores = wait_semaphores.data(), + .pWaitDstStageMask = &wait_stages, + .commandBufferCount = 1, + .pCommandBuffers = &*command_buffer, + .signalSemaphoreCount = signal_semaphores.size(), + .pSignalSemaphores = signal_semaphores.data() + }; + const std::array submit_queue = { submit_info }; + this->vk_queue_graphics->submit(submit_queue, *this->vk_fences_in_flight[current_frame_index]); + + const std::array swapchains = { *this->vk_swapchain }; + const vk::PresentInfoKHR present_info { + .waitSemaphoreCount = signal_semaphores.size(), + .pWaitSemaphores = signal_semaphores.data(), + .swapchainCount = swapchains.size(), + .pSwapchains = swapchains.data(), + .pImageIndices = &next_image.second, + .pResults = nullptr // Optional + }; + const vk::Result present_result = this->vk_queue_present->presentKHR(present_info); + if (present_result == vk::Result::eErrorOutOfDateKHR || present_result == vk::Result::eSuboptimalKHR) { + this->RecreateSwapchain(); + return; + } else if (present_result != vk::Result::eSuccess) + throw std::runtime_error("failed to present image!"); + } + void RecordCommandBuffer(const vk::raii::CommandBuffer& command_buffer, const std::uint32_t image_index, const std::size_t frame_index) const { + constexpr vk::CommandBufferBeginInfo command_buffer_begin_info { + //.flags = vk::CommandBufferUsageFlagBits::, // Optional + //.pInheritanceInfo = nullptr // Optional + }; + command_buffer.begin(command_buffer_begin_info); + + constexpr std::array clear_colors = { vk::ClearValue { .color = std::array { 0.0f, 0.0f, 0.0f, 1.0f } }, vk::ClearValue { .color = std::array { 0.0f, 0.0f, 0.0f, 1.0f } }, vk::ClearValue { .depthStencil = { 1.0f, 0 } } }; + const vk::RenderPassBeginInfo render_pass_info { + .renderPass = **this->vk_render_pass, + .framebuffer = *this->vk_swapchain_framebuffers.at(image_index), + .renderArea = { + .offset = { 0, 0 }, + .extent = this->vk_swapchain_extent }, + .clearValueCount = clear_colors.size(), + .pClearValues = clear_colors.data() + }; + command_buffer.beginRenderPass(render_pass_info, vk::SubpassContents::eInline); + this->RecordDynamic(command_buffer); + this->vk_pipeline_third.Bind(command_buffer, frame_index); + this->vk_pipeline_third.Draw(command_buffer); + + this->vk_pipeline_second.Bind(command_buffer, frame_index); + this->vk_pipeline_second.Draw(command_buffer); + command_buffer.endRenderPass(); + command_buffer.end(); + }; + void RecordDynamic(const vk::raii::CommandBuffer& command_buffer) const { + const vk::Viewport viewport { + .x = 0.0f, + .y = 0.0f, + .width = (float)this->vk_swapchain_extent.width, + .height = (float)this->vk_swapchain_extent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + const vk::Rect2D scissor { + .offset = { 0, 0 }, + .extent = this->vk_swapchain_extent + }; + command_buffer.setViewport(0, viewport); + command_buffer.setScissor(0, scissor); + } + +public: + ~VulkanExampleApplication() { + this->vk_gpu->waitIdle(); + } + +private: + static constexpr auto app_name = "VulkZample"; + static constexpr bool allow_swrast = true; + +#if defined(ENABLE_VULKAN_VALIDATION) + static constexpr auto debug_message_info = vk::DebugUtilsMessengerCreateInfoEXT { + .messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo, + .messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, + .pfnUserCallback = &VulkanExampleApplication_VulkanDebugCallback + }; +#endif + static constexpr int vk_max_frames_in_flight = 2; + static constexpr std::array required_vulkan_device_extensions = { vk::KHRSwapchainExtensionName }; + static constexpr std::array required_vulkan_validation_layers = { "VK_LAYER_KHRONOS_validation" }; + static constexpr vk::Format required_vulkan_default_format = vk::Format::eR8G8B8A8Unorm; // formats Preffered front to back. FIFO + static constexpr std::array required_vulkan_screen_formats = { vk::Format::eB8G8R8A8Unorm, vk::Format::eR8G8B8A8Unorm, vk::Format::eB8G8R8Unorm, vk::Format::eR8G8B8Unorm }; // used similar ones from, seemed optimal... https://github.com/KhronosGroup/Vulkan-Hpp/blob/6f72ceca515d59f40d64b64cf2734f6261e1f9f2/samples/utils/utils.cpp#L537 + static constexpr vk::ColorSpaceKHR required_vulkan_default_screen_colorspace = vk::ColorSpaceKHR::eSrgbNonlinear; + static constexpr vk::ColorSpaceKHR required_vulkan_screen_colorspace = vk::ColorSpaceKHR::eSrgbNonlinear; +}; + +#if defined(ENABLE_VULKAN_VALIDATION) +static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanExampleApplication_VulkanDebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_type, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, + void* user_data) { + + std::cerr << "[debug log] validation layer: " << callback_data->pMessage << std::endl; + if (message_severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + if constexpr (fatal_errors) { + const std::string_view msg = callback_data->pMessage; + if (!msg.starts_with("loader_scanned_icd_add: Could not get 'vkCreateInstance' via 'vk_icdGetInstanceProcAddr'")) + throw std::logic_error(std::string("Received Vulkan-ValidationLayer Error: ") + callback_data->pMessage); + } + } + return vk::False; +} +/*static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanExampleApplication_VulkanReportCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT type, + uint64_t obj, + size_t location, + int32_t code, + const char* layer_prefix, + const char* msg, + void* user_data) { + + printf("[debug log] report layer: %s\n", msg); + + return vk::False; +}*/ +#endif + +int main() { + + try { + VulkanExampleApplication app; +#if !defined(ENABLE_TESTS) + app.Run(); +#else + app.RunTests(); +#endif + } catch (const vk::SystemError& error) { + std::cerr << error.what() << std::endl; + return EXIT_FAILURE; + } catch (const std::exception& error) { + std::cerr << error.what() << std::endl; + return EXIT_FAILURE; + } catch (...) { + std::cerr << "Received Unknown Error in Main()..." << std::endl; + throw "Unknown Exception, throwing..."; + } + + return EXIT_SUCCESS; +} diff --git a/shader/shader.frag.glsl b/shader/shader.frag.glsl index 4b942ed..006b8a3 100644 --- a/shader/shader.frag.glsl +++ b/shader/shader.frag.glsl @@ -19,12 +19,24 @@ #version 450 -layout(location = 0) in vec3 frag_color; +layout(location = 0) in vec4 frag_color; layout(location = 1) in vec2 frag_texture_coordinate; layout(binding = 0) uniform sampler2D texture_sampler; +layout(location = 2) flat in uint frag_draw_mode; layout(location = 0) out vec4 out_color; void main() { - out_color = texture(texture_sampler, frag_texture_coordinate); + if (frag_draw_mode == 1) { + out_color = frag_color; + } else { + vec4 tex = texture(texture_sampler, frag_texture_coordinate); + if (frag_draw_mode == 2) + tex = frag_color * tex; + else if (frag_draw_mode == 3) + tex = vec4(frag_color.rgb, frag_color.a * tex.r); + else + tex = vec4(0.0, 0.0, 0.0, 1.0); + out_color = tex; + } } diff --git a/shader/shader.second.vertex.glsl b/shader/shader.second.vertex.glsl index 216466e..6a9d771 100644 --- a/shader/shader.second.vertex.glsl +++ b/shader/shader.second.vertex.glsl @@ -20,14 +20,17 @@ #version 450 layout(location = 0) in vec2 in_position; -layout(location = 2) in vec3 in_color; +layout(location = 2) in vec4 in_color; layout(location = 4) in vec2 in_texture_coordinate; +layout(location = 6) in uint in_drawmode; -layout(location = 0) out vec3 frag_color; +layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_texture_coordinate; +layout(location = 2) flat out uint frag_draw_mode; void main() { - gl_Position = vec4(in_position, 0.5, 1.0); + gl_Position = vec4(in_position, 0.0, 1.0); frag_color = in_color; frag_texture_coordinate = in_texture_coordinate; + frag_draw_mode = in_drawmode; } diff --git a/shader/shader.third.vertex.glsl b/shader/shader.third.vertex.glsl index c361502..5c6ae62 100644 --- a/shader/shader.third.vertex.glsl +++ b/shader/shader.third.vertex.glsl @@ -26,14 +26,17 @@ layout(binding = 1) uniform UniformBufferObject { } ubo; layout(location = 0) in vec3 in_position; -layout(location = 2) in vec3 in_color; +layout(location = 2) in vec4 in_color; layout(location = 4) in vec2 in_texture_coordinate; +layout(location = 6) in uint in_drawmode; -layout(location = 0) out vec3 frag_color; +layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_texture_coordinate; +layout(location = 2) flat out uint frag_draw_mode; void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(in_position, 1.0); frag_color = in_color; frag_texture_coordinate = in_texture_coordinate; + frag_draw_mode = in_drawmode; } diff --git a/src/main.cpp b/src/main.cpp index ecd5a0d..8597964 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -123,11 +123,27 @@ private: // Swapchain/Framebuffer std::vector vk_semephores_render_finished; private: + enum DrawMode : std::uint32_t { + kPlain = 1, + kTextured = 2, + kFreetype = 3 + }; + static constexpr vk::ShaderModuleCreateInfo GetSharedShaderFragment() { + return { .codeSize = embeded_shader_frag_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_frag_glsl_spv.begin) }; + } + struct Vertex2 { glm::vec2 pos; - glm::vec3 color; + glm::vec4 color; glm::vec2 texture_coordinates; + DrawMode draw_mode; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoFragment() { + return VulkanExampleApplication::GetSharedShaderFragment(); + }; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoVertex() { + return { .codeSize = embeded_shader_second_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_second_vertex_glsl_spv.begin) }; + }; static constexpr vk::VertexInputBindingDescription GetBindingDescription() { constexpr vk::VertexInputBindingDescription binding_description { .binding = 0, @@ -136,8 +152,8 @@ private: }; 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, @@ -146,14 +162,18 @@ private: vk::VertexInputAttributeDescription { .location = 2, .binding = 0, - .format = vk::Format::eR32G32B32Sfloat, + .format = vk::Format::eR32G32B32A32Sfloat, .offset = offsetof(Vertex2, color) }, vk::VertexInputAttributeDescription { .location = 4, .binding = 0, .format = vk::Format::eR32G32Sfloat, - .offset = offsetof(Vertex2, texture_coordinates) } - + .offset = offsetof(Vertex2, texture_coordinates) }, + vk::VertexInputAttributeDescription { + .location = 6, + .binding = 0, + .format = vk::Format::eR32Uint, + .offset = offsetof(Vertex2, draw_mode) } }; return attribute_descriptions; } @@ -162,11 +182,19 @@ private: static inline const std::vector rectangle = { 0, 1, 2, 2, 3, 0 }; }; }; + struct Vertex3 { glm::vec3 pos; - glm::vec3 color; + glm::vec4 color; glm::vec2 texture_coordinates; + DrawMode draw_mode; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoFragment() { + return VulkanExampleApplication::GetSharedShaderFragment(); + }; + static constexpr vk::ShaderModuleCreateInfo GetShaderInfoVertex() { + return { .codeSize = embeded_shader_third_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_third_vertex_glsl_spv.begin) }; + } static constexpr vk::VertexInputBindingDescription GetBindingDescription() { constexpr vk::VertexInputBindingDescription binding_description { .binding = 0, @@ -175,8 +203,8 @@ private: }; 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, @@ -185,13 +213,18 @@ private: vk::VertexInputAttributeDescription { .location = 2, .binding = 0, - .format = vk::Format::eR32G32B32Sfloat, + .format = vk::Format::eR32G32B32A32Sfloat, .offset = offsetof(Vertex3, color) }, vk::VertexInputAttributeDescription { .location = 4, .binding = 0, .format = vk::Format::eR32G32Sfloat, - .offset = offsetof(Vertex3, texture_coordinates) } + .offset = offsetof(Vertex3, texture_coordinates) }, + vk::VertexInputAttributeDescription { + .location = 6, + .binding = 0, + .format = vk::Format::eR32Uint, + .offset = offsetof(Vertex3, draw_mode) } }; return attribute_descriptions; @@ -243,10 +276,10 @@ private: public: using IndexT = std::uint16_t; - void CreatePipeline(const vk::raii::Device& gpu, const vk::RenderPass& render_pass, const std::pair& gfx_shaders, const vk::SampleCountFlagBits& msaa_samples = vk::SampleCountFlagBits::e1) { + void CreatePipeline(const vk::raii::Device& gpu, const vk::RenderPass& render_pass, const vk::SampleCountFlagBits& msaa_samples = vk::SampleCountFlagBits::e1) { { // Load Shaders - this->vk_shader_vertex.emplace(gpu, gfx_shaders.first); - this->vk_shader_frag.emplace(gpu, gfx_shaders.second); + this->vk_shader_vertex.emplace(gpu, VertexT::GetShaderInfoVertex()); + this->vk_shader_frag.emplace(gpu, VertexT::GetShaderInfoFragment()); } { constexpr auto descriptor_set_layout_bindings = [&]() { @@ -550,22 +583,22 @@ private: VulkanRenderPipeline vk_pipeline_third; static inline const std::vector vertices_rect_sample = { - { { -0.2f, -0.2f }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f } }, - { { 0.2f, -0.2f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, - { { 0.2f, 0.2f }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 1.0f } }, - { { -0.2f, 0.2f }, { 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f } } + { { -0.2f, -0.2f }, { 0.0f, 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.2f, -0.2f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.2f, 0.2f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.2f, 0.2f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured } }; static inline const std::vector vertices_cube_sample = { - { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f } }, - { { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, - { { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 1.0f } }, - { { -0.5f, 0.5f, 0.0f }, { 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f } }, + { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.5f, 0.5f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured }, - { { -0.5f, -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f } }, - { { 0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, - { { 0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 1.0f } }, - { { -0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f } } + { { -0.5f, -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, DrawMode::kTextured }, + { { 0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f }, DrawMode::kTextured }, + { { -0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f }, DrawMode::kTextured } }; private: @@ -982,12 +1015,8 @@ public: this->vk_render_pass.emplace(*this->vk_gpu, render_pass_create_info); } { - const auto shader_info_frag = vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_frag_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_frag_glsl_spv.begin) }; - const auto shader_second_info_vertex = vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_second_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_second_vertex_glsl_spv.begin) }; - this->vk_pipeline_second.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, { shader_second_info_vertex, shader_info_frag }, this->vk_msaa_samples); - - const auto shader_third_info_vertex = vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_third_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_third_vertex_glsl_spv.begin) }; - this->vk_pipeline_third.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, { shader_third_info_vertex, shader_info_frag }, this->vk_msaa_samples); + this->vk_pipeline_second.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, this->vk_msaa_samples); + this->vk_pipeline_third.CreatePipeline(*this->vk_gpu, *this->vk_render_pass, this->vk_msaa_samples); } { this->CreateBufferFrame();