Adds features to enable loading/saving metallibs to blobcache

CompileMetalShaders results in the appropriate command line tool
being run to generate metallib blobs from shader source.

DisableProgramCaching results in not saving programs to BlobCache

LoadMetalShadersFromBlobCache results in trying to load metallibs
from BlobCache.

Bug: chromium:1423136
Change-Id: I01a4d7a5d60ed5ac978fb99db01b741e0f19e76b
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4434293
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
This commit is contained in:
Scott Violet
2023-04-17 10:08:18 -07:00
committed by Angle LUCI CQ
parent 443ac5b483
commit 65f4d2a45c
20 changed files with 600 additions and 66 deletions

View File

@@ -283,6 +283,18 @@ struct FeaturesMtl : FeatureSetBase
"disableStagedInitializationOfPackedTextureFormats", FeatureCategory::MetalFeatures,
"Staged GPU upload of some packed texture formats such as RGB9_E5 fail on Intel GPUs.",
&members, "http://anglebug.com/8092"};
FeatureInfo compileMetalShaders = {
"compileMetalShaders", FeatureCategory::MetalFeatures,
"Compiles metal shaders using command line tools and saves to BlobCache. "
"Requires using --no-sandbox and disabling enableParallelMtlLibraryCompilation.",
&members, "http://crbug.com/1423136"};
FeatureInfo loadMetalShadersFromBlobCache = {
"loadMetalShadersFromBlobCache", FeatureCategory::MetalFeatures,
"Loads metal shaders from blob cache. Useful if compile_metal_shaders was used to "
"generate shaders.",
&members, "http://crbug.com/1423136"};
};
inline FeaturesMtl::FeaturesMtl() = default;

View File

@@ -117,6 +117,10 @@ struct FrontendFeatures : FeatureSetBase
"enableShaderSubstitution", FeatureCategory::FrontendWorkarounds,
"Check the filesystem for shaders to use instead of those provided through glShaderSource",
&members, "http://anglebug.com/7761"};
FeatureInfo disableProgramCaching = {"disableProgramCaching", FeatureCategory::FrontendFeatures,
"Disables saving programs to the cache", &members,
"http://anglebug.com/1423136"};
};
inline FrontendFeatures::FrontendFeatures() = default;

View File

@@ -145,6 +145,14 @@
"Check the filesystem for shaders to use instead of those provided through glShaderSource"
],
"issue": "http://anglebug.com/7761"
},
{
"name": "disable_program_caching",
"category": "Features",
"description": [
"Disables saving programs to the cache"
],
"issue": "http://anglebug.com/1423136"
}
]
}

View File

@@ -312,6 +312,24 @@
"Staged GPU upload of some packed texture formats such as RGB9_E5 fail on Intel GPUs."
],
"issue": "http://anglebug.com/8092"
},
{
"name": "compile_metal_shaders",
"category": "Features",
"description": [
"Compiles metal shaders using command line tools and saves to BlobCache. ",
"Requires using --no-sandbox and disabling enableParallelMtlLibraryCompilation."
],
"issue": "http://crbug.com/1423136"
},
{
"name": "load_metal_shaders_from_blob_cache",
"category": "Features",
"description": [
"Loads metal shaders from blob cache. Useful if compile_metal_shaders was used to ",
"generate shaders."
],
"issue": "http://crbug.com/1423136"
}
]
}

View File

@@ -4,25 +4,25 @@
"include/platform/FeaturesGL_autogen.h":
"331b1660b3982f6540e362764df26021",
"include/platform/FeaturesMtl_autogen.h":
"d5e309c1c728d714f94a4e764523526a",
"8ac8af62087b103ce3744d55c18a41f5",
"include/platform/FeaturesVk_autogen.h":
"f493869b097defc3557cee62198a549e",
"include/platform/FrontendFeatures_autogen.h":
"be41034e621326dd51e86b139705ea39",
"391ebdb90344949e7060cb867a456511",
"include/platform/d3d_features.json":
"c3f7694511855304b3f678a6ad461d1e",
"include/platform/frontend_features.json":
"c2ace937cdaa65aedb66e7be5e5b761d",
"db98716ec9e23fc17f32bf9bc53cc331",
"include/platform/gen_features.py":
"062989f7a8f3ff3b383f98fc8908dc33",
"include/platform/gl_features.json":
"cef92e7ede4c824b8bae37123401d354",
"include/platform/mtl_features.json":
"111f543f723b51f05c6e2b32c745c30c",
"e859b9d81f83924af68a3154ff86463e",
"include/platform/vk_features.json":
"d68423837be648217b7c7777fe9519df",
"util/angle_features_autogen.cpp":
"948adc4d0c0177ef480d681a5e72e226",
"e764afd630db8ba3d9cb5f223797ce8c",
"util/angle_features_autogen.h":
"e5a41f9a8cf70b30ef7db6cce5c6697c"
"da92d495a307f96e98f66bf475c85a19"
}

