~sircmpwn/xrgears

f162555056b018a3805f96df49158518ec63f94c — Lubosz Sarnecki 1 year, 9 months ago 7c50376
vik: Use struct and array initializers.

Remove vikInitializers.
M examples/triangle/triangle.cpp => examples/triangle/triangle.cpp +3 -1
@@ 343,7 343,9 @@ class Triangle : public vik::Application {
      // Buffer copies have to be submitted to a queue, so we need a command buffer for them
      // Note: Some devices offer a dedicated transfer queue (with only the transfer bit set) that may be faster when doing lots of copies
      VkCommandBuffer copyCmd = renderer->create_command_buffer();
      VkCommandBufferBeginInfo cmdBufInfo = vik::initializers::commandBufferBeginInfo();
      VkCommandBufferBeginInfo cmdBufInfo = {
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
      };
      vik_log_check(vkBeginCommandBuffer(copyCmd, &cmdBufInfo));

      // Put buffer region copies into command buffer

M examples/xrgears/xrgears.cpp => examples/xrgears/xrgears.cpp +178 -115
@@ 178,17 178,25 @@ class XRGears : public vik::Application {
    // Set target frame buffer
    render_pass_begin_info.framebuffer = framebuffer;

    VkCommandBufferBeginInfo command_bufffer_info = vik::initializers::commandBufferBeginInfo();
    vik_log_check(vkBeginCommandBuffer(command_buffer, &command_bufffer_info));
    VkCommandBufferBeginInfo command_buffer_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
    };
    vik_log_check(vkBeginCommandBuffer(command_buffer, &command_buffer_info));

    vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);

    VkViewport viewport = vik::initializers::viewport(
          (float) renderer->width, (float) renderer->height, 0.0f, 1.0f);
    VkViewport viewport = {
      .width = (float)renderer->width,
      .height = (float)renderer->height,
      .minDepth = 0.0f,
      .maxDepth = 1.0f
    };
    vkCmdSetViewport(command_buffer, 0, 1, &viewport);

    VkRect2D scissor = vik::initializers::rect2D(
          renderer->width, renderer->height, 0, 0);
    VkRect2D scissor = {
      .offset = { .x = 0, .y = 0 },
      .extent = { .width = renderer->width, .height = renderer->height }
    };
    vkCmdSetScissor(command_buffer, 0, 1, &scissor);

    // Final composition as full screen quad


@@ 205,7 213,9 @@ class XRGears : public vik::Application {
      offscreen_command_buffer = renderer->create_command_buffer();

    // Create a semaphore used to synchronize offscreen rendering and usage
    VkSemaphoreCreateInfo semaphore_info = vik::initializers::semaphoreCreateInfo();
    VkSemaphoreCreateInfo semaphore_info = {
      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
    };
    vik_log_check(vkCreateSemaphore(renderer->device, &semaphore_info,
                                    nullptr, &offscreen_semaphore));



@@ 216,7 226,9 @@ class XRGears : public vik::Application {
  void build_pbr_command_buffer(const VkCommandBuffer& command_buffer,
                                const VkFramebuffer& framebuffer,
                                bool offscreen) {
    VkCommandBufferBeginInfo command_buffer_info = vik::initializers::commandBufferBeginInfo();
    VkCommandBufferBeginInfo command_buffer_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
    };
    vik_log_check(vkBeginCommandBuffer(command_buffer, &command_buffer_info));

    if (vik::debugmarker::active)


@@ 266,16 278,18 @@ class XRGears : public vik::Application {
  }

  void set_mono_viewport_and_scissors(VkCommandBuffer command_buffer) {
    VkViewport viewport = vik::initializers::viewport(
          (float)renderer->width,
          (float)renderer->height,
          0.0f, 1.0f);
    VkViewport viewport = {
      .width = (float)renderer->width,
      .height = (float)renderer->height,
      .minDepth = 0.0f,
      .maxDepth = 1.0f
    };
    vkCmdSetViewport(command_buffer, 0, 1, &viewport);

    VkRect2D scissor = vik::initializers::rect2D(
          renderer->width,
          renderer->height,
          0.0f, 0.0f);
    VkRect2D scissor = {
      .offset = { .x = 0, .y = 0 },
      .extent = { .width = renderer->width, .height = renderer->height }
    };
    vkCmdSetScissor(command_buffer, 0, 1, &scissor);
  }



@@ 301,8 315,14 @@ class XRGears : public vik::Application {
    vkCmdSetViewport(command_buffer, 0, 2, viewports);

    VkRect2D scissor_rects[2] = {
      vik::initializers::rect2D(renderer->width/2, renderer->height, 0, 0),
      vik::initializers::rect2D(renderer->width/2, renderer->height, renderer->width / 2, 0),
      {
        .offset = { .x = 0, .y = 0 },
        .extent = { .width = renderer->width/2, .height = renderer->height }
      },
      {
        .offset = { .x = (int32_t) renderer->width / 2, .y = 0 },
        .extent = { .width = renderer->width/2, .height = renderer->height }
      },
    };
    vkCmdSetScissor(command_buffer, 0, 2, scissor_rects);
  }


@@ 393,57 413,65 @@ class XRGears : public vik::Application {
  void prepare_vertices() {
    // Binding and attribute descriptions are shared across all gears
    vertices.binding_descriptions.resize(1);
    vertices.binding_descriptions[0] =
        vik::initializers::vertexInputBindingDescription(
          VERTEX_BUFFER_BIND_ID,
          sizeof(vik::Vertex),
          VK_VERTEX_INPUT_RATE_VERTEX);
    vertices.binding_descriptions[0] = {
      .binding = VERTEX_BUFFER_BIND_ID,
      .stride = sizeof(vik::Vertex),
      .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
    };

    // Attribute descriptions
    // Describes memory layout and shader positions
    vertices.attribute_descriptions.resize(3);
    // Location 0 : Position
    vertices.attribute_descriptions[0] = (VkVertexInputAttributeDescription) {
    vertices.attribute_descriptions[0] = {
      .location = 0,
      .binding = VERTEX_BUFFER_BIND_ID,
      .format = VK_FORMAT_R32G32B32_SFLOAT,
      .offset = 0
    };
    // Location 1 : Normal
    vertices.attribute_descriptions[1] = (VkVertexInputAttributeDescription) {
    vertices.attribute_descriptions[1] = {
      .location = 1,
      .binding = VERTEX_BUFFER_BIND_ID,
      .format = VK_FORMAT_R32G32B32_SFLOAT,
      .offset = sizeof(float) * 3
    };
    // Location 2 : Color
    vertices.attribute_descriptions[2] = (VkVertexInputAttributeDescription) {
    vertices.attribute_descriptions[2] = {
      .location = 2,
      .binding = VERTEX_BUFFER_BIND_ID,
      .format = VK_FORMAT_R32G32B32_SFLOAT,
      .offset = sizeof(float) * 6
    };

    vertices.input_state = vik::initializers::pipelineVertexInputStateCreateInfo();
    vertices.input_state.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.binding_descriptions.size());
    vertices.input_state.pVertexBindingDescriptions = vertices.binding_descriptions.data();
    vertices.input_state.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attribute_descriptions.size());
    vertices.input_state.pVertexAttributeDescriptions = vertices.attribute_descriptions.data();
    vertices.input_state = (VkPipelineVertexInputStateCreateInfo) {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
      .vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.binding_descriptions.size()),
      .pVertexBindingDescriptions = vertices.binding_descriptions.data(),
      .vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attribute_descriptions.size()),
      .pVertexAttributeDescriptions = vertices.attribute_descriptions.data()
    };
  }

  void init_descriptor_pool() {
    // Example uses two ubos
    std::vector<VkDescriptorPoolSize> pool_sizes = {
      vik::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 16),
      vik::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6)
      {
        .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        .descriptorCount = 16
      },
      {
        .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
        .descriptorCount = 6
      }
    };

    VkDescriptorPoolCreateInfo descriptor_pool_info =
        vik::initializers::descriptorPoolCreateInfo(
          static_cast<uint32_t>(pool_sizes.size()),
          pool_sizes.data(),
          6);

    VkDescriptorPoolCreateInfo descriptor_pool_info = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
      .maxSets = 6,
      .poolSizeCount = static_cast<uint32_t>(pool_sizes.size()),
      .pPoolSizes = pool_sizes.data()
    };
    vik_log_check(vkCreateDescriptorPool(renderer->device,
                                         &descriptor_pool_info,
                                         nullptr, &renderer->descriptor_pool));


@@ 452,53 480,64 @@ class XRGears : public vik::Application {
  void init_descriptor_set_layout() {
    std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = {
      // ubo model
      vik::initializers::descriptorSetLayoutBinding(
      VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      VK_SHADER_STAGE_GEOMETRY_BIT,  // VK_SHADER_STAGE_VERTEX_BIT,
      0),
      {
        .binding = 0,
        .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_GEOMETRY_BIT
      },
      // ubo lights
      vik::initializers::descriptorSetLayoutBinding(
      VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      VK_SHADER_STAGE_FRAGMENT_BIT,
      1),
      {
        .binding = 1,
        .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
      },
      // ubo camera
      vik::initializers::descriptorSetLayoutBinding(
      VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
      2)
      {
        .binding = 2,
        .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT
      }
    };

    // cube map sampler
    if (enable_sky)
      set_layout_bindings.push_back(vik::initializers::descriptorSetLayoutBinding(
                                    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
                                    VK_SHADER_STAGE_FRAGMENT_BIT,
                                    3));
    if (enable_sky) {
      VkDescriptorSetLayoutBinding sky_binding = {
        .binding = 3,
        .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
      };
      set_layout_bindings.push_back(sky_binding);
    }

    VkDescriptorSetLayoutCreateInfo descriptor_layout =
        vik::initializers::descriptorSetLayoutCreateInfo(set_layout_bindings);
    VkDescriptorSetLayoutCreateInfo descriptor_layout = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
      .bindingCount = static_cast<uint32_t>(set_layout_bindings.size()),
      .pBindings = set_layout_bindings.data()
    };

    vik_log_check(vkCreateDescriptorSetLayout(renderer->device,
                                              &descriptor_layout,
                                              nullptr, &descriptor_set_layout));

    VkPipelineLayoutCreateInfo pipeline_layout_info =
        vik::initializers::pipelineLayoutCreateInfo(&descriptor_set_layout, 1);

    /*
     * Push Constants
     */

    std::vector<VkPushConstantRange> push_constant_ranges = {
      vik::initializers::pushConstantRange(
        VK_SHADER_STAGE_FRAGMENT_BIT,
        sizeof(vik::Material::PushBlock),
        sizeof(glm::vec3)),
    std::vector<VkPushConstantRange> push_constant_ranges = {{
      .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
      .offset = sizeof(glm::vec3),
      .size = sizeof(vik::Material::PushBlock)
    }};

    VkPipelineLayoutCreateInfo pipeline_layout_info = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
      .setLayoutCount = 1,
      .pSetLayouts = &descriptor_set_layout,
      .pushConstantRangeCount = (uint32_t) push_constant_ranges.size(),
      .pPushConstantRanges = push_constant_ranges.data()
    };

    pipeline_layout_info.pushConstantRangeCount = push_constant_ranges.size();
    pipeline_layout_info.pPushConstantRanges = push_constant_ranges.data();

    vik_log_check(vkCreatePipelineLayout(renderer->device,
                                         &pipeline_layout_info,
                                         nullptr, &pipeline_layout));


@@ 506,12 545,12 @@ class XRGears : public vik::Application {

  void init_descriptor_set() {
    if (enable_sky) {
      VkDescriptorSetAllocateInfo info =
          vik::initializers::descriptorSetAllocateInfo(
            renderer->descriptor_pool,
            &descriptor_set_layout,
            1);

      VkDescriptorSetAllocateInfo info = {
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
        .descriptorPool = renderer->descriptor_pool,
        .descriptorSetCount = 1,
        .pSetLayouts = &descriptor_set_layout
      };
      sky_box->create_descriptor_set(info, &camera->uniform_buffer.descriptor);
    }



@@ 524,44 563,66 @@ class XRGears : public vik::Application {
}

  void init_pipelines() {
    VkPipelineInputAssemblyStateCreateInfo input_assembly_state =
        vik::initializers::pipelineInputAssemblyStateCreateInfo(
          VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
          0,
          VK_FALSE);

    VkPipelineRasterizationStateCreateInfo rasterization_state =
        vik::initializers::pipelineRasterizationStateCreateInfo(
          VK_POLYGON_MODE_FILL,
          VK_CULL_MODE_BACK_BIT,
          VK_FRONT_FACE_CLOCKWISE);

    VkPipelineColorBlendAttachmentState blend_attachment_state =
        vik::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);

    VkPipelineColorBlendStateCreateInfo color_blend_state =
        vik::initializers::pipelineColorBlendStateCreateInfo(1, &blend_attachment_state);

    VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
        vik::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);

    VkPipelineViewportStateCreateInfo viewport_state;
    if (enable_stereo)
      viewport_state = vik::initializers::pipelineViewportStateCreateInfo(2, 2, 0);
    else
      viewport_state = vik::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
    VkPipelineInputAssemblyStateCreateInfo input_assembly_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
      .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
      .primitiveRestartEnable = VK_FALSE
    };

    VkPipelineRasterizationStateCreateInfo rasterization_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
      .depthClampEnable = VK_FALSE,
      .polygonMode = VK_POLYGON_MODE_FILL,
      .cullMode = VK_CULL_MODE_BACK_BIT,
      .frontFace = VK_FRONT_FACE_CLOCKWISE,
      .lineWidth = 1.0f
    };

    VkPipelineColorBlendAttachmentState blend_attachment_state = {
      .blendEnable = VK_FALSE,
      .colorWriteMask = 0xf
    };

    VkPipelineColorBlendStateCreateInfo color_blend_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
      .attachmentCount = 1,
      .pAttachments = &blend_attachment_state
    };

    VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
      .depthTestEnable = VK_TRUE,
      .depthWriteEnable = VK_TRUE,
      .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
      .front = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      },
      .back = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      }
    };

    VkPipelineMultisampleStateCreateInfo multisample_state =
        vik::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT);
    VkPipelineViewportStateCreateInfo viewport_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
      .viewportCount = static_cast<uint32_t>(enable_stereo ? 2 : 1),
      .scissorCount = static_cast<uint32_t>(enable_stereo ? 2 : 1)
    };

    VkPipelineMultisampleStateCreateInfo multisample_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
      .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
    };

    std::vector<VkDynamicState> dynamic_state_enables = {
      VK_DYNAMIC_STATE_VIEWPORT,
      VK_DYNAMIC_STATE_SCISSOR,
      VK_DYNAMIC_STATE_LINE_WIDTH
    };
    VkPipelineDynamicStateCreateInfo dynamicState =
        vik::initializers::pipelineDynamicStateCreateInfo(dynamic_state_enables);
    VkPipelineDynamicStateCreateInfo dynamic_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
      .dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size()),
      .pDynamicStates = dynamic_state_enables.data()
    };

    // Load shaders
    std::array<VkPipelineShaderStageCreateInfo, 3> shader_stages;


