[Bf-blender-cvs] [697b447c206] master: Metal: MTLContext implementation and immediate mode rendering support.

Thomas Dinges noreply at git.blender.org
Thu Sep 22 17:35:08 CEST 2022


Commit: 697b447c2069bbbbaa9929aab0ea1f66ef8bf4d0
Author: Thomas Dinges
Date:   Thu Sep 22 17:27:51 2022 +0200
Branches: master
https://developer.blender.org/rB697b447c2069bbbbaa9929aab0ea1f66ef8bf4d0

Metal: MTLContext implementation and immediate mode rendering support.

MTLContext provides functionality for command encoding, binding management and graphics device management. MTLImmediate provides simple draw enablement with dynamically encoded data. These draws utilise temporary scratch buffer memory to provide minimal bandwidth overhead during workload submission.

This patch also contains empty placeholders for MTLBatch and MTLDrawList to enable testing of first pixels on-screen without failure.

The Metal API also requires access to the GHOST_Context to ensure the same pre-initialized Metal GPU device is used by the viewport. Given the explicit nature of Metal, explicit control is also needed over presentation, to ensure correct work scheduling and rendering pipeline state.

Authored by Apple: Michael Parkin-White

Ref T96261

(The diff is based on 043f59cb3b5835ba1a0bbf6f1cbad080b527f7f6)

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D15953

===================================================================

M	intern/ghost/intern/GHOST_Context.h
M	intern/ghost/intern/GHOST_ContextCGL.h
M	intern/ghost/intern/GHOST_ContextCGL.mm
M	intern/ghost/intern/GHOST_Window.cpp
M	intern/ghost/intern/GHOST_Window.h
M	intern/ghost/test/multitest/MultiTest.c
M	source/blender/draw/DRW_engine.h
M	source/blender/draw/engines/eevee/eevee_lightcache.c
M	source/blender/draw/engines/eevee_next/eevee_shader.cc
M	source/blender/draw/intern/DRW_render.h
M	source/blender/draw/intern/draw_manager.c
M	source/blender/draw/intern/draw_manager_shader.c
M	source/blender/gpu/CMakeLists.txt
M	source/blender/gpu/GPU_context.h
M	source/blender/gpu/GPU_material.h
M	source/blender/gpu/intern/gpu_backend.hh
M	source/blender/gpu/intern/gpu_codegen.cc
M	source/blender/gpu/intern/gpu_codegen.h
M	source/blender/gpu/intern/gpu_context.cc
M	source/blender/gpu/intern/gpu_material.c
M	source/blender/gpu/intern/gpu_node_graph.c
M	source/blender/gpu/intern/gpu_node_graph.h
M	source/blender/gpu/intern/gpu_shader_builder.cc
M	source/blender/gpu/intern/gpu_shader_interface.cc
M	source/blender/gpu/metal/mtl_backend.hh
M	source/blender/gpu/metal/mtl_backend.mm
A	source/blender/gpu/metal/mtl_batch.hh
M	source/blender/gpu/metal/mtl_command_buffer.mm
M	source/blender/gpu/metal/mtl_common.hh
M	source/blender/gpu/metal/mtl_context.hh
M	source/blender/gpu/metal/mtl_context.mm
A	source/blender/gpu/metal/mtl_drawlist.hh
A	source/blender/gpu/metal/mtl_immediate.hh
A	source/blender/gpu/metal/mtl_immediate.mm
M	source/blender/gpu/metal/mtl_memory.hh
M	source/blender/gpu/metal/mtl_shader.hh
M	source/blender/gpu/metal/mtl_shader.mm
M	source/blender/gpu/metal/mtl_shader_generator.mm
M	source/blender/gpu/metal/mtl_texture.mm
M	source/blender/gpu/metal/mtl_texture_util.mm
M	source/blender/gpu/opengl/gl_backend.hh
M	source/blender/gpu/tests/gpu_testing.cc
M	source/blender/render/intern/pipeline.cc
M	source/blender/windowmanager/intern/wm_playanim.c
M	source/blender/windowmanager/intern/wm_window.c

===================================================================

diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h
index 3546fb6bbc7..04d445e7f85 100644
--- a/intern/ghost/intern/GHOST_Context.h
+++ b/intern/ghost/intern/GHOST_Context.h
@@ -36,19 +36,19 @@ class GHOST_Context : public GHOST_IContext {
    * Swaps front and back buffers of a window.
    * \return A boolean success indicator.
    */
-  virtual GHOST_TSuccess swapBuffers() = 0;
+  virtual GHOST_TSuccess swapBuffers() override = 0;
 
   /**
    * Activates the drawing context of this window.
    * \return A boolean success indicator.
    */
-  virtual GHOST_TSuccess activateDrawingContext() = 0;
+  virtual GHOST_TSuccess activateDrawingContext() override = 0;
 
   /**
    * Release the drawing context of the calling thread.
    * \return A boolean success indicator.
    */
-  virtual GHOST_TSuccess releaseDrawingContext() = 0;
+  virtual GHOST_TSuccess releaseDrawingContext() override = 0;
 
   /**
    * Call immediately after new to initialize.  If this fails then immediately delete the object.
@@ -130,7 +130,7 @@ class GHOST_Context : public GHOST_IContext {
    * Gets the OpenGL frame-buffer associated with the OpenGL context
    * \return The ID of an OpenGL frame-buffer object.
    */
-  virtual unsigned int getDefaultFramebuffer()
+  virtual unsigned int getDefaultFramebuffer() override
   {
     return 0;
   }
diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h
index fa6d6fc6fa0..5caabb8ce00 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.h
+++ b/intern/ghost/intern/GHOST_ContextCGL.h
@@ -9,8 +9,13 @@
 
 #include "GHOST_Context.h"
 
+#include <Cocoa/Cocoa.h>
+#include <Metal/Metal.h>
+#include <QuartzCore/QuartzCore.h>
+
 @class CAMetalLayer;
 @class MTLCommandQueue;
+ at class MTLDevice;
 @class MTLRenderPipelineState;
 @class MTLTexture;
 @class NSOpenGLContext;
@@ -36,62 +41,89 @@ class GHOST_ContextCGL : public GHOST_Context {
    * Swaps front and back buffers of a window.
    * \return A boolean success indicator.
    */
-  GHOST_TSuccess swapBuffers();
+  GHOST_TSuccess swapBuffers() override;
 
   /**
    * Activates the drawing context of this window.
    * \return A boolean success indicator.
    */
-  GHOST_TSuccess activateDrawingContext();
+  GHOST_TSuccess activateDrawingContext() override;
 
   /**
    * Release the drawing context of the calling thread.
    * \return A boolean success indicator.
    */
-  GHOST_TSuccess releaseDrawingContext();
+  GHOST_TSuccess releaseDrawingContext() override;
 
-  unsigned int getDefaultFramebuffer();
+  unsigned int getDefaultFramebuffer() override;
 
   /**
    * Call immediately after new to initialize.  If this fails then immediately delete the object.
    * \return Indication as to whether initialization has succeeded.
    */
-  GHOST_TSuccess initializeDrawingContext();
+  GHOST_TSuccess initializeDrawingContext() override;
 
   /**
    * Removes references to native handles from this context and then returns
    * \return GHOST_kSuccess if it is OK for the parent to release the handles and
    * GHOST_kFailure if releasing the handles will interfere with sharing
    */
-  GHOST_TSuccess releaseNativeHandles();
+  GHOST_TSuccess releaseNativeHandles() override;
 
   /**
    * Sets the swap interval for #swapBuffers.
    * \param interval: The swap interval to use.
    * \return A boolean success indicator.
    */
-  GHOST_TSuccess setSwapInterval(int interval);
+  GHOST_TSuccess setSwapInterval(int interval) override;
 
   /**
    * Gets the current swap interval for #swapBuffers.
    * \param intervalOut: Variable to store the swap interval if it can be read.
    * \return Whether the swap interval can be read.
    */
-  GHOST_TSuccess getSwapInterval(int &);
+  GHOST_TSuccess getSwapInterval(int &) override;
 
   /**
    * Updates the drawing context of this window.
    * Needed whenever the window is changed.
    * \return Indication of success.
    */
-  GHOST_TSuccess updateDrawingContext();
+  GHOST_TSuccess updateDrawingContext() override;
+
+  /**
+   * Returns a texture that Metal code can use as a render target. The current
+   * contents of this texture will be composited on top of the framebuffer
+   * each time `swapBuffers` is called.
+   */
+  id<MTLTexture> metalOverlayTexture();
+
+  /**
+   * Return a pointer to the Metal command queue used by this context.
+   */
+  MTLCommandQueue *metalCommandQueue();
+
+  /**
+   * Return a pointer to the Metal device associated with this context.
+   */
+  MTLDevice *metalDevice();
+
+  /**
+   * Register present callback
+   */
+  void metalRegisterPresentCallback(void (*callback)(
+      MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>));
 
  private:
   /** Metal state */
+  /* Set this flag to `true` when rendering with Metal API for Viewport.
+   * TODO(Metal): This should be assigned to externally. */
+  bool m_useMetalForRendering = false;
   NSView *m_metalView;
   CAMetalLayer *m_metalLayer;
   MTLCommandQueue *m_metalCmdQueue;
   MTLRenderPipelineState *m_metalRenderPipeline;
+  bool m_ownsMetalDevice;
 
   /** OpenGL state, for GPUs that don't support Metal */
   NSOpenGLView *m_openGLView;
@@ -102,9 +134,31 @@ class GHOST_ContextCGL : public GHOST_Context {
   /** The virtualized default frame-buffer. */
   unsigned int m_defaultFramebuffer;
 
-  /** The virtualized default frame-buffer's texture. */
-  MTLTexture *m_defaultFramebufferMetalTexture;
-
+  /** The virtualized default framebuffer's texture */
+  /**
+   * Texture that you can render into with Metal. The texture will be
+   * composited on top of `m_defaultFramebufferMetalTexture` whenever
+   * `swapBuffers` is called.
+   */
+  static const int METAL_SWAPCHAIN_SIZE = 3;
+  struct MTLSwapchainTexture {
+    id<MTLTexture> texture;
+    unsigned int index;
+  };
+  MTLSwapchainTexture m_defaultFramebufferMetalTexture[METAL_SWAPCHAIN_SIZE];
+  unsigned int current_swapchain_index = 0;
+
+  /* Present callback.
+   * We use this such that presentation can be controlled from within the Metal
+   * Context. This is required for optimal performance and clean control flow.
+   * Also helps ensure flickering does not occur by present being dependent
+   * on existing submissions. */
+  void (*contextPresentCallback)(MTLRenderPassDescriptor *,
+                                 id<MTLRenderPipelineState>,
+                                 id<MTLTexture>,
+                                 id<CAMetalDrawable>);
+
+  int mtl_SwapInterval;
   const bool m_debug;
 
   /** The first created OpenGL context (for sharing display lists) */
@@ -117,4 +171,5 @@ class GHOST_ContextCGL : public GHOST_Context {
   void metalInitFramebuffer();
   void metalUpdateFramebuffer();
   void metalSwapBuffers();
+  void initClear();
 };
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index 488aa58aa59..6a0fed79fb0 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -55,139 +55,277 @@ GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
       m_openGLView(openGLView),
       m_openGLContext(nil),
       m_defaultFramebuffer(0),
-      m_defaultFramebufferMetalTexture(nil),
       m_debug(false)
 {
+  /* Init Metal Swapchain. */
+  current_swapchain_index = 0;
+  for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
+    m_defaultFramebufferMetalTexture[i].texture = nil;
+    m_defaultFramebufferMetalTexture[i].index = i;
+  }
   if (m_metalView) {
+    m_ownsMetalDevice = false;
     metalInit();
   }
+  else {
+    /* Prepare offscreen GHOST Context Metal device. */
+    id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
+
+    if (m_debug) {
+      printf("Selected Metal Device: %s\n", [metalDevice.name UTF8String]);
+    }
+
+    m_ownsMetalDevice = true;
+    if (metalDevice) {
+      m_metalLayer = [[CAMetalLayer alloc] init];
+      [m_metalLayer setEdgeAntialiasingMask:0];
+      [m_metalLayer setMasksToBounds:NO];
+      [m_metalLayer setOpaque:YES];
+      [m_metalLayer setFramebufferOnly:YES];
+      [m_metalLayer setPresentsWithTransaction:NO];
+      [m_metalLayer removeAllAnimations];
+      [m_metalLayer setDevice:metalDevice];
+      m_metalLayer.allowsNextDrawableTimeout = NO;
+      metalInit();
+    }
+    else {
+      ghost_fatal_error_dialog(
+          "[ERROR] Failed to create Metal device for offscreen GHOST Context.\n");
+    }
+  }
+
+  /* Initialise swapinterval. */
+  mtl_SwapInterval = 60;
 }
 
 GHOST_ContextCGL::~GHOST_ContextCGL()
 {
   metalFree();
 
-  if (m_openGLContext != nil) {
-    if (m_openGLContext == [NSOpenGLContext currentContext]) {
-      [NSOpenGLContext clearCurrentContext];
+  if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+    if (m_openGLContext != nil) {
+      if (m_openGLContext == [NSOpenGLContext currentContext]) {
+        [NSOpenGLContext clearCurrentContext];
 
-      if (m_openGLView) {
-        [m_openGLView clearGLContext];
+        if (m_openGLView) {
+          [m_openGLView clearGLContext];
+        }
       }
-    }
 
-    if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
-      assert(s_sharedCount > 0);
+      if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
+        assert(s_sharedCount > 0);
 
-      s_sharedCount--;
+        s_sharedCount--;
 
-      if (s_sharedCount == 0)
-        s_sharedOpenGLContext = nil;
+        if (s_sharedCount == 0)
+          s_sharedOpenGLContext = nil;
 
-      [m_openGLContext release];
+        [m_openGLContext release];
+      }
+    }
+#endif
+  }
+
+  if (m_ownsMetalDevice) {
+    if (m_metalLayer) {
+      [m_metalLayer release];
+      m_metalLayer = nil;
     }
   }
 }
 
 GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
 {
-  if (m_openGLContext != nil) {
-    if (m_metalView) {
-      metalSwapBuffers();
+  GHOST_TSuccess return_value = GHOST_kFailure;
+
+  if (!m_useMetalForRendering) {
+#if WITH_OPENGL
+    if (m_openGLContext != nil) {
+      if (m_metalView) {
+        metalSwapBuffers();
+      }
+      else if (m_openGLView) {
+        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+        [m_openGL

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list