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 during development */ | |||||
| // {WM_SUPPORT_LEVEL_UNSUPPORTED, WM_GPU_VENDOR_AMD, "", "", "4.5.13542"}, | |||||
| #endif | |||||
| {WM_SUPPORT_LEVEL_LIMITED, WM_GPU_VENDOR_UNKNOWN, "", "", ""}, | |||||
brecht: Don't show a warning for unknown vendors, we should purely be blacklisting, not whitelisting. | |||||
| {-1, WM_GPU_VENDOR_UNKNOWN, "", "", ""}}; | |||||
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]) | |||||
brechtUnsubmitted 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… | |||||
| { | |||||
| if (G.factory_startup) { | |||||
| return false; | |||||
| } | |||||
| /* 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); | |||||
LazyDodoUnsubmitted 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… | |||||
jbakkerAuthorUnsubmitted 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… | |||||
| fclose(fp); | |||||
| result = true; | |||||
| } | |||||
| return result; | |||||
| } | |||||
| 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) | |||||
brechtUnsubmitted 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… | |||||
| { | |||||
| if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) { | |||||
| return WM_GPU_VENDOR_AMD; | |||||
| } | |||||
| else if (strstr(vendor, "NVIDIA")) { | |||||
| 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) | |||||
brechtUnsubmitted Done Inline Actions__APPLE -> __APPLE__ brecht: `__APPLE` -> `__APPLE__` | |||||
| BLI_dynstr_append(ds, "apple/"); | |||||
| #else /* UNIX */ | |||||
| BLI_dynstr_append(ds, "linux/"); | |||||
| #endif | |||||
| 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 && !G.factory_startup && | |||||
| 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")); | |||||
brechtUnsubmitted Done Inline ActionsLimitted -> Limited brecht: Limitted -> Limited | |||||
| 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")); | |||||
| result = GHOST_DialogShow; | |||||
LazyDodoUnsubmitted Done Inline ActionsLazyDodo: {F7785798}
1) layout doesn't look great
2) `Your graphics card and driver has limited.`… | |||||
| break; | |||||
| case WM_SUPPORT_LEVEL_UNSUPPORTED: | |||||
| strcpy(title, | |||||
| CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Blender - Platform Unsupported")); | |||||
| strcpy( | |||||
| message, | |||||
| CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, | |||||
| "Your graphics card and driver is not supported.\n" | |||||
brechtUnsubmitted Done Inline Actionsand -> or brecht: and -> or | |||||
LazyDodoUnsubmitted Done Inline Actionsis -> are LazyDodo: is -> are | |||||
| "Press help to see how you can update your driver to resolve the issue.\n\n" | |||||
brechtUnsubmitted 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. | |||||
| "The program will now close.")); | |||||
| result = GHOST_DialogShow | GHOST_DialogExit; | |||||
| break; | |||||
| } | |||||
| wm_platform_support_create_link(link, gpu_vendor); | |||||
| /* 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); | |||||
brechtUnsubmitted Done Inline ActionsWhy not save in background mode? brecht: Why not save in background mode? | |||||
jbakkerAuthorUnsubmitted 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… | |||||
| } | |||||
| else { | |||||
| /* don't show the messagebox when running in background mode. Printing to | |||||
| * console is enough. */ | |||||
| result = GHOST_DialogNoDialog; | |||||
| } | |||||
| 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.