diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp index 80d2b9940f..33b3701afb 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -24,6 +24,7 @@ #include "dawn_native/DawnNative.h" #include "dawn_wire/WireClient.h" #include "dawn_wire/WireServer.h" +#include "utils/PlatformDebugLogger.h" #include "utils/SystemUtils.h" #include "utils/TerribleCommandBuffer.h" #include "utils/WGPUHelpers.h" @@ -35,7 +36,7 @@ #include #include -#ifdef DAWN_ENABLE_BACKEND_OPENGL +#if defined(DAWN_ENABLE_BACKEND_OPENGL) # include "GLFW/glfw3.h" # include "dawn_native/OpenGLBackend.h" #endif // DAWN_ENABLE_BACKEND_OPENGL @@ -81,7 +82,7 @@ namespace { DawnTestEnvironment* gTestEnv = nullptr; -} // namespace +} // anonymous namespace const RGBA8 RGBA8::kZero = RGBA8(0, 0, 0, 0); const RGBA8 RGBA8::kBlack = RGBA8(0, 0, 0, 255); @@ -186,6 +187,11 @@ void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) { DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { ParseArgs(argc, argv); + if (mEnableBackendValidation) { + mPlatformDebugLogger = + std::unique_ptr(utils::CreatePlatformDebugLogger()); + } + // Create a temporary instance to select available and preferred adapters. This is done before // test instantiation so GetAvailableAdapterTestParamsForBackends can generate test // parameterizations all selected adapters. We drop the instance at the end of this function @@ -199,6 +205,8 @@ DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { PrintTestConfigurationAndAdapterInfo(); } +DawnTestEnvironment::~DawnTestEnvironment() = default; + void DawnTestEnvironment::ParseArgs(int argc, char** argv) { size_t argLen = 0; // Set when parsing --arg=X arguments for (int i = 1; i < argc; ++i) { diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index 903ab60d81..1116b5685b 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -155,6 +155,7 @@ BackendTestConfig VulkanBackend(std::initializer_list forceEnabledW std::initializer_list forceDisabledWorkarounds = {}); namespace utils { + class PlatformDebugLogger; class TerribleCommandBuffer; } // namespace utils @@ -176,7 +177,7 @@ void InitDawnEnd2EndTestEnvironment(int argc, char** argv); class DawnTestEnvironment : public testing::Environment { public: DawnTestEnvironment(int argc, char** argv); - ~DawnTestEnvironment() override = default; + ~DawnTestEnvironment() override; static void SetEnvironment(DawnTestEnvironment* env); @@ -219,6 +220,8 @@ class DawnTestEnvironment : public testing::Environment { std::string mWireTraceDir; std::vector mDevicePreferences; std::vector mAdapterProperties; + + std::unique_ptr mPlatformDebugLogger; }; class DawnTestBase { diff --git a/src/utils/BUILD.gn b/src/utils/BUILD.gn index a2c57c3e6e..eff41835b1 100644 --- a/src/utils/BUILD.gn +++ b/src/utils/BUILD.gn @@ -70,6 +70,7 @@ static_library("dawn_utils") { "ComboRenderBundleEncoderDescriptor.h", "ComboRenderPipelineDescriptor.cpp", "ComboRenderPipelineDescriptor.h", + "PlatformDebugLogger.h", "SystemUtils.cpp", "SystemUtils.h", "TerribleCommandBuffer.cpp", @@ -89,6 +90,12 @@ static_library("dawn_utils") { libs = [] frameworks = [] + if (is_win) { + sources += [ "WindowsDebugLogger.cpp" ] + } else { + sources += [ "EmptyDebugLogger.cpp" ] + } + if (is_win) { sources += [ "WindowsTimer.cpp" ] } else if (is_mac) { diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 3a959401a2..080c2d5603 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(dawn_utils PRIVATE "ComboRenderPipelineDescriptor.h" "GLFWUtils.cpp" "GLFWUtils.h" + "PlatformDebugLogger.h" "SystemUtils.cpp" "SystemUtils.h" "TerribleCommandBuffer.cpp" @@ -42,6 +43,12 @@ target_link_libraries(dawn_utils glfw ) +if(WIN32) + target_sources(dawn_utils PRIVATE "WindowsDebugLogger.cpp") +else() + target_sources(dawn_utils PRIVATE "EmptyDebugLogger.cpp") +endif() + if(WIN32) target_sources(dawn_utils PRIVATE "WindowsTimer.cpp") elseif(APPLE) diff --git a/src/utils/EmptyDebugLogger.cpp b/src/utils/EmptyDebugLogger.cpp new file mode 100644 index 0000000000..ed0ad7ff53 --- /dev/null +++ b/src/utils/EmptyDebugLogger.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/PlatformDebugLogger.h" + +namespace utils { + + class EmptyDebugLogger : public PlatformDebugLogger { + public: + EmptyDebugLogger() = default; + ~EmptyDebugLogger() override = default; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger() { + return new EmptyDebugLogger(); + } + +} // namespace utils diff --git a/src/utils/PlatformDebugLogger.h b/src/utils/PlatformDebugLogger.h new file mode 100644 index 0000000000..33c46dec44 --- /dev/null +++ b/src/utils/PlatformDebugLogger.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_PLATFORMDEBUGLOGGER_H_ +#define UTILS_PLATFORMDEBUGLOGGER_H_ + +namespace utils { + + class PlatformDebugLogger { + public: + virtual ~PlatformDebugLogger() = default; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger(); + +} // namespace utils + +#endif // UTILS_PLATFORMDEBUGLOGGER_H_ diff --git a/src/utils/WindowsDebugLogger.cpp b/src/utils/WindowsDebugLogger.cpp new file mode 100644 index 0000000000..0383e0a0d0 --- /dev/null +++ b/src/utils/WindowsDebugLogger.cpp @@ -0,0 +1,104 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/PlatformDebugLogger.h" + +#include "common/Assert.h" +#include "common/windows_with_undefs.h" + +#include +#include + +namespace utils { + + class WindowsDebugLogger : public PlatformDebugLogger { + public: + WindowsDebugLogger() : PlatformDebugLogger() { + if (IsDebuggerPresent()) { + // This condition is true when running inside Visual Studio or some other debugger. + // Messages are already printed there so we don't need to do anything. + return; + } + + mShouldExitHandle = CreateEventA(nullptr, TRUE, FALSE, nullptr); + ASSERT(mShouldExitHandle != nullptr); + + mThread = std::thread( + [](HANDLE shouldExit) { + // https://blogs.msdn.microsoft.com/reiley/2011/07/29/a-debugging-approach-to-outputdebugstring/ + // for the layout of this struct. + struct { + DWORD process_id; + char data[4096 - sizeof(DWORD)]; + }* dbWinBuffer = nullptr; + + HANDLE file = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, + 0, sizeof(*dbWinBuffer), "DBWIN_BUFFER"); + ASSERT(file != nullptr); + ASSERT(file != INVALID_HANDLE_VALUE); + + dbWinBuffer = static_cast( + MapViewOfFile(file, SECTION_MAP_READ, 0, 0, 0)); + ASSERT(dbWinBuffer != nullptr); + + HANDLE dbWinBufferReady = + CreateEventA(nullptr, FALSE, FALSE, "DBWIN_BUFFER_READY"); + ASSERT(dbWinBufferReady != nullptr); + + HANDLE dbWinDataReady = CreateEventA(nullptr, FALSE, FALSE, "DBWIN_DATA_READY"); + ASSERT(dbWinDataReady != nullptr); + + std::array waitHandles = {shouldExit, dbWinDataReady}; + while (true) { + SetEvent(dbWinBufferReady); + DWORD wait = WaitForMultipleObjects(waitHandles.size(), waitHandles.data(), + FALSE, INFINITE); + if (wait == WAIT_OBJECT_0) { + break; + } + ASSERT(wait == WAIT_OBJECT_0 + 1); + fprintf(stderr, "%.*s\n", static_cast(sizeof(dbWinBuffer->data)), + dbWinBuffer->data); + fflush(stderr); + } + + CloseHandle(dbWinDataReady); + CloseHandle(dbWinBufferReady); + UnmapViewOfFile(dbWinBuffer); + CloseHandle(file); + }, + mShouldExitHandle); + } + + ~WindowsDebugLogger() override { + if (mShouldExitHandle != nullptr) { + ASSERT(SetEvent(mShouldExitHandle)); + CloseHandle(mShouldExitHandle); + } + + if (mThread.joinable()) { + mThread.join(); + } + } + + private: + std::thread mThread; + HANDLE mShouldExitHandle = INVALID_HANDLE_VALUE; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger() { + return new WindowsDebugLogger(); + } + +} // namespace utils