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 <bajones@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Brandon Jones 2022-04-28 01:06:53 +00:00 committed by Dawn LUCI CQ
parent be6c2ffadd
commit d44e7b3b0a
7 changed files with 124 additions and 31 deletions

View File

@ -497,6 +497,7 @@ namespace dawn::native {
void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error) { void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error) {
ASSERT(error != nullptr); ASSERT(error != nullptr);
AppendDebugLayerMessages(error.get());
HandleError(error->GetType(), error->GetFormattedMessage().c_str()); HandleError(error->GetType(), error->GetFormattedMessage().c_str());
} }

View File

@ -380,6 +380,9 @@ namespace dawn::native {
void APISetLabel(const char* label); void APISetLabel(const char* label);
void APIDestroy(); void APIDestroy();
virtual void AppendDebugLayerMessages(ErrorData* error) {
}
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
DeviceBase(); DeviceBase();

View File

@ -53,6 +53,10 @@ namespace dawn::native {
mDebugGroups.push_back(std::move(label)); mDebugGroups.push_back(std::move(label));
} }
void ErrorData::AppendBackendMessage(std::string message) {
mBackendMessages.push_back(std::move(message));
}
InternalErrorType ErrorData::GetType() const { InternalErrorType ErrorData::GetType() const {
return mType; return mType;
} }
@ -73,6 +77,10 @@ namespace dawn::native {
return mDebugGroups; return mDebugGroups;
} }
const std::vector<std::string>& ErrorData::GetBackendMessages() const {
return mBackendMessages;
}
std::string ErrorData::GetFormattedMessage() const { std::string ErrorData::GetFormattedMessage() const {
std::ostringstream ss; std::ostringstream ss;
ss << mMessage << "\n"; 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. // stack trace for debugging purposes.
if (mContexts.empty() || mType != InternalErrorType::Validation) { if (mContexts.empty() || mType != InternalErrorType::Validation) {
for (const auto& callsite : mBacktrace) { 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(); return ss.str();
} }

View File

@ -48,12 +48,14 @@ namespace dawn::native {
void AppendBacktrace(const char* file, const char* function, int line); void AppendBacktrace(const char* file, const char* function, int line);
void AppendContext(std::string context); void AppendContext(std::string context);
void AppendDebugGroup(std::string label); void AppendDebugGroup(std::string label);
void AppendBackendMessage(std::string message);
InternalErrorType GetType() const; InternalErrorType GetType() const;
const std::string& GetMessage() const; const std::string& GetMessage() const;
const std::vector<BacktraceRecord>& GetBacktrace() const; const std::vector<BacktraceRecord>& GetBacktrace() const;
const std::vector<std::string>& GetContexts() const; const std::vector<std::string>& GetContexts() const;
const std::vector<std::string>& GetDebugGroups() const; const std::vector<std::string>& GetDebugGroups() const;
const std::vector<std::string>& GetBackendMessages() const;
std::string GetFormattedMessage() const; std::string GetFormattedMessage() const;
@ -63,6 +65,7 @@ namespace dawn::native {
std::vector<BacktraceRecord> mBacktrace; std::vector<BacktraceRecord> mBacktrace;
std::vector<std::string> mContexts; std::vector<std::string> mContexts;
std::vector<std::string> mDebugGroups; std::vector<std::string> mDebugGroups;
std::vector<std::string> mBackendMessages;
}; };
} // namespace dawn::native } // namespace dawn::native

View File

@ -19,19 +19,54 @@
#include <string> #include <string>
namespace dawn::native::d3d12 { 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 "<Unknown HRESULT>";
}
}
MaybeError CheckHRESULTImpl(HRESULT result, const char* context) { MaybeError CheckHRESULTImpl(HRESULT result, const char* context) {
if (DAWN_LIKELY(SUCCEEDED(result))) { if (DAWN_LIKELY(SUCCEEDED(result))) {
return {}; return {};
} }
std::ostringstream messageStream; std::ostringstream messageStream;
messageStream << context << " failed with "; messageStream << context << " failed with " << HRESULTAsString(result) << " (0x"
if (result == E_FAKE_ERROR_FOR_TESTING) { << std::uppercase << std::setfill('0') << std::setw(8) << std::hex << result
messageStream << "E_FAKE_ERROR_FOR_TESTING"; << ")";
} else {
messageStream << "0x" << std::uppercase << std::setfill('0') << std::setw(8) << std::hex
<< result;
}
if (result == DXGI_ERROR_DEVICE_REMOVED) { if (result == DXGI_ERROR_DEVICE_REMOVED) {
return DAWN_DEVICE_LOST_ERROR(messageStream.str()); return DAWN_DEVICE_LOST_ERROR(messageStream.str());

View File

@ -630,6 +630,45 @@ namespace dawn::native::d3d12 {
return {}; 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<uint8_t[]> messageData(new uint8_t[messageLength]);
D3D12_MESSAGE* message = reinterpret_cast<D3D12_MESSAGE*>(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() { MaybeError Device::CheckDebugLayerAndGenerateErrors() {
if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) { if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) {
return {}; return {};
@ -647,33 +686,29 @@ namespace dawn::native::d3d12 {
return {}; return {};
} }
std::ostringstream messages; auto error = DAWN_INTERNAL_ERROR("The D3D12 debug layer reported uncaught errors.");
uint64_t errorsToPrint = std::min(kMaxDebugMessagesToPrint, totalErrors);
for (uint64_t i = 0; i < errorsToPrint; ++i) { AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error.get());
SIZE_T messageLength = 0;
HRESULT hr = infoQueue->GetMessage(i, nullptr, &messageLength); return error;
if (FAILED(hr)) {
messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n';
continue;
} }
std::unique_ptr<uint8_t[]> messageData(new uint8_t[messageLength]); void Device::AppendDebugLayerMessages(ErrorData* error) {
D3D12_MESSAGE* message = reinterpret_cast<D3D12_MESSAGE*>(messageData.get()); if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) {
hr = infoQueue->GetMessage(i, message, &messageLength); return;
if (FAILED(hr)) {
messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n';
continue;
} }
messages << message->pDescription << " (" << message->ID << ")\n"; ComPtr<ID3D12InfoQueue> infoQueue;
if (FAILED(mD3d12Device.As(&infoQueue))) {
return;
} }
if (errorsToPrint < totalErrors) { uint64_t totalErrors = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter();
messages << (totalErrors - errorsToPrint) << " messages silenced\n";
}
// We only print up to the first kMaxDebugMessagesToPrint errors
infoQueue->ClearStoredMessages();
return DAWN_INTERNAL_ERROR(messages.str()); if (totalErrors == 0) {
return;
}
AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error);
} }
void Device::DestroyImpl() { void Device::DestroyImpl() {

View File

@ -202,6 +202,7 @@ namespace dawn::native::d3d12 {
MaybeError WaitForIdleForDestruction() override; MaybeError WaitForIdleForDestruction() override;
MaybeError CheckDebugLayerAndGenerateErrors(); MaybeError CheckDebugLayerAndGenerateErrors();
void AppendDebugLayerMessages(ErrorData* error) override;
MaybeError ApplyUseDxcToggle(); MaybeError ApplyUseDxcToggle();