blob: e8af58f02486506e9cb58951cca92d0c76d6d55c [file]
/*
* 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/SkTArray.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;
// Override Caps's implementation in order to consult Vulkan-specific texture properties.
DstReadStrategy getDstReadStrategy() const override;
ImmutableSamplerInfo getImmutableSamplerInfo(const TextureInfo&) const override;
std::string toString(const ImmutableSamplerInfo&) 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 {}; }
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 supportsFrameBoundary() const { return fSupportsFrameBoundary; }
bool supportsPipelineCreationCacheControl() const {
return fSupportsPipelineCreationCacheControl;
}
bool supportsOcclusionQueryPrecise() const { return fOcclusionQueryPrecise; }
uint32_t timestampValidBits(uint32_t queueIndex) const {
return fQueueFamilyTimestampValidBits[queueIndex];
}
float timestampPeriod() const { return fTimestampPeriod; }
private:
void init(const ContextOptions&,
const skgpu::VulkanInterface*,
VkPhysicalDevice,
uint32_t physicalDeviceVersion,
const VkPhysicalDeviceFeatures2*,
const skgpu::VulkanExtensions*,
Protected);
struct EnabledFeatures {
// VkPhysicalDeviceFeatures
bool fDualSrcBlend = false;
// Vulkan 1.0 core:
bool fOcclusionQueryPrecise = false;
// Vulkan 1.1 core:
bool fProtectedMemory = 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;
// From VkPhysicalDeviceFrameBoundaryFeaturesEXT:
bool fFrameBoundary = false;
// From VkPhysicalDevicePipelineCreationCacheControlFeatures or
// VkPhysicalDeviceVulkan13Features
bool fPipelineCreationCacheControl = false;
// From VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT
bool fFormatRGBA10x6WithoutYCbCrSampler = 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&,
const EnabledFeatures&);
void initDepthStencilFormatTable(const skgpu::VulkanInterface*,
VkPhysicalDevice,
const VkPhysicalDeviceProperties&);
TextureInfo onGetDefaultTextureInfo(SkEnumBitMask<TextureUsage> usage,
TextureFormat,
SampleCount,
Mipmapped,
Protected,
Discardable) const override;
std::pair<SkEnumBitMask<TextureUsage>, SkEnumBitMask<SampleCount>> getTextureSupport(
TextureFormat format, Tiling) const override;
std::pair<SkEnumBitMask<TextureUsage>, Tiling> getTextureUsage(
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(SampleCount 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, SampleCount sampleCount) const;
bool isStorage(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 = false;
// Indicates that a format is only supported if we are wrapping a texture with it.
SkDEBUGCODE(bool fIsWrappedOnly = false;)
};
// Map VkFormat to FormatInfo.
static const int kNumVkFormats = 24;
FormatInfo fFormatTable[kNumVkFormats];
FormatInfo& getFormatInfoForInit(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() const;
VkFormatProperties fFormatProperties = {};
SupportedSampleCounts fSupportedSampleCounts;
};
// Map depth/stencil VkFormats to DepthStencilFormatInfo.
static const size_t kNumDepthStencilVkFormats = 5;
DepthStencilFormatInfo fDepthStencilFormatTable[kNumDepthStencilVkFormats];
DepthStencilFormatInfo& getDepthStencilFormatInfoForInit(VkFormat);
const DepthStencilFormatInfo& getDepthStencilFormatInfo(VkFormat) const;
uint32_t fMaxVertexAttributes;
uint64_t fMaxUniformBufferRange;
uint64_t fMaxStorageBufferRange;
VkPhysicalDeviceMemoryProperties2 fPhysicalDeviceMemoryProperties2;
// 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;
bool fSupportsFrameBoundary = false;
bool fSupportsPipelineCreationCacheControl = false;
bool fOcclusionQueryPrecise = false;
// Flags to enable workarounds for driver bugs
bool fMustLoadFullImageForMSAA = false;
skia_private::TArray<uint32_t> fQueueFamilyTimestampValidBits;
float fTimestampPeriod = 1.0f;
};
} // namespace skgpu::graphite
#endif // skgpu_graphite_VulkanCaps_DEFINED