blob: 49f293c15d29d3efecf7abe401ff37668cdf7889 [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_graphite_VulkanCaps_DEFINED
#define skgpu_graphite_VulkanCaps_DEFINED
#include "include/private/base/SkTDArray.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/vk/VulkanInterface.h"
#include "src/gpu/vk/VulkanUtilsPriv.h"
namespace skgpu::graphite {
struct ContextOptions;
class VulkanTextureInfo;
class VulkanCaps final : public Caps {
public:
VulkanCaps(const ContextOptions&,
const skgpu::VulkanInterface*,
VkPhysicalDevice,
uint32_t physicalDeviceVersion,
const VkPhysicalDeviceFeatures2*,
const skgpu::VulkanExtensions*,
Protected);
~VulkanCaps() override;
bool isSampleCountSupported(TextureFormat, uint8_t requestedSampleCount) const override;
TextureFormat getDepthStencilFormat(SkEnumBitMask<DepthStencilFlags>) const override;
TextureInfo getDefaultAttachmentTextureInfo(AttachmentDesc,
Protected,
Discardable) const override;
TextureInfo getDefaultSampledTextureInfo(SkColorType,
Mipmapped,
Protected,
Renderable) const override;
TextureInfo getTextureInfoForSampledCopy(const TextureInfo&, Mipmapped) const override;
TextureInfo getDefaultCompressedTextureInfo(SkTextureCompressionType,
Mipmapped,
Protected) const override;
TextureInfo getDefaultStorageTextureInfo(SkColorType) const override;
// Override Caps's implementation in order to consult Vulkan-specific texture properties.
DstReadStrategy getDstReadStrategy() const override;
ImmutableSamplerInfo getImmutableSamplerInfo(const TextureInfo&) const override;
UniqueKey makeGraphicsPipelineKey(const GraphicsPipelineDesc&,
const RenderPassDesc&) const override;
bool extractGraphicsDescs(const UniqueKey&,
GraphicsPipelineDesc*,
RenderPassDesc*,
const RendererProvider*) const override;
UniqueKey makeComputePipelineKey(const ComputePipelineDesc&) const override { return {}; }
bool isRenderable(const TextureInfo&) const override;
bool isStorage(const TextureInfo&) const override;
bool isFormatSupported(VkFormat) const;
bool isTexturable(const VulkanTextureInfo&) const;
bool isRenderable(const VulkanTextureInfo&) const;
bool isTransferSrc(const VulkanTextureInfo&) const;
bool isTransferDst(const VulkanTextureInfo&) const;
void buildKeyForTexture(SkISize dimensions,
const TextureInfo&,
ResourceType,
GraphiteResourceKey*) const override;
bool shouldAlwaysUseDedicatedImageMemory() const {
return fShouldAlwaysUseDedicatedImageMemory;
}
// Returns whether a pure GPU accessible buffer is more performant to read than a buffer that is
// also host visible. If so then in some cases we may prefer the cost of doing a copy to the
// buffer. This typically would only be the case for buffers that are written once and read
// many times on the gpu.
bool gpuOnlyBuffersMorePerformant() const { return fGpuOnlyBuffersMorePerformant; }
// For our CPU write and GPU read buffers (vertex, uniform, etc.), we should keep these buffers
// persistently mapped. In general, the answer will be yes. The main case where we don't do this
// is when using special memory that is DEVICE_LOCAL and HOST_VISIBLE on discrete GPUs.
bool shouldPersistentlyMapCpuToGpuBuffers() const {
return fShouldPersistentlyMapCpuToGpuBuffers;
}
bool supportsYcbcrConversion() const { return fSupportsYcbcrConversion; }
bool supportsDeviceFaultInfo() const { return fSupportsDeviceFaultInfo; }
// Whether a barrier is required before reading from input attachments (barrier is needed if
// !coherent).
bool isInputAttachmentReadCoherent() const { return fIsInputAttachmentReadCoherent; }
// isInputAttachmentReadCoherent() is based on whether
// VK_EXT_rasterization_order_attachment_access is supported, but is also enabled on a few
// architectures where it's known a priori that input attachment reads are coherent. The
// following determines whether that extension is enabled (in which case a pipeline creation
// flag is necessary) or not. When disabled, a subpass self-dependency is needed instead.
bool supportsRasterizationOrderColorAttachmentAccess() const {
return fSupportsRasterizationOrderColorAttachmentAccess;
}
uint32_t maxVertexAttributes() const { return fMaxVertexAttributes; }
uint64_t maxUniformBufferRange() const { return fMaxUniformBufferRange; }
uint64_t maxStorageBufferRange() const { return fMaxStorageBufferRange; }
const VkPhysicalDeviceMemoryProperties2& physicalDeviceMemoryProperties2() const {
return fPhysicalDeviceMemoryProperties2;
}
bool mustLoadFullImageForMSAA() const { return fMustLoadFullImageForMSAA; }
bool avoidMSAA() const { return fAvoidMSAA; }
private:
void init(const ContextOptions&,
const skgpu::VulkanInterface*,
VkPhysicalDevice,
uint32_t physicalDeviceVersion,
const VkPhysicalDeviceFeatures2*,
const skgpu::VulkanExtensions*,
Protected);
struct EnabledFeatures {
// VkPhysicalDeviceFeatures
bool fDualSrcBlend = false;
// From VkPhysicalDeviceSamplerYcbcrConversionFeatures or VkPhysicalDeviceVulkan11Features:
bool fSamplerYcbcrConversion = false;
// From VkPhysicalDeviceFaultFeaturesEXT:
bool fDeviceFault = false;
// From VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT:
bool fAdvancedBlendModes = false;
bool fCoherentAdvancedBlendModes = false;
// From VK_EXT_rasterization_order_attachment_access:
bool fRasterizationOrderColorAttachmentAccess = false;
// From VkPhysicalDeviceExtendedDynamicStateFeaturesEXT or Vulkan 1.3 (no features):
bool fExtendedDynamicState = false;
// From VkPhysicalDeviceExtendedDynamicState2FeaturesEXT or Vulkan 1.3 (no features):
bool fExtendedDynamicState2 = false;
// From VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT:
bool fVertexInputDynamicState = false;
// From VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT:
bool fGraphicsPipelineLibrary = false;
// From VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT:
bool fMultisampledRenderToSingleSampled = false;
// From VkPhysicalDeviceHostImageCopyFeatures:
bool fHostImageCopy = false;
};
EnabledFeatures getEnabledFeatures(const VkPhysicalDeviceFeatures2*,
uint32_t physicalDeviceVersion);
struct PhysicalDeviceProperties {
VkPhysicalDeviceProperties2 fBase;
VkPhysicalDeviceDriverProperties fDriver;
VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT fGpl;
VkPhysicalDeviceHostImageCopyPropertiesEXT fHic;
bool fHicHasShaderReadOnlyDstLayout = false;
};
void getProperties(const skgpu::VulkanInterface*,
VkPhysicalDevice,
uint32_t physicalDeviceVersion,
const skgpu::VulkanExtensions*,
const EnabledFeatures&,
PhysicalDeviceProperties*);
void applyDriverCorrectnessWorkarounds(const PhysicalDeviceProperties&);
void initShaderCaps(const EnabledFeatures, const uint32_t vendorID);
void initFormatTable(const skgpu::VulkanInterface*,
VkPhysicalDevice,
const VkPhysicalDeviceProperties&);
void initDepthStencilFormatTable(const skgpu::VulkanInterface*,
VkPhysicalDevice,
const VkPhysicalDeviceProperties&);
const ColorTypeInfo* getColorTypeInfo(SkColorType, const TextureInfo&) const override;
bool onIsTexturable(const TextureInfo&) const override;
bool supportsWritePixels(const TextureInfo&) const override;
bool supportsReadPixels(const TextureInfo&) const override;
std::pair<SkColorType, bool /*isRGBFormat*/> supportedWritePixelsColorType(
SkColorType dstColorType,
const TextureInfo& dstTextureInfo,
SkColorType srcColorType) const override;
std::pair<SkColorType, bool /*isRGBFormat*/> supportedReadPixelsColorType(
SkColorType srcColorType,
const TextureInfo& srcTextureInfo,
SkColorType dstColorType) const override;
/*
* Whether the texture supports multisampled-render-to-single-sampled. When
* VK_EXT_multisampled_render_to_single_sampled is supported, all textures created by Graphite
* that are renderable will support this feature. Textures imported into Graphite however
* depend on whether the application has created the image with the
* VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT flag.
*/
bool msaaTextureRenderToSingleSampledSupport(const TextureInfo&) const override;
// Struct that determines and stores which sample count quantities a VkFormat supports.
struct SupportedSampleCounts {
void initSampleCounts(const skgpu::VulkanInterface*,
const VulkanCaps&,
VkPhysicalDevice,
VkFormat,
VkImageUsageFlags);
bool isSampleCountSupported(int requestedCount) const;
VkSampleCountFlags fSampleCounts = 0;
};
// Struct that determines and stores useful information about VkFormats.
struct FormatInfo {
uint32_t colorTypeFlags(SkColorType colorType) const {
for (int i = 0; i < fColorTypeInfoCount; ++i) {
if (fColorTypeInfos[i].fColorType == colorType) {
return fColorTypeInfos[i].fFlags;
}
}
return 0;
}
void init(const skgpu::VulkanInterface*, const VulkanCaps&, VkPhysicalDevice, VkFormat);
bool isTexturable(VkImageTiling) const;
bool isRenderable(VkImageTiling, uint32_t sampleCount) const;
bool isStorage(VkImageTiling) const;
bool isTransferSrc(VkImageTiling) const;
bool isTransferDst(VkImageTiling) const;
bool isEfficientWithHostImageCopy(VkImageTiling, Protected) const;
std::unique_ptr<ColorTypeInfo[]> fColorTypeInfos;
int fColorTypeInfoCount = 0;
VkFormatProperties fFormatProperties;
SupportedSampleCounts fSupportedSampleCounts;
/*
* The VK_IMAGE_USAGE_HOST_TRANSFER_BIT flag may cause the image to be put in a suboptimal
* physical layout. In practice, images that could have had framebuffer compression end up
* with framebuffer compression disabled. Using `VkHostImageCopyDevicePerformanceQuery`, we
* can determine if the layout is going to be suboptimal and avoid this flag.
*
* `fIsEfficientWithHostImageCopy` indicates whether the VK_IMAGE_USAGE_HOST_TRANSFER_BIT is
* efficient for this format with the following assumptions:
*
* - Image tiling is VK_IMAGE_TILING_OPTIMAL (note that VK_IMAGE_TILING_LINEAR is always
* efficient for host image copy).
* - Image type is 2D.
* - Image create flags is 0.
* - Image usage flags is a subset of VK_IMAGE_USAGE_SAMPLED_BIT |
* VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
* VK_IMAGE_USAGE_TRANSFER_DST_BIT
*/
bool fIsEfficientWithHostImageCopy;
// Indicates that a format is only supported if we are wrapping a texture with it.
SkDEBUGCODE(bool fIsWrappedOnly = false;)
};
// Map SkColorType to VkFormat.
VkFormat fColorTypeToFormatTable[kSkColorTypeCnt];
void setColorType(SkColorType, std::initializer_list<VkFormat> formats);
VkFormat getFormatFromColorType(SkColorType) const;
// Map VkFormat to FormatInfo.
static const size_t kNumVkFormats = 23;
FormatInfo fFormatTable[kNumVkFormats];
FormatInfo& getFormatInfo(VkFormat);
const FormatInfo& getFormatInfo(VkFormat) const;
// A more lightweight equivalent to FormatInfo for depth/stencil VkFormats.
struct DepthStencilFormatInfo {
void init(const skgpu::VulkanInterface*, const VulkanCaps&, VkPhysicalDevice, VkFormat);
bool isDepthStencilSupported(VkFormatFeatureFlags) const;
VkFormatProperties fFormatProperties;
SupportedSampleCounts fSupportedSampleCounts;
};
// Map DepthStencilFlags to VkFormat.
static const size_t kNumDepthStencilFlags = 4;
VkFormat fDepthStencilFlagsToFormatTable[kNumDepthStencilFlags];
// Map depth/stencil VkFormats to DepthStencilFormatInfo.
static const size_t kNumDepthStencilVkFormats = 5;
DepthStencilFormatInfo fDepthStencilFormatTable[kNumDepthStencilVkFormats];
DepthStencilFormatInfo& getDepthStencilFormatInfo(VkFormat);
const DepthStencilFormatInfo& getDepthStencilFormatInfo(VkFormat) const;
uint32_t fMaxVertexAttributes;
uint64_t fMaxUniformBufferRange;
uint64_t fMaxStorageBufferRange;
VkPhysicalDeviceMemoryProperties2 fPhysicalDeviceMemoryProperties2;
// ColorTypeInfo struct for use w/ external formats.
static constexpr SkColorType kExternalFormatColorType = SkColorType::kRGBA_8888_SkColorType;
const ColorTypeInfo fExternalFormatColorTypeInfo = {kExternalFormatColorType,
kExternalFormatColorType,
/*flags=*/0,
skgpu::Swizzle::RGBA(),
skgpu::Swizzle::RGBA()};
// Various bools to define whether certain Vulkan features are supported.
bool fSupportsMemorylessAttachments = false;
bool fSupportsYcbcrConversion = false;
bool fShouldAlwaysUseDedicatedImageMemory = false;
bool fGpuOnlyBuffersMorePerformant = false;
bool fShouldPersistentlyMapCpuToGpuBuffers = true;
bool fSupportsDeviceFaultInfo = false;
bool fSupportsRasterizationOrderColorAttachmentAccess = false;
bool fIsInputAttachmentReadCoherent = false;
// Flags to enable workarounds for driver bugs
bool fMustLoadFullImageForMSAA = false;
bool fAvoidMSAA = false;
};
} // namespace skgpu::graphite
#endif // skgpu_graphite_VulkanCaps_DEFINED