GPU: Platform Support Level

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
This commit is contained in:
Jeroen Bakker 2019-10-03 16:21:23 +02:00
parent 45745f7862
commit 56dd7feb06
33 changed files with 999 additions and 125 deletions

View 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);

View 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.

View 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;

View 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)

View 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.

View 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?";

View 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;

View 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.

View 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,

View 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.

View 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__ */

View 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 {

View File

@ -33,6 +33,7 @@
#include "eevee_private.h"
#include "GPU_extensions.h"
#include "GPU_platform.h"
#include "GPU_state.h"
static struct {

View 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"

View 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"

View 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"

View 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

View 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

View File

@ -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__ */

View 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"

View 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"

View 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;

View 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;
}

View File

@ -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);
}

View File

@ -21,6 +21,10 @@
#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);

View 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"

View 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"

View 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

View 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();
}

View File

@ -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;
}

View File

@ -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

View 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);
}
/** \} */

View File

@ -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