View File

@@ -16,59 +16,6 @@ namespace angle
namespace base
{
// Implementation of SHA-1. Only handles data in byte-sized blocks,
// which simplifies the code a fair bit.
// Identifier names follow notation in FIPS PUB 180-3, where you'll
// also find a description of the algorithm:
// http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf
// Usage example:
//
// SecureHashAlgorithm sha;
// while(there is data to hash)
// sha.Update(moredata, size of data);
// sha.Final();
// memcpy(somewhere, sha.Digest(), 20);
//
// to reuse the instance of sha, call sha.Init();
// TODO(jhawkins): Replace this implementation with a per-platform
// implementation using each platform's crypto library. See
// http://crbug.com/47218
class SecureHashAlgorithm
{
public:
SecureHashAlgorithm() { Init(); }
static const int kDigestSizeBytes;
void Init();
void Update(const void *data, size_t nbytes);
void Final();
// 20 bytes of message digest.
const unsigned char *Digest() const { return reinterpret_cast<const unsigned char *>(H); }
private:
void Pad();
void Process();
uint32_t A, B, C, D, E;
uint32_t H[5];
union
{
uint32_t W[80];
uint8_t M[64];
};
uint32_t cursor;
uint64_t l;
};
static inline uint32_t f(uint32_t t, uint32_t B, uint32_t C, uint32_t D)
{
if (t < 20)
@@ -224,6 +171,13 @@ void SecureHashAlgorithm::Process()
cursor = 0;
}
std::array<uint8_t, kSHA1Length> SecureHashAlgorithm::DigestAsArray() const
{
std::array<uint8_t, kSHA1Length> digest;
memcpy(digest.data(), Digest(), SecureHashAlgorithm::kDigestSizeBytes);
return digest;
}
std::string SHA1HashString(const std::string &str)
{
char hash[SecureHashAlgorithm::kDigestSizeBytes];

View File

@@ -7,6 +7,7 @@
#include <stddef.h>
#include <array>
#include <string>
#include "anglebase/base_export.h"
@@ -29,6 +30,61 @@ ANGLEBASE_EXPORT std::string SHA1HashString(const std::string &str);
// in |hash|. |hash| must be kSHA1Length bytes long.
ANGLEBASE_EXPORT void SHA1HashBytes(const unsigned char *data, size_t len, unsigned char *hash);
// Implementation of SHA-1. Only handles data in byte-sized blocks,
// which simplifies the code a fair bit.
// Identifier names follow notation in FIPS PUB 180-3, where you'll
// also find a description of the algorithm:
// http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf
// Usage example:
//
// SecureHashAlgorithm sha;
// while(there is data to hash)
// sha.Update(moredata, size of data);
// sha.Final();
// memcpy(somewhere, sha.Digest(), 20);
//
// to reuse the instance of sha, call sha.Init();
// TODO(jhawkins): Replace this implementation with a per-platform
// implementation using each platform's crypto library. See
// http://crbug.com/47218
class SecureHashAlgorithm
{
public:
SecureHashAlgorithm() { Init(); }
static const int kDigestSizeBytes;
void Init();
void Update(const void *data, size_t nbytes);
void Final();
// 20 bytes of message digest.
const unsigned char *Digest() const { return reinterpret_cast<const unsigned char *>(H); }
std::array<uint8_t, kSHA1Length> DigestAsArray() const;
private:
void Pad();
void Process();
uint32_t A, B, C, D, E;
uint32_t H[5];
union
{
uint32_t W[80];
uint8_t M[64];
};
uint32_t cursor;
uint64_t l;
};
} // namespace base
} // namespace angle