@@ 575,29 636,31 @@ class XRGears : public vik::Application {
    shader_stages[2] = vik::Shader::load(renderer->device, "xrgears/multiview.geom.spv", VK_SHADER_STAGE_GEOMETRY_BIT);

    // Vertex bindings an attributes
    std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {
      vik::initializers::vertexInputBindingDescription(0, vertex_layout.stride(), VK_VERTEX_INPUT_RATE_VERTEX),
    };
    std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {{
      .binding = 0,
      .stride = vertex_layout.stride(),
      .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
    }};
    std::vector<VkVertexInputAttributeDescription> vertex_input_attributes = {
      // Location 0: Position
      {
        .location = 0,
        .binding = 0,
        .format = VK_FORMAT_R32G32_SFLOAT,
        .format = VK_FORMAT_R32G32B32_SFLOAT,
        .offset = 0
      },
      // Location 1: Normals
      {
        .location = 1,
        .binding = 0,
        .format = VK_FORMAT_R32G32_SFLOAT,
        .format = VK_FORMAT_R32G32B32_SFLOAT,
        .offset = sizeof(float) * 3
      },
      // Location 2: Color
      {
        .location = 2,
        .binding = 0,
        .format = VK_FORMAT_R32G32_SFLOAT,
        .format = VK_FORMAT_R32G32B32_SFLOAT,
        .offset = sizeof(float) * 6
      }
    };


@@ 621,7 684,7 @@ class XRGears : public vik::Application {
      .pMultisampleState = &multisample_state,
      .pDepthStencilState = &depth_stencil_state,
      .pColorBlendState = &color_blend_state,
      .pDynamicState = &dynamicState,
      .pDynamicState = &dynamic_state,
      .layout = pipeline_layout,
      .basePipelineHandle = nullptr,
      .basePipelineIndex = -1,

M vitamin-k/render/vikBuffer.hpp => vitamin-k/render/vikBuffer.hpp +13 -10
@@ 106,11 106,13 @@ struct Buffer {
    * @return VkResult of the flush call
    */
  VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) {
    VkMappedMemoryRange mappedRange = {};
    mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    mappedRange.memory = memory;
    mappedRange.offset = offset;
    mappedRange.size = size;
    VkMappedMemoryRange mappedRange = {
      .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
      .memory = memory,
      .offset = offset,
      .size = size
    };

    return vkFlushMappedMemoryRanges(device, 1, &mappedRange);
  }



@@ 125,11 127,12 @@ struct Buffer {
    * @return VkResult of the invalidate call
    */
  VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) {
    VkMappedMemoryRange mappedRange = {};
    mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    mappedRange.memory = memory;
    mappedRange.offset = offset;
    mappedRange.size = size;
    VkMappedMemoryRange mappedRange = {
      .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
      .memory = memory,
      .offset = offset,
      .size = size
    };
    return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange);
  }


M vitamin-k/render/vikDebug.hpp => vitamin-k/render/vikDebug.hpp +9 -7
@@ 100,10 100,11 @@ static void setupDebugging(VkInstance instance, VkDebugReportFlagsEXT flags, VkD
  DestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"));
  dbgBreakCallback = reinterpret_cast<PFN_vkDebugReportMessageEXT>(vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT"));

  VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
  dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
  dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)messageCallback;
  dbgCreateInfo.flags = flags;
  VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {
    .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT,
    .flags = flags,
    .pfnCallback = (PFN_vkDebugReportCallbackEXT) messageCallback
  };

  VkResult err = CreateDebugReportCallback(
        instance,


@@ 182,10 183,11 @@ static void setObjectTag(VkDevice device, uint64_t object, VkDebugReportObjectTy
static void beginRegion(VkCommandBuffer cmdbuffer, const char* pMarkerName, glm::vec4 color) {
  // Check for valid function pointer (may not be present if not running in a debugging application)
  if (pfnCmdDebugMarkerBegin) {
    VkDebugMarkerMarkerInfoEXT markerInfo = {};
    markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
    VkDebugMarkerMarkerInfoEXT markerInfo = {
      .sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
      .pMarkerName = pMarkerName
    };
    memcpy(markerInfo.color, &color[0], sizeof(float) * 4);
    markerInfo.pMarkerName = pMarkerName;
    pfnCmdDebugMarkerBegin(cmdbuffer, &markerInfo);
  }
}

M vitamin-k/render/vikDevice.hpp => vitamin-k/render/vikDevice.hpp +112 -61
@@ 199,11 199,13 @@ class Device {
    // Graphics queue
    if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT) {
      queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT);
      VkDeviceQueueCreateInfo queueInfo{};
      queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
      queueInfo.queueFamilyIndex = queueFamilyIndices.graphics;
      queueInfo.queueCount = 1;
      queueInfo.pQueuePriorities = &defaultQueuePriority;
      VkDeviceQueueCreateInfo queueInfo = {
        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
        .queueFamilyIndex = queueFamilyIndices.graphics,
        .queueCount = 1,
        .pQueuePriorities = &defaultQueuePriority
      };

      queueCreateInfos.push_back(queueInfo);
    } else {
      queueFamilyIndices.graphics = VK_NULL_HANDLE;


@@ 214,11 216,13 @@ class Device {
      queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT);
      if (queueFamilyIndices.compute != queueFamilyIndices.graphics) {
        // If compute family index differs, we need an additional queue create info for the compute queue
        VkDeviceQueueCreateInfo queueInfo{};
        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueInfo.queueFamilyIndex = queueFamilyIndices.compute;
        queueInfo.queueCount = 1;
        queueInfo.pQueuePriorities = &defaultQueuePriority;
        VkDeviceQueueCreateInfo queueInfo = {
          .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
          .queueFamilyIndex = queueFamilyIndices.compute,
          .queueCount = 1,
          .pQueuePriorities = &defaultQueuePriority
        };

        queueCreateInfos.push_back(queueInfo);
      }
    } else {


@@ 231,11 235,13 @@ class Device {
      queueFamilyIndices.transfer = getQueueFamilyIndex(VK_QUEUE_TRANSFER_BIT);
      if ((queueFamilyIndices.transfer != queueFamilyIndices.graphics) && (queueFamilyIndices.transfer != queueFamilyIndices.compute)) {
        // If compute family index differs, we need an additional queue create info for the compute queue
        VkDeviceQueueCreateInfo queueInfo{};
        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueInfo.queueFamilyIndex = queueFamilyIndices.transfer;
        queueInfo.queueCount = 1;
        queueInfo.pQueuePriorities = &defaultQueuePriority;
        VkDeviceQueueCreateInfo queueInfo = {
          .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
          .queueFamilyIndex = queueFamilyIndices.transfer,
          .queueCount = 1,
          .pQueuePriorities = &defaultQueuePriority
        };

        queueCreateInfos.push_back(queueInfo);
      }
    } else {


@@ 257,11 263,12 @@ class Device {
    for (auto window_ext : window_extensions)
      enable_if_supported(&deviceExtensions, window_ext);

    VkDeviceCreateInfo deviceCreateInfo = {};
    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());;
    deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
    deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
    VkDeviceCreateInfo deviceCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
      .queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()),
      .pQueueCreateInfos = queueCreateInfos.data(),
      .pEnabledFeatures = &enabledFeatures
    };

    // Enable the debug marker extension if it is present (likely meaning a debugging tool is present)
    // enableDebugMarkers = enableIfSupported(&deviceExtensions, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);


@@ 295,17 302,25 @@ class Device {
    */
  VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr) {
    // Create the buffer handle
    VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo(usageFlags, size);
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    VkBufferCreateInfo bufferCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .size = size,
      .usage = usageFlags,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE
    };
    vik_log_check(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer));

    // Create the memory backing up the buffer handle
    VkMemoryRequirements memReqs;
    VkMemoryAllocateInfo memAlloc = initializers::memoryAllocateInfo();
    vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs);
    memAlloc.allocationSize = memReqs.size;
    // Find a memory type index that fits the properties of the buffer
    memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);

    VkMemoryAllocateInfo memAlloc {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      // Find a memory type index that fits the properties of the buffer
      .memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags)
    };

    vik_log_check(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory));

    // If a pointer to the buffer data has been passed, map the buffer and copy over the data


@@ 315,10 330,14 @@ class Device {
      memcpy(mapped, data, size);
      // If host coherency hasn't been requested, do a manual flush to make writes visible
      if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) {
        VkMappedMemoryRange mappedRange = initializers::mappedMemoryRange();
        mappedRange.memory = *memory;
        mappedRange.offset = 0;
        mappedRange.size = size;

        VkMappedMemoryRange mappedRange = {
          .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
          .memory = *memory,
          .offset = 0,
          .size = size
        };

        vkFlushMappedMemoryRanges(logicalDevice, 1, &mappedRange);
      }
      vkUnmapMemory(logicalDevice, *memory);


@@ 357,17 376,26 @@ class Device {
    buffer->device = logicalDevice;

    // Create the buffer handle
    VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo(usageFlags, size);
    VkBufferCreateInfo bufferCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .size = size,
      .usage = usageFlags
    };
    vik_log_check(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &buffer->buffer));

    // Create the memory backing up the buffer handle
    VkMemoryRequirements memReqs;
    VkMemoryAllocateInfo memAlloc = initializers::memoryAllocateInfo();
    vkGetBufferMemoryRequirements(logicalDevice, buffer->buffer, &memReqs);
    memAlloc.allocationSize = memReqs.size;
    // Find a memory type index that fits the properties of the buffer
    memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);
    vik_log_check(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, &buffer->memory));

    VkMemoryAllocateInfo memAlloc {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      // Find a memory type index that fits the properties of the buffer
      .memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags)
    };

    vik_log_check(vkAllocateMemory(logicalDevice, &memAlloc,
                                   nullptr, &buffer->memory));

    buffer->alignment = memReqs.alignment;
    buffer->size = memAlloc.allocationSize;


@@ 424,10 452,12 @@ class Device {
    * @return A handle to the created command buffer
    */
  VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) {
    VkCommandPoolCreateInfo cmdPoolInfo = {};
    cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    cmdPoolInfo.queueFamilyIndex = queueFamilyIndex;
    cmdPoolInfo.flags = createFlags;
    VkCommandPoolCreateInfo cmdPoolInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
      .flags = createFlags,
      .queueFamilyIndex = queueFamilyIndex
    };

    VkCommandPool cmdPool;
    vik_log_check(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool));
    return cmdPool;


@@ 442,14 472,21 @@ class Device {
    * @return A handle to the allocated command buffer
    */
  VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false) {
    VkCommandBufferAllocateInfo cmdBufAllocateInfo = initializers::commandBufferAllocateInfo(commandPool, level, 1);
    VkCommandBufferAllocateInfo cmdBufAllocateInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .commandPool = commandPool,
      .level = level,
      .commandBufferCount = 1
    };

    VkCommandBuffer cmdBuffer;
    vik_log_check(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer));

    // If requested, also start recording for the new command buffer
    if (begin) {
      VkCommandBufferBeginInfo cmdBufInfo = initializers::commandBufferBeginInfo();
      VkCommandBufferBeginInfo cmdBufInfo = {
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
      };
      vik_log_check(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));
    }



@@ 472,12 509,16 @@ class Device {

    vik_log_check(vkEndCommandBuffer(commandBuffer));

    VkSubmitInfo submitInfo = initializers::submitInfo();
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;
    VkSubmitInfo submitInfo = {
      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
      .commandBufferCount = 1,
      .pCommandBuffers = &commandBuffer
    };

    // Create fence to ensure that the command buffer has finished executing
    VkFenceCreateInfo fenceInfo = initializers::fenceCreateInfo(0);
    VkFenceCreateInfo fenceInfo = {
      .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
    };
    VkFence fence;
    vik_log_check(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence));



@@ 527,11 568,15 @@ class Device {
    GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceProperties2KHR);

    if (fpGetPhysicalDeviceFeatures2KHR) {
      VkPhysicalDeviceFeatures2KHR device_features{};
      VkPhysicalDeviceMultiviewFeaturesKHR multi_view_features{};
      multi_view_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR;
      device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
      device_features.pNext = &multi_view_features;
      VkPhysicalDeviceMultiviewFeaturesKHR multi_view_features = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR
      };

      VkPhysicalDeviceFeatures2KHR device_features = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
        .pNext = &multi_view_features
      };

      fpGetPhysicalDeviceFeatures2KHR(physicalDevice, &device_features);

      vik_log_i("multiview %d", multi_view_features.multiview);


@@ 544,12 589,15 @@ class Device {
    }

    if (fpGetPhysicalDeviceProperties2KHR) {
      VkPhysicalDeviceProperties2KHR device_props{};
      VkPhysicalDeviceMultiviewPropertiesKHR multi_view_props = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR
      };

      VkPhysicalDeviceProperties2KHR device_props = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
        .pNext = &multi_view_props
      };

      VkPhysicalDeviceMultiviewPropertiesKHR multi_view_props{};
      multi_view_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR;
      device_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
      device_props.pNext = &multi_view_props;
      fpGetPhysicalDeviceProperties2KHR(physicalDevice, &device_props);

      vik_log_i("maxMultiviewViewCount %d",


@@ 557,11 605,14 @@ class Device {
      vik_log_i("maxMultiviewInstanceIndex %d",
                multi_view_props.maxMultiviewInstanceIndex);

      VkPhysicalDeviceProperties2KHR device_props2{};
      VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX multi_view_props2{};
      multi_view_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX;
      device_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
      device_props2.pNext = &multi_view_props2;
      VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX multi_view_props2 = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX
      };

      VkPhysicalDeviceProperties2KHR device_props2 = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
        .pNext = &multi_view_props2
      };
      fpGetPhysicalDeviceProperties2KHR(physicalDevice, &device_props2);

      vik_log_i("perViewPositionAllComponents %d",

M vitamin-k/render/vikDistortion.hpp => vitamin-k/render/vikDistortion.hpp +112 -85
@@ 65,69 65,90 @@ class Distortion {
  void init_pipeline(const VkRenderPass& render_pass,
                     const VkPipelineCache& pipeline_cache,
                     Settings::DistortionType distortion_type) {
    VkPipelineInputAssemblyStateCreateInfo input_assembly_state =
        initializers::pipelineInputAssemblyStateCreateInfo(
          VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
          0,
          VK_FALSE);

    VkPipelineRasterizationStateCreateInfo rasterization_state =
        initializers::pipelineRasterizationStateCreateInfo(
          VK_POLYGON_MODE_FILL,
          VK_CULL_MODE_BACK_BIT,
          VK_FRONT_FACE_CLOCKWISE,
          0);

    VkPipelineColorBlendAttachmentState blend_attachment_state =
        initializers::pipelineColorBlendAttachmentState(
          0xf,
          VK_FALSE);

    VkPipelineColorBlendStateCreateInfo color_blend_state =
        initializers::pipelineColorBlendStateCreateInfo(
          1,
          &blend_attachment_state);

    VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
        initializers::pipelineDepthStencilStateCreateInfo(
          VK_TRUE,
          VK_TRUE,
          VK_COMPARE_OP_LESS_OR_EQUAL);

    VkPipelineViewportStateCreateInfo viewport_state =
        initializers::pipelineViewportStateCreateInfo(1, 1, 0);

    VkPipelineMultisampleStateCreateInfo multisample_state =
        initializers::pipelineMultisampleStateCreateInfo(
          VK_SAMPLE_COUNT_1_BIT,
          0);
    VkPipelineInputAssemblyStateCreateInfo input_assembly_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
      .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
      .primitiveRestartEnable = VK_FALSE
    };

    VkPipelineRasterizationStateCreateInfo rasterization_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
      .depthClampEnable = VK_FALSE,
      .polygonMode = VK_POLYGON_MODE_FILL,
      .cullMode = VK_CULL_MODE_BACK_BIT,
      .frontFace = VK_FRONT_FACE_CLOCKWISE,
      .lineWidth = 1.0f
    };

    VkPipelineColorBlendAttachmentState blend_attachment_state = {
      .blendEnable = VK_FALSE,
      .colorWriteMask = 0xf
    };

    VkPipelineColorBlendStateCreateInfo color_blend_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
      .attachmentCount = 1,
      .pAttachments = &blend_attachment_state
    };

    VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
      .depthTestEnable = VK_TRUE,
      .depthWriteEnable = VK_TRUE,
      .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
      .front = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      },
      .back = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      }
    };

    VkPipelineViewportStateCreateInfo viewport_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
      .viewportCount = 1,
      .scissorCount = 1
    };

    VkPipelineMultisampleStateCreateInfo multisample_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
      .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
    };

    std::vector<VkDynamicState> dynamic_state_enables = {
      VK_DYNAMIC_STATE_VIEWPORT,
      VK_DYNAMIC_STATE_SCISSOR
    };
    VkPipelineDynamicStateCreateInfo dynamic_state =
        initializers::pipelineDynamicStateCreateInfo(
          dynamic_state_enables.data(),
          static_cast<uint32_t>(dynamic_state_enables.size()),
          0);
    VkGraphicsPipelineCreateInfo pipeline_info =
        initializers::pipelineCreateInfo(
          nullptr,
          render_pass,
          0);
    VkPipelineDynamicStateCreateInfo dynamic_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
      .dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size()),
      .pDynamicStates = dynamic_state_enables.data()
    };

    std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;

    pipeline_info.pInputAssemblyState = &input_assembly_state;
    pipeline_info.pRasterizationState = &rasterization_state;
    pipeline_info.pColorBlendState = &color_blend_state;
    pipeline_info.pMultisampleState = &multisample_state;
    pipeline_info.pViewportState = &viewport_state;
    pipeline_info.pDepthStencilState = &depth_stencil_state;
    pipeline_info.pDynamicState = &dynamic_state;
    pipeline_info.stageCount = static_cast<uint32_t>(shader_stages.size());
    pipeline_info.pStages = shader_stages.data();
    VkPipelineVertexInputStateCreateInfo empty_input_state = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
    };

    VkGraphicsPipelineCreateInfo pipeline_info = {
      .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
      .flags = 0,
      .stageCount = static_cast<uint32_t>(shader_stages.size()),
      .pStages = shader_stages.data(),
      .pVertexInputState = &empty_input_state,
      .pInputAssemblyState = &input_assembly_state,
      .pViewportState = &viewport_state,
      .pRasterizationState = &rasterization_state,
      .pMultisampleState = &multisample_state,
      .pDepthStencilState = &depth_stencil_state,
      .pColorBlendState = &color_blend_state,
      .pDynamicState = &dynamic_state,
      .layout = pipeline_layout,
      .renderPass = render_pass,
      .basePipelineHandle = VK_NULL_HANDLE,
      .basePipelineIndex = -1
    };

    // Final fullscreen composition pass pipeline
    shader_stages[0] =


