Add runtime search paths to Instance descriptor So that Chrome can configure Dawn to load SwiftShader from the bundled/module directory. The shared libraries and ICD are packaged in a separate directory while dawn::native is linked statically into the Chrome executable. Change the Vulkan backend to use these paths for loading Vulkan. Bug: chromium:1266550 Change-Id: I40468b481881f6c249694c1c61137bc0c9b8fe76 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/78840 Reviewed-by: Loko Kung <lokokung@google.com> Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json index c18b3c3..f0fea53 100644 --- a/dawn.json +++ b/dawn.json
@@ -1410,6 +1410,15 @@ "extensible": "in", "members": [] }, + "dawn instance descriptor": { + "tags": ["dawn", "native"], + "category": "structure", + "chained": "in", + "members": [ + {"name": "additional runtime search paths count", "type": "uint32_t", "default": 0}, + {"name": "additional runtime search paths", "type": "char", "annotation": "const*const*", "length": "additional runtime search paths count"} + ] + }, "vertex attribute": { "category": "structure", "extensible": false, @@ -2378,7 +2387,8 @@ {"value": 1000, "name": "dawn texture internal usage descriptor", "tags": ["dawn"]}, {"value": 1001, "name": "primitive depth clamping state", "tags": ["dawn", "emscripten"]}, {"value": 1002, "name": "dawn toggles device descriptor", "tags": ["dawn", "native"]}, - {"value": 1003, "name": "dawn encoder internal usage descriptor", "tags": ["dawn"]} + {"value": 1003, "name": "dawn encoder internal usage descriptor", "tags": ["dawn"]}, + {"value": 1004, "name": "dawn instance descriptor", "tags": ["dawn", "native"]} ] }, "texture": {
diff --git a/src/common/SystemUtils.cpp b/src/common/SystemUtils.cpp index 9dc066a..41a7d87 100644 --- a/src/common/SystemUtils.cpp +++ b/src/common/SystemUtils.cpp
@@ -88,93 +88,119 @@ #endif #if defined(DAWN_PLATFORM_WINDOWS) -std::string GetExecutablePath() { +std::optional<std::string> GetHModulePath(HMODULE module) { std::array<char, MAX_PATH> executableFileBuf; DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(), static_cast<DWORD>(executableFileBuf.size())); - return executablePathLen > 0 ? std::string(executableFileBuf.data()) : ""; + if (executablePathLen == 0) { + return {}; + } + return executableFileBuf.data(); +} +std::optional<std::string> GetExecutablePath() { + return GetHModulePath(nullptr); } #elif defined(DAWN_PLATFORM_LINUX) -std::string GetExecutablePath() { +std::optional<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 ""; + return {}; } path[result] = '\0'; return path.data(); } #elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS) -std::string GetExecutablePath() { +std::optional<std::string> GetExecutablePath() { uint32_t size = 0; _NSGetExecutablePath(nullptr, &size); std::vector<char> buffer(size + 1); if (_NSGetExecutablePath(buffer.data(), &size) != 0) { - return ""; + return {}; } buffer[size] = '\0'; return buffer.data(); } #elif defined(DAWN_PLATFORM_FUCHSIA) -std::string GetExecutablePath() { +std::optional<std::string> GetExecutablePath() { // TODO: Implement on Fuchsia - return ""; + return {}; } #elif defined(DAWN_PLATFORM_EMSCRIPTEN) -std::string GetExecutablePath() { - UNREACHABLE(); - return ""; +std::optional<std::string> GetExecutablePath() { + 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) : ""; +std::optional<std::string> GetExecutableDirectory() { + std::optional<std::string> exePath = GetExecutablePath(); + if (!exePath) { + return {}; + } + size_t lastPathSepLoc = exePath->find_last_of(GetPathSeparator()); + if (lastPathSepLoc == std::string::npos) { + return {}; + } + return exePath->substr(0, lastPathSepLoc + 1); } #if defined(DAWN_PLATFORM_LINUX) || defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS) -std::string GetModulePath() { +std::optional<std::string> GetModulePath() { static int placeholderSymbol = 0; Dl_info dlInfo; if (dladdr(&placeholderSymbol, &dlInfo) == 0) { - return ""; + return {}; } std::array<char, PATH_MAX> absolutePath; if (realpath(dlInfo.dli_fname, absolutePath.data()) == NULL) { - return ""; + return {}; } return absolutePath.data(); } #elif defined(DAWN_PLATFORM_WINDOWS) -std::string GetModulePath() { - UNREACHABLE(); - return ""; +std::optional<std::string> GetModulePath() { + static int placeholderSymbol = 0; + HMODULE module = nullptr; +// GetModuleHandleEx is unavailable on UWP +# if defined(DAWN_IS_WINUWP) + return {}; +# else + if (!GetModuleHandleExA( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<LPCSTR>(&placeholderSymbol), &module)) { + return {}; + } +# endif + return GetHModulePath(module); } #elif defined(DAWN_PLATFORM_FUCHSIA) -std::string GetModulePath() { - UNREACHABLE(); - return ""; +std::optional<std::string> GetModulePath() { + return {}; } #elif defined(DAWN_PLATFORM_EMSCRIPTEN) -std::string GetModulePath() { - UNREACHABLE(); - return ""; +std::optional<std::string> GetModulePath() { + return {}; } #else # error "Implement GetModulePath for your platform." #endif -std::string GetModuleDirectory() { - std::string modPath = GetModulePath(); - size_t lastPathSepLoc = modPath.find_last_of(GetPathSeparator()); - return lastPathSepLoc != std::string::npos ? modPath.substr(0, lastPathSepLoc + 1) : ""; +std::optional<std::string> GetModuleDirectory() { + std::optional<std::string> modPath = GetModulePath(); + if (!modPath) { + return {}; + } + size_t lastPathSepLoc = modPath->find_last_of(GetPathSeparator()); + if (lastPathSepLoc == std::string::npos) { + return {}; + } + return modPath->substr(0, lastPathSepLoc + 1); } // ScopedEnvironmentVar
diff --git a/src/common/SystemUtils.h b/src/common/SystemUtils.h index 875c5aa..b7b9aa4 100644 --- a/src/common/SystemUtils.h +++ b/src/common/SystemUtils.h
@@ -17,6 +17,7 @@ #include "common/Platform.h" +#include <optional> #include <string> const char* GetPathSeparator(); @@ -25,8 +26,11 @@ std::pair<std::string, bool> GetEnvironmentVar(const char* variableName); bool SetEnvironmentVar(const char* variableName, const char* value); // Directories are always returned with a trailing path separator. -std::string GetExecutableDirectory(); -std::string GetModuleDirectory(); +// May return std::nullopt if the path is too long, there is no current +// module (statically linked into executable), or the function is not +// implemented on the platform. +std::optional<std::string> GetExecutableDirectory(); +std::optional<std::string> GetModuleDirectory(); #ifdef DAWN_PLATFORM_MACOS void GetMacOSVersion(int32_t* majorVersion, int32_t* minorVersion = nullptr);
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn index 1f2e920..5a18b30 100644 --- a/src/dawn_native/BUILD.gn +++ b/src/dawn_native/BUILD.gn
@@ -331,10 +331,10 @@ "VertexFormat.cpp", "VertexFormat.h", "dawn_platform.h", - "webgpu_absl_format.cpp", - "webgpu_absl_format.h", "utils/WGPUHelpers.cpp", "utils/WGPUHelpers.h", + "webgpu_absl_format.cpp", + "webgpu_absl_format.h", ] if (dawn_use_x11) { @@ -673,6 +673,9 @@ "//third_party/fuchsia-sdk/sdk:trace_engine", ] } + if (dawn_is_winuwp) { + defines += [ "DAWN_IS_WINUWP" ] + } if (enable_vulkan_validation_layers) { defines += [ "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS", @@ -681,17 +684,11 @@ } if (enable_vulkan_loader) { data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ] - defines += [ "DAWN_ENABLE_VULKAN_LOADER" ] } if (use_swiftshader) { - data_deps += [ - "${dawn_swiftshader_dir}/src/Vulkan:icd_file", - "${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan", - ] - defines += [ - "DAWN_ENABLE_SWIFTSHADER", - "DAWN_SWIFTSHADER_VK_ICD_JSON=\"${swiftshader_icd_file_name}\"", - ] + data_deps += + [ "${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan" ] + defines += [ "DAWN_ENABLE_SWIFTSHADER" ] } }
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp index 498d9b1..2eb033f 100644 --- a/src/dawn_native/DawnNative.cpp +++ b/src/dawn_native/DawnNative.cpp
@@ -187,7 +187,8 @@ // Instance - Instance::Instance() : mImpl(InstanceBase::Create()) { + Instance::Instance(const WGPUInstanceDescriptor* desc) + : mImpl(InstanceBase::Create(reinterpret_cast<const InstanceDescriptor*>(desc))) { } Instance::~Instance() {
diff --git a/src/dawn_native/Instance.cpp b/src/dawn_native/Instance.cpp index 7b49351..c0f4e1b 100644 --- a/src/dawn_native/Instance.cpp +++ b/src/dawn_native/Instance.cpp
@@ -17,6 +17,8 @@ #include "common/Assert.h" #include "common/GPUInfo.h" #include "common/Log.h" +#include "common/SystemUtils.h" +#include "dawn_native/ChainUtils_autogen.h" #include "dawn_native/ErrorData.h" #include "dawn_native/Surface.h" #include "dawn_native/ValidationUtils_autogen.h" @@ -96,15 +98,36 @@ // static InstanceBase* InstanceBase::Create(const InstanceDescriptor* descriptor) { Ref<InstanceBase> instance = AcquireRef(new InstanceBase); - if (!instance->Initialize(descriptor)) { + static constexpr InstanceDescriptor kDefaultDesc = {}; + if (descriptor == nullptr) { + descriptor = &kDefaultDesc; + } + if (instance->ConsumedError(instance->Initialize(descriptor))) { return nullptr; } return instance.Detach(); } // TODO(crbug.com/dawn/832): make the platform an initialization parameter of the instance. - bool InstanceBase::Initialize(const InstanceDescriptor*) { - return true; + MaybeError InstanceBase::Initialize(const InstanceDescriptor* descriptor) { + DAWN_TRY(ValidateSingleSType(descriptor->nextInChain, wgpu::SType::DawnInstanceDescriptor)); + const DawnInstanceDescriptor* dawnDesc = nullptr; + FindInChain(descriptor->nextInChain, &dawnDesc); + if (dawnDesc != nullptr) { + for (uint32_t i = 0; i < dawnDesc->additionalRuntimeSearchPathsCount; ++i) { + mRuntimeSearchPaths.push_back(dawnDesc->additionalRuntimeSearchPaths[i]); + } + } + // Default paths to search are next to the shared library, next to the executable, and + // no path (just libvulkan.so). + if (auto p = GetModuleDirectory()) { + mRuntimeSearchPaths.push_back(std::move(*p)); + } + if (auto p = GetExecutableDirectory()) { + mRuntimeSearchPaths.push_back(std::move(*p)); + } + mRuntimeSearchPaths.push_back(""); + return {}; } void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options, @@ -386,6 +409,10 @@ return mDefaultPlatform.get(); } + const std::vector<std::string>& InstanceBase::GetRuntimeSearchPaths() const { + return mRuntimeSearchPaths; + } + const XlibXcbFunctions* InstanceBase::GetOrCreateXlibXcbFunctions() { #if defined(DAWN_USE_X11) if (mXlibXcbFunctions == nullptr) {
diff --git a/src/dawn_native/Instance.h b/src/dawn_native/Instance.h index 913b2b6..f269d96 100644 --- a/src/dawn_native/Instance.h +++ b/src/dawn_native/Instance.h
@@ -76,6 +76,8 @@ void SetPlatform(dawn::platform::Platform* platform); dawn::platform::Platform* GetPlatform(); + const std::vector<std::string>& GetRuntimeSearchPaths() const; + // Get backend-independent libraries that need to be loaded dynamically. const XlibXcbFunctions* GetOrCreateXlibXcbFunctions(); @@ -89,7 +91,7 @@ InstanceBase(const InstanceBase& other) = delete; InstanceBase& operator=(const InstanceBase& other) = delete; - bool Initialize(const InstanceDescriptor* descriptor); + MaybeError Initialize(const InstanceDescriptor* descriptor); // Lazily creates connections to all backends that have been compiled. void EnsureBackendConnection(wgpu::BackendType backendType); @@ -99,6 +101,8 @@ ResultOrError<Ref<AdapterBase>> RequestAdapterInternal( const RequestAdapterOptions* options); + std::vector<std::string> mRuntimeSearchPaths; + BackendsBitset mBackendsConnected; bool mDiscoveredDefaultAdapters = false;
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp index aeba2aa..79f8706 100644 --- a/src/dawn_native/vulkan/BackendVk.cpp +++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -172,55 +172,43 @@ ScopedEnvironmentVar vkICDFilenames; ScopedEnvironmentVar vkLayerPath; -#if defined(DAWN_ENABLE_VULKAN_LOADER) - // If enabled, we use our own built Vulkan loader by specifying an absolute path to the - // shared library. Note that when we are currently getting the absolute path for the custom - // loader by getting the path to the dawn native library and traversing relative from there. - // This has implications for dawn tests because some of them are linking statically to - // dawn_native which means the "module" is actually the test as well. If the directory - // location of the tests change w.r.t the shared lib then this may break. Essentially we are - // assuming that our custom built Vulkan loader will always be in the same directory as the - // shared dawn native library and all test binaries that link statically. - const std::string resolvedVulkanLibPath = GetModuleDirectory() + kVulkanLibName; -#else - const std::string resolvedVulkanLibPath = kVulkanLibName; -#endif // defined(DAWN_ENABLE_VULKAN_LOADER) + const std::vector<std::string>& searchPaths = instance->GetRuntimeSearchPaths(); + + auto CommaSeparatedResolvedSearchPaths = [&](const char* name) { + std::string list; + bool first = true; + for (const std::string& path : searchPaths) { + if (!first) { + list += ", "; + } + first = false; + list += (path + name); + } + return list; + }; + + auto LoadVulkan = [&](const char* libName) -> MaybeError { + for (const std::string& path : searchPaths) { + std::string resolvedPath = path + libName; + if (mVulkanLib.Open(resolvedPath)) { + return {}; + } + } + return DAWN_FORMAT_INTERNAL_ERROR("Couldn't load Vulkan. Searched %s.", + CommaSeparatedResolvedSearchPaths(libName)); + }; switch (icd) { case ICD::None: { - if (!mVulkanLib.Open(resolvedVulkanLibPath)) { - return DAWN_FORMAT_INTERNAL_ERROR("Couldn't load %s.", resolvedVulkanLibPath); - } + DAWN_TRY(LoadVulkan(kVulkanLibName)); + // Succesfully loaded driver; break. break; } case ICD::SwiftShader: { #if defined(DAWN_ENABLE_SWIFTSHADER) - // First try to load the system Vulkan driver, if that fails, try to load with - // Swiftshader. Note: The system driver could potentially be Swiftshader if it was - // installed. -# if defined(DAWN_SWIFTSHADER_VK_ICD_JSON) - if (mVulkanLib.Open(resolvedVulkanLibPath)) { - std::string fullSwiftshaderICDPath = - GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON; - if (!vkICDFilenames.Set("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) { - return DAWN_FORMAT_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES to %s.", - fullSwiftshaderICDPath); - } - // Succesfully loaded driver and set VK_ICD_FILENAMES. - break; - } else -# endif // defined(DAWN_SWIFTSHADER_VK_ICD_JSON) - // Fallback to loading SwiftShader directly. - if (mVulkanLib.Open(kSwiftshaderLibName)) { - // Succesfully loaded SwiftShader. - break; - } - return DAWN_FORMAT_INTERNAL_ERROR( - "Failed to load SwiftShader. DAWN_SWIFTSHADER_VK_ICD_JSON was not defined and " - "could not load %s.", - kSwiftshaderLibName); + DAWN_TRY(LoadVulkan(kSwiftshaderLibName)); + break; #endif // defined(DAWN_ENABLE_SWIFTSHADER) - // ICD::SwiftShader should not be passed if SwiftShader is not enabled. UNREACHABLE(); } @@ -228,7 +216,8 @@ if (instance->IsBackendValidationEnabled()) { #if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS) - std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR; + auto execDir = GetExecutableDirectory(); + std::string vkDataDir = execDir.value_or("") + DAWN_VK_DATA_DIR; if (!vkLayerPath.Set("VK_LAYER_PATH", vkDataDir.c_str())) { return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH"); }
diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h index 4850d8c..9f6e3c22 100644 --- a/src/include/dawn_native/DawnNative.h +++ b/src/include/dawn_native/DawnNative.h
@@ -136,7 +136,7 @@ // for this instance. class DAWN_NATIVE_EXPORT Instance { public: - Instance(); + explicit Instance(const WGPUInstanceDescriptor* desc = nullptr); ~Instance(); Instance(const Instance& other) = delete;
diff --git a/src/tests/unittests/SystemUtilsTests.cpp b/src/tests/unittests/SystemUtilsTests.cpp index e754701..d60c61d 100644 --- a/src/tests/unittests/SystemUtilsTests.cpp +++ b/src/tests/unittests/SystemUtilsTests.cpp
@@ -40,10 +40,12 @@ // Tests for GetExecutableDirectory TEST(SystemUtilsTests, GetExecutableDirectory) { + auto dir = GetExecutableDirectory(); // Test returned value is non-empty string - EXPECT_NE(GetExecutableDirectory(), ""); - // Test last charecter in path - EXPECT_EQ(GetExecutableDirectory().back(), *GetPathSeparator()); + EXPECT_NE(dir, std::optional{std::string("")}); + ASSERT_NE(dir, std::nullopt); + // Test last character in path + EXPECT_EQ(dir->back(), *GetPathSeparator()); } // Tests for ScopedEnvironmentVar