View File

@@ -47,6 +47,12 @@ Optional<std::string> GetTempDirectory();
Optional<std::string> CreateTemporaryFileInDirectory(const std::string &directory);
Optional<std::string> CreateTemporaryFile();
#if defined(ANGLE_PLATFORM_POSIX)
// Same as CreateTemporaryFileInDirectory(), but allows for supplying an extension.
Optional<std::string> CreateTemporaryFileInDirectoryWithExtension(const std::string &directory,
const std::string &extension);
#endif
// Get absolute time in seconds. Use this function to get an absolute time with an unknown origin.
double GetCurrentSystemTime();
// Get CPU time for current process in seconds.

View File

@@ -302,12 +302,18 @@ Optional<std::string> GetTempDirectory()
Optional<std::string> CreateTemporaryFileInDirectory(const std::string &directory)
{
std::string tempFileTemplate = directory + "/.angle.XXXXXX";
return CreateTemporaryFileInDirectoryWithExtension(directory, std::string());
}
Optional<std::string> CreateTemporaryFileInDirectoryWithExtension(const std::string &directory,
const std::string &extension)
{
std::string tempFileTemplate = directory + "/.angle.XXXXXX" + extension;
char tempFile[1000];
strcpy(tempFile, tempFileTemplate.c_str());
int fd = mkstemp(tempFile);
int fd = mkstemps(tempFile, static_cast<int>(extension.size()));
close(fd);
if (fd != -1)

View File

@@ -1333,7 +1333,7 @@ void Program::resolveLinkImpl(const Context *context)
std::lock_guard<std::mutex> cacheLock(context->getProgramCacheMutex());
MemoryProgramCache *cache = context->getMemoryProgramCache();
// TODO: http://anglebug.com/4530: Enable program caching for separable programs
if (cache && !isSeparable() &&
if (cache && !isSeparable() && !context->getFrontendFeatures().disableProgramCaching.enabled &&
(mState.mExecutable->mLinkedTransformFeedbackVaryings.empty() ||
!context->getFrontendFeatures().disableProgramCachingForTransformFeedback.enabled))
{

View File

@@ -75,6 +75,8 @@ metal_backend_sources = [
"mtl_state_cache.mm",
"mtl_utils.h",
"mtl_utils.mm",
"process.cpp",
"process.h",
"renderermtl_utils.cpp",
"renderermtl_utils.h",
"shaders/constants.h",

View File

@@ -53,6 +53,9 @@ class ContextDevice final : public WrappedObject<id<MTLDevice>>, angle::NonCopya
MTLCompileOptions *options,
__autoreleasing NSError **error) const;
AutoObjCPtr<id<MTLLibrary>> newLibraryWithData(dispatch_data_t data,
__autoreleasing NSError **error) const;
AutoObjCPtr<id<MTLDepthStencilState>> newDepthStencilStateWithDescriptor(
MTLDepthStencilDescriptor *descriptor) const;

View File

@@ -114,6 +114,12 @@ AutoObjCPtr<id<MTLLibrary>> ContextDevice::newLibraryWithSource(
return adoptObjCObj([get() newLibraryWithSource:source options:options error:error]);
}
AutoObjCPtr<id<MTLLibrary>> ContextDevice::newLibraryWithData(dispatch_data_t data,
__autoreleasing NSError **error) const
{
return adoptObjCObj([get() newLibraryWithData:data error:error]);
}
AutoObjCPtr<id<MTLDepthStencilState>> ContextDevice::newDepthStencilStateWithDescriptor(
MTLDepthStencilDescriptor *descriptor) const
{

View File

@@ -9,9 +9,21 @@
#include "libANGLE/renderer/metal/mtl_library_cache.h"
#include <stdio.h>
#include <limits>
#include "common/MemoryBuffer.h"
#include "common/angleutils.h"
#include "common/hash_utils.h"
#include "common/mathutil.h"
#include "common/string_utils.h"
#include "common/system_utils.h"
#include "libANGLE/histogram_macros.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/mtl_context_device.h"
#include "libANGLE/renderer/metal/process.h"
#include "platform/PlatformMethods.h"
namespace rx
{
@@ -38,6 +50,67 @@ AutoObjCPtr<id<MTLLibrary>> LibraryCache::get(const std::string &source,
}
}
namespace
{
// Reads a metallib file at the specified path.
angle::MemoryBuffer ReadMetallibFromFile(const std::string &path)
{
// TODO: optimize this to avoid the unnecessary strings.
std::string metallib;
if (!angle::ReadFileToString(path, &metallib))
{
FATAL() << "Failed reading back metallib";
}
angle::MemoryBuffer buffer;
if (!buffer.resize(metallib.size()))
{
FATAL() << "Failed to resize metallib buffer";
}
memcpy(buffer.data(), metallib.data(), metallib.size());
return buffer;
}
// Generates a key for the BlobCache based on the specified params.
egl::BlobCacheKey GenerateBlobCacheKeyForShaderLibrary(
const std::string &source,
const std::map<std::string, std::string> &macros,
bool enableFastMath)
{
angle::base::SecureHashAlgorithm sha1;
sha1.Update(source.c_str(), source.size());
const size_t macro_count = macros.size();
sha1.Update(&macro_count, sizeof(size_t));
for (const auto &macro : macros)
{
sha1.Update(macro.first.c_str(), macro.first.size());
sha1.Update(macro.second.c_str(), macro.second.size());
}
sha1.Update(&enableFastMath, sizeof(bool));
sha1.Final();
return sha1.DigestAsArray();
}
// Returns a new MTLLibrary from the specified data.
AutoObjCPtr<id<MTLLibrary>> NewMetalLibraryFromMetallib(ContextMtl *context,
const uint8_t *data,
size_t size)
{
ANGLE_MTL_OBJC_SCOPE
{
// Copy the data as the life of the BlobCache is not necessarily the same as that of the
// metallibrary.
auto mtl_data = dispatch_data_create(data, size, dispatch_get_main_queue(),
DISPATCH_DATA_DESTRUCTOR_DEFAULT);
NSError *nsError = nil;
return context->getMetalDevice().newLibraryWithData(mtl_data, &nsError);
}
}
} // namespace
AutoObjCPtr<id<MTLLibrary>> LibraryCache::getOrCompileShaderLibrary(
ContextMtl *context,
const std::string &source,
@@ -58,13 +131,50 @@ AutoObjCPtr<id<MTLLibrary>> LibraryCache::getOrCompileShaderLibrary(
// Lock this cache entry while compiling the shader. This causes other threads calling this
// function to wait and not duplicate the compilation.
std::lock_guard<std::mutex> entryLockGuard(entry.lock);
if (!entry.library)
if (entry.library)
{
entry.library = CreateShaderLibrary(context->getMetalDevice(), source, macros,
enableFastMath, errorOut);
return entry.library;
}
if (features.compileMetalShaders.enabled)
{
if (features.enableParallelMtlLibraryCompilation.enabled)
{
// When enableParallelMtlLibraryCompilation is enabled, compilation happens in the
// background. Chrome's ProgramCache only saves to disk when called at certain points,
// which are not present when compiling in the background.
FATAL() << "EnableParallelMtlLibraryCompilation is not compatible with "
"compileMetalShdaders";
}
std::string metallib_filename = CompileShaderLibraryToFile(source, macros, enableFastMath);
angle::MemoryBuffer memory_buffer = ReadMetallibFromFile(metallib_filename);
entry.library =
NewMetalLibraryFromMetallib(context, memory_buffer.data(), memory_buffer.size());
auto cache_key = GenerateBlobCacheKeyForShaderLibrary(source, macros, enableFastMath);
context->getDisplay()->getBlobCache()->put(cache_key, std::move(memory_buffer));
return entry.library;
}
if (features.loadMetalShadersFromBlobCache.enabled)
{
auto cache_key = GenerateBlobCacheKeyForShaderLibrary(source, macros, enableFastMath);
egl::BlobCache::Value value;
size_t buffer_size;
angle::ScratchBuffer scratch_buffer;
if (context->getDisplay()->getBlobCache()->get(&scratch_buffer, cache_key, &value,
&buffer_size))
{
entry.library = NewMetalLibraryFromMetallib(context, value.data(), value.size());
}
ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.MetalShaderInBlobCache", entry.library);
if (entry.library)
{
return entry.library;
}
}
entry.library =
CreateShaderLibrary(context->getMetalDevice(), source, macros, enableFastMath, errorOut);
return entry.library;
}

View File

@@ -114,6 +114,11 @@ AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDev
size_t binarySourceLen,
AutoObjCPtr<NSError *> *error);
// Compiles a shader library into a metallib file, returning the path to it.
std::string CompileShaderLibraryToFile(const std::string &source,
const std::map<std::string, std::string> &macros,
bool enableFastMath);
bool SupportsAppleGPUFamily(id<MTLDevice> device, uint8_t appleFamily);
bool SupportsMacGPUFamily(id<MTLDevice> device, uint8_t macFamily);

View File

@@ -14,12 +14,14 @@
#include <TargetConditionals.h>
#include "common/MemoryBuffer.h"
#include "common/string_utils.h"
#include "common/system_utils.h"
#include "gpu_info_util/SystemInfo_internal.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_render_utils.h"
#include "libANGLE/renderer/metal/process.h"
// Compiler can turn on programmatical frame capture in release build by defining
// ANGLE_METAL_FRAME_CAPTURE flag.
@@ -888,6 +890,75 @@ AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(
}
}
std::string CompileShaderLibraryToFile(const std::string &source,
const std::map<std::string, std::string> &macros,
bool enableFastMath)
{
auto tmpDir = angle::GetTempDirectory();
if (!tmpDir.valid())
{
FATAL() << "angle::GetTempDirectory() failed";
}
// NOTE: metal/metallib seem to require extensions, otherwise they interpret the files
// differently.
auto metalFileName =
angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metal");
auto airFileName = angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".air");
auto metallibFileName =
angle::CreateTemporaryFileInDirectoryWithExtension(tmpDir.value(), ".metallib");
if (!metalFileName.valid() || !airFileName.valid() || !metallibFileName.valid())
{
FATAL() << "Unable to generate temporary files for compiling metal";
}
// Save the source.
{
angle::SaveFileHelper saveFileHelper(metalFileName.value());
saveFileHelper << source;
}
// metal -> air
std::vector<std::string> metalToAirArgv{"/usr/bin/xcrun",
"/usr/bin/xcrun",
"-sdk",
"macosx",
"metal",
"-c",
metalFileName.value(),
"-o",
airFileName.value()};
// Macros are passed using `-D key=value`.
for (const auto &macro : macros)
{
metalToAirArgv.push_back("-D");
// TODO: not sure if this needs to escape strings or what (for example, might
// a space cause problems)?
metalToAirArgv.push_back(macro.first + "=" + macro.second);
}
// TODO: is this right, not sure if enableFastMath is same as -ffast-math.
if (enableFastMath)
{
metalToAirArgv.push_back("-ffast-math");
}
Process metalToAirProcess(metalToAirArgv);
int exitCode = -1;
if (!metalToAirProcess.DidLaunch() || !metalToAirProcess.WaitForExit(exitCode) || exitCode != 0)
{
FATAL() << "Generating air file failed";
}
// air -> metallib
const std::vector<std::string> airToMetallibArgv{
"xcrun", "/usr/bin/xcrun", "-sdk", "macosx",
"metallib", airFileName.value(), "-o", metallibFileName.value()};
Process air_to_metallib_process(airToMetallibArgv);
if (!air_to_metallib_process.DidLaunch() || !air_to_metallib_process.WaitForExit(exitCode) ||
exitCode != 0)
{
FATAL() << "Ggenerating metallib file failed";
}
return metallibFileName.value();
}
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDevice,
const uint8_t *binarySource,
size_t binarySourceLen,

View File

@@ -0,0 +1,227 @@
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// process.cpp:
// Process manages a child process. This is largely copied from chrome.
//
#include "libANGLE/renderer/metal/process.h"
#include <crt_externs.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <os/availability.h>
#include <spawn.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include "common/base/anglebase/logging.h"
#include "common/debug.h"
namespace rx
{
namespace mtl
{
namespace
{
// This code is copied from chrome's process launching code:
// (base/process/launch_mac.cc).
typedef pid_t ProcessId;
constexpr ProcessId kNullProcessId = 0;
// DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
// functions return an errno value, as opposed to setting errno directly. This
// macro emulates a DPCHECK().
#define DPSXCHECK(expr) \
do \
{ \
int rv = (expr); \
DCHECK(rv == 0); \
} while (0)
// DCHECK(rv == 0) << #expr << ": -" << rv << " " << strerror(rv);
class PosixSpawnAttr
{
public:
PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
posix_spawnattr_t *get() { return &attr_; }
private:
posix_spawnattr_t attr_;
};
class PosixSpawnFileActions
{
public:
PosixSpawnFileActions() { DPSXCHECK(posix_spawn_file_actions_init(&file_actions_)); }
PosixSpawnFileActions(const PosixSpawnFileActions &) = delete;
PosixSpawnFileActions &operator=(const PosixSpawnFileActions &) = delete;
~PosixSpawnFileActions() { DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_)); }
void Open(int filedes, const char *path, int mode)
{
DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path, mode, 0));
}
void Dup2(int filedes, int newfiledes)
{
DPSXCHECK(posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
}
void Inherit(int filedes)
{
DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
}
void Chdir(const char *path) API_AVAILABLE(macos(10.15))
{
DPSXCHECK(posix_spawn_file_actions_addchdir_np(&file_actions_, path));
}
const posix_spawn_file_actions_t *get() const { return &file_actions_; }
private:
posix_spawn_file_actions_t file_actions_;
};
// This is a slimmed down version of chrome's LaunchProcess().
ProcessId LaunchProcess(const std::vector<std::string> &argv)
{
PosixSpawnAttr attr;
DPSXCHECK(posix_spawnattr_setflags(attr.get(), POSIX_SPAWN_CLOEXEC_DEFAULT));
PosixSpawnFileActions file_actions;
// Process file descriptors for the child. By default, LaunchProcess will
// open stdin to /dev/null and inherit stdout and stderr.
bool inherit_stdout = true, inherit_stderr = true;
bool null_stdin = true;
if (null_stdin)
{
file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
}
if (inherit_stdout)
{
file_actions.Inherit(STDOUT_FILENO);
}
if (inherit_stderr)
{
file_actions.Inherit(STDERR_FILENO);
}
std::vector<char *> argv_cstr;
argv_cstr.reserve(argv.size() + 1);
for (const auto &arg : argv)
{
argv_cstr.push_back(const_cast<char *>(arg.c_str()));
}
argv_cstr.push_back(nullptr);
const bool clear_environment = false;
char *empty_environ = nullptr;
char **new_environ = clear_environment ? &empty_environ : *_NSGetEnviron();
const char *executable_path = argv_cstr[0];
pid_t pid;
// Use posix_spawnp as some callers expect to have PATH consulted.
int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(), &argv_cstr[0],
new_environ);
if (rv != 0)
{
FATAL() << "posix_spawnp failed";
return kNullProcessId;
}
return pid;
}
ProcessId GetParentProcessId(ProcessId process)
{
struct kinfo_proc info;
size_t length = sizeof(struct kinfo_proc);
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process};
if (sysctl(mib, 4, &info, &length, NULL, 0) < 0)
{
FATAL() << "sysctl failed";
return -1;
}
if (length == 0)
return -1;
return info.kp_eproc.e_ppid;
}
#define HANDLE_EINTR(x) \
({ \
decltype(x) eintr_wrapper_result; \
do \
{ \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
bool WaitForExitImpl(ProcessId pid, int &exit_code)
{
const ProcessId our_pid = getpid();
ASSERT(pid != our_pid);
const bool exited = (GetParentProcessId(pid) < 0);
int status;
const bool wait_result = HANDLE_EINTR(waitpid(pid, &status, 0)) > 0;
if (!wait_result)
{
return exited;
}
if (WIFSIGNALED(status))
{
exit_code = -1;
return true;
}
if (WIFEXITED(status))
{
exit_code = WEXITSTATUS(status);
return true;
}
return exited;
}
} // namespace
Process::Process(const std::vector<std::string> &argv) : pid_(LaunchProcess(argv)) {}
Process::~Process()
{
// TODO: figure out if should terminate/wait/whatever.
}
bool Process::WaitForExit(int &exit_code)
{
ASSERT(pid_ != kNullProcessId);
return WaitForExitImpl(pid_, exit_code);
}
bool Process::DidLaunch() const
{
return pid_ != kNullProcessId;
}
} // namespace mtl
} // namespace rx