@@ 152,10 173,6 @@ class Distortion {
                                         fragment_shader_name,
                                         VK_SHADER_STAGE_FRAGMENT_BIT);

    VkPipelineVertexInputStateCreateInfo empty_input_state =
        initializers::pipelineVertexInputStateCreateInfo();
    pipeline_info.pVertexInputState = &empty_input_state;
    pipeline_info.layout = pipeline_layout;
    vik_log_check(vkCreateGraphicsPipelines(device, pipeline_cache, 1,
                                            &pipeline_info, nullptr,
                                            &pipeline));


@@ 165,11 182,14 @@ class Distortion {
  }

  VkWriteDescriptorSet get_uniform_write_descriptor_set(uint32_t binding) {
    return initializers::writeDescriptorSet(
          descriptor_set,
          VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
          binding,
          &ubo_handle.descriptor);
    return (VkWriteDescriptorSet) {
      .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
      .dstSet = descriptor_set,
      .dstBinding = binding,
      .descriptorCount = 1,
      .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      .pBufferInfo = &ubo_handle.descriptor
    };
  }

  void init_descriptor_set(OffscreenPass *offscreenPass,


@@ 177,11 197,12 @@ class Distortion {
    std::vector<VkWriteDescriptorSet> write_descriptor_sets;

    // Textured quad descriptor set
    VkDescriptorSetAllocateInfo allocInfo =
        initializers::descriptorSetAllocateInfo(
          descriptorPool,
          &descriptor_set_layout,
          1);
    VkDescriptorSetAllocateInfo allocInfo = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
      .descriptorPool = descriptorPool,
      .descriptorSetCount = 1,
      .pSetLayouts = &descriptor_set_layout
    };

    vik_log_check(vkAllocateDescriptorSets(device, &allocInfo, &descriptor_set));



@@ 201,33 222,39 @@ class Distortion {
  }

  void init_descriptor_set_layout() {
    // Deferred shading layout
    std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = {
      // Binding 0 : Render texture target
      initializers::descriptorSetLayoutBinding(
      VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      VK_SHADER_STAGE_FRAGMENT_BIT,
      0),
      {
        .binding = 0,
        .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
      },
      // Binding 1 : Fragment shader uniform buffer
      initializers::descriptorSetLayoutBinding(
      VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      VK_SHADER_STAGE_FRAGMENT_BIT,
      1),
      {
        .binding = 1,
        .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        .descriptorCount = 1,
        .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
      }
    };

    VkDescriptorSetLayoutCreateInfo set_layout_info =
        initializers::descriptorSetLayoutCreateInfo(
          set_layout_bindings.data(),
          static_cast<uint32_t>(set_layout_bindings.size()));
    VkDescriptorSetLayoutCreateInfo set_layout_info = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
      .bindingCount = static_cast<uint32_t>(set_layout_bindings.size()),
      .pBindings = set_layout_bindings.data()
    };

    vik_log_check(vkCreateDescriptorSetLayout(device, &set_layout_info, nullptr,
                                              &descriptor_set_layout));
  }

  void init_pipeline_layout() {
    VkPipelineLayoutCreateInfo pipeline_layout_info =
        initializers::pipelineLayoutCreateInfo(&descriptor_set_layout, 1);

    VkPipelineLayoutCreateInfo pipeline_layout_info = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
      .setLayoutCount = 1,
      .pSetLayouts = &descriptor_set_layout
    };
    vik_log_check(vkCreatePipelineLayout(device, &pipeline_layout_info,
                                         nullptr, &pipeline_layout));
  }

D vitamin-k/render/vikInitializers.hpp => vitamin-k/render/vikInitializers.hpp +0 -439
@@ 1,439 0,0 @@
/*
* Initializers for Vulkan structures and objects used by the examples
* Saves lot of VK_STRUCTURE_TYPE assignments
* Some initializers are parameterized for convenience
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include <vector>
#include "vulkan/vulkan.h"

namespace vik {
namespace initializers {

inline VkMemoryAllocateInfo memoryAllocateInfo() {
  VkMemoryAllocateInfo memAllocInfo {};
  memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  return memAllocInfo;
}

inline VkMappedMemoryRange mappedMemoryRange() {
  VkMappedMemoryRange mappedMemoryRange {};
  mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
  return mappedMemoryRange;
}

inline VkCommandBufferAllocateInfo commandBufferAllocateInfo(
    VkCommandPool commandPool,
    VkCommandBufferLevel level,
    uint32_t bufferCount) {
  VkCommandBufferAllocateInfo commandBufferAllocateInfo {};
  commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  commandBufferAllocateInfo.commandPool = commandPool;
  commandBufferAllocateInfo.level = level;
  commandBufferAllocateInfo.commandBufferCount = bufferCount;
  return commandBufferAllocateInfo;
}

inline VkCommandBufferBeginInfo commandBufferBeginInfo() {
  VkCommandBufferBeginInfo cmdBufferBeginInfo {};
  cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  return cmdBufferBeginInfo;
}

inline VkRenderPassBeginInfo renderPassBeginInfo() {
  VkRenderPassBeginInfo renderPassBeginInfo {};
  renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  return renderPassBeginInfo;
}

/** @brief Initialize an image memory barrier with no image transfer ownership */
inline VkImageMemoryBarrier imageMemoryBarrier() {
  VkImageMemoryBarrier imageMemoryBarrier {};
  imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  return imageMemoryBarrier;
}

inline VkImageCreateInfo imageCreateInfo() {
  VkImageCreateInfo imageCreateInfo {};
  imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  return imageCreateInfo;
}

inline VkSamplerCreateInfo samplerCreateInfo() {
  VkSamplerCreateInfo samplerCreateInfo {};
  samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
  samplerCreateInfo.maxAnisotropy = 1.0f;
  return samplerCreateInfo;
}

inline VkImageViewCreateInfo imageViewCreateInfo() {
  VkImageViewCreateInfo imageViewCreateInfo {};
  imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  return imageViewCreateInfo;
}

inline VkSemaphoreCreateInfo semaphoreCreateInfo() {
  VkSemaphoreCreateInfo semaphoreCreateInfo {};
  semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  return semaphoreCreateInfo;
}

inline VkFenceCreateInfo fenceCreateInfo(VkFenceCreateFlags flags = 0) {
  VkFenceCreateInfo fenceCreateInfo {};
  fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
  fenceCreateInfo.flags = flags;
  return fenceCreateInfo;
}

inline VkSubmitInfo submitInfo() {
  VkSubmitInfo submitInfo {};
  submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  return submitInfo;
}

inline VkViewport viewport(
    float width,
    float height,
    float minDepth,
    float maxDepth) {
  VkViewport viewport {};
  viewport.width = width;
  viewport.height = height;
  viewport.minDepth = minDepth;
  viewport.maxDepth = maxDepth;
  return viewport;
}

inline VkRect2D rect2D(
    uint32_t width,
    uint32_t height,
    int32_t offsetX,
    int32_t offsetY) {
  VkRect2D rect2D {};
  rect2D.extent.width = width;
  rect2D.extent.height = height;
  rect2D.offset.x = offsetX;
  rect2D.offset.y = offsetY;
  return rect2D;
}

inline VkBufferCreateInfo bufferCreateInfo() {
  VkBufferCreateInfo bufCreateInfo {};
  bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  return bufCreateInfo;
}

inline VkBufferCreateInfo bufferCreateInfo(
    VkBufferUsageFlags usage,
    VkDeviceSize size) {
  VkBufferCreateInfo bufCreateInfo {};
  bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  bufCreateInfo.usage = usage;
  bufCreateInfo.size = size;
  return bufCreateInfo;
}

inline VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
    uint32_t poolSizeCount,
    VkDescriptorPoolSize* pPoolSizes,
    uint32_t maxSets) {
  VkDescriptorPoolCreateInfo descriptorPoolInfo {};
  descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  descriptorPoolInfo.poolSizeCount = poolSizeCount;
  descriptorPoolInfo.pPoolSizes = pPoolSizes;
  descriptorPoolInfo.maxSets = maxSets;
  return descriptorPoolInfo;
}

inline VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
    const std::vector<VkDescriptorPoolSize>& poolSizes,
    uint32_t maxSets) {
  VkDescriptorPoolCreateInfo descriptorPoolInfo{};
  descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  descriptorPoolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
  descriptorPoolInfo.pPoolSizes = poolSizes.data();
  descriptorPoolInfo.maxSets = maxSets;
  return descriptorPoolInfo;
}

inline VkDescriptorPoolSize descriptorPoolSize(
    VkDescriptorType type,
    uint32_t descriptorCount) {
  VkDescriptorPoolSize descriptorPoolSize {};
  descriptorPoolSize.type = type;
  descriptorPoolSize.descriptorCount = descriptorCount;
  return descriptorPoolSize;
}

inline VkDescriptorSetLayoutBinding descriptorSetLayoutBinding(
    VkDescriptorType type,
    VkShaderStageFlags stageFlags,
    uint32_t binding,
    uint32_t descriptorCount = 1) {
  VkDescriptorSetLayoutBinding setLayoutBinding {};
  setLayoutBinding.descriptorType = type;
  setLayoutBinding.stageFlags = stageFlags;
  setLayoutBinding.binding = binding;
  setLayoutBinding.descriptorCount = descriptorCount;
  return setLayoutBinding;
}

inline VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
    const VkDescriptorSetLayoutBinding* pBindings,
    uint32_t bindingCount) {
  VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo {};
  descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  descriptorSetLayoutCreateInfo.pBindings = pBindings;
  descriptorSetLayoutCreateInfo.bindingCount = bindingCount;
  return descriptorSetLayoutCreateInfo;
}

inline VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
    const std::vector<VkDescriptorSetLayoutBinding>& bindings) {
  VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
  descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  descriptorSetLayoutCreateInfo.pBindings = bindings.data();
  descriptorSetLayoutCreateInfo.bindingCount = static_cast<uint32_t>(bindings.size());
  return descriptorSetLayoutCreateInfo;
}

inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
    const VkDescriptorSetLayout* pSetLayouts,
    uint32_t setLayoutCount = 1) {
  VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo {};
  pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
  pipelineLayoutCreateInfo.pSetLayouts = pSetLayouts;
  return pipelineLayoutCreateInfo;
}

inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
    uint32_t setLayoutCount = 1) {
  VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
  pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
  return pipelineLayoutCreateInfo;
}

inline VkDescriptorSetAllocateInfo descriptorSetAllocateInfo(
    VkDescriptorPool descriptorPool,
    const VkDescriptorSetLayout* pSetLayouts,
    uint32_t descriptorSetCount) {
  VkDescriptorSetAllocateInfo descriptorSetAllocateInfo {};
  descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  descriptorSetAllocateInfo.descriptorPool = descriptorPool;
  descriptorSetAllocateInfo.pSetLayouts = pSetLayouts;
  descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
  return descriptorSetAllocateInfo;
}

inline VkDescriptorImageInfo descriptorImageInfo(VkSampler sampler, VkImageView imageView, VkImageLayout imageLayout) {
  VkDescriptorImageInfo descriptorImageInfo {};
  descriptorImageInfo.sampler = sampler;
  descriptorImageInfo.imageView = imageView;
  descriptorImageInfo.imageLayout = imageLayout;
  return descriptorImageInfo;
}

inline VkWriteDescriptorSet writeDescriptorSet(
    VkDescriptorSet dstSet,
    VkDescriptorType type,
    uint32_t binding,
    VkDescriptorBufferInfo* bufferInfo,
    uint32_t descriptorCount = 1) {
  VkWriteDescriptorSet writeDescriptorSet {};
  writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  writeDescriptorSet.dstSet = dstSet;
  writeDescriptorSet.descriptorType = type;
  writeDescriptorSet.dstBinding = binding;
  writeDescriptorSet.pBufferInfo = bufferInfo;
  writeDescriptorSet.descriptorCount = descriptorCount;
  return writeDescriptorSet;
}

inline VkWriteDescriptorSet writeDescriptorSet(
    VkDescriptorSet dstSet,
    VkDescriptorType type,
    uint32_t binding,
    VkDescriptorImageInfo *imageInfo,
    uint32_t descriptorCount = 1) {
  VkWriteDescriptorSet writeDescriptorSet {};
  writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  writeDescriptorSet.dstSet = dstSet;
  writeDescriptorSet.descriptorType = type;
  writeDescriptorSet.dstBinding = binding;
  writeDescriptorSet.pImageInfo = imageInfo;
  writeDescriptorSet.descriptorCount = descriptorCount;
  return writeDescriptorSet;
}

inline VkVertexInputBindingDescription vertexInputBindingDescription(
    uint32_t binding,
    uint32_t stride,
    VkVertexInputRate inputRate) {
  VkVertexInputBindingDescription vInputBindDescription {};
  vInputBindDescription.binding = binding;
  vInputBindDescription.stride = stride;
  vInputBindDescription.inputRate = inputRate;
  return vInputBindDescription;
}

inline VkVertexInputAttributeDescription vertexInputAttributeDescription(
    uint32_t binding,
    uint32_t location,
    VkFormat format,
    uint32_t offset) {
  VkVertexInputAttributeDescription vInputAttribDescription {};
  vInputAttribDescription.location = location;
  vInputAttribDescription.binding = binding;
  vInputAttribDescription.format = format;
  vInputAttribDescription.offset = offset;
  return vInputAttribDescription;
}

inline VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo() {
  VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo {};
  pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  return pipelineVertexInputStateCreateInfo;
}

inline VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
    VkPrimitiveTopology topology,
    VkPipelineInputAssemblyStateCreateFlags flags,
    VkBool32 primitiveRestartEnable) {
  VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo {};
  pipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  pipelineInputAssemblyStateCreateInfo.topology = topology;
  pipelineInputAssemblyStateCreateInfo.flags = flags;
  pipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = primitiveRestartEnable;
  return pipelineInputAssemblyStateCreateInfo;
}

inline VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
    VkPolygonMode polygonMode,
    VkCullModeFlags cullMode,
    VkFrontFace frontFace,
    VkPipelineRasterizationStateCreateFlags flags = 0) {
  VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo {};
  pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  pipelineRasterizationStateCreateInfo.polygonMode = polygonMode;
  pipelineRasterizationStateCreateInfo.cullMode = cullMode;
  pipelineRasterizationStateCreateInfo.frontFace = frontFace;
  pipelineRasterizationStateCreateInfo.flags = flags;
  pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
  pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
  return pipelineRasterizationStateCreateInfo;
}

inline VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
    VkColorComponentFlags colorWriteMask,
    VkBool32 blendEnable) {
  VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState {};
  pipelineColorBlendAttachmentState.colorWriteMask = colorWriteMask;
  pipelineColorBlendAttachmentState.blendEnable = blendEnable;
  return pipelineColorBlendAttachmentState;
}

inline VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
    uint32_t attachmentCount,
    const VkPipelineColorBlendAttachmentState * pAttachments) {
  VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo {};
  pipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  pipelineColorBlendStateCreateInfo.attachmentCount = attachmentCount;
  pipelineColorBlendStateCreateInfo.pAttachments = pAttachments;
  return pipelineColorBlendStateCreateInfo;
}

