From f4b03031e84f727df6eb21c28125b3a4ef98f1c0 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 21 Dec 2022 20:54:36 +0100 Subject: [PATCH] GPU: Select GPU Backend from Preferences. (MacOS) only: In the System tab of the user preferences the user has the ability to select a GPU backend that Blender will use. After changing the GPU backend setting, the user has to restart Blender before the setting is used. It was added to start collecting feedback on the Metal backend without using the command lines. By default Blender will select OpenGL as backend. When Metal is selected (via `--gpu-backend metal` or via user preferences) OpenGL will be used as fallback when the platform isn't capable of running Metal. --- release/datafiles/userdef/userdef_default.c | 3 ++ .../scripts/startup/bl_ui/space_userpref.py | 22 ++++++++ .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/CMakeLists.txt | 1 + .../blenloader/intern/versioning_userdef.c | 7 +++ source/blender/gpu/GPU_context.h | 24 +++++++++ source/blender/gpu/intern/gpu_context.cc | 50 ++++++++++++++++++- source/blender/makesdna/DNA_userdef_types.h | 5 +- source/blender/makesrna/intern/rna_userdef.c | 46 +++++++++++++++++ .../blender/windowmanager/intern/wm_files.c | 16 ++++++ source/creator/creator_args.c | 6 +-- 11 files changed, 174 insertions(+), 8 deletions(-) diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index b4a1ab6d5f8..24e093833c4 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -14,6 +14,8 @@ #include "BKE_blender_version.h" +#include "GPU_platform.h" + #include "BLO_readfile.h" /* own include */ const UserDef U_default = { @@ -99,6 +101,7 @@ const UserDef U_default = { .gp_euclideandist = 2, .gp_eraser = 25, .gp_settings = 0, + .gpu_backend = GPU_BACKEND_OPENGL, /** Initialized by: #BKE_studiolight_default. */ .light_param = {{0}}, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 759b222c562..44bc807c7dd 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -604,6 +604,27 @@ class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel): del addon +class USERPREF_PT_system_gpu_backend(SystemPanel, CenterAlignMixIn, Panel): + bl_label = "GPU Backend" + + @classmethod + def poll(cls, _context): + # Only for Apple so far + import sys + return sys.platform == "darwin" + + def draw_centered(self, context, layout): + import gpu + prefs = context.preferences + system = prefs.system + + col = layout.column() + col.prop(system, "gpu_backend") + + if system.gpu_backend != gpu.platform.backend_type_get(): + layout.label(text="Requires a restart of Blender to take effect.", icon='INFO') + + class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Operating System Settings" @@ -2406,6 +2427,7 @@ classes = ( USERPREF_PT_animation_fcurves, USERPREF_PT_system_cycles_devices, + USERPREF_PT_system_gpu_backend, USERPREF_PT_system_os_settings, USERPREF_PT_system_memory, USERPREF_PT_system_video_sequencer, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 91a458e347f..7c241a4fa12 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,7 +25,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 4 +#define BLENDER_FILE_SUBVERSION 5 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 86793d38b0b..a7e99e7df2e 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -10,6 +10,7 @@ set(INC ../depsgraph ../draw ../editors/include + ../gpu ../imbuf ../makesdna ../makesrna diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index b4890131861..10a21356c8f 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -31,6 +31,8 @@ #include "BLO_readfile.h" +#include "GPU_platform.h" + #include "readfile.h" /* Own include. */ #include "WM_types.h" @@ -766,6 +768,11 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->dupflag |= USER_DUP_CURVES | USER_DUP_POINTCLOUD; } + /* Set GPU backend to OpenGL. */ + if (!USER_VERSION_ATLEAST(305, 5)) { + userdef->gpu_backend = GPU_BACKEND_OPENGL; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index ac82774039a..fd20283380f 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -25,6 +25,30 @@ void GPU_backend_type_selection_set(const eGPUBackendType backend); eGPUBackendType GPU_backend_type_selection_get(void); eGPUBackendType GPU_backend_get_type(void); +/** + * Detect the most suited eGPUBackendType. + * + * - The detected backend will be set in `GPU_backend_type_selection_set`. + * - When GPU_backend_type_selection_is_overridden it checks the overridden backend. + * When not overridden it checks a default list. + * - OpenGL backend will be checked as fallback for Metal. + * + * Returns true when detection found a supported backend, otherwise returns false. + * When no supported backend is found GPU_backend_type_selection_set is called with + * GPU_BACKEND_NONE. + */ +bool GPU_backend_type_selection_detect(void); + +/** + * Alter the GPU_backend_type_selection_detect to only test a specific backend + */ +void GPU_backend_type_selection_set_override(eGPUBackendType backend_type); + +/** + * Check if the GPU_backend_type_selection_detect is overridden to only test a specific backend. + */ +bool GPU_backend_type_selection_is_overridden(void); + /** Opaque type hiding blender::gpu::Context. */ typedef struct GPUContext GPUContext; diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 0443417a32a..101542802e6 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -227,11 +227,14 @@ void GPU_render_step() * Until a global switch is added, Metal also needs to be enabled in GHOST_ContextCGL: * `m_useMetalForRendering = true`. */ static eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL; +static std::optional g_backend_type_override = std::nullopt; +static std::optional g_backend_type_supported = std::nullopt; static GPUBackend *g_backend = nullptr; void GPU_backend_type_selection_set(const eGPUBackendType backend) { g_backend_type = backend; + g_backend_type_supported = std::nullopt; } eGPUBackendType GPU_backend_type_selection_get() @@ -239,7 +242,44 @@ eGPUBackendType GPU_backend_type_selection_get() return g_backend_type; } -bool GPU_backend_supported(void) +void GPU_backend_type_selection_set_override(const eGPUBackendType backend_type) +{ + g_backend_type_override = backend_type; +} + +bool GPU_backend_type_selection_is_overridden(void) +{ + return g_backend_type_override.has_value(); +} + +bool GPU_backend_type_selection_detect(void) +{ + blender::Vector backends_to_check; + if (GPU_backend_type_selection_is_overridden()) { + backends_to_check.append(*g_backend_type_override); + } + else { + backends_to_check.append(GPU_BACKEND_OPENGL); + } + + /* Add fallback to OpenGL when Metal backend is requested on a platform that doens't support + * metal. */ + if (backends_to_check[0] == GPU_BACKEND_METAL) { + backends_to_check.append(GPU_BACKEND_OPENGL); + } + + for (const eGPUBackendType backend_type : backends_to_check) { + GPU_backend_type_selection_set(backend_type); + if (GPU_backend_supported()) { + return true; + } + } + + GPU_backend_type_selection_set(GPU_BACKEND_NONE); + return false; +} + +static bool gpu_backend_supported() { switch (g_backend_type) { case GPU_BACKEND_OPENGL: @@ -266,6 +306,14 @@ bool GPU_backend_supported(void) } } +bool GPU_backend_supported() +{ + if (!g_backend_type_supported.has_value()) { + g_backend_type_supported = gpu_backend_supported(); + } + return *g_backend_type_supported; +} + static void gpu_backend_create() { BLI_assert(g_backend == nullptr); diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 8a644803fd7..18c4508efae 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -828,7 +828,10 @@ typedef struct UserDef { /** Seconds to zoom around current frame. */ float view_frame_seconds; - char _pad7[6]; + /** #eGPUBackendType */ + short gpu_backend; + + char _pad7[4]; /** Private, defaults to 20 for 72 DPI setting. */ short widget_unit; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index b93983d0a87..db12ac82f38 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -29,6 +29,8 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "GPU_platform.h" + #include "UI_interface_icons.h" #include "rna_internal.h" @@ -138,6 +140,13 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = { + {GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"}, + {GPU_BACKEND_METAL, "METAL", 0, "Metal", "Use Metal backend"}, + {GPU_BACKEND_VULKAN, "VULKAN", 0, "Vulkan", "Use Vulkan backend"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "BLI_math_vector.h" @@ -1031,6 +1040,33 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char return GPU_mem_stats_supported() ? PROP_EDITABLE : 0; } +static const EnumPropertyItem *rna_preference_gpu_backend_itemf(struct bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + int totitem = 0; + EnumPropertyItem *result = NULL; + for (int i = 0; rna_enum_preference_gpu_backend_items[i].identifier != NULL; i++) { + const EnumPropertyItem *item = &rna_enum_preference_gpu_backend_items[i]; +# ifndef WITH_METAL_BACKEND + if (item->value == GPU_BACKEND_METAL) { + continue; + } +# endif +# ifndef WITH_VULKAN_BACKEND + if (item->value == GPU_BACKEND_VULKAN) { + continue; + } +# endif + RNA_enum_item_add(&result, &totitem, item); + } + + RNA_enum_item_end(&result, &totitem); + *r_free = true; + return result; +} + #else # define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \ @@ -5604,6 +5640,16 @@ static void rna_def_userdef_system(BlenderRNA *brna) "modifiers in the stack"); RNA_def_property_update(prop, 0, "rna_UserDef_subdivision_update"); + /* GPU backend selection */ + prop = RNA_def_property(srna, "gpu_backend", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gpu_backend"); + RNA_def_property_enum_items(prop, rna_enum_preference_gpu_backend_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_preference_gpu_backend_itemf"); + RNA_def_property_ui_text( + prop, + "GPU Backend", + "GPU backend to use. (Requires restarting Blender for changes to take effect)"); + /* Audio */ prop = RNA_def_property(srna, "audio_mixing_buffer", PROP_ENUM, PROP_NONE); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 7b1fcf2a231..712bee6ef8b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -107,6 +107,8 @@ #include "GHOST_C-api.h" #include "GHOST_Path-api.h" +#include "GPU_context.h" + #include "UI_interface.h" #include "UI_resources.h" #include "UI_view2d.h" @@ -442,6 +444,17 @@ static void wm_window_match_do(bContext *C, /** \name Preferences Initialization & Versioning * \{ */ +static void wm_gpu_backend_override_from_userdef(void) +{ + /* Check if GPU backend is already set from the command line arguments. The command line + * arguments have higher priority than user preferences. */ + if (GPU_backend_type_selection_is_overridden()) { + return; + } + + GPU_backend_type_selection_set_override(U.gpu_backend); +} + /** * In case #UserDef was read, re-initialize values that depend on it. */ @@ -475,6 +488,9 @@ static void wm_init_userdef(Main *bmain) WM_init_input_devices(); BLO_sanitize_experimental_features_userpref_blend(&U); + + wm_gpu_backend_override_from_userdef(); + GPU_backend_type_selection_detect(); } /* return codes */ diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 2ebdcdb35ba..e942c0d18ee 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1150,11 +1150,7 @@ static int arg_handle_gpu_backend_set(int argc, const char **argv, void *UNUSED( return 0; } - GPU_backend_type_selection_set(gpu_backend); - if (!GPU_backend_supported()) { - printf("\nError: GPU backend not supported.\n"); - return 0; - } + GPU_backend_type_selection_set_override(gpu_backend); return 1; }