vk_conversion.cpp

cross platform rendering playground

src/backend/vulkan/vk_conversion.cpp

21.93 KB
#include "vk_conversion.h"

#include "backend/vulkan/vk_api.h"

namespace vk {

namespace {

VkImageUsageFlags image_usage_bit_to_vk(ImageUsage flag) {
    switch (flag) {
    case ImageUsage::ColorAttachment:
        return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    case ImageUsage::DepthAttachment:
        return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
    case ImageUsage::StencilAttachment:
        return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
    case ImageUsage::Sampled:
        return VK_IMAGE_USAGE_SAMPLED_BIT;
    case ImageUsage::Storage:
        return VK_IMAGE_USAGE_STORAGE_BIT;
    case ImageUsage::TransferSrc:
        return VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    case ImageUsage::TransferDst:
        return VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    case ImageUsage::InputAttachment:
        return VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
    default:
        return 0;
    }
}

} // anonymous namespace

VkImageAspectFlags image_aspect_bit_to_vk(ImageAspect flag) {
    switch (flag) {
    case ImageAspect::Color:
        return VK_IMAGE_ASPECT_COLOR_BIT;
    case ImageAspect::Depth:
        return VK_IMAGE_ASPECT_DEPTH_BIT;
    case ImageAspect::Stencil:
        return VK_IMAGE_ASPECT_STENCIL_BIT;
    default:
        return 0;
    }
}

VkFormat image_format_to_vk(ImageFormat format) {
    switch (format) {
    case ImageFormat::UNDEFINED:
        return VK_FORMAT_UNDEFINED;
    case ImageFormat::R8_UNORM:
        return VK_FORMAT_R8_UNORM;
    case ImageFormat::RGBA8_UNORM:
        return VK_FORMAT_R8G8B8A8_UNORM;
    case ImageFormat::RGBA8_SRGB:
        return VK_FORMAT_R8G8B8A8_SRGB;
    case ImageFormat::BGRA8_UNORM:
        return VK_FORMAT_B8G8R8A8_UNORM;
    case ImageFormat::ASTC6X6_SRGB:
        return VK_FORMAT_ASTC_6x6_SRGB_BLOCK;
    case ImageFormat::ASTC6X6_UNORM:
        return VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
    case ImageFormat::BC7_SRGB:
        return VK_FORMAT_BC7_SRGB_BLOCK;
    case ImageFormat::BC7_UNORM:
        return VK_FORMAT_BC7_UNORM_BLOCK;
    case ImageFormat::D32_FLOAT:
        return VK_FORMAT_D32_SFLOAT;
    case ImageFormat::RGBA16_FLOAT:
        return VK_FORMAT_R16G16B16A16_SFLOAT;
    case ImageFormat::RGBA32_FLOAT:
        return VK_FORMAT_R32G32B32A32_SFLOAT;
    case ImageFormat::R32_FLOAT:
        return VK_FORMAT_R32_SFLOAT;
    case ImageFormat::R32_UINT:
        return VK_FORMAT_R32_UINT;
    case ImageFormat::R16_FLOAT:
        return VK_FORMAT_R16_SFLOAT;
    case ImageFormat::R10G10B10A2_UNORM:
        return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
    case ImageFormat::RG16_FLOAT:
        return VK_FORMAT_R16G16_SFLOAT;
    case ImageFormat::RG32_FLOAT:
        return VK_FORMAT_R32G32_SFLOAT;
    case ImageFormat::RGB32_FLOAT:
        return VK_FORMAT_R32G32B32_SFLOAT;
    default:
        return VK_FORMAT_UNDEFINED;
    }
}

ImageFormat image_format_from_vk(VkFormat format) {
    switch (format) {
    case VK_FORMAT_UNDEFINED:
        return ImageFormat::UNDEFINED;

    case VK_FORMAT_R8_UNORM:
        return ImageFormat::R8_UNORM;

    case VK_FORMAT_R8G8B8A8_UNORM:
        return ImageFormat::RGBA8_UNORM;

    case VK_FORMAT_R8G8B8A8_SRGB:
        return ImageFormat::RGBA8_SRGB;

    case VK_FORMAT_B8G8R8A8_UNORM:
        return ImageFormat::BGRA8_UNORM;

    case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
        return ImageFormat::ASTC6X6_SRGB;

    case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
        return ImageFormat::ASTC6X6_UNORM;

    case VK_FORMAT_BC7_SRGB_BLOCK:
        return ImageFormat::BC7_SRGB;

    case VK_FORMAT_BC7_UNORM_BLOCK:
        return ImageFormat::BC7_UNORM;

    case VK_FORMAT_D32_SFLOAT:
        return ImageFormat::D32_FLOAT;

    case VK_FORMAT_R16G16B16A16_SFLOAT:
        return ImageFormat::RGBA16_FLOAT;

    case VK_FORMAT_R32G32B32A32_SFLOAT:
        return ImageFormat::RGBA32_FLOAT;

    case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
        return ImageFormat::R10G10B10A2_UNORM;

    case VK_FORMAT_R16G16_SFLOAT:
        return ImageFormat::RG16_FLOAT;

    case VK_FORMAT_R32_UINT:
        return ImageFormat::R32_UINT;

    default:
        return ImageFormat::UNDEFINED;
    }
}

VkImageViewType image_view_type_to_vk(TextureViewDimension type) {
    switch (type) {
    case TextureViewDimension::TEXTURE_2D:
        return VK_IMAGE_VIEW_TYPE_2D;
    case TextureViewDimension::TEXTURE_2D_ARRAY:
        return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
    case TextureViewDimension::TEXTURE_CUBE:
        return VK_IMAGE_VIEW_TYPE_CUBE;
    case TextureViewDimension::TEXTURE_3D:
        return VK_IMAGE_VIEW_TYPE_3D;
    }
    return VK_IMAGE_VIEW_TYPE_2D;
}

VkComponentSwizzle component_swizzle_to_vk(ComponentSwizzle swizzle) {
    switch (swizzle) {
    case ComponentSwizzle::Identity:
        return VK_COMPONENT_SWIZZLE_IDENTITY;
    case ComponentSwizzle::R:
        return VK_COMPONENT_SWIZZLE_R;
    case ComponentSwizzle::G:
        return VK_COMPONENT_SWIZZLE_G;
    case ComponentSwizzle::B:
        return VK_COMPONENT_SWIZZLE_B;
    case ComponentSwizzle::A:
        return VK_COMPONENT_SWIZZLE_A;
    case ComponentSwizzle::Zero:
        return VK_COMPONENT_SWIZZLE_ZERO;
    case ComponentSwizzle::One:
        return VK_COMPONENT_SWIZZLE_ONE;
    }
    return VK_COMPONENT_SWIZZLE_IDENTITY;
}

VkImageUsageFlags image_usage_to_vk(Device &device, ImageDesc const &desc) {
    VkImageUsageFlags flags = 0;
    u32 bits = static_cast<u32>(desc.usage);
    for (int i = 0; i < 8; ++i) {
        if (bits & (1u << i)) {
            flags |= image_usage_bit_to_vk(static_cast<ImageUsage>(1u << i));
        }
    }
    VkFormatProperties props{};
    vkGetPhysicalDeviceFormatProperties(device.physical, image_format_to_vk(desc.format), &props);
    if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT) {
        flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    }
    if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_DST_BIT) {
        flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    }
    return flags;
}

