blob: 5499c1f85539c4ed6a064b699f09807050cf1e19 [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.
*/
#include "src/gpu/graphite/dawn/DawnCaps.h"
#include <algorithm>
#include <string>
#include "include/core/SkTextureCompressionType.h"
#include "include/gpu/graphite/ContextOptions.h"
#include "include/gpu/graphite/TextureInfo.h"
#include "include/gpu/graphite/dawn/DawnBackendContext.h"
#include "include/gpu/graphite/dawn/DawnGraphiteTypes.h"
#include "src/gpu/SwizzlePriv.h"
#include "src/gpu/graphite/ComputePipelineDesc.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/GraphiteResourceKey.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/ResourceTypes.h"
#include "src/gpu/graphite/TextureInfoPriv.h"
#include "src/gpu/graphite/UniformManager.h"
#include "src/gpu/graphite/dawn/DawnGraphicsPipeline.h"
#include "src/gpu/graphite/dawn/DawnGraphiteUtils.h"
#include "src/sksl/SkSLUtil.h"
#if defined(__EMSCRIPTEN__)
#include <emscripten/version.h>
#endif
namespace {
skgpu::UniqueKey::Domain get_pipeline_domain() {
static const skgpu::UniqueKey::Domain kDawnGraphicsPipelineDomain =
skgpu::UniqueKey::GenerateDomain();
return kDawnGraphicsPipelineDomain;
}
// These are all the valid wgpu::TextureFormat that we currently support in Skia.
// They are roughly ordered from most frequently used to least to improve lookup times in arrays.
static constexpr wgpu::TextureFormat kFormats[] = {
wgpu::TextureFormat::RGBA8Unorm,
wgpu::TextureFormat::R8Unorm,
#if !defined(__EMSCRIPTEN__)
wgpu::TextureFormat::R16Unorm,
#endif
wgpu::TextureFormat::BGRA8Unorm,
wgpu::TextureFormat::RGBA16Float,
wgpu::TextureFormat::R16Float,
wgpu::TextureFormat::RG8Unorm,
#if !defined(__EMSCRIPTEN__)
wgpu::TextureFormat::RG16Unorm,
#endif
wgpu::TextureFormat::RGB10A2Unorm,
wgpu::TextureFormat::RG16Float,
wgpu::TextureFormat::Stencil8,
wgpu::TextureFormat::Depth16Unorm,
wgpu::TextureFormat::Depth32Float,
wgpu::TextureFormat::Depth24PlusStencil8,
wgpu::TextureFormat::BC1RGBAUnorm,
wgpu::TextureFormat::ETC2RGB8Unorm,
#if !defined(__EMSCRIPTEN__)
wgpu::TextureFormat::External,
#endif
};
#if !defined(__EMSCRIPTEN__)
bool IsMultiplanarFormat(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
return true;
default:
return false;
}
}
#endif
} // anonymous namespace
namespace skgpu::graphite {
DawnCaps::DawnCaps(const DawnBackendContext& backendContext, const ContextOptions& options)
: Caps() {
this->initCaps(backendContext, options);
this->initShaderCaps(backendContext.fDevice);
this->initFormatTable(backendContext.fDevice);
this->finishInitialization(options);
}
DawnCaps::~DawnCaps() = default;
bool DawnCaps::isTexturableIgnoreSampleCount(const TextureInfo& info) const {
auto overrideDawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info);
overrideDawnInfo.fSampleCount = 1;
TextureInfo overrideInfo = TextureInfos::MakeDawn(overrideDawnInfo);
return this->isTexturable(overrideInfo);
}
bool DawnCaps::onIsTexturable(const TextureInfo& info) const {
if (!info.isValid()) {
return false;
}
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info);
if (!(dawnInfo.fUsage & wgpu::TextureUsage::TextureBinding)) {
return false;
}
#if !defined(__EMSCRIPTEN__)
switch (dawnInfo.fFormat) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm: {
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane0Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
return false;
}
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane1Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
return false;
}
break;
}
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm: {
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane0Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::R16Unorm) {
return false;
}
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane1Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::RG16Unorm) {
return false;
}
break;
}
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm: {
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane0Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
return false;
}
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane1Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::RG8Unorm) {
return false;
}
if (dawnInfo.fAspect == wgpu::TextureAspect::Plane2Only &&
dawnInfo.getViewFormat() != wgpu::TextureFormat::R8Unorm) {
return false;
}
break;
}
default:
break;
}
#endif
const FormatInfo& formatInfo = this->getFormatInfo(dawnInfo.getViewFormat());
return SkToBool(FormatInfo::kTexturable_Flag & formatInfo.fFlags);
}
bool DawnCaps::isRenderable(const TextureInfo& info) const {
if (!info.isValid()) {
return false;
}
TextureFormat format = TextureInfoPriv::ViewFormat(info);
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info);
return (dawnInfo.fUsage & wgpu::TextureUsage::RenderAttachment) &&
this->isSampleCountSupported(format, info.numSamples());
}
bool DawnCaps::isStorage(const TextureInfo& info) const {
if (!info.isValid()) {
return false;
}
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info);
if (!(dawnInfo.fUsage & wgpu::TextureUsage::StorageBinding)) {
return false;
}
const FormatInfo& formatInfo = this->getFormatInfo(dawnInfo.getViewFormat());
return dawnInfo.fSampleCount == 1 && SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags);
}
bool DawnCaps::isSampleCountSupported(TextureFormat format, uint8_t requestedSampleCount) const {
const FormatInfo& formatInfo = this->getFormatInfo(TextureFormatToDawnFormat(format));
if (!SkToBool(formatInfo.fFlags & FormatInfo::kRenderable_Flag)) {
return 0;
}
if (SkToBool(formatInfo.fFlags & FormatInfo::kMSAA_Flag)) {
// WebGPU only supports a sample count of 1 or 4.
return requestedSampleCount == 1 || requestedSampleCount == 4;
} else {
return requestedSampleCount == 1;
}
}
TextureFormat DawnCaps::getDepthStencilFormat(SkEnumBitMask<DepthStencilFlags> mask) const {
// TODO: Decide if we want to change this to always return a combined depth and stencil format
// to allow more sharing of depth stencil allocations.
if (mask == DepthStencilFlags::kDepth) {
// If needed for workarounds or performance, Depth32Float is also available but requires 2x
// the amount of memory.
return TextureFormat::kD16;
} else if (mask == DepthStencilFlags::kStencil) {
return TextureFormat::kS8;
} else if (mask == DepthStencilFlags::kDepthStencil) {
// For WebGPU, this maps to kDepth24PlusStencil8, which might end up choosing the equivalent
// of D32F_S8 under the hood.
return TextureFormat::kD24_S8;
}
return TextureFormat::kUnsupported;
}
TextureInfo DawnCaps::getDefaultAttachmentTextureInfo(AttachmentDesc desc,
Protected,
Discardable discardable) const {
if (!this->isSampleCountSupported(desc.fFormat, desc.fSampleCount)) {
return {};
}
DawnTextureInfo info;
info.fSampleCount = desc.fSampleCount;
info.fMipmapped = Mipmapped::kNo;
info.fFormat = TextureFormatToDawnFormat(desc.fFormat);
info.fUsage = wgpu::TextureUsage::RenderAttachment;
if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None &&
discardable == Discardable::kYes) {
info.fUsage |= fSupportedTransientAttachmentUsage;
}
if (fEmulateLoadStoreResolve && !TextureFormatIsDepthOrStencil(desc.fFormat)) {
// When emulating the store, the color attachment is sampled into the resolve.
info.fUsage |= wgpu::TextureUsage::TextureBinding;
}
return TextureInfos::MakeDawn(info);
}
TextureInfo DawnCaps::getDefaultSampledTextureInfo(SkColorType colorType,
Mipmapped mipmapped,
Protected,
Renderable renderable) const {
wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
wgpu::TextureUsage::CopyDst |
wgpu::TextureUsage::CopySrc;
if (renderable == Renderable::kYes) {
usage |= wgpu::TextureUsage::RenderAttachment;
}
wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
if (format == wgpu::TextureFormat::Undefined) {
return {};
}
DawnTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fFormat = format;
info.fViewFormat = format;
info.fUsage = usage;
return TextureInfos::MakeDawn(info);
}
TextureInfo DawnCaps::getTextureInfoForSampledCopy(const TextureInfo& textureInfo,
Mipmapped mipmapped) const {
DawnTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fFormat = TextureInfoPriv::Get<DawnTextureInfo>(textureInfo).getViewFormat();
info.fUsage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst |
wgpu::TextureUsage::CopySrc;
return TextureInfos::MakeDawn(info);
}
namespace {
wgpu::TextureFormat format_from_compression(SkTextureCompressionType compression) {
switch (compression) {
case SkTextureCompressionType::kETC2_RGB8_UNORM:
return wgpu::TextureFormat::ETC2RGB8Unorm;
case SkTextureCompressionType::kBC1_RGBA8_UNORM:
return wgpu::TextureFormat::BC1RGBAUnorm;
default:
return wgpu::TextureFormat::Undefined;
}
}
}
TextureInfo DawnCaps::getDefaultCompressedTextureInfo(SkTextureCompressionType compression,
Mipmapped mipmapped,
Protected) const {
wgpu::TextureUsage usage = wgpu::TextureUsage::TextureBinding |
wgpu::TextureUsage::CopyDst |
wgpu::TextureUsage::CopySrc;
wgpu::TextureFormat format = format_from_compression(compression);
if (format == wgpu::TextureFormat::Undefined) {
return {};
}
DawnTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = mipmapped;
info.fFormat = format;
info.fViewFormat = format;
info.fUsage = usage;
return TextureInfos::MakeDawn(info);
}
TextureInfo DawnCaps::getDefaultStorageTextureInfo(SkColorType colorType) const {
wgpu::TextureFormat format = this->getFormatFromColorType(colorType);
if (format == wgpu::TextureFormat::Undefined) {
SkDebugf("colorType=%d is not supported\n", static_cast<int>(colorType));
return {};
}
const FormatInfo& formatInfo = this->getFormatInfo(format);
if (!SkToBool(FormatInfo::kStorage_Flag & formatInfo.fFlags)) {
return {};
}
wgpu::TextureUsage usage = wgpu::TextureUsage::StorageBinding |
wgpu::TextureUsage::TextureBinding |
wgpu::TextureUsage::CopySrc;
DawnTextureInfo info;
info.fSampleCount = 1;
info.fMipmapped = Mipmapped::kNo;
info.fFormat = format;
info.fViewFormat = format;
info.fUsage = usage;
return TextureInfos::MakeDawn(info);
}
SkISize DawnCaps::getDepthAttachmentDimensions(const TextureInfo& textureInfo,
const SkISize colorAttachmentDimensions) const {
#if !defined(__EMSCRIPTEN__)
// For multiplanar textures, texture->textureInfo() uses the format of planes instead of
// textures (R8, R8G8, vs R8BG8Biplanar420Unorm), so we have to query texture format from
// wgpu::Texture object, and then use it reconstruct the full dimensions.
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(textureInfo);
wgpu::TextureFormat format = dawnInfo.fFormat;
if (IsMultiplanarFormat(format) && dawnInfo.fAspect == wgpu::TextureAspect::Plane1Only) {
// Dawn requires depth attachment to match the size of Y plane (texture size).
return SkISize::Make(colorAttachmentDimensions.width() * 2,
colorAttachmentDimensions.height() * 2);
}
#endif
return colorAttachmentDimensions;
}
const Caps::ColorTypeInfo* DawnCaps::getColorTypeInfo(SkColorType colorType,
const TextureInfo& textureInfo) const {
auto dawnFormat = TextureInfoPriv::Get<DawnTextureInfo>(textureInfo).getViewFormat();
if (dawnFormat == wgpu::TextureFormat::Undefined) {
SkASSERT(false);
return nullptr;
}
const FormatInfo& info = this->getFormatInfo(dawnFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const ColorTypeInfo& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == colorType) {
return &ctInfo;
}
}
return nullptr;
}
bool DawnCaps::supportsWritePixels(const TextureInfo& textureInfo) const {
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(textureInfo);
return dawnInfo.fUsage & wgpu::TextureUsage::CopyDst;
}
bool DawnCaps::supportsReadPixels(const TextureInfo& textureInfo) const {
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(textureInfo);
return dawnInfo.fUsage & wgpu::TextureUsage::CopySrc;
}
std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedWritePixelsColorType(
SkColorType dstColorType,
const TextureInfo& dstTextureInfo,
SkColorType srcColorType) const {
const auto viewFormat = TextureInfoPriv::Get<DawnTextureInfo>(dstTextureInfo).getViewFormat();
const FormatInfo& info = this->getFormatInfo(viewFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const auto& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == dstColorType) {
return {ctInfo.fTransferColorType, false};
}
}
return {kUnknown_SkColorType, false};
}
std::pair<SkColorType, bool /*isRGBFormat*/> DawnCaps::supportedReadPixelsColorType(
SkColorType srcColorType,
const TextureInfo& srcTextureInfo,
SkColorType dstColorType) const {
const auto viewFormat = TextureInfoPriv::Get<DawnTextureInfo>(srcTextureInfo).getViewFormat();
if (DawnFormatToCompressionType(viewFormat) != SkTextureCompressionType::kNone) {
return {kUnknown_SkColorType, false};
}
const FormatInfo& info = this->getFormatInfo(viewFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
const auto& ctInfo = info.fColorTypeInfos[i];
if (ctInfo.fColorType == srcColorType) {
return {ctInfo.fTransferColorType, false};
}
}
return {kUnknown_SkColorType, false};
}
void DawnCaps::initCaps(const DawnBackendContext& backendContext, const ContextOptions& options) {
// GetAdapter() is not available in WASM and there's no way to get AdapterInfo off of
// the WGPUDevice directly.
#if !defined(__EMSCRIPTEN__)
wgpu::AdapterInfo info;
backendContext.fDevice.GetAdapter().GetInfo(&info);
#if defined(GPU_TEST_UTILS)
this->setDeviceName(std::string(info.device));
#endif
#endif // defined(__EMSCRIPTEN__)
#if defined(__EMSCRIPTEN__)
wgpu::SupportedLimits supportedLimits;
// TODO(crbug.com/42241199): Update to use wgpu::Status when webgpu.h in Emscripten is updated.
[[maybe_unused]] bool limitsSucceeded = backendContext.fDevice.GetLimits(&supportedLimits);
SkASSERT(limitsSucceeded);
wgpu::Limits& limits = supportedLimits.limits;
#else
wgpu::Limits limits;
wgpu::DawnTexelCopyBufferRowAlignmentLimits alignmentLimits{};
if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
limits.nextInChain = &alignmentLimits;
}
[[maybe_unused]] wgpu::Status status = backendContext.fDevice.GetLimits(&limits);
SkASSERT(status == wgpu::Status::Success);
#endif // defined(__EMSCRIPTEN__)
fMaxTextureSize = limits.maxTextureDimension2D;
fRequiredTransferBufferAlignment = 4;
fRequiredUniformBufferAlignment = limits.minUniformBufferOffsetAlignment;
fRequiredStorageBufferAlignment = limits.minStorageBufferOffsetAlignment;
fMaxVaryings = limits.maxInterStageShaderVariables;
// Dawn requires 256 bytes per row alignment for buffer texture copies.
fTextureDataRowBytesAlignment = 256;
#if !defined(__EMSCRIPTEN__)
// If the device supports the DawnTexelCopyBufferRowAlignment feature, the alignment can be
// queried from its limits.
if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
fTextureDataRowBytesAlignment = alignmentLimits.minTexelCopyBufferRowAlignment;
}
#endif
fResourceBindingReqs.fUniformBufferLayout = Layout::kStd140;
// The WGSL generator assumes tightly packed std430 layout for SSBOs which is also the default
// for all types outside the uniform address space in WGSL.
fResourceBindingReqs.fStorageBufferLayout = Layout::kStd430;
fResourceBindingReqs.fSeparateTextureAndSamplerBinding = true;
fResourceBindingReqs.fUniformsSetIdx = DawnGraphicsPipeline::kUniformBufferBindGroupIndex;
fResourceBindingReqs.fTextureSamplerSetIdx = DawnGraphicsPipeline::kTextureBindGroupIndex;
fResourceBindingReqs.fIntrinsicBufferBinding =
DawnGraphicsPipeline::kIntrinsicUniformBufferIndex;
fResourceBindingReqs.fRenderStepBufferBinding =
DawnGraphicsPipeline::kRenderStepUniformBufferIndex;
fResourceBindingReqs.fPaintParamsBufferBinding = DawnGraphicsPipeline::kPaintUniformBufferIndex;
fResourceBindingReqs.fGradientBufferBinding = DawnGraphicsPipeline::kGradientBufferIndex;
#if !defined(__EMSCRIPTEN__)
// We need at least 4 SSBOs for intrinsic, render step, paint & gradient buffers.
// TODO(b/418235681): Enable SSBOs after fixing performance regressions for Dawn/Vulkan.
fStorageBufferSupport = info.backendType != wgpu::BackendType::OpenGL &&
info.backendType != wgpu::BackendType::OpenGLES &&
info.backendType != wgpu::BackendType::Vulkan &&
limits.maxStorageBuffersInVertexStage >= 4 &&
limits.maxStorageBuffersInFragmentStage >= 4;
#else
// WASM doesn't provide a way to query the backend, so can't tell if we are on a backend that
// needs to have SSBOs disabled. Pessimistically assume we could be. Once the above conditions
// go away in Dawn-native, then we can assume SSBOs are always supported in pure WebGPU too.
fStorageBufferSupport = false;
#endif
fDrawBufferCanBeMapped = false;
fComputeSupport = true;
// TODO: support clamp to border.
fClampToBorderSupport = false;
#if defined(GPU_TEST_UTILS)
fDrawBufferCanBeMappedForReadback = false;
#endif
#if defined(__EMSCRIPTEN__)
// For wasm, we use async map.
fBufferMapsAreAsync = true;
#else
// For Dawn native, we use direct mapping.
fBufferMapsAreAsync = false;
fDrawBufferCanBeMapped =
backendContext.fDevice.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages);
fMSAARenderToSingleSampledSupport =
backendContext.fDevice.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled);
if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TransientAttachments)) {
fSupportedTransientAttachmentUsage = wgpu::TextureUsage::TransientAttachment;
}
#endif
if (fSupportedTransientAttachmentUsage == wgpu::TextureUsage::None) {
// Without transient attachments, we currently emulate load/resolve using separate render
// passes, so that mismatched MSAA & resolve attachments' sizes can work. This helps
// reuse MSAA textures better to reduce memory usage.
// TODO(b/399640773): Avoid this when Dawn implements the partial resolve feature
// that can support mismatched sized MSAA & resolve attachments.
fEmulateLoadStoreResolve = true;
fDifferentResolveAttachmentSizeSupport = true;
}
#if !defined(__EMSCRIPTEN__)
if (!fEmulateLoadStoreResolve) {
if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
fSupportedResolveTextureLoadOp = wgpu::LoadOp::ExpandResolveTexture;
}
fSupportsPartialLoadResolve =
backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture);
}
#endif
if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TimestampQuery)) {
// Native Dawn has an API for writing timestamps on command buffers. WebGPU only supports
// begin and end timestamps on render and compute passes.
#if !defined(__EMSCRIPTEN__)
// TODO(b/42240559): On Apple silicon, the timer queries don't have the correct dependencies
// to measure all the encoders that the start/end commands encapsulate in the commandbuffer.
// We would prefer to keep this API as it lets us measure our texture uploads. If either
// this is fixed in Dawn, we can unconditionally take this approach for dawn-native; or
// the WebGPU API can hopefully be extended to capture blit passes.
fSupportsCommandBufferTimestamps = info.backendType != wgpu::BackendType::Metal;
#endif
// The emscripten C/C++ interface before 3.1.48 for timestamp query writes on render and
// compute passes is different than on current emsdk. The older API isn't correctly
// translated to the current JS WebGPU API in emsdk. So we require 3.1.48+.
#if !defined(__EMSCRIPTEN__) \
|| (__EMSCRIPTEN_major__ > 3) \
|| (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ > 1) \
|| (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 48)
fSupportedGpuStats |= GpuStatsFlags::kElapsedTime;
#endif
}
if (!backendContext.fTick) {
fAllowCpuSync = false;
// This seems paradoxical. However, if we use the async pipeline creation methods (e.g
// Device::CreateRenderPipelineAsync) then we may have to synchronize before a submit that
// uses the pipeline. If we use the methods that look synchronous (e.g.
// Device::CreateRenderPipeline) they actually operate asynchronously on WebGPU but the
// browser becomes responsible for synchronizing when we call submit.
fUseAsyncPipelineCreation = false;
// The implementation busy waits after popping.
fAllowScopedErrorChecks = false;
}
fFullCompressedUploadSizeMustAlignToBlockDims = true;
}
void DawnCaps::initShaderCaps(const wgpu::Device& device) {
SkSL::ShaderCaps* shaderCaps = fShaderCaps.get();
// WGSL does not support infinities regardless of hardware support. There are discussions around
// enabling it using an extension in the future.
shaderCaps->fInfinitySupport = false;
// WGSL supports shader derivatives in the fragment shader
shaderCaps->fShaderDerivativeSupport = true;
#if !defined(__EMSCRIPTEN__)
if (device.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
shaderCaps->fDualSourceBlendingSupport = true;
}
if (device.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
shaderCaps->fFBFetchSupport = true;
}
#endif
}
void DawnCaps::initFormatTable(const wgpu::Device& device) {
// NOTE: wgpu::TextureFormat's naming convention orders channels from least significant to most,
// matching the data address ordering of a little endian system.
FormatInfo* info;
// Format: RGBA8Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA8Unorm, Surface: kRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_8888_SkColorType;
ctInfo.fTransferColorType = kRGBA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: RGBA8Unorm, Surface: kRGB_888x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_888x_SkColorType;
ctInfo.fTransferColorType = kRGB_888x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
}
}
// Format: R8Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R8Unorm)];
#if !defined(__EMSCRIPTEN__)
info->fFlags = FormatInfo::kAllFlags;
if (!device.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
info->fFlags &= ~FormatInfo::kStorage_Flag;
}
#else
info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
#endif
info->fColorTypeInfoCount = 3;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R8Unorm, Surface: kR8_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR8_unorm_SkColorType;
ctInfo.fTransferColorType = kR8_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: R8Unorm, Surface: kAlpha_8
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kAlpha_8_SkColorType;
ctInfo.fTransferColorType = kAlpha_8_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
// Format: R8Unorm, Surface: kGray_8
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kGray_8_SkColorType;
ctInfo.fTransferColorType = kGray_8_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("rrr1");
}
}
#if !defined(__EMSCRIPTEN__)
const bool supportUnorm16 = device.HasFeature(wgpu::FeatureName::Unorm16TextureFormats);
// TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
// Format: R16Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Unorm)];
if (supportUnorm16) {
info->fFlags = FormatInfo::kAllFlags & ~FormatInfo::kStorage_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R16Unorm, Surface: kA16_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kA16_unorm_SkColorType;
ctInfo.fTransferColorType = kA16_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
}
}
#endif
// Format: BGRA8Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BGRA8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: BGRA8Unorm, Surface: kBGRA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kBGRA_8888_SkColorType;
ctInfo.fTransferColorType = kBGRA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: BGRA8Unorm, Surface: kRGB_888x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_888x_SkColorType;
// There is no kBGR_888x color type, so report that the data is BGRA and rely on
// SkConvertPixels to force alpha to opaque when kRGB_888x is either the src or dst type
ctInfo.fTransferColorType = kBGRA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
}
}
// Format: RGBA16Float
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGBA16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGBA16Float, Surface: RGBA_F16
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_F16_SkColorType;
ctInfo.fTransferColorType = kRGBA_F16_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: RGBA16Float, Surface: RGB_F16F16F16x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_F16F16F16x_SkColorType;
ctInfo.fTransferColorType = kRGB_F16F16F16x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
}
}
// Format: R16Float
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::R16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: R16Float, Surface: kA16_float
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kA16_float_SkColorType;
ctInfo.fTransferColorType = kA16_float_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle("000r");
ctInfo.fWriteSwizzle = skgpu::Swizzle("a000");
}
}
// TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
// Format: RG8Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG8Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG8Unorm, Surface: kR8G8_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR8G8_unorm_SkColorType;
ctInfo.fTransferColorType = kR8G8_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
#if !defined(__EMSCRIPTEN__)
// TODO(crbug.com/dawn/1856): Support storage binding for compute shader in Dawn.
// Format: RG16Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Unorm)];
if (supportUnorm16) {
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG16Unorm, Surface: kR16G16_unorm
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR16G16_unorm_SkColorType;
ctInfo.fTransferColorType = kR16G16_unorm_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
}
#endif
// Format: RGB10A2Unorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RGB10A2Unorm)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 2;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RGB10A2Unorm, Surface: kRGBA_1010102
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_1010102_SkColorType;
ctInfo.fTransferColorType = kRGBA_1010102_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
// Format: RGB10A2Unorm, Surface: kRGB_101010x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_101010x_SkColorType;
ctInfo.fTransferColorType = kRGB_101010x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
ctInfo.fReadSwizzle = skgpu::Swizzle::RGB1();
}
}
// Format: RG16Float
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::RG16Float)];
info->fFlags = FormatInfo::kAllFlags;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: RG16Float, Surface: kR16G16_float
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kR16G16_float_SkColorType;
ctInfo.fTransferColorType = kR16G16_float_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag | ColorTypeInfo::kRenderable_Flag;
}
}
// Format: ETC2RGB8Unorm
{
if (device.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::ETC2RGB8Unorm)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: ETC2RGB8Unorm, Surface: kRGB_888x
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGB_888x_SkColorType;
ctInfo.fTransferColorType = kRGB_888x_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
}
}
}
// Format: BC1RGBAUnorm
{
if (device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::BC1RGBAUnorm)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: BC1RGBAUnorm, Surface: kRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_8888_SkColorType;
ctInfo.fTransferColorType = kRGBA_8888_SkColorType;
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
}
}
}
/*
* Non-color formats (renderable but with no color type)
*/
// Format: Stencil8
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Stencil8)];
info->fFlags = FormatInfo::kMSAA_Flag | FormatInfo::kRenderable_Flag;
info->fColorTypeInfoCount = 0;
}
// Format: Depth16UNorm
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth16Unorm)];
info->fFlags = FormatInfo::kMSAA_Flag | FormatInfo::kRenderable_Flag;
info->fColorTypeInfoCount = 0;
}
// Format: Depth32Float
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth32Float)];
info->fFlags = FormatInfo::kMSAA_Flag | FormatInfo::kRenderable_Flag;
info->fColorTypeInfoCount = 0;
}
// Format: Depth24PlusStencil8
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::Depth24PlusStencil8)];
info->fFlags = FormatInfo::kMSAA_Flag | FormatInfo::kRenderable_Flag;
info->fColorTypeInfoCount = 0;
}
#if !defined(__EMSCRIPTEN__)
// Format: External
{
info = &fFormatTable[GetFormatIndex(wgpu::TextureFormat::External)];
info->fFlags = FormatInfo::kTexturable_Flag;
info->fColorTypeInfoCount = 1;
info->fColorTypeInfos = std::make_unique<ColorTypeInfo[]>(info->fColorTypeInfoCount);
int ctIdx = 0;
// Format: External, Surface: kRGBA_8888
{
auto& ctInfo = info->fColorTypeInfos[ctIdx++];
ctInfo.fColorType = kRGBA_8888_SkColorType;
}
}
#endif
////////////////////////////////////////////////////////////////////////////
// Map SkColorTypes (used for creating SkSurfaces) to wgpu::TextureFormat.
// The order in which the formats are passed into the setColorType function
// indicates the priority in selecting which format we use for a given
// SkColorType.
std::fill_n(fColorTypeToFormatTable, kSkColorTypeCnt, wgpu::TextureFormat::Undefined);
this->setColorType(kAlpha_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
this->setColorType(kRGBA_8888_SkColorType, { wgpu::TextureFormat::RGBA8Unorm });
this->setColorType(kRGB_888x_SkColorType,
{wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::BGRA8Unorm});
this->setColorType(kBGRA_8888_SkColorType, { wgpu::TextureFormat::BGRA8Unorm });
this->setColorType(kGray_8_SkColorType, { wgpu::TextureFormat::R8Unorm });
this->setColorType(kR8_unorm_SkColorType, { wgpu::TextureFormat::R8Unorm });
this->setColorType(kRGBA_F16_SkColorType, { wgpu::TextureFormat::RGBA16Float });
this->setColorType(kRGB_F16F16F16x_SkColorType, { wgpu::TextureFormat::RGBA16Float });
this->setColorType(kA16_float_SkColorType, { wgpu::TextureFormat::R16Float });
this->setColorType(kR8G8_unorm_SkColorType, { wgpu::TextureFormat::RG8Unorm });
this->setColorType(kRGBA_1010102_SkColorType, { wgpu::TextureFormat::RGB10A2Unorm });
this->setColorType(kRGB_101010x_SkColorType, { wgpu::TextureFormat::RGB10A2Unorm });
this->setColorType(kR16G16_float_SkColorType, { wgpu::TextureFormat::RG16Float });
#if !defined(__EMSCRIPTEN__)
this->setColorType(kA16_unorm_SkColorType, { wgpu::TextureFormat::R16Unorm });
this->setColorType(kR16G16_unorm_SkColorType, { wgpu::TextureFormat::RG16Unorm });
#endif
}
// static
size_t DawnCaps::GetFormatIndex(wgpu::TextureFormat format) {
for (size_t i = 0; i < std::size(kFormats); ++i) {
if (format == kFormats[i]) {
return i;
}
}
SkDEBUGFAILF("Unsupported wgpu::TextureFormat: 0x%08X\n", static_cast<uint32_t>(format));
return 0;
}
void DawnCaps::setColorType(SkColorType colorType,
std::initializer_list<wgpu::TextureFormat> formats) {
static_assert(std::size(kFormats) <= kFormatCount,
"Size is not compatible for DawnCaps::fFormatTable and kFormats");
int idx = static_cast<int>(colorType);
for (auto it = formats.begin(); it != formats.end(); ++it) {
const auto& info = this->getFormatInfo(*it);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
if (info.fColorTypeInfos[i].fColorType == colorType) {
fColorTypeToFormatTable[idx] = *it;
return;
}
}
}
}
// TextureFormat is backed by a uint8_t, so 8 bits are always sufficient (including using
// kUnsupported) to represent an unused attachment. To make room for the load-from-resolve bit, we
// reduce the uint8_t of fSampleCount to 3 bits with the SampleToKey function.
static constexpr int kFormatBits = 8; // x2 attachments (color & depthStencil formats)
static constexpr int kSampleBits = 3; // x2 attachments (color & depthStencil numSamples)
static constexpr int kResolveBits = 1;
static_assert(2*(kFormatBits + kSampleBits) + kResolveBits <= 32);
static_assert(kTextureFormatCount < 1 << kFormatBits);
static constexpr int kDepthStencilNumSamplesOffset = /*loadResolveOffset=0 + */ kResolveBits;
static constexpr int kDepthStencilFormatOffset = kDepthStencilNumSamplesOffset + kSampleBits;
static constexpr int kColorNumSamplesOffset = kDepthStencilFormatOffset + kFormatBits;
static constexpr int kColorFormatOffset = kColorNumSamplesOffset + kSampleBits;
static constexpr int kAdditionalFlagOffset = kColorFormatOffset + kFormatBits;
static_assert(kAdditionalFlagOffset <= 31);
static constexpr uint32_t kFormatMask = (1 << kFormatBits) - 1;
static constexpr uint32_t kNumSamplesMask = (1 << kSampleBits) - 1;
static constexpr uint32_t kResolveMask = (1 << kResolveBits) - 1;
uint32_t DawnCaps::getRenderPassDescKeyForPipeline(const RenderPassDesc& renderPassDesc,
bool additionalFlag) const {
// The color attachment should be valid; the depth-stencil attachment may not be if it's not
// being used. The full resolve attachment (if present) does not need to be included.
const auto& color = renderPassDesc.fColorAttachment;
const auto& depthStencil = renderPassDesc.fDepthStencilAttachment;
SkASSERT(color.fFormat != TextureFormat::kUnsupported);
// Note: if Dawn supports ExpandResolveTexture load op and the render pass uses it to load the
// resolve texture, a render pipeline will need to be created with
// wgpu::ColorTargetStateExpandResolveTextureDawn chained struct in order to be compatible.
// Hence a render pipeline created for a render pass using ExpandResolveTexture load op will be
// different from the one created for a render pass not using that load op. So we need to
// include a bit flag to differentiate the two kinds of pipelines.
const bool shouldIncludeLoadResolveAttachmentBit = this->loadOpAffectsMSAAPipelines();
uint32_t loadResolveAttachmentKey = 0;
if (shouldIncludeLoadResolveAttachmentBit &&
renderPassDesc.fColorResolveAttachment.fFormat != TextureFormat::kUnsupported &&
renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
loadResolveAttachmentKey = 1;
}
SkASSERT(SamplesToKey(color.fSampleCount) < (1 << kSampleBits));
SkASSERT(SamplesToKey(depthStencil.fSampleCount) < (1 << kSampleBits));
SkASSERT(loadResolveAttachmentKey < (1 << kResolveBits));
uint32_t additionalFlagKey = additionalFlag ? 1 : 0;
return (additionalFlagKey << kAdditionalFlagOffset) |
(static_cast<uint32_t>(color.fFormat) << kColorFormatOffset) |
(SamplesToKey(color.fSampleCount) << kColorNumSamplesOffset) |
(static_cast<uint32_t>(depthStencil.fFormat) << kDepthStencilFormatOffset) |
(SamplesToKey(depthStencil.fSampleCount) << kDepthStencilNumSamplesOffset) |
loadResolveAttachmentKey;
}
static constexpr int kDawnGraphicsPipelineKeyData32Count = 4;
UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc) const {
UniqueKey pipelineKey;
{
// 4 uint32_t's (render step id, paint id, uint32 RenderPassDesc, uint16 write swizzle key)
UniqueKey::Builder builder(&pipelineKey, get_pipeline_domain(),
kDawnGraphicsPipelineKeyData32Count, "DawnGraphicsPipeline");
// Add GraphicsPipelineDesc key.
builder[0] = static_cast<uint32_t>(pipelineDesc.renderStepID());
builder[1] = pipelineDesc.paintParamsID().asUInt();
// Add RenderPassDesc key and write swizzle (which is separate from the RenderPassDescKey
// because it is applied in the program writing to the target, and is not actually part of
// the underlying GPU render pass config).
builder[2] = this->getRenderPassDescKeyForPipeline(renderPassDesc);
builder[3] = renderPassDesc.fWriteSwizzle.asKey();
builder.finish();
}
return pipelineKey;
}
bool DawnCaps::extractGraphicsDescs(const UniqueKey& key,
GraphicsPipelineDesc* pipelineDesc,
RenderPassDesc* renderPassDesc,
const RendererProvider* rendererProvider) const {
SkASSERT(key.domain() == get_pipeline_domain());
SkASSERT(key.dataSize() == 4 * kDawnGraphicsPipelineKeyData32Count);
const uint32_t* rawKeyData = key.data();
SkASSERT(RenderStep::IsValidRenderStepID(rawKeyData[0]));
RenderStep::RenderStepID renderStepID = static_cast<RenderStep::RenderStepID>(rawKeyData[0]);
SkDEBUGCODE(const RenderStep* renderStep = rendererProvider->lookup(renderStepID);)
*pipelineDesc = GraphicsPipelineDesc(renderStepID, UniquePaintParamsID(rawKeyData[1]));
SkASSERT(renderStep->performsShading() == pipelineDesc->paintParamsID().isValid());
const uint32_t rpDescBits = rawKeyData[2];
TextureFormat colorFormat =
static_cast<TextureFormat>((rpDescBits >> kColorFormatOffset) & kFormatMask);
uint8_t colorSamples =
SkTo<uint8_t>(1 << ((rpDescBits >> kColorNumSamplesOffset) & kNumSamplesMask));
TextureFormat depthStencilFormat =
static_cast<TextureFormat>((rpDescBits >> kDepthStencilFormatOffset) & kFormatMask);
uint8_t depthStencilSamples =
SkTo<uint8_t>(1 << ((rpDescBits >> kDepthStencilNumSamplesOffset) & kNumSamplesMask));
const bool loadFromResolve = (rpDescBits & kResolveMask) != 0;
// This bit should only be set if Dawn supports ExpandResolveTexture load op
SkASSERT(!loadFromResolve || this->loadOpAffectsMSAAPipelines());
// Recreate the RenderPassDesc, assuming that if there is MSAA on the color attachment that
// a resolve attachment is also required. The resolve attachment's load op will match the key.
SkASSERT(colorSamples == depthStencilSamples ||
depthStencilFormat == TextureFormat::kUnsupported);
*renderPassDesc = {};
renderPassDesc->fColorAttachment = {colorFormat,
LoadOp::kClear,
StoreOp::kStore,
colorSamples};
renderPassDesc->fDepthStencilAttachment = {depthStencilFormat,
LoadOp::kClear,
StoreOp::kDiscard,
depthStencilSamples};
if (colorSamples > 1) {
renderPassDesc->fColorResolveAttachment = {colorFormat,
loadFromResolve ? LoadOp::kLoad : LoadOp::kClear,
StoreOp::kStore,
/*fSampleCount=*/1};
renderPassDesc->fColorAttachment.fStoreOp = StoreOp::kDiscard;
}
renderPassDesc->fSampleCount = colorSamples;
renderPassDesc->fWriteSwizzle = SwizzleCtorAccessor::Make(rawKeyData[3]);
renderPassDesc->fDstReadStrategy = this->getDstReadStrategy();
return true;
}
UniqueKey DawnCaps::makeComputePipelineKey(const ComputePipelineDesc& pipelineDesc) const {
UniqueKey pipelineKey;
{
static const skgpu::UniqueKey::Domain kComputePipelineDomain = UniqueKey::GenerateDomain();
// The key is made up of a single uint32_t corresponding to the compute step ID.
UniqueKey::Builder builder(&pipelineKey, kComputePipelineDomain, 1, "ComputePipeline");
builder[0] = pipelineDesc.computeStep()->uniqueID();
// TODO(b/240615224): The local work group size should factor into the key here since it is
// specified in the shader text on Dawn/SPIR-V. This is not a problem right now since
// ComputeSteps don't vary their workgroup size dynamically.
builder.finish();
}
return pipelineKey;
}
ImmutableSamplerInfo DawnCaps::getImmutableSamplerInfo(const TextureInfo& textureInfo) const {
#if !defined(__EMSCRIPTEN__)
const wgpu::YCbCrVkDescriptor& ycbcrConversionInfo =
TextureInfoPriv::Get<DawnTextureInfo>(textureInfo).fYcbcrVkDescriptor;
if (DawnDescriptorIsValid(ycbcrConversionInfo)) {
return DawnDescriptorToImmutableSamplerInfo(ycbcrConversionInfo);
}
#endif
// If the YCbCr conversion for is invalid, then return a default ImmutableSamplerInfo struct.
return {};
}
void DawnCaps::buildKeyForTexture(SkISize dimensions,
const TextureInfo& info,
ResourceType type,
GraphiteResourceKey* key) const {
const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info);
SkASSERT(!dimensions.isEmpty());
SkASSERT(dawnInfo.getViewFormat() != wgpu::TextureFormat::Undefined);
// FIXME we can reduce this by packing format into samplesKey and then we're back down to 5 ints
// we could go further if we said textures were likely to be under 65kx65kf...
uint32_t formatKey = static_cast<uint32_t>(dawnInfo.getViewFormat());
uint32_t samplesKey = SamplesToKey(info.numSamples());
// We don't have to key the number of mip levels because it is inherit in the combination of
// isMipped and dimensions.
bool isMipped = info.mipmapped() == Mipmapped::kYes;
// Confirm all the below parts of the key can fit in a single uint32_t. The sum of the shift
// amounts in the asserts must be less than or equal to 32.
SkASSERT(samplesKey < (1u << 3)); // sample key is first 3 bits
SkASSERT(static_cast<uint32_t>(isMipped) < (1u << 1)); // isMapped is 4th bit
SkASSERT(static_cast<uint32_t>(dawnInfo.fUsage) < (1u << 28)); // usage is remaining 28 bits
// We need two uint32_ts for dimensions, 1 for format, and 1 for the rest of the key;
int num32DataCnt = 2 + 1 + 1;
bool hasYcbcrInfo = false;
#if !defined(__EMSCRIPTEN__)
// If we are using ycbcr texture/sampling, more key information is needed.
if ((hasYcbcrInfo = DawnDescriptorIsValid(dawnInfo.fYcbcrVkDescriptor))) {
num32DataCnt += 3; // non-format flags and 64-bit format
}
#endif
GraphiteResourceKey::Builder builder(key, type, num32DataCnt);
builder[0] = dimensions.width();
builder[1] = dimensions.height();
builder[2] = formatKey;
builder[3] = (samplesKey << 0) |
(static_cast<uint32_t>(isMipped) << 3) |
(static_cast<uint32_t>(dawnInfo.fUsage) << 4);
#if !defined(__EMSCRIPTEN__)
if (hasYcbcrInfo) {
ImmutableSamplerInfo packedInfo =
DawnDescriptorToImmutableSamplerInfo(dawnInfo.fYcbcrVkDescriptor);
builder[4] = packedInfo.fNonFormatYcbcrConversionInfo;
// Even though we already have formatKey appended to the texture key, we still need to add
// fYcbcrVkDescriptor's vkFormat or externalFormat. The latter two are distinct from
// dawnSpec's wgpu::TextureFormat.
builder[5] = (uint32_t) packedInfo.fFormat;
builder[6] = (uint32_t) (packedInfo.fFormat >> 32);
}
#endif
}
} // namespace skgpu::graphite