/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrVkCaps_DEFINED #define GrVkCaps_DEFINED #include "include/gpu/vk/GrVkTypes.h" #include "src/gpu/GrCaps.h" class GrShaderCaps; class GrVkExtensions; struct GrVkInterface; class GrVkRenderTarget; /** * Stores some capabilities of a Vk backend. */ class GrVkCaps : public GrCaps { public: /** * Creates a GrVkCaps that is set such that nothing is supported. The init function should * be called to fill out the caps. */ GrVkCaps(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface, VkPhysicalDevice device, const VkPhysicalDeviceFeatures2& features, uint32_t instanceVersion, uint32_t physicalDeviceVersion, const GrVkExtensions& extensions, GrProtected isProtected = GrProtected::kNo); bool isFormatSRGB(const GrBackendFormat&) const override; bool isFormatTexturable(const GrBackendFormat&, GrTextureType) const override; bool isVkFormatTexturable(VkFormat) const; bool isFormatCopyable(const GrBackendFormat&) const override { return true; } bool isFormatAsColorTypeRenderable(GrColorType ct, const GrBackendFormat& format, int sampleCount = 1) const override; bool isFormatRenderable(const GrBackendFormat& format, int sampleCount) const override; bool isFormatRenderable(VkFormat, int sampleCount) const; int getRenderTargetSampleCount(int requestedCount, const GrBackendFormat&) const override; int getRenderTargetSampleCount(int requestedCount, VkFormat) const; int maxRenderTargetSampleCount(const GrBackendFormat&) const override; int maxRenderTargetSampleCount(VkFormat format) const; SupportedWrite supportedWritePixelsColorType(GrColorType surfaceColorType, const GrBackendFormat& surfaceFormat, GrColorType srcColorType) const override; SurfaceReadPixelsSupport surfaceSupportsReadPixels(const GrSurface*) const override; bool isVkFormatTexturableLinearly(VkFormat format) const { return SkToBool(FormatInfo::kTexturable_Flag & this->getFormatInfo(format).fLinearFlags); } bool formatCanBeDstofBlit(VkFormat format, bool linearTiled) const { const FormatInfo& info = this->getFormatInfo(format); const uint16_t& flags = linearTiled ? info.fLinearFlags : info.fOptimalFlags; return SkToBool(FormatInfo::kBlitDst_Flag & flags); } bool formatCanBeSrcofBlit(VkFormat format, bool linearTiled) const { const FormatInfo& info = this->getFormatInfo(format); const uint16_t& flags = linearTiled ? info.fLinearFlags : info.fOptimalFlags; return SkToBool(FormatInfo::kBlitSrc_Flag & flags); } // Gets the GrColorType that should be used to transfer data in/out of a transfer buffer to // write/read data when using a VkFormat with a specified color type. GrColorType transferColorType(VkFormat, GrColorType surfaceColorType) const; // On some GPUs (Windows Nvidia and Imagination) calls to QueueWaitIdle return before actually // signalling the fences on the command buffers even though they have completed. This causes // issues when then deleting the command buffers. Therefore we additionally will call // vkWaitForFences on each outstanding command buffer to make sure the driver signals the fence. bool mustSyncCommandBuffersWithQueue() const { return fMustSyncCommandBuffersWithQueue; } // Returns true if we should always make dedicated allocations for VkImages. bool shouldAlwaysUseDedicatedImageMemory() const { return fShouldAlwaysUseDedicatedImageMemory; } // Always use a transfer buffer instead of vkCmdUpdateBuffer to upload data to a VkBuffer. bool avoidUpdateBuffers() const { return fAvoidUpdateBuffers; } /** * Returns both a supported and most preferred stencil format to use in draws. */ VkFormat preferredStencilFormat() const { return fPreferredStencilFormat; } // Returns total number of bits used by stencil + depth + padding static int GetStencilFormatTotalBitCount(VkFormat format) { switch (format) { case VK_FORMAT_S8_UINT: return 8; case VK_FORMAT_D24_UNORM_S8_UINT: return 32; case VK_FORMAT_D32_SFLOAT_S8_UINT: // can optionally have 24 unused bits at the end so we assume the total bits is 64. return 64; default: SkASSERT(false); return 0; } } // Returns whether the device supports VK_KHR_Swapchain. Internally Skia never uses any of the // swapchain functions, but we may need to transition to and from the // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR image layout, so we must know whether that layout is // supported. bool supportsSwapchain() const { return fSupportsSwapchain; } // Returns whether the device supports the ability to extend VkPhysicalDeviceProperties struct. bool supportsPhysicalDeviceProperties2() const { return fSupportsPhysicalDeviceProperties2; } // Returns whether the device supports the ability to extend VkMemoryRequirements struct. bool supportsMemoryRequirements2() const { return fSupportsMemoryRequirements2; } // Returns whether the device supports the ability to extend the vkBindMemory call. bool supportsBindMemory2() const { return fSupportsBindMemory2; } // Returns whether or not the device suports the various API maintenance fixes to Vulkan 1.0. In // Vulkan 1.1 all these maintenance are part of the core spec. bool supportsMaintenance1() const { return fSupportsMaintenance1; } bool supportsMaintenance2() const { return fSupportsMaintenance2; } bool supportsMaintenance3() const { return fSupportsMaintenance3; } // Returns true if the device supports passing in a flag to say we are using dedicated GPU when // allocating memory. For some devices this allows them to return more optimized memory knowning // they will never need to suballocate amonst multiple objects. bool supportsDedicatedAllocation() const { return fSupportsDedicatedAllocation; } // Returns true if the device supports importing of external memory into Vulkan memory. bool supportsExternalMemory() const { return fSupportsExternalMemory; } // Returns true if the device supports importing Android hardware buffers into Vulkan memory. bool supportsAndroidHWBExternalMemory() const { return fSupportsAndroidHWBExternalMemory; } // Returns true if it supports ycbcr conversion for samplers bool supportsYcbcrConversion() const { return fSupportsYcbcrConversion; } // Returns the number of descriptor slots used by immutable ycbcr VkImages. // // TODO: We should update this to return a count for a specific format or external format. We // can use vkGetPhysicalDeviceImageFormatProperties2 with a // VkSamplerYcbcrConversionImageFormatProperties to query this. However, right now that call // does not support external android formats which is where the majority of ycbcr images are // coming from. So for now we stay safe and always return 3 here which is the max value that the // count could be for any format. uint32_t ycbcrCombinedImageSamplerDescriptorCount() const { return 3; } // Returns true if the device supports protected memory. bool supportsProtectedMemory() const { return fSupportsProtectedMemory; } // Returns true if the VK_EXT_image_drm_format_modifier is enabled. bool supportsDRMFormatModifiers() const { return fSupportsDRMFormatModifiers; } // Returns whether we prefer to record draws directly into a primary command buffer. bool preferPrimaryOverSecondaryCommandBuffers() const { return fPreferPrimaryOverSecondaryCommandBuffers; } int maxPerPoolCachedSecondaryCommandBuffers() const { return fMaxPerPoolCachedSecondaryCommandBuffers; } uint32_t maxInputAttachmentDescriptors() const { return fMaxInputAttachmentDescriptors; } bool mustInvalidatePrimaryCmdBufferStateAfterClearAttachments() const { return fMustInvalidatePrimaryCmdBufferStateAfterClearAttachments; } // For host visible allocations, this returns true if we require that they are coherent. This // is used to work around bugs for devices that don't handle non-coherent memory correctly. bool mustUseCoherentHostVisibleMemory() const { return fMustUseCoherentHostVisibleMemory; } // 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.), should we keep these buffers // persistently mapped. In general the answer will be yes. The main case 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; } // The max draw count that can be passed into indirect draw calls. uint32_t maxDrawIndirectDrawCount() const { return fMaxDrawIndirectDrawCount; } /** * Helpers used by canCopySurface. In all cases if the SampleCnt parameter is zero that means * the surface is not a render target, otherwise it is the number of samples in the render * target. */ bool canCopyImage(VkFormat dstFormat, int dstSampleCnt, bool dstHasYcbcr, VkFormat srcFormat, int srcSamplecnt, bool srcHasYcbcr) const; bool canCopyAsBlit(VkFormat dstConfig, int dstSampleCnt, bool dstIsLinear, bool dstHasYcbcr, VkFormat srcConfig, int srcSampleCnt, bool srcIsLinear, bool srcHasYcbcr) const; bool canCopyAsResolve(VkFormat dstConfig, int dstSampleCnt, bool dstHasYcbcr, VkFormat srcConfig, int srcSamplecnt, bool srcHasYcbcr) const; GrBackendFormat getBackendFormatFromCompressionType(SkImage::CompressionType) const override; VkFormat getFormatFromColorType(GrColorType colorType) const { int idx = static_cast(colorType); return fColorTypeToFormatTable[idx]; } GrSwizzle getWriteSwizzle(const GrBackendFormat&, GrColorType) const override; uint64_t computeFormatKey(const GrBackendFormat&) const override; int getFragmentUniformBinding() const; int getFragmentUniformSet() const; void addExtraSamplerKey(GrProcessorKeyBuilder*, GrSamplerState, const GrBackendFormat&) const override; GrProgramDesc makeDesc(GrRenderTarget*, const GrProgramInfo&, ProgramDescOverrideFlags) const override; GrInternalSurfaceFlags getExtraSurfaceFlagsForDeferredRT() const override; VkShaderStageFlags getPushConstantStageFlags() const; // If true then when doing MSAA draws, we will prefer to discard the msaa attachment on load // and stores. The use of this feature for specific draws depends on the render target having a // resolve attachment, and if we need to load previous data the resolve attachment must be // usable as an input attachment. Otherwise we will just write out and store the msaa attachment // like normal. // This flag is similar to enabling gl render to texture for msaa rendering. bool preferDiscardableMSAAAttachment() const { return fPreferDiscardableMSAAAttachment; } bool mustLoadFullImageWithDiscardableMSAA() const { return fMustLoadFullImageWithDiscardableMSAA; } bool supportsDiscardableMSAAForDMSAA() const { return fSupportsDiscardableMSAAForDMSAA; } bool renderTargetSupportsDiscardableMSAA(const GrVkRenderTarget*) const; bool programInfoWillUseDiscardableMSAA(const GrProgramInfo&) const; bool dmsaaResolveCanBeUsedAsTextureInSameRenderPass() const override { return false; } bool supportsMemorylessAttachments() const { return fSupportsMemorylessAttachments; } #if GR_TEST_UTILS std::vector getTestingCombinations() const override; #endif private: enum VkVendor { kAMD_VkVendor = 4098, kARM_VkVendor = 5045, kHisi_VkVendor = 6629, kImagination_VkVendor = 4112, kIntel_VkVendor = 32902, kNvidia_VkVendor = 4318, kQualcomm_VkVendor = 20803, }; void init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface, VkPhysicalDevice device, const VkPhysicalDeviceFeatures2&, uint32_t physicalDeviceVersion, const GrVkExtensions&, GrProtected isProtected); void initGrCaps(const GrVkInterface* vkInterface, VkPhysicalDevice physDev, const VkPhysicalDeviceProperties&, const VkPhysicalDeviceMemoryProperties&, const VkPhysicalDeviceFeatures2&, const GrVkExtensions&); void initShaderCaps(const VkPhysicalDeviceProperties&, const VkPhysicalDeviceFeatures2&); void initFormatTable(const GrVkInterface*, VkPhysicalDevice, const VkPhysicalDeviceProperties&); void initStencilFormat(const GrVkInterface* iface, VkPhysicalDevice physDev); void applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties&); bool onSurfaceSupportsWritePixels(const GrSurface*) const override; bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) const override; GrBackendFormat onGetDefaultBackendFormat(GrColorType) const override; bool onAreColorTypeAndFormatCompatible(GrColorType, const GrBackendFormat&) const override; SupportedRead onSupportedReadPixelsColorType(GrColorType, const GrBackendFormat&, GrColorType) const override; GrSwizzle onGetReadSwizzle(const GrBackendFormat&, GrColorType) const override; GrDstSampleFlags onGetDstSampleFlagsForProxy(const GrRenderTargetProxy*) const override; bool onSupportsDynamicMSAA(const GrRenderTargetProxy*) const override; // ColorTypeInfo for a specific format struct ColorTypeInfo { GrColorType fColorType = GrColorType::kUnknown; GrColorType fTransferColorType = GrColorType::kUnknown; enum { kUploadData_Flag = 0x1, // Does Ganesh itself support rendering to this colorType & format pair. Renderability // still additionally depends on if the format itself is renderable. kRenderable_Flag = 0x2, // Indicates that this colorType is supported only if we are wrapping a texture with // the given format and colorType. We do not allow creation with this pair. kWrappedOnly_Flag = 0x4, }; uint32_t fFlags = 0; GrSwizzle fReadSwizzle; GrSwizzle fWriteSwizzle; }; struct FormatInfo { uint32_t colorTypeFlags(GrColorType colorType) const { for (int i = 0; i < fColorTypeInfoCount; ++i) { if (fColorTypeInfos[i].fColorType == colorType) { return fColorTypeInfos[i].fFlags; } } return 0; } void init(const GrVkInterface*, VkPhysicalDevice, const VkPhysicalDeviceProperties&, VkFormat); static void InitFormatFlags(VkFormatFeatureFlags, uint16_t* flags); void initSampleCounts(const GrVkInterface*, VkPhysicalDevice, const VkPhysicalDeviceProperties&, VkFormat); enum { kTexturable_Flag = 0x1, kRenderable_Flag = 0x2, kBlitSrc_Flag = 0x4, kBlitDst_Flag = 0x8, }; uint16_t fOptimalFlags = 0; uint16_t fLinearFlags = 0; SkTDArray fColorSampleCounts; std::unique_ptr fColorTypeInfos; int fColorTypeInfoCount = 0; }; static const size_t kNumVkFormats = 25; FormatInfo fFormatTable[kNumVkFormats]; FormatInfo& getFormatInfo(VkFormat); const FormatInfo& getFormatInfo(VkFormat) const; VkFormat fColorTypeToFormatTable[kGrColorTypeCnt]; void setColorType(GrColorType, std::initializer_list formats); VkFormat fPreferredStencilFormat; SkSTArray<1, GrVkYcbcrConversionInfo> fYcbcrInfos; bool fMustSyncCommandBuffersWithQueue = false; bool fShouldAlwaysUseDedicatedImageMemory = false; bool fAvoidUpdateBuffers = false; bool fSupportsSwapchain = false; bool fSupportsPhysicalDeviceProperties2 = false; bool fSupportsMemoryRequirements2 = false; bool fSupportsBindMemory2 = false; bool fSupportsMaintenance1 = false; bool fSupportsMaintenance2 = false; bool fSupportsMaintenance3 = false; bool fSupportsDedicatedAllocation = false; bool fSupportsExternalMemory = false; bool fSupportsAndroidHWBExternalMemory = false; bool fSupportsYcbcrConversion = false; bool fSupportsProtectedMemory = false; bool fSupportsDRMFormatModifiers = false; bool fPreferPrimaryOverSecondaryCommandBuffers = true; bool fMustInvalidatePrimaryCmdBufferStateAfterClearAttachments = false; bool fMustUseCoherentHostVisibleMemory = false; bool fGpuOnlyBuffersMorePerformant = false; bool fShouldPersistentlyMapCpuToGpuBuffers = true; // We default this to 100 since we already cap the max render tasks at 100 before doing a // submission in the GrDrawingManager, so we shouldn't be going over 100 secondary command // buffers per primary anyways. int fMaxPerPoolCachedSecondaryCommandBuffers = 100; uint32_t fMaxInputAttachmentDescriptors = 0; bool fPreferDiscardableMSAAAttachment = false; bool fMustLoadFullImageWithDiscardableMSAA = false; bool fSupportsDiscardableMSAAForDMSAA = true; bool fSupportsMemorylessAttachments = false; uint32_t fMaxDrawIndirectDrawCount = 0; using INHERITED = GrCaps; }; #endif