View File

@@ -0,0 +1,40 @@
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// process.h:
// Process manages a child process. This is largely copied from chrome.
//
#ifndef LIBANGLE_RENDERER_METAL_PROCESS_H_
#define LIBANGLE_RENDERER_METAL_PROCESS_H_
#include <sys/types.h>
#include <string>
#include <vector>
namespace rx
{
namespace mtl
{
class Process
{
public:
Process(const std::vector<std::string> &argv);
Process(const Process &) = delete;
Process &operator=(const Process &) = delete;
~Process();
bool WaitForExit(int &exit_code);
bool DidLaunch() const;
private:
const pid_t pid_;
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_PROCESS_H_ */

View File

@@ -59,6 +59,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::ClampPointSize, "clampPointSize"},
{Feature::ClearToZeroOrOneBroken, "clearToZeroOrOneBroken"},
{Feature::ClipSrcRegionForBlitFramebuffer, "clipSrcRegionForBlitFramebuffer"},
{Feature::CompileMetalShaders, "compileMetalShaders"},
{Feature::CompressVertexData, "compressVertexData"},
{Feature::CopyIOSurfaceToNonIOSurfaceForReadOptimization,
"copyIOSurfaceToNonIOSurfaceForReadOptimization"},
@@ -76,6 +77,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::DisableMultisampledRenderToTexture, "disableMultisampledRenderToTexture"},
{Feature::DisableNativeParallelCompile, "disableNativeParallelCompile"},
{Feature::DisableProgramBinary, "disableProgramBinary"},
{Feature::DisableProgramCaching, "disableProgramCaching"},
{Feature::DisableProgramCachingForTransformFeedback,
"disableProgramCachingForTransformFeedback"},
{Feature::DisableProgrammableBlending, "disableProgrammableBlending"},
@@ -184,6 +186,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::LimitMaxDrawBuffersForTesting, "limitMaxDrawBuffersForTesting"},
{Feature::LimitMaxMSAASamplesTo4, "limitMaxMSAASamplesTo4"},
{Feature::LimitWebglMaxTextureSizeTo4096, "limitWebglMaxTextureSizeTo4096"},
{Feature::LoadMetalShadersFromBlobCache, "loadMetalShadersFromBlobCache"},
{Feature::LogMemoryReportCallbacks, "logMemoryReportCallbacks"},
{Feature::LogMemoryReportStats, "logMemoryReportStats"},
{Feature::LoseContextOnOutOfMemory, "loseContextOnOutOfMemory"},

View File

@@ -57,6 +57,7 @@ enum class Feature
ClampPointSize,
ClearToZeroOrOneBroken,
ClipSrcRegionForBlitFramebuffer,
CompileMetalShaders,
CompressVertexData,
CopyIOSurfaceToNonIOSurfaceForReadOptimization,
CopyTextureToBufferForReadOptimization,
@@ -73,6 +74,7 @@ enum class Feature
DisableMultisampledRenderToTexture,
DisableNativeParallelCompile,
DisableProgramBinary,
DisableProgramCaching,
DisableProgramCachingForTransformFeedback,
DisableProgrammableBlending,
DisableRasterizerOrderViews,
@@ -175,6 +177,7 @@ enum class Feature
LimitMaxDrawBuffersForTesting,
LimitMaxMSAASamplesTo4,
LimitWebglMaxTextureSizeTo4096,
LoadMetalShadersFromBlobCache,
LogMemoryReportCallbacks,
LogMemoryReportStats,
LoseContextOnOutOfMemory,