VkImageAspectFlags image_aspect_to_vk(ImageAspect aspect) {
    VkImageAspectFlags flags = 0;

    u32 bits = static_cast<u32>(aspect);

    if (bits & static_cast<u32>(ImageAspect::Color)) {
        flags |= VK_IMAGE_ASPECT_COLOR_BIT;
    }

    if (bits & static_cast<u32>(ImageAspect::Depth)) {
        flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
    }

    if (bits & static_cast<u32>(ImageAspect::Stencil)) {
        flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
    }

    return flags;
}

VkSamplerAddressMode sampler_address_to_vk(SamplerAddressMode mode) {
    switch (mode) {
    case SamplerAddressMode::WRAP:
        return VK_SAMPLER_ADDRESS_MODE_REPEAT;
    case SamplerAddressMode::MIRROR:
        return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
    case SamplerAddressMode::CLAMP:
        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    case SamplerAddressMode::BORDER:
        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
    default:
        return VK_SAMPLER_ADDRESS_MODE_REPEAT;
    }
}

VkFilter sampler_filter_to_vk(SamplerFilter filter) {
    switch (filter) {
    case SamplerFilter::POINT:
        return VK_FILTER_NEAREST;
    case SamplerFilter::LINEAR:
    case SamplerFilter::ANISOTROPIC:
        return VK_FILTER_LINEAR;
    default:
        return VK_FILTER_NEAREST;
    }
}

