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>
This commit is contained in:
Austin Eng 2022-02-02 21:00:52 +00:00 committed by Dawn LUCI CQ
parent 6806a61a26
commit 2fbc170827
10 changed files with 156 additions and 96 deletions

View File

@ -1410,6 +1410,15 @@
"extensible": "in", "extensible": "in",
"members": [] "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": { "vertex attribute": {
"category": "structure", "category": "structure",
"extensible": false, "extensible": false,
@ -2378,7 +2387,8 @@
{"value": 1000, "name": "dawn texture internal usage descriptor", "tags": ["dawn"]}, {"value": 1000, "name": "dawn texture internal usage descriptor", "tags": ["dawn"]},
{"value": 1001, "name": "primitive depth clamping state", "tags": ["dawn", "emscripten"]}, {"value": 1001, "name": "primitive depth clamping state", "tags": ["dawn", "emscripten"]},
{"value": 1002, "name": "dawn toggles device descriptor", "tags": ["dawn", "native"]}, {"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": { "texture": {

View File

@ -88,93 +88,119 @@ bool SetEnvironmentVar(const char* variableName, const char* value) {
#endif #endif
#if defined(DAWN_PLATFORM_WINDOWS) #if defined(DAWN_PLATFORM_WINDOWS)
std::string GetExecutablePath() { std::optional<std::string> GetHModulePath(HMODULE module) {
std::array<char, MAX_PATH> executableFileBuf; std::array<char, MAX_PATH> executableFileBuf;
DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(), DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(),
static_cast<DWORD>(executableFileBuf.size())); 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) #elif defined(DAWN_PLATFORM_LINUX)
std::string GetExecutablePath() { std::optional<std::string> GetExecutablePath() {
std::array<char, PATH_MAX> path; std::array<char, PATH_MAX> path;
ssize_t result = readlink("/proc/self/exe", path.data(), PATH_MAX - 1); ssize_t result = readlink("/proc/self/exe", path.data(), PATH_MAX - 1);
if (result < 0 || static_cast<size_t>(result) >= PATH_MAX - 1) { if (result < 0 || static_cast<size_t>(result) >= PATH_MAX - 1) {
return ""; return {};
} }
path[result] = '\0'; path[result] = '\0';
return path.data(); return path.data();
} }
#elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS) #elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
std::string GetExecutablePath() { std::optional<std::string> GetExecutablePath() {
uint32_t size = 0; uint32_t size = 0;
_NSGetExecutablePath(nullptr, &size); _NSGetExecutablePath(nullptr, &size);
std::vector<char> buffer(size + 1); std::vector<char> buffer(size + 1);
if (_NSGetExecutablePath(buffer.data(), &size) != 0) { if (_NSGetExecutablePath(buffer.data(), &size) != 0) {
return ""; return {};
} }
buffer[size] = '\0'; buffer[size] = '\0';
return buffer.data(); return buffer.data();
} }
#elif defined(DAWN_PLATFORM_FUCHSIA) #elif defined(DAWN_PLATFORM_FUCHSIA)
std::string GetExecutablePath() { std::optional<std::string> GetExecutablePath() {
// TODO: Implement on Fuchsia // TODO: Implement on Fuchsia
return ""; return {};
} }
#elif defined(DAWN_PLATFORM_EMSCRIPTEN) #elif defined(DAWN_PLATFORM_EMSCRIPTEN)
std::string GetExecutablePath() { std::optional<std::string> GetExecutablePath() {
UNREACHABLE(); return {};
return "";
} }
#else #else
# error "Implement GetExecutablePath for your platform." # error "Implement GetExecutablePath for your platform."
#endif #endif
std::string GetExecutableDirectory() { std::optional<std::string> GetExecutableDirectory() {
std::string exePath = GetExecutablePath(); std::optional<std::string> exePath = GetExecutablePath();
size_t lastPathSepLoc = exePath.find_last_of(GetPathSeparator()); if (!exePath) {
return lastPathSepLoc != std::string::npos ? exePath.substr(0, lastPathSepLoc + 1) : ""; 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) #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; static int placeholderSymbol = 0;
Dl_info dlInfo; Dl_info dlInfo;
if (dladdr(&placeholderSymbol, &dlInfo) == 0) { if (dladdr(&placeholderSymbol, &dlInfo) == 0) {
return ""; return {};
} }
std::array<char, PATH_MAX> absolutePath; std::array<char, PATH_MAX> absolutePath;
if (realpath(dlInfo.dli_fname, absolutePath.data()) == NULL) { if (realpath(dlInfo.dli_fname, absolutePath.data()) == NULL) {
return ""; return {};
} }
return absolutePath.data(); return absolutePath.data();
} }
#elif defined(DAWN_PLATFORM_WINDOWS) #elif defined(DAWN_PLATFORM_WINDOWS)
std::string GetModulePath() { std::optional<std::string> GetModulePath() {
UNREACHABLE(); static int placeholderSymbol = 0;
return ""; 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) #elif defined(DAWN_PLATFORM_FUCHSIA)
std::string GetModulePath() { std::optional<std::string> GetModulePath() {
UNREACHABLE(); return {};
return "";
} }
#elif defined(DAWN_PLATFORM_EMSCRIPTEN) #elif defined(DAWN_PLATFORM_EMSCRIPTEN)
std::string GetModulePath() { std::optional<std::string> GetModulePath() {
UNREACHABLE(); return {};
return "";
} }
#else #else
# error "Implement GetModulePath for your platform." # error "Implement GetModulePath for your platform."
#endif #endif
std::string GetModuleDirectory() { std::optional<std::string> GetModuleDirectory() {
std::string modPath = GetModulePath(); std::optional<std::string> modPath = GetModulePath();
size_t lastPathSepLoc = modPath.find_last_of(GetPathSeparator()); if (!modPath) {
return lastPathSepLoc != std::string::npos ? modPath.substr(0, lastPathSepLoc + 1) : ""; return {};
}
size_t lastPathSepLoc = modPath->find_last_of(GetPathSeparator());
if (lastPathSepLoc == std::string::npos) {
return {};
}
return modPath->substr(0, lastPathSepLoc + 1);
} }
// ScopedEnvironmentVar // ScopedEnvironmentVar

View File

@ -17,6 +17,7 @@
#include "common/Platform.h" #include "common/Platform.h"
#include <optional>
#include <string> #include <string>
const char* GetPathSeparator(); const char* GetPathSeparator();
@ -25,8 +26,11 @@ const char* GetPathSeparator();
std::pair<std::string, bool> GetEnvironmentVar(const char* variableName); std::pair<std::string, bool> GetEnvironmentVar(const char* variableName);
bool SetEnvironmentVar(const char* variableName, const char* value); bool SetEnvironmentVar(const char* variableName, const char* value);
// Directories are always returned with a trailing path separator. // Directories are always returned with a trailing path separator.
std::string GetExecutableDirectory(); // May return std::nullopt if the path is too long, there is no current
std::string GetModuleDirectory(); // 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 #ifdef DAWN_PLATFORM_MACOS
void GetMacOSVersion(int32_t* majorVersion, int32_t* minorVersion = nullptr); void GetMacOSVersion(int32_t* majorVersion, int32_t* minorVersion = nullptr);

View File

@ -331,10 +331,10 @@ source_set("dawn_native_sources") {
"VertexFormat.cpp", "VertexFormat.cpp",
"VertexFormat.h", "VertexFormat.h",
"dawn_platform.h", "dawn_platform.h",
"webgpu_absl_format.cpp",
"webgpu_absl_format.h",
"utils/WGPUHelpers.cpp", "utils/WGPUHelpers.cpp",
"utils/WGPUHelpers.h", "utils/WGPUHelpers.h",
"webgpu_absl_format.cpp",
"webgpu_absl_format.h",
] ]
if (dawn_use_x11) { if (dawn_use_x11) {
@ -673,6 +673,9 @@ source_set("dawn_native_sources") {
"//third_party/fuchsia-sdk/sdk:trace_engine", "//third_party/fuchsia-sdk/sdk:trace_engine",
] ]
} }
if (dawn_is_winuwp) {
defines += [ "DAWN_IS_WINUWP" ]
}
if (enable_vulkan_validation_layers) { if (enable_vulkan_validation_layers) {
defines += [ defines += [
"DAWN_ENABLE_VULKAN_VALIDATION_LAYERS", "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS",
@ -681,17 +684,11 @@ source_set("dawn_native_sources") {
} }
if (enable_vulkan_loader) { if (enable_vulkan_loader) {
data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ] data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ]
defines += [ "DAWN_ENABLE_VULKAN_LOADER" ]
} }
if (use_swiftshader) { if (use_swiftshader) {
data_deps += [ data_deps +=
"${dawn_swiftshader_dir}/src/Vulkan:icd_file", [ "${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan" ]
"${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan", defines += [ "DAWN_ENABLE_SWIFTSHADER" ]
]
defines += [
"DAWN_ENABLE_SWIFTSHADER",
"DAWN_SWIFTSHADER_VK_ICD_JSON=\"${swiftshader_icd_file_name}\"",
]
} }
} }

View File

@ -187,7 +187,8 @@ namespace dawn::native {
// Instance // Instance
Instance::Instance() : mImpl(InstanceBase::Create()) { Instance::Instance(const WGPUInstanceDescriptor* desc)
: mImpl(InstanceBase::Create(reinterpret_cast<const InstanceDescriptor*>(desc))) {
} }
Instance::~Instance() { Instance::~Instance() {

View File

@ -17,6 +17,8 @@
#include "common/Assert.h" #include "common/Assert.h"
#include "common/GPUInfo.h" #include "common/GPUInfo.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/SystemUtils.h"
#include "dawn_native/ChainUtils_autogen.h"
#include "dawn_native/ErrorData.h" #include "dawn_native/ErrorData.h"
#include "dawn_native/Surface.h" #include "dawn_native/Surface.h"
#include "dawn_native/ValidationUtils_autogen.h" #include "dawn_native/ValidationUtils_autogen.h"
@ -96,15 +98,36 @@ namespace dawn::native {
// static // static
InstanceBase* InstanceBase::Create(const InstanceDescriptor* descriptor) { InstanceBase* InstanceBase::Create(const InstanceDescriptor* descriptor) {
Ref<InstanceBase> instance = AcquireRef(new InstanceBase); 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 nullptr;
} }
return instance.Detach(); return instance.Detach();
} }
// TODO(crbug.com/dawn/832): make the platform an initialization parameter of the instance. // TODO(crbug.com/dawn/832): make the platform an initialization parameter of the instance.
bool InstanceBase::Initialize(const InstanceDescriptor*) { MaybeError InstanceBase::Initialize(const InstanceDescriptor* descriptor) {
return true; 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, void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options,
@ -386,6 +409,10 @@ namespace dawn::native {
return mDefaultPlatform.get(); return mDefaultPlatform.get();
} }
const std::vector<std::string>& InstanceBase::GetRuntimeSearchPaths() const {
return mRuntimeSearchPaths;
}
const XlibXcbFunctions* InstanceBase::GetOrCreateXlibXcbFunctions() { const XlibXcbFunctions* InstanceBase::GetOrCreateXlibXcbFunctions() {
#if defined(DAWN_USE_X11) #if defined(DAWN_USE_X11)
if (mXlibXcbFunctions == nullptr) { if (mXlibXcbFunctions == nullptr) {

View File

@ -76,6 +76,8 @@ namespace dawn::native {
void SetPlatform(dawn::platform::Platform* platform); void SetPlatform(dawn::platform::Platform* platform);
dawn::platform::Platform* GetPlatform(); dawn::platform::Platform* GetPlatform();
const std::vector<std::string>& GetRuntimeSearchPaths() const;
// Get backend-independent libraries that need to be loaded dynamically. // Get backend-independent libraries that need to be loaded dynamically.
const XlibXcbFunctions* GetOrCreateXlibXcbFunctions(); const XlibXcbFunctions* GetOrCreateXlibXcbFunctions();
@ -89,7 +91,7 @@ namespace dawn::native {
InstanceBase(const InstanceBase& other) = delete; InstanceBase(const InstanceBase& other) = delete;
InstanceBase& operator=(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. // Lazily creates connections to all backends that have been compiled.
void EnsureBackendConnection(wgpu::BackendType backendType); void EnsureBackendConnection(wgpu::BackendType backendType);
@ -99,6 +101,8 @@ namespace dawn::native {
ResultOrError<Ref<AdapterBase>> RequestAdapterInternal( ResultOrError<Ref<AdapterBase>> RequestAdapterInternal(
const RequestAdapterOptions* options); const RequestAdapterOptions* options);
std::vector<std::string> mRuntimeSearchPaths;
BackendsBitset mBackendsConnected; BackendsBitset mBackendsConnected;
bool mDiscoveredDefaultAdapters = false; bool mDiscoveredDefaultAdapters = false;

View File

@ -172,55 +172,43 @@ namespace dawn::native::vulkan {
ScopedEnvironmentVar vkICDFilenames; ScopedEnvironmentVar vkICDFilenames;
ScopedEnvironmentVar vkLayerPath; ScopedEnvironmentVar vkLayerPath;
#if defined(DAWN_ENABLE_VULKAN_LOADER) const std::vector<std::string>& searchPaths = instance->GetRuntimeSearchPaths();
// 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 auto CommaSeparatedResolvedSearchPaths = [&](const char* name) {
// loader by getting the path to the dawn native library and traversing relative from there. std::string list;
// This has implications for dawn tests because some of them are linking statically to bool first = true;
// dawn_native which means the "module" is actually the test as well. If the directory for (const std::string& path : searchPaths) {
// location of the tests change w.r.t the shared lib then this may break. Essentially we are if (!first) {
// assuming that our custom built Vulkan loader will always be in the same directory as the list += ", ";
// shared dawn native library and all test binaries that link statically. }
const std::string resolvedVulkanLibPath = GetModuleDirectory() + kVulkanLibName; first = false;
#else list += (path + name);
const std::string resolvedVulkanLibPath = kVulkanLibName; }
#endif // defined(DAWN_ENABLE_VULKAN_LOADER) 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) { switch (icd) {
case ICD::None: { case ICD::None: {
if (!mVulkanLib.Open(resolvedVulkanLibPath)) { DAWN_TRY(LoadVulkan(kVulkanLibName));
return DAWN_FORMAT_INTERNAL_ERROR("Couldn't load %s.", resolvedVulkanLibPath); // Succesfully loaded driver; break.
}
break; break;
} }
case ICD::SwiftShader: { case ICD::SwiftShader: {
#if defined(DAWN_ENABLE_SWIFTSHADER) #if defined(DAWN_ENABLE_SWIFTSHADER)
// First try to load the system Vulkan driver, if that fails, try to load with DAWN_TRY(LoadVulkan(kSwiftshaderLibName));
// 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; 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);
#endif // defined(DAWN_ENABLE_SWIFTSHADER) #endif // defined(DAWN_ENABLE_SWIFTSHADER)
// ICD::SwiftShader should not be passed if SwiftShader is not enabled. // ICD::SwiftShader should not be passed if SwiftShader is not enabled.
UNREACHABLE(); UNREACHABLE();
} }
@ -228,7 +216,8 @@ namespace dawn::native::vulkan {
if (instance->IsBackendValidationEnabled()) { if (instance->IsBackendValidationEnabled()) {
#if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS) #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())) { if (!vkLayerPath.Set("VK_LAYER_PATH", vkDataDir.c_str())) {
return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH"); return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH");
} }

View File

@ -136,7 +136,7 @@ namespace dawn::native {
// for this instance. // for this instance.
class DAWN_NATIVE_EXPORT Instance { class DAWN_NATIVE_EXPORT Instance {
public: public:
Instance(); explicit Instance(const WGPUInstanceDescriptor* desc = nullptr);
~Instance(); ~Instance();
Instance(const Instance& other) = delete; Instance(const Instance& other) = delete;

View File

@ -40,10 +40,12 @@ TEST(SystemUtilsTests, SetEnvironmentVar) {
// Tests for GetExecutableDirectory // Tests for GetExecutableDirectory
TEST(SystemUtilsTests, GetExecutableDirectory) { TEST(SystemUtilsTests, GetExecutableDirectory) {
auto dir = GetExecutableDirectory();
// Test returned value is non-empty string // Test returned value is non-empty string
EXPECT_NE(GetExecutableDirectory(), ""); EXPECT_NE(dir, std::optional{std::string("")});
// Test last charecter in path ASSERT_NE(dir, std::nullopt);
EXPECT_EQ(GetExecutableDirectory().back(), *GetPathSeparator()); // Test last character in path
EXPECT_EQ(dir->back(), *GetPathSeparator());
} }
// Tests for ScopedEnvironmentVar // Tests for ScopedEnvironmentVar