inexorgame / vulkan-renderer

A new 3D game engine for Linux and Windows using C++20 and Vulkan API 1.3, in very early but ongoing development
https://inexor.org
MIT License
777 stars 34 forks source link

Use C++20's designated initializers #487

Closed IAmNotHanni closed 1 year ago

IAmNotHanni commented 1 year ago

Introduction

Designated initializers are a C++20 feature that makes initialization code more readable. In addition it enforces that the order of initialization is the same as the order of declaration in the struct. Compare those two code parts:

Old code:

VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.image = image;
barrier.subresourceRange = subres_range;

New code with designated initializers:

const auto barrier = make_info<VkImageMemoryBarrier>({
    .oldLayout = old_layout,
    .newLayout = new_layout,
    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
    .image = image,
    .subresourceRange = subres_range,
});

This code example uses the following make_info template:

template <>
VkImageMemoryBarrier make_info(VkImageMemoryBarrier info) {
    info.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    return info;
}

Summary

Designated initializers

Notes

// This struct is not const, and not all members are set in here because some are set depending on certain conditions
// See code below
auto barrier = make_info<VkImageMemoryBarrier>({
    .oldLayout = old_layout,
    .newLayout = new_layout,
    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
    .image = image,
    .subresourceRange = subres_range,
});

// This could be part of the struct by using lambdas, but will it make it easier to read?
switch (old_layout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
    barrier.srcAccessMask = 0;
    break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
    barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
    barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
    barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
    barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
    break;
default:
    break;
}

switch (new_layout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
    barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
    barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
    barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
    barrier.dstAccessMask = barrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
    if (barrier.srcAccessMask == 0) {
        barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
    }
    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    break;
default:
    break;
}
IAmNotHanni commented 1 year ago