VkCompareOp sampler_comparison_to_vk(SamplerComparisonFunc func) {
    switch (func) {
    case SamplerComparisonFunc::NEVER:
        return VK_COMPARE_OP_NEVER;
    case SamplerComparisonFunc::LESS:
        return VK_COMPARE_OP_LESS;
    case SamplerComparisonFunc::EQUAL:
        return VK_COMPARE_OP_EQUAL;
    case SamplerComparisonFunc::LESS_EQUAL:
        return VK_COMPARE_OP_LESS_OR_EQUAL;
    case SamplerComparisonFunc::GREATER:
        return VK_COMPARE_OP_GREATER;
    case SamplerComparisonFunc::NOT_EQUAL:
        return VK_COMPARE_OP_NOT_EQUAL;
    case SamplerComparisonFunc::GREATER_EQUAL:
        return VK_COMPARE_OP_GREATER_OR_EQUAL;
    case SamplerComparisonFunc::ALWAYS:
        return VK_COMPARE_OP_ALWAYS;
    default:
        return VK_COMPARE_OP_ALWAYS;
    }
}

VkPrimitiveTopology pipeline_topology_to_vk(rhi::PipelineTopology topology) {
    switch (topology) {
    case rhi::PipelineTopology::TRIANGLES:
        return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    case rhi::PipelineTopology::LINES:
        return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
    case rhi::PipelineTopology::POINTS:
        return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
    default:
        return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    }
}

VkPolygonMode pipeline_fill_mode_to_vk(rhi::PipelineFillMode fill_mode) {
    switch (fill_mode) {
    case rhi::PipelineFillMode::SOLID:
        return VK_POLYGON_MODE_FILL;
    case rhi::PipelineFillMode::WIREFRAME:
        return VK_POLYGON_MODE_LINE;
    default:
        return VK_POLYGON_MODE_FILL;
    }
}

VkCullModeFlags pipeline_cull_mode_to_vk(rhi::PipelineCullMode cull_mode) {
    switch (cull_mode) {
    case rhi::PipelineCullMode::NONE:
        return VK_CULL_MODE_NONE;
    case rhi::PipelineCullMode::FRONT:
        return VK_CULL_MODE_FRONT_BIT;
    case rhi::PipelineCullMode::BACK:
        return VK_CULL_MODE_BACK_BIT;
    default:
        return VK_CULL_MODE_BACK_BIT;
    }
}

VkFrontFace triangle_winding_to_vk(rhi::PipelineTriangleWindingOrder winding) {
    switch (winding) {
    case rhi::PipelineTriangleWindingOrder::CW:
        return VK_FRONT_FACE_CLOCKWISE;
    case rhi::PipelineTriangleWindingOrder::CCW:
        return VK_FRONT_FACE_COUNTER_CLOCKWISE;
    default:
        return VK_FRONT_FACE_CLOCKWISE;
    }
}

VkCompareOp pipeline_compare_op_to_vk(rhi::PipelineCompareOp compare_op) {
    switch (compare_op) {
    case rhi::PipelineCompareOp::NEVER:
        return VK_COMPARE_OP_NEVER;
    case rhi::PipelineCompareOp::LESS:
        return VK_COMPARE_OP_LESS;
    case rhi::PipelineCompareOp::EQUAL:
        return VK_COMPARE_OP_EQUAL;
    case rhi::PipelineCompareOp::LESS_EQUAL:
        return VK_COMPARE_OP_LESS_OR_EQUAL;
    case rhi::PipelineCompareOp::GREATER:
        return VK_COMPARE_OP_GREATER;
    case rhi::PipelineCompareOp::NOT_EQUAL:
        return VK_COMPARE_OP_NOT_EQUAL;
    case rhi::PipelineCompareOp::GREATER_EQUAL:
        return VK_COMPARE_OP_GREATER_OR_EQUAL;
    case rhi::PipelineCompareOp::ALWAYS:
        return VK_COMPARE_OP_ALWAYS;
    default:
        return VK_COMPARE_OP_LESS;
    }
}

