| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include "flutter/fml/macros.h" |
| #include "impeller/renderer/backend/vulkan/vk.h" |
| #include "impeller/renderer/descriptor_set_layout.h" |
| #include "impeller/renderer/formats.h" |
| #include "impeller/renderer/shader_types.h" |
| #include "vulkan/vulkan_enums.hpp" |
| |
| namespace impeller { |
| |
| constexpr vk::SampleCountFlagBits ToVKSampleCountFlagBits(SampleCount count) { |
| switch (count) { |
| case SampleCount::kCount1: |
| return vk::SampleCountFlagBits::e1; |
| case SampleCount::kCount4: |
| return vk::SampleCountFlagBits::e4; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::BlendFactor ToVKBlendFactor(BlendFactor factor) { |
| switch (factor) { |
| case BlendFactor::kZero: |
| return vk::BlendFactor::eZero; |
| case BlendFactor::kOne: |
| return vk::BlendFactor::eOne; |
| case BlendFactor::kSourceColor: |
| return vk::BlendFactor::eSrcColor; |
| case BlendFactor::kOneMinusSourceColor: |
| return vk::BlendFactor::eOneMinusSrcColor; |
| case BlendFactor::kSourceAlpha: |
| return vk::BlendFactor::eSrcAlpha; |
| case BlendFactor::kOneMinusSourceAlpha: |
| return vk::BlendFactor::eOneMinusSrcAlpha; |
| case BlendFactor::kDestinationColor: |
| return vk::BlendFactor::eDstColor; |
| case BlendFactor::kOneMinusDestinationColor: |
| return vk::BlendFactor::eOneMinusDstColor; |
| case BlendFactor::kDestinationAlpha: |
| return vk::BlendFactor::eDstAlpha; |
| case BlendFactor::kOneMinusDestinationAlpha: |
| return vk::BlendFactor::eOneMinusDstAlpha; |
| case BlendFactor::kSourceAlphaSaturated: |
| return vk::BlendFactor::eSrcAlphaSaturate; |
| case BlendFactor::kBlendColor: |
| return vk::BlendFactor::eConstantColor; |
| case BlendFactor::kOneMinusBlendColor: |
| return vk::BlendFactor::eOneMinusConstantColor; |
| case BlendFactor::kBlendAlpha: |
| return vk::BlendFactor::eConstantAlpha; |
| case BlendFactor::kOneMinusBlendAlpha: |
| return vk::BlendFactor::eOneMinusConstantAlpha; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::BlendOp ToVKBlendOp(BlendOperation op) { |
| switch (op) { |
| case BlendOperation::kAdd: |
| return vk::BlendOp::eAdd; |
| case BlendOperation::kSubtract: |
| return vk::BlendOp::eSubtract; |
| case BlendOperation::kReverseSubtract: |
| return vk::BlendOp::eReverseSubtract; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::ColorComponentFlags ToVKColorComponentFlags( |
| std::underlying_type_t<ColorWriteMask> type) { |
| using UnderlyingType = decltype(type); |
| |
| vk::ColorComponentFlags mask; |
| |
| if (type & static_cast<UnderlyingType>(ColorWriteMask::kRed)) { |
| mask |= vk::ColorComponentFlagBits::eR; |
| } |
| |
| if (type & static_cast<UnderlyingType>(ColorWriteMask::kGreen)) { |
| mask |= vk::ColorComponentFlagBits::eG; |
| } |
| |
| if (type & static_cast<UnderlyingType>(ColorWriteMask::kBlue)) { |
| mask |= vk::ColorComponentFlagBits::eB; |
| } |
| |
| if (type & static_cast<UnderlyingType>(ColorWriteMask::kAlpha)) { |
| mask |= vk::ColorComponentFlagBits::eA; |
| } |
| |
| return mask; |
| } |
| |
| constexpr vk::PipelineColorBlendAttachmentState |
| ToVKPipelineColorBlendAttachmentState(const ColorAttachmentDescriptor& desc) { |
| vk::PipelineColorBlendAttachmentState res; |
| |
| res.setBlendEnable(desc.blending_enabled); |
| |
| res.setSrcColorBlendFactor(ToVKBlendFactor(desc.src_color_blend_factor)); |
| res.setColorBlendOp(ToVKBlendOp(desc.color_blend_op)); |
| res.setDstColorBlendFactor(ToVKBlendFactor(desc.dst_color_blend_factor)); |
| |
| res.setSrcAlphaBlendFactor(ToVKBlendFactor(desc.src_alpha_blend_factor)); |
| res.setAlphaBlendOp(ToVKBlendOp(desc.alpha_blend_op)); |
| res.setDstAlphaBlendFactor(ToVKBlendFactor(desc.dst_alpha_blend_factor)); |
| |
| res.setColorWriteMask(ToVKColorComponentFlags(desc.write_mask)); |
| |
| return res; |
| } |
| |
| constexpr std::optional<vk::ShaderStageFlagBits> ToVKShaderStageFlagBits( |
| ShaderStage stage) { |
| switch (stage) { |
| case ShaderStage::kUnknown: |
| return std::nullopt; |
| case ShaderStage::kVertex: |
| return vk::ShaderStageFlagBits::eVertex; |
| case ShaderStage::kFragment: |
| return vk::ShaderStageFlagBits::eFragment; |
| case ShaderStage::kTessellationControl: |
| return vk::ShaderStageFlagBits::eTessellationControl; |
| case ShaderStage::kTessellationEvaluation: |
| return vk::ShaderStageFlagBits::eTessellationEvaluation; |
| case ShaderStage::kCompute: |
| return vk::ShaderStageFlagBits::eCompute; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::Format ToVKImageFormat(PixelFormat format) { |
| switch (format) { |
| case PixelFormat::kUnknown: |
| case PixelFormat::kB10G10R10XR: |
| case PixelFormat::kB10G10R10A10XR: |
| case PixelFormat::kB10G10R10XRSRGB: |
| return vk::Format::eUndefined; |
| case PixelFormat::kA8UNormInt: |
| // TODO(csg): This is incorrect. Don't depend on swizzle support for GLES. |
| return vk::Format::eR8Unorm; |
| case PixelFormat::kR8G8B8A8UNormInt: |
| return vk::Format::eR8G8B8A8Unorm; |
| case PixelFormat::kR8G8B8A8UNormIntSRGB: |
| return vk::Format::eR8G8B8A8Srgb; |
| case PixelFormat::kB8G8R8A8UNormInt: |
| return vk::Format::eB8G8R8A8Unorm; |
| case PixelFormat::kB8G8R8A8UNormIntSRGB: |
| return vk::Format::eB8G8R8A8Srgb; |
| case PixelFormat::kR32G32B32A32Float: |
| return vk::Format::eR32G32B32A32Sfloat; |
| case PixelFormat::kR16G16B16A16Float: |
| return vk::Format::eR16G16B16A16Sfloat; |
| case PixelFormat::kS8UInt: |
| return vk::Format::eS8Uint; |
| case PixelFormat::kD32FloatS8UInt: |
| return vk::Format::eD32SfloatS8Uint; |
| case PixelFormat::kR8UNormInt: |
| return vk::Format::eR8Unorm; |
| case PixelFormat::kR8G8UNormInt: |
| return vk::Format::eR8G8Unorm; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr PixelFormat ToPixelFormat(vk::Format format) { |
| switch (format) { |
| case vk::Format::eUndefined: |
| return PixelFormat::kUnknown; |
| case vk::Format::eR8G8B8A8Unorm: |
| return PixelFormat::kR8G8B8A8UNormInt; |
| case vk::Format::eR8G8B8A8Srgb: |
| return PixelFormat::kR8G8B8A8UNormIntSRGB; |
| case vk::Format::eB8G8R8A8Unorm: |
| return PixelFormat::kB8G8R8A8UNormInt; |
| case vk::Format::eB8G8R8A8Srgb: |
| return PixelFormat::kB8G8R8A8UNormIntSRGB; |
| case vk::Format::eR32G32B32A32Sfloat: |
| return PixelFormat::kR32G32B32A32Float; |
| case vk::Format::eR16G16B16A16Sfloat: |
| return PixelFormat::kR16G16B16A16Float; |
| case vk::Format::eS8Uint: |
| return PixelFormat::kS8UInt; |
| case vk::Format::eD32SfloatS8Uint: |
| return PixelFormat::kD32FloatS8UInt; |
| case vk::Format::eR8Unorm: |
| return PixelFormat::kR8UNormInt; |
| case vk::Format::eR8G8Unorm: |
| return PixelFormat::kR8G8UNormInt; |
| default: |
| return PixelFormat::kUnknown; |
| } |
| } |
| |
| constexpr vk::SampleCountFlagBits ToVKSampleCount(SampleCount sample_count) { |
| switch (sample_count) { |
| case SampleCount::kCount1: |
| return vk::SampleCountFlagBits::e1; |
| case SampleCount::kCount4: |
| return vk::SampleCountFlagBits::e4; |
| } |
| } |
| |
| constexpr vk::Filter ToVKSamplerMinMagFilter(MinMagFilter filter) { |
| switch (filter) { |
| case MinMagFilter::kNearest: |
| return vk::Filter::eNearest; |
| case MinMagFilter::kLinear: |
| return vk::Filter::eLinear; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::SamplerMipmapMode ToVKSamplerMipmapMode(MipFilter filter) { |
| vk::SamplerCreateInfo sampler_info; |
| switch (filter) { |
| case MipFilter::kNearest: |
| return vk::SamplerMipmapMode::eNearest; |
| case MipFilter::kLinear: |
| return vk::SamplerMipmapMode::eLinear; |
| case MipFilter::kNone: |
| return vk::SamplerMipmapMode::eNearest; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::SamplerAddressMode ToVKSamplerAddressMode( |
| SamplerAddressMode mode) { |
| switch (mode) { |
| case SamplerAddressMode::kRepeat: |
| return vk::SamplerAddressMode::eRepeat; |
| case SamplerAddressMode::kMirror: |
| return vk::SamplerAddressMode::eMirroredRepeat; |
| case SamplerAddressMode::kClampToEdge: |
| return vk::SamplerAddressMode::eClampToEdge; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::ShaderStageFlags ToVkShaderStage(ShaderStage stage) { |
| switch (stage) { |
| case ShaderStage::kUnknown: |
| return vk::ShaderStageFlagBits::eAll; |
| case ShaderStage::kFragment: |
| return vk::ShaderStageFlagBits::eFragment; |
| case ShaderStage::kTessellationControl: |
| return vk::ShaderStageFlagBits::eTessellationControl; |
| case ShaderStage::kTessellationEvaluation: |
| return vk::ShaderStageFlagBits::eTessellationEvaluation; |
| case ShaderStage::kCompute: |
| return vk::ShaderStageFlagBits::eCompute; |
| case ShaderStage::kVertex: |
| return vk::ShaderStageFlagBits::eVertex; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::DescriptorSetLayoutBinding ToVKDescriptorSetLayoutBinding( |
| const DescriptorSetLayout& layout) { |
| vk::DescriptorSetLayoutBinding binding; |
| binding.binding = layout.binding; |
| binding.descriptorCount = layout.descriptor_count; |
| vk::DescriptorType desc_type = vk::DescriptorType(); |
| switch (layout.descriptor_type) { |
| case DescriptorType::kSampledImage: |
| desc_type = vk::DescriptorType::eCombinedImageSampler; |
| break; |
| case DescriptorType::kUniformBuffer: |
| desc_type = vk::DescriptorType::eUniformBuffer; |
| break; |
| } |
| binding.descriptorType = desc_type; |
| binding.stageFlags = ToVkShaderStage(layout.shader_stage); |
| return binding; |
| } |
| |
| constexpr vk::AttachmentLoadOp ToVKAttachmentLoadOp(LoadAction load_action) { |
| switch (load_action) { |
| case LoadAction::kLoad: |
| return vk::AttachmentLoadOp::eLoad; |
| case LoadAction::kClear: |
| return vk::AttachmentLoadOp::eClear; |
| case LoadAction::kDontCare: |
| return vk::AttachmentLoadOp::eDontCare; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::AttachmentStoreOp ToVKAttachmentStoreOp( |
| StoreAction store_action) { |
| switch (store_action) { |
| case StoreAction::kStore: |
| return vk::AttachmentStoreOp::eStore; |
| case StoreAction::kDontCare: |
| return vk::AttachmentStoreOp::eDontCare; |
| case StoreAction::kMultisampleResolve: |
| case StoreAction::kStoreAndMultisampleResolve: |
| return vk::AttachmentStoreOp::eDontCare; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::IndexType ToVKIndexType(IndexType index_type) { |
| switch (index_type) { |
| case IndexType::k16bit: |
| return vk::IndexType::eUint16; |
| case IndexType::k32bit: |
| return vk::IndexType::eUint32; |
| case IndexType::kUnknown: |
| return vk::IndexType::eUint32; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::PolygonMode ToVKPolygonMode(PolygonMode mode) { |
| switch (mode) { |
| case PolygonMode::kFill: |
| return vk::PolygonMode::eFill; |
| case PolygonMode::kLine: |
| return vk::PolygonMode::eLine; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::PrimitiveTopology ToVKPrimitiveTopology(PrimitiveType primitive) { |
| switch (primitive) { |
| case PrimitiveType::kTriangle: |
| return vk::PrimitiveTopology::eTriangleList; |
| case PrimitiveType::kTriangleStrip: |
| return vk::PrimitiveTopology::eTriangleStrip; |
| case PrimitiveType::kLine: |
| return vk::PrimitiveTopology::eLineList; |
| case PrimitiveType::kLineStrip: |
| return vk::PrimitiveTopology::eLineStrip; |
| case PrimitiveType::kPoint: |
| return vk::PrimitiveTopology::ePointList; |
| } |
| |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr bool PixelFormatIsDepthStencil(PixelFormat format) { |
| switch (format) { |
| case PixelFormat::kUnknown: |
| case PixelFormat::kA8UNormInt: |
| case PixelFormat::kR8UNormInt: |
| case PixelFormat::kR8G8UNormInt: |
| case PixelFormat::kR8G8B8A8UNormInt: |
| case PixelFormat::kR8G8B8A8UNormIntSRGB: |
| case PixelFormat::kB8G8R8A8UNormInt: |
| case PixelFormat::kB8G8R8A8UNormIntSRGB: |
| case PixelFormat::kR32G32B32A32Float: |
| case PixelFormat::kR16G16B16A16Float: |
| case PixelFormat::kB10G10R10XR: |
| case PixelFormat::kB10G10R10XRSRGB: |
| case PixelFormat::kB10G10R10A10XR: |
| return false; |
| case PixelFormat::kS8UInt: |
| case PixelFormat::kD32FloatS8UInt: |
| return true; |
| } |
| return false; |
| } |
| |
| enum class AttachmentKind { |
| kColor, |
| kDepth, |
| kStencil, |
| }; |
| |
| constexpr vk::AttachmentDescription CreateAttachmentDescription( |
| PixelFormat format, |
| SampleCount sample_count, |
| AttachmentKind kind, |
| LoadAction load_action, |
| StoreAction store_action) { |
| vk::AttachmentDescription vk_attachment; |
| |
| vk_attachment.format = ToVKImageFormat(format); |
| vk_attachment.samples = ToVKSampleCount(sample_count); |
| |
| // The Vulkan spec has somewhat complicated rules for when these ops are used |
| // and ignored. Just set safe defaults. |
| vk_attachment.loadOp = vk::AttachmentLoadOp::eDontCare; |
| vk_attachment.storeOp = vk::AttachmentStoreOp::eDontCare; |
| vk_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare; |
| vk_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare; |
| |
| switch (kind) { |
| case AttachmentKind::kColor: |
| // If the attachment uses a color format, then loadOp and storeOp are |
| // used, and stencilLoadOp and stencilStoreOp are ignored. |
| vk_attachment.loadOp = ToVKAttachmentLoadOp(load_action); |
| vk_attachment.storeOp = ToVKAttachmentStoreOp(store_action); |
| break; |
| case AttachmentKind::kDepth: |
| // If the format has depth and/or stencil components, loadOp and storeOp |
| // apply only to the depth data, while stencilLoadOp and stencilStoreOp |
| // define how the stencil data is handled. |
| vk_attachment.loadOp = ToVKAttachmentLoadOp(load_action); |
| vk_attachment.storeOp = ToVKAttachmentStoreOp(store_action); |
| [[fallthrough]]; |
| case AttachmentKind::kStencil: |
| vk_attachment.stencilLoadOp = ToVKAttachmentLoadOp(load_action); |
| vk_attachment.stencilStoreOp = ToVKAttachmentStoreOp(store_action); |
| break; |
| } |
| |
| switch (kind) { |
| case AttachmentKind::kColor: |
| vk_attachment.initialLayout = vk_attachment.finalLayout = |
| vk::ImageLayout::eColorAttachmentOptimal; |
| break; |
| case AttachmentKind::kDepth: |
| case AttachmentKind::kStencil: |
| // Separate depth stencil layouts feature is only available in Vulkan 1.2. |
| vk_attachment.initialLayout = vk_attachment.finalLayout = |
| vk::ImageLayout::eDepthStencilAttachmentOptimal; |
| break; |
| } |
| |
| return vk_attachment; |
| } |
| |
| static constexpr vk::AttachmentReference kUnusedAttachmentReference = { |
| VK_ATTACHMENT_UNUSED, vk::ImageLayout::eUndefined}; |
| |
| constexpr vk::CullModeFlags ToVKCullModeFlags(CullMode mode) { |
| switch (mode) { |
| case CullMode::kNone: |
| return vk::CullModeFlagBits::eNone; |
| case CullMode::kFrontFace: |
| return vk::CullModeFlagBits::eFront; |
| case CullMode::kBackFace: |
| return vk::CullModeFlagBits::eBack; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::CompareOp ToVKCompareOp(CompareFunction op) { |
| switch (op) { |
| case CompareFunction::kNever: |
| return vk::CompareOp::eNever; |
| case CompareFunction::kAlways: |
| return vk::CompareOp::eAlways; |
| case CompareFunction::kLess: |
| return vk::CompareOp::eLess; |
| case CompareFunction::kEqual: |
| return vk::CompareOp::eEqual; |
| case CompareFunction::kLessEqual: |
| return vk::CompareOp::eLessOrEqual; |
| case CompareFunction::kGreater: |
| return vk::CompareOp::eGreater; |
| case CompareFunction::kNotEqual: |
| return vk::CompareOp::eNotEqual; |
| case CompareFunction::kGreaterEqual: |
| return vk::CompareOp::eGreaterOrEqual; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::StencilOp ToVKStencilOp(StencilOperation op) { |
| switch (op) { |
| case StencilOperation::kKeep: |
| return vk::StencilOp::eKeep; |
| case StencilOperation::kZero: |
| return vk::StencilOp::eZero; |
| case StencilOperation::kSetToReferenceValue: |
| return vk::StencilOp::eReplace; |
| case StencilOperation::kIncrementClamp: |
| return vk::StencilOp::eIncrementAndClamp; |
| case StencilOperation::kDecrementClamp: |
| return vk::StencilOp::eDecrementAndClamp; |
| case StencilOperation::kInvert: |
| return vk::StencilOp::eInvert; |
| case StencilOperation::kIncrementWrap: |
| return vk::StencilOp::eIncrementAndWrap; |
| case StencilOperation::kDecrementWrap: |
| return vk::StencilOp::eDecrementAndWrap; |
| break; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr vk::StencilOpState ToVKStencilOpState( |
| const StencilAttachmentDescriptor& desc) { |
| vk::StencilOpState state; |
| state.failOp = ToVKStencilOp(desc.stencil_failure); |
| state.passOp = ToVKStencilOp(desc.depth_stencil_pass); |
| state.depthFailOp = ToVKStencilOp(desc.depth_failure); |
| state.compareOp = ToVKCompareOp(desc.stencil_compare); |
| state.compareMask = desc.read_mask; |
| state.writeMask = desc.write_mask; |
| // This is irrelevant as the stencil references are always dynamic state and |
| // will be set in the render pass. |
| state.reference = 1988; |
| return state; |
| } |
| |
| constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format) { |
| switch (format) { |
| case PixelFormat::kUnknown: |
| case PixelFormat::kA8UNormInt: |
| case PixelFormat::kR8UNormInt: |
| case PixelFormat::kR8G8UNormInt: |
| case PixelFormat::kR8G8B8A8UNormInt: |
| case PixelFormat::kR8G8B8A8UNormIntSRGB: |
| case PixelFormat::kB8G8R8A8UNormInt: |
| case PixelFormat::kB8G8R8A8UNormIntSRGB: |
| case PixelFormat::kR32G32B32A32Float: |
| case PixelFormat::kR16G16B16A16Float: |
| case PixelFormat::kB10G10R10XR: |
| case PixelFormat::kB10G10R10XRSRGB: |
| case PixelFormat::kB10G10R10A10XR: |
| return vk::ImageAspectFlagBits::eColor; |
| case PixelFormat::kS8UInt: |
| return vk::ImageAspectFlagBits::eStencil; |
| case PixelFormat::kD32FloatS8UInt: |
| return vk::ImageAspectFlagBits::eDepth | |
| vk::ImageAspectFlagBits::eStencil; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| constexpr uint32_t ToArrayLayerCount(TextureType type) { |
| switch (type) { |
| case TextureType::kTexture2D: |
| case TextureType::kTexture2DMultisample: |
| return 1u; |
| case TextureType::kTextureCube: |
| return 6u; |
| } |
| FML_UNREACHABLE(); |
| } |
| |
| vk::PipelineDepthStencilStateCreateInfo ToVKPipelineDepthStencilStateCreateInfo( |
| std::optional<DepthAttachmentDescriptor> depth, |
| std::optional<StencilAttachmentDescriptor> front, |
| std::optional<StencilAttachmentDescriptor> back); |
| |
| } // namespace impeller |