inline VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo(
    VkBool32 depthTestEnable,
    VkBool32 depthWriteEnable,
    VkCompareOp depthCompareOp) {
  VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo {};
  pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
  pipelineDepthStencilStateCreateInfo.depthTestEnable = depthTestEnable;
  pipelineDepthStencilStateCreateInfo.depthWriteEnable = depthWriteEnable;
  pipelineDepthStencilStateCreateInfo.depthCompareOp = depthCompareOp;
  pipelineDepthStencilStateCreateInfo.front = pipelineDepthStencilStateCreateInfo.back;
  pipelineDepthStencilStateCreateInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
  return pipelineDepthStencilStateCreateInfo;
}

inline VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
    uint32_t viewportCount,
    uint32_t scissorCount,
    VkPipelineViewportStateCreateFlags flags = 0) {
  VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo {};
  pipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  pipelineViewportStateCreateInfo.viewportCount = viewportCount;
  pipelineViewportStateCreateInfo.scissorCount = scissorCount;
  pipelineViewportStateCreateInfo.flags = flags;
  return pipelineViewportStateCreateInfo;
}

inline VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
    VkSampleCountFlagBits rasterizationSamples,
    VkPipelineMultisampleStateCreateFlags flags = 0) {
  VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo {};
  pipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  pipelineMultisampleStateCreateInfo.rasterizationSamples = rasterizationSamples;
  pipelineMultisampleStateCreateInfo.flags = flags;
  return pipelineMultisampleStateCreateInfo;
}

inline VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
    const VkDynamicState * pDynamicStates,
    uint32_t dynamicStateCount,
    VkPipelineDynamicStateCreateFlags flags = 0) {
  VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo {};
  pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
  pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates;
  pipelineDynamicStateCreateInfo.dynamicStateCount = dynamicStateCount;
  pipelineDynamicStateCreateInfo.flags = flags;
  return pipelineDynamicStateCreateInfo;
}

inline VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
    const std::vector<VkDynamicState>& pDynamicStates,
    VkPipelineDynamicStateCreateFlags flags = 0) {
  VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo{};
  pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
  pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates.data();
  pipelineDynamicStateCreateInfo.dynamicStateCount = static_cast<uint32_t>(pDynamicStates.size());
  pipelineDynamicStateCreateInfo.flags = flags;
  return pipelineDynamicStateCreateInfo;
}

inline VkGraphicsPipelineCreateInfo pipelineCreateInfo(
    VkPipelineLayout layout,
    VkRenderPass renderPass,
    VkPipelineCreateFlags flags = 0) {
  VkGraphicsPipelineCreateInfo pipelineCreateInfo {};
  pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  pipelineCreateInfo.layout = layout;
  pipelineCreateInfo.renderPass = renderPass;
  pipelineCreateInfo.flags = flags;
  pipelineCreateInfo.basePipelineIndex = -1;
  pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
  return pipelineCreateInfo;
}

inline VkPushConstantRange pushConstantRange(
    VkShaderStageFlags stageFlags,
    uint32_t size,
    uint32_t offset) {
  VkPushConstantRange pushConstantRange {};
  pushConstantRange.stageFlags = stageFlags;
  pushConstantRange.offset = offset;
  pushConstantRange.size = size;
  return pushConstantRange;
}
}  // namespace initializers
}  // namespace vik

M vitamin-k/render/vikOffscreenPass.hpp => vitamin-k/render/vikOffscreenPass.hpp +165 -112
@@ 75,25 75,35 @@ class OffscreenPass {
      FrameBufferAttachment *attachment) {
    attachment->format = format;

    VkImageCreateInfo image = initializers::imageCreateInfo();
    image.imageType = VK_IMAGE_TYPE_2D;
    image.format = format;
    image.extent.width = offScreenFrameBuf.width;
    image.extent.height = offScreenFrameBuf.height;
    image.extent.depth = 1;
    image.mipLevels = 1;
    image.arrayLayers = 1;
    image.samples = VK_SAMPLE_COUNT_1_BIT;
    image.tiling = VK_IMAGE_TILING_OPTIMAL;
    VkImageCreateInfo image = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = format,
      .extent = {
        .width = offScreenFrameBuf.width,
        .height = offScreenFrameBuf.height,
        .depth = 1
      },
      .mipLevels = 1,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL
    };

    image.usage = usage | VK_IMAGE_USAGE_SAMPLED_BIT;

    VkMemoryAllocateInfo memAlloc = initializers::memoryAllocateInfo();
    VkMemoryRequirements memReqs;

    vik_log_check(vkCreateImage(device, &image, nullptr, &attachment->image));
    vkGetImageMemoryRequirements(device, attachment->image, &memReqs);
    memAlloc.allocationSize = memReqs.size;
    memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

    VkMemoryAllocateInfo memAlloc = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      .memoryTypeIndex =
        vulkanDevice->getMemoryType(memReqs.memoryTypeBits,
                                    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
    };

    vik_log_check(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->mem));
    vik_log_check(vkBindImageMemory(device, attachment->image, attachment->mem, 0));



@@ 105,16 115,20 @@ class OffscreenPass {
    }
    assert(aspectMask > 0);

    VkImageViewCreateInfo imageView = initializers::imageViewCreateInfo();
    imageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
    imageView.format = format;
    imageView.subresourceRange = {};
    imageView.subresourceRange.aspectMask = aspectMask;
    imageView.subresourceRange.baseMipLevel = 0;
    imageView.subresourceRange.levelCount = 1;
    imageView.subresourceRange.baseArrayLayer = 0;
    imageView.subresourceRange.layerCount = 1;
    imageView.image = attachment->image;
    VkImageViewCreateInfo imageView = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = attachment->image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = format,
      .subresourceRange = {
        .aspectMask = aspectMask,
        .baseMipLevel = 0,
        .levelCount = 1,
        .baseArrayLayer = 0,
        .layerCount = 1,
      },
    };

    vik_log_check(vkCreateImageView(device, &imageView, nullptr, &attachment->view));
  }



