Add Vulkan validation layers on Windows
Deploy self-built Vulkan validation layers instead of system installed
one. And it will reuse third_party/angle's Vulkan validation layers if
building with chromium.
Bug: dawn:150
Change-Id: I94e26f7a152fb2a1c39bcb102d60024f4d65eee6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/11120
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index a5288db..59e3228 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -13,6 +13,7 @@
# limitations under the License.
import("//build_overrides/build.gni")
+import("//build_overrides/vulkan_validation_layers.gni")
import("generator/dawn_generator.gni")
import("scripts/dawn_component.gni")
import("scripts/dawn_features.gni")
@@ -496,6 +497,12 @@
"//third_party/fuchsia-sdk:vulkan_validation",
]
}
+ if (dawn_enable_vulkan_validation_layers) {
+ defines = [
+ "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS",
+ "DAWN_VK_DATA_DIR=\"$vulkan_data_subdir\"",
+ ]
+ }
}
}
@@ -534,6 +541,16 @@
}
if (dawn_enable_vulkan) {
sources += [ "src/dawn_native/vulkan/VulkanBackend.cpp" ]
+
+ if (dawn_enable_vulkan_validation_layers) {
+ data_deps = [
+ "${dawn_vulkan_validation_layers_dir}:vulkan_validation_layers",
+ ]
+ if (!is_android) {
+ data_deps +=
+ [ "${dawn_vulkan_validation_layers_dir}:vulkan_gen_json_files" ]
+ }
+ }
}
}
@@ -795,6 +812,7 @@
"src/tests/unittests/RingBufferAllocatorTests.cpp",
"src/tests/unittests/SerialMapTests.cpp",
"src/tests/unittests/SerialQueueTests.cpp",
+ "src/tests/unittests/SystemUtilsTests.cpp",
"src/tests/unittests/ToBackendTests.cpp",
"src/tests/unittests/validation/BindGroupValidationTests.cpp",
"src/tests/unittests/validation/BufferValidationTests.cpp",
diff --git a/DEPS b/DEPS
index ed71745..0229c75 100644
--- a/DEPS
+++ b/DEPS
@@ -100,6 +100,18 @@
'url': '{dawn_git}/clang-format@2451c56cd368676cdb230fd5ad11731ab859f1a3',
'condition': 'dawn_standalone and checkout_linux',
},
+
+ # Khronos Vulkan-Headers
+ 'third_party/vulkan-headers': {
+ 'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@5b44df19e040fca0048ab30c553a8c2d2cb9623e',
+ 'condition': 'dawn_standalone',
+ },
+
+ # Khronos Vulkan-ValidationLayers
+ 'third_party/vulkan-validation-layers': {
+ 'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@9fba37afae13a11bd49ae942bf82e5bf1098e381',
+ 'condition': 'dawn_standalone',
+ },
}
hooks = [
diff --git a/build_overrides/dawn.gni b/build_overrides/dawn.gni
index 4d21a33..968d43b 100644
--- a/build_overrides/dawn.gni
+++ b/build_overrides/dawn.gni
@@ -31,3 +31,4 @@
dawn_shaderc_dir = "//third_party/shaderc"
dawn_spirv_tools_dir = "//third_party/SPIRV-Tools"
dawn_spirv_cross_dir = "//third_party/spirv-cross"
+dawn_vulkan_validation_layers_dir = "//third_party/vulkan-validation-layers"
diff --git a/build_overrides/vulkan_validation_layers.gni b/build_overrides/vulkan_validation_layers.gni
new file mode 100644
index 0000000..d430597
--- /dev/null
+++ b/build_overrides/vulkan_validation_layers.gni
@@ -0,0 +1,24 @@
+# Copyright 2019 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.
+
+# These are variables that are overridable by projects that include Dawn.
+# The values in this file are the defaults for when we are building from
+# Dawn's repository.
+vulkan_headers_dir = "//third_party/vulkan-headers"
+vvl_spirv_tools_dir = "//third_party/SPIRV-Tools"
+vvl_glslang_dir = "//third_party/glslang"
+
+# Subdirectories for generated files
+vulkan_data_subdir = "vulkandata"
+vulkan_gen_subdir = ""
diff --git a/scripts/dawn_features.gni b/scripts/dawn_features.gni
index aed6c75..ded8e3b 100644
--- a/scripts/dawn_features.gni
+++ b/scripts/dawn_features.gni
@@ -45,3 +45,11 @@
# compiler, since it is a sub-class of if.
dawn_enable_cross_reflection = false
}
+
+# GN does not allow reading a variable defined in the same declare_args().
+# Put them in two separate declare_args() when setting the value of one
+# argument based on another.
+declare_args() {
+ # Uses our built version of Vulkan validation layers
+ dawn_enable_vulkan_validation_layers = dawn_enable_vulkan && is_win
+}
diff --git a/scripts/dawn_overrides_with_defaults.gni b/scripts/dawn_overrides_with_defaults.gni
index ab5a21d..2e69e76 100644
--- a/scripts/dawn_overrides_with_defaults.gni
+++ b/scripts/dawn_overrides_with_defaults.gni
@@ -56,3 +56,7 @@
if (!defined(dawn_spirv_tools_dir)) {
dawn_spirv_tools_dir = "//third_party/SPIRV-Tools"
}
+
+if (!defined(dawn_vulkan_validaion_layers_dir)) {
+ dawn_vulkan_validaion_layers_dir = "//third_party/vulkan-validation-layers"
+}
diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn
index c362b4c..fdc4377 100644
--- a/src/common/BUILD.gn
+++ b/src/common/BUILD.gn
@@ -100,6 +100,8 @@
"SerialQueue.h",
"SerialStorage.h",
"SwapChainUtils.h",
+ "SystemUtils.cpp",
+ "SystemUtils.h",
"vulkan_platform.h",
"windows_with_undefs.h",
"xlib_with_undefs.h",
diff --git a/src/common/SystemUtils.cpp b/src/common/SystemUtils.cpp
new file mode 100644
index 0000000..88fc7d7
--- /dev/null
+++ b/src/common/SystemUtils.cpp
@@ -0,0 +1,117 @@
+// Copyright 2019 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 "common/SystemUtils.h"
+
+#if defined(DAWN_PLATFORM_WINDOWS)
+# include <Windows.h>
+# include <vector>
+#elif defined(DAWN_PLATFORM_LINUX)
+# include <limits.h>
+# include <unistd.h>
+# include <cstdlib>
+#elif defined(DAWN_PLATFORM_MACOS)
+# include <mach-o/dyld.h>
+# include <vector>
+#endif
+
+#include <array>
+
+#if defined(DAWN_PLATFORM_WINDOWS)
+const char* GetPathSeparator() {
+ return "\\";
+}
+
+std::string GetEnvironmentVar(const char* variableName) {
+ // First pass a size of 0 to get the size of variable value.
+ char* tempBuf = nullptr;
+ DWORD result = GetEnvironmentVariableA(variableName, tempBuf, 0);
+ if (result == 0) {
+ return "";
+ }
+
+ // Then get variable value with its actual size.
+ std::vector<char> buffer(result + 1);
+ if (GetEnvironmentVariableA(variableName, buffer.data(), static_cast<DWORD>(buffer.size())) ==
+ 0) {
+ return "";
+ }
+ return std::string(buffer.data());
+}
+
+bool SetEnvironmentVar(const char* variableName, const char* value) {
+ return SetEnvironmentVariableA(variableName, value) == TRUE;
+}
+#elif defined(DAWN_PLATFORM_POSIX)
+const char* GetPathSeparator() {
+ return "/";
+}
+
+std::string GetEnvironmentVar(const char* variableName) {
+ char* value = getenv(variableName);
+ return value == nullptr ? "" : std::string(value);
+}
+
+bool SetEnvironmentVar(const char* variableName, const char* value) {
+ return setenv(variableName, value, 1) == 0;
+}
+#else
+# error "Implement Get/SetEnvironmentVar for your platform."
+#endif
+
+#if defined(DAWN_PLATFORM_WINDOWS)
+std::string GetExecutablePath() {
+ std::array<char, MAX_PATH> executableFileBuf;
+ DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(),
+ static_cast<DWORD>(executableFileBuf.size()));
+ return executablePathLen > 0 ? std::string(executableFileBuf.data()) : "";
+}
+#elif defined(DAWN_PLATFORM_LINUX)
+std::string GetExecutablePath() {
+ std::array<char, PATH_MAX> path;
+ ssize_t result = readlink("/proc/self/exe", path.data(), PATH_MAX - 1);
+ if (result < 0 || static_cast<size_t>(result) >= PATH_MAX - 1) {
+ return "";
+ }
+
+ path[result] = '\0';
+ return path.data();
+}
+#elif defined(DAWN_PLATFORM_MACOS)
+std::string GetExecutablePath() {
+ uint32_t size = 0;
+ _NSGetExecutablePath(nullptr, &size);
+
+ std::vector<char> buffer(size + 1);
+ if (_NSGetExecutablePath(buffer.data(), &size) != 0) {
+ return "";
+ }
+
+ buffer[size] = '\0';
+ return buffer.data();
+}
+#elif defined(DAWN_PLATFORM_FUCHSIA)
+std::string GetExecutablePath() {
+ // TODO: Implement on Fuchsia
+ return "";
+}
+#else
+# error "Implement GetExecutablePath for your platform."
+#endif
+
+std::string GetExecutableDirectory() {
+ std::string exePath = GetExecutablePath();
+ size_t lastPathSepLoc = exePath.find_last_of(GetPathSeparator());
+ return lastPathSepLoc != std::string::npos ? exePath.substr(0, lastPathSepLoc + 1) : "";
+}
diff --git a/src/common/SystemUtils.h b/src/common/SystemUtils.h
new file mode 100644
index 0000000..2edf1e3
--- /dev/null
+++ b/src/common/SystemUtils.h
@@ -0,0 +1,27 @@
+// Copyright 2019 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 COMMON_SYSTEMUTILS_H_
+#define COMMON_SYSTEMUTILS_H_
+
+#include "common/Platform.h"
+
+#include <string>
+
+const char* GetPathSeparator();
+std::string GetEnvironmentVar(const char* variableName);
+bool SetEnvironmentVar(const char* variableName, const char* value);
+std::string GetExecutableDirectory();
+
+#endif // COMMON_SYSTEMUTILS_H_
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index 398569d..9ac3224 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -14,6 +14,7 @@
#include "dawn_native/vulkan/BackendVk.h"
+#include "common/SystemUtils.h"
#include "dawn_native/Instance.h"
#include "dawn_native/VulkanBackend.h"
#include "dawn_native/vulkan/AdapterVk.h"
@@ -62,6 +63,15 @@
}
MaybeError Backend::Initialize() {
+#if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS)
+ if (GetInstance()->IsBackendValidationEnabled()) {
+ std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR;
+ if (!SetEnvironmentVar("VK_LAYER_PATH", vkDataDir.c_str())) {
+ return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't set VK_LAYER_PATH with ") +
+ vkDataDir);
+ }
+ }
+#endif
if (!mVulkanLib.Open(kVulkanLibName)) {
return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't open ") + kVulkanLibName);
}
diff --git a/src/tests/unittests/SystemUtilsTests.cpp b/src/tests/unittests/SystemUtilsTests.cpp
new file mode 100644
index 0000000..540f4ef
--- /dev/null
+++ b/src/tests/unittests/SystemUtilsTests.cpp
@@ -0,0 +1,41 @@
+// Copyright 2019 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 <gtest/gtest.h>
+
+#include "common/SystemUtils.h"
+
+// Tests for GetEnvironmentVar
+TEST(SystemUtilsTests, GetEnvironmentVar) {
+ // Test nonexistent environment variable
+ ASSERT_EQ(GetEnvironmentVar("NonexistentEnvironmentVar"), "");
+}
+
+// Tests for SetEnvironmentVar
+TEST(SystemUtilsTests, SetEnvironmentVar) {
+ // Test new environment variable
+ ASSERT_TRUE(SetEnvironmentVar("EnvironmentVarForTest", "NewEnvironmentVarValue"));
+ ASSERT_EQ(GetEnvironmentVar("EnvironmentVarForTest"), "NewEnvironmentVarValue");
+ // Test override environment variable
+ ASSERT_TRUE(SetEnvironmentVar("EnvironmentVarForTest", "OverrideEnvironmentVarValue"));
+ ASSERT_EQ(GetEnvironmentVar("EnvironmentVarForTest"), "OverrideEnvironmentVarValue");
+}
+
+// Tests for GetExecutableDirectory
+TEST(SystemUtilsTests, GetExecutableDirectory) {
+ // Test returned value is non-empty string
+ ASSERT_NE(GetExecutableDirectory(), "");
+ // Test last charecter in path
+ ASSERT_EQ(GetExecutableDirectory().back(), *GetPathSeparator());
+}