From d44e7b3b0a291b2501451ab9333823a21c90aca7 Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Thu, 28 Apr 2022 01:06:53 +0000 Subject: [PATCH] Surface D3D12 validation messages in WebGPU errors This change ensures that when errors are raised from WebGPU, including lost context errors, they can include any associated D3D12 validation layer messages in the message text if backend validation is enabled. This will allow these messages to be surfaced in places like when running CTS tests on browser build infrastructure. Also makes a minor adjustment to how HRESULT codes are reported so that they're easier to read. Bug: dawn:1396 Change-Id: Ib5c039157c57e6926bc82941a68be03e33e9084c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/88044 Commit-Queue: Brandon Jones Kokoro: Kokoro Reviewed-by: Austin Eng --- src/dawn/native/Device.cpp | 1 + src/dawn/native/Device.h | 3 + src/dawn/native/ErrorData.cpp | 17 +++++- src/dawn/native/ErrorData.h | 3 + src/dawn/native/d3d12/D3D12Error.cpp | 49 +++++++++++++--- src/dawn/native/d3d12/DeviceD3D12.cpp | 81 +++++++++++++++++++-------- src/dawn/native/d3d12/DeviceD3D12.h | 1 + 7 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp index 5bccc64480..5f920150ee 100644 --- a/src/dawn/native/Device.cpp +++ b/src/dawn/native/Device.cpp @@ -497,6 +497,7 @@ namespace dawn::native { void DeviceBase::ConsumeError(std::unique_ptr error) { ASSERT(error != nullptr); + AppendDebugLayerMessages(error.get()); HandleError(error->GetType(), error->GetFormattedMessage().c_str()); } diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h index 7a109fd2c1..3f5346102d 100644 --- a/src/dawn/native/Device.h +++ b/src/dawn/native/Device.h @@ -380,6 +380,9 @@ namespace dawn::native { void APISetLabel(const char* label); void APIDestroy(); + virtual void AppendDebugLayerMessages(ErrorData* error) { + } + protected: // Constructor used only for mocking and testing. DeviceBase(); diff --git a/src/dawn/native/ErrorData.cpp b/src/dawn/native/ErrorData.cpp index 66f28dc3ea..b23640cce3 100644 --- a/src/dawn/native/ErrorData.cpp +++ b/src/dawn/native/ErrorData.cpp @@ -53,6 +53,10 @@ namespace dawn::native { mDebugGroups.push_back(std::move(label)); } + void ErrorData::AppendBackendMessage(std::string message) { + mBackendMessages.push_back(std::move(message)); + } + InternalErrorType ErrorData::GetType() const { return mType; } @@ -73,6 +77,10 @@ namespace dawn::native { return mDebugGroups; } + const std::vector& ErrorData::GetBackendMessages() const { + return mBackendMessages; + } + std::string ErrorData::GetFormattedMessage() const { std::ostringstream ss; ss << mMessage << "\n"; @@ -83,7 +91,7 @@ namespace dawn::native { } } - // For non-validation errors, or erros that lack a context include the + // For non-validation errors, or errors that lack a context include the // stack trace for debugging purposes. if (mContexts.empty() || mType != InternalErrorType::Validation) { for (const auto& callsite : mBacktrace) { @@ -99,6 +107,13 @@ namespace dawn::native { } } + if (!mBackendMessages.empty()) { + ss << "\nBackend messages:\n"; + for (auto message : mBackendMessages) { + ss << " * " << message << "\n"; + } + } + return ss.str(); } diff --git a/src/dawn/native/ErrorData.h b/src/dawn/native/ErrorData.h index e62decb51a..bc45012112 100644 --- a/src/dawn/native/ErrorData.h +++ b/src/dawn/native/ErrorData.h @@ -48,12 +48,14 @@ namespace dawn::native { void AppendBacktrace(const char* file, const char* function, int line); void AppendContext(std::string context); void AppendDebugGroup(std::string label); + void AppendBackendMessage(std::string message); InternalErrorType GetType() const; const std::string& GetMessage() const; const std::vector& GetBacktrace() const; const std::vector& GetContexts() const; const std::vector& GetDebugGroups() const; + const std::vector& GetBackendMessages() const; std::string GetFormattedMessage() const; @@ -63,6 +65,7 @@ namespace dawn::native { std::vector mBacktrace; std::vector mContexts; std::vector mDebugGroups; + std::vector mBackendMessages; }; } // namespace dawn::native diff --git a/src/dawn/native/d3d12/D3D12Error.cpp b/src/dawn/native/d3d12/D3D12Error.cpp index 23a95568d4..b26aa63b8d 100644 --- a/src/dawn/native/d3d12/D3D12Error.cpp +++ b/src/dawn/native/d3d12/D3D12Error.cpp @@ -19,19 +19,54 @@ #include namespace dawn::native::d3d12 { + const char* HRESULTAsString(HRESULT result) { + // There's a lot of possible HRESULTS, but these ones are the ones specifically listed as + // being returned from D3D12, in addition to fake codes used internally for testing. + // https://docs.microsoft.com/en-us/windows/win32/direct3d12/d3d12-graphics-reference-returnvalues + switch (result) { + case S_OK: + return "S_OK"; + case S_FALSE: + return "S_FALSE"; + + case E_FAIL: + return "E_FAIL"; + case E_INVALIDARG: + return "E_INVALIDARG"; + case E_OUTOFMEMORY: + return "E_OUTOFMEMORY"; + case E_NOTIMPL: + return "E_NOTIMPL"; + + case DXGI_ERROR_INVALID_CALL: + return "DXGI_ERROR_INVALID_CALL"; + case DXGI_ERROR_WAS_STILL_DRAWING: + return "DXGI_ERROR_WAS_STILL_DRAWING"; + + case D3D12_ERROR_ADAPTER_NOT_FOUND: + return "D3D12_ERROR_ADAPTER_NOT_FOUND"; + case D3D12_ERROR_DRIVER_VERSION_MISMATCH: + return "D3D12_ERROR_DRIVER_VERSION_MISMATCH"; + + case E_FAKE_ERROR_FOR_TESTING: + return "E_FAKE_ERROR_FOR_TESTING"; + case E_FAKE_OUTOFMEMORY_ERROR_FOR_TESTING: + return "E_FAKE_OUTOFMEMORY_ERROR_FOR_TESTING"; + + default: + return ""; + } + } + MaybeError CheckHRESULTImpl(HRESULT result, const char* context) { if (DAWN_LIKELY(SUCCEEDED(result))) { return {}; } std::ostringstream messageStream; - messageStream << context << " failed with "; - if (result == E_FAKE_ERROR_FOR_TESTING) { - messageStream << "E_FAKE_ERROR_FOR_TESTING"; - } else { - messageStream << "0x" << std::uppercase << std::setfill('0') << std::setw(8) << std::hex - << result; - } + messageStream << context << " failed with " << HRESULTAsString(result) << " (0x" + << std::uppercase << std::setfill('0') << std::setw(8) << std::hex << result + << ")"; if (result == DXGI_ERROR_DEVICE_REMOVED) { return DAWN_DEVICE_LOST_ERROR(messageStream.str()); diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp index f7c062d9c8..f8442869e4 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp @@ -630,6 +630,45 @@ namespace dawn::native::d3d12 { return {}; } + void AppendDebugLayerMessagesToError(ID3D12InfoQueue* infoQueue, + uint64_t totalErrors, + ErrorData* error) { + ASSERT(totalErrors > 0); + ASSERT(error != nullptr); + + uint64_t errorsToPrint = std::min(kMaxDebugMessagesToPrint, totalErrors); + for (uint64_t i = 0; i < errorsToPrint; ++i) { + std::ostringstream messageStream; + SIZE_T messageLength = 0; + HRESULT hr = infoQueue->GetMessage(i, nullptr, &messageLength); + if (FAILED(hr)) { + messageStream << " ID3D12InfoQueue::GetMessage failed with " << hr; + error->AppendBackendMessage(messageStream.str()); + continue; + } + + std::unique_ptr messageData(new uint8_t[messageLength]); + D3D12_MESSAGE* message = reinterpret_cast(messageData.get()); + hr = infoQueue->GetMessage(i, message, &messageLength); + if (FAILED(hr)) { + messageStream << " ID3D12InfoQueue::GetMessage failed with " << hr; + error->AppendBackendMessage(messageStream.str()); + continue; + } + + messageStream << message->pDescription << " (" << message->ID << ")"; + error->AppendBackendMessage(messageStream.str()); + } + if (errorsToPrint < totalErrors) { + std::ostringstream messages; + messages << (totalErrors - errorsToPrint) << " messages silenced"; + error->AppendBackendMessage(messages.str()); + } + + // We only print up to the first kMaxDebugMessagesToPrint errors + infoQueue->ClearStoredMessages(); + } + MaybeError Device::CheckDebugLayerAndGenerateErrors() { if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) { return {}; @@ -647,33 +686,29 @@ namespace dawn::native::d3d12 { return {}; } - std::ostringstream messages; - uint64_t errorsToPrint = std::min(kMaxDebugMessagesToPrint, totalErrors); - for (uint64_t i = 0; i < errorsToPrint; ++i) { - SIZE_T messageLength = 0; - HRESULT hr = infoQueue->GetMessage(i, nullptr, &messageLength); - if (FAILED(hr)) { - messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n'; - continue; - } + auto error = DAWN_INTERNAL_ERROR("The D3D12 debug layer reported uncaught errors."); - std::unique_ptr messageData(new uint8_t[messageLength]); - D3D12_MESSAGE* message = reinterpret_cast(messageData.get()); - hr = infoQueue->GetMessage(i, message, &messageLength); - if (FAILED(hr)) { - messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n'; - continue; - } + AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error.get()); - messages << message->pDescription << " (" << message->ID << ")\n"; + return error; + } + + void Device::AppendDebugLayerMessages(ErrorData* error) { + if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) { + return; } - if (errorsToPrint < totalErrors) { - messages << (totalErrors - errorsToPrint) << " messages silenced\n"; - } - // We only print up to the first kMaxDebugMessagesToPrint errors - infoQueue->ClearStoredMessages(); - return DAWN_INTERNAL_ERROR(messages.str()); + ComPtr infoQueue; + if (FAILED(mD3d12Device.As(&infoQueue))) { + return; + } + uint64_t totalErrors = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); + + if (totalErrors == 0) { + return; + } + + AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error); } void Device::DestroyImpl() { diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h index 84f0cd4ce5..fe80f859e8 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h @@ -202,6 +202,7 @@ namespace dawn::native::d3d12 { MaybeError WaitForIdleForDestruction() override; MaybeError CheckDebugLayerAndGenerateErrors(); + void AppendDebugLayerMessages(ErrorData* error) override; MaybeError ApplyUseDxcToggle();