@@ 169,91 183,114 @@ class OffscreenPass {
    std::vector<VkAttachmentReference> colorReferences;
    colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });

    VkAttachmentReference depthReference = {};
    depthReference.attachment = 1;
    depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
    VkAttachmentReference depthReference = {
      .attachment = 1,
      .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
    };

    VkSubpassDescription subpass = {};
    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.pColorAttachments = colorReferences.data();
    subpass.colorAttachmentCount = static_cast<uint32_t>(colorReferences.size());
    subpass.pDepthStencilAttachment = &depthReference;
    VkSubpassDescription subpass = {
      .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
      .colorAttachmentCount = static_cast<uint32_t>(colorReferences.size()),
      .pColorAttachments = colorReferences.data(),
      .pDepthStencilAttachment = &depthReference
    };

    // Use subpass dependencies for attachment layput transitions
    std::array<VkSubpassDependency, 2> dependencies;

    dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[0].dstSubpass = 0;
    dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    dependencies[1].srcSubpass = 0;
    dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    VkRenderPassCreateInfo renderPassInfo = {};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    renderPassInfo.pAttachments = attachmentDescs.data();
    renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size());
    renderPassInfo.subpassCount = 1;
    renderPassInfo.pSubpasses = &subpass;
    renderPassInfo.dependencyCount = 2;
    renderPassInfo.pDependencies = dependencies.data();

    vik_log_check(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offScreenFrameBuf.renderPass));
    std::array<VkSubpassDependency, 2> dependencies = {
      (VkSubpassDependency) {
        .srcSubpass = VK_SUBPASS_EXTERNAL,
        .dstSubpass = 0,
        .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      },
      (VkSubpassDependency)  {
        .srcSubpass = 0,
        .dstSubpass = VK_SUBPASS_EXTERNAL,
        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      }
    };

    VkRenderPassCreateInfo renderPassInfo = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
      .attachmentCount = static_cast<uint32_t>(attachmentDescs.size()),
      .pAttachments = attachmentDescs.data(),
      .subpassCount = 1,
      .pSubpasses = &subpass,
      .dependencyCount = 2,
      .pDependencies = dependencies.data()
    };

    vik_log_check(vkCreateRenderPass(device, &renderPassInfo,
                                     nullptr, &offScreenFrameBuf.renderPass));

    std::array<VkImageView, 2> attachments;
    attachments[0] = offScreenFrameBuf.diffuseColor.view;
    attachments[1] = offScreenFrameBuf.depth.view;

    VkFramebufferCreateInfo fbufCreateInfo = {};
    fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fbufCreateInfo.pNext = nullptr;
    fbufCreateInfo.renderPass = offScreenFrameBuf.renderPass;
    fbufCreateInfo.pAttachments = attachments.data();
    fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
    fbufCreateInfo.width = offScreenFrameBuf.width;
    fbufCreateInfo.height = offScreenFrameBuf.height;
    fbufCreateInfo.layers = 1;
    vik_log_check(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offScreenFrameBuf.frameBuffer));
    VkFramebufferCreateInfo fbufCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
      .renderPass = offScreenFrameBuf.renderPass,
      .attachmentCount = static_cast<uint32_t>(attachments.size()),
      .pAttachments = attachments.data(),
      .width = offScreenFrameBuf.width,
      .height = offScreenFrameBuf.height,
      .layers = 1
    };

    vik_log_check(vkCreateFramebuffer(device, &fbufCreateInfo,
                                      nullptr, &offScreenFrameBuf.frameBuffer));

    // Create sampler to sample from the color attachments
    VkSamplerCreateInfo sampler = initializers::samplerCreateInfo();
    sampler.magFilter = VK_FILTER_NEAREST;
    sampler.minFilter = VK_FILTER_NEAREST;
    sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    sampler.addressModeV = sampler.addressModeU;
    sampler.addressModeW = sampler.addressModeU;
    sampler.mipLodBias = 0.0f;
    sampler.maxAnisotropy = 1.0;
    sampler.minLod = 0.0f;
    sampler.maxLod = 1.0f;
    sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;

    VkSamplerCreateInfo sampler = {
      .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
      .magFilter = VK_FILTER_NEAREST,
      .minFilter = VK_FILTER_NEAREST,
      .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
      .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .mipLodBias = 0.0f,
      .maxAnisotropy = 1.0f,
      .minLod = 0.0f,
      .maxLod = 1.0f,
      .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
    };

    vik_log_check(vkCreateSampler(device, &sampler, nullptr, &colorSampler));
  }

  VkDescriptorImageInfo get_descriptor_image_info() {
    // Image descriptors for the offscreen color attachments
    return initializers::descriptorImageInfo(
          colorSampler,
          offScreenFrameBuf.diffuseColor.view,
          VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
    VkDescriptorImageInfo descriptor_info = {
          .sampler = colorSampler,
          .imageView = offScreenFrameBuf.diffuseColor.view,
          .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
        };
    return descriptor_info;
  }

  VkWriteDescriptorSet get_image_write_descriptor_set(const VkDescriptorSet& descriptorSet, VkDescriptorImageInfo *texDescriptorPosition, uint32_t binding) {
    return initializers::writeDescriptorSet(
          descriptorSet,
          VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
          binding,
          texDescriptorPosition);
  VkWriteDescriptorSet
  get_image_write_descriptor_set(const VkDescriptorSet& descriptorSet,
                                 VkDescriptorImageInfo *texDescriptorPosition,
                                 uint32_t binding) {
    return  (VkWriteDescriptorSet) {
      .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
      .dstSet = descriptorSet,
      .dstBinding = binding,
      .descriptorCount = 1,
      .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .pImageInfo = texDescriptorPosition
    };
  }

  void beginRenderPass(const VkCommandBuffer& cmdBuffer) {


@@ 262,30 299,40 @@ class OffscreenPass {
    clearValues[0].color = { { 1.0f, 1.0f, 1.0f, 1.0f } };
    clearValues[1].depthStencil = { 1.0f, 0 };

    VkRenderPassBeginInfo renderPassBeginInfo = initializers::renderPassBeginInfo();
    renderPassBeginInfo.renderPass =  offScreenFrameBuf.renderPass;
    renderPassBeginInfo.framebuffer = offScreenFrameBuf.frameBuffer;
    renderPassBeginInfo.renderArea.extent.width = offScreenFrameBuf.width;
    renderPassBeginInfo.renderArea.extent.height = offScreenFrameBuf.height;
    renderPassBeginInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
    renderPassBeginInfo.pClearValues = clearValues.data();
    VkRenderPassBeginInfo renderPassBeginInfo = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
      .renderPass =  offScreenFrameBuf.renderPass,
      .framebuffer = offScreenFrameBuf.frameBuffer,
      .renderArea = {
        .extent = {
          .width = offScreenFrameBuf.width,
          .height = offScreenFrameBuf.height
        }
      },
      .clearValueCount = static_cast<uint32_t>(clearValues.size()),
      .pClearValues = clearValues.data()
    };

    vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
    vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo,
                         VK_SUBPASS_CONTENTS_INLINE);
  }

  void setViewPortAndScissor(const VkCommandBuffer& cmdBuffer) {
    VkViewport viewport =
        initializers::viewport(
          (float)offScreenFrameBuf.width,
          (float)offScreenFrameBuf.height,
          0.0f, 1.0f);
    VkViewport viewport = {
      .width = (float)offScreenFrameBuf.width,
      .height = (float)offScreenFrameBuf.height,
      .minDepth = 0.0f,
      .maxDepth = 1.0f
    };
    vkCmdSetViewport(cmdBuffer, 0, 1, &viewport);

    VkRect2D scissor =
        initializers::rect2D(
          offScreenFrameBuf.width,
          offScreenFrameBuf.height,
          0, 0);
    VkRect2D scissor = {
      .offset = { .x = 0, .y = 0 },
      .extent = {
        .width = offScreenFrameBuf.width,
        .height = offScreenFrameBuf.height
      }
    };
    vkCmdSetScissor(cmdBuffer, 0, 1, &scissor);
  }



@@ 302,8 349,14 @@ class OffscreenPass {
    vkCmdSetViewport(cmdBuffer, 0, 2, viewports);

    VkRect2D scissorRects[2] = {
      initializers::rect2D(w / 2, h, 0, 0),
      initializers::rect2D(w / 2, h, w / 2, 0),
      {
        .offset = { .x = 0, .y = 0 },
        .extent = { .width = w / 2, .height = h }
      },
      {
        .offset = { .x = (int32_t) w / 2, .y = 0 },
        .extent = { .width = w / 2, .height = h }
      },
    };
    vkCmdSetScissor(cmdBuffer, 0, 2, scissorRects);
  }

M vitamin-k/render/vikRenderer.hpp => vitamin-k/render/vikRenderer.hpp +179 -160
@@ 21,7 21,6 @@

#include "vikSwapChain.hpp"
#include "vikDebug.hpp"
#include "vikInitializers.hpp"
#include "vikDevice.hpp"
#include "vikSwapChainVK.hpp"
#include "vikTimer.hpp"


@@ 185,11 184,12 @@ class Renderer {

    query_supported_extensions();

    VkApplicationInfo app_info = {};
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app_info.pApplicationName = name.c_str();
    app_info.pEngineName = "vitamin-k";
    app_info.apiVersion = VK_MAKE_VERSION(1, 0, 2);
    VkApplicationInfo app_info = {
      .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
      .pApplicationName = name.c_str(),
      .pEngineName = "vitamin-k",
      .apiVersion = VK_MAKE_VERSION(1, 0, 2)
    };

    std::vector<const char*> extensions;
    enable_if_supported(&extensions, VK_KHR_SURFACE_EXTENSION_NAME);


@@ 202,11 202,12 @@ class Renderer {
    if (settings->validation)
      enable_if_supported(&extensions, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);

    VkInstanceCreateInfo instance_info = {};
    instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    instance_info.pApplicationInfo = &app_info;
    instance_info.enabledExtensionCount = extensions.size();
    instance_info.ppEnabledExtensionNames = extensions.data();
    VkInstanceCreateInfo instance_info = {
      .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
      .pApplicationInfo = &app_info,
      .enabledExtensionCount = (uint32_t) extensions.size(),
      .ppEnabledExtensionNames = extensions.data()
    };

    if (settings->validation) {
      instance_info.enabledLayerCount = debug::validationLayerCount;


@@ 218,14 219,15 @@ class Renderer {

  void create_frame_buffer(VkFramebuffer *frame_buffer,
                           const std::vector<VkImageView> &attachments) {
    VkFramebufferCreateInfo frame_buffer_info = {};
    frame_buffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    frame_buffer_info.renderPass = render_pass;
    frame_buffer_info.attachmentCount = attachments.size();
    frame_buffer_info.pAttachments = attachments.data();
    frame_buffer_info.width = width;
    frame_buffer_info.height = height;
    frame_buffer_info.layers = 1;
    VkFramebufferCreateInfo frame_buffer_info = {
      .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
      .renderPass = render_pass,
      .attachmentCount = (uint32_t) attachments.size(),
      .pAttachments = attachments.data(),
      .width = width,
      .height = height,
      .layers = 1
    };

    vik_log_check(vkCreateFramebuffer(device,
                                      &frame_buffer_info,


@@ 240,11 242,12 @@ class Renderer {
    // Create one command buffer for each swap chain image and reuse for rendering
    cmd_buffers.resize(count);

    VkCommandBufferAllocateInfo cmd_buffer_info =
        initializers::commandBufferAllocateInfo(
          cmd_pool,
          VK_COMMAND_BUFFER_LEVEL_PRIMARY,
          count);
    VkCommandBufferAllocateInfo cmd_buffer_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .commandPool = cmd_pool,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = count
    };

    vik_log_check(vkAllocateCommandBuffers(device,
                                           &cmd_buffer_info,


@@ 299,11 302,12 @@ class Renderer {
  VkCommandBuffer create_command_buffer() {
    VkCommandBuffer cmd_buffer;

    VkCommandBufferAllocateInfo cmd_buffer_info =
        initializers::commandBufferAllocateInfo(
          cmd_pool,
          VK_COMMAND_BUFFER_LEVEL_PRIMARY,
          1);
    VkCommandBufferAllocateInfo cmd_buffer_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .commandPool = cmd_pool,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = 1
    };

    vik_log_check(vkAllocateCommandBuffers(device,
                                           &cmd_buffer_info,


@@ 313,8 317,9 @@ class Renderer {
  }

  void create_pipeline_cache() {
    VkPipelineCacheCreateInfo pipeline_cache_info = {};
    pipeline_cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
    VkPipelineCacheCreateInfo pipeline_cache_info = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO
    };
    vik_log_check(vkCreatePipelineCache(device, &pipeline_cache_info,
                                        nullptr, &pipeline_cache));
  }


@@ 483,7 488,9 @@ class Renderer {

  virtual void init_semaphores() {
    // Create synchronization objects
    VkSemaphoreCreateInfo semaphore_info = initializers::semaphoreCreateInfo();
    VkSemaphoreCreateInfo semaphore_info = {
      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
    };
    // Create a semaphore used to synchronize image presentation
    // Ensures that the image is displayed before we start submitting new commands to the queu
    vik_log_check(vkCreateSemaphore(device, &semaphore_info, nullptr, &semaphores.present_complete));


@@ 493,10 500,12 @@ class Renderer {
  }

  void create_command_pool(uint32_t index) {
    VkCommandPoolCreateInfo cmd_pool_info = {};
    cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    cmd_pool_info.queueFamilyIndex = index;
    cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    VkCommandPoolCreateInfo cmd_pool_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
      .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
      .queueFamilyIndex = index
    };

    vik_log_check(vkCreateCommandPool(device, &cmd_pool_info,
                                      nullptr, &cmd_pool));
  }


@@ 550,17 559,19 @@ class Renderer {

    // The submit info structure specifices a
    // command buffer queue submission batch
    VkSubmitInfo submit_info = initializers::submitInfo();

    // Pointer to the list of pipeline stages that the semaphore waits will occur at
    // submit_info.pWaitDstStageMask = stage_flags.data();
    submit_info.waitSemaphoreCount = 1;
    // Semaphore(s) to wait upon before the submitted command buffer starts executing
    submit_info.pWaitSemaphores = &semaphores.present_complete;
    submit_info.signalSemaphoreCount = 1;
    // Semaphore(s) to be signaled when command buffers have completed
    submit_info.pSignalSemaphores = &semaphores.render_complete;
    submit_info.commandBufferCount = 1;
    VkSubmitInfo submit_info = {
      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
      // Pointer to the list of pipeline stages that the semaphore waits will occur at
      // submit_info.pWaitDstStageMask = stage_flags.data();
      .waitSemaphoreCount = 1,
      // Semaphore(s) to wait upon before the submitted command buffer starts executing
      .pWaitSemaphores = &semaphores.present_complete,
      .commandBufferCount = 1,
      .signalSemaphoreCount = 1,
      // Semaphore(s) to be signaled when command buffers have completed
      .pSignalSemaphores = &semaphores.render_complete
    };

    return submit_info;
  }



@@ 583,140 594,148 @@ class Renderer {
  }

  void create_render_pass() {
    std::array<VkAttachmentDescription, 2> attachments = {};
    // Color attachment
    attachments[0].format = window->get_swap_chain()->surface_format.format;
    attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
    attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
    // Depth attachment
    attachments[1].format = depth_format;
    attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
    attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkAttachmentReference color_reference = {};
    color_reference.attachment = 0;
    color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    VkAttachmentReference depth_reference = {};
    depth_reference.attachment = 1;
    depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkSubpassDescription subpass_description = {};
    subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass_description.colorAttachmentCount = 1;
    subpass_description.pColorAttachments = &color_reference;
    subpass_description.pDepthStencilAttachment = &depth_reference;
    subpass_description.inputAttachmentCount = 0;
    subpass_description.pInputAttachments = nullptr;
    subpass_description.preserveAttachmentCount = 0;
    subpass_description.pPreserveAttachments = nullptr;
    subpass_description.pResolveAttachments = nullptr;
    std::array<VkAttachmentDescription, 2> attachments = {
      (VkAttachmentDescription) {
        // Color attachment
        .format = window->get_swap_chain()->surface_format.format,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
      },
      (VkAttachmentDescription) {
        // Depth attachment
        .format = depth_format,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
      }
    };

    VkAttachmentReference color_reference = {
      .attachment = 0,
      .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    };

    VkAttachmentReference depth_reference = {
      .attachment = 1,
      .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
    };

    VkSubpassDescription subpass_description = {
      .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
      .colorAttachmentCount = 1,
      .pColorAttachments = &color_reference,
      .pDepthStencilAttachment = &depth_reference
    };

    // Subpass dependencies for layout transitions
    std::array<VkSubpassDependency, 2> dependencies;

    dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[0].dstSubpass = 0;
    dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    dependencies[1].srcSubpass = 0;
    dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    VkRenderPassCreateInfo render_pass_info = {};
    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    render_pass_info.attachmentCount = static_cast<uint32_t>(attachments.size());
    render_pass_info.pAttachments = attachments.data();
    render_pass_info.subpassCount = 1;
    render_pass_info.pSubpasses = &subpass_description;
    render_pass_info.dependencyCount = static_cast<uint32_t>(dependencies.size());
    render_pass_info.pDependencies = dependencies.data();
    std::array<VkSubpassDependency, 2> dependencies = {
      (VkSubpassDependency) {
        .srcSubpass = VK_SUBPASS_EXTERNAL,
        .dstSubpass = 0,
        .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      },
      (VkSubpassDependency) {
        .srcSubpass = 0,
        .dstSubpass = VK_SUBPASS_EXTERNAL,
        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      }
    };

    VkRenderPassCreateInfo render_pass_info = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
      .attachmentCount = static_cast<uint32_t>(attachments.size()),
      .pAttachments = attachments.data(),
      .subpassCount = 1,
      .pSubpasses = &subpass_description,
      .dependencyCount = static_cast<uint32_t>(dependencies.size()),
      .pDependencies = dependencies.data()
    };

#if 0
    // VK_KHX_multiview
    /*
    VkRenderPassMultiviewCreateInfoKHX renderPassMvInfo = {};
    renderPassMvInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX;
    renderPassMvInfo.subpassCount = 1;
    renderPassMvInfo.dependencyCount = 1;
    renderPassMvInfo.correlationMaskCount = 1;
    renderPassMvInfo.pNext = NULL;

    uint32_t correlationMasks[] = { 2 };
    renderPassMvInfo.pCorrelationMasks = correlationMasks;

    uint32_t viewMasks[] = { 1 };
    renderPassMvInfo.pViewMasks = viewMasks;

    int32_t viewOffsets[] = { 2 };
    renderPassMvInfo.pViewOffsets = viewOffsets;

    renderPassInfo.pNext = &renderPassMvInfo;
    renderPassInfo.pNext = NULL;
    */
    // VK_KHX_multiview
    VkRenderPassMultiviewCreateInfo renderPassMvInfo = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO,
      .subpassCount = 1,
      .dependencyCount = 1,
      .correlationMaskCount = 1,
      .pCorrelationMasks = correlationMasks,
      .pViewMasks = viewMasks,
      .pViewOffsets = viewOffsets,
    };
    render_pass_info.pNext = &renderPassMvInfo;
#endif

    vik_log_check(vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass));
    vik_log_check(vkCreateRenderPass(device, &render_pass_info,
                                     nullptr, &render_pass));
  }

  void init_depth_stencil() {
    VkImageCreateInfo image = {};
    image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    image.imageType = VK_IMAGE_TYPE_2D;
    image.format = depth_format;
    image.extent = { width, height, 1 };
    image.mipLevels = 1;
    image.arrayLayers = 1;
    image.samples = VK_SAMPLE_COUNT_1_BIT;
    image.tiling = VK_IMAGE_TILING_OPTIMAL;
    image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    image.flags = 0;

    VkMemoryAllocateInfo mem_alloc = {};
    mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    mem_alloc.allocationSize = 0;
    mem_alloc.memoryTypeIndex = 0;

    VkImageViewCreateInfo depthStencilView = {};
    depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
    depthStencilView.format = depth_format;
    depthStencilView.flags = 0;
    depthStencilView.subresourceRange = {};
    depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
    depthStencilView.subresourceRange.baseMipLevel = 0;
    depthStencilView.subresourceRange.levelCount = 1;
    depthStencilView.subresourceRange.baseArrayLayer = 0;
    depthStencilView.subresourceRange.layerCount = 1;
    VkImageCreateInfo image = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = depth_format,
      .extent = {
        .width = width,
        .height = height,
        .depth = 1
      },
      .mipLevels = 1,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL,
      .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
    };

    VkMemoryRequirements memReqs;

    vik_log_check(vkCreateImage(device, &image, nullptr, &depth_stencil.image));
    vkGetImageMemoryRequirements(device, depth_stencil.image, &memReqs);
    mem_alloc.allocationSize = memReqs.size;
    mem_alloc.memoryTypeIndex = vik_device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

    VkMemoryAllocateInfo mem_alloc = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      .memoryTypeIndex = vik_device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
    };

    vik_log_check(vkAllocateMemory(device, &mem_alloc, nullptr, &depth_stencil.mem));
    vik_log_check(vkBindImageMemory(device, depth_stencil.image, depth_stencil.mem, 0));

    depthStencilView.image = depth_stencil.image;
    VkImageViewCreateInfo depthStencilView = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = depth_stencil.image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = depth_format,
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
        .baseMipLevel = 0,
        .levelCount = 1,
        .baseArrayLayer = 0,
        .layerCount = 1
      }
    };
    vik_log_check(vkCreateImageView(device, &depthStencilView, nullptr, &depth_stencil.view));
  }


M vitamin-k/render/vikRendererTextOverlay.hpp => vitamin-k/render/vikRendererTextOverlay.hpp +14 -19
@@ 61,20 61,6 @@ class RendererTextOverlay : public Renderer {
    text_overlay->set_update_cb(cb);
  }

  VkSubmitInfo init_text_submit_info() {
    // Wait for color attachment output to finish before rendering the text overlay
    // VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;

    VkSubmitInfo submit_info = initializers::submitInfo();
    // submit_info.pWaitDstStageMask = &stageFlags;
    submit_info.waitSemaphoreCount = 1;
    submit_info.pWaitSemaphores = &semaphores.render_complete;
    submit_info.signalSemaphoreCount = 1;
    submit_info.pSignalSemaphores = &text_overlay_complete;
    submit_info.commandBufferCount = 1;
    return submit_info;
  }

  void update_text_overlay() {
    if (!settings->enable_text_overlay)
      return;


@@ 91,11 77,18 @@ class RendererTextOverlay : public Renderer {
  }

  void submit_text_overlay() {
    VkSubmitInfo submit_info = init_text_submit_info();
    submit_info.pCommandBuffers = &text_overlay->cmdBuffers[current_buffer];

    VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    submit_info.pWaitDstStageMask = &stageFlags;

    VkSubmitInfo submit_info = {
      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
      .waitSemaphoreCount = 1,
      .pWaitSemaphores = &semaphores.render_complete,
      .pWaitDstStageMask = &stageFlags,
      .commandBufferCount = 1,
      .pCommandBuffers = &text_overlay->cmdBuffers[current_buffer],
      .signalSemaphoreCount = 1,
      .pSignalSemaphores = &text_overlay_complete
    };

    vik_log_check(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
  }


@@ 133,7 126,9 @@ class RendererTextOverlay : public Renderer {

  void init_semaphores() {
    Renderer::init_semaphores();
     VkSemaphoreCreateInfo semaphore_info = initializers::semaphoreCreateInfo();
    VkSemaphoreCreateInfo semaphore_info = {
      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
    };
    // Create a semaphore used to synchronize command submission
    // Ensures that the image is not presented until all commands for the text overlay have been sumbitted and executed
    // Will be inserted after the render complete semaphore if the text overlay is enabled

M vitamin-k/render/vikShader.hpp => vitamin-k/render/vikShader.hpp +17 -12
@@ 22,14 22,17 @@ namespace vik {
class Shader {
 public:
  static VkPipelineShaderStageCreateInfo load(const VkDevice& device, std::string fileName, VkShaderStageFlagBits stage) {
    VkPipelineShaderStageCreateInfo shaderStage = {};
    shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    shaderStage.stage = stage;

    std::string path = Assets::get_shader_path() + fileName;
    shaderStage.module = load(path.c_str(), device);
    shaderStage.pName = "main";
    assert(shaderStage.module != VK_NULL_HANDLE);
    VkShaderModule module = load(path.c_str(), device);
    assert(module != VK_NULL_HANDLE);

    VkPipelineShaderStageCreateInfo shaderStage = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
      .stage = stage,
      .module = module,
      .pName = "main"
    };

    return shaderStage;
  }



@@ 46,12 49,14 @@ class Shader {
      assert(size > 0);

      VkShaderModule shaderModule;
      VkShaderModuleCreateInfo moduleCreateInfo{};
      moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
      moduleCreateInfo.codeSize = size;
      moduleCreateInfo.pCode = (uint32_t*)shaderCode;
      VkShaderModuleCreateInfo moduleCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
        .codeSize = size,
        .pCode = (uint32_t*)shaderCode
      };

      vik_log_check(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
      vik_log_check(vkCreateShaderModule(device, &moduleCreateInfo,
                                         NULL, &shaderModule));

      // vik_log_d("Creating shader module %p (%s)", shaderModule, fileName);


M vitamin-k/render/vikSwapChain.hpp => vitamin-k/render/vikSwapChain.hpp +18 -17
@@ 76,24 76,25 @@ class SwapChain {

  void create_image_view(const VkDevice &device, const VkImage& image,
                         const VkFormat &format, VkImageView *view) {
    VkImageViewCreateInfo view_create_info = {};
    view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    view_create_info.pNext = NULL;
    view_create_info.format = format;
    view_create_info.components = {
      VK_COMPONENT_SWIZZLE_R,
      VK_COMPONENT_SWIZZLE_G,
      VK_COMPONENT_SWIZZLE_B,
      VK_COMPONENT_SWIZZLE_A
    VkImageViewCreateInfo view_create_info = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = format,
      .components = {
        .r = VK_COMPONENT_SWIZZLE_R,
        .g = VK_COMPONENT_SWIZZLE_G,
        .b = VK_COMPONENT_SWIZZLE_B,
        .a = VK_COMPONENT_SWIZZLE_A
      },
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        .levelCount = 1,
        .baseArrayLayer = 0,
        .layerCount = 1,
      }
    };
    view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    view_create_info.subresourceRange.baseMipLevel = 0;
    view_create_info.subresourceRange.levelCount = 1;
    view_create_info.subresourceRange.baseArrayLayer = 0;
    view_create_info.subresourceRange.layerCount = 1;
    view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
    view_create_info.flags = 0;
    view_create_info.image = image;

    vik_log_check(vkCreateImageView(device, &view_create_info, nullptr, view));
  }

M vitamin-k/render/vikSwapChainDRM.hpp => vitamin-k/render/vikSwapChainDRM.hpp +1 -1
@@ 26,7 26,7 @@

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_fourcc.h>
#include <libdrm/drm_fourcc.h>

#include <sys/ioctl.h>
#include <sys/stat.h>

M vitamin-k/render/vikSwapChainVK.hpp => vitamin-k/render/vikSwapChainVK.hpp +33 -28
@@ 53,36 53,38 @@ class SwapChainVK : public SwapChain {
    VkSurfaceCapabilitiesKHR surfCaps;
    vik_log_check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfCaps));

    VkSwapchainCreateInfoKHR swap_chain_info = {};
    swap_chain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;

    swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
    swap_chain_info.imageArrayLayers = 1;
    swap_chain_info.queueFamilyIndexCount = 0;
    // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
    swap_chain_info.clipped = VK_TRUE;

    swap_chain_info.surface = surface;
    swap_chain_info.imageFormat = surface_format.format;
    swap_chain_info.imageColorSpace = surface_format.colorSpace;

    VkSwapchainKHR oldSwapchain = swap_chain;
    swap_chain_info.oldSwapchain = oldSwapchain;

    VkExtent2D swapchainExtent = select_extent(surfCaps, width, height);
    swap_chain_info.imageExtent = { swapchainExtent.width, swapchainExtent.height };

    swap_chain_info.minImageCount = select_image_count(surfCaps);
    swap_chain_info.preTransform = select_transform_flags(surfCaps);
    swap_chain_info.presentMode = select_present_mode();
    swap_chain_info.compositeAlpha = select_composite_alpha(surfCaps);
    VkSwapchainCreateInfoKHR swap_chain_info = {
      .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
      .surface = surface,
      .minImageCount = select_image_count(surfCaps),
      .imageFormat = surface_format.format,
      .imageColorSpace = surface_format.colorSpace,
      .imageExtent = {
        .width = swapchainExtent.width,
        .height = swapchainExtent.height
      },
      .imageArrayLayers = 1,
      .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
      .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .queueFamilyIndexCount = 0,
      .preTransform = select_transform_flags(surfCaps),
      .compositeAlpha = select_composite_alpha(surfCaps),
      .presentMode = select_present_mode(),
      // Setting clipped to VK_TRUE allows the implementation
      // to discard rendering outside of the surface area
      .clipped = VK_TRUE,
      .oldSwapchain = oldSwapchain
    };

    // Set additional usage flag for blitting from the swapchain images if supported
    if (is_blit_supported())
      swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;

    vik_log_check(vkCreateSwapchainKHR(device, &swap_chain_info, nullptr, &swap_chain));
    vik_log_check(vkCreateSwapchainKHR(device, &swap_chain_info,
                                       nullptr, &swap_chain));

    // If an existing swap chain is re-created, destroy the old swap chain
    // This also cleans up all the presentable images


@@ 200,12 202,15 @@ class SwapChainVK : public SwapChain {
  *
  * @return VkResult of the queue presentation
  */
  VkResult present(VkQueue queue, uint32_t index, VkSemaphore semaphore = VK_NULL_HANDLE) {
    VkPresentInfoKHR presentInfo = {};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = &swap_chain;
    presentInfo.pImageIndices = &index;
  VkResult present(VkQueue queue, uint32_t index,
                   VkSemaphore semaphore = VK_NULL_HANDLE) {
    VkPresentInfoKHR presentInfo = {
      .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
      .swapchainCount = 1,
      .pSwapchains = &swap_chain,
      .pImageIndices = &index,
    };

    // Check if a wait semaphore has been specified to wait for before presenting the image
    if (semaphore != VK_NULL_HANDLE) {
      presentInfo.pWaitSemaphores = &semaphore;

M vitamin-k/render/vikTextOverlay.hpp => vitamin-k/render/vikTextOverlay.hpp +374 -256
@@ 166,17 166,20 @@ class TextOverlay {
    // Command buffer

    // Pool
    VkCommandPoolCreateInfo cmdPoolInfo = {};
    cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics;
    cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    vik_log_check(vkCreateCommandPool(vulkanDevice->logicalDevice, &cmdPoolInfo, nullptr, &commandPool));

    VkCommandBufferAllocateInfo cmdBufAllocateInfo =
        initializers::commandBufferAllocateInfo(
          commandPool,
          VK_COMMAND_BUFFER_LEVEL_PRIMARY,
          (uint32_t)cmdBuffers.size());
    VkCommandPoolCreateInfo cmdPoolInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
      .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
      .queueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics
    };
    vik_log_check(vkCreateCommandPool(vulkanDevice->logicalDevice, &cmdPoolInfo,
                                      nullptr, &commandPool));

    VkCommandBufferAllocateInfo cmdBufAllocateInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .commandPool = commandPool,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = (uint32_t)cmdBuffers.size()
    };

    vik_log_check(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data()));



@@ 191,26 194,35 @@ class TextOverlay {
    vertexBuffer.map();

    // Font texture
    VkImageCreateInfo imageInfo = initializers::imageCreateInfo();
    imageInfo.imageType = VK_IMAGE_TYPE_2D;
    imageInfo.format = VK_FORMAT_R8_UNORM;
    imageInfo.extent.width = STB_FONT_WIDTH;
    imageInfo.extent.height = STB_FONT_HEIGHT;
    imageInfo.extent.depth = 1;
    imageInfo.mipLevels = 1;
    imageInfo.arrayLayers = 1;
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
    VkImageCreateInfo imageInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = VK_FORMAT_R8_UNORM,
      .extent = {
        .width = STB_FONT_WIDTH,
        .height = STB_FONT_HEIGHT,
        .depth = 1,
      },
      .mipLevels = 1,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL,
      .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
    };

    vik_log_check(vkCreateImage(vulkanDevice->logicalDevice, &imageInfo, nullptr, &image));

    VkMemoryRequirements memReqs;
    VkMemoryAllocateInfo allocInfo = initializers::memoryAllocateInfo();
    vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, image, &memReqs);
    allocInfo.allocationSize = memReqs.size;
    allocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

    VkMemoryAllocateInfo allocInfo {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      .memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
    };

    vik_log_check(vkAllocateMemory(vulkanDevice->logicalDevice, &allocInfo, nullptr, &imageMemory));
    vik_log_check(vkBindImageMemory(vulkanDevice->logicalDevice, image, imageMemory, 0));



@@ 233,7 245,9 @@ class TextOverlay {
    cmdBufAllocateInfo.commandBufferCount = 1;
    vik_log_check(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, &copyCmd));

    VkCommandBufferBeginInfo cmdBufInfo = initializers::commandBufferBeginInfo();
    VkCommandBufferBeginInfo cmdBufInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
    };
    vik_log_check(vkBeginCommandBuffer(copyCmd, &cmdBufInfo));

    // Prepare for transfer


@@ 244,13 258,18 @@ class TextOverlay {
          VK_IMAGE_LAYOUT_PREINITIALIZED,
          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);

    VkBufferImageCopy bufferCopyRegion = {};
    bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    bufferCopyRegion.imageSubresource.mipLevel = 0;
    bufferCopyRegion.imageSubresource.layerCount = 1;
    bufferCopyRegion.imageExtent.width = STB_FONT_WIDTH;
    bufferCopyRegion.imageExtent.height = STB_FONT_HEIGHT;
    bufferCopyRegion.imageExtent.depth = 1;
    VkBufferImageCopy bufferCopyRegion = {
      .imageSubresource = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .mipLevel = 0,
        .layerCount = 1,
      },
      .imageExtent = {
        .width = STB_FONT_WIDTH,
        .height = STB_FONT_HEIGHT,
        .depth = 1
      },
    };

    vkCmdCopyBufferToImage(
          copyCmd,


@@ 270,9 289,11 @@ class TextOverlay {

    vik_log_check(vkEndCommandBuffer(copyCmd));

    VkSubmitInfo submitInfo = initializers::submitInfo();
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &copyCmd;
    VkSubmitInfo submitInfo = {
      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
      .commandBufferCount = 1,
      .pCommandBuffers = &copyCmd
    };

    vik_log_check(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
    vik_log_check(vkQueueWaitIdle(queue));


@@ 281,79 302,113 @@ class TextOverlay {

    vkFreeCommandBuffers(vulkanDevice->logicalDevice, commandPool, 1, &copyCmd);

    VkImageViewCreateInfo imageViewInfo = initializers::imageViewCreateInfo();
    imageViewInfo.image = image;
    imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    imageViewInfo.format = imageInfo.format;
    imageViewInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
    imageViewInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
    VkImageViewCreateInfo imageViewInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = imageInfo.format,
      .components = {
        .r = VK_COMPONENT_SWIZZLE_R,
        .g = VK_COMPONENT_SWIZZLE_G,
        .b = VK_COMPONENT_SWIZZLE_B,
        .a = VK_COMPONENT_SWIZZLE_A
      },
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        .levelCount = 1,
        .baseArrayLayer = 0,
        .layerCount = 1
      }
    };

    vik_log_check(vkCreateImageView(vulkanDevice->logicalDevice, &imageViewInfo, nullptr, &view));

    // Sampler
    VkSamplerCreateInfo samplerInfo = initializers::samplerCreateInfo();
    samplerInfo.magFilter = VK_FILTER_LINEAR;
    samplerInfo.minFilter = VK_FILTER_LINEAR;
    samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.mipLodBias = 0.0f;
    samplerInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerInfo.minLod = 0.0f;
    samplerInfo.maxLod = 1.0f;
    samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
    VkSamplerCreateInfo samplerInfo = {
      .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
      .magFilter = VK_FILTER_LINEAR,
      .minFilter = VK_FILTER_LINEAR,
      .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
      .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .mipLodBias = 0.0f,
      .maxAnisotropy = 1.0f,
      .compareOp = VK_COMPARE_OP_NEVER,
      .minLod = 0.0f,
      .maxLod = 1.0f,
      .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
    };

    vik_log_check(vkCreateSampler(vulkanDevice->logicalDevice, &samplerInfo, nullptr, &sampler));

    // Descriptor
    // Font uses a separate descriptor pool
    std::array<VkDescriptorPoolSize, 1> poolSizes;
    poolSizes[0] = initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1);

    VkDescriptorPoolCreateInfo descriptorPoolInfo =
        initializers::descriptorPoolCreateInfo(
          static_cast<uint32_t>(poolSizes.size()),
          poolSizes.data(),
          1);
    std::vector<VkDescriptorPoolSize> poolSizes = {{
      .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .descriptorCount = 1
    }};

    VkDescriptorPoolCreateInfo descriptorPoolInfo = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
      .maxSets = 1,
      .poolSizeCount = static_cast<uint32_t>(poolSizes.size()),
      .pPoolSizes = poolSizes.data()
    };

    vik_log_check(vkCreateDescriptorPool(vulkanDevice->logicalDevice, &descriptorPoolInfo, nullptr, &descriptorPool));

    // Descriptor set layout
    std::array<VkDescriptorSetLayoutBinding, 1> setLayoutBindings;
    setLayoutBindings[0] = initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0);

    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo =
        initializers::descriptorSetLayoutCreateInfo(
          setLayoutBindings.data(),
          static_cast<uint32_t>(setLayoutBindings.size()));

    std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {{
      .binding = 0,
      .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .descriptorCount = 1,
      .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
    }};

    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
      .bindingCount = static_cast<uint32_t>(setLayoutBindings.size()),
      .pBindings = setLayoutBindings.data()
    };
    vik_log_check(vkCreateDescriptorSetLayout(vulkanDevice->logicalDevice, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout));

    // Pipeline layout
    VkPipelineLayoutCreateInfo pipelineLayoutInfo =
        initializers::pipelineLayoutCreateInfo(
          &descriptorSetLayout,
          1);

    VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
      .setLayoutCount = 1,
      .pSetLayouts = &descriptorSetLayout
    };
    vik_log_check(vkCreatePipelineLayout(vulkanDevice->logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout));

    // Descriptor set
    VkDescriptorSetAllocateInfo descriptorSetAllocInfo =
        initializers::descriptorSetAllocateInfo(
          descriptorPool,
          &descriptorSetLayout,
          1);
    VkDescriptorSetAllocateInfo descriptorSetAllocInfo = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
      .descriptorPool = descriptorPool,
      .descriptorSetCount = 1,
      .pSetLayouts = &descriptorSetLayout
    };

    vik_log_check(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &descriptorSetAllocInfo, &descriptorSet));

    VkDescriptorImageInfo texDescriptor =
        initializers::descriptorImageInfo(
          sampler,
          view,
          VK_IMAGE_LAYOUT_GENERAL);
    VkDescriptorImageInfo texDescriptor = {
      .sampler = sampler,
      .imageView = view,
      .imageLayout = VK_IMAGE_LAYOUT_GENERAL
    };

    std::array<VkWriteDescriptorSet, 1> writeDescriptorSets;
    writeDescriptorSets[0] = initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texDescriptor);
    vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
    std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
      (VkWriteDescriptorSet) {
        .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
        .dstSet = descriptorSet,
        .dstBinding = 0,
        .descriptorCount = 1,
        .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
        .pImageInfo = &texDescriptor
      }
    };
    vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);

    // Pipeline cache
    VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};


@@ 361,7 416,9 @@ class TextOverlay {
    vik_log_check(vkCreatePipelineCache(vulkanDevice->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache));

    // Command buffer execution fence
    VkFenceCreateInfo fenceCreateInfo = initializers::fenceCreateInfo();
    VkFenceCreateInfo fenceCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
    };
    vik_log_check(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &fence));
  }



