blob: 40033ab76aba6b66b28d9c7dd9fd30c8c64b062a [file] [log] [blame]
/*
* Copyright 2021 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_Caps_DEFINED
#define skgpu_graphite_Caps_DEFINED
#include <optional>
#include <string>
#include <string_view>
#include "include/core/SkImageInfo.h"
#include "include/core/SkRefCnt.h"
#include "include/private/base/SkAlign.h"
#include "src/base/SkEnumBitMask.h"
#include "src/gpu/ResourceKey.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/graphite/ResourceTypes.h"
#include "src/gpu/graphite/TextureProxy.h"
#include "src/text/gpu/SDFTControl.h"
#if defined(GRAPHITE_TEST_UTILS)
#include "include/private/gpu/graphite/ContextOptionsPriv.h"
#endif
enum class SkBlendMode;
enum class SkTextureCompressionType;
class SkCapabilities;
namespace SkSL { struct ShaderCaps; }
namespace skgpu { class ShaderErrorHandler; }
namespace skgpu::graphite {
enum class BufferType : int;
struct ContextOptions;
class ComputePipelineDesc;
class GraphicsPipelineDesc;
class GraphiteResourceKey;
class RendererProvider;
struct RenderPassDesc;
class TextureInfo;
struct ResourceBindingRequirements {
// The required data layout rules for the contents of a uniform buffer.
Layout fUniformBufferLayout = Layout::kInvalid;
// The required data layout rules for the contents of a storage buffer.
Layout fStorageBufferLayout = Layout::kInvalid;
// Whether combined texture-sampler types are supported. Backends that do not support
// combined image samplers (i.e. sampler2D) require a texture and sampler object to be bound
// separately and their binding indices explicitly specified in the shader text.
bool fSeparateTextureAndSamplerBinding = false;
// Whether buffer, texture, and sampler resource bindings use distinct index ranges.
bool fDistinctIndexRanges = false;
};
enum class DstReadRequirement {
kNone,
kTextureCopy,
kTextureSample,
kFramebufferFetch,
};
class Caps {
public:
virtual ~Caps();
const SkSL::ShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
sk_sp<SkCapabilities> capabilities() const;
#if defined(GRAPHITE_TEST_UTILS)
std::string_view deviceName() const { return fDeviceName; }
PathRendererStrategy requestedPathRendererStrategy() const {
return fRequestedPathRendererStrategy;
}
#endif
virtual TextureInfo getDefaultSampledTextureInfo(SkColorType,
Mipmapped mipmapped,
Protected,
Renderable) const = 0;
virtual TextureInfo getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
Mipmapped mipmapped) const = 0;
virtual TextureInfo getDefaultCompressedTextureInfo(SkTextureCompressionType,
Mipmapped mipmapped,
Protected) const = 0;
virtual TextureInfo getDefaultMSAATextureInfo(const TextureInfo& singleSampledInfo,
Discardable discardable) const = 0;
virtual TextureInfo getDefaultDepthStencilTextureInfo(SkEnumBitMask<DepthStencilFlags>,
uint32_t sampleCount,
Protected) const = 0;
virtual TextureInfo getDefaultStorageTextureInfo(SkColorType) const = 0;
// Get required depth attachment dimensions for a givin color attachment info and dimensions.
virtual SkISize getDepthAttachmentDimensions(const TextureInfo&,
const SkISize colorAttachmentDimensions) const;
virtual UniqueKey makeGraphicsPipelineKey(const GraphicsPipelineDesc&,
const RenderPassDesc&) const = 0;
virtual UniqueKey makeComputePipelineKey(const ComputePipelineDesc&) const = 0;
// Returns a GraphiteResourceKey based upon a SamplerDesc with any additional information that
// backends append within their implementation. By default, simply returns a key based upon
// the SamplerDesc with no extra info.
// TODO: Rather than going through a GraphiteResourceKey, migrate to having a cache of samplers
// keyed off of SamplerDesc to minimize heap allocations.
virtual GraphiteResourceKey makeSamplerKey(const SamplerDesc& samplerDesc) const;
// Backends can optionally override this method to return meaningful sampler conversion info.
// By default, simply return a default ImmutableSamplerInfo.
virtual ImmutableSamplerInfo getImmutableSamplerInfo(sk_sp<TextureProxy> proxy) const {
return {};
}
virtual bool extractGraphicsDescs(const UniqueKey&,
GraphicsPipelineDesc*,
RenderPassDesc*,
const RendererProvider*) const { return false; }
bool areColorTypeAndTextureInfoCompatible(SkColorType, const TextureInfo&) const;
virtual uint32_t channelMask(const TextureInfo&) const = 0;
bool isTexturable(const TextureInfo&) const;
virtual bool isRenderable(const TextureInfo&) const = 0;
virtual bool isStorage(const TextureInfo&) const = 0;
int maxTextureSize() const { return fMaxTextureSize; }
int defaultMSAASamplesCount() const { return fDefaultMSAASamples; }
virtual void buildKeyForTexture(SkISize dimensions,
const TextureInfo&,
ResourceType,
Shareable,
GraphiteResourceKey*) const = 0;
const ResourceBindingRequirements& resourceBindingRequirements() const {
return fResourceBindingReqs;
}
// Returns the required alignment in bytes for the offset into a uniform buffer when binding it
// to a draw.
size_t requiredUniformBufferAlignment() const { return fRequiredUniformBufferAlignment; }
// Returns the required alignment in bytes for the offset into a storage buffer when binding it
// to a draw.
size_t requiredStorageBufferAlignment() const { return fRequiredStorageBufferAlignment; }
// Returns the required alignment in bytes for the offset and size of copies involving a buffer.
size_t requiredTransferBufferAlignment() const { return fRequiredTransferBufferAlignment; }
// Returns the aligned rowBytes when transfering to or from a Texture
size_t getAlignedTextureDataRowBytes(size_t rowBytes) const {
return SkAlignTo(rowBytes, fTextureDataRowBytesAlignment);
}
/**
* Backends may have restrictions on what types of textures support Device::writePixels().
* If this returns false then the caller should implement a fallback where a temporary texture
* is created, pixels are written to it, and then that is copied or drawn into the the surface.
*/
virtual bool supportsWritePixels(const TextureInfo& textureInfo) const = 0;
/**
* Backends may have restrictions on what types of textures support Device::readPixels().
* If this returns false then the caller should implement a fallback where a temporary texture
* is created, the original texture is copied or drawn into it, and then pixels read from
* the temporary texture.
*/
virtual bool supportsReadPixels(const TextureInfo& textureInfo) const = 0;
/**
* Given a dst pixel config and a src color type what color type must the caller coax the
* the data into in order to use writePixels.
*
* We currently don't have an SkColorType for a 3 channel RGB format. Additionally the current
* implementation of raster pipeline requires power of 2 channels, so it is not easy to add such
* an SkColorType. Thus we need to check for data that is 3 channels using the isRGBFormat
* return value and handle it manually
*/
virtual std::pair<SkColorType, bool /*isRGB888Format*/> supportedWritePixelsColorType(
SkColorType dstColorType,
const TextureInfo& dstTextureInfo,
SkColorType srcColorType) const = 0;
/**
* Given a src surface's color type and its texture info as well as a color type the caller
* would like read into, this provides a legal color type that the caller can use for
* readPixels. The returned color type may differ from the passed dstColorType, in
* which case the caller must convert the read pixel data (see GrConvertPixels). When converting
* to dstColorType the swizzle in the returned struct should be applied. The caller must check
* the returned color type for kUnknown.
*
* We currently don't have an SkColorType for a 3 channel RGB format. Additionally the current
* implementation of raster pipeline requires power of 2 channels, so it is not easy to add such
* an SkColorType. Thus we need to check for data that is 3 channels using the isRGBFormat
* return value and handle it manually
*/
virtual std::pair<SkColorType, bool /*isRGBFormat*/> supportedReadPixelsColorType(
SkColorType srcColorType,
const TextureInfo& srcTextureInfo,
SkColorType dstColorType) const = 0;
/**
* Checks whether the passed color type is renderable. If so, the same color type is passed
* back. If not, provides an alternative (perhaps lower bit depth and/or unorm instead of float)
* color type that is supported or kUnknown if there no renderable fallback format.
*/
SkColorType getRenderableColorType(SkColorType) const;
bool clampToBorderSupport() const { return fClampToBorderSupport; }
bool protectedSupport() const { return fProtectedSupport; }
// Supports BackendSemaphores
bool semaphoreSupport() const { return fSemaphoreSupport; }
// If false then calling Context::submit with SyncToCpu::kYes is an error.
bool allowCpuSync() const { return fAllowCpuSync; }
// Returns whether storage buffers are supported.
bool storageBufferSupport() const { return fStorageBufferSupport; }
// Returns whether storage buffers are preferred over uniform buffers, when both will yield
// correct results.
bool storageBufferPreferred() const { return fStorageBufferPreferred; }
// Returns whether a draw buffer can be mapped.
bool drawBufferCanBeMapped() const { return fDrawBufferCanBeMapped; }
#if defined(GRAPHITE_TEST_UTILS)
bool drawBufferCanBeMappedForReadback() const { return fDrawBufferCanBeMappedForReadback; }
#endif
// Returns whether using Buffer::asyncMap() must be used to map buffers. map() may only be
// called after asyncMap() is called and will fail if the asynchronous map is not complete. This
// excludes premapped buffers for which map() can be called freely until the first unmap() call.
bool bufferMapsAreAsync() const { return fBufferMapsAreAsync; }
// Returns whether multisampled render to single sampled is supported.
bool msaaRenderToSingleSampledSupport() const { return fMSAARenderToSingleSampledSupport; }
// Returns whether compute shaders are supported.
bool computeSupport() const { return fComputeSupport; }
/**
* Returns true if the given backend supports importing AHardwareBuffers. This will only
* ever be supported on Android devices with API level >= 26.
*/
bool supportsAHardwareBufferImages() const { return fSupportsAHardwareBufferImages; }
// Returns the skgpu::Swizzle to use when sampling or reading back from a texture with the
// passed in SkColorType and TextureInfo.
skgpu::Swizzle getReadSwizzle(SkColorType, const TextureInfo&) const;
// Returns the skgpu::Swizzle to use when writing colors to a surface with the passed in
// SkColorType and TextureInfo.
skgpu::Swizzle getWriteSwizzle(SkColorType, const TextureInfo&) const;
skgpu::ShaderErrorHandler* shaderErrorHandler() const { return fShaderErrorHandler; }
// Returns what method of dst read is required for a draw using the dst color.
DstReadRequirement getDstReadRequirement() const;
float minDistanceFieldFontSize() const { return fMinDistanceFieldFontSize; }
float glyphsAsPathsFontSize() const { return fGlyphsAsPathsFontSize; }
size_t glyphCacheTextureMaximumBytes() const { return fGlyphCacheTextureMaximumBytes; }
int maxPathAtlasTextureSize() const { return fMaxPathAtlasTextureSize; }
bool allowMultipleAtlasTextures() const { return fAllowMultipleAtlasTextures; }
bool supportBilerpFromGlyphAtlas() const { return fSupportBilerpFromGlyphAtlas; }
bool requireOrderedRecordings() const { return fRequireOrderedRecordings; }
// When uploading to a full compressed texture do we need to pad the size out to a multiple of
// the block width and height.
bool fullCompressedUploadSizeMustAlignToBlockDims() const {
return fFullCompressedUploadSizeMustAlignToBlockDims;
}
sktext::gpu::SDFTControl getSDFTControl(bool useSDFTForSmallText) const;
bool setBackendLabels() const { return fSetBackendLabels; }
protected:
Caps();
// Subclasses must call this at the end of their init method in order to do final processing on
// the caps.
void finishInitialization(const ContextOptions&);
#if defined(GRAPHITE_TEST_UTILS)
void setDeviceName(const char* n) {
fDeviceName = n;
}
#endif
// There are only a few possible valid sample counts (1, 2, 4, 8, 16). So we can key on those 5
// options instead of the actual sample value.
static inline uint32_t SamplesToKey(uint32_t numSamples) {
switch (numSamples) {
case 1:
return 0;
case 2:
return 1;
case 4:
return 2;
case 8:
return 3;
case 16:
return 4;
default:
SkUNREACHABLE;
}
}
// ColorTypeInfo for a specific format. Used in format tables.
struct ColorTypeInfo {
ColorTypeInfo() = default;
ColorTypeInfo(SkColorType ct, SkColorType transferCt, uint32_t flags,
skgpu::Swizzle readSwizzle, skgpu::Swizzle writeSwizzle)
: fColorType(ct)
, fTransferColorType(transferCt)
, fFlags(flags)
, fReadSwizzle(readSwizzle)
, fWriteSwizzle(writeSwizzle) {}
SkColorType fColorType = kUnknown_SkColorType;
SkColorType fTransferColorType = kUnknown_SkColorType;
enum {
kUploadData_Flag = 0x1,
// Does Graphite itself support rendering to this colorType & format pair. Renderability
// still additionally depends on if the format itself is renderable.
kRenderable_Flag = 0x2,
};
uint32_t fFlags = 0;
skgpu::Swizzle fReadSwizzle;
skgpu::Swizzle fWriteSwizzle;
};
int fMaxTextureSize = 0;
int fDefaultMSAASamples = 4;
size_t fRequiredUniformBufferAlignment = 0;
size_t fRequiredStorageBufferAlignment = 0;
size_t fRequiredTransferBufferAlignment = 0;
size_t fTextureDataRowBytesAlignment = 1;
std::unique_ptr<SkSL::ShaderCaps> fShaderCaps;
bool fClampToBorderSupport = true;
bool fProtectedSupport = false;
bool fSemaphoreSupport = false;
bool fAllowCpuSync = true;
bool fStorageBufferSupport = false;
bool fStorageBufferPreferred = false;
bool fDrawBufferCanBeMapped = true;
bool fBufferMapsAreAsync = false;
bool fMSAARenderToSingleSampledSupport = false;
bool fComputeSupport = false;
bool fSupportsAHardwareBufferImages = false;
bool fFullCompressedUploadSizeMustAlignToBlockDims = false;
#if defined(GRAPHITE_TEST_UTILS)
bool fDrawBufferCanBeMappedForReadback = true;
#endif
ResourceBindingRequirements fResourceBindingReqs;
//////////////////////////////////////////////////////////////////////////////////////////
// Client-provided Caps
/**
* If present, use this object to report shader compilation failures. If not, report failures
* via SkDebugf and assert.
*/
ShaderErrorHandler* fShaderErrorHandler = nullptr;
#if defined(GRAPHITE_TEST_UTILS)
std::string fDeviceName;
int fMaxTextureAtlasSize = 2048;
PathRendererStrategy fRequestedPathRendererStrategy;
#endif
size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4;
float fMinDistanceFieldFontSize = 18;
float fGlyphsAsPathsFontSize = 324;
int fMaxPathAtlasTextureSize = 8192;
bool fAllowMultipleAtlasTextures = true;
bool fSupportBilerpFromGlyphAtlas = false;
// Set based on client options
bool fRequireOrderedRecordings = false;
bool fSetBackendLabels = false;
private:
virtual bool onIsTexturable(const TextureInfo&) const = 0;
virtual const ColorTypeInfo* getColorTypeInfo(SkColorType, const TextureInfo&) const = 0;
sk_sp<SkCapabilities> fCapabilities;
};
} // namespace skgpu::graphite
#endif // skgpu_graphite_Caps_DEFINED