mirror of
https://github.com/godotengine/godot-angle-static.git
synced 2026-01-06 02:09:55 +03:00
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:
committed by
Angle LUCI CQ
parent
443ac5b483
commit
65f4d2a45c
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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> ¯os,
|
||||
bool enableFastMath)
|
||||
{
|
||||
angle::base::SecureHashAlgorithm sha1;
|
||||
sha1.Update(source.c_str(), source.size());
|
||||
const size_t macro_count = macros.size();
|
||||
sha1.Update(¯o_count, sizeof(size_t));
|
||||
for (const auto ¯o : 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> ¯os,
|
||||
bool enableFastMath);
|
||||
|
||||
bool SupportsAppleGPUFamily(id<MTLDevice> device, uint8_t appleFamily);
|
||||
|
||||
bool SupportsMacGPUFamily(id<MTLDevice> device, uint8_t macFamily);
|
||||
|
||||
@@ -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> ¯os,
|
||||
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 ¯o : 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,
|
||||
|
||||
227
src/libANGLE/renderer/metal/process.cpp
Normal file
227
src/libANGLE/renderer/metal/process.cpp
Normal 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
|
||||
40
src/libANGLE/renderer/metal/process.h
Normal file
40
src/libANGLE/renderer/metal/process.h
Normal 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_ */
|
||||
@@ -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"},
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user