@@ 369,94 426,132 @@ class TextOverlay {
  * Prepare a separate pipeline for the font rendering decoupled from the main application
  */
  void preparePipeline() {
    VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
        initializers::pipelineInputAssemblyStateCreateInfo(
          VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
          0,
          VK_FALSE);

    VkPipelineRasterizationStateCreateInfo rasterizationState =
        initializers::pipelineRasterizationStateCreateInfo(
          VK_POLYGON_MODE_FILL,
          VK_CULL_MODE_BACK_BIT,
          VK_FRONT_FACE_CLOCKWISE,
          0);
    VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
      .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
      .primitiveRestartEnable = VK_FALSE
    };

    VkPipelineRasterizationStateCreateInfo rasterizationState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
      .depthClampEnable = VK_FALSE,
      .polygonMode = VK_POLYGON_MODE_FILL,
      .cullMode = VK_CULL_MODE_BACK_BIT,
      .frontFace = VK_FRONT_FACE_CLOCKWISE,
      .lineWidth = 1.0f
    };

    // Enable blending
    VkPipelineColorBlendAttachmentState blendAttachmentState =
        initializers::pipelineColorBlendAttachmentState(0xf, VK_TRUE);

    blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
    blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
    blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
    blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
    blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
    blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
    blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;

    VkPipelineColorBlendStateCreateInfo colorBlendState =
        initializers::pipelineColorBlendStateCreateInfo(
          1,
          &blendAttachmentState);
    VkPipelineColorBlendAttachmentState blendAttachmentState = {
      .blendEnable = VK_TRUE,
      .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
      .dstColorBlendFactor = VK_BLEND_FACTOR_ONE,
      .colorBlendOp = VK_BLEND_OP_ADD,
      .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
      .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
      .alphaBlendOp = VK_BLEND_OP_ADD,
      .colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
                        VK_COLOR_COMPONENT_G_BIT |
                        VK_COLOR_COMPONENT_B_BIT |
                        VK_COLOR_COMPONENT_A_BIT
    };

    VkPipelineColorBlendStateCreateInfo colorBlendState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
      .attachmentCount = 1,
      .pAttachments = &blendAttachmentState
    };

    VkPipelineDepthStencilStateCreateInfo depthStencilState =
        initializers::pipelineDepthStencilStateCreateInfo(
          VK_FALSE,
          VK_FALSE,
          VK_COMPARE_OP_LESS_OR_EQUAL);
    VkPipelineDepthStencilStateCreateInfo depthStencilState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
      .depthTestEnable = VK_FALSE,
      .depthWriteEnable = VK_FALSE,
      .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
      .front = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      },
      .back = {
        .compareOp = VK_COMPARE_OP_ALWAYS
      }
    };

    VkPipelineViewportStateCreateInfo viewportState =
        initializers::pipelineViewportStateCreateInfo(1, 1, 0);
    VkPipelineViewportStateCreateInfo viewportState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
      .viewportCount = 1,
      .scissorCount = 1
    };

    VkPipelineMultisampleStateCreateInfo multisampleState =
        initializers::pipelineMultisampleStateCreateInfo(
          VK_SAMPLE_COUNT_1_BIT,
          0);
    VkPipelineMultisampleStateCreateInfo multisampleState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
      .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
    };

    std::vector<VkDynamicState> dynamicStateEnables = {
      VK_DYNAMIC_STATE_VIEWPORT,
      VK_DYNAMIC_STATE_SCISSOR
    };

    VkPipelineDynamicStateCreateInfo dynamicState =
        initializers::pipelineDynamicStateCreateInfo(
          dynamicStateEnables.data(),
          static_cast<uint32_t>(dynamicStateEnables.size()),
          0);

    std::array<VkVertexInputBindingDescription, 2> vertexBindings = {};
    vertexBindings[0] = initializers::vertexInputBindingDescription(0, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX);
    vertexBindings[1] = initializers::vertexInputBindingDescription(1, sizeof(glm::vec4), VK_VERTEX_INPUT_RATE_VERTEX);

    std::array<VkVertexInputAttributeDescription, 2> vertexAttribs = {};
    // Position
    vertexAttribs[0] = initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
    // UV
    vertexAttribs[1] = initializers::vertexInputAttributeDescription(1, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(glm::vec2));

    VkPipelineVertexInputStateCreateInfo inputState = initializers::pipelineVertexInputStateCreateInfo();
    inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexBindings.size());
    inputState.pVertexBindingDescriptions = vertexBindings.data();
    inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttribs.size());
    inputState.pVertexAttributeDescriptions = vertexAttribs.data();

    VkGraphicsPipelineCreateInfo pipelineCreateInfo =
        initializers::pipelineCreateInfo(
          pipelineLayout,
          renderPass,
          0);

    pipelineCreateInfo.pVertexInputState = &inputState;
    pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
    pipelineCreateInfo.pRasterizationState = &rasterizationState;
    pipelineCreateInfo.pColorBlendState = &colorBlendState;
    pipelineCreateInfo.pMultisampleState = &multisampleState;
    pipelineCreateInfo.pViewportState = &viewportState;
    pipelineCreateInfo.pDepthStencilState = &depthStencilState;
    pipelineCreateInfo.pDynamicState = &dynamicState;
    pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
    pipelineCreateInfo.pStages = shaderStages.data();
    VkPipelineDynamicStateCreateInfo dynamicState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
      .dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size()),
      .pDynamicStates = dynamicStateEnables.data()
    };

    std::vector<VkVertexInputBindingDescription> vertexBindings = {
      {
        .binding = 0,
        .stride = sizeof(glm::vec4),
        .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
      },
      {
        .binding = 1,
        .stride = sizeof(glm::vec4),
        .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
      }
    };

    std::vector<VkVertexInputAttributeDescription> vertexAttribs = {
      // Position
      {
        .location = 0,
        .binding = 0,
        .format = VK_FORMAT_R32G32_SFLOAT,
        .offset = 0
      },
      // UV
      {
        .location = 1,
        .binding = 1,
        .format = VK_FORMAT_R32G32_SFLOAT,
        .offset = sizeof(glm::vec2)
      }
    };

    VkPipelineVertexInputStateCreateInfo inputState = {
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
      .vertexBindingDescriptionCount = static_cast<uint32_t>(vertexBindings.size()),
      .pVertexBindingDescriptions = vertexBindings.data(),
      .vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttribs.size()),
      .pVertexAttributeDescriptions = vertexAttribs.data()
    };

    VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
      .stageCount = static_cast<uint32_t>(shaderStages.size()),
      .pStages = shaderStages.data(),
      .pVertexInputState = &inputState,
      .pInputAssemblyState = &inputAssemblyState,
      .pViewportState = &viewportState,
      .pRasterizationState = &rasterizationState,
      .pMultisampleState = &multisampleState,
      .pDepthStencilState = &depthStencilState,
      .pColorBlendState = &colorBlendState,
      .pDynamicState = &dynamicState,
      .layout = pipelineLayout,
      .renderPass = renderPass,
      .basePipelineHandle = VK_NULL_HANDLE,
      .basePipelineIndex = -1
    };
    vik_log_check(vkCreateGraphicsPipelines(vulkanDevice->logicalDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
  }



@@ 464,80 559,86 @@ class TextOverlay {
  * Prepare a separate render pass for rendering the text as an overlay
  */
  void prepareRenderPass() {
    VkAttachmentDescription attachments[2] = {};

    // Color attachment
    attachments[0].format = colorFormat;
    attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
    // Don't clear the framebuffer (like the renderpass from the example does)
    attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

    // Depth attachment
    attachments[1].format = depthFormat;
    attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
    attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkAttachmentReference colorReference = {};
    colorReference.attachment = 0;
    colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    VkAttachmentReference depthReference = {};
    depthReference.attachment = 1;
    depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkSubpassDependency subpassDependencies[2] = {};

    // Transition from final to initial (VK_SUBPASS_EXTERNAL refers to all commmands executed outside of the actual renderpass)
    subpassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
    subpassDependencies[0].dstSubpass = 0;
    subpassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    subpassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpassDependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    subpassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    subpassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    // Transition from initial to final
    subpassDependencies[1].srcSubpass = 0;
    subpassDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
    subpassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    subpassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    subpassDependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    subpassDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

    VkSubpassDescription subpassDescription = {};
    subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpassDescription.flags = 0;
    subpassDescription.inputAttachmentCount = 0;
    subpassDescription.pInputAttachments = NULL;
    subpassDescription.colorAttachmentCount = 1;
    subpassDescription.pColorAttachments = &colorReference;
    subpassDescription.pResolveAttachments = NULL;
    subpassDescription.pDepthStencilAttachment = &depthReference;
    subpassDescription.preserveAttachmentCount = 0;
    subpassDescription.pPreserveAttachments = NULL;

    VkRenderPassCreateInfo renderPassInfo = {};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    renderPassInfo.pNext = NULL;
    renderPassInfo.attachmentCount = 2;
    renderPassInfo.pAttachments = attachments;
    renderPassInfo.subpassCount = 1;
    renderPassInfo.pSubpasses = &subpassDescription;
    renderPassInfo.dependencyCount = 2;
    renderPassInfo.pDependencies = subpassDependencies;

    vik_log_check(vkCreateRenderPass(vulkanDevice->logicalDevice, &renderPassInfo, nullptr, &renderPass));
    VkAttachmentDescription attachments[2] = {
      {
        // Color attachment
        .format = colorFormat,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        // Don't clear the framebuffer (like the renderpass from the example does)
        .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
      },
      {
        // Depth attachment
        .format = depthFormat,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
      }
    };

    VkAttachmentReference colorReference = {
      .attachment = 0,
      .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
    };

    VkAttachmentReference depthReference = {
      depthReference.attachment = 1,
      depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
    };

    VkSubpassDependency subpassDependencies[2] = {
      {
        // Transition from final to initial (VK_SUBPASS_EXTERNAL refers
        // to all commmands executed outside of the actual renderpass)
        .srcSubpass = VK_SUBPASS_EXTERNAL,
        .dstSubpass = 0,
        .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      },
      {
        // Transition from initial to final
        .srcSubpass = 0,
        .dstSubpass = VK_SUBPASS_EXTERNAL,
        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
        .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
      }
    };

    VkSubpassDescription subpassDescription = {
      .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
      .colorAttachmentCount = 1,
      .pColorAttachments = &colorReference,
      .pDepthStencilAttachment = &depthReference
    };

    VkRenderPassCreateInfo renderPassInfo = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
      .attachmentCount = 2,
      .pAttachments = attachments,
      .subpassCount = 1,
      .pSubpasses = &subpassDescription,
      .dependencyCount = 2,
      .pDependencies = subpassDependencies
    };
    vik_log_check(vkCreateRenderPass(vulkanDevice->logicalDevice,
                                     &renderPassInfo, nullptr, &renderPass));
  }

  /**


@@ 636,15 737,23 @@ class TextOverlay {
  * Update the command buffers to reflect text changes
  */
  void updateCommandBuffers() {
    VkCommandBufferBeginInfo cmdBufInfo = initializers::commandBufferBeginInfo();
    VkCommandBufferBeginInfo cmdBufInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
    };

    VkRenderPassBeginInfo renderPassBeginInfo = initializers::renderPassBeginInfo();
    renderPassBeginInfo.renderPass = renderPass;
    renderPassBeginInfo.renderArea.extent.width = *frameBufferWidth;
    renderPassBeginInfo.renderArea.extent.height = *frameBufferHeight;
    // None of the attachments will be cleared
    renderPassBeginInfo.clearValueCount = 0;
    renderPassBeginInfo.pClearValues = nullptr;
    VkRenderPassBeginInfo renderPassBeginInfo = {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
      .renderPass = renderPass,
      .renderArea = {
        .extent = {
          .width = *frameBufferWidth,
          .height = *frameBufferHeight
        }
      },
      // None of the attachments will be cleared
      .clearValueCount = 0,
      .pClearValues = nullptr
    };

    for (size_t i = 0; i < cmdBuffers.size(); ++i) {
      renderPassBeginInfo.framebuffer = *frameBuffers[i];


@@ 656,10 765,18 @@ class TextOverlay {

      vkCmdBeginRenderPass(cmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);

      VkViewport viewport = initializers::viewport((float)*frameBufferWidth, (float)*frameBufferHeight, 0.0f, 1.0f);
      VkViewport viewport = {
        .width = (float)*frameBufferWidth,
        .height = (float)*frameBufferHeight,
        .minDepth = 0.0f,
        .maxDepth = 1.0f
      };
      vkCmdSetViewport(cmdBuffers[i], 0, 1, &viewport);

      VkRect2D scissor = initializers::rect2D(*frameBufferWidth, *frameBufferHeight, 0, 0);
      VkRect2D scissor = {
        .offset = { .x = 0, .y = 0 },
        .extent = { .width = *frameBufferWidth, .height = *frameBufferHeight }
      };
      vkCmdSetScissor(cmdBuffers[i], 0, 1, &scissor);

      vkCmdBindPipeline(cmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);


@@ 703,11 820,12 @@ class TextOverlay {
  void reallocateCommandBuffers() {
    vkFreeCommandBuffers(vulkanDevice->logicalDevice, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());

    VkCommandBufferAllocateInfo cmdBufAllocateInfo =
        initializers::commandBufferAllocateInfo(
          commandPool,
          VK_COMMAND_BUFFER_LEVEL_PRIMARY,
          static_cast<uint32_t>(cmdBuffers.size()));
    VkCommandBufferAllocateInfo cmdBufAllocateInfo = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .commandPool = commandPool,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = static_cast<uint32_t>(cmdBuffers.size())
    };

    vik_log_check(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data()));
  }

M vitamin-k/render/vikTexture.hpp => vitamin-k/render/vikTexture.hpp +398 -262
@@ 103,7 103,9 @@ class Texture2D : public Texture {
    // limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
    VkBool32 useStaging = !forceLinear;

    VkMemoryAllocateInfo memAllocInfo = initializers::memoryAllocateInfo();
    VkMemoryAllocateInfo memAllocInfo = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    };
    VkMemoryRequirements memReqs;

    // Use a separate command buffer for texture loading


@@ 114,11 116,13 @@ class Texture2D : public Texture {
      VkBuffer stagingBuffer;
      VkDeviceMemory stagingMemory;

      VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo();
      bufferCreateInfo.size = tex2D.size();
      // This buffer is used as a transfer source for the buffer copy
      bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
      bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
      VkBufferCreateInfo bufferCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
        .size = tex2D.size(),
        // This buffer is used as a transfer source for the buffer copy
        .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE
      };

      vik_log_check(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));



@@ 143,15 147,20 @@ class Texture2D : public Texture {
      uint32_t offset = 0;

      for (uint32_t i = 0; i < mipLevels; i++) {
        VkBufferImageCopy bufferCopyRegion = {};
        bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        bufferCopyRegion.imageSubresource.mipLevel = i;
        bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
        bufferCopyRegion.imageSubresource.layerCount = 1;
        bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(tex2D[i].extent().x);
        bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(tex2D[i].extent().y);
        bufferCopyRegion.imageExtent.depth = 1;
        bufferCopyRegion.bufferOffset = offset;
        VkBufferImageCopy bufferCopyRegion = {
          .bufferOffset = offset,
          .imageSubresource = {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .mipLevel = i,
            .baseArrayLayer = 0,
            .layerCount = 1,
          },
          .imageExtent = {
            .width = static_cast<uint32_t>(tex2D[i].extent().x),
            .height = static_cast<uint32_t>(tex2D[i].extent().y),
            .depth = 1,
          }
        };

        bufferCopyRegions.push_back(bufferCopyRegion);



@@ 159,17 168,24 @@ class Texture2D : public Texture {
      }

      // Create optimal tiled target image
      VkImageCreateInfo imageCreateInfo = initializers::imageCreateInfo();
      imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
      imageCreateInfo.format = format;
      imageCreateInfo.mipLevels = mipLevels;
      imageCreateInfo.arrayLayers = 1;
      imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
      imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
      imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
      imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      imageCreateInfo.extent = { width, height, 1 };
      imageCreateInfo.usage = imageUsageFlags;
      VkImageCreateInfo imageCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        .imageType = VK_IMAGE_TYPE_2D,
        .format = format,
        .extent = {
          .width = width,
          .height = height,
          .depth = 1
        },
        .mipLevels = mipLevels,
        .arrayLayers = 1,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .tiling = VK_IMAGE_TILING_OPTIMAL,
        .usage = imageUsageFlags,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
      };

      // Ensure that the TRANSFER_DST bit is set for staging
      if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
        imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;


@@ 183,11 199,12 @@ class Texture2D : public Texture {
      vik_log_check(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory));
      vik_log_check(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));

      VkImageSubresourceRange subresourceRange = {};
      subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
      subresourceRange.baseMipLevel = 0;
      subresourceRange.levelCount = mipLevels;
      subresourceRange.layerCount = 1;
      VkImageSubresourceRange subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        .levelCount = mipLevels,
        .layerCount = 1
      };

      // Image barrier for optimal image (target)
      // Optimal image will be used as destination for the copy


