Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt23
-rw-r--r--source/blender/gpu/CMakeLists.txt64
-rw-r--r--source/blender/gpu/GPU_context.h14
-rw-r--r--source/blender/gpu/GPU_platform.h11
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh6
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh8
-rw-r--r--source/blender/gpu/intern/gpu_context.cc70
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc9
-rw-r--r--source/blender/gpu/intern/gpu_platform_private.hh2
-rw-r--r--source/blender/gpu/metal/mtl_backend.hh77
-rw-r--r--source/blender/gpu/metal/mtl_backend.mm404
-rw-r--r--source/blender/gpu/metal/mtl_capabilities.hh45
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc8
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh5
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c5
15 files changed, 719 insertions, 32 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04cedcc6722..d31a0c4a63d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -531,6 +531,19 @@ mark_as_advanced(
WITH_GPU_SHADER_BUILDER
)
+# Metal
+
+if (APPLE)
+ option(WITH_METAL_BACKEND "Use Metal for graphics instead of (or as well as) OpenGL on macOS." OFF)
+ mark_as_advanced(WITH_METAL_BACKEND)
+else()
+ set(WITH_METAL_BACKEND OFF)
+endif()
+
+if (WITH_METAL_BACKEND)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE)
+endif()
+
if(WIN32)
option(WITH_GL_ANGLE "Link with the ANGLE library, an OpenGL ES 2.0 implementation based on Direct3D, instead of the system OpenGL library." OFF)
mark_as_advanced(WITH_GL_ANGLE)
@@ -1275,6 +1288,16 @@ else()
endif()
#-----------------------------------------------------------------------------
+# Configure Metal.
+if (WITH_METAL_BACKEND)
+ add_definitions(-DWITH_METAL_BACKEND)
+
+ # No need to add frameworks here, all the ones we need for Metal and
+ # Metal-OpenGL Interop are already being added by
+ # build_files/cmake/platform/platform_apple.cmake
+endif()
+
+#-----------------------------------------------------------------------------
# Configure OpenMP.
if(WITH_OPENMP)
if(NOT OPENMP_CUSTOM)
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 45e614991ca..6c795aba560 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -5,7 +5,7 @@
# to more easily highlight codepadths in other libraries that need to be refactored,
# bf_gpu is allowed to have opengl regardless of this option.
-if(NOT WITH_OPENGL)
+if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND)
add_definitions(-DWITH_OPENGL)
endif()
@@ -13,6 +13,7 @@ set(INC
.
intern
opengl
+ metal
../blenkernel
../blenlib
../bmesh
@@ -78,27 +79,6 @@ set(SRC
intern/gpu_vertex_format.cc
intern/gpu_viewport.c
- opengl/gl_backend.cc
- opengl/gl_batch.cc
- opengl/gl_compute.cc
- opengl/gl_context.cc
- opengl/gl_debug.cc
- opengl/gl_debug_layer.cc
- opengl/gl_drawlist.cc
- opengl/gl_framebuffer.cc
- opengl/gl_immediate.cc
- opengl/gl_index_buffer.cc
- opengl/gl_query.cc
- opengl/gl_shader.cc
- opengl/gl_shader_interface.cc
- opengl/gl_shader_log.cc
- opengl/gl_state.cc
- opengl/gl_storage_buffer.cc
- opengl/gl_texture.cc
- opengl/gl_uniform_buffer.cc
- opengl/gl_vertex_array.cc
- opengl/gl_vertex_buffer.cc
-
GPU_batch.h
GPU_batch_presets.h
GPU_batch_utils.h
@@ -157,6 +137,30 @@ set(SRC
intern/gpu_uniform_buffer_private.hh
intern/gpu_vertex_buffer_private.hh
intern/gpu_vertex_format_private.h
+)
+
+set(OPENGL_SRC
+
+ opengl/gl_backend.cc
+ opengl/gl_batch.cc
+ opengl/gl_compute.cc
+ opengl/gl_context.cc
+ opengl/gl_debug.cc
+ opengl/gl_debug_layer.cc
+ opengl/gl_drawlist.cc
+ opengl/gl_framebuffer.cc
+ opengl/gl_immediate.cc
+ opengl/gl_index_buffer.cc
+ opengl/gl_query.cc
+ opengl/gl_shader.cc
+ opengl/gl_shader_interface.cc
+ opengl/gl_shader_log.cc
+ opengl/gl_state.cc
+ opengl/gl_storage_buffer.cc
+ opengl/gl_texture.cc
+ opengl/gl_uniform_buffer.cc
+ opengl/gl_vertex_array.cc
+ opengl/gl_vertex_buffer.cc
opengl/gl_backend.hh
opengl/gl_batch.hh
@@ -178,6 +182,22 @@ set(SRC
opengl/gl_vertex_buffer.hh
)
+set(METAL_SRC
+ metal/mtl_backend.mm
+
+ metal/mtl_backend.hh
+ metal/mtl_capabilities.hh
+)
+
+# Select Backend source based on availability
+if(WITH_OPENGL)
+ list(APPEND SRC ${OPENGL_SRC})
+endif()
+
+if(WITH_METAL_BACKEND)
+ list(APPEND SRC ${METAL_SRC})
+endif()
+
set(LIB
${BLENDER_GL_LIBRARIES}
)
diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h
index 85433913456..453c117501d 100644
--- a/source/blender/gpu/GPU_context.h
+++ b/source/blender/gpu/GPU_context.h
@@ -11,18 +11,15 @@
#include "GPU_batch.h"
#include "GPU_common.h"
+#include "GPU_platform.h"
#ifdef __cplusplus
extern "C" {
#endif
-typedef enum eGPUBackendType {
- GPU_BACKEND_NONE = 0,
- GPU_BACKEND_OPENGL,
-} eGPUBackendType;
-
void GPU_backend_init(eGPUBackendType backend);
void GPU_backend_exit(void);
+bool GPU_backend_supported(eGPUBackendType type);
eGPUBackendType GPU_backend_get_type(void);
@@ -49,6 +46,13 @@ GPUContext *GPU_context_active_get(void);
void GPU_context_main_lock(void);
void GPU_context_main_unlock(void);
+/* GPU Begin/end work blocks */
+void GPU_render_begin();
+void GPU_render_end();
+
+/* For operations which need to run exactly once per frame -- even if there are no render updates. */
+void GPU_render_step();
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index d5a0fcfa921..bd20d0c7db1 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -12,6 +12,13 @@
/* GPU platform support */
+typedef enum eGPUBackendType {
+ GPU_BACKEND_NONE = 0,
+ GPU_BACKEND_OPENGL = 1 << 0,
+ GPU_BACKEND_METAL = 1 << 1,
+ GPU_BACKEND_ANY = 0xFFFFFFFFu
+} eGPUBackendType;
+
/* GPU Types */
typedef enum eGPUDeviceType {
GPU_DEVICE_NVIDIA = (1 << 0),
@@ -51,8 +58,10 @@ extern "C" {
#endif
/* GPU Types */
-
+/* TODO: Verify all use-cases of GPU_type_matches to determine which graphics API it should apply to, and replace
+ * with `GPU_type_matches_ex` where appropriate. */
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
+bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend);
eGPUSupportLevel GPU_platform_support_level(void);
const char *GPU_platform_vendor(void);
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index ad906c74980..6e07e6c3229 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -49,6 +49,12 @@ class GPUBackend {
virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0;
virtual StorageBuf *storagebuf_alloc(int size, GPUUsageType usage, const char *name) = 0;
virtual VertBuf *vertbuf_alloc() = 0;
+
+ /* Render Frame Coordination --
+ * Used for performing per-frame actions globally */
+ virtual void render_begin() = 0;
+ virtual void render_end() = 0;
+ virtual void render_step() = 0;
};
} // namespace gpu
diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh
index d645d82a879..4a951eb8458 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -20,11 +20,13 @@ namespace blender::gpu {
*/
struct GPUCapabilities {
int max_texture_size = 0;
+ int max_texture_3d_size = 0;
int max_texture_layers = 0;
int max_textures = 0;
int max_textures_vert = 0;
int max_textures_geom = 0;
int max_textures_frag = 0;
+ int max_samplers = 0;
int max_work_group_count[3] = {0, 0, 0};
int max_work_group_size[3] = {0, 0, 0};
int max_uniforms_vert = 0;
@@ -41,6 +43,8 @@ struct GPUCapabilities {
bool compute_shader_support = false;
bool shader_storage_buffer_objects_support = false;
bool shader_image_load_store_support = false;
+ bool transform_feedback_support = false;
+
/* OpenGL related workarounds. */
bool mip_render_workaround = false;
bool depth_blitting_workaround = false;
@@ -48,6 +52,10 @@ struct GPUCapabilities {
bool broken_amd_driver = false;
bool use_hq_normals_workaround = false;
/* Vulkan related workarounds. */
+
+ /* Metal related workarounds. */
+ /* Minimum per-vertex stride in bytes (For a vertex buffer). */
+ int minimum_per_vertex_stride = 1;
};
extern GPUCapabilities GCaps;
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index 60e95e09a99..5b40d3850a5 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -13,7 +13,9 @@
*/
/* TODO: Create cmake option. */
-#define WITH_OPENGL_BACKEND 1
+#if WITH_OPENGL
+ #define WITH_OPENGL_BACKEND 1
+#endif
#include "BLI_assert.h"
#include "BLI_utildefines.h"
@@ -32,6 +34,9 @@
# include "gl_backend.hh"
# include "gl_context.hh"
#endif
+#ifdef WITH_METAL_BACKEND
+# include "mtl_backend.hh"
+#endif
#include <mutex>
#include <vector>
@@ -141,21 +146,73 @@ void GPU_context_main_unlock()
/** \} */
/* -------------------------------------------------------------------- */
+/** \name GPU Begin/end work blocks
+ *
+ * Used to explicitly define a per-frame block within which GPU work will happen.
+ * Used for global autoreleasepool flushing in Metal
+ * \{ */
+
+void GPU_render_begin() {
+ GPUBackend* backend = GPUBackend::get();
+ BLI_assert(backend);
+ backend->render_begin();
+}
+void GPU_render_end() {
+ GPUBackend* backend = GPUBackend::get();
+ BLI_assert(backend);
+ backend->render_end();
+}
+void GPU_render_step() {
+ GPUBackend* backend = GPUBackend::get();
+ BLI_assert(backend);
+ backend->render_step();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Backend selection
* \{ */
static GPUBackend *g_backend;
+bool GPU_backend_supported(eGPUBackendType type)
+{
+ switch (type) {
+ case GPU_BACKEND_OPENGL:
+#ifdef WITH_OPENGL_BACKEND
+ return true;
+#else
+ return false;
+#endif
+ case GPU_BACKEND_METAL:
+#ifdef WITH_METAL_BACKEND
+ return MTLBackend::metal_is_supported();
+#else
+ return false;
+#endif
+ default:
+ BLI_assert(false && "No backend specified");
+ return false;
+ }
+}
+
void GPU_backend_init(eGPUBackendType backend_type)
{
BLI_assert(g_backend == nullptr);
+ BLI_assert(GPU_backend_supported(backend_type));
switch (backend_type) {
-#if WITH_OPENGL_BACKEND
+#ifdef WITH_OPENGL_BACKEND
case GPU_BACKEND_OPENGL:
g_backend = new GLBackend;
break;
#endif
+#ifdef WITH_METAL_BACKEND
+ case GPU_BACKEND_METAL:
+ g_backend = new MTLBackend;
+ break;
+#endif
default:
BLI_assert(0);
break;
@@ -172,9 +229,18 @@ void GPU_backend_exit()
eGPUBackendType GPU_backend_get_type()
{
+
+#ifdef WITH_OPENGL_BACKEND
if (g_backend && dynamic_cast<GLBackend *>(g_backend) != nullptr) {
return GPU_BACKEND_OPENGL;
}
+#endif
+
+#ifdef WITH_METAL_BACKEND
+ if (g_backend && dynamic_cast<MTLBackend *>(g_backend) != nullptr) {
+ return GPU_BACKEND_METAL;
+ }
+#endif
return GPU_BACKEND_NONE;
}
diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc
index 969a3033c6f..8d4e80abd97 100644
--- a/source/blender/gpu/intern/gpu_platform.cc
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -65,6 +65,7 @@ void GPUPlatformGlobal::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)
@@ -83,6 +84,7 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
this->version = BLI_strdup(version_str);
this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str);
this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str);
+ this->backend = backend;
}
void GPUPlatformGlobal::clear()
@@ -143,8 +145,13 @@ const char *GPU_platform_gpu_name()
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
{
+ return GPU_type_matches_ex(device, os, driver, GPU_BACKEND_ANY);
+}
+
+bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend)
+{
BLI_assert(GPG.initialized);
- return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver);
+ return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver) && (GPG.backend & backend);
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh
index a6c400ad319..6e6c24a8662 100644
--- a/source/blender/gpu/intern/gpu_platform_private.hh
+++ b/source/blender/gpu/intern/gpu_platform_private.hh
@@ -23,12 +23,14 @@ class GPUPlatformGlobal {
char *version = nullptr;
char *support_key = nullptr;
char *gpu_name = nullptr;
+ eGPUBackendType backend = GPU_BACKEND_NONE;
public:
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);
diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh
new file mode 100644
index 00000000000..0a84636b9e4
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_backend.hh
@@ -0,0 +1,77 @@
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_vector.hh"
+
+#include "gpu_backend.hh"
+#include "mtl_capabilities.hh"
+
+namespace blender {
+namespace gpu {
+
+class Batch;
+class DrawList;
+class FrameBuffer;
+class IndexBuf;
+class QueryPool;
+class Shader;
+class Texture;
+class UniformBuf;
+class VertBuf;
+class MTLContext;
+
+class MTLBackend : public GPUBackend {
+ friend class MTLContext;
+
+ public:
+ /* Capabilities. */
+ static MTLCapabilities capabilities;
+
+ inline ~MTLBackend()
+ {
+ MTLBackend::platform_exit();
+ }
+
+ static bool metal_is_supported();
+ inline static MTLBackend *get()
+ {
+ return static_cast<MTLBackend *>(GPUBackend::get());
+ }
+
+ void samplers_update() override;
+ inline void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override
+ {
+ /* Placeholder */
+ }
+
+ /* MTL Allocators need to be implemented in separate .mm files, due to allocation of Objective-C
+ * objects. */
+ Context *context_alloc(void *ghost_window) override;
+ Batch *batch_alloc() override;
+ DrawList *drawlist_alloc(int list_length) override;
+ FrameBuffer *framebuffer_alloc(const char *name) override;
+ IndexBuf *indexbuf_alloc() override;
+ QueryPool *querypool_alloc() override;
+ Shader *shader_alloc(const char *name) override;
+ Texture *texture_alloc(const char *name) override;
+ UniformBuf *uniformbuf_alloc(int size, const char *name) override;
+ VertBuf *vertbuf_alloc() override;
+
+ /* Render Frame Coordination. */
+ void render_begin() override;
+ void render_end() override;
+ void render_step() override;
+ bool is_inside_render_boundary();
+
+ private:
+ static void platform_init(MTLContext *ctx);
+ static void platform_exit();
+
+ static void capabilities_init(MTLContext *ctx);
+};
+
+} // namespace gpu
+} // namespace blender \ No newline at end of file
diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm
new file mode 100644
index 00000000000..c4a35218158
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_backend.mm
@@ -0,0 +1,404 @@
+/** \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.
+ * Esures temporary objects are freed within a frame's
+ * lifetime. */
+ 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
+ * \{ */
+
+/* For Metal, platform_init needs to be called after MTLContext initialisation. */
+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);
+
+ /* Initialise Capabilities. */
+ MTLBackend::capabilities.supports_argument_buffers_tier2 = ([device argumentBuffersSupport] ==
+ MTLArgumentBuffersTier2);
+ 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. */
+ GCaps.max_uniforms_vert = 1024;
+ 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 =
+ false; /* TODO(Metal): implement Storage Buffer support.*/
+
+ /* Maximum buffer bindings: 31. Consider required slot for uniforms/UBOs/Vertex attributes.
+ * 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;
+
+ /* In Metal, total_thread_count is 512 or 1024, such that
+ * threadgroup `width*height*depth <= total_thread_count` */
+ unsigned int max_threads_per_threadgroup_per_dim =
+ ([device supportsFamily:MTLGPUFamilyApple4] ||
+ MTLBackend::capabilities.supports_family_mac1) ? 1024 : 512;
+ 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. */
+ /* Minimum per-vertex stride is 4 bytes in Metal. A bound vertex buffer must contribute atleast 4 bytes per vertex. */
+ GCaps.minimum_per_vertex_stride = 4;
+}
+
+/** \} */
+
+} // gpu
+} // blender
diff --git a/source/blender/gpu/metal/mtl_capabilities.hh b/source/blender/gpu/metal/mtl_capabilities.hh
new file mode 100644
index 00000000000..8acdf5f303a
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_capabilities.hh
@@ -0,0 +1,45 @@
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+namespace blender {
+namespace gpu {
+
+/*** Derived from: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf ***/
+/** Upper Bound/Fixed Limits **/
+
+#define METAL_MAX_TEXTURE_SLOTS 128
+#define METAL_MAX_SAMPLER_SLOTS METAL_MAX_TEXTURE_SLOTS
+#define METAL_MAX_UNIFORM_BUFFER_BINDINGS 31
+#define METAL_MAX_VERTEX_INPUT_ATTRIBUTES 31
+#define METAL_MAX_UNIFORMS_PER_BLOCK 64
+
+/* Context-specific limits -- populated in 'MTLBackend::platform_init' */
+typedef struct MTLCapabilities {
+
+ /* Variable Limits & faeture sets */
+ int max_color_render_targets = 4; /* Minimum = 4 */
+ int buffer_alignment_for_textures = 256; /* Upper bound = 256 bytes */
+ int minimum_buffer_offset_alignment = 256; /* Upper bound = 256 bytes */
+
+ /* Capabilities */
+ bool supports_vertex_amplification = false;
+ bool supports_texture_swizzle = true;
+ bool supports_cubemaps = true;
+ bool supports_layered_rendering = true;
+ bool supports_memory_barriers = false;
+ bool supports_sampler_border_color = false;
+ bool supports_argument_buffers_tier2 = false;
+
+ /* GPU Family */
+ bool supports_family_mac1 = false;
+ bool supports_family_mac2 = false;
+ bool supports_family_mac_catalyst1 = false;
+ bool supports_family_mac_catalyst2 = false;
+
+} MTLCapabilities;
+
+} // namespace gpu
+} // namespace blender
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 883f9403920..2c9cbdb99d8 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -140,7 +140,7 @@ void GLBackend::platform_init()
}
}
- GPG.init(device, os, driver, support_level, vendor, renderer, version);
+ GPG.init(device, os, driver, support_level, GPU_BACKEND_OPENGL, vendor, renderer, version);
}
void GLBackend::platform_exit()
@@ -418,6 +418,12 @@ static void detect_workarounds()
(strstr(renderer, "HD Graphics 4400") || strstr(renderer, "HD Graphics 4600"))) {
GCaps.shader_storage_buffer_objects_support = false;
}
+
+ /* Metal-related Workarounds. */
+
+ /* Minimum Per-Vertex stride is 1 byte for OpenGL. */
+ GCaps.minimum_per_vertex_stride = 1;
+
} // namespace blender::gpu
/** Internal capabilities. */
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh
index e72726d9c7b..29249111294 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -136,6 +136,11 @@ class GLBackend : public GPUBackend {
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
}
+ /* Render Frame Coordination */
+ void render_begin(void) override{};
+ void render_end(void) override{};
+ void render_step(void) override{};
+
private:
static void platform_init();
static void platform_exit();
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 46f7b67c2ba..3c8474b1b6c 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -1037,6 +1037,10 @@ void wm_draw_update(bContext *C)
wmWindowManager *wm = CTX_wm_manager(C);
GPU_context_main_lock();
+
+ GPU_render_begin();
+ GPU_render_step();
+
BKE_image_free_unused_gpu_textures();
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
@@ -1075,6 +1079,7 @@ void wm_draw_update(bContext *C)
/* Draw non-windows (surfaces) */
wm_surfaces_iter(C, wm_draw_surface);
+ GPU_render_end();
GPU_context_main_unlock();
}