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();