@@ 232,20 249,27 @@ class Texture2D : public Texture {
      VkImage mappableImage;
      VkDeviceMemory mappableMemory;

      VkImageCreateInfo imageCreateInfo = initializers::imageCreateInfo();
      imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
      imageCreateInfo.format = format;
      imageCreateInfo.extent = { width, height, 1 };
      imageCreateInfo.mipLevels = 1;
      imageCreateInfo.arrayLayers = 1;
      imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
      imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
      imageCreateInfo.usage = imageUsageFlags;
      imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
      imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      VkImageCreateInfo imageCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        .imageType = VK_IMAGE_TYPE_2D,
        .format = format,
        .extent = {
          .width = width,
          .height = height,
          .depth = 1
        },
        .mipLevels = 1,
        .arrayLayers = 1,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .tiling = VK_IMAGE_TILING_LINEAR,
        .usage = imageUsageFlags,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
      };

      // Load mip map level 0 to linear tiling image
      vik_log_check(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &mappableImage));
      vik_log_check(vkCreateImage(device->logicalDevice, &imageCreateInfo,
                                  nullptr, &mappableImage));

      // Get memory requirements for this image
      // like size and alignment


@@ 264,9 288,10 @@ class Texture2D : public Texture {

      // Get sub resource layout
      // Mip map count, array layer, etc.
      VkImageSubresource subRes = {};
      subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
      subRes.mipLevel = 0;
      VkImageSubresource subRes = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .mipLevel = 0
      };

      VkSubresourceLayout subResLayout;
      void *data;


