| /* | |
| * 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/DawnGraphiteUtils.h" | |
| #include "include/core/SkContext.h" | |
| #include "include/gpu/ShaderErrorHandler.h" | |
| #include "include/gpu/graphite/Context.h" | |
| #include "include/gpu/graphite/ContextOptions.h" | |
| #include "include/gpu/graphite/dawn/DawnBackendContext.h" | |
| #include "include/private/SkAttributes.h" | |
| #include "include/private/SkTArray.h" | |
| #include "src/core/SkContextPriv.h" | |
| #include "src/gpu/graphite/ContextPriv.h" | |
| #include "src/gpu/graphite/TextureFormat.h" | |
| #include "src/gpu/graphite/dawn/DawnQueueManager.h" | |
| #include "src/gpu/graphite/dawn/DawnSharedContext.h" | |
| using namespace skia_private; | |
| namespace SkContexts { | |
| std::unique_ptr<SkContext> MakeGraphite(const skgpu::graphite::DawnBackendContext& dawnContext, | |
| const SkContextOptions& contextOptions, | |
| const skgpu::graphite::ContextOptions& graphiteOptions) { | |
| auto context = skgpu::graphite::ContextFactory::MakeDawn(dawnContext, graphiteOptions); | |
| if (!context) { | |
| return nullptr; | |
| } | |
| return SkContextCtorAccessor::MakeContext( | |
| contextOptions, skgpu::graphite::ContextPriv::AsGPUContext(std::move(context))); | |
| } | |
| } // namespace SkContexts | |
| namespace skgpu::graphite { | |
| namespace ContextFactory { | |
| std::unique_ptr<Context> MakeDawn(const DawnBackendContext& backendContext, | |
| const ContextOptions& options) { | |
| sk_sp<SharedContext> sharedContext = DawnSharedContext::Make(backendContext, options); | |
| if (!sharedContext) { | |
| return nullptr; | |
| } | |
| auto queueManager = | |
| std::make_unique<DawnQueueManager>(backendContext.fQueue, sharedContext.get()); | |
| if (!queueManager) { | |
| return nullptr; | |
| } | |
| auto context = ContextCtorAccessor::MakeContext(std::move(sharedContext), | |
| std::move(queueManager), | |
| options); | |
| SkASSERT(context); | |
| return context; | |
| } | |
| } // namespace ContextFactory | |
| namespace { | |
| // If the extension is available (or if it's empty), the format flag bitmask is supported | |
| using ExtensionLimitedFlags = std::pair<std::optional<wgpu::FeatureName>, | |
| SkEnumBitMask<DawnFormatFlag>>; | |
| // These two operators allow an ExtensionLimitedFlags to be built with syntax like: | |
| // wgpu::FeatureName >> Filter | Resolve | ... | |
| // without needing extra parentheses or braces. The | operator is needed to resolve precedence. | |
| ExtensionLimitedFlags operator>>(std::optional<wgpu::FeatureName> extension, DawnFormatFlag flags) { | |
| return std::make_pair(extension, flags); | |
| } | |
| ExtensionLimitedFlags operator|(ExtensionLimitedFlags flagSet, | |
| SkEnumBitMask<DawnFormatFlag> flags) { | |
| return std::make_pair(flagSet.first, flagSet.second | flags); | |
| } | |
| // Stores all ExtensionLimitedFlags for every format and every extension | |
| class DawnFormatCapabilityMap { | |
| public: | |
| static const DawnFormatCapabilityMap& Get(); | |
| SkEnumBitMask<DawnFormatFlag> getFormatFlags(wgpu::Device device, | |
| wgpu::TextureFormat format) const { | |
| const ExtensionFlagList& features = | |
| fFormatFeatures[(int) DawnFormatToTextureFormat(format)]; | |
| SkEnumBitMask<DawnFormatFlag> supported; | |
| for (const ExtensionLimitedFlags& featureSet : features) { | |
| if (!featureSet.first.has_value() || device.HasFeature(*featureSet.first)) { | |
| supported |= featureSet.second; | |
| } | |
| } | |
| return supported; | |
| } | |
| private: | |
| // N = 3 since no current wgpu::TextureFormat modifies its behavior more than its baseline and | |
| // two additional extensions. | |
| using ExtensionFlagList = STArray<3, ExtensionLimitedFlags>; | |
| using FormatFlags = std::pair<wgpu::TextureFormat, ExtensionFlagList>; | |
| DawnFormatCapabilityMap(std::initializer_list<FormatFlags> features) { | |
| for (const auto& [format, featureList] : features) { | |
| TextureFormat tf = DawnFormatToTextureFormat(format); | |
| if (tf == TextureFormat::kUnsupported) { continue; } // Skip if Graphite doesn't use it | |
| int index = static_cast<int>(tf); | |
| if (fFormatFeatures[index].empty()) { | |
| fFormatFeatures[index] = featureList; | |
| } else { | |
| // Allow concatenation, so long as there aren't duplicate features | |
| #if defined(SK_DEBUG) | |
| for (const ExtensionLimitedFlags& priorExt : fFormatFeatures[index]) { | |
| for (const ExtensionLimitedFlags& newExt : featureList) { | |
| SkASSERT(priorExt.first != newExt.first); | |
| } | |
| } | |
| #endif | |
| fFormatFeatures[index].push_back_n(featureList.size(), featureList.data()); | |
| } | |
| } | |
| SkASSERT(fFormatFeatures[static_cast<int>(TextureFormat::kUnsupported)].empty()); | |
| } | |
| // We only care about wgpu::TextureFormats that map back to a TextureFormat. | |
| std::array<ExtensionFlagList, kTextureFormatCount> fFormatFeatures; | |
| }; | |
| const DawnFormatCapabilityMap& DawnFormatCapabilityMap::Get() { | |
| using enum DawnFormatFlag; | |
| using enum wgpu::TextureFormat; | |
| // Alias some commonly used feature names | |
| using F = wgpu::FeatureName; | |
| static constexpr std::optional<F> Compat = std::nullopt; // The baseline | |
| static constexpr F Core = F::CoreFeaturesAndLimits; | |
| static constexpr F Tier1 = F::TextureFormatsTier1; | |
| static constexpr F Tier2 = F::TextureFormatsTier2; | |
| // To read this table with the HTML table in the links below, each format corresponds to a row | |
| // in the original document. For a format, the first extension listed is the "Required feature" | |
| // column, where blank is `Compat`. Any additional conditional capabilities in the row are | |
| // listed below the primary extension, followed by >> to indicate the capabilities they add. | |
| // | |
| // As noted in DawnFormatFlag, we convert the `GPUTextureSampleType` column to `Filter` if it | |
| // includes "float". The other sample types ("unfilterable-float", "uint", and "sint") are | |
| // implicitly defined by the data type of the format, having "filterable-float" capabilities is | |
| // the bit we care about. | |
| static const DawnFormatCapabilityMap kSingleton{{ | |
| // Plain color formats: https://gpuweb.github.io/gpuweb/#plain-color-formats | |
| // 8 bits per component | |
| {R8Unorm, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {R8Snorm, {Compat >> Filter, | |
| Tier1 >> Render | Blend | MSAA | Resolve | WriteOnly | ReadOnly}}, | |
| {R8Uint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {R8Sint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {RG8Unorm, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RG8Snorm, {Compat >> Filter, | |
| Tier1 >> Render | Blend | MSAA | Resolve | WriteOnly | ReadOnly}}, | |
| {RG8Uint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RG8Sint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RGBA8Unorm, {Compat >> Filter | Render | Blend | MSAA | Resolve | WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA8UnormSrgb, {Compat >> Filter | Render | Blend | MSAA | Resolve}}, | |
| {RGBA8Snorm, {Compat >> Filter | WriteOnly | ReadOnly, | |
| Tier1 >> Render | Blend | MSAA | Resolve}}, | |
| {RGBA8Uint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Core >> MSAA, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA8Sint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Core >> MSAA, | |
| Tier2 >> ReadWrite}}, | |
| {BGRA8Unorm, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| F::BGRA8UnormStorage >> WriteOnly}}, | |
| {BGRA8UnormSrgb, {Core >> Filter | Render | Blend | MSAA | Resolve}}, | |
| // 16 bits per component | |
| {R16Unorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {R16Snorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {R16Uint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {R16Sint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {R16Float, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {RG16Unorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {RG16Snorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {RG16Uint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RG16Sint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RG16Float, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RGBA16Unorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {RGBA16Snorm, {Tier1 >> Render | Blend | MSAA | WriteOnly | ReadOnly}}, | |
| {RGBA16Uint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Core >> MSAA, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA16Sint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Core >> MSAA, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA16Float, {Compat >> Filter | Render | Blend | WriteOnly | ReadOnly, | |
| Core >> MSAA | Resolve, | |
| Tier2 >> ReadWrite}}, | |
| // 32 bits per component | |
| {R32Uint, {Compat >> Render | WriteOnly | ReadOnly | ReadWrite}}, | |
| {R32Sint, {Compat >> Render | WriteOnly | ReadOnly | ReadWrite}}, | |
| {R32Float, {Compat >> Render | WriteOnly | ReadOnly | ReadWrite, | |
| Core >> MSAA, | |
| F::Float32Filterable >> Filter, | |
| F::Float32Blendable >> Blend}}, | |
| {RG32Uint, {Compat >> Render, | |
| Core >> WriteOnly | ReadOnly}}, | |
| {RG32Sint, {Compat >> Render, | |
| Core >> WriteOnly | ReadOnly}}, | |
| {RG32Float, {Compat >> Render, | |
| Core >> WriteOnly | ReadOnly, | |
| F::Float32Filterable >> Filter, | |
| F::Float32Blendable >> Blend}}, | |
| {RGBA32Uint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA32Sint, {Compat >> Render | WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite}}, | |
| {RGBA32Float, {Compat >> Render| WriteOnly | ReadOnly, | |
| Tier2 >> ReadWrite, | |
| F::Float32Filterable >> Filter, | |
| F::Float32Blendable >> Blend}}, | |
| // mixed component width, 32 bits per texel | |
| {RGB10A2Uint, {Compat >> Render, | |
| Core >> MSAA, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RGB10A2Unorm, {Compat >> Filter | Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| {RG11B10Ufloat, {Compat >> Filter, | |
| F::RG11B10UfloatRenderable >> Render | Blend | MSAA | Resolve, | |
| Tier1 >> WriteOnly | ReadOnly}}, | |
| // Depth-stencil formats: https://gpuweb.github.io/gpuweb/#depth-formats | |
| {Stencil8, {Compat >> Render | MSAA}}, | |
| {Depth16Unorm, {Compat >> Render | MSAA}}, | |
| {Depth24Plus, {Compat >> Render | MSAA}}, | |
| {Depth24PlusStencil8, {Compat >> Render | MSAA}}, | |
| {Depth32Float, {Compat >> Render | MSAA}}, | |
| {Depth32FloatStencil8, {Compat >> Render | MSAA}}, | |
| // Packed formats: https://gpuweb.github.io/gpuweb/#packed-formats | |
| // (Only including compressed formats for BC1 and ETC2 for brevity, Skia doesn't use ASTC) | |
| {RGB9E5Ufloat, {Compat >> Filter}}, | |
| {BC1RGBAUnorm, {F::TextureCompressionBC >> Filter}}, | |
| {BC1RGBAUnormSrgb, {F::TextureCompressionBC >> Filter}}, | |
| {ETC2RGB8Unorm, {F::TextureCompressionETC2 >> Filter}}, | |
| {ETC2RGB8UnormSrgb, {F::TextureCompressionETC2 >> Filter}}, | |
| #if !defined(__EMSCRIPTEN__) | |
| // Dawn-native only (either format OR feature), these are not included in the format tables. | |
| // NOTE: Unorm16TextureFormats is not an extension of TextureFormatsTier1 and is deprecated. | |
| // There will be a new Unorm16Filterable extension that augments the Tier1 capabilities. | |
| {R16Unorm, {F::Unorm16TextureFormats >> Filter | Render | Blend | MSAA | Resolve}}, | |
| {RG16Unorm, {F::Unorm16TextureFormats >> Filter | Render | Blend | MSAA | Resolve}}, | |
| {RGBA16Unorm, {F::Unorm16TextureFormats >> Filter | Render | Blend | MSAA | Resolve}}, | |
| // The multiplanar formats have no format capabilities on their own, they produce texture | |
| // views for each plane that have more capabilities. | |
| {R8BG8Biplanar420Unorm, {Core >> None}}, | |
| {R10X6BG10X6Biplanar420Unorm, {Core >> None}}, | |
| {R8BG8Biplanar422Unorm, {Core >> None}}, | |
| {R10X6BG10X6Biplanar422Unorm, {Core >> None}}, | |
| {R8BG8Biplanar444Unorm, {Core >> None}}, | |
| {R10X6BG10X6Biplanar444Unorm, {Core >> None}}, | |
| // This corresponds to Vulkan's external formats, which are always filterable. | |
| {OpaqueYCbCrAndroid, {F::YCbCrVulkanSamplers >> Filter}}, | |
| #endif | |
| }}; | |
| return kSingleton; | |
| } | |
| } // anonymous namespace | |
| SkEnumBitMask<DawnFormatFlag> DawnTextureFormatSupport(wgpu::Device device, | |
| wgpu::TextureFormat format) { | |
| return DawnFormatCapabilityMap::Get().getFormatFlags(device, format); | |
| } | |
| // *** Ground truth bidirectional map between TextureFormat and wgpu::TextureFormat *** | |
| #define DAWN_FORMAT_MAPPING(M) \ | |
| M(TextureFormat::kR8, wgpu::TextureFormat::R8Unorm) \ | |
| M(TextureFormat::kR16, wgpu::TextureFormat::R16Unorm) \ | |
| M(TextureFormat::kR16F, wgpu::TextureFormat::R16Float) \ | |
| M(TextureFormat::kR32F, wgpu::TextureFormat::R32Float) \ | |
| /*TextureFormat::kA8, unsupported */ \ | |
| M(TextureFormat::kRG8, wgpu::TextureFormat::RG8Unorm) \ | |
| M(TextureFormat::kRG16, wgpu::TextureFormat::RG16Unorm) \ | |
| M(TextureFormat::kRG16F, wgpu::TextureFormat::RG16Float) \ | |
| M(TextureFormat::kRG32F, wgpu::TextureFormat::RG32Float) \ | |
| /*TextureFormat::kRGB8, unsupported */ \ | |
| /*TextureFormat::kBGR8, unsupported */ \ | |
| /*TextureFormat::kB5_G6_R5, unsupported */ \ | |
| /*TextureFormat::kR5_G6_B5, unsupported */ \ | |
| /*TextureFormat::kRGB16, unsupported */ \ | |
| /*TextureFormat::kRGB16F, unsupported */ \ | |
| /*TextureFormat::kRGB32F, unsupported */ \ | |
| /*TextureFormat::kRGB8_sRGB, unsupported */ \ | |
| /*TextureFormat::kBGR10_XR, unsupported */ \ | |
| M(TextureFormat::kRGBA8, wgpu::TextureFormat::RGBA8Unorm) \ | |
| M(TextureFormat::kRGBA16, wgpu::TextureFormat::RGBA16Unorm) \ | |
| M(TextureFormat::kRGBA16F, wgpu::TextureFormat::RGBA16Float) \ | |
| M(TextureFormat::kRGBA32F, wgpu::TextureFormat::RGBA32Float) \ | |
| M(TextureFormat::kRGB10_A2, wgpu::TextureFormat::RGB10A2Unorm) \ | |
| /*TextureFormat::kRGBA10x6, unsupported */ \ | |
| M(TextureFormat::kRGBA8_sRGB, wgpu::TextureFormat::RGBA8UnormSrgb) \ | |
| M(TextureFormat::kBGRA8, wgpu::TextureFormat::BGRA8Unorm) \ | |
| /*TextureFormat::kBGR10_A2, unsupported */ \ | |
| M(TextureFormat::kBGRA8_sRGB, wgpu::TextureFormat::BGRA8UnormSrgb) \ | |
| /*TextureFormat::kABGR4, unsupported */ \ | |
| /*TextureFormat::kARGB4, unsupported */ \ | |
| /*TextureFormat::kBGRA10x6_XR, unsupported */ \ | |
| M(TextureFormat::kRGB8_ETC2, wgpu::TextureFormat::ETC2RGB8Unorm) \ | |
| M(TextureFormat::kRGB8_ETC2_sRGB, wgpu::TextureFormat::ETC2RGB8UnormSrgb) \ | |
| /*TextureFormat::kRGB_BC1 unsupported */ \ | |
| M(TextureFormat::kRGBA8_BC1, wgpu::TextureFormat::BC1RGBAUnorm) \ | |
| M(TextureFormat::kRGBA8_BC1_sRGB, wgpu::TextureFormat::BC1RGBAUnormSrgb) \ | |
| /*TextureFormat::kYUV8_P2_420, Native-only */ \ | |
| /*TextureFormat::kYUV8_P3_420, unsupported */ \ | |
| /*TextureFormat::kYUV10x6_P2_420, Native-only */ \ | |
| /*TextureFormat::kYUV8_P2_422, Native-only */ \ | |
| /*TextureFormat::kYUV8_P3_422, unsupported */ \ | |
| /*TextureFormat::kYUV10x6_P2_422, Native-only */ \ | |
| /*TextureFormat::kYUV8_P2_444, Native-only */ \ | |
| /*TextureFormat::kYUV8_P3_444, unsupported */ \ | |
| /*TextureFormat::kYUV10x6_P2_444, Native-only */ \ | |
| /*TextureFormat::kExternal, Native-only */ \ | |
| M(TextureFormat::kS8, wgpu::TextureFormat::Stencil8) \ | |
| M(TextureFormat::kD16, wgpu::TextureFormat::Depth16Unorm) \ | |
| M(TextureFormat::kD32F, wgpu::TextureFormat::Depth32Float) \ | |
| M(TextureFormat::kD24_S8, wgpu::TextureFormat::Depth24PlusStencil8) \ | |
| M(TextureFormat::kD32F_S8, wgpu::TextureFormat::Depth32FloatStencil8) \ | |
| #if !defined(__EMSCRIPTEN__) | |
| #define DAWN_FORMAT_MAPPING_NATIVE_ONLY(M) \ | |
| M(TextureFormat::kYUV8_P2_420, wgpu::TextureFormat::R8BG8Biplanar420Unorm) \ | |
| M(TextureFormat::kYUV10x6_P2_420, wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm) \ | |
| M(TextureFormat::kYUV8_P2_422, wgpu::TextureFormat::R8BG8Biplanar422Unorm) \ | |
| M(TextureFormat::kYUV10x6_P2_422, wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm) \ | |
| M(TextureFormat::kYUV8_P2_444, wgpu::TextureFormat::R8BG8Biplanar444Unorm) \ | |
| M(TextureFormat::kYUV10x6_P2_444, wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm) \ | |
| M(TextureFormat::kExternal, wgpu::TextureFormat::OpaqueYCbCrAndroid) | |
| #else | |
| #define DAWN_FORMAT_MAPPING_NATIVE_ONLY(M) | |
| #endif | |
| TextureFormat DawnFormatToTextureFormat(wgpu::TextureFormat format) { | |
| #define M(TF, WGPU) case WGPU: return TF; | |
| switch(format) { | |
| DAWN_FORMAT_MAPPING(M) | |
| DAWN_FORMAT_MAPPING_NATIVE_ONLY(M) | |
| default: return TextureFormat::kUnsupported; | |
| } | |
| #undef M | |
| } | |
| wgpu::TextureFormat TextureFormatToDawnFormat(TextureFormat format) { | |
| #define M(TF, WGPU) case TF: return WGPU; | |
| switch(format) { | |
| DAWN_FORMAT_MAPPING(M) | |
| DAWN_FORMAT_MAPPING_NATIVE_ONLY(M) | |
| default: return wgpu::TextureFormat::Undefined; | |
| } | |
| #undef M | |
| } | |
| static bool check_shader_module([[maybe_unused]] const DawnSharedContext* sharedContext, | |
| wgpu::ShaderModule* module, | |
| const char* shaderText, | |
| ShaderErrorHandler* errorHandler) { | |
| // Prior to emsdk 3.1.51 wgpu::ShaderModule::GetCompilationInfo is unimplemented. | |
| #if defined(__EMSCRIPTEN__) && \ | |
| ((__EMSCRIPTEN_major__ < 3 || \ | |
| (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ < 1) || \ | |
| (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ < 51))) | |
| return true; | |
| #else | |
| struct Handler { | |
| static void Fn(WGPUCompilationInfoRequestStatus status, | |
| const WGPUCompilationInfo* info, | |
| void* userdata) { | |
| Handler* self = reinterpret_cast<Handler*>(userdata); | |
| SkASSERT(status == WGPUCompilationInfoRequestStatus_Success); | |
| // Walk the message list and check for hard errors. | |
| self->fSuccess = true; | |
| for (size_t index = 0; index < info->messageCount; ++index) { | |
| const WGPUCompilationMessage& entry = SK_UNSAFE_TODO(info->messages[index]); | |
| if (entry.type == WGPUCompilationMessageType_Error) { | |
| self->fSuccess = false; | |
| break; | |
| } | |
| } | |
| // If we found a hard error, report the compilation messages to the error handler. | |
| if (!self->fSuccess) { | |
| std::string errors; | |
| for (size_t index = 0; index < info->messageCount; ++index) { | |
| const WGPUCompilationMessage& entry = SK_UNSAFE_TODO(info->messages[index]); | |
| std::string messageString(entry.message.data, entry.message.length); | |
| errors += "line " + std::to_string(entry.lineNum) + ':' + | |
| std::to_string(entry.linePos) + ' ' + messageString + '\n'; | |
| } | |
| self->fErrorHandler->compileError( | |
| self->fShaderText, errors.c_str(), /*shaderWasCached=*/false); | |
| } | |
| } | |
| const char* fShaderText; | |
| ShaderErrorHandler* fErrorHandler; | |
| bool fSuccess = false; | |
| }; | |
| Handler handler; | |
| handler.fShaderText = shaderText; | |
| handler.fErrorHandler = errorHandler; | |
| #if defined(__EMSCRIPTEN__) | |
| // Deprecated function. | |
| module->GetCompilationInfo(&Handler::Fn, &handler); | |
| #else | |
| // New API. | |
| wgpu::FutureWaitInfo waitInfo{}; | |
| waitInfo.future = module->GetCompilationInfo( | |
| wgpu::CallbackMode::WaitAnyOnly, | |
| [handlerPtr = &handler](wgpu::CompilationInfoRequestStatus status, | |
| const wgpu::CompilationInfo* info) { | |
| Handler::Fn(static_cast<WGPUCompilationInfoRequestStatus>(status), | |
| reinterpret_cast<const WGPUCompilationInfo*>(info), | |
| handlerPtr); | |
| }); | |
| const auto& instance = static_cast<const DawnSharedContext*>(sharedContext)->instance(); | |
| [[maybe_unused]] auto status = | |
| instance.WaitAny(1, &waitInfo, /*timeoutNS=*/std::numeric_limits<uint64_t>::max()); | |
| SkASSERT(status == wgpu::WaitStatus::Success); | |
| #endif // defined(__EMSCRIPTEN__) | |
| return handler.fSuccess; | |
| #endif | |
| } | |
| bool DawnCompileWGSLShaderModule(const DawnSharedContext* sharedContext, | |
| const char* label, | |
| const SkSL::NativeShader& wgsl, | |
| wgpu::ShaderModule* module, | |
| ShaderErrorHandler* errorHandler) { | |
| #if defined(__EMSCRIPTEN__) | |
| wgpu::ShaderModuleWGSLDescriptor wgslDesc; | |
| #else | |
| wgpu::ShaderSourceWGSL wgslDesc; | |
| #endif | |
| wgslDesc.code = wgsl.fText.c_str(); | |
| wgpu::ShaderModuleDescriptor desc; | |
| desc.nextInChain = &wgslDesc; | |
| if (sharedContext->caps()->setBackendLabels()) { | |
| desc.label = label; | |
| } | |
| *module = sharedContext->device().CreateShaderModule(&desc); | |
| return check_shader_module(sharedContext, module, wgsl.fText.c_str(), errorHandler); | |
| } | |
| #if !defined(__EMSCRIPTEN__) | |
| namespace { | |
| static constexpr int kUsesExternalFormatBits = 1; | |
| static constexpr int kYcbcrModelBits = 3; | |
| static constexpr int kYcbcrRangeBits = 1; | |
| static constexpr int kXChromaOffsetBits = 1; | |
| static constexpr int kYChromaOffsetBits = 1; | |
| // wgpu::FilterMode contains Undefined/Nearest/Linear entries (Linear is 2). | |
| static constexpr int kChromaFilterBits = 2; | |
| static constexpr int kForceExplicitReconBits = 1; | |
| static constexpr int kComponentBits = 3; | |
| static constexpr int kUsesExternalFormatShift = 0; | |
| static constexpr int kYcbcrModelShift = kUsesExternalFormatShift + kUsesExternalFormatBits; | |
| static constexpr int kYcbcrRangeShift = kYcbcrModelShift + kYcbcrModelBits; | |
| static constexpr int kXChromaOffsetShift = kYcbcrRangeShift + kYcbcrRangeBits; | |
| static constexpr int kYChromaOffsetShift = kXChromaOffsetShift + kXChromaOffsetBits; | |
| static constexpr int kChromaFilterShift = kYChromaOffsetShift + kYChromaOffsetBits; | |
| static constexpr int kForceExplicitReconShift = kChromaFilterShift + kChromaFilterBits; | |
| static constexpr int kComponentRShift = kForceExplicitReconShift + kForceExplicitReconBits; | |
| static constexpr int kComponentGShift = kComponentRShift + kComponentBits; | |
| static constexpr int kComponentBShift = kComponentGShift + kComponentBits; | |
| static constexpr int kComponentAShift = kComponentBShift + kComponentBits; | |
| static constexpr uint32_t kUseExternalFormatMask = | |
| ((1 << kUsesExternalFormatBits) - 1) << kUsesExternalFormatShift; | |
| static constexpr uint32_t kYcbcrModelMask = | |
| ((1 << kYcbcrModelBits) - 1) << kYcbcrModelShift; | |
| static constexpr uint32_t kYcbcrRangeMask = | |
| ((1 << kYcbcrRangeBits) - 1) << kYcbcrRangeShift; | |
| static constexpr uint32_t kXChromaOffsetMask = | |
| ((1 << kXChromaOffsetBits) - 1) << kXChromaOffsetShift; | |
| static constexpr uint32_t kYChromaOffsetMask = | |
| ((1 << kYChromaOffsetBits) - 1) << kYChromaOffsetShift; | |
| static constexpr uint32_t kChromaFilterMask = | |
| ((1 << kChromaFilterBits) - 1) << kChromaFilterShift; | |
| static constexpr uint32_t kForceExplicitReconMask = | |
| ((1 << kForceExplicitReconBits) - 1) << kForceExplicitReconShift; | |
| static constexpr uint32_t kComponentRMask = ((1 << kComponentBits) - 1) << kComponentRShift; | |
| static constexpr uint32_t kComponentBMask = ((1 << kComponentBits) - 1) << kComponentBShift; | |
| static constexpr uint32_t kComponentGMask = ((1 << kComponentBits) - 1) << kComponentGShift; | |
| static constexpr uint32_t kComponentAMask = ((1 << kComponentBits) - 1) << kComponentAShift; | |
| } // anonymous namespace | |
| bool DawnDescriptorsAreEquivalent(const wgpu::YCbCrVkDescriptor& desc1, | |
| const wgpu::YCbCrVkDescriptor& desc2) { | |
| return desc1.vkFormat == desc2.vkFormat && | |
| desc1.vkYCbCrModel == desc2.vkYCbCrModel && | |
| desc1.vkYCbCrRange == desc2.vkYCbCrRange && | |
| desc1.vkComponentSwizzleRed == desc2.vkComponentSwizzleRed && | |
| desc1.vkComponentSwizzleGreen == desc2.vkComponentSwizzleGreen && | |
| desc1.vkComponentSwizzleBlue == desc2.vkComponentSwizzleBlue && | |
| desc1.vkComponentSwizzleAlpha == desc2.vkComponentSwizzleAlpha && | |
| desc1.vkXChromaOffset == desc2.vkXChromaOffset && | |
| desc1.vkYChromaOffset == desc2.vkYChromaOffset && | |
| desc1.vkChromaFilter == desc2.vkChromaFilter && | |
| desc1.forceExplicitReconstruction == desc2.forceExplicitReconstruction && | |
| desc1.externalFormat == desc2.externalFormat; | |
| } | |
| bool DawnDescriptorIsValid(const wgpu::YCbCrVkDescriptor& desc) { | |
| static const wgpu::YCbCrVkDescriptor kDefaultYcbcrDescriptor = {}; | |
| return !DawnDescriptorsAreEquivalent(desc, kDefaultYcbcrDescriptor); | |
| } | |
| bool DawnDescriptorUsesExternalFormat(const wgpu::YCbCrVkDescriptor& desc) { | |
| SkASSERT(desc.externalFormat != 0 || desc.vkFormat != 0); | |
| return desc.externalFormat != 0; | |
| } | |
| ImmutableSamplerInfo DawnDescriptorToImmutableSamplerInfo(const wgpu::YCbCrVkDescriptor& desc) { | |
| static_assert(kComponentAShift + kComponentBits <= 32); | |
| SkASSERT(desc.vkYCbCrModel < (1u << kYcbcrModelBits )); | |
| SkASSERT(desc.vkYCbCrRange < (1u << kYcbcrRangeBits )); | |
| SkASSERT(desc.vkXChromaOffset < (1u << kXChromaOffsetBits )); | |
| SkASSERT(desc.vkYChromaOffset < (1u << kYChromaOffsetBits )); | |
| SkASSERT(static_cast<uint32_t>(desc.vkChromaFilter) < (1u << kChromaFilterBits )); | |
| SkASSERT(desc.vkComponentSwizzleRed < (1u << kComponentBits )); | |
| SkASSERT(desc.vkComponentSwizzleGreen < (1u << kComponentBits )); | |
| SkASSERT(desc.vkComponentSwizzleBlue < (1u << kComponentBits )); | |
| SkASSERT(desc.vkComponentSwizzleAlpha < (1u << kComponentBits )); | |
| SkASSERT(static_cast<uint32_t>(desc.forceExplicitReconstruction) | |
| < (1u << kForceExplicitReconBits)); | |
| const bool usesExternalFormat = DawnDescriptorUsesExternalFormat(desc); | |
| ImmutableSamplerInfo info; | |
| info.fNonFormatYcbcrConversionInfo = | |
| (((uint32_t)(usesExternalFormat ) << kUsesExternalFormatShift) | | |
| ((uint32_t)(desc.vkYCbCrModel ) << kYcbcrModelShift ) | | |
| ((uint32_t)(desc.vkYCbCrRange ) << kYcbcrRangeShift ) | | |
| ((uint32_t)(desc.vkXChromaOffset ) << kXChromaOffsetShift ) | | |
| ((uint32_t)(desc.vkYChromaOffset ) << kYChromaOffsetShift ) | | |
| ((uint32_t)(desc.vkChromaFilter ) << kChromaFilterShift ) | | |
| ((uint32_t)(desc.forceExplicitReconstruction ) << kForceExplicitReconShift) | | |
| ((uint32_t)(desc.vkComponentSwizzleRed ) << kComponentRShift ) | | |
| ((uint32_t)(desc.vkComponentSwizzleGreen ) << kComponentGShift ) | | |
| ((uint32_t)(desc.vkComponentSwizzleBlue ) << kComponentBShift ) | | |
| ((uint32_t)(desc.vkComponentSwizzleAlpha ) << kComponentAShift )); | |
| info.fFormat = usesExternalFormat ? desc.externalFormat : desc.vkFormat; | |
| return info; | |
| } | |
| wgpu::YCbCrVkDescriptor DawnDescriptorFromImmutableSamplerInfo(ImmutableSamplerInfo info) { | |
| const uint32_t nonFormatInfo = info.fNonFormatYcbcrConversionInfo; | |
| wgpu::YCbCrVkDescriptor desc; | |
| const bool usesExternalFormat = | |
| (nonFormatInfo >> kUsesExternalFormatShift) & kUseExternalFormatMask; | |
| if (usesExternalFormat) { | |
| desc.vkFormat = 0; | |
| desc.externalFormat = info.fFormat; | |
| } else { | |
| desc.vkFormat = (uint32_t) info.fFormat; | |
| desc.externalFormat = 0; | |
| } | |
| desc.vkYCbCrModel = (nonFormatInfo & kYcbcrModelMask) >> kYcbcrModelShift; | |
| desc.vkYCbCrRange = (nonFormatInfo & kYcbcrRangeMask) >> kYcbcrRangeShift; | |
| desc.vkComponentSwizzleRed = (nonFormatInfo & kComponentRMask) >> kComponentRShift; | |
| desc.vkComponentSwizzleGreen = (nonFormatInfo & kComponentGMask) >> kComponentGShift; | |
| desc.vkComponentSwizzleBlue = (nonFormatInfo & kComponentBMask) >> kComponentBShift; | |
| desc.vkComponentSwizzleAlpha = (nonFormatInfo & kComponentAMask) >> kComponentAShift; | |
| desc.vkXChromaOffset = (nonFormatInfo & kXChromaOffsetMask) >> kXChromaOffsetShift; | |
| desc.vkYChromaOffset = (nonFormatInfo & kYChromaOffsetMask) >> kYChromaOffsetShift; | |
| desc.vkChromaFilter = static_cast<wgpu::FilterMode>( | |
| (nonFormatInfo & kChromaFilterMask) >> kChromaFilterShift); | |
| desc.forceExplicitReconstruction = | |
| (nonFormatInfo & kForceExplicitReconMask) >> kForceExplicitReconShift; | |
| return desc; | |
| } | |
| #endif // !defined(__EMSCRIPTEN__) | |
| } // namespace skgpu::graphite |