VkShaderStageFlags shader_stage_to_vk(rhi::ShaderStage stages) {
    if ((stages & rhi::ShaderStage::ALL) == rhi::ShaderStage::ALL) {
        return VK_SHADER_STAGE_ALL;
    }

    VkShaderStageFlags flags = 0;

    if ((stages & rhi::ShaderStage::VERTEX) == rhi::ShaderStage::VERTEX) {
        flags |= VK_SHADER_STAGE_VERTEX_BIT;
    }

    if ((stages & rhi::ShaderStage::FRAGMENT) == rhi::ShaderStage::FRAGMENT) {
        flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
    }

    if ((stages & rhi::ShaderStage::COMPUTE) == rhi::ShaderStage::COMPUTE) {
        flags |= VK_SHADER_STAGE_COMPUTE_BIT;
    }

    if ((stages & rhi::ShaderStage::MESH) == rhi::ShaderStage::MESH) {
        flags |= VK_SHADER_STAGE_MESH_BIT_EXT;
    }

    if ((stages & rhi::ShaderStage::TASK) == rhi::ShaderStage::TASK) {
        flags |= VK_SHADER_STAGE_TASK_BIT_EXT;
    }

    if ((stages & rhi::ShaderStage::GEOMETRY) == rhi::ShaderStage::GEOMETRY) {
        flags |= VK_SHADER_STAGE_GEOMETRY_BIT;
    }

    if ((stages & rhi::ShaderStage::RAYGEN) == rhi::ShaderStage::RAYGEN) {
        flags |= VK_SHADER_STAGE_RAYGEN_BIT_KHR;
    }

    if ((stages & rhi::ShaderStage::CLOSEST_HIT) == rhi::ShaderStage::CLOSEST_HIT) {
        flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
    }

    if ((stages & rhi::ShaderStage::ANY_HIT) == rhi::ShaderStage::ANY_HIT) {
        flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
    }

    if ((stages & rhi::ShaderStage::MISS) == rhi::ShaderStage::MISS) {
        flags |= VK_SHADER_STAGE_MISS_BIT_KHR;
    }

    if ((stages & rhi::ShaderStage::INTERSECTION) == rhi::ShaderStage::INTERSECTION) {
        flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
    }

    if ((stages & rhi::ShaderStage::CALLABLE) == rhi::ShaderStage::CALLABLE) {
        flags |= VK_SHADER_STAGE_CALLABLE_BIT_KHR;
    }

    return flags;
}

enum VkBlendFactor pipeline_blend_factor_to_vk(rhi::PipelineBlendFactor factor) {
    switch (factor) {
    case rhi::PipelineBlendFactor::ZERO:
        return VK_BLEND_FACTOR_ZERO;
    case rhi::PipelineBlendFactor::ONE:
        return VK_BLEND_FACTOR_ONE;
    case rhi::PipelineBlendFactor::SRC_ALPHA:
        return VK_BLEND_FACTOR_SRC_ALPHA;
    case rhi::PipelineBlendFactor::ONE_MINUS_SRC_ALPHA:
        return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
    case rhi::PipelineBlendFactor::DST_ALPHA:
        return VK_BLEND_FACTOR_DST_ALPHA;
    case rhi::PipelineBlendFactor::ONE_MINUS_DST_ALPHA:
        return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
    case rhi::PipelineBlendFactor::SRC_COLOR:
        return VK_BLEND_FACTOR_SRC_COLOR;
    case rhi::PipelineBlendFactor::ONE_MINUS_SRC_COLOR:
        return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
    case rhi::PipelineBlendFactor::DST_COLOR:
        return VK_BLEND_FACTOR_DST_COLOR;
    case rhi::PipelineBlendFactor::ONE_MINUS_DST_COLOR:
        return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
    case rhi::PipelineBlendFactor::SRC_ALPHA_SATURATE:
        return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
    default:
        return VK_BLEND_FACTOR_ONE;
    }
}

enum VkBlendOp pipeline_blend_op_to_vk(rhi::PipelineBlendOp op) {
    switch (op) {
    case rhi::PipelineBlendOp::ADD:
        return VK_BLEND_OP_ADD;
    case rhi::PipelineBlendOp::SUBTRACT:
        return VK_BLEND_OP_SUBTRACT;
    case rhi::PipelineBlendOp::REVERSE_SUBTRACT:
        return VK_BLEND_OP_REVERSE_SUBTRACT;
    case rhi::PipelineBlendOp::MIN:
        return VK_BLEND_OP_MIN;
    case rhi::PipelineBlendOp::MAX:
        return VK_BLEND_OP_MAX;
    default:
        return VK_BLEND_OP_ADD;
    }
}

VkSampleCountFlagBits sample_count_to_vk(rhi::SampleCount samples) {
    switch (samples) {
    case rhi::SampleCount::Sample1:
        return VK_SAMPLE_COUNT_1_BIT;
    case rhi::SampleCount::Sample2:
        return VK_SAMPLE_COUNT_2_BIT;
    case rhi::SampleCount::Sample4:
        return VK_SAMPLE_COUNT_4_BIT;
    case rhi::SampleCount::Sample8:
        return VK_SAMPLE_COUNT_8_BIT;
    case rhi::SampleCount::Sample16:
        return VK_SAMPLE_COUNT_16_BIT;
    case rhi::SampleCount::Sample32:
        return VK_SAMPLE_COUNT_32_BIT;
    case rhi::SampleCount::Sample64:
        return VK_SAMPLE_COUNT_64_BIT;
    }

    return VK_SAMPLE_COUNT_1_BIT;
}

