blob: 6cc151e81939c77f53a3f7ecc956fe40cf1f3f28 [file] [log] [blame]
// 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 <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/scalar.h"
namespace impeller {
class Texture;
//------------------------------------------------------------------------------
/// @brief Specified where the allocation resides and how it is used.
///
enum class StorageMode {
//----------------------------------------------------------------------------
/// Allocations can be mapped onto the hosts address space and also be used by
/// the device.
///
kHostVisible,
//----------------------------------------------------------------------------
/// Allocations can only be used by the device. This location is optimal for
/// use by the device. If the host needs to access these allocations, the
/// transfer queue must be used to transfer this allocation onto the a host
/// visible buffer.
///
kDevicePrivate,
//----------------------------------------------------------------------------
/// Used by the device for temporary render targets. These allocations cannot
/// be transferred from and to other allocations using the transfer queue.
/// Render pass cannot initialize the contents of these buffers using load and
/// store actions.
///
/// These allocations reside in tile memory which has higher bandwidth, lower
/// latency and lower power consumption. The total device memory usage is
/// also lower as a separate allocation does not need to be created in
/// device memory. Prefer using these allocations for intermediates like depth
/// and stencil buffers.
///
kDeviceTransient,
};
constexpr const char* StorageModeToString(StorageMode mode) {
switch (mode) {
case StorageMode::kHostVisible:
return "HostVisible";
case StorageMode::kDevicePrivate:
return "DevicePrivate";
case StorageMode::kDeviceTransient:
return "DeviceTransient";
}
FML_UNREACHABLE();
}
//------------------------------------------------------------------------------
/// @brief The Pixel formats supported by Impeller. The naming convention
/// denotes the usage of the component, the bit width of that
/// component, and then one or more qualifiers to its
/// interpretation.
///
/// For instance, `kR8G8B8A8UNormIntSRGB` is a 32 bits-per-pixel
/// format ordered in RGBA with 8 bits per component with each
/// component expressed as an unsigned normalized integer and a
/// conversion from sRGB to linear color space.
///
/// Key:
/// R -> Red Component
/// G -> Green Component
/// B -> Blue Component
/// D -> Depth Component
/// S -> Stencil Component
/// U -> Unsigned (Lack of this denotes a signed component)
/// Norm -> Normalized
/// SRGB -> sRGB to linear interpretation
///
/// While the effective bit width of the pixel can be determined by
/// adding up the widths of each component, only the non-esoteric
/// formats are tightly packed. Do not assume tight packing for the
/// esoteric formats and use blit passes to convert to a
/// non-esoteric pass.
///
enum class PixelFormat {
kUnknown,
kA8UNormInt,
kR8UNormInt,
kR8G8UNormInt,
kR8G8B8A8UNormInt,
kR8G8B8A8UNormIntSRGB,
kB8G8R8A8UNormInt,
kB8G8R8A8UNormIntSRGB,
kR32G32B32A32Float,
kR16G16B16A16Float,
kB10G10R10XR,
kB10G10R10XRSRGB,
kB10G10R10A10XR,
// Depth and stencil formats.
kS8UInt,
kD24UnormS8Uint,
kD32FloatS8UInt,
};
constexpr const char* PixelFormatToString(PixelFormat format) {
switch (format) {
case PixelFormat::kUnknown:
return "Unknown";
case PixelFormat::kA8UNormInt:
return "A8UNormInt";
case PixelFormat::kR8UNormInt:
return "R8UNormInt";
case PixelFormat::kR8G8UNormInt:
return "R8G8UNormInt";
case PixelFormat::kR8G8B8A8UNormInt:
return "R8G8B8A8UNormInt";
case PixelFormat::kR8G8B8A8UNormIntSRGB:
return "R8G8B8A8UNormIntSRGB";
case PixelFormat::kB8G8R8A8UNormInt:
return "B8G8R8A8UNormInt";
case PixelFormat::kB8G8R8A8UNormIntSRGB:
return "B8G8R8A8UNormIntSRGB";
case PixelFormat::kR32G32B32A32Float:
return "R32G32B32A32Float";
case PixelFormat::kR16G16B16A16Float:
return "R16G16B16A16Float";
case PixelFormat::kB10G10R10XR:
return "B10G10R10XR";
case PixelFormat::kB10G10R10XRSRGB:
return "B10G10R10XRSRGB";
case PixelFormat::kB10G10R10A10XR:
return "B10G10R10A10XR";
case PixelFormat::kS8UInt:
return "S8UInt";
case PixelFormat::kD24UnormS8Uint:
return "D24UnormS8Uint";
case PixelFormat::kD32FloatS8UInt:
return "D32FloatS8UInt";
}
FML_UNREACHABLE();
}
enum class BlendFactor {
kZero,
kOne,
kSourceColor,
kOneMinusSourceColor,
kSourceAlpha,
kOneMinusSourceAlpha,
kDestinationColor,
kOneMinusDestinationColor,
kDestinationAlpha,
kOneMinusDestinationAlpha,
kSourceAlphaSaturated,
kBlendColor,
kOneMinusBlendColor,
kBlendAlpha,
kOneMinusBlendAlpha,
};
enum class BlendOperation {
kAdd,
kSubtract,
kReverseSubtract,
};
enum class LoadAction {
kDontCare,
kLoad,
kClear,
};
enum class StoreAction {
kDontCare,
kStore,
kMultisampleResolve,
kStoreAndMultisampleResolve,
};
constexpr const char* LoadActionToString(LoadAction action) {
switch (action) {
case LoadAction::kDontCare:
return "DontCare";
case LoadAction::kLoad:
return "Load";
case LoadAction::kClear:
return "Clear";
}
}
constexpr const char* StoreActionToString(StoreAction action) {
switch (action) {
case StoreAction::kDontCare:
return "DontCare";
case StoreAction::kStore:
return "Store";
case StoreAction::kMultisampleResolve:
return "MultisampleResolve";
case StoreAction::kStoreAndMultisampleResolve:
return "StoreAndMultisampleResolve";
}
}
constexpr bool CanClearAttachment(LoadAction action) {
switch (action) {
case LoadAction::kLoad:
return false;
case LoadAction::kDontCare:
case LoadAction::kClear:
return true;
}
FML_UNREACHABLE();
}
constexpr bool CanDiscardAttachmentWhenDone(StoreAction action) {
switch (action) {
case StoreAction::kStore:
case StoreAction::kStoreAndMultisampleResolve:
return false;
case StoreAction::kDontCare:
case StoreAction::kMultisampleResolve:
return true;
}
FML_UNREACHABLE();
}
enum class TextureType {
kTexture2D,
kTexture2DMultisample,
kTextureCube,
kTextureExternalOES,
};
constexpr const char* TextureTypeToString(TextureType type) {
switch (type) {
case TextureType::kTexture2D:
return "Texture2D";
case TextureType::kTexture2DMultisample:
return "Texture2DMultisample";
case TextureType::kTextureCube:
return "TextureCube";
case TextureType::kTextureExternalOES:
return "TextureExternalOES";
}
FML_UNREACHABLE();
}
constexpr bool IsMultisampleCapable(TextureType type) {
switch (type) {
case TextureType::kTexture2D:
case TextureType::kTextureCube:
case TextureType::kTextureExternalOES:
return false;
case TextureType::kTexture2DMultisample:
return true;
}
return false;
}
enum class SampleCount {
kCount1 = 1,
kCount4 = 4,
};
using TextureUsageMask = uint64_t;
enum class TextureUsage : TextureUsageMask {
kUnknown = 0,
kShaderRead = 1 << 0,
kShaderWrite = 1 << 1,
kRenderTarget = 1 << 2,
};
constexpr bool TextureUsageIsRenderTarget(TextureUsageMask mask) {
return static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) & mask;
}
constexpr const char* TextureUsageToString(TextureUsage usage) {
switch (usage) {
case TextureUsage::kUnknown:
return "Unknown";
case TextureUsage::kShaderRead:
return "ShaderRead";
case TextureUsage::kShaderWrite:
return "ShaderWrite";
case TextureUsage::kRenderTarget:
return "RenderTarget";
}
FML_UNREACHABLE();
}
std::string TextureUsageMaskToString(TextureUsageMask mask);
// Texture coordinate system.
enum class TextureCoordinateSystem {
// Alternative coordinate system used when uploading texture data from the
// host.
// (0, 0) is the bottom-left of the image with +Y going up.
kUploadFromHost,
// Default coordinate system.
// (0, 0) is the top-left of the image with +Y going down.
kRenderToTexture,
};
enum class CullMode {
kNone,
kFrontFace,
kBackFace,
};
enum class IndexType {
kUnknown,
k16bit,
k32bit,
/// Does not use the index buffer.
kNone,
};
enum class PrimitiveType {
kTriangle,
kTriangleStrip,
kLine,
kLineStrip,
kPoint,
// Triangle fans are implementation dependent and need extra extensions
// checks. Hence, they are not supported here.
};
enum class PolygonMode {
kFill,
kLine,
};
struct DepthRange {
Scalar z_near = 0.0;
Scalar z_far = 1.0;
constexpr bool operator==(const DepthRange& other) const {
return z_near == other.z_near && z_far == other.z_far;
}
};
struct Viewport {
Rect rect;
DepthRange depth_range;
constexpr bool operator==(const Viewport& other) const {
return rect == other.rect && depth_range == other.depth_range;
}
};
enum class MinMagFilter {
/// Select nearest to the sample point. Most widely supported.
kNearest,
/// Select two points and linearly interpolate between them. Some formats
/// may not support this.
kLinear,
};
enum class MipFilter {
/// Sample from the nearest mip level.
kNearest,
/// Sample from the two nearest mip levels and linearly interpolate between
/// them.
kLinear,
};
enum class SamplerAddressMode {
kClampToEdge,
kRepeat,
kMirror,
// More modes are almost always supported but they are usually behind
// extensions checks. The ones current in these structs are safe (always
// supported) defaults.
/// @brief decal sampling mode is only supported on devices that pass
/// the `Capabilities.SupportsDecalSamplerAddressMode` check.
kDecal,
};
enum class ColorWriteMask : uint64_t {
kNone = 0,
kRed = 1 << 0,
kGreen = 1 << 1,
kBlue = 1 << 2,
kAlpha = 1 << 3,
kAll = kRed | kGreen | kBlue | kAlpha,
};
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) {
switch (format) {
case PixelFormat::kUnknown:
return 0u;
case PixelFormat::kA8UNormInt:
case PixelFormat::kR8UNormInt:
case PixelFormat::kS8UInt:
return 1u;
case PixelFormat::kR8G8UNormInt:
return 2u;
case PixelFormat::kR8G8B8A8UNormInt:
case PixelFormat::kR8G8B8A8UNormIntSRGB:
case PixelFormat::kB8G8R8A8UNormInt:
case PixelFormat::kB8G8R8A8UNormIntSRGB:
case PixelFormat::kB10G10R10XRSRGB:
case PixelFormat::kB10G10R10XR:
return 4u;
case PixelFormat::kD24UnormS8Uint:
return 4u;
case PixelFormat::kD32FloatS8UInt:
return 5u;
case PixelFormat::kR16G16B16A16Float:
case PixelFormat::kB10G10R10A10XR:
return 8u;
case PixelFormat::kR32G32B32A32Float:
return 16u;
}
return 0u;
}
//------------------------------------------------------------------------------
/// @brief Describe the color attachment that will be used with this
/// pipeline.
///
/// Blending at specific color attachments follows the pseudo-code:
/// ```
/// if (blending_enabled) {
/// final_color.rgb = (src_color_blend_factor * new_color.rgb)
/// <color_blend_op>
/// (dst_color_blend_factor * old_color.rgb);
/// final_color.a = (src_alpha_blend_factor * new_color.a)
/// <alpha_blend_op>
/// (dst_alpha_blend_factor * old_color.a);
/// } else {
/// final_color = new_color;
/// }
/// // IMPORTANT: The write mask is applied irrespective of whether
/// // blending_enabled is set.
/// final_color = final_color & write_mask;
/// ```
///
/// The default blend mode is 1 - source alpha.
struct ColorAttachmentDescriptor {
PixelFormat format = PixelFormat::kUnknown;
bool blending_enabled = false;
BlendFactor src_color_blend_factor = BlendFactor::kSourceAlpha;
BlendOperation color_blend_op = BlendOperation::kAdd;
BlendFactor dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha;
BlendFactor src_alpha_blend_factor = BlendFactor::kSourceAlpha;
BlendOperation alpha_blend_op = BlendOperation::kAdd;
BlendFactor dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha;
std::underlying_type_t<ColorWriteMask> write_mask =
static_cast<uint64_t>(ColorWriteMask::kAll);
constexpr bool operator==(const ColorAttachmentDescriptor& o) const {
return format == o.format && //
blending_enabled == o.blending_enabled && //
src_color_blend_factor == o.src_color_blend_factor && //
color_blend_op == o.color_blend_op && //
dst_color_blend_factor == o.dst_color_blend_factor && //
src_alpha_blend_factor == o.src_alpha_blend_factor && //
alpha_blend_op == o.alpha_blend_op && //
dst_alpha_blend_factor == o.dst_alpha_blend_factor && //
write_mask == o.write_mask;
}
constexpr size_t Hash() const {
return fml::HashCombine(format, blending_enabled, src_color_blend_factor,
color_blend_op, dst_color_blend_factor,
src_alpha_blend_factor, alpha_blend_op,
dst_alpha_blend_factor, write_mask);
}
};
enum class CompareFunction {
/// Comparison test never passes.
kNever,
/// Comparison test passes always passes.
kAlways,
/// Comparison test passes if new_value < current_value.
kLess,
/// Comparison test passes if new_value == current_value.
kEqual,
/// Comparison test passes if new_value <= current_value.
kLessEqual,
/// Comparison test passes if new_value > current_value.
kGreater,
/// Comparison test passes if new_value != current_value.
kNotEqual,
/// Comparison test passes if new_value >= current_value.
kGreaterEqual,
};
enum class StencilOperation {
/// Don't modify the current stencil value.
kKeep,
/// Reset the stencil value to zero.
kZero,
/// Reset the stencil value to the reference value.
kSetToReferenceValue,
/// Increment the current stencil value by 1. Clamp it to the maximum.
kIncrementClamp,
/// Decrement the current stencil value by 1. Clamp it to zero.
kDecrementClamp,
/// Perform a logical bitwise invert on the current stencil value.
kInvert,
/// Increment the current stencil value by 1. If at maximum, set to zero.
kIncrementWrap,
/// Decrement the current stencil value by 1. If at zero, set to maximum.
kDecrementWrap,
};
struct DepthAttachmentDescriptor {
//----------------------------------------------------------------------------
/// Indicates how to compare the value with that in the depth buffer.
///
CompareFunction depth_compare = CompareFunction::kAlways;
//----------------------------------------------------------------------------
/// Indicates when writes must be performed to the depth buffer.
///
bool depth_write_enabled = false;
constexpr bool operator==(const DepthAttachmentDescriptor& o) const {
return depth_compare == o.depth_compare &&
depth_write_enabled == o.depth_write_enabled;
}
constexpr size_t GetHash() const {
return fml::HashCombine(depth_compare, depth_write_enabled);
}
};
struct StencilAttachmentDescriptor {
//----------------------------------------------------------------------------
/// Indicates the operation to perform between the reference value and the
/// value in the stencil buffer. Both values have the read_mask applied to
/// them before performing this operation.
///
CompareFunction stencil_compare = CompareFunction::kAlways;
//----------------------------------------------------------------------------
/// Indicates what to do when the stencil test has failed.
///
StencilOperation stencil_failure = StencilOperation::kKeep;
//----------------------------------------------------------------------------
/// Indicates what to do when the stencil test passes but the depth test
/// fails.
///
StencilOperation depth_failure = StencilOperation::kKeep;
//----------------------------------------------------------------------------
/// Indicates what to do when both the stencil and depth tests pass.
///
StencilOperation depth_stencil_pass = StencilOperation::kKeep;
//----------------------------------------------------------------------------
/// The mask applied to the reference and stencil buffer values before
/// performing the stencil_compare operation.
///
uint32_t read_mask = ~0;
//----------------------------------------------------------------------------
/// The mask applied to the new stencil value before it is written into the
/// stencil buffer.
///
uint32_t write_mask = ~0;
constexpr bool operator==(const StencilAttachmentDescriptor& o) const {
return stencil_compare == o.stencil_compare &&
stencil_failure == o.stencil_failure &&
depth_failure == o.depth_failure &&
depth_stencil_pass == o.depth_stencil_pass &&
read_mask == o.read_mask && write_mask == o.write_mask;
}
constexpr size_t GetHash() const {
return fml::HashCombine(stencil_compare, stencil_failure, depth_failure,
depth_stencil_pass, read_mask, write_mask);
}
};
struct Attachment {
std::shared_ptr<Texture> texture;
std::shared_ptr<Texture> resolve_texture;
LoadAction load_action = LoadAction::kDontCare;
StoreAction store_action = StoreAction::kStore;
bool IsValid() const;
};
struct ColorAttachment : public Attachment {
Color clear_color = Color::BlackTransparent();
};
struct DepthAttachment : public Attachment {
double clear_depth = 0.0;
};
struct StencilAttachment : public Attachment {
uint32_t clear_stencil = 0;
};
std::string AttachmentToString(const Attachment& attachment);
std::string ColorAttachmentToString(const ColorAttachment& color);
std::string DepthAttachmentToString(const DepthAttachment& depth);
std::string StencilAttachmentToString(const StencilAttachment& stencil);
} // namespace impeller
namespace std {
template <>
struct hash<impeller::DepthAttachmentDescriptor> {
constexpr std::size_t operator()(
const impeller::DepthAttachmentDescriptor& des) const {
return des.GetHash();
}
};
template <>
struct hash<impeller::StencilAttachmentDescriptor> {
constexpr std::size_t operator()(
const impeller::StencilAttachmentDescriptor& des) const {
return des.GetHash();
}
};
} // namespace std