2022-03-23 11:47:05 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
2022-03-22 12:38:28 +01:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup gpu
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "BKE_global.h"
|
|
|
|
|
|
|
|
|
|
#include "gpu_backend.hh"
|
|
|
|
|
#include "mtl_backend.hh"
|
|
|
|
|
|
|
|
|
|
#include "gpu_capabilities_private.hh"
|
|
|
|
|
#include "gpu_platform_private.hh"
|
|
|
|
|
|
|
|
|
|
#include <Cocoa/Cocoa.h>
|
|
|
|
|
#include <Metal/Metal.h>
|
|
|
|
|
#include <QuartzCore/QuartzCore.h>
|
|
|
|
|
|
|
|
|
|
namespace blender {
|
|
|
|
|
namespace gpu {
|
|
|
|
|
|
|
|
|
|
/* Global per-thread AutoReleasePool. */
|
|
|
|
|
thread_local NSAutoreleasePool *g_autoreleasepool = nil;
|
|
|
|
|
thread_local int g_autoreleasepool_depth = 0;
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Metal Backend
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
void MTLBackend::samplers_update(){
|
|
|
|
|
/* Placeholder -- Handled in MTLContext. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Context *MTLBackend::context_alloc(void *ghost_window)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLContext. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Batch *MTLBackend::batch_alloc()
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLBatch. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DrawList *MTLBackend::drawlist_alloc(int list_length)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLDrawList. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FrameBuffer *MTLBackend::framebuffer_alloc(const char *name)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLFrameBuffer. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
IndexBuf *MTLBackend::indexbuf_alloc()
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLIndexBuf. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QueryPool *MTLBackend::querypool_alloc()
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLQueryPool. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Shader *MTLBackend::shader_alloc(const char *name)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLShader. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Texture *MTLBackend::texture_alloc(const char *name)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLTexture. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLUniformBuf. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VertBuf *MTLBackend::vertbuf_alloc()
|
|
|
|
|
{
|
|
|
|
|
/* TODO(Metal): Implement MTLVertBuf. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MTLBackend::render_begin()
|
|
|
|
|
{
|
|
|
|
|
/* All Rendering must occur within a render boundary */
|
|
|
|
|
/* Track a call-count for nested calls, used to ensure we are inside an
|
|
|
|
|
* autoreleasepool from all rendering path. */
|
|
|
|
|
BLI_assert(g_autoreleasepool_depth >= 0);
|
|
|
|
|
|
|
|
|
|
if (g_autoreleasepool == nil) {
|
|
|
|
|
g_autoreleasepool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
}
|
|
|
|
|
g_autoreleasepool_depth++;
|
|
|
|
|
BLI_assert(g_autoreleasepool_depth > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MTLBackend::render_end()
|
|
|
|
|
{
|
|
|
|
|
/* If call-count reaches zero, drain auto release pool.
|
2022-03-23 12:15:50 +11:00
|
|
|
* Ensures temporary objects are freed within a frame's lifetime. */
|
2022-03-22 12:38:28 +01:00
|
|
|
BLI_assert(g_autoreleasepool != nil);
|
|
|
|
|
g_autoreleasepool_depth--;
|
|
|
|
|
BLI_assert(g_autoreleasepool_depth >= 0);
|
|
|
|
|
|
|
|
|
|
if (g_autoreleasepool_depth == 0) {
|
|
|
|
|
[g_autoreleasepool drain];
|
|
|
|
|
g_autoreleasepool = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MTLBackend::render_step()
|
|
|
|
|
{
|
|
|
|
|
/* Placeholder */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MTLBackend::is_inside_render_boundary()
|
|
|
|
|
{
|
|
|
|
|
return (g_autoreleasepool != nil);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Platform
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-03-23 12:15:50 +11:00
|
|
|
/* For Metal, platform_init needs to be called after MTLContext initialization. */
|
2022-03-22 12:38:28 +01:00
|
|
|
void MTLBackend::platform_init(MTLContext *ctx)
|
|
|
|
|
{
|
|
|
|
|
if (GPG.initialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eGPUDeviceType device = GPU_DEVICE_UNKNOWN;
|
|
|
|
|
eGPUOSType os = GPU_OS_ANY;
|
|
|
|
|
eGPUDriverType driver = GPU_DRIVER_ANY;
|
|
|
|
|
eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
|
|
|
|
|
|
|
|
|
|
BLI_assert(ctx);
|
|
|
|
|
id<MTLDevice> mtl_device = nil; /*ctx->device; TODO(Metal): Implement MTLContext. */
|
|
|
|
|
BLI_assert(device);
|
|
|
|
|
|
|
|
|
|
NSString *gpu_name = [mtl_device name];
|
|
|
|
|
const char *vendor = [gpu_name UTF8String];
|
|
|
|
|
const char *renderer = "Metal API";
|
|
|
|
|
const char *version = "1.2";
|
|
|
|
|
printf("METAL API - DETECTED GPU: %s\n", vendor);
|
|
|
|
|
|
|
|
|
|
/* macOS is the only supported platform, but check to ensure we are not building with Metal
|
|
|
|
|
* enablement on another platform. */
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
os = GPU_OS_WIN;
|
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
|
os = GPU_OS_MAC;
|
|
|
|
|
#else
|
|
|
|
|
os = GPU_OS_UNIX;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
BLI_assert(os == GPU_OS_MAC && "Platform must be macOS");
|
|
|
|
|
|
|
|
|
|
/* Determine Vendor from name. */
|
|
|
|
|
if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
|
|
|
|
|
device = GPU_DEVICE_ATI;
|
|
|
|
|
driver = GPU_DRIVER_OFFICIAL;
|
|
|
|
|
}
|
|
|
|
|
else if (strstr(vendor, "NVIDIA")) {
|
|
|
|
|
device = GPU_DEVICE_NVIDIA;
|
|
|
|
|
driver = GPU_DRIVER_OFFICIAL;
|
|
|
|
|
}
|
|
|
|
|
else if (strstr(vendor, "Intel")) {
|
|
|
|
|
device = GPU_DEVICE_INTEL;
|
|
|
|
|
driver = GPU_DRIVER_OFFICIAL;
|
|
|
|
|
}
|
|
|
|
|
else if (strstr(vendor, "Apple") || strstr(vendor, "APPLE")) {
|
|
|
|
|
/* Apple Silicon. */
|
|
|
|
|
device = GPU_DEVICE_APPLE;
|
|
|
|
|
driver = GPU_DRIVER_OFFICIAL;
|
|
|
|
|
}
|
|
|
|
|
else if (strstr(renderer, "Apple Software Renderer")) {
|
|
|
|
|
device = GPU_DEVICE_SOFTWARE;
|
|
|
|
|
driver = GPU_DRIVER_SOFTWARE;
|
|
|
|
|
}
|
|
|
|
|
else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
|
|
|
|
|
device = GPU_DEVICE_SOFTWARE;
|
|
|
|
|
driver = GPU_DRIVER_SOFTWARE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
|
|
|
|
|
printf("Detected configuration:\n");
|
|
|
|
|
printf("Vendor: %s\n", vendor);
|
|
|
|
|
printf("Renderer: %s\n", renderer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GPG.init(device, os, driver, support_level, GPU_BACKEND_METAL, vendor, renderer, version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MTLBackend::platform_exit()
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(GPG.initialized);
|
|
|
|
|
GPG.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Capabilities
|
|
|
|
|
* \{ */
|
|
|
|
|
MTLCapabilities MTLBackend::capabilities = {};
|
|
|
|
|
|
|
|
|
|
static const char *mtl_extensions_get_null(int i)
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool supports_barycentric_whitelist(id<MTLDevice> device)
|
|
|
|
|
{
|
|
|
|
|
NSString *gpu_name = [device name];
|
|
|
|
|
BLI_assert([gpu_name length]);
|
|
|
|
|
const char *vendor = [gpu_name UTF8String];
|
|
|
|
|
|
|
|
|
|
/* Verify GPU support. */
|
|
|
|
|
bool supported_gpu = [device supportsFamily:MTLGPUFamilyMac2];
|
|
|
|
|
bool should_support_barycentrics = false;
|
|
|
|
|
|
|
|
|
|
/* Known good configs. */
|
|
|
|
|
if (strstr(vendor, "AMD") || strstr(vendor, "Apple") || strstr(vendor, "APPLE")) {
|
|
|
|
|
should_support_barycentrics = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Explicit support for Intel-based platforms. */
|
|
|
|
|
if ((strstr(vendor, "Intel") || strstr(vendor, "INTEL"))) {
|
|
|
|
|
should_support_barycentrics = true;
|
|
|
|
|
}
|
|
|
|
|
return supported_gpu && should_support_barycentrics;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MTLBackend::metal_is_supported()
|
|
|
|
|
{
|
|
|
|
|
/* Device compatibility information using Metal Feature-set tables.
|
|
|
|
|
* See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
|
|
|
|
|
|
|
|
|
|
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
|
|
|
|
|
|
|
|
|
|
/* Metal Viewport requires macOS Version 10.15 onwards. */
|
|
|
|
|
bool supported_os_version = version.majorVersion >= 11 ||
|
|
|
|
|
(version.majorVersion == 10 ? version.minorVersion >= 15 : false);
|
|
|
|
|
if (!supported_os_version) {
|
|
|
|
|
printf(
|
|
|
|
|
"OS Version too low to run minimum required metal version. Required at least 10.15, got "
|
|
|
|
|
"%ld.%ld \n",
|
|
|
|
|
(long)version.majorVersion,
|
|
|
|
|
(long)version.minorVersion);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (@available(macOS 10.15, *)) {
|
|
|
|
|
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
|
|
|
|
|
|
|
|
|
/* Debug: Enable low power GPU with Environment Var: METAL_FORCE_INTEL. */
|
|
|
|
|
static const char *forceIntelStr = getenv("METAL_FORCE_INTEL");
|
|
|
|
|
bool forceIntel = forceIntelStr ? (atoi(forceIntelStr) != 0) : false;
|
|
|
|
|
|
|
|
|
|
if (forceIntel) {
|
|
|
|
|
NSArray<id<MTLDevice>> *allDevices = MTLCopyAllDevices();
|
|
|
|
|
for (id<MTLDevice> _device in allDevices) {
|
|
|
|
|
if (_device.lowPower) {
|
|
|
|
|
device = _device;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Metal Viewport requires argument buffer tier-2 support and Barycentric Coordinates.
|
|
|
|
|
* These are available on most hardware configurations supporting Metal 2.2. */
|
|
|
|
|
bool supports_argument_buffers_tier2 = ([device argumentBuffersSupport] ==
|
|
|
|
|
MTLArgumentBuffersTier2);
|
|
|
|
|
bool supports_barycentrics = [device supportsShaderBarycentricCoordinates] ||
|
|
|
|
|
supports_barycentric_whitelist(device);
|
|
|
|
|
bool supported_metal_version = [device supportsFamily:MTLGPUFamilyMac2];
|
|
|
|
|
|
|
|
|
|
bool result = supports_argument_buffers_tier2 && supports_barycentrics &&
|
|
|
|
|
supported_os_version && supported_metal_version;
|
|
|
|
|
|
|
|
|
|
if (!supports_argument_buffers_tier2) {
|
|
|
|
|
printf("[Metal] Device does not support argument buffers tier 2\n");
|
|
|
|
|
}
|
|
|
|
|
if (!supports_barycentrics) {
|
|
|
|
|
printf("[Metal] Device does not support barycentrics coordinates\n");
|
|
|
|
|
}
|
|
|
|
|
if (!supported_metal_version) {
|
|
|
|
|
printf("[Metal] Device does not support metal 2.2 or higher\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
printf("Device with name %s supports metal minimum requirements\n",
|
|
|
|
|
[[device name] UTF8String]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MTLBackend::capabilities_init(MTLContext *ctx)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(ctx);
|
|
|
|
|
id<MTLDevice> device = nil; /*ctx->device TODO(Metal): Implement MTLContext. */
|
|
|
|
|
BLI_assert(device);
|
|
|
|
|
|
2022-03-23 12:15:50 +11:00
|
|
|
/* Initialize Capabilities. */
|
2022-03-22 12:38:28 +01:00
|
|
|
MTLBackend::capabilities.supports_argument_buffers_tier2 = ([device argumentBuffersSupport] ==
|
2022-03-22 13:44:15 +01:00
|
|
|
MTLArgumentBuffersTier2);
|
2022-03-22 12:38:28 +01:00
|
|
|
MTLBackend::capabilities.supports_family_mac1 = [device supportsFamily:MTLGPUFamilyMac1];
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac2 = [device supportsFamily:MTLGPUFamilyMac2];
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac_catalyst1 = [device
|
|
|
|
|
supportsFamily:MTLGPUFamilyMacCatalyst1];
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac_catalyst2 = [device
|
|
|
|
|
supportsFamily:MTLGPUFamilyMacCatalyst2];
|
|
|
|
|
|
|
|
|
|
/* Common Global Capabilities. */
|
|
|
|
|
GCaps.max_texture_size = ([device supportsFamily:MTLGPUFamilyApple3] ||
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac1) ?
|
|
|
|
|
16384 :
|
|
|
|
|
8192;
|
|
|
|
|
GCaps.max_texture_3d_size = 2048;
|
|
|
|
|
GCaps.max_texture_layers = 2048;
|
|
|
|
|
GCaps.max_textures = (MTLBackend::capabilities.supports_family_mac1) ?
|
|
|
|
|
128 :
|
|
|
|
|
(([device supportsFamily:MTLGPUFamilyApple4]) ? 96 : 31);
|
|
|
|
|
if (GCaps.max_textures <= 32) {
|
|
|
|
|
BLI_assert(false);
|
|
|
|
|
}
|
|
|
|
|
GCaps.max_samplers = (MTLBackend::capabilities.supports_argument_buffers_tier2) ? 1024 : 16;
|
|
|
|
|
|
|
|
|
|
GCaps.max_textures_vert = GCaps.max_textures;
|
|
|
|
|
GCaps.max_textures_geom = 0; /* N/A geometry shaders not supported. */
|
|
|
|
|
GCaps.max_textures_frag = GCaps.max_textures;
|
|
|
|
|
|
|
|
|
|
/* Conservative uniform data limit is 4KB per-stage -- This is the limit of setBytes.
|
|
|
|
|
* MTLBuffer path is also supported but not as efficient. */
|
2022-03-22 13:44:15 +01:00
|
|
|
GCaps.max_uniforms_vert = 1024;
|
2022-03-22 12:38:28 +01:00
|
|
|
GCaps.max_uniforms_frag = 1024;
|
|
|
|
|
|
|
|
|
|
GCaps.max_batch_indices = 1 << 31;
|
|
|
|
|
GCaps.max_batch_vertices = 1 << 31;
|
|
|
|
|
GCaps.max_vertex_attribs = 31;
|
|
|
|
|
GCaps.max_varying_floats = 60;
|
|
|
|
|
|
|
|
|
|
/* Feature support */
|
|
|
|
|
GCaps.mem_stats_support = false;
|
|
|
|
|
GCaps.shader_image_load_store_support = ([device supportsFamily:MTLGPUFamilyApple3] ||
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac1 ||
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac2);
|
|
|
|
|
GCaps.compute_shader_support = false; /* TODO(Metal): Add compute support. */
|
|
|
|
|
GCaps.shader_storage_buffer_objects_support =
|
2022-03-31 13:00:10 +11:00
|
|
|
false; /* TODO(Metal): implement Storage Buffer support. */
|
2022-03-22 12:38:28 +01:00
|
|
|
|
2022-03-22 13:44:15 +01:00
|
|
|
/* Maximum buffer bindings: 31. Consider required slot for uniforms/UBOs/Vertex attributes.
|
2022-03-22 12:38:28 +01:00
|
|
|
* Can use argument buffers if a higher limit is required. */
|
|
|
|
|
GCaps.max_shader_storage_buffer_bindings = 24;
|
|
|
|
|
|
|
|
|
|
if (GCaps.compute_shader_support) {
|
|
|
|
|
GCaps.max_work_group_count[0] = 65535;
|
|
|
|
|
GCaps.max_work_group_count[1] = 65535;
|
|
|
|
|
GCaps.max_work_group_count[2] = 65535;
|
|
|
|
|
|
2022-03-22 13:44:15 +01:00
|
|
|
/* In Metal, total_thread_count is 512 or 1024, such that
|
2022-03-22 12:38:28 +01:00
|
|
|
* threadgroup `width*height*depth <= total_thread_count` */
|
2022-03-22 13:44:15 +01:00
|
|
|
unsigned int max_threads_per_threadgroup_per_dim =
|
|
|
|
|
([device supportsFamily:MTLGPUFamilyApple4] ||
|
|
|
|
|
MTLBackend::capabilities.supports_family_mac1) ?
|
|
|
|
|
1024 :
|
|
|
|
|
512;
|
2022-03-22 12:38:28 +01:00
|
|
|
GCaps.max_work_group_size[0] = max_threads_per_threadgroup_per_dim;
|
|
|
|
|
GCaps.max_work_group_size[1] = max_threads_per_threadgroup_per_dim;
|
|
|
|
|
GCaps.max_work_group_size[2] = max_threads_per_threadgroup_per_dim;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GCaps.transform_feedback_support = true;
|
|
|
|
|
|
|
|
|
|
/* OPENGL Related workarounds -- none needed for Metal. */
|
|
|
|
|
GCaps.extensions_len = 0;
|
|
|
|
|
GCaps.extension_get = mtl_extensions_get_null;
|
|
|
|
|
GCaps.mip_render_workaround = false;
|
|
|
|
|
GCaps.depth_blitting_workaround = false;
|
|
|
|
|
GCaps.use_main_context_workaround = false;
|
|
|
|
|
GCaps.broken_amd_driver = false;
|
|
|
|
|
|
|
|
|
|
/* Metal related workarounds. */
|
2022-03-23 12:15:50 +11:00
|
|
|
/* Minimum per-vertex stride is 4 bytes in Metal.
|
|
|
|
|
* A bound vertex buffer must contribute at least 4 bytes per vertex. */
|
2022-03-22 12:38:28 +01:00
|
|
|
GCaps.minimum_per_vertex_stride = 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
} // gpu
|
|
|
|
|
} // blender
|