Fix Vulkan icd selection

#105 burmalda ili govnolda
This commit is contained in:
komaruworld committed 2026-09-04 18:08:36 +03:00
1 parent 3890efbaaa
commit a6fdb5b0cd
4 files changed
+182 -17

No files matched your search

+10
View File
@@ -1,13 +1,23 @@
#ifndef MOCKTAIL_RUNTIME_GRAPHICS_LAUNCH_POLICY_H_
#define MOCKTAIL_RUNTIME_GRAPHICS_LAUNCH_POLICY_H_
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
#include "runtime/runtime_config.h"
namespace mocktail {
namespace runtime {
// Readable ICD manifest for `vendor` that this build can actually load, or an
// empty path. Directories are searched in order; a manifest built for another
// architecture is never selected.
std::string SelectVulkanIcdManifest(
const std::vector<std::filesystem::path>& directories,
std::string_view vendor);
// Publishes the resolved graphics backend before the managed payload updater
// starts. OpenGL is a strict system EGL/GLES path; it never silently retries
// through ANGLE/Vulkan or accepts a window without a real graphics context.
+63 -17
View File
@@ -2,12 +2,14 @@
#include "runtime/frame_rate_policy.h"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <string>
#include <string_view>
#include <system_error>
#include <unistd.h>
#include <vector>
@@ -28,12 +30,28 @@ constexpr const char* kIcdDirectories[] = {
"/usr/local/share/vulkan/icd.d",
};
constexpr char kNativeArchitecture[] = "x86_64";
constexpr const char* kForeignArchitectures[] = {
"i686", "i586", "i486", "i386", "x86.", "aarch64",
"arm64", "armhf", "armv7", "ppc64", "riscv64", "s390x",
};
struct HostGpus {
bool intel = false;
bool nvidia = false;
bool amd = false;
};
bool ForeignArchitecture(const std::string& name) {
for (const char* architecture : kForeignArchitectures) {
if (name.find(architecture) != std::string::npos) {
return true;
}
}
return false;
}
bool SetValue(const char* name, const std::string& value, std::string* error) {
if (setenv(name, value.c_str(), 1) == 0) {
return true;
@@ -110,25 +128,11 @@ std::string FindIcdFile(const char* filename_needle) {
if (filename_needle == nullptr || filename_needle[0] == '\0') {
return {};
}
std::vector<std::filesystem::path> directories;
for (const char* directory : kIcdDirectories) {
std::error_code error;
for (std::filesystem::directory_iterator iterator(directory, error), end;
!error && iterator != end; iterator.increment(error)) {
if (!iterator->is_regular_file(error)) {
continue;
}
const std::string name = iterator->path().filename().string();
if (name.find(".json") == std::string::npos ||
name.find(filename_needle) == std::string::npos) {
continue;
}
const std::string path = iterator->path().string();
if (access(path.c_str(), R_OK) == 0) {
return path;
}
}
directories.emplace_back(directory);
}
return {};
return SelectVulkanIcdManifest(directories, filename_needle);
}
std::string SelectHardwareIcd(const HostGpus& gpus) {
@@ -209,6 +213,48 @@ bool ApplyVulkanIcdPolicy(const HostGpus& gpus, std::string* error) {
} // namespace
std::string SelectVulkanIcdManifest(
const std::vector<std::filesystem::path>& directories,
std::string_view vendor) {
if (vendor.empty()) {
return {};
}
for (const std::filesystem::path& directory : directories) {
std::error_code error;
std::vector<std::string> names;
for (std::filesystem::directory_iterator iterator(directory, error), end;
!error && iterator != end; iterator.increment(error)) {
if (!iterator->is_regular_file(error)) {
continue;
}
const std::string name = iterator->path().filename().string();
if (name.find(".json") == std::string::npos ||
name.find(vendor) == std::string::npos || ForeignArchitecture(name)) {
continue;
}
names.push_back(name);
}
std::sort(names.begin(), names.end());
std::string generic;
for (const std::string& name : names) {
const std::string path = (directory / name).string();
if (access(path.c_str(), R_OK) != 0) {
continue;
}
if (name.find(kNativeArchitecture) != std::string::npos) {
return path;
}
if (generic.empty()) {
generic = path;
}
}
if (!generic.empty()) {
return generic;
}
}
return {};
}
bool ApplyGraphicsLaunchPolicy(const RuntimeConfig& config,
std::string* error) {
if (config.graphics_backend() == GraphicsBackend::kUnknown) {
+7
View File
@@ -71,6 +71,12 @@ target_link_libraries(support_bundle_runtime_test PRIVATE
GTest::gtest_main
)
add_executable(vulkan_icd_selection_test vulkan_icd_selection_test.cc)
target_link_libraries(vulkan_icd_selection_test PRIVATE
Mocktail::Runtime
GTest::gtest_main
)
add_executable(session_log_test session_log_test.cc)
target_link_libraries(session_log_test PRIVATE
Mocktail::Runtime
@@ -844,6 +850,7 @@ gtest_discover_tests(payload_update_preflight_test)
gtest_discover_tests(apk_provider_test)
gtest_discover_tests(native_update_test)
gtest_discover_tests(support_bundle_runtime_test)
gtest_discover_tests(vulkan_icd_selection_test)
gtest_discover_tests(session_log_test)
gtest_discover_tests(failure_dialog_test)
gtest_discover_tests(frame_rate_policy_test)
+102
View File
@@ -0,0 +1,102 @@
#include <gtest/gtest.h>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <string>
#include <system_error>
#include <vector>
#include "runtime/graphics_launch_policy.h"
namespace mocktail {
namespace runtime {
namespace {
class TemporaryDirectory final {
public:
TemporaryDirectory() {
char pattern[] = "/tmp/mocktail_icd_XXXXXX";
const char* created = mkdtemp(pattern);
if (created != nullptr) root_ = created;
}
~TemporaryDirectory() {
std::error_code error;
std::filesystem::remove_all(root_, error);
}
const std::filesystem::path& root() const { return root_; }
private:
std::filesystem::path root_;
};
void WriteManifest(const std::filesystem::path& path) {
std::filesystem::create_directories(path.parent_path());
std::ofstream(path) << R"({"ICD":{"library_path":"libGLX_nvidia.so.0"}})";
}
TEST(VulkanIcdSelectionTest, PrefersTheNativeArchitectureManifest) {
TemporaryDirectory temporary;
WriteManifest(temporary.root() / "nvidia_icd.i686.json");
WriteManifest(temporary.root() / "nvidia_icd.x86_64.json");
EXPECT_EQ(SelectVulkanIcdManifest({temporary.root()}, "nvidia_icd"),
(temporary.root() / "nvidia_icd.x86_64.json").string());
}
TEST(VulkanIcdSelectionTest, NeverSelectsAForeignArchitectureManifest) {
TemporaryDirectory temporary;
WriteManifest(temporary.root() / "nvidia_icd.i686.json");
WriteManifest(temporary.root() / "radeon_icd.aarch64.json");
EXPECT_TRUE(
SelectVulkanIcdManifest({temporary.root()}, "nvidia_icd").empty());
EXPECT_TRUE(
SelectVulkanIcdManifest({temporary.root()}, "radeon_icd").empty());
}
TEST(VulkanIcdSelectionTest, AcceptsAnUnqualifiedManifest) {
TemporaryDirectory temporary;
WriteManifest(temporary.root() / "nvidia_icd.json");
EXPECT_EQ(SelectVulkanIcdManifest({temporary.root()}, "nvidia_icd"),
(temporary.root() / "nvidia_icd.json").string());
}
TEST(VulkanIcdSelectionTest, KeepsDirectoryPrecedenceOverName) {
TemporaryDirectory temporary;
const std::filesystem::path first = temporary.root() / "first";
const std::filesystem::path second = temporary.root() / "second";
WriteManifest(first / "nvidia_icd.json");
WriteManifest(second / "nvidia_icd.x86_64.json");
EXPECT_EQ(SelectVulkanIcdManifest({first, second}, "nvidia_icd"),
(first / "nvidia_icd.json").string());
}
TEST(VulkanIcdSelectionTest, IsStableWhateverTheDirectoryOrderIs) {
TemporaryDirectory temporary;
WriteManifest(temporary.root() / "nvidia_icd.x86_64.json");
WriteManifest(temporary.root() / "nvidia_icd.json");
WriteManifest(temporary.root() / "nvidia_icd.i686.json");
const std::string selected =
SelectVulkanIcdManifest({temporary.root()}, "nvidia_icd");
EXPECT_EQ(selected, (temporary.root() / "nvidia_icd.x86_64.json").string());
EXPECT_EQ(selected, SelectVulkanIcdManifest({temporary.root()},
"nvidia_icd"));
}
TEST(VulkanIcdSelectionTest, IgnoresAMissingDirectory) {
TemporaryDirectory temporary;
EXPECT_TRUE(SelectVulkanIcdManifest({temporary.root() / "absent"},
"nvidia_icd")
.empty());
EXPECT_TRUE(SelectVulkanIcdManifest({temporary.root()}, "").empty());
}
} // namespace
} // namespace runtime
} // namespace mocktail