VkCommandPoolCreateFlags to_vk_cmd_pool_flags(CmdPoolUsage flags) {
    VkCommandPoolCreateFlags vk_flags = 0;
    if (Any(flags, CmdPoolUsage::Transient)) {
        vk_flags |= VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
    }
    if (Any(flags, CmdPoolUsage::ResetCommandBuffer)) {
        vk_flags |= VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    }
    if (Any(flags, CmdPoolUsage::Protected)) {
        vk_flags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
    }
    return vk_flags;
}

VkCommandBufferLevel to_vk_cmd_buffer_level(CmdBufferLevel level) {
    switch (level) {
    case CmdBufferLevel::Primary:
        return VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    case CmdBufferLevel::Secondary:
        return VK_COMMAND_BUFFER_LEVEL_SECONDARY;
    default:
        return VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    }
}

VkCommandBufferUsageFlags to_vk_cmd_buffer_usage_flags(CmdBufferUsage flags) {
    VkCommandBufferUsageFlags vk_flags = 0;
    if (Any(flags, CmdBufferUsage::OneTimeSubmit)) {
        vk_flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    }
    if (Any(flags, CmdBufferUsage::RenderPassContinue)) {
        vk_flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
    }
    if (Any(flags, CmdBufferUsage::SimultaneousUse)) {
        vk_flags |= VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
    }
    return vk_flags;
}

VkPipelineStageFlags2 pipeline_stage_flags_to_vk(PipelineStages stages) {
    return static_cast<VkPipelineStageFlags2>(stages);
}

static bool is_depth_aspect(ImageAspect aspect) {
    return Any(aspect, ImageAspect::Depth) || Any(aspect, ImageAspect::Stencil);
}

StateSync image_state_to_vk(ResourceState state, ImageAspect aspect) {
    const bool depth = is_depth_aspect(aspect);
    switch (state) {
    case ResourceState::Idle:
        return {};
    case ResourceState::ColorDraw:
        return {
            VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
            VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT,
            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        };
    case ResourceState::ColorFetch:
        return {
            VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
            VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT,
            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        };
    case ResourceState::DepthDraw:
        return {
            VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
            VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
        };
    case ResourceState::DepthFetch:
        return {
            VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
            VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
            VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
        };
    case ResourceState::TextureSample:
        return {
            VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT,
            VK_ACCESS_2_SHADER_READ_BIT,
            depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
        };
    case ResourceState::TextureSampleNonFragment:
        return {
            VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_SHADER_READ_BIT,
            depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
        };
    case ResourceState::StorageRead:
        return {VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_STORAGE_READ_BIT, VK_IMAGE_LAYOUT_GENERAL};
    case ResourceState::StorageReadWrite:
        return {
            VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT,
            VK_IMAGE_LAYOUT_GENERAL
        };
    case ResourceState::TransferFrom:
        return {VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL};
    case ResourceState::TransferTo:
        return {VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL};
    case ResourceState::Display:
        return {VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, VK_ACCESS_2_NONE, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR};
    case ResourceState::AccelBuild:
        return {
            VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
            VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR,
            VK_IMAGE_LAYOUT_GENERAL
        };
    case ResourceState::AccelTrace:
        return {VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL};
    default:
        return {VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL};
    }
}

StateSync buffer_state_to_vk(ResourceState state) {
    switch (state) {
    case ResourceState::Idle:
        return {};
    case ResourceState::StorageRead:
        return {
            VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
                VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::StorageReadWrite:
        return {
            VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::VertexFetch:
        return {
            VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT,
            VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::IndexFetch:
        return {VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT, VK_ACCESS_2_INDEX_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED};
    case ResourceState::IndirectFetch:
        return {
            VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::UniformRead:
        return {
            VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
                VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_UNIFORM_READ_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::TransferFrom:
        return {VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED};
    case ResourceState::TransferTo:
        return {
            VK_PIPELINE_STAGE_2_TRANSFER_BIT | VK_PIPELINE_STAGE_2_CLEAR_BIT,
            VK_ACCESS_2_TRANSFER_WRITE_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::TextureSample:
    case ResourceState::TextureSampleNonFragment:
        return {
            VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
                VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
            VK_ACCESS_2_SHADER_READ_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::AccelBuild:
        return {
            VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
            VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::AccelTrace:
        return {
            VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
            VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR,
            VK_IMAGE_LAYOUT_UNDEFINED
        };
    case ResourceState::HostRead:
        return {VK_PIPELINE_STAGE_2_HOST_BIT, VK_ACCESS_2_HOST_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED};
    default:
        return {};
    }
}

} // namespace vk