GPU: Platform Support Level
authorJeroen Bakker <j.bakker@atmind.nl>
Thu, 3 Oct 2019 14:21:23 +0000 (16:21 +0200)
committerJeroen Bakker <j.bakker@atmind.nl>
Fri, 4 Oct 2019 14:23:39 +0000 (16:23 +0200)
Adds a check when starting blender if your platform is supported. We use a blacklist
as drivers are updated more regular then blender (stable releases).

The mechanism detects if the support level changed or has been validated by the user previously.
Changes can happen due to users updating their drivers, but also when we change the support
level in our code base.

When the user has seen the limited support level message it is saved in the user config.
It would be better to have a system specific config section, but currently not clear
what could benefit from that.

When the platform is unsupported or has limited support a dialog box will appear including a link
to our user manual describing what to do.

**Windows**
Windows uses the MessageBox that is provided by the windows kernel.

**X11**
We use a very lowlevel messagebox for X11. It is very limited in use and can be fine tuned when needed.

**SDL/APPLE**
There is no implementation for SDL or APPLE at this moment as the platform support feature targets mostly Windows users.

Reviewed By: brecht

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

33 files changed:
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/GHOST_Types.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemCocoa.mm
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
source/blender/blenkernel/BKE_appdir.h
source/blender/draw/engines/eevee/eevee_effects.c
source/blender/draw/engines/eevee/eevee_occlusion.c
source/blender/draw/intern/draw_manager_exec.c
source/blender/editors/screen/screen_draw.c
source/blender/editors/space_node/drawnode.c
source/blender/gpu/CMakeLists.txt
source/blender/gpu/GPU_extensions.h
source/blender/gpu/GPU_platform.h [new file with mode: 0644]
source/blender/gpu/intern/gpu_batch.c
source/blender/gpu/intern/gpu_draw.c
source/blender/gpu/intern/gpu_extensions.c
source/blender/gpu/intern/gpu_init_exit.c
source/blender/gpu/intern/gpu_platform.c [new file with mode: 0644]
source/blender/gpu/intern/gpu_private.h
source/blender/gpu/intern/gpu_shader.c
source/blender/gpu/intern/gpu_texture.c
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/intern/wm_init_exit.c
source/blender/windowmanager/intern/wm_platform_support.c [new file with mode: 0644]
source/blender/windowmanager/intern/wm_platform_support.h [new file with mode: 0644]
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/intern/wm_window_private.h [new file with mode: 0644]

index 220e7aab8cdefe5a653daf885138617f95eb14ad..5e0216c03391103a2f548905dfc721d307be973a 100644 (file)
@@ -65,6 +65,21 @@ extern GHOST_SystemHandle GHOST_CreateSystem(void);
  */
 extern GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle);
 
+/**
+ * Show a system message box to the user
+ * \param systemhandle    The handle to the system
+ * \param title           Title of the message box
+ * \param message         Message of the message box
+ * \param link            Optional (hyper)link to a webpage to show when pressing help
+ * \param dialog_options  Options to configure the message box.
+ * \return void.
+ */
+extern void GHOST_ShowMessageBox(GHOST_SystemHandle systemhandle,
+                                 const char *title,
+                                 const char *message,
+                                 const char *link,
+                                 GHOST_DialogOptions dialog_options);
+
 /**
  * Creates an event consumer object
  * \param eventCallback The event callback routine.
@@ -191,6 +206,7 @@ extern GHOST_WindowHandle GHOST_CreateDialogWindow(GHOST_SystemHandle systemhand
  * Create a new offscreen context.
  * Never explicitly delete the context, use disposeContext() instead.
  * \param systemhandle The handle to the system
+ * \param platform_support_callback An optional callback to check platform support
  * \return A handle to the new context ( == NULL if creation failed).
  */
 extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle);
