| /* | |
| * Copyright 2023 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/DawnErrorChecker.h" | |
| #include "include/private/SkAssert.h" | |
| #include "include/private/SkAttributes.h" | |
| #include "include/private/SkLog.h" | |
| #include "src/gpu/graphite/dawn/DawnAsyncWait.h" | |
| #include "src/gpu/graphite/dawn/DawnSharedContext.h" | |
| namespace skgpu::graphite { | |
| namespace { | |
| constexpr const char* kErrorScopeNames[] = {"validation", "out-of-memory", "internal"}; | |
| constexpr DawnErrorType kErrorScopeTypes[] = { | |
| DawnErrorType::kValidation, DawnErrorType::kOutOfMemory, DawnErrorType::kInternal}; | |
| static_assert(std::size(kErrorScopeNames) == std::size(kErrorScopeTypes)); | |
| constexpr int kScopeCount = std::size(kErrorScopeTypes); | |
| } // namespace | |
| DawnErrorChecker::DawnErrorChecker(const DawnSharedContext* sharedContext) | |
| : fArmed(true), fSharedContext(sharedContext) { | |
| fSharedContext->device().PushErrorScope(wgpu::ErrorFilter::Validation); | |
| fSharedContext->device().PushErrorScope(wgpu::ErrorFilter::OutOfMemory); | |
| fSharedContext->device().PushErrorScope(wgpu::ErrorFilter::Internal); | |
| } | |
| DawnErrorChecker::~DawnErrorChecker() { | |
| [[maybe_unused]] auto err = this->popErrorScopes(); | |
| SkASSERT(!fArmed); | |
| SkASSERT(err == DawnErrorType::kNoError); | |
| } | |
| SkEnumBitMask<DawnErrorType> DawnErrorChecker::popErrorScopes() { | |
| if (!fArmed) { | |
| return DawnErrorType::kNoError; | |
| } | |
| #if defined(__EMSCRIPTEN__) | |
| struct ErrorState { | |
| SkEnumBitMask<DawnErrorType> fError; | |
| int fScopeIdx; | |
| DawnAsyncWait fWait; | |
| ErrorState(const DawnSharedContext* sharedContext) | |
| : fError(DawnErrorType::kNoError) | |
| , fScopeIdx(kScopeCount - 1) | |
| , fWait(sharedContext) {} | |
| } errorState(fSharedContext); | |
| wgpu::ErrorCallback errorCallback = [](WGPUErrorType status, const char* msg, void* userData) { | |
| ErrorState* errorState = static_cast<ErrorState*>(userData); | |
| if (status != WGPUErrorType_NoError) { | |
| SkASSERT(errorState->fScopeIdx >= 0); | |
| const char* errorScopeName = kErrorScopeNames[errorState->fScopeIdx]; | |
| SKIA_LOG_E("Failed in error scope (%s): %s", errorScopeName, msg); | |
| errorState->fError |= kErrorScopeTypes[errorState->fScopeIdx]; | |
| } | |
| errorState->fScopeIdx--; | |
| errorState->fWait.signal(); | |
| }; | |
| // Pop all three error scopes: | |
| // Internal | |
| fSharedContext->device().PopErrorScope(errorCallback, &errorState); | |
| errorState.fWait.busyWait(); | |
| errorState.fWait.reset(); | |
| // OutOfMemory | |
| fSharedContext->device().PopErrorScope(errorCallback, &errorState); | |
| errorState.fWait.busyWait(); | |
| errorState.fWait.reset(); | |
| // Validation | |
| fSharedContext->device().PopErrorScope(errorCallback, &errorState); | |
| errorState.fWait.busyWait(); | |
| #else | |
| struct ErrorState { | |
| SkEnumBitMask<DawnErrorType> fError = DawnErrorType::kNoError; | |
| int fScopeIdx = kScopeCount - 1; | |
| } errorState = {}; | |
| auto errorCallback = [](wgpu::PopErrorScopeStatus status, | |
| wgpu::ErrorType type, | |
| wgpu::StringView msg, | |
| ErrorState* errorState) { | |
| SkASSERT(status == wgpu::PopErrorScopeStatus::Success); | |
| if (type != wgpu::ErrorType::NoError) { | |
| SkASSERT(errorState->fScopeIdx >= 0); | |
| const char* errorScopeName = SK_UNSAFE_TODO(kErrorScopeNames[errorState->fScopeIdx]); | |
| SKIA_LOG_E("Failed in error scope (%s): %.*s", | |
| errorScopeName, | |
| static_cast<int>(msg.length), | |
| msg.data); | |
| errorState->fError |= SK_UNSAFE_TODO(kErrorScopeTypes[errorState->fScopeIdx]); | |
| } | |
| errorState->fScopeIdx--; | |
| }; | |
| // Pop all three error scopes: | |
| auto internalFuture = fSharedContext->device().PopErrorScope( | |
| wgpu::CallbackMode::WaitAnyOnly, errorCallback, &errorState); | |
| auto oomFuture = fSharedContext->device().PopErrorScope( | |
| wgpu::CallbackMode::WaitAnyOnly, errorCallback, &errorState); | |
| auto validationFuture = fSharedContext->device().PopErrorScope( | |
| wgpu::CallbackMode::WaitAnyOnly, errorCallback, &errorState); | |
| wgpu::WaitStatus status = wgpu::WaitStatus::Success; | |
| wgpu::Instance instance = fSharedContext->instance(); | |
| status = instance.WaitAny(internalFuture, /*timeout=*/std::numeric_limits<uint64_t>::max()); | |
| if (status != wgpu::WaitStatus::Success) { | |
| SKIA_LOG_E("Failed waiting for 'internal' error scope to pop."); | |
| } | |
| SkASSERT(status == wgpu::WaitStatus::Success); | |
| status = instance.WaitAny(oomFuture, /*timeout=*/std::numeric_limits<uint64_t>::max()); | |
| if (status != wgpu::WaitStatus::Success) { | |
| SKIA_LOG_E("Failed waiting for 'out-of-memory' error scope to pop."); | |
| } | |
| SkASSERT(status == wgpu::WaitStatus::Success); | |
| status = instance.WaitAny(validationFuture, /*timeout=*/std::numeric_limits<uint64_t>::max()); | |
| if (status != wgpu::WaitStatus::Success) { | |
| SKIA_LOG_E("Failed waiting for 'validation' error scope to pop."); | |
| } | |
| SkASSERT(status == wgpu::WaitStatus::Success); | |
| #endif // defined(__EMSCRIPTEN__) | |
| fArmed = false; | |
| return errorState.fError; | |
| } | |
| } // namespace skgpu::graphite |