Blender  V3.3
mtl_backend.mm
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "BKE_global.h"
8 
9 #include "gpu_backend.hh"
10 #include "mtl_backend.hh"
11 #include "mtl_context.hh"
12 #include "mtl_framebuffer.hh"
13 #include "mtl_query.hh"
14 #include "mtl_uniform_buffer.hh"
15 
17 #include "gpu_platform_private.hh"
18 
19 #include <Cocoa/Cocoa.h>
20 #include <Metal/Metal.h>
21 #include <QuartzCore/QuartzCore.h>
22 
23 namespace blender::gpu {
24 
25 /* Global per-thread AutoReleasePool. */
26 thread_local NSAutoreleasePool *g_autoreleasepool = nil;
27 thread_local int g_autoreleasepool_depth = 0;
28 
29 /* -------------------------------------------------------------------- */
34  /* Placeholder -- Handled in MTLContext. */
35 };
36 
37 Context *MTLBackend::context_alloc(void *ghost_window)
38 {
39  return new MTLContext(ghost_window);
40 };
41 
43 {
44  /* TODO(Metal): Implement MTLBatch. */
45  return nullptr;
46 };
47 
49 {
50  /* TODO(Metal): Implement MTLDrawList. */
51  return nullptr;
52 };
53 
55 {
56  MTLContext *mtl_context = static_cast<MTLContext *>(
57  reinterpret_cast<Context *>(GPU_context_active_get()));
58  return new MTLFrameBuffer(mtl_context, name);
59 };
60 
62 {
63  /* TODO(Metal): Implement MTLIndexBuf. */
64  return nullptr;
65 };
66 
68 {
69  return new MTLQueryPool();
70 };
71 
72 Shader *MTLBackend::shader_alloc(const char *name)
73 {
74  /* TODO(Metal): Implement MTLShader. */
75  return nullptr;
76 };
77 
79 {
80  return new gpu::MTLTexture(name);
81 }
82 
84 {
85  return new MTLUniformBuf(size, name);
86 };
87 
89 {
90  /* TODO(Metal): Implement MTLStorageBuf. */
91  return nullptr;
92 }
93 
95 {
96  /* TODO(Metal): Implement MTLVertBuf. */
97  return nullptr;
98 }
99 
101 {
102  /* All Rendering must occur within a render boundary */
103  /* Track a call-count for nested calls, used to ensure we are inside an
104  * autoreleasepool from all rendering path. */
106 
107  if (g_autoreleasepool == nil) {
108  g_autoreleasepool = [[NSAutoreleasePool alloc] init];
109  }
112 }
113 
115 {
116  /* If call-count reaches zero, drain auto release pool.
117  * Ensures temporary objects are freed within a frame's lifetime. */
121 
122  if (g_autoreleasepool_depth == 0) {
123  [g_autoreleasepool drain];
124  g_autoreleasepool = nil;
125  }
126 }
127 
129 {
130  /* NOTE(Metal): Primarily called from main thread, but below data-structures
131  * and operations are thread-safe, and GPUContext rendering coordination
132  * is also thread-safe. */
133 
134  /* Flush any MTLSafeFreeLists which have previously been released by any MTLContext. */
136 
137  /* End existing MTLSafeFreeList and begin new list --
138  * Buffers wont `free` until all associated in-flight command buffers have completed.
139  * Decrement final reference count for ensuring the previous list is certainly
140  * released. */
141  MTLSafeFreeList *cmd_free_buffer_list =
144  cmd_free_buffer_list->decrement_reference();
145 }
146 
148 {
149  return (g_autoreleasepool != nil);
150 }
151 
154 /* -------------------------------------------------------------------- */
158 /* For Metal, platform_init needs to be called after MTLContext initialization. */
159 void MTLBackend::platform_init(MTLContext *ctx)
160 {
161  if (GPG.initialized) {
162  return;
163  }
164 
166  eGPUOSType os = GPU_OS_ANY;
169 
170  BLI_assert(ctx);
171  id<MTLDevice> mtl_device = nil; /*ctx->device; TODO(Metal): Implement MTLContext. */
172  BLI_assert(device);
173 
174  NSString *gpu_name = [mtl_device name];
175  const char *vendor = [gpu_name UTF8String];
176  const char *renderer = "Metal API";
177  const char *version = "1.2";
178  printf("METAL API - DETECTED GPU: %s\n", vendor);
179 
180  /* macOS is the only supported platform, but check to ensure we are not building with Metal
181  * enablement on another platform. */
182 #ifdef _WIN32
183  os = GPU_OS_WIN;
184 #elif defined(__APPLE__)
185  os = GPU_OS_MAC;
186 #else
187  os = GPU_OS_UNIX;
188 #endif
189 
190  BLI_assert(os == GPU_OS_MAC && "Platform must be macOS");
191 
192  /* Determine Vendor from name. */
193  if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
194  device = GPU_DEVICE_ATI;
195  driver = GPU_DRIVER_OFFICIAL;
196  }
197  else if (strstr(vendor, "NVIDIA")) {
198  device = GPU_DEVICE_NVIDIA;
199  driver = GPU_DRIVER_OFFICIAL;
200  }
201  else if (strstr(vendor, "Intel")) {
202  device = GPU_DEVICE_INTEL;
203  driver = GPU_DRIVER_OFFICIAL;
204  }
205  else if (strstr(vendor, "Apple") || strstr(vendor, "APPLE")) {
206  /* Apple Silicon. */
207  device = GPU_DEVICE_APPLE;
208  driver = GPU_DRIVER_OFFICIAL;
209  }
210  else if (strstr(renderer, "Apple Software Renderer")) {
211  device = GPU_DEVICE_SOFTWARE;
212  driver = GPU_DRIVER_SOFTWARE;
213  }
214  else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
215  device = GPU_DEVICE_SOFTWARE;
216  driver = GPU_DRIVER_SOFTWARE;
217  }
218  else {
219  printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
220  printf("Detected configuration:\n");
221  printf("Vendor: %s\n", vendor);
222  printf("Renderer: %s\n", renderer);
223  }
224 
225  GPG.init(device, os, driver, support_level, GPU_BACKEND_METAL, vendor, renderer, version);
226 }
227 
228 void MTLBackend::platform_exit()
229 {
231  GPG.clear();
232 }
233 
236 /* -------------------------------------------------------------------- */
239 MTLCapabilities MTLBackend::capabilities = {};
240 
241 static const char *mtl_extensions_get_null(int i)
242 {
243  return nullptr;
244 }
245 
246 bool supports_barycentric_whitelist(id<MTLDevice> device)
247 {
248  NSString *gpu_name = [device name];
249  BLI_assert([gpu_name length]);
250  const char *vendor = [gpu_name UTF8String];
251 
252  /* Verify GPU support. */
253  bool supported_gpu = [device supportsFamily:MTLGPUFamilyMac2];
254  bool should_support_barycentrics = false;
255 
256  /* Known good configs. */
257  if (strstr(vendor, "AMD") || strstr(vendor, "Apple") || strstr(vendor, "APPLE")) {
258  should_support_barycentrics = true;
259  }
260 
261  /* Explicit support for Intel-based platforms. */
262  if ((strstr(vendor, "Intel") || strstr(vendor, "INTEL"))) {
263  should_support_barycentrics = true;
264  }
265  return supported_gpu && should_support_barycentrics;
266 }
267 
269 {
270  /* Device compatibility information using Metal Feature-set tables.
271  * See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
272 
273  NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
274 
275  /* Metal Viewport requires macOS Version 10.15 onward. */
276  bool supported_os_version = version.majorVersion >= 11 ||
277  (version.majorVersion == 10 ? version.minorVersion >= 15 : false);
278  if (!supported_os_version) {
279  printf(
280  "OS Version too low to run minimum required metal version. Required at least 10.15, got "
281  "%ld.%ld \n",
282  (long)version.majorVersion,
283  (long)version.minorVersion);
284  return false;
285  }
286 
287  if (@available(macOS 10.15, *)) {
288  id<MTLDevice> device = MTLCreateSystemDefaultDevice();
289 
290  /* Debug: Enable low power GPU with Environment Var: METAL_FORCE_INTEL. */
291  static const char *forceIntelStr = getenv("METAL_FORCE_INTEL");
292  bool forceIntel = forceIntelStr ? (atoi(forceIntelStr) != 0) : false;
293 
294  if (forceIntel) {
295  NSArray<id<MTLDevice>> *allDevices = MTLCopyAllDevices();
296  for (id<MTLDevice> _device in allDevices) {
297  if (_device.lowPower) {
298  device = _device;
299  }
300  }
301  }
302 
303  /* Metal Viewport requires argument buffer tier-2 support and Barycentric Coordinates.
304  * These are available on most hardware configurations supporting Metal 2.2. */
305  bool supports_argument_buffers_tier2 = ([device argumentBuffersSupport] ==
306  MTLArgumentBuffersTier2);
307  bool supports_barycentrics = [device supportsShaderBarycentricCoordinates] ||
309  bool supported_metal_version = [device supportsFamily:MTLGPUFamilyMac2];
310 
311  bool result = supports_argument_buffers_tier2 && supports_barycentrics &&
312  supported_os_version && supported_metal_version;
313 
314  if (!supports_argument_buffers_tier2) {
315  printf("[Metal] Device does not support argument buffers tier 2\n");
316  }
317  if (!supports_barycentrics) {
318  printf("[Metal] Device does not support barycentrics coordinates\n");
319  }
320  if (!supported_metal_version) {
321  printf("[Metal] Device does not support metal 2.2 or higher\n");
322  }
323 
324  if (result) {
325  printf("Device with name %s supports metal minimum requirements\n",
326  [[device name] UTF8String]);
327  }
328 
329  return result;
330  }
331  return false;
332 }
333 
334 void MTLBackend::capabilities_init(MTLContext *ctx)
335 {
336  BLI_assert(ctx);
337  id<MTLDevice> device = nil; /*ctx->device TODO(Metal): Implement MTLContext. */
338  BLI_assert(device);
339 
340  /* Initialize Capabilities. */
341  MTLBackend::capabilities.supports_argument_buffers_tier2 = ([device argumentBuffersSupport] ==
342  MTLArgumentBuffersTier2);
343  MTLBackend::capabilities.supports_family_mac1 = [device supportsFamily:MTLGPUFamilyMac1];
344  MTLBackend::capabilities.supports_family_mac2 = [device supportsFamily:MTLGPUFamilyMac2];
346  supportsFamily:MTLGPUFamilyMacCatalyst1];
348  supportsFamily:MTLGPUFamilyMacCatalyst2];
349 
350  /* Common Global Capabilities. */
351  GCaps.max_texture_size = ([device supportsFamily:MTLGPUFamilyApple3] ||
353  16384 :
354  8192;
355  GCaps.max_texture_3d_size = 2048;
356  GCaps.max_texture_layers = 2048;
358  128 :
359  (([device supportsFamily:MTLGPUFamilyApple4]) ? 96 : 31);
360  if (GCaps.max_textures <= 32) {
361  BLI_assert(false);
362  }
364 
366  GCaps.max_textures_geom = 0; /* N/A geometry shaders not supported. */
368 
369  /* Conservative uniform data limit is 4KB per-stage -- This is the limit of setBytes.
370  * MTLBuffer path is also supported but not as efficient. */
371  GCaps.max_uniforms_vert = 1024;
372  GCaps.max_uniforms_frag = 1024;
373 
374  GCaps.max_batch_indices = 1 << 31;
375  GCaps.max_batch_vertices = 1 << 31;
378 
379  /* Feature support */
380  GCaps.mem_stats_support = false;
381  GCaps.shader_image_load_store_support = ([device supportsFamily:MTLGPUFamilyApple3] ||
384  GCaps.compute_shader_support = false; /* TODO(Metal): Add compute support. */
386  false; /* TODO(Metal): implement Storage Buffer support. */
387 
388  /* Maximum buffer bindings: 31. Consider required slot for uniforms/UBOs/Vertex attributes.
389  * Can use argument buffers if a higher limit is required. */
391 
393  GCaps.max_work_group_count[0] = 65535;
394  GCaps.max_work_group_count[1] = 65535;
395  GCaps.max_work_group_count[2] = 65535;
396 
397  /* In Metal, total_thread_count is 512 or 1024, such that
398  * threadgroup `width*height*depth <= total_thread_count` */
399  uint max_threads_per_threadgroup_per_dim = ([device supportsFamily:MTLGPUFamilyApple4] ||
401  1024 :
402  512;
403  GCaps.max_work_group_size[0] = max_threads_per_threadgroup_per_dim;
404  GCaps.max_work_group_size[1] = max_threads_per_threadgroup_per_dim;
405  GCaps.max_work_group_size[2] = max_threads_per_threadgroup_per_dim;
406  }
407 
409 
410  /* OPENGL Related workarounds -- none needed for Metal. */
411  GCaps.extensions_len = 0;
416  GCaps.broken_amd_driver = false;
417 
418  /* Metal related workarounds. */
419  /* Minimum per-vertex stride is 4 bytes in Metal.
420  * A bound vertex buffer must contribute at least 4 bytes per vertex. */
422 }
423 
426 } // blender::gpu
#define BLI_assert(a)
Definition: BLI_assert.h:46
unsigned int uint
Definition: BLI_sys_types.h:67
GPUContext * GPU_context_active_get(void)
Definition: gpu_context.cc:142
@ GPU_BACKEND_METAL
Definition: GPU_platform.h:18
eGPUDriverType
Definition: GPU_platform.h:43
@ GPU_DRIVER_ANY
Definition: GPU_platform.h:47
@ GPU_DRIVER_OFFICIAL
Definition: GPU_platform.h:44
@ GPU_DRIVER_SOFTWARE
Definition: GPU_platform.h:46
eGPUSupportLevel
Definition: GPU_platform.h:50
@ GPU_SUPPORT_LEVEL_SUPPORTED
Definition: GPU_platform.h:51
eGPUOSType
Definition: GPU_platform.h:36
@ GPU_OS_WIN
Definition: GPU_platform.h:37
@ GPU_OS_UNIX
Definition: GPU_platform.h:39
@ GPU_OS_ANY
Definition: GPU_platform.h:40
@ GPU_OS_MAC
Definition: GPU_platform.h:38
eGPUDeviceType
Definition: GPU_platform.h:23
@ GPU_DEVICE_UNKNOWN
Definition: GPU_platform.h:30
@ GPU_DEVICE_ATI
Definition: GPU_platform.h:25
@ GPU_DEVICE_SOFTWARE
Definition: GPU_platform.h:29
@ GPU_DEVICE_NVIDIA
Definition: GPU_platform.h:24
@ GPU_DEVICE_APPLE
Definition: GPU_platform.h:28
@ GPU_DEVICE_INTEL
Definition: GPU_platform.h:26
GPUUsageType
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void init(eGPUDeviceType gpu_device, eGPUOSType os_type, eGPUDriverType driver_type, eGPUSupportLevel gpu_support_level, eGPUBackendType backend, const char *vendor_str, const char *renderer_str, const char *version_str)
Definition: gpu_platform.cc:64
UniformBuf * uniformbuf_alloc(int size, const char *name) override
Definition: mtl_backend.mm:83
void render_step() override
Definition: mtl_backend.mm:128
void render_begin() override
Definition: mtl_backend.mm:100
QueryPool * querypool_alloc() override
Definition: mtl_backend.mm:67
Context * context_alloc(void *ghost_window) override
Definition: mtl_backend.mm:37
FrameBuffer * framebuffer_alloc(const char *name) override
Definition: mtl_backend.mm:54
StorageBuf * storagebuf_alloc(int size, GPUUsageType usage, const char *name) override
Definition: mtl_backend.mm:88
static bool metal_is_supported()
Definition: mtl_backend.mm:268
IndexBuf * indexbuf_alloc() override
Definition: mtl_backend.mm:61
void render_end() override
Definition: mtl_backend.mm:114
void samplers_update() override
Definition: mtl_backend.mm:33
Batch * batch_alloc() override
Definition: mtl_backend.mm:42
static MTLCapabilities capabilities
Definition: mtl_backend.hh:31
VertBuf * vertbuf_alloc() override
Definition: mtl_backend.mm:94
Texture * texture_alloc(const char *name) override
Definition: mtl_backend.mm:78
DrawList * drawlist_alloc(int list_length) override
Definition: mtl_backend.mm:48
Shader * shader_alloc(const char *name) override
Definition: mtl_backend.mm:72
MTLSafeFreeList * get_current_safe_list()
Definition: mtl_memory.mm:325
static MTLBufferPool & get_global_memory_manager()
Definition: mtl_context.hh:713
GPUPlatformGlobal GPG
Definition: gpu_platform.cc:26
bool supports_barycentric_whitelist(id< MTLDevice > device)
Definition: mtl_backend.mm:246
GPUCapabilities GCaps
static const char * mtl_extensions_get_null(int i)
Definition: mtl_backend.mm:241
thread_local int g_autoreleasepool_depth
Definition: mtl_backend.mm:27
thread_local NSAutoreleasePool * g_autoreleasepool
Definition: mtl_backend.mm:26
T length(const vec_base< T, Size > &a)