index 1eeb6cd852f2c4a0bf7fa5f0a6056c7659aba7f9..d89785ad9b2b81191582e0d8040ef77b90b21535 100644 (file)
@@ -437,6 +437,23 @@ class GHOST_ISystem {
    */
   virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const = 0;
 
+  /***************************************************************************************
+   * System Message Box.
+   ***************************************************************************************/
+
+  /**
+   * Show a system message box
+   *
+   * \param title                   The title of the message box
+   * \param message                 The message to display
+   * \param link                    An optional hyperlink
+   * \param dialog_options Options  how to display the message
+   */
+  virtual GHOST_TSuccess showMessageBox(const char * /*title*/,
+                                        const char * /*message*/,
+                                        const char * /*link*/,
+                                        GHOST_DialogOptions /*dialog_options*/) const = 0;
+
  protected:
   /**
    * Initialize the system.
index 891f9d982b91017f9ba4aa4226ee596c0cb89629..32472373b1768f0ed9ca6845aca7f6c54fe86cfe 100644 (file)
@@ -58,6 +58,11 @@ typedef enum {
   GHOST_glAlphaBackground = (1 << 2),
 } GHOST_GLFlags;
 
+typedef enum GHOST_DialogOptions {
+  GHOST_DialogWarning = (1 << 0),
+  GHOST_DialogError = (1 << 1),
+} GHOST_DialogOptions;
+
 #ifdef _MSC_VER
 typedef __int64 GHOST_TInt64;
 typedef unsigned __int64 GHOST_TUns64;
index d907c10570c675f6492368a19391c6e2af6d9a61..4d755de77ff77a415c0ee612efe0e813c4d530da 100644 (file)
@@ -47,6 +47,16 @@ GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle)
   return system->disposeSystem();
 }
 
+void GHOST_ShowMessageBox(GHOST_SystemHandle systemhandle,
+                          const char *title,
+                          const char *message,
+                          const char *link,
+                          GHOST_DialogOptions dialog_options)
+{
+  GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
+  system->showMessageBox(title, message, link, dialog_options);
+}
+
 GHOST_EventConsumerHandle GHOST_CreateEventConsumer(GHOST_EventCallbackProcPtr eventCallback,
                                                     GHOST_TUserDataPtr userdata)
 {
@@ -651,8 +661,13 @@ GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandl
 GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthandle)
 {
   GHOST_IContext *context = (GHOST_IContext *)contexthandle;
-
-  return context->activateDrawingContext();
+  if (context) {
+    return context->activateDrawingContext();
+  }
+  else {
+    GHOST_PRINT("GHOST_ActivateOpenGLContext: Context not valid");
+    return GHOST_kFailure;
+  }
 }
 
 GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle)
index 2e45592e0559a940dc10e035fa7b5f4cc621db32..ea85611870ad7e4fa307293ff6418fe70fcad540 100644 (file)
@@ -309,6 +309,21 @@ class GHOST_System : public GHOST_ISystem {
    */
   virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const = 0;
 
+  /**
+   * Show a system message box
+   * \param title                   The title of the message box
+   * \param message                 The message to display
+   * \param link                    An optional hyperlink
+   * \param dialog_options Options  how to display the message
+   */
+  virtual GHOST_TSuccess showMessageBox(const char * /*title*/,
+                                        const char * /*message*/,
+                                        const char * /*link*/,
+                                        GHOST_DialogOptions /*dialog_options*/) const
+  {
+    return GHOST_kFailure;
+  };
+
  protected:
   /**
    * Initialize the system.
index 68bac7d153bfb856ed469f2d9e4957926312dfb5..5902abf83820a227a5a26da82876be0587b6271e 100644 (file)
@@ -1383,7 +1383,8 @@ bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
 
   // Check open windows if some changes are not saved
   if (m_windowManager->getAnyModifiedState()) {
-    @autoreleasepool {
+    @autoreleasepool
+    {
       NSAlert *alert = [[NSAlert alloc] init];
       NSString *title = [NSString stringWithFormat:@"Opening %@", [filepath lastPathComponent]];
       NSString *text = @"Current document has not been saved.\nDo you really want to proceed?";
index 1b04e1fde79acd8aeee433999491384cd270bc54..1423e0df8ec46c175992b3d643f95a885adf1ec9 100644 (file)
@@ -31,6 +31,7 @@
 #include <shlobj.h>
 #include <tlhelp32.h>
 #include <psapi.h>
+#include <shellapi.h>
 #include <windowsx.h>
 
 #include "utfconv.h"
@@ -358,14 +359,8 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
     goto finished;
   }
   else {
-    MessageBox(NULL,
-               "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n"
-               "Installing the latest driver for your graphics card may resolve the issue.\n\n"
-               "The program will now close.",
-               "Blender - Unsupported Graphics Card or Driver",
-               MB_OK | MB_ICONERROR);
     delete context;
-    exit();
+    return NULL;
   }
 
 #elif defined(WITH_GL_PROFILE_COMPAT)
@@ -1776,6 +1771,47 @@ void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
   }
 }
 
+/** \name Message Box
+ * \{ */
+static const char *MESSAGE_BOX_HELP_LINK_PTR = NULL;
+VOID CALLBACK showMessageBoxCallBack(LPHELPINFO lpHelpInfo)
+{
+  if (MESSAGE_BOX_HELP_LINK_PTR) {
+    ShellExecute(NULL, "open", MESSAGE_BOX_HELP_LINK_PTR, NULL, NULL, SW_SHOWNORMAL);
+  }
+}
+
+GHOST_TSuccess GHOST_SystemWin32::showMessageBox(const char *title,
+                                                 const char *message,
+                                                 const char *link,
+                                                 GHOST_DialogOptions dialog_options) const
+{
+  uint style = MB_OK |
+               (dialog_options & GHOST_DialogError ?
+                    MB_ICONERROR :
+                    dialog_options & GHOST_DialogWarning ? MB_ICONWARNING : MB_ICONINFORMATION);
+  bool show_help = link && strlen(link);
+  if (show_help) {
+    GHOST_ASSERT(MESSAGE_BOX_HELP_LINK_PTR == NULL,
+                 "showMessageBox: MESSAGE_BOX_HELP_LINK_PTR is in use");
+    style |= MB_HELP;
+    MESSAGE_BOX_HELP_LINK_PTR = link;
+  }
+
+  MSGBOXPARAMSA message_box_params = {0};
+  message_box_params.cbSize = sizeof(MSGBOXCALLBACK);
+  message_box_params.lpszText = message;
+  message_box_params.lpszCaption = title;
+  message_box_params.dwStyle = style;
+  message_box_params.lpszText = message;
+  message_box_params.lpfnMsgBoxCallback = showMessageBoxCallBack;
+
+  MessageBoxIndirectA(&message_box_params);
+  MESSAGE_BOX_HELP_LINK_PTR = NULL;
+  return GHOST_kSuccess;
+}
+/* \} */
+
 static DWORD GetParentProcessID(void)
 {
   HANDLE snapshot;
index a92dea84d705b037b5c387ec7c263a822a9603c8..6e803b3d760bc16adf42bf16ffebea3b8d585a3c 100644 (file)
@@ -204,6 +204,18 @@ class GHOST_SystemWin32 : public GHOST_System {
    */
   void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
 
+  /**
+   * Show a system message box
+   * \param title                   The title of the message box
+   * \param message                 The message to display
+   * \param link                    An optional hyperlink
+   * \param dialog_options Options  how to display the message
+   */
+  GHOST_TSuccess showMessageBox(const char *title,
+                                const char *message,
+                                const char *link,
+                                GHOST_DialogOptions dialog_options) const;
+
   /**
    * Creates a drag'n'drop event and pushes it immediately onto the event queue.
    * Called by GHOST_DropTargetWin32 class.
index a9435f3faff9ba8dbc0bf4b4455f1f9159b555a0..a6c6aaffd6f399c5b278ddfae06cabbb6f2b66ac 100644 (file)
@@ -2145,6 +2145,216 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
   }
 }
 
+/** \name Message Box
+ * \{ */
+class DialogData {
+ public:
+  /* Width of the dialog */
+  uint width;
+  /* Heigth of the dialog */
+  uint height;
+  /* Default padding (x direction) between controls and edge of dialog */
+  uint padding_x;
+  /* Default padding (y direction) between controls and edge of dialog */
+  uint padding_y;
+  /* Width of a single button */
+  uint button_width;
+  /* Height of a single button */
+  uint button_height;
+  /* Inset of a button to its text */
+  uint button_inset_x;
+  /* Size of the border of the button */
+  uint button_border_size;
+  /* Height of a line of text */
+  uint line_height;
+  /* offset of the text inside the button */
+  uint button_text_offset_y;
+
+  /* Construct a new DialogData with the default settings */
+  DialogData()
+      : width(640),
+        height(175),
+        padding_x(10),
+        padding_y(5),
+        button_width(50),
+        button_height(24),
+        button_inset_x(10),
+        button_border_size(1),
+        line_height(16)
+  {
+    button_text_offset_y = button_height - line_height;
+  }
+
+  void drawButton(Display *display,
+                  Window &window,
+                  GC &borderGC,
+                  GC &buttonGC,
+                  uint button_num,
+                  const char *label)
+  {
+    XFillRectangle(display,
+                   window,
+                   borderGC,
+                   width - (padding_x + button_width) * button_num,
+                   height - padding_y - button_height,
+                   button_width,
+                   button_height);
+
+    XFillRectangle(display,
+                   window,
+                   buttonGC,
+                   width - (padding_x + button_width) * button_num + button_border_size,
+                   height - padding_y - button_height + button_border_size,
+                   button_width - button_border_size * 2,
+                   button_height - button_border_size * 2);
+
+    XDrawString(display,
+                window,
+                borderGC,
+                width - (padding_x + button_width) * button_num + button_inset_x,
+                height - padding_y - button_text_offset_y,
+                label,
+                strlen(label));
+  }
+
+  /* Is the mouse inside the given button */
+  bool isInsideButton(XEvent &e, uint button_num)
+  {
+    return ((e.xmotion.y > height - padding_y - button_height) &&
+            (e.xmotion.y < height - padding_y) &&
+            (e.xmotion.x > width - (padding_x + button_width) * button_num) &&
+            (e.xmotion.x < width - padding_x - (padding_x + button_width) * (button_num - 1)));
+  }
+};
+
+static void split(const char *text, const char *seps, char ***str, int *count)
+{
+  char *tok, *data;
+  int i;
+  *count = 0;
+
+  data = strdup(text);
+  for (tok = strtok(data, seps); tok != NULL; tok = strtok(NULL, seps))
+    (*count)++;
+  free(data);
+
+  data = strdup(text);
+  *str = (char **)malloc((size_t)(*count) * sizeof(char *));
+  for (i = 0, tok = strtok(data, seps); tok != NULL; tok = strtok(NULL, seps), i++)
+    (*str)[i] = strdup(tok);
+  free(data);
+}
+
+GHOST_TSuccess GHOST_SystemX11::showMessageBox(const char *title,
+                                               const char *message,
+                                               const char *link,
+                                               GHOST_DialogOptions) const
+{
+  char **text_splitted = NULL;
+  int textLines = 0;
+  split(message, "\n", &text_splitted, &textLines);
+
+  DialogData dialog_data;
+  XSizeHints hints;
+
+  Window window;
+  XEvent e;
+  int screen = DefaultScreen(m_display);
+  window = XCreateSimpleWindow(m_display,
+                               RootWindow(m_display, screen),
+                               0,
+                               0,
+                               dialog_data.width,
+                               dialog_data.height,
+                               1,
+                               BlackPixel(m_display, screen),
+                               WhitePixel(m_display, screen));
+
+  /* Window Should not be resizable */
+  {
+    hints.flags = PSize | PMinSize | PMaxSize;
+    hints.min_width = hints.max_width = hints.base_width = dialog_data.width;
+    hints.min_height = hints.max_height = hints.base_height = dialog_data.height;
+    XSetWMNormalHints(m_display, window, &hints);
+  }
+
+  /* Set title */
+  {
+    Atom wm_Name = XInternAtom(m_display, "_NET_WM_NAME", False);
+    Atom utf8Str = XInternAtom(m_display, "UTF8_STRING", False);
+
+    Atom winType = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
+    Atom typeDialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+
+    XChangeProperty(m_display,
+                    window,
+                    wm_Name,
+                    utf8Str,
+                    8,
+                    PropModeReplace,
+                    (const unsigned char *)title,
+                    (int)strlen(title));
+
+    XChangeProperty(
+        m_display, window, winType, XA_ATOM, 32, PropModeReplace, (unsigned char *)&typeDialog, 1);
+  }
+
+  /* Create buttons GC */
+  XGCValues buttonBorderGCValues;
+  buttonBorderGCValues.foreground = BlackPixel(m_display, screen);
+  buttonBorderGCValues.background = WhitePixel(m_display, screen);
+  XGCValues buttonGCValues;
+  buttonGCValues.foreground = WhitePixel(m_display, screen);
+  buttonGCValues.background = BlackPixel(m_display, screen);
+
+  GC buttonBorderGC = XCreateGC(m_display, window, GCForeground, &buttonBorderGCValues);
+  GC buttonGC = XCreateGC(m_display, window, GCForeground, &buttonGCValues);
+
+  XSelectInput(m_display, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
+  XMapWindow(m_display, window);
+
+  while (1) {
+    XNextEvent(m_display, &e);
+    if (e.type == Expose) {
+      for (int i = 0; i < textLines; i++) {
+        XDrawString(m_display,
+                    window,
+                    DefaultGC(m_display, screen),
+                    dialog_data.padding_x,
+                    dialog_data.padding_x + (i + 1) * dialog_data.line_height,
+                    text_splitted[i],
+                    (int)strlen(text_splitted[i]));
+      }
+      dialog_data.drawButton(m_display, window, buttonBorderGC, buttonGC, 1, "Ok");
+      if (strlen(link)) {
+        dialog_data.drawButton(m_display, window, buttonBorderGC, buttonGC, 2, "Help");
+      }
+    }
+    else if (e.type == ButtonRelease) {
+      if (dialog_data.isInsideButton(e, 1)) {
+        break;
+      }
+      else if (strlen(link) && dialog_data.isInsideButton(e, 2)) {
+        string cmd = "xdg-open \"" + string(link) + "\"";
+        if (system(cmd.c_str()) != 0) {
+          GHOST_PRINTF("GHOST_SystemX11::showMessageBox: Unable to run system command [%s]", cmd);
+        }
+      }
+    }
+  }
+
+  for (int i = 0; i < textLines; i++) {
+    free(text_splitted[i]);
+  }
+  free(text_splitted);
+
+  XDestroyWindow(m_display, window);
+  XFreeGC(m_display, buttonBorderGC);
+  XFreeGC(m_display, buttonGC);
+  return GHOST_kSuccess;
+}
+/* \} */
+
 #ifdef WITH_XDND
 GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType,
                                                   GHOST_TDragnDropTypes draggedObjectType,
index 6ad2d6decae1b138a083166496d690a0d7a886ab..ba7371c6ea0c5e29d9788f6914306e8f28bf3e33 100644 (file)
@@ -233,6 +233,17 @@ class GHOST_SystemX11 : public GHOST_System {
    */
   void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
 
+  /**
+   * Show a system message box
+   * \param title                   The title of the message box
+   * \param message                 The message to display
+   * \param link                    An optional hyperlink
+   * \param dialog_options Options  how to display the message
+   */
+  GHOST_TSuccess showMessageBox(const char *title,
+                                const char *message,
+                                const char *link,
+                                GHOST_DialogOptions dialog_options) const;
 #ifdef WITH_XDND
   /**
    * Creates a drag'n'drop event and pushes it immediately onto the event queue.
index e55cb69a5c62091e9f5f8a95b9ec2367d9caf4b4..b35abb1ecef33e50e8d2bad1df234bae07531e54 100644 (file)
@@ -91,5 +91,6 @@ enum {
 #define BLENDER_QUIT_FILE "quit.blend"
 #define BLENDER_BOOKMARK_FILE "bookmarks.txt"
 #define BLENDER_HISTORY_FILE "recent-files.txt"
+#define BLENDER_PLATFORM_SUPPORT_FILE "platform_support.txt"
 
 #endif /* __BKE_APPDIR_H__ */
index 7df1c299454d2fc6eca87a382e7671bf55bd95ae..d59d1f56e92398578c544e2de5e17a636cf7bc4a 100644 (file)
@@ -29,6 +29,7 @@
 #include "eevee_private.h"
 #include "GPU_texture.h"
 #include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_state.h"
 
 static struct {
index c60aa173b489a924b460fcdcea37a47f216488bb..48e9b5bcc13f42c6342a681661da0c4bfd8e07d3 100644 (file)
@@ -33,6 +33,7 @@
 #include "eevee_private.h"
 
 #include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_state.h"
 
 static struct {
index 3de9ce74dbca176e02f5da331f654133aa657c74..fa7c44c1b1f7b45307a76f7c549e27465c17ef51 100644 (file)
@@ -29,6 +29,7 @@
 #include "BKE_global.h"
 
 #include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "intern/gpu_shader_private.h"
 #include "intern/gpu_primitive_private.h"
 
index 316604156de01f1f456d777faf5b0648195f4e08..a6b8bba73e320e4f80969243957de9c3ff9538a3 100644 (file)
@@ -21,7 +21,7 @@
 #include "ED_screen.h"
 
 #include "GPU_batch_presets.h"
-#include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_framebuffer.h"
 #include "GPU_immediate.h"
 #include "GPU_matrix.h"
index c0eedfeea1d0067e3a7f6e34e9a55ec52a02cf6b..a5b18ff7589bc373693435f0bd452df98ff309b4 100644 (file)
@@ -47,7 +47,7 @@
 
 #include "GPU_batch.h"
 #include "GPU_batch_presets.h"
-#include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_immediate.h"
 #include "GPU_matrix.h"
 #include "GPU_state.h"
index bc08da4b2cb98a236d22b00663057e5a9adec96e..9320e8491942e767067531ba3661f85d5e5a2831 100644 (file)
@@ -69,6 +69,7 @@ set(SRC
   intern/gpu_init_exit.c
   intern/gpu_material.c
   intern/gpu_matrix.c
+  intern/gpu_platform.c
   intern/gpu_primitive.c
   intern/gpu_select.c
   intern/gpu_select_pick.c
@@ -101,6 +102,7 @@ set(SRC
   GPU_legacy_stubs.h
   GPU_material.h
   GPU_matrix.h
+  GPU_platform.h
   GPU_primitive.h
   GPU_select.h
   GPU_shader.h
index 023cbb804d9f31e0e9f3362c1f3bb6c205fa7b44..245f7f475105e2d93c7881309678007d2cd37bf5 100644 (file)
@@ -55,34 +55,6 @@ void GPU_mem_stats_get(int *totalmem, int *freemem);
 
 void GPU_code_generate_glsl_lib(void);
 
-/* GPU Types */
-
-typedef enum eGPUDeviceType {
-  GPU_DEVICE_NVIDIA = (1 << 0),
-  GPU_DEVICE_ATI = (1 << 1),
-  GPU_DEVICE_INTEL = (1 << 2),
-  GPU_DEVICE_INTEL_UHD = (1 << 3),
-  GPU_DEVICE_SOFTWARE = (1 << 4),
-  GPU_DEVICE_UNKNOWN = (1 << 5),
-  GPU_DEVICE_ANY = (0xff),
-} eGPUDeviceType;
-
-typedef enum eGPUOSType {
-  GPU_OS_WIN = (1 << 8),
-  GPU_OS_MAC = (1 << 9),
-  GPU_OS_UNIX = (1 << 10),
-  GPU_OS_ANY = (0xff00),
-} eGPUOSType;
-
-typedef enum eGPUDriverType {
-  GPU_DRIVER_OFFICIAL = (1 << 16),
-  GPU_DRIVER_OPENSOURCE = (1 << 17),
-  GPU_DRIVER_SOFTWARE = (1 << 18),
-  GPU_DRIVER_ANY = (0xff0000),
-} eGPUDriverType;
-
-bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
new file mode 100644 (file)
index 0000000..f199a74
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#ifndef __GPU_PLATFORM_H__
+#define __GPU_PLATFORM_H__
+
+#include "BLI_sys_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* GPU platform support */
+
+/* GPU Types */
+typedef enum eGPUDeviceType {
+  GPU_DEVICE_NVIDIA = (1 << 0),
+  GPU_DEVICE_ATI = (1 << 1),
+  GPU_DEVICE_INTEL = (1 << 2),
+  GPU_DEVICE_INTEL_UHD = (1 << 3),
+  GPU_DEVICE_SOFTWARE = (1 << 4),
+  GPU_DEVICE_UNKNOWN = (1 << 5),
+  GPU_DEVICE_ANY = (0xff),
+} eGPUDeviceType;
+
+typedef enum eGPUOSType {
+  GPU_OS_WIN = (1 << 8),
+  GPU_OS_MAC = (1 << 9),
+  GPU_OS_UNIX = (1 << 10),
+  GPU_OS_ANY = (0xff00),
+} eGPUOSType;
+
+typedef enum eGPUDriverType {
+  GPU_DRIVER_OFFICIAL = (1 << 16),
+  GPU_DRIVER_OPENSOURCE = (1 << 17),
+  GPU_DRIVER_SOFTWARE = (1 << 18),
+  GPU_DRIVER_ANY = (0xff0000),
+} eGPUDriverType;
+
+typedef enum eGPUSupportLevel {
+  GPU_SUPPORT_LEVEL_SUPPORTED,
+  GPU_SUPPORT_LEVEL_LIMITED,
+  GPU_SUPPORT_LEVEL_UNSUPPORTED,
+} eGPUSupportLevel;
+
+bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
+eGPUSupportLevel GPU_platform_support_level(void);
+const char *GPU_platform_support_level_key(void);
+const char *GPU_platform_gpu_name(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GPU_PLATFORM_H__ */
index 9c1e627b6fdff3f99b47274f7e3eeaeb6ad58190..dcc94cd843c0cdc702f3e3c9e81afe76c6695366 100644 (file)
@@ -29,6 +29,7 @@
 #include "GPU_batch.h"
 #include "GPU_batch_presets.h"
 #include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_matrix.h"
 #include "GPU_shader.h"
 
index c9ae6c60293a99d5f4b56783350c986065a0e178..7fa2eb6424c2bb26e7d9f04c40b5efe3e9018387 100644 (file)
@@ -66,6 +66,7 @@
 #include "GPU_draw.h"
 #include "GPU_extensions.h"
 #include "GPU_glew.h"
+#include "GPU_platform.h"
 #include "GPU_texture.h"
 
 #include "PIL_time.h"
index c6425854ee440b8ac9896f07a89eb362d4ed80fe..33f918559f7c5daa4363ffa00d5ef67f4dcd57cf 100644 (file)
@@ -35,6 +35,7 @@
 #include "GPU_framebuffer.h"
 #include "GPU_glew.h"
 #include "GPU_texture.h"
+#include "GPU_platform.h"
 
 #include "intern/gpu_private.h"
 
@@ -68,9 +69,6 @@ static struct GPUGlobal {
   GLint maxubosize;
   GLint maxubobinds;
   int samples_color_texture_max;
-  eGPUDeviceType device;
-  eGPUOSType os;
-  eGPUDriverType driver;
   float line_width_range[2];
   /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers
    * calculate dfdy in shader differently when drawing to an offscreen buffer. First
@@ -126,13 +124,6 @@ static void gpu_detect_mip_render_workaround(void)
   GPU_texture_free(tex);
 }
 
-/* GPU Types */
-
-bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
-{
-  return (GG.device & device) && (GG.os & os) && (GG.driver & driver);
-}
-
 /* GPU Extensions */
 
 int GPU_max_texture_size(void)
@@ -266,11 +257,7 @@ void gpu_extensions_init(void)
   const char *renderer = (const char *)glGetString(GL_RENDERER);
   const char *version = (const char *)glGetString(GL_VERSION);
 
-  if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
-    GG.device = GPU_DEVICE_ATI;
-    GG.driver = GPU_DRIVER_OFFICIAL;
-
-#ifdef _WIN32
+  if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) {
     if (strstr(version, "4.5.13399") || strstr(version, "4.5.13417") ||
         strstr(version, "4.5.13422")) {
       /* The renderers include:
@@ -282,75 +269,14 @@ void gpu_extensions_init(void)
 
       GG.unused_fb_slot_workaround = true;
     }
-#endif
+  }
 
-#if defined(__APPLE__)
+  if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) {
     if (strstr(renderer, "AMD Radeon Pro") || strstr(renderer, "AMD Radeon R9") ||
         strstr(renderer, "AMD Radeon RX")) {
       GG.depth_blitting_workaround = true;
     }
-#endif
-  }
-  else if (strstr(vendor, "NVIDIA")) {
-    GG.device = GPU_DEVICE_NVIDIA;
-    GG.driver = GPU_DRIVER_OFFICIAL;
-  }
-  else if (strstr(vendor, "Intel") ||
-           /* src/mesa/drivers/dri/intel/intel_context.c */
-           strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) {
-    GG.device = GPU_DEVICE_INTEL;
-    GG.driver = GPU_DRIVER_OFFICIAL;
-
-    if (strstr(renderer, "UHD Graphics") ||
-        /* Not UHD but affected by the same bugs. */
-        strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2")) {
-      GG.device |= GPU_DEVICE_INTEL_UHD;
-    }
   }
-  else if ((strstr(renderer, "Mesa DRI R")) ||
-           (strstr(renderer, "Radeon") && strstr(vendor, "X.Org")) ||
-           (strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
-           (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
-           (strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) {
-    GG.device = GPU_DEVICE_ATI;
-    GG.driver = GPU_DRIVER_OPENSOURCE;
-  }
-  else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
-    GG.device = GPU_DEVICE_NVIDIA;
-    GG.driver = GPU_DRIVER_OPENSOURCE;
-  }
-  else if (strstr(vendor, "Mesa")) {
-    GG.device = GPU_DEVICE_SOFTWARE;
-    GG.driver = GPU_DRIVER_SOFTWARE;
-  }
-  else if (strstr(vendor, "Microsoft")) {
-    GG.device = GPU_DEVICE_SOFTWARE;
-    GG.driver = GPU_DRIVER_SOFTWARE;
-  }
-  else if (strstr(renderer, "Apple Software Renderer")) {
-    GG.device = GPU_DEVICE_SOFTWARE;
-    GG.driver = GPU_DRIVER_SOFTWARE;
-  }
-  else if (strstr(renderer, "llvmpipe")) {
-    GG.device = GPU_DEVICE_SOFTWARE;
-    GG.driver = GPU_DRIVER_SOFTWARE;
-  }
-  else {
-    printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
-    printf("Detected OpenGL configuration:\n");
-    printf("Vendor: %s\n", vendor);
-    printf("Renderer: %s\n", renderer);
-    GG.device = GPU_DEVICE_ANY;
-    GG.driver = GPU_DRIVER_ANY;
-  }
-
-#ifdef _WIN32
-  GG.os = GPU_OS_WIN;
-#elif defined(__APPLE__)
-  GG.os = GPU_OS_MAC;
-#else
-  GG.os = GPU_OS_UNIX;
-#endif
 
   GG.glew_arb_base_instance_is_supported = GLEW_ARB_base_instance;
   gpu_detect_mip_render_workaround();
@@ -372,11 +298,12 @@ void gpu_extensions_init(void)
   GG.dfdyfactors[0] = 1.0;
   GG.dfdyfactors[1] = 1.0;
 
-  if ((strstr(vendor, "ATI") && strstr(version, "3.3.10750"))) {
+  if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) &&
+      strstr(version, "3.3.10750")) {
     GG.dfdyfactors[0] = 1.0;
     GG.dfdyfactors[1] = -1.0;
   }
-  else if ((GG.device == GPU_DEVICE_INTEL) && (GG.os == GPU_OS_WIN)) {
+  else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) {
     if (strstr(version, "4.0.0 - Build 10.18.10.3308") ||
         strstr(version, "4.0.0 - Build 9.18.10.3186") ||
         strstr(version, "4.0.0 - Build 9.18.10.3165") ||
@@ -401,8 +328,7 @@ void gpu_extensions_init(void)
       GG.context_local_shaders_workaround = true;
     }
   }
-  else if ((GG.device == GPU_DEVICE_ATI) && (GG.os == GPU_OS_UNIX) &&
-           (GG.driver == GPU_DRIVER_OPENSOURCE)) {
+  else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
     /* See T70187: merging vertices fail. This has been tested from 18.2.2 till 19.3.0~dev of the
      * Mesa driver */
     GG.unused_fb_slot_workaround = true;
index 0009e7d8c47f2e2dd1b74a647decb77e9378fd12..7b6016e11cb80a866c41df20ad2366df92910245 100644 (file)
@@ -46,7 +46,7 @@ void GPU_init(void)
   }
 
   initialized = true;
-
+  gpu_platform_init();
   gpu_extensions_init(); /* must come first */
 
   gpu_codegen_init();
@@ -82,7 +82,8 @@ void GPU_exit(void)
   gpu_framebuffer_module_exit();
   gpu_codegen_exit();
 
-  gpu_extensions_exit(); /* must come last */
+  gpu_extensions_exit();
+  gpu_platform_exit(); /* must come last */
 
   initialized = false;
 }
diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.c
new file mode 100644 (file)
index 0000000..bcdf322
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Wrap OpenGL features such as textures, shaders and GLSL
+ * with checks for drivers and GPU support.
+ */
+#include "GPU_platform.h"
+#include "GPU_glew.h"
+#include "gpu_private.h"
+
+#include <string.h>
+
+#include "BLI_dynstr.h"
+#include "BLI_string.h"
+
+#include "MEM_guardedalloc.h"
+
+static struct GPUPlatformGlobal {
+  bool initialized;
+  eGPUDeviceType device;
+  eGPUOSType os;
+  eGPUDriverType driver;
+  eGPUSupportLevel support_level;
+  char *support_key;
+  char *gpu_name;
+} GPG = {false};
+
+typedef struct GPUPlatformSupportTest {
+  eGPUSupportLevel support_level;
+  eGPUDeviceType device;
+  eGPUOSType os;
+  eGPUDriverType driver;
+  const char *vendor;
+  const char *renderer;
+  const char *version;
+} GPUPlatformSupportTest;
+
+// clang-format off
+static GPUPlatformSupportTest GPU_PLATFORM_SUPPORT_TESTS[] = {
+    /* AMD has confirmed that drivers with this specific OpenGL backend
+     * has issues. The issue is that clearing multiple color buffers at once is
+     * not working, but is part of the OpenGL specification. Their
+     * advice is to tell our users to not use this driver. (T69972) */
+    {GPU_SUPPORT_LEVEL_UNSUPPORTED, GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_ANY, "", "", "4.5.13571"},
+
+    /* This terminator record must be the last item */
+    {-1, GPU_DEVICE_ANY, GPU_OS_ANY, GPU_DRIVER_ANY, "", "", ""}};
+// clang-format on
+
+static bool gpu_platform_support_match(const GPUPlatformSupportTest *test_record,
+                                       const char *vendor,
+                                       const char *renderer,
+                                       const char *version)
+{
+  return GPU_type_matches(test_record->device, test_record->os, test_record->driver) &&
+         (strstr(vendor, test_record->vendor) && strstr(renderer, test_record->renderer) &&
+          strstr(version, test_record->version));
+}
+
+eGPUSupportLevel GPU_platform_support_level(void)
+{
+  return GPG.support_level;
+}
+
+const char *GPU_platform_support_level_key(void)
+{
+  return GPG.support_key;
+}
+
+const char *GPU_platform_gpu_name(void)
+{
+  return GPG.gpu_name;
+}
+
+/* GPU Types */
+bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
+{
+  return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver);
+}
+
+static char *gpu_platform_create_key(eGPUSupportLevel support_level,
+                                     const char *vendor,
+                                     const char *renderer,
+                                     const char *version)
+{
+  DynStr *ds = BLI_dynstr_new();
+  BLI_dynstr_append(ds, "{");
+  BLI_dynstr_append(ds, vendor);
+  BLI_dynstr_append(ds, "/");
+  BLI_dynstr_append(ds, renderer);
+  BLI_dynstr_append(ds, "/");
+  BLI_dynstr_append(ds, version);
+  BLI_dynstr_append(ds, "}");
+  BLI_dynstr_append(ds, "=");
+  if (support_level == GPU_SUPPORT_LEVEL_SUPPORTED) {
+    BLI_dynstr_append(ds, "SUPPORTED");
+  }
+  else if (support_level == GPU_SUPPORT_LEVEL_LIMITED) {
+    BLI_dynstr_append(ds, "LIMITED");
+  }
+  else {
+    BLI_dynstr_append(ds, "UNSUPPORTED");
+  }
+
+  char *support_key = BLI_dynstr_get_cstring(ds);
+  BLI_dynstr_free(ds);
+  BLI_str_replace_char(support_key, '\n', ' ');
+  BLI_str_replace_char(support_key, '\r', ' ');
+  return support_key;
+}
+
+static char *gpu_platform_create_gpu_name(const char *vendor,
+                                          const char *renderer,
+                                          const char *version)
+{
+  DynStr *ds = BLI_dynstr_new();
+  BLI_dynstr_append(ds, vendor);
+  BLI_dynstr_append(ds, " ");
+  BLI_dynstr_append(ds, renderer);
+  BLI_dynstr_append(ds, " ");
+  BLI_dynstr_append(ds, version);
+
+  char *gpu_name = BLI_dynstr_get_cstring(ds);
+  BLI_dynstr_free(ds);
+  BLI_str_replace_char(gpu_name, '\n', ' ');
+  BLI_str_replace_char(gpu_name, '\r', ' ');
+  return gpu_name;
+}
+
+void gpu_platform_init(void)
+{
+  if (GPG.initialized) {
+    return;
+  }
+
+#ifdef _WIN32
+  GPG.os = GPU_OS_WIN;
+#elif defined(__APPLE__)
+  GPG.os = GPU_OS_MAC;
+#else
+  GPG.os = GPU_OS_UNIX;
+#endif
+
+  const char *vendor = (const char *)glGetString(GL_VENDOR);
+  const char *renderer = (const char *)glGetString(GL_RENDERER);
+  const char *version = (const char *)glGetString(GL_VERSION);
+
+  if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
+    GPG.device = GPU_DEVICE_ATI;
+    GPG.driver = GPU_DRIVER_OFFICIAL;
+  }
+  else if (strstr(vendor, "NVIDIA")) {
+    GPG.device = GPU_DEVICE_NVIDIA;
+    GPG.driver = GPU_DRIVER_OFFICIAL;
+  }
+  else if (strstr(vendor, "Intel") ||
+           /* src/mesa/drivers/dri/intel/intel_context.c */
+           strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) {
+    GPG.device = GPU_DEVICE_INTEL;
+    GPG.driver = GPU_DRIVER_OFFICIAL;
+
+    if (strstr(renderer, "UHD Graphics") ||
+        /* Not UHD but affected by the same bugs. */
+        strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2")) {
+      GPG.device |= GPU_DEVICE_INTEL_UHD;
+    }
+  }
+  else if ((strstr(renderer, "Mesa DRI R")) ||
+           (strstr(renderer, "Radeon") && strstr(vendor, "X.Org")) ||
+           (strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
+           (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
+           (strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) {
+    GPG.device = GPU_DEVICE_ATI;
+    GPG.driver = GPU_DRIVER_OPENSOURCE;
+  }
+  else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
+    GPG.device = GPU_DEVICE_NVIDIA;
+    GPG.driver = GPU_DRIVER_OPENSOURCE;
+  }
+  else if (strstr(vendor, "Mesa")) {
+    GPG.device = GPU_DEVICE_SOFTWARE;
+    GPG.driver = GPU_DRIVER_SOFTWARE;
+  }
+  else if (strstr(vendor, "Microsoft")) {
+    GPG.device = GPU_DEVICE_SOFTWARE;
+    GPG.driver = GPU_DRIVER_SOFTWARE;
+  }
+  else if (strstr(renderer, "Apple Software Renderer")) {
+    GPG.device = GPU_DEVICE_SOFTWARE;
+    GPG.driver = GPU_DRIVER_SOFTWARE;
+  }
+  else if (strstr(renderer, "llvmpipe")) {
+    GPG.device = GPU_DEVICE_SOFTWARE;
+    GPG.driver = GPU_DRIVER_SOFTWARE;
+  }
+  else {
+    printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
+    printf("Detected OpenGL configuration:\n");
+    printf("Vendor: %s\n", vendor);
+    printf("Renderer: %s\n", renderer);
+    GPG.device = GPU_DEVICE_ANY;
+    GPG.driver = GPU_DRIVER_ANY;
+  }
+
+  /* Detect support level */
+  if (!GLEW_VERSION_3_3) {
+    GPG.support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
+  }
+  else {
+    for (int index = 0; GPU_PLATFORM_SUPPORT_TESTS[index].support_level != -1; index++) {
+      GPUPlatformSupportTest *test = &GPU_PLATFORM_SUPPORT_TESTS[index];
+      if (gpu_platform_support_match(test, vendor, renderer, version)) {
+        GPG.support_level = test->support_level;
+        break;
+      }
+    }
+  }
+  GPG.support_key = gpu_platform_create_key(GPG.support_level, vendor, renderer, version);
+  GPG.gpu_name = gpu_platform_create_gpu_name(vendor, renderer, version);
+  GPG.initialized = true;
+}
+
+void gpu_platform_exit(void)
+{
+  MEM_SAFE_FREE(GPG.support_key);
+  MEM_SAFE_FREE(GPG.gpu_name);
+}
index b9af8f1b38c2dab3d06db4ba5aa5799ec0d7c32c..7846bff87f457e87f041b5aff21ea5918dae1a24 100644 (file)
 #ifndef __GPU_PRIVATE_H__
 #define __GPU_PRIVATE_H__
 
+/* call this before running any of the functions below */
+void gpu_platform_init(void);
+void gpu_platform_exit(void);
+
 /* call this before running any of the functions below */
 void gpu_extensions_init(void);
 void gpu_extensions_exit(void);
index 015df078228e1911b07993e9d1353e36d81ac19b..5df73d1a0c628e0650bc36a47bbfd142504b43fc 100644 (file)
@@ -36,6 +36,7 @@
 #include "DNA_space_types.h"
 
 #include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_matrix.h"
 #include "GPU_shader.h"
 #include "GPU_texture.h"
index a54d90f37f5ff8373605da0a61a2ba72fbee98cc..497fc13a2c82cd618b55665962f1bcf19070ea59 100644 (file)
@@ -38,6 +38,7 @@
 #include "GPU_extensions.h"
 #include "GPU_glew.h"
 #include "GPU_framebuffer.h"
+#include "GPU_platform.h"
 #include "GPU_texture.h"
 
 #include "gpu_context_private.h"
index ddd0ddb46da37ae1d5a72bd6b66c8e079127d014..ebb9e1106d67bed8afdfe4082090fb0453121bee 100644 (file)
@@ -69,6 +69,7 @@ set(SRC
   intern/wm_operators.c
   intern/wm_panel_type.c
   intern/wm_playanim.c
+  intern/wm_platform_support.c
   intern/wm_splash_screen.c
   intern/wm_stereo.c
   intern/wm_subwindow.c
index 70d8315384011e13c762d2663df6a68d2646777d..c8c35ba1bfccb6629526191749c9ab218363b1bf 100644 (file)
@@ -99,6 +99,7 @@
 #include "wm.h"
 #include "wm_files.h"
 #include "wm_window.h"
+#include "wm_platform_support.h"
 
 #include "ED_anim_api.h"
 #include "ED_armature.h"
@@ -314,6 +315,10 @@ void WM_init(bContext *C, int argc, const char **argv)
 #endif
     WM_init_opengl(G_MAIN);
 
+    if (!WM_platform_support_perform_checks()) {
+      exit(-1);
+    }
+
     UI_init();
   }
 
diff --git a/source/blender/windowmanager/intern/wm_platform_support.c b/source/blender/windowmanager/intern/wm_platform_support.c
new file mode 100644 (file)
index 0000000..9ed6f9f
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#include "wm_platform_support.h"
+#include "wm_window_private.h"
+
+#include <string.h>
+
+#include "BLI_sys_types.h"
+#include "BLI_dynstr.h"
+#include "BLI_path_util.h"
+#include "BLI_fileops.h"
+#include "BLI_string.h"
+#include "BLI_linklist.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_appdir.h"
+#include "BKE_global.h"
+
+#include "GPU_platform.h"
+
+#include "GHOST_C-api.h"
+
+#define WM_PLATFORM_SUPPORT_TEXT_SIZE 1024
+
+/* Check if user has already approved the given platform_support_key. */
+static bool wm_platform_support_check_approval(const char *platform_support_key, bool update)
+{
+  const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
+  bool result = false;
+
+  if (G.factory_startup) {
+    return result;
+  }
+
+  if (cfgdir) {
+    char filepath[FILE_MAX];
+    BLI_make_file_string("/", filepath, cfgdir, BLENDER_PLATFORM_SUPPORT_FILE);
+    LinkNode *lines = BLI_file_read_as_lines(filepath);
+    for (LinkNode *line_node = lines; line_node; line_node = line_node->next) {
+      char *line = line_node->link;
+      if (STREQ(line, platform_support_key)) {
+        result = true;
+        break;
+      }
+    }
+
+    if (!result && update) {
+      FILE *fp = BLI_fopen(filepath, "a");
+      if (fp) {
+        fprintf(fp, "%s\n", platform_support_key);
+        fclose(fp);
+      }
+    }
+
+    BLI_file_free_lines(lines);
+  }
+  return result;
+}
+
+static void wm_platform_support_create_link(char *link)
+{
+  DynStr *ds = BLI_dynstr_new();
+
+  BLI_dynstr_append(ds, "https://docs.blender.org/manual/en/dev/troubleshooting/gpu/");
+#if defined(_WIN32)
+  BLI_dynstr_append(ds, "windows/");
+#elif defined(__APPLE__)
+  BLI_dynstr_append(ds, "apple/");
+#else /* UNIX */
+  BLI_dynstr_append(ds, "linux/");
+#endif
+
+  if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+    BLI_dynstr_append(ds, "intel.html");
+  }
+  else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+    BLI_dynstr_append(ds, "nvidia.html");
+  }
+  else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+    BLI_dynstr_append(ds, "amd.html");
+  }
+  else {
+    BLI_dynstr_append(ds, "unknown.html");
+  }
+
+  BLI_assert(BLI_dynstr_get_len(ds) < WM_PLATFORM_SUPPORT_TEXT_SIZE);
+  BLI_dynstr_get_cstring_ex(ds, link);
+  BLI_dynstr_free(ds);
+}
+
+bool WM_platform_support_perform_checks()
+{
+  char title[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+  char message[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+  char link[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+
+  bool result = true;
+
+  eGPUSupportLevel support_level = GPU_platform_support_level();
+  const char *platform_key = GPU_platform_support_level_key();
+
+  /* check if previous check matches the current check. Don't update the approval when running in
+   * `background`. this could have been triggered by installing addons via installers.  */
+  if (support_level != GPU_SUPPORT_LEVEL_UNSUPPORTED && !G.factory_startup &&
+      wm_platform_support_check_approval(platform_key, !G.background)) {
+    /* if it matches the user has confirmed and whishes to use it */
+    return result;
+  }
+
+  /* update the message and link based on the found support level */
+  GHOST_DialogOptions dialog_options = 0;
+
+  switch (support_level) {
+    default:
+    case GPU_SUPPORT_LEVEL_SUPPORTED:
+      break;
+
+    case GPU_SUPPORT_LEVEL_LIMITED: {
+      size_t slen = 0;
+      STR_CONCAT(title, slen, "Blender - ");
+      STR_CONCAT(
+          title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Limited Platform Support"));
+      slen = 0;
+      STR_CONCAT(
+          message,
+          slen,
+          CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+                     "Your graphics card or driver has limited support. It may work, but with "
+                     "issues."));
+
+      /* TODO: Extra space is needed for the split function in GHOST_SystemX11. We should change
+       * the behavior in GHOST_SystemX11. */
+      STR_CONCAT(message, slen, "\n \n");
+      STR_CONCAT(message,
+                 slen,
+                 CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+                            "Press help to see if the support can be improved."));
+      STR_CONCAT(message, slen, "\n \n");
+      STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
+      STR_CONCAT(message, slen, GPU_platform_gpu_name());
+
+      dialog_options = GHOST_DialogWarning;
+      break;
+    }
+
+    case GPU_SUPPORT_LEVEL_UNSUPPORTED: {
+      size_t slen = 0;
+      STR_CONCAT(title, slen, "Blender - ");
+      STR_CONCAT(
+          title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Platform Unsupported"));
+      slen = 0;
+      STR_CONCAT(message,
+                 slen,
+                 CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+                            "Your graphics card or driver is not supported."));
+
+      STR_CONCAT(message, slen, "\n \n");
+      STR_CONCAT(message,
+                 slen,
+                 CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+                            "Press help to see if the support can be improved."));
+
+      STR_CONCAT(message, slen, "\n \n");
+      STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
+      STR_CONCAT(message, slen, GPU_platform_gpu_name());
+      STR_CONCAT(message, slen, "\n \n");
+
+      STR_CONCAT(message,
+                 slen,
+                 CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "The program will now close."));
+      dialog_options = GHOST_DialogError;
+      result = false;
+      break;
+    }
+  }
+  wm_platform_support_create_link(link);
+
+  bool show_message = ELEM(
+      support_level, GPU_SUPPORT_LEVEL_LIMITED, GPU_SUPPORT_LEVEL_UNSUPPORTED);
+
+  /* We are running in the background print the message in the console. */
+  if ((G.background || G.debug & G_DEBUG) && show_message) {
+    printf("%s\n\n%s\n%s\n", title, message, link);
+  }
+  if (G.background) {
+    /* don't show the messagebox when running in background mode. Printing to
+     * console is enough. */
+    result = true;
+  }
+  else if (show_message) {
+    WM_ghost_show_message_box(title, message, link, dialog_options);
+  }
+
+  return result;
+}
\ No newline at end of file
diff --git a/source/blender/windowmanager/intern/wm_platform_support.h b/source/blender/windowmanager/intern/wm_platform_support.h
new file mode 100644 (file)
index 0000000..a8e20f6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#ifndef __WM_PLATFORM_SUPPORT_H__
+#define __WM_PLATFORM_SUPPORT_H__
+
+#include "BLI_sys_types.h"
+
+bool WM_platform_support_perform_checks(void);
+
+#endif
index 1ae95917c99c3eabd26129dc3db7bdcb807ca479..ac10f9489a225b72173562a7eec8889b01df42bc 100644 (file)
@@ -61,7 +61,9 @@
 #include "wm.h"
 #include "wm_draw.h"
 #include "wm_files.h"
+#include "wm_platform_support.h"
 #include "wm_window.h"
+#include "wm_window_private.h"
 #include "wm_event_system.h"
 
 #include "ED_anim_api.h"
@@ -78,7 +80,7 @@
 #include "GPU_batch.h"
 #include "GPU_batch_presets.h"
 #include "GPU_draw.h"
-#include "GPU_extensions.h"
+#include "GPU_platform.h"
 #include "GPU_framebuffer.h"
 #include "GPU_init_exit.h"
 #include "GPU_immediate.h"
@@ -2431,4 +2433,12 @@ void WM_opengl_context_release(void *context)
   GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
 }
 
+void WM_ghost_show_message_box(const char *title,
+                               const char *message,
+                               const char *link,
+                               GHOST_DialogOptions dialog_options)
+{
+  BLI_assert(g_system);
+  GHOST_ShowMessageBox(g_system, title, message, link, dialog_options);
+}
 /** \} */
diff --git a/source/blender/windowmanager/intern/wm_window_private.h b/source/blender/windowmanager/intern/wm_window_private.h
new file mode 100644 (file)
index 0000000..189b777
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#ifndef __WM_WINDOW_PRIVATE_H__
+#define __WM_WINDOW_PRIVATE_H__
+
+#include "BLI_sys_types.h"
+#include "GHOST_Types.h"
+
+/* *************** Message box *************** */
+/* `WM_ghost_show_message_box` is implemented in `wm_windows.c` it is
+ * defined here as it was implemented to be used for showing
+ * a message to the user when the platform is not (fully) supported.
+ *
+ * In all other cases this message box should not be used. */
+void WM_ghost_show_message_box(const char *title,
+                               const char *message,
+                               const char *link,
+                               GHOST_DialogOptions dialog_options);
+
+#endif