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:
parent
be6c2ffadd
commit
d44e7b3b0a
|
@ -497,6 +497,7 @@ namespace dawn::native {
|
|||
|
||||
void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error) {
|
||||
ASSERT(error != nullptr);
|
||||
AppendDebugLayerMessages(error.get());
|
||||
HandleError(error->GetType(), error->GetFormattedMessage().c_str());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<std::string>& 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<BacktraceRecord>& GetBacktrace() const;
|
||||
const std::vector<std::string>& GetContexts() const;
|
||||
const std::vector<std::string>& GetDebugGroups() const;
|
||||
const std::vector<std::string>& GetBackendMessages() const;
|
||||
|
||||
std::string GetFormattedMessage() const;
|
||||
|
||||
|
@ -63,6 +65,7 @@ namespace dawn::native {
|
|||
std::vector<BacktraceRecord> mBacktrace;
|
||||
std::vector<std::string> mContexts;
|
||||
std::vector<std::string> mDebugGroups;
|
||||
std::vector<std::string> mBackendMessages;
|
||||
};
|
||||
|
||||
} // namespace dawn::native
|
||||
|
|
|
@ -19,19 +19,54 @@
|
|||
#include <string>
|
||||
|
||||
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) {
|
||||
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());
|
||||
|
|
|
@ -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<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() {
|
||||
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.");
|
||||
|
||||
AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error.get());
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
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)) {
|
||||
messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n';
|
||||
continue;
|
||||
void Device::AppendDebugLayerMessages(ErrorData* error) {
|
||||
if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
messages << message->pDescription << " (" << message->ID << ")\n";
|
||||
ComPtr<ID3D12InfoQueue> infoQueue;
|
||||
if (FAILED(mD3d12Device.As(&infoQueue))) {
|
||||
return;
|
||||
}
|
||||
if (errorsToPrint < totalErrors) {
|
||||
messages << (totalErrors - errorsToPrint) << " messages silenced\n";
|
||||
}
|
||||
// We only print up to the first kMaxDebugMessagesToPrint errors
|
||||
infoQueue->ClearStoredMessages();
|
||||
uint64_t totalErrors = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter();
|
||||
|
||||
return DAWN_INTERNAL_ERROR(messages.str());
|
||||
if (totalErrors == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error);
|
||||
}
|
||||
|
||||
void Device::DestroyImpl() {
|
||||
|
|
|
@ -202,6 +202,7 @@ namespace dawn::native::d3d12 {
|
|||
MaybeError WaitForIdleForDestruction() override;
|
||||
|
||||
MaybeError CheckDebugLayerAndGenerateErrors();
|
||||
void AppendDebugLayerMessages(ErrorData* error) override;
|
||||
|
||||
MaybeError ApplyUseDxcToggle();
|
||||
|
||||
|
|
Loading…
Reference in New Issue