1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef skgpu_graphite_Caps_DEFINED 9 #define skgpu_graphite_Caps_DEFINED 10 11 #include <optional> 12 #include <string> 13 #include <string_view> 14 #include <utility> 15 16 #include "include/core/SkImageInfo.h" 17 #include "include/core/SkRefCnt.h" 18 #include "include/private/base/SkAlign.h" 19 #include "src/base/SkEnumBitMask.h" 20 #include "src/gpu/ResourceKey.h" 21 #include "src/gpu/Swizzle.h" 22 #include "src/gpu/graphite/ResourceTypes.h" 23 #include "src/gpu/graphite/TextureProxy.h" 24 #include "src/text/gpu/SubRunControl.h" 25 26 #if defined(GPU_TEST_UTILS) 27 #include "src/gpu/graphite/ContextOptionsPriv.h" 28 #endif 29 30 enum class SkBlendMode; 31 enum class SkTextureCompressionType; 32 class SkCapabilities; 33 class SkStream; 34 class SkWStream; 35 36 namespace SkSL { struct ShaderCaps; } 37 38 namespace skgpu { class ShaderErrorHandler; } 39 40 namespace skgpu::graphite { 41 42 enum class BufferType : int; 43 struct ContextOptions; 44 class ComputePipelineDesc; 45 class GraphicsPipelineDesc; 46 class GraphiteResourceKey; 47 class RendererProvider; 48 struct RenderPassDesc; 49 class TextureInfo; 50 51 struct ResourceBindingRequirements { 52 // The required data layout rules for the contents of a uniform buffer. 53 Layout fUniformBufferLayout = Layout::kInvalid; 54 55 // The required data layout rules for the contents of a storage buffer. 56 Layout fStorageBufferLayout = Layout::kInvalid; 57 58 // Whether combined texture-sampler types are supported. Backends that do not support 59 // combined image samplers (i.e. sampler2D) require a texture and sampler object to be bound 60 // separately and their binding indices explicitly specified in the shader text. 61 bool fSeparateTextureAndSamplerBinding = false; 62 63 // Whether intrinsic constant information is stored as push constants (rather than normal UBO). 64 // Currently only relevant or possibly true for Vulkan. 65 bool fUseVulkanPushConstantsForIntrinsicConstants = false; 66 67 // Whether compute shader textures use separate index ranges from other resources (i.e. buffers) 68 bool fComputeUsesDistinctIdxRangesForTextures = false; 69 70 // Define set indices. We assume that even if textures and samplers must be bound separately, 71 // they will still be contained within the same set/group. 72 static constexpr int kUnassigned = -1; 73 int fUniformsSetIdx = kUnassigned; 74 int fTextureSamplerSetIdx = kUnassigned; 75 int fInputAttachmentSetIdx = kUnassigned; 76 // Define uniform buffer bindings 77 int fIntrinsicBufferBinding = kUnassigned; 78 int fRenderStepBufferBinding = kUnassigned; 79 int fPaintParamsBufferBinding = kUnassigned; 80 int fGradientBufferBinding = kUnassigned; 81 }; 82 83 class Caps { 84 public: 85 virtual ~Caps(); 86 shaderCaps()87 const SkSL::ShaderCaps* shaderCaps() const { return fShaderCaps.get(); } 88 89 sk_sp<SkCapabilities> capabilities() const; 90 91 #if defined(GPU_TEST_UTILS) deviceName()92 std::string_view deviceName() const { return fDeviceName; } 93 requestedPathRendererStrategy()94 PathRendererStrategy requestedPathRendererStrategy() const { 95 return fRequestedPathRendererStrategy; 96 } 97 #endif 98 99 virtual TextureInfo getDefaultSampledTextureInfo(SkColorType, 100 Mipmapped mipmapped, 101 Protected, 102 Renderable) const = 0; 103 104 virtual TextureInfo getTextureInfoForSampledCopy(const TextureInfo& textureInfo, 105 Mipmapped mipmapped) const = 0; 106 107 virtual TextureInfo getDefaultCompressedTextureInfo(SkTextureCompressionType, 108 Mipmapped mipmapped, 109 Protected) const = 0; 110 111 virtual TextureInfo getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo, 112 Discardable discardable) const = 0; 113 114 // Currently the uses of this API always are asking for a discardable DepthStencil attachment. 115 // However, we still pass in the parameter here because we don't want the backend caps to be 116 // making decisons on if a thing should be discardable or not. This also allows us to eventual 117 // use non discardable DS attachments, but there aren't any current plans to do so. If we find 118 // out over time that we really will only ever use discardable ones, we could rename this 119 // function to be getDefaultDiscardableDepthStencilTextureInfo. 120 virtual TextureInfo getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags>, 121 uint32_t sampleCount, 122 Protected, 123 Discardable discardable) const = 0; 124 125 virtual TextureInfo getDefaultStorageTextureInfo(SkColorType) const = 0; 126 127 // Get required depth attachment dimensions for a givin color attachment info and dimensions. 128 virtual SkISize getDepthAttachmentDimensions(const TextureInfo&, 129 const SkISize colorAttachmentDimensions) const; 130 131 virtual UniqueKey makeGraphicsPipelineKey(const GraphicsPipelineDesc&, 132 const RenderPassDesc&) const = 0; 133 virtual UniqueKey makeComputePipelineKey(const ComputePipelineDesc&) const = 0; 134 135 extractGraphicsDescs(const UniqueKey &,GraphicsPipelineDesc *,RenderPassDesc *,const RendererProvider *)136 virtual bool extractGraphicsDescs(const UniqueKey&, 137 GraphicsPipelineDesc*, 138 RenderPassDesc*, 139 const RendererProvider*) const { return false; } 140 141 virtual bool serializeTextureInfo(const TextureInfo&, SkWStream*) const = 0; 142 virtual bool deserializeTextureInfo(SkStream*, TextureInfo*) const = 0; 143 144 bool areColorTypeAndTextureInfoCompatible(SkColorType, const TextureInfo&) const; 145 146 bool isTexturable(const TextureInfo&) const; 147 virtual bool isRenderable(const TextureInfo&) const = 0; 148 virtual bool isStorage(const TextureInfo&) const = 0; 149 loadOpAffectsMSAAPipelines()150 virtual bool loadOpAffectsMSAAPipelines() const { return false; } 151 maxTextureSize()152 int maxTextureSize() const { return fMaxTextureSize; } defaultMSAASamplesCount()153 int defaultMSAASamplesCount() const { return fDefaultMSAASamples; } 154 155 virtual void buildKeyForTexture(SkISize dimensions, 156 const TextureInfo&, 157 ResourceType, 158 GraphiteResourceKey*) const = 0; 159 resourceBindingRequirements()160 const ResourceBindingRequirements& resourceBindingRequirements() const { 161 return fResourceBindingReqs; 162 } 163 164 // Returns the required alignment in bytes for the offset into a uniform buffer when binding it 165 // to a draw. requiredUniformBufferAlignment()166 size_t requiredUniformBufferAlignment() const { return fRequiredUniformBufferAlignment; } 167 168 // Returns the required alignment in bytes for the offset into a storage buffer when binding it 169 // to a draw. requiredStorageBufferAlignment()170 size_t requiredStorageBufferAlignment() const { return fRequiredStorageBufferAlignment; } 171 172 // Returns the required alignment in bytes for the offset and size of copies involving a buffer. requiredTransferBufferAlignment()173 size_t requiredTransferBufferAlignment() const { return fRequiredTransferBufferAlignment; } 174 175 // Returns the aligned rowBytes when transfering to or from a Texture getAlignedTextureDataRowBytes(size_t rowBytes)176 size_t getAlignedTextureDataRowBytes(size_t rowBytes) const { 177 return SkAlignTo(rowBytes, fTextureDataRowBytesAlignment); 178 } 179 180 // Backends can optionally override this method to return meaningful sampler conversion info. 181 // By default, simply return a default ImmutableSamplerInfo (e.g. no immutable sampler). getImmutableSamplerInfo(const TextureInfo &)182 virtual ImmutableSamplerInfo getImmutableSamplerInfo(const TextureInfo&) const { 183 return {}; 184 } 185 186 /** 187 * Backends may have restrictions on what types of textures support Device::writePixels(). 188 * If this returns false then the caller should implement a fallback where a temporary texture 189 * is created, pixels are written to it, and then that is copied or drawn into the the surface. 190 */ 191 virtual bool supportsWritePixels(const TextureInfo& textureInfo) const = 0; 192 193 /** 194 * Backends may have restrictions on what types of textures support Device::readPixels(). 195 * If this returns false then the caller should implement a fallback where a temporary texture 196 * is created, the original texture is copied or drawn into it, and then pixels read from 197 * the temporary texture. 198 */ 199 virtual bool supportsReadPixels(const TextureInfo& textureInfo) const = 0; 200 201 /** 202 * Given a dst pixel config and a src color type what color type must the caller coax the 203 * the data into in order to use writePixels. 204 * 205 * We currently don't have an SkColorType for a 3 channel RGB format. Additionally the current 206 * implementation of raster pipeline requires power of 2 channels, so it is not easy to add such 207 * an SkColorType. Thus we need to check for data that is 3 channels using the isRGBFormat 208 * return value and handle it manually 209 */ 210 virtual std::pair<SkColorType, bool /*isRGB888Format*/> supportedWritePixelsColorType( 211 SkColorType dstColorType, 212 const TextureInfo& dstTextureInfo, 213 SkColorType srcColorType) const = 0; 214 215 /** 216 * Given a src surface's color type and its texture info as well as a color type the caller 217 * would like read into, this provides a legal color type that the caller can use for 218 * readPixels. The returned color type may differ from the passed dstColorType, in 219 * which case the caller must convert the read pixel data (see GrConvertPixels). When converting 220 * to dstColorType the swizzle in the returned struct should be applied. The caller must check 221 * the returned color type for kUnknown. 222 * 223 * We currently don't have an SkColorType for a 3 channel RGB format. Additionally the current 224 * implementation of raster pipeline requires power of 2 channels, so it is not easy to add such 225 * an SkColorType. Thus we need to check for data that is 3 channels using the isRGBFormat 226 * return value and handle it manually 227 */ 228 virtual std::pair<SkColorType, bool /*isRGBFormat*/> supportedReadPixelsColorType( 229 SkColorType srcColorType, 230 const TextureInfo& srcTextureInfo, 231 SkColorType dstColorType) const = 0; 232 233 /** 234 * Checks whether the passed color type is renderable. If so, the same color type is passed 235 * back. If not, provides an alternative (perhaps lower bit depth and/or unorm instead of float) 236 * color type that is supported or kUnknown if there no renderable fallback format. 237 */ 238 SkColorType getRenderableColorType(SkColorType) const; 239 240 // Determines the orientation of the NDC coordinates emitted by the vertex stage relative to 241 // both Skia's presumed top-left Y-down system and the viewport coordinates (which are also 242 // always top-left, Y-down for all supported backends).) 243 // 244 // If true is returned, then (-1,-1) in normalized device coords maps to the top-left of the 245 // configured viewport and positive Y points down. This aligns with Skia's conventions. 246 // If false is returned, then (-1,-1) in NDC maps to the bottom-left of the viewport and 247 // positive Y points up (so NDC is flipped relative to sk_Position and the viewport coords). 248 // 249 // There is no backend difference in handling the X axis so it's assumed -1 maps to the left 250 // edge and +1 maps to the right edge. ndcYAxisPointsDown()251 bool ndcYAxisPointsDown() const { return fNDCYAxisPointsDown; } 252 clampToBorderSupport()253 bool clampToBorderSupport() const { return fClampToBorderSupport; } 254 protectedSupport()255 bool protectedSupport() const { return fProtectedSupport; } 256 257 // Supports BackendSemaphores semaphoreSupport()258 bool semaphoreSupport() const { return fSemaphoreSupport; } 259 260 // If false then calling Context::submit with SyncToCpu::kYes is an error. allowCpuSync()261 bool allowCpuSync() const { return fAllowCpuSync; } 262 263 // Returns whether storage buffers are supported and to be preferred over uniform buffers. storageBufferSupport()264 bool storageBufferSupport() const { return fStorageBufferSupport; } 265 266 // The gradient buffer is an unsized float array so it is only optimal memory-wise to use it if 267 // the storage buffer memory layout is std430 or in metal, which is also the only supported 268 // way the data is packed. gradientBufferSupport()269 bool gradientBufferSupport() const { 270 return fStorageBufferSupport && 271 (fResourceBindingReqs.fStorageBufferLayout == Layout::kStd430 || 272 fResourceBindingReqs.fStorageBufferLayout == Layout::kMetal); 273 } 274 275 // Returns whether a draw buffer can be mapped. drawBufferCanBeMapped()276 bool drawBufferCanBeMapped() const { return fDrawBufferCanBeMapped; } 277 278 #if defined(GPU_TEST_UTILS) drawBufferCanBeMappedForReadback()279 bool drawBufferCanBeMappedForReadback() const { return fDrawBufferCanBeMappedForReadback; } 280 #endif 281 282 // Returns whether using Buffer::asyncMap() must be used to map buffers. map() may only be 283 // called after asyncMap() is called and will fail if the asynchronous map is not complete. This 284 // excludes premapped buffers for which map() can be called freely until the first unmap() call. bufferMapsAreAsync()285 bool bufferMapsAreAsync() const { return fBufferMapsAreAsync; } 286 287 // Returns whether multisampled render to single sampled is supported. msaaRenderToSingleSampledSupport()288 bool msaaRenderToSingleSampledSupport() const { return fMSAARenderToSingleSampledSupport; } 289 290 // Returns whether a render pass can have MSAA/depth/stencil attachments and a resolve 291 // attachment with mismatched sizes. Note: the MSAA attachment and the depth/stencil attachment 292 // still need to match their sizes. differentResolveAttachmentSizeSupport()293 bool differentResolveAttachmentSizeSupport() const { 294 return fDifferentResolveAttachmentSizeSupport; 295 } 296 297 // Returns whether compute shaders are supported. computeSupport()298 bool computeSupport() const { return fComputeSupport; } 299 300 /** 301 * Returns true if the given backend supports importing AHardwareBuffers. This will only 302 * ever be supported on Android devices with API level >= 26. 303 */ supportsAHardwareBufferImages()304 bool supportsAHardwareBufferImages() const { return fSupportsAHardwareBufferImages; } 305 306 // Returns the skgpu::Swizzle to use when sampling or reading back from a texture with the 307 // passed in SkColorType and TextureInfo. 308 skgpu::Swizzle getReadSwizzle(SkColorType, const TextureInfo&) const; 309 310 // Returns the skgpu::Swizzle to use when writing colors to a surface with the passed in 311 // SkColorType and TextureInfo. 312 skgpu::Swizzle getWriteSwizzle(SkColorType, const TextureInfo&) const; 313 shaderErrorHandler()314 skgpu::ShaderErrorHandler* shaderErrorHandler() const { return fShaderErrorHandler; } 315 316 // Returns what method of dst read a draw should use for obtaining the dst color. Backends can 317 // use the default implementation or override this method as needed. 318 // TODO(b/390458117): Once the Vulkan backend supports DstReadStrategy::kReadFromInput for 319 // MSAA textures (and if we are comfortable assuming by this point that all render targets, at 320 // least on Android, support input attachment usage) then the TextureInfo argument can be 321 // removed and backend Caps implementations can report kReadFromInput support some other way 322 // (such as a simple member attribute bool). For now, we must check each render target's info. 323 virtual DstReadStrategy getDstReadStrategy(const TextureInfo&) const; 324 minDistanceFieldFontSize()325 float minDistanceFieldFontSize() const { return fMinDistanceFieldFontSize; } glyphsAsPathsFontSize()326 float glyphsAsPathsFontSize() const { return fGlyphsAsPathsFontSize; } 327 glyphCacheTextureMaximumBytes()328 size_t glyphCacheTextureMaximumBytes() const { return fGlyphCacheTextureMaximumBytes; } maxPathAtlasTextureSize()329 int maxPathAtlasTextureSize() const { return fMaxPathAtlasTextureSize; } 330 allowMultipleAtlasTextures()331 bool allowMultipleAtlasTextures() const { return fAllowMultipleAtlasTextures; } supportBilerpFromGlyphAtlas()332 bool supportBilerpFromGlyphAtlas() const { return fSupportBilerpFromGlyphAtlas; } 333 requireOrderedRecordings()334 bool requireOrderedRecordings() const { return fRequireOrderedRecordings; } 335 336 // When uploading to a full compressed texture do we need to pad the size out to a multiple of 337 // the block width and height. fullCompressedUploadSizeMustAlignToBlockDims()338 bool fullCompressedUploadSizeMustAlignToBlockDims() const { 339 return fFullCompressedUploadSizeMustAlignToBlockDims; 340 } 341 342 sktext::gpu::SubRunControl getSubRunControl(bool useSDFTForSmallText) const; 343 setBackendLabels()344 bool setBackendLabels() const { return fSetBackendLabels; } 345 supportedGpuStats()346 GpuStatsFlags supportedGpuStats() const { return fSupportedGpuStats; } 347 348 protected: 349 Caps(); 350 351 // Subclasses must call this at the end of their init method in order to do final processing on 352 // the caps. 353 void finishInitialization(const ContextOptions&); 354 355 #if defined(GPU_TEST_UTILS) setDeviceName(std::string n)356 void setDeviceName(std::string n) { 357 fDeviceName = std::move(n); 358 } 359 #endif 360 361 // There are only a few possible valid sample counts (1, 2, 4, 8, 16). So we can key on those 5 362 // options instead of the actual sample value. SamplesToKey(uint32_t numSamples)363 static inline uint32_t SamplesToKey(uint32_t numSamples) { 364 switch (numSamples) { 365 case 1: 366 return 0; 367 case 2: 368 return 1; 369 case 4: 370 return 2; 371 case 8: 372 return 3; 373 case 16: 374 return 4; 375 default: 376 SkUNREACHABLE; 377 } 378 } 379 380 // ColorTypeInfo for a specific format. Used in format tables. 381 struct ColorTypeInfo { 382 ColorTypeInfo() = default; ColorTypeInfoColorTypeInfo383 ColorTypeInfo(SkColorType ct, SkColorType transferCt, uint32_t flags, 384 skgpu::Swizzle readSwizzle, skgpu::Swizzle writeSwizzle) 385 : fColorType(ct) 386 , fTransferColorType(transferCt) 387 , fFlags(flags) 388 , fReadSwizzle(readSwizzle) 389 , fWriteSwizzle(writeSwizzle) {} 390 391 SkColorType fColorType = kUnknown_SkColorType; 392 SkColorType fTransferColorType = kUnknown_SkColorType; 393 enum { 394 kUploadData_Flag = 0x1, 395 // Does Graphite itself support rendering to this colorType & format pair. Renderability 396 // still additionally depends on if the format itself is renderable. 397 kRenderable_Flag = 0x2, 398 }; 399 uint32_t fFlags = 0; 400 401 skgpu::Swizzle fReadSwizzle; 402 skgpu::Swizzle fWriteSwizzle; 403 }; 404 405 int fMaxTextureSize = 0; 406 int fDefaultMSAASamples = 4; 407 size_t fRequiredUniformBufferAlignment = 0; 408 size_t fRequiredStorageBufferAlignment = 0; 409 size_t fRequiredTransferBufferAlignment = 0; 410 size_t fTextureDataRowBytesAlignment = 1; 411 412 std::unique_ptr<SkSL::ShaderCaps> fShaderCaps; 413 414 bool fNDCYAxisPointsDown = false; // Most backends have NDC +Y pointing up 415 bool fClampToBorderSupport = true; 416 bool fProtectedSupport = false; 417 bool fSemaphoreSupport = false; 418 bool fAllowCpuSync = true; 419 bool fStorageBufferSupport = false; 420 bool fDrawBufferCanBeMapped = true; 421 bool fBufferMapsAreAsync = false; 422 bool fMSAARenderToSingleSampledSupport = false; 423 bool fDifferentResolveAttachmentSizeSupport = false; 424 425 bool fComputeSupport = false; 426 bool fSupportsAHardwareBufferImages = false; 427 bool fFullCompressedUploadSizeMustAlignToBlockDims = false; 428 429 #if defined(GPU_TEST_UTILS) 430 bool fDrawBufferCanBeMappedForReadback = true; 431 #endif 432 433 ResourceBindingRequirements fResourceBindingReqs; 434 435 GpuStatsFlags fSupportedGpuStats = GpuStatsFlags::kNone; 436 437 ////////////////////////////////////////////////////////////////////////////////////////// 438 // Client-provided Caps 439 440 /** 441 * If present, use this object to report shader compilation failures. If not, report failures 442 * via SkDebugf and assert. 443 */ 444 ShaderErrorHandler* fShaderErrorHandler = nullptr; 445 446 #if defined(GPU_TEST_UTILS) 447 std::string fDeviceName; 448 int fMaxTextureAtlasSize = 2048; 449 PathRendererStrategy fRequestedPathRendererStrategy; 450 #endif 451 size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4; 452 453 float fMinDistanceFieldFontSize = 18; 454 float fGlyphsAsPathsFontSize = 324; 455 456 int fMaxPathAtlasTextureSize = 8192; 457 458 bool fAllowMultipleAtlasTextures = true; 459 bool fSupportBilerpFromGlyphAtlas = false; 460 461 bool fRequireOrderedRecordings = false; 462 463 bool fSetBackendLabels = false; 464 465 private: 466 virtual bool onIsTexturable(const TextureInfo&) const = 0; 467 virtual const ColorTypeInfo* getColorTypeInfo(SkColorType, const TextureInfo&) const = 0; 468 469 sk_sp<SkCapabilities> fCapabilities; 470 }; 471 472 } // namespace skgpu::graphite 473 474 #endif // skgpu_graphite_Caps_DEFINED 475