Changeset View
Standalone View
source/blender/windowmanager/intern/wm_platform_support.c
- This file was added.
| /* | |||||
| * 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 "BLI_sys_types.h" | |||||
| #include "BLI_dynstr.h" | |||||
| #include "BLI_math_base.h" | |||||
| #include "BLI_path_util.h" | |||||
| #include "BLI_fileops.h" | |||||
| #include "BLT_translation.h" | |||||
| #include "BKE_appdir.h" | |||||
| #include "BKE_global.h" | |||||
| #include "MEM_guardedalloc.h" | |||||
| #include "wm_platform_support.h" | |||||
| #include "GPU_glew.h" | |||||
| #include <string.h> | |||||
| #include <stdio.h> | |||||
| typedef struct WM_PlatformSupport { | |||||
| /* Have we already checked. Must be the first field */ | |||||
| bool already_checked; | |||||
| } WM_PlatformSupport; | |||||
| typedef enum eWM_GPUVendor { | |||||
| WM_GPU_VENDOR_INTEL, | |||||
| WM_GPU_VENDOR_NVIDIA, | |||||
| WM_GPU_VENDOR_AMD, | |||||
| WM_GPU_VENDOR_UNKNOWN | |||||
| } eWM_GPUVendor; | |||||
| typedef enum eWM_SupportLevel { | |||||
| WM_SUPPORT_LEVEL_SUPPORTED, | |||||
| WM_SUPPORT_LEVEL_LIMITED, | |||||
| WM_SUPPORT_LEVEL_UNSUPPORTED, | |||||
| } eWM_SupportLevel; | |||||
| typedef struct WM_PlatformSupportTest { | |||||
| eWM_SupportLevel support_level; | |||||
| eWM_GPUVendor gpu_vendor; | |||||
| const char *vendor; | |||||
| const char *renderer; | |||||
| const char *version; | |||||
| } WM_PlatformSupportTest; | |||||
| static WM_PlatformSupport WM_PLATFORM_SUPPORT = {false}; | |||||
| static WM_PlatformSupportTest WM_PLATFORM_SUPPORT_TESTS[] = { | |||||
| #if defined(_WIN32) | |||||
| /* 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) */ | |||||
| {WM_SUPPORT_LEVEL_UNSUPPORTED, WM_GPU_VENDOR_AMD, "", "", "4.5.13517"}, | |||||
| #elif defined(__APPLE) | |||||
| #else /* UNIX */ | |||||
| /* DO NOT COMMIT! Just a test */ | |||||
| {WM_SUPPORT_LEVEL_UNSUPPORTED, WM_GPU_VENDOR_AMD, "", "", "4.5.13542"}, | |||||
| #endif | |||||
| {WM_SUPPORT_LEVEL_LIMITED, WM_GPU_VENDOR_UNKNOWN, "", "", ""}, | |||||
| {-1, WM_GPU_VENDOR_UNKNOWN, "", "", ""}}; | |||||
brecht: Don't show a warning for unknown vendors, we should purely be blacklisting, not whitelisting. | |||||
Done Inline ActionsChange latest to dev since the 2.80 manual does not have this info. We could change it after the release, though maybe it is better regardless to always link to the very latest manual, in case the driver download links change or we want to add extra info. brecht: Change `latest` to `dev` since the 2.80 manual does not have this info.
We could change it… | |||||
| static bool wm_platform_support_match(const WM_PlatformSupportTest *test_record, | |||||
| eWM_GPUVendor gpu_vendor, | |||||
| const char *vendor, | |||||
| const char *renderer, | |||||
| const char *version) | |||||
| { | |||||
| return (test_record->gpu_vendor == gpu_vendor && strstr(vendor, test_record->vendor) && | |||||
| strstr(renderer, test_record->renderer) && strstr(version, test_record->version)); | |||||
| } | |||||
| static bool wm_platform_support_load_state(char platform_support_key[1024]) | |||||
| { | |||||
Done Inline ActionsIf a computer has graphics switching and both cards are unsupported, we will show the warning many times. So I think this should store multiple keys. For the implementation, BLI_file_read_as_lines could be used. New lines would have to be stripped. It's may also be simplest if wm_platform_support_load_state and wm_platform_support_save_state get merged into a single function. It can check if the given key is in the file, and if not append it. brecht: If a computer has graphics switching and both cards are unsupported, we will show the warning… | |||||
| /* System config should actually be better... */ | |||||
| const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); | |||||
| bool result = false; | |||||
| if (cfgdir) { | |||||
| char filepath[FILE_MAX]; | |||||
| BLI_make_file_string("/", filepath, cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); | |||||
| FILE *fp = BLI_fopen(filepath, "r"); | |||||
| if (!fp) { | |||||
| return false; | |||||
| } | |||||
| result = fgets(platform_support_key, sizeof(char[1024]), fp) > 0; | |||||
| fclose(fp); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| static bool wm_platform_support_save_state(char platform_support_key[1024]) | |||||
| { | |||||
| const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); | |||||
| bool result = false; | |||||
| if (cfgdir) { | |||||
| char filepath[FILE_MAX]; | |||||
| BLI_make_file_string("/", filepath, cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); | |||||
| FILE *fp = BLI_fopen(filepath, "w"); | |||||
| if (!fp) { | |||||
| return result; | |||||
| } | |||||
| fprintf(fp, "%s\n", platform_support_key); | |||||
| fclose(fp); | |||||
| result = true; | |||||
| } | |||||
| return result; | |||||
Done Inline Actionsthis writes a \n that later will be returned in wm_platform_support_load_state causing the strcmp later on to fail and give the popup every time the user starts blender. LazyDodo: this writes a `\n` that later will be returned in `wm_platform_support_load_state` causing the… | |||||
Done Inline ActionsGotcha! well we will be refactoring load_state and save_state so this will not be an issue anymore jbakker: Gotcha! well we will be refactoring load_state and save_state so this will not be an issue… | |||||
| } | |||||
| static char *wm_platform_create_platform_key(eWM_SupportLevel 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 == WM_SUPPORT_LEVEL_SUPPORTED) { | |||||
| BLI_dynstr_append(ds, "SUPPORTED"); | |||||
| } | |||||
| else if (support_level == WM_SUPPORT_LEVEL_LIMITED) { | |||||
| BLI_dynstr_append(ds, "LIMITED"); | |||||
| } | |||||
| else { | |||||
| BLI_dynstr_append(ds, "UNSUPPORTED"); | |||||
| } | |||||
| char *platform_key = BLI_dynstr_get_cstring(ds); | |||||
| BLI_dynstr_free(ds); | |||||
| return platform_key; | |||||
| } | |||||
| static eWM_GPUVendor wm_platform_support_gpu_vendor(const char *vendor, const char *renderer) | |||||
| { | |||||
| if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) { | |||||
| return WM_GPU_VENDOR_AMD; | |||||
| } | |||||
| else if (strstr(vendor, "NVIDIA")) { | |||||
Done Inline ActionsI think we should deduplicate this code with gpu_extensions_init. What I suggest is to add gpu_platform.c containing gpu_platform_init() to detect GG.device, GG.driver and GG.os. GPU_type_matches(), eWM_SupportLevel, WM_PlatformSupportTest would all in that file as well then. gpu_extensions_init() would use GPU_type_matches(). brecht: I think we should deduplicate this code with `gpu_extensions_init`.
What I suggest is to add… | |||||
| return WM_GPU_VENDOR_NVIDIA; | |||||
| } | |||||
| else if (strstr(vendor, "Intel")) { | |||||
| return WM_GPU_VENDOR_INTEL; | |||||
| } | |||||
| 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 "))) { | |||||
| return WM_GPU_VENDOR_AMD; | |||||
| } | |||||
| else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) { | |||||
| return WM_GPU_VENDOR_NVIDIA; | |||||
| } | |||||
| else { | |||||
| return WM_GPU_VENDOR_UNKNOWN; | |||||
| } | |||||
| } | |||||
| static void wm_platform_support_create_link(char *link, eWM_GPUVendor gpu_vendor) | |||||
| { | |||||
| DynStr *ds = BLI_dynstr_new(); | |||||
| BLI_dynstr_append(ds, "https://docs.blender.org/manual/en/latest/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 | |||||
Done Inline Actions__APPLE -> __APPLE__ brecht: `__APPLE` -> `__APPLE__` | |||||
| if (gpu_vendor == WM_GPU_VENDOR_INTEL) { | |||||
| BLI_dynstr_append(ds, "intel.html"); | |||||
| } | |||||
| else if (gpu_vendor == WM_GPU_VENDOR_NVIDIA) { | |||||
| BLI_dynstr_append(ds, "nvidia.html"); | |||||
| } | |||||
| else if (gpu_vendor == WM_GPU_VENDOR_AMD) { | |||||
| BLI_dynstr_append(ds, "amd.html"); | |||||
| } | |||||
| else { | |||||
| BLI_dynstr_append(ds, "unknown.html"); | |||||
| } | |||||
| BLI_assert(BLI_dynstr_get_len(ds) < 1024); | |||||
| BLI_dynstr_get_cstring_ex(ds, link); | |||||
| BLI_dynstr_free(ds); | |||||
| } | |||||
| GHOST_DialogOptions wm_platform_support_ghost_callback(char *title, char *message, char *link) | |||||
| { | |||||
| /* User has already seen the message or has already confirmed in a | |||||
| * previous run */ | |||||
| if (WM_PLATFORM_SUPPORT.already_checked) { | |||||
| return GHOST_DialogNoDialog; | |||||
| } | |||||
| eWM_SupportLevel support_level = WM_SUPPORT_LEVEL_SUPPORTED; | |||||
| const char *vendor = (const char *)glGetString(GL_VENDOR); | |||||
| const char *renderer = (const char *)glGetString(GL_RENDERER); | |||||
| const char *version = (const char *)glGetString(GL_VERSION); | |||||
| eWM_GPUVendor gpu_vendor = wm_platform_support_gpu_vendor(vendor, renderer); | |||||
| /* GLEW should at least report version 3.3 */ | |||||
| if (!GLEW_VERSION_3_3) { | |||||
| support_level = WM_SUPPORT_LEVEL_UNSUPPORTED; | |||||
| } | |||||
| else { | |||||
| for (int index = 0; WM_PLATFORM_SUPPORT_TESTS[index].support_level != -1; index++) { | |||||
| if (wm_platform_support_match( | |||||
| &WM_PLATFORM_SUPPORT_TESTS[index], gpu_vendor, vendor, renderer, version)) { | |||||
| support_level = WM_PLATFORM_SUPPORT_TESTS[index].support_level; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| char *platform_key = wm_platform_create_platform_key(support_level, vendor, renderer, version); | |||||
| /* load previous platform check */ | |||||
| char last_confirmed_platform_key[1024]; | |||||
| /* check if previous check matches the current check */ | |||||
| if (support_level != WM_SUPPORT_LEVEL_UNSUPPORTED && | |||||
| wm_platform_support_load_state(last_confirmed_platform_key) && | |||||
| strcmp(last_confirmed_platform_key, platform_key) == 0) { | |||||
| /* if it matches the user has confirmed and whishes to use it only report */ | |||||
| WM_PLATFORM_SUPPORT.already_checked = true; | |||||
| return GHOST_DialogNoDialog; | |||||
| } | |||||
| /* update the message and link based on the found support level */ | |||||
| GHOST_DialogOptions result = GHOST_DialogNoDialog; | |||||
| /* TODO: do not translate Application name */ | |||||
| switch (support_level) { | |||||
| default: | |||||
| case WM_SUPPORT_LEVEL_SUPPORTED: | |||||
| result = GHOST_DialogNoDialog; | |||||
| break; | |||||
| case WM_SUPPORT_LEVEL_LIMITED: | |||||
| strcpy(title, | |||||
| CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Blender - Limitted Platform Support")); | |||||
| strcpy(message, | |||||
| CTX_IFACE_( | |||||
| BLT_I18NCONTEXT_ID_WINDOWMANAGER, | |||||
| "Your graphics card and driver has limited. It may work, but with issues.\n" | |||||
| "Press help to see how you can update your driver to resolve the issue.\n\n")); | |||||
Done Inline ActionsLimitted -> Limited brecht: Limitted -> Limited | |||||
| result = GHOST_DialogShow; | |||||
| break; | |||||
| case WM_SUPPORT_LEVEL_UNSUPPORTED: | |||||
| strcpy(title, | |||||
| CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Blender - Platform Unsupported")); | |||||
Done Inline ActionsLazyDodo: {F7785798}
1) layout doesn't look great
2) `Your graphics card and driver has limited.`… | |||||
| strcpy( | |||||
| message, | |||||
| CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, | |||||
| "Your graphics card and driver is not supported.\n" | |||||
| "Press help to see how you can update your driver to resolve the issue.\n\n" | |||||
| "The program will now close.")); | |||||
| result = GHOST_DialogShow | GHOST_DialogExit; | |||||
| break; | |||||
| } | |||||
Done Inline Actionsand -> or brecht: and -> or | |||||
Done Inline Actionsis -> are LazyDodo: is -> are | |||||
| wm_platform_support_create_link(link, gpu_vendor); | |||||
Done Inline ActionsThis seems to say a solution is definitely possible, might want to formulate it a bit different. brecht: This seems to say a solution is definitely possible, might want to formulate it a bit different. | |||||
| /* We are running in the background print the message in the console. */ | |||||
| if ((G.background || G.debug & G_DEBUG) && (result & GHOST_DialogShow)) { | |||||
| printf("%s\n\n%s\n%s\n", title, message, link); | |||||
| } | |||||
| /* Mark that we bothered the user already */ | |||||
| WM_PLATFORM_SUPPORT.already_checked = true; | |||||
| if (!G.background) { | |||||
| wm_platform_support_save_state(platform_key); | |||||
| } | |||||
| else { | |||||
| /* don't show the messagebox when running in background mode. Printing to | |||||
| * console is enough. */ | |||||
| result = GHOST_DialogNoDialog; | |||||
Done Inline ActionsWhy not save in background mode? brecht: Why not save in background mode? | |||||
Done Inline ActionsWhen running in background mode the user might not notice that there is a support issue. By not doing this during background we know for sure that the message has been seen. jbakker: When running in background mode the user might not notice that there is a support issue.
There… | |||||
| } | |||||
| MEM_freeN(platform_key); | |||||
| return result; | |||||
| } | |||||
| No newline at end of file | |||||

Don't show a warning for unknown vendors, we should purely be blacklisting, not whitelisting.