@@ 290,46 315,63 @@ class Texture2D : public Texture {
      this->imageLayout = imageLayout;

      // Setup image memory barrier
      tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);
      tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT,
                            VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);

      device->flushCommandBuffer(copyCmd, copyQueue);
    }

    // Create a defaultsampler
    VkSamplerCreateInfo samplerCreateInfo = {};
    samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
    samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
    samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerCreateInfo.minLod = 0.0f;
    // Max level-of-detail should match mip level count
    samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f;
    // Enable anisotropic filtering
    samplerCreateInfo.maxAnisotropy = 8;
    samplerCreateInfo.anisotropyEnable = VK_TRUE;
    samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
    vik_log_check(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
    VkSamplerCreateInfo samplerCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
      .magFilter = VK_FILTER_LINEAR,
      .minFilter = VK_FILTER_LINEAR,
      .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
      .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .mipLodBias = 0.0f,
      .anisotropyEnable = VK_TRUE,
      .maxAnisotropy = 8,
      .compareOp = VK_COMPARE_OP_NEVER,
      .minLod = 0.0f,
      // Max level-of-detail should match mip level count
      .maxLod = (useStaging) ? (float)mipLevels : 0.0f,
      // Enable anisotropic filtering
      .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
    };

    vik_log_check(vkCreateSampler(device->logicalDevice, &samplerCreateInfo,
                                  nullptr, &sampler));

    // Create image view
    // Textures are not directly accessed by the shaders and
    // are abstracted by image views containing additional
    // information and sub resource ranges
    VkImageViewCreateInfo viewCreateInfo = {};
    viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewCreateInfo.format = format;
    viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
    viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
    // Linear tiling usually won't support mip maps
    // Only set mip map count if optimal tiling is used
    viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1;
    viewCreateInfo.image = image;
    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
    VkImageViewCreateInfo viewCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = format,
      .components = {
        .r = VK_COMPONENT_SWIZZLE_R,
        .g = VK_COMPONENT_SWIZZLE_G,
        .b = VK_COMPONENT_SWIZZLE_B,
        .a = VK_COMPONENT_SWIZZLE_A
      },
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        // Linear tiling usually won't support mip maps
        // Only set mip map count if optimal tiling is used
        .levelCount = (useStaging) ? mipLevels : 1,
        .baseArrayLayer = 0,
        .layerCount = 1
      }
    };

    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo,
                                    nullptr, &view));

    // Update descriptor image info member that can be used for setting up descriptor sets
    updateDescriptor();


@@ 367,9 409,6 @@ class Texture2D : public Texture {
    this->height = height;
    mipLevels = 1;

    VkMemoryAllocateInfo memAllocInfo = initializers::memoryAllocateInfo();
    VkMemoryRequirements memReqs;

    // Use a separate command buffer for texture loading
    VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);



@@ 377,20 416,27 @@ class Texture2D : public Texture {
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingMemory;

    VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo();
    bufferCreateInfo.size = bufferSize;
    // This buffer is used as a transfer source for the buffer copy
    bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    VkBufferCreateInfo bufferCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .size = bufferSize,
      // This buffer is used as a transfer source for the buffer copy
      .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE
    };

    vik_log_check(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
    vik_log_check(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo,
                                 nullptr, &stagingBuffer));

    VkMemoryRequirements memReqs;
    // Get memory requirements for the staging buffer (alignment, memory type bits)
    vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);

    memAllocInfo.allocationSize = memReqs.size;
    // Get memory type index for a host visible buffer
    memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
    VkMemoryAllocateInfo memAllocInfo = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      // Get memory type index for a host visible buffer
      .memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
    };

    vik_log_check(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
    vik_log_check(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));


@@ 401,28 447,40 @@ class Texture2D : public Texture {
    memcpy(data, buffer, bufferSize);
    vkUnmapMemory(device->logicalDevice, stagingMemory);

    VkBufferImageCopy bufferCopyRegion = {};
    bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    bufferCopyRegion.imageSubresource.mipLevel = 0;
    bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
    bufferCopyRegion.imageSubresource.layerCount = 1;
    bufferCopyRegion.imageExtent.width = width;
    bufferCopyRegion.imageExtent.height = height;
    bufferCopyRegion.imageExtent.depth = 1;
    bufferCopyRegion.bufferOffset = 0;
    VkBufferImageCopy bufferCopyRegion = {
      .bufferOffset = 0,
      .imageSubresource = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .mipLevel = 0,
        .baseArrayLayer = 0,
        .layerCount = 1,
      },
      .imageExtent = {
        .width = width,
        .height = height,
        .depth = 1,
      }
    };

    // Create optimal tiled target image
    VkImageCreateInfo imageCreateInfo = initializers::imageCreateInfo();
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = format;
    imageCreateInfo.mipLevels = mipLevels;
    imageCreateInfo.arrayLayers = 1;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent = { width, height, 1 };
    imageCreateInfo.usage = imageUsageFlags;
    VkImageCreateInfo imageCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = format,
      .extent = {
        .width = width,
        .height = height,
        .depth = 1
      },
      .mipLevels = mipLevels,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL,
      .usage = imageUsageFlags,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
    };

    // Ensure that the TRANSFER_DST bit is set for staging
    if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
      imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;


@@ 436,11 494,12 @@ class Texture2D : public Texture {
    vik_log_check(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory));
    vik_log_check(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));

    VkImageSubresourceRange subresourceRange = {};
    subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subresourceRange.baseMipLevel = 0;
    subresourceRange.levelCount = mipLevels;
    subresourceRange.layerCount = 1;
    VkImageSubresourceRange subresourceRange = {
      .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
      .baseMipLevel = 0,
      .levelCount = mipLevels,
      .layerCount = 1
    };

    // Image barrier for optimal image (target)
    // Optimal image will be used as destination for the copy


@@ 476,31 535,45 @@ class Texture2D : public Texture {
    vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);

    // Create sampler
    VkSamplerCreateInfo samplerCreateInfo = {};
    samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerCreateInfo.magFilter = filter;
    samplerCreateInfo.minFilter = filter;
    samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerCreateInfo.minLod = 0.0f;
    samplerCreateInfo.maxLod = 0.0f;
    VkSamplerCreateInfo samplerCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
      .magFilter = filter,
      .minFilter = filter,
      .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
      .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
      .mipLodBias = 0.0f,
      .compareOp = VK_COMPARE_OP_NEVER,
      .minLod = 0.0f,
      .maxLod = 0.0f
    };

    vik_log_check(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));

    // Create image view
    VkImageViewCreateInfo viewCreateInfo = {};
    viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewCreateInfo.pNext = NULL;
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewCreateInfo.format = format;
    viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
    viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
    viewCreateInfo.subresourceRange.levelCount = 1;
    viewCreateInfo.image = image;
    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
    VkImageViewCreateInfo viewCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D,
      .format = format,
      .components = {
        .r = VK_COMPONENT_SWIZZLE_R,
        .g = VK_COMPONENT_SWIZZLE_G,
        .b = VK_COMPONENT_SWIZZLE_B,
        .a = VK_COMPONENT_SWIZZLE_A
      },
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        .levelCount = 1,
        .baseArrayLayer = 0,
        .layerCount = 1
      }
    };

    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo,
                                    nullptr, &view));

    // Update descriptor image info member that can be used for setting up descriptor sets
    updateDescriptor();


@@ 542,27 615,30 @@ class Texture2DArray : public Texture {
    layerCount = static_cast<uint32_t>(tex2DArray.layers());
    mipLevels = static_cast<uint32_t>(tex2DArray.levels());

    VkMemoryAllocateInfo memAllocInfo = initializers::memoryAllocateInfo();
    VkMemoryRequirements memReqs;

    // Create a host-visible staging buffer that contains the raw image data
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingMemory;

    VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo();
    bufferCreateInfo.size = tex2DArray.size();
    // This buffer is used as a transfer source for the buffer copy
    bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    VkBufferCreateInfo bufferCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .size = tex2DArray.size(),
      // This buffer is used as a transfer source for the buffer copy
      .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE
    };

    vik_log_check(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

    // Get memory requirements for the staging buffer (alignment, memory type bits)
    VkMemoryRequirements memReqs;
    vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);

    memAllocInfo.allocationSize = memReqs.size;
    // Get memory type index for a host visible buffer
    memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
    VkMemoryAllocateInfo memAllocInfo = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      // Get memory type index for a host visible buffer
      .memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
    };

    vik_log_check(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
    vik_log_check(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));


@@ 579,16 655,20 @@ class Texture2DArray : public Texture {

    for (uint32_t layer = 0; layer < layerCount; layer++) {
      for (uint32_t level = 0; level < mipLevels; level++) {
        VkBufferImageCopy bufferCopyRegion = {};
        bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        bufferCopyRegion.imageSubresource.mipLevel = level;
        bufferCopyRegion.imageSubresource.baseArrayLayer = layer;
        bufferCopyRegion.imageSubresource.layerCount = 1;
        bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(tex2DArray[layer][level].extent().x);
        bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(tex2DArray[layer][level].extent().y);
        bufferCopyRegion.imageExtent.depth = 1;
        bufferCopyRegion.bufferOffset = offset;

        VkBufferImageCopy bufferCopyRegion = {
          .bufferOffset = offset,
          .imageSubresource = {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .mipLevel = level,
            .baseArrayLayer = layer,
            .layerCount = 1
          },
          .imageExtent = {
            .width = static_cast<uint32_t>(tex2DArray[layer][level].extent().x),
            .height = static_cast<uint32_t>(tex2DArray[layer][level].extent().y),
            .depth = 1,
          }
        };
        bufferCopyRegions.push_back(bufferCopyRegion);

        // Increase offset into staging buffer for next level / face


@@ 597,20 677,27 @@ class Texture2DArray : public Texture {
    }

    // Create optimal tiled target image
    VkImageCreateInfo imageCreateInfo = initializers::imageCreateInfo();
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = format;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent = { width, height, 1 };
    imageCreateInfo.usage = imageUsageFlags;
    VkImageCreateInfo imageCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = format,
      .extent = {
        .width = width,
        .height = height,
        .depth = 1
      },
      .mipLevels = mipLevels,
      .arrayLayers = layerCount,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL,
      .usage = imageUsageFlags,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
    };

    // Ensure that the TRANSFER_DST bit is set for staging
    if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
      imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    imageCreateInfo.arrayLayers = layerCount;
    imageCreateInfo.mipLevels = mipLevels;

    vik_log_check(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));



@@ 627,11 714,12 @@ class Texture2DArray : public Texture {

    // Image barrier for optimal image (target)
    // Set initial layout for all array layers (faces) of the optimal (target) tiled texture
    VkImageSubresourceRange subresourceRange = {};
    subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subresourceRange.baseMipLevel = 0;
    subresourceRange.levelCount = mipLevels;
    subresourceRange.layerCount = layerCount;
    VkImageSubresourceRange subresourceRange = {
      .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
      .baseMipLevel = 0,
      .levelCount = mipLevels,
      .layerCount = layerCount
    };

    tools::setImageLayout(
          copyCmd,


@@ 661,31 749,48 @@ class Texture2DArray : public Texture {
    device->flushCommandBuffer(copyCmd, copyQueue);

    // Create sampler
    VkSamplerCreateInfo samplerCreateInfo = initializers::samplerCreateInfo();
    samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
    samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
    samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU;
    samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.maxAnisotropy = 8;
    samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerCreateInfo.minLod = 0.0f;
    samplerCreateInfo.maxLod = (float)mipLevels;
    samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
    vik_log_check(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
    VkSamplerCreateInfo samplerCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
      .magFilter = VK_FILTER_LINEAR,
      .minFilter = VK_FILTER_LINEAR,
      .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
      .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
      .mipLodBias = 0.0f,
      .maxAnisotropy = 8,
      .compareOp = VK_COMPARE_OP_NEVER,
      .minLod = 0.0f,
      .maxLod = (float)mipLevels,
      .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
    };

    vik_log_check(vkCreateSampler(device->logicalDevice, &samplerCreateInfo,
                                  nullptr, &sampler));

    // Create image view
    VkImageViewCreateInfo viewCreateInfo = initializers::imageViewCreateInfo();
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
    viewCreateInfo.format = format;
    viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
    viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
    viewCreateInfo.subresourceRange.layerCount = layerCount;
    viewCreateInfo.subresourceRange.levelCount = mipLevels;
    viewCreateInfo.image = image;
    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
    VkImageViewCreateInfo viewCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
      .image = image,
      .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
      .format = format,
      .components = {
        .r = VK_COMPONENT_SWIZZLE_R,
        .g = VK_COMPONENT_SWIZZLE_G,
        .b = VK_COMPONENT_SWIZZLE_B,
        .a = VK_COMPONENT_SWIZZLE_A
      },
      .subresourceRange = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .baseMipLevel = 0,
        .levelCount = mipLevels,
        .baseArrayLayer = 0,
        .layerCount = layerCount
      }
    };

    vik_log_check(vkCreateImageView(device->logicalDevice, &viewCreateInfo,
                                    nullptr, &view));

    // Clean up staging resources
    vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);


@@ 730,27 835,29 @@ class TextureCubeMap : public Texture {
    height = static_cast<uint32_t>(texCube.extent().y);
    mipLevels = static_cast<uint32_t>(texCube.levels());

    VkMemoryAllocateInfo memAllocInfo = initializers::memoryAllocateInfo();
    VkMemoryRequirements memReqs;

    // Create a host-visible staging buffer that contains the raw image data
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingMemory;

    VkBufferCreateInfo bufferCreateInfo = initializers::bufferCreateInfo();
    bufferCreateInfo.size = texCube.size();
    // This buffer is used as a transfer source for the buffer copy
    bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    VkBufferCreateInfo bufferCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .size = texCube.size(),
      // This buffer is used as a transfer source for the buffer copy
      .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE
    };
    vik_log_check(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

    // Get memory requirements for the staging buffer (alignment, memory type bits)
    VkMemoryRequirements memReqs;
    vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);

    memAllocInfo.allocationSize = memReqs.size;
    // Get memory type index for a host visible buffer
    memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
    VkMemoryAllocateInfo memAllocInfo = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .allocationSize = memReqs.size,
      // Get memory type index for a host visible buffer
      .memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
    };

    vik_log_check(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
    vik_log_check(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));


@@ 767,16 874,20 @@ class TextureCubeMap : public Texture {

    for (uint32_t face = 0; face < 6; face++) {
      for (uint32_t level = 0; level < mipLevels; level++) {
        VkBufferImageCopy bufferCopyRegion = {};
        bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        bufferCopyRegion.imageSubresource.mipLevel = level;
        bufferCopyRegion.imageSubresource.baseArrayLayer = face;
        bufferCopyRegion.imageSubresource.layerCount = 1;
        bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(texCube[face][level].extent().x);
        bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(texCube[face][level].extent().y);
        bufferCopyRegion.imageExtent.depth = 1;
        bufferCopyRegion.bufferOffset = offset;

        VkBufferImageCopy bufferCopyRegion = {
          .bufferOffset = offset,
          .imageSubresource = {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .mipLevel = level,
            .baseArrayLayer = face,
            .layerCount = 1
          },
          .imageExtent = {
            .width = static_cast<uint32_t>(texCube[face][level].extent().x),
            .height = static_cast<uint32_t>(texCube[face][level].extent().y),
            .depth = 1
          }
        };
        bufferCopyRegions.push_back(bufferCopyRegion);

        // Increase offset into staging buffer for next level / face


@@ 785,31 896,38 @@ class TextureCubeMap : public Texture {
    }

    // Create optimal tiled target image
    VkImageCreateInfo imageCreateInfo = initializers::imageCreateInfo();
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = format;
    imageCreateInfo.mipLevels = mipLevels;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent = { width, height, 1 };
    imageCreateInfo.usage = imageUsageFlags;
    VkImageCreateInfo imageCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      // This flag is required for cube map images
      .flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = format,
      .extent = {
        .width = width,
        .height = height,
        .depth = 1
      },
      .mipLevels = mipLevels,
      // Cube faces count as array layers in Vulkan
      .arrayLayers = 6,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_OPTIMAL,
      .usage = imageUsageFlags,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
    };

    // Ensure that the TRANSFER_DST bit is set for staging
    if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
      imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    // Cube faces count as array layers in Vulkan
    imageCreateInfo.arrayLayers = 6;
    // This flag is required for cube map images
    imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;


    vik_log_check(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
    vik_log_check(vkCreateImage(device->logicalDevice, &imageCreateInfo,
                                nullptr, &image));

    vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);

    memAllocInfo.allocationSize = memReqs.size;
    memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);