Vulkan: GPU device selection
Allows users to override the auto detection for GPU selection. Normally the GPU selection is done by looping over the order Vulkan provides and finding the highest performing device based on its type (discrete, integrated, software). However users might have multiple discrete cards and want to switch between them. Or developers want to validate other GPUs without rebooting. This PR adds the ability to override the auto detection for the vulkan backend.  **Future improvements**: - This PR does not include a command line option. This can be added later for render farms. Pull Request: https://projects.blender.org/blender/blender/pulls/127860
This commit is contained in:
@@ -713,9 +713,19 @@ typedef struct {
|
||||
uint32_t frequency;
|
||||
} GHOST_DisplaySetting;
|
||||
|
||||
typedef struct {
|
||||
/** Index of the GPU device in the list provided by the platform. */
|
||||
int index;
|
||||
/** (PCI) Vendor ID of the GPU. */
|
||||
uint vendor_id;
|
||||
/** Device ID of the GPU provided by the vendor. */
|
||||
uint device_id;
|
||||
} GHOST_GPUDevice;
|
||||
|
||||
typedef struct {
|
||||
int flags;
|
||||
GHOST_TDrawingContextType context_type;
|
||||
GHOST_GPUDevice preferred_device;
|
||||
} GHOST_GPUSettings;
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
|
||||
@@ -320,6 +320,7 @@ static std::optional<GHOST_DeviceVK> vulkan_device;
|
||||
|
||||
static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
|
||||
VkSurfaceKHR vk_surface,
|
||||
const GHOST_GPUDevice &preferred_device,
|
||||
const vector<const char *> &required_extensions)
|
||||
{
|
||||
if (vulkan_device.has_value()) {
|
||||
@@ -335,8 +336,10 @@ static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
|
||||
vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices.data());
|
||||
|
||||
int best_device_score = -1;
|
||||
int device_index = -1;
|
||||
for (const auto &physical_device : physical_devices) {
|
||||
GHOST_DeviceVK device_vk(vk_instance, physical_device);
|
||||
device_index++;
|
||||
|
||||
if (!device_vk.has_extensions(required_extensions)) {
|
||||
continue;
|
||||
@@ -386,6 +389,16 @@ static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* User has configured a preferred device. Add bonus score when vendor and device match. Driver
|
||||
* id isn't considered as drivers update more frequently and can break the device selection. */
|
||||
if (device_vk.properties.deviceID == preferred_device.device_id &&
|
||||
device_vk.properties.vendorID == preferred_device.vendor_id)
|
||||
{
|
||||
device_score += 500;
|
||||
if (preferred_device.index == device_index) {
|
||||
device_score += 10;
|
||||
}
|
||||
}
|
||||
if (device_score > best_device_score) {
|
||||
best_physical_device = physical_device;
|
||||
best_device_score = device_score;
|
||||
@@ -421,7 +434,8 @@ GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
|
||||
#endif
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int debug)
|
||||
int debug,
|
||||
const GHOST_GPUDevice &preferred_device)
|
||||
: GHOST_Context(stereoVisual),
|
||||
#ifdef _WIN32
|
||||
m_hwnd(hwnd),
|
||||
@@ -440,6 +454,7 @@ GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
|
||||
m_context_major_version(contextMajorVersion),
|
||||
m_context_minor_version(contextMinorVersion),
|
||||
m_debug(debug),
|
||||
m_preferred_device(preferred_device),
|
||||
m_command_pool(VK_NULL_HANDLE),
|
||||
m_command_buffer(VK_NULL_HANDLE),
|
||||
m_surface(VK_NULL_HANDLE),
|
||||
@@ -1052,7 +1067,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!ensure_vulkan_device(instance, m_surface, required_device_extensions)) {
|
||||
if (!ensure_vulkan_device(instance, m_surface, m_preferred_device, required_device_extensions)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ class GHOST_ContextVK : public GHOST_Context {
|
||||
#endif
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int debug);
|
||||
int debug,
|
||||
const GHOST_GPUDevice &preferred_device);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -173,6 +174,7 @@ class GHOST_ContextVK : public GHOST_Context {
|
||||
const int m_context_major_version;
|
||||
const int m_context_minor_version;
|
||||
const int m_debug;
|
||||
const GHOST_GPUDevice m_preferred_device;
|
||||
|
||||
VkCommandPool m_command_pool;
|
||||
VkCommandBuffer m_command_buffer;
|
||||
|
||||
@@ -758,7 +758,8 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
|
||||
gpuSettings.flags & GHOST_gpuStereoVisual,
|
||||
gpuSettings.flags & GHOST_gpuDebugContext,
|
||||
is_dialog,
|
||||
(GHOST_WindowCocoa *)parentWindow);
|
||||
(GHOST_WindowCocoa *)parentWindow,
|
||||
gpuSettings.preferred_device);
|
||||
|
||||
if (window->getValid()) {
|
||||
/* Store the pointer to the window. */
|
||||
@@ -791,7 +792,8 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GPUSettings gpuS
|
||||
switch (gpuSettings.context_type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, nullptr, 1, 2, debug_context);
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
false, nullptr, 1, 2, debug_context, gpuSettings.preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -8273,7 +8273,8 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GPUSettings gp
|
||||
nullptr,
|
||||
1,
|
||||
2,
|
||||
debug_context);
|
||||
debug_context,
|
||||
gpuSettings.preferred_device);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
context->setUserData(wl_surface);
|
||||
@@ -8400,7 +8401,8 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
|
||||
is_dialog,
|
||||
((gpuSettings.flags & GHOST_gpuStereoVisual) != 0),
|
||||
exclusive,
|
||||
(gpuSettings.flags & GHOST_gpuDebugContext) != 0);
|
||||
(gpuSettings.flags & GHOST_gpuDebugContext) != 0,
|
||||
gpuSettings.preferred_device);
|
||||
|
||||
if (window) {
|
||||
if (window->getValid()) {
|
||||
|
||||
@@ -288,7 +288,8 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
|
||||
false,
|
||||
(GHOST_WindowWin32 *)parentWindow,
|
||||
((gpuSettings.flags & GHOST_gpuDebugContext) != 0),
|
||||
is_dialog);
|
||||
is_dialog,
|
||||
gpuSettings.preferred_device);
|
||||
|
||||
if (window->getValid()) {
|
||||
/* Store the pointer to the window */
|
||||
@@ -316,7 +317,8 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GPUSettings gpuS
|
||||
switch (gpuSettings.context_type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, (HWND)0, 1, 2, debug_context);
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
false, (HWND)0, 1, 2, debug_context, gpuSettings.preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -384,7 +384,8 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
|
||||
is_dialog,
|
||||
((gpuSettings.flags & GHOST_gpuStereoVisual) != 0),
|
||||
exclusive,
|
||||
(gpuSettings.flags & GHOST_gpuDebugContext) != 0);
|
||||
(gpuSettings.flags & GHOST_gpuDebugContext) != 0,
|
||||
gpuSettings.preferred_device);
|
||||
|
||||
if (window) {
|
||||
/* Both are now handle in GHOST_WindowX11.cc
|
||||
@@ -419,7 +420,8 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GPUSettings gpuSet
|
||||
nullptr,
|
||||
1,
|
||||
2,
|
||||
debug_context);
|
||||
debug_context,
|
||||
gpuSettings.preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ class GHOST_WindowCocoa : public GHOST_Window {
|
||||
* \param state: The state the window is initially opened with.
|
||||
* \param type: The type of drawing context installed in this window.
|
||||
* \param stereoVisual: Stereo visual for quad buffered stereo.
|
||||
* \param preferred_device: Preferred device to use when new device will be created.
|
||||
*/
|
||||
GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
|
||||
const char *title,
|
||||
@@ -50,11 +51,12 @@ class GHOST_WindowCocoa : public GHOST_Window {
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
|
||||
const bool stereoVisual = false,
|
||||
bool is_debug = false,
|
||||
bool dialog = false,
|
||||
GHOST_WindowCocoa *parentWindow = nullptr);
|
||||
GHOST_TDrawingContextType type,
|
||||
const bool stereoVisual,
|
||||
bool is_debug,
|
||||
bool dialog,
|
||||
GHOST_WindowCocoa *parentWindow,
|
||||
const GHOST_GPUDevice &preferred_device);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -312,6 +314,7 @@ class GHOST_WindowCocoa : public GHOST_Window {
|
||||
bool m_immediateDraw;
|
||||
bool m_debug_context; // for debug messages during context setup
|
||||
bool m_is_dialog;
|
||||
GHOST_GPUDevice m_preferred_device;
|
||||
};
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
|
||||
@@ -332,7 +332,8 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
|
||||
const bool stereoVisual,
|
||||
bool is_debug,
|
||||
bool is_dialog,
|
||||
GHOST_WindowCocoa *parentWindow)
|
||||
GHOST_WindowCocoa *parentWindow,
|
||||
const GHOST_GPUDevice &preferred_device)
|
||||
: GHOST_Window(width, height, state, stereoVisual, false),
|
||||
m_openGLView(nil),
|
||||
m_metalView(nil),
|
||||
@@ -341,7 +342,8 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
|
||||
m_customCursor(nullptr),
|
||||
m_immediateDraw(false),
|
||||
m_debug_context(is_debug),
|
||||
m_is_dialog(is_dialog)
|
||||
m_is_dialog(is_dialog),
|
||||
m_preferred_device(preferred_device)
|
||||
{
|
||||
m_fullScreen = false;
|
||||
|
||||
@@ -847,7 +849,8 @@ GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType ty
|
||||
switch (type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 2, true);
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
m_wantStereoVisual, m_metalLayer, 1, 2, true, m_preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1755,11 +1755,13 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
||||
const bool is_dialog,
|
||||
const bool stereoVisual,
|
||||
const bool exclusive,
|
||||
const bool is_debug)
|
||||
const bool is_debug,
|
||||
const GHOST_GPUDevice &preferred_device)
|
||||
: GHOST_Window(width, height, state, stereoVisual, exclusive),
|
||||
system_(system),
|
||||
window_(new GWL_Window),
|
||||
is_debug_context_(is_debug)
|
||||
is_debug_context_(is_debug),
|
||||
preferred_device_(preferred_device)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*system->server_mutex};
|
||||
@@ -2505,7 +2507,8 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
|
||||
window_->backend.vulkan_window_info,
|
||||
1,
|
||||
2,
|
||||
is_debug_context_);
|
||||
is_debug_context_,
|
||||
preferred_device_);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ class GHOST_WindowWayland : public GHOST_Window {
|
||||
const bool is_dialog,
|
||||
const bool stereoVisual,
|
||||
const bool exclusive,
|
||||
const bool is_debug);
|
||||
const bool is_debug,
|
||||
const GHOST_GPUDevice &preferred_device);
|
||||
|
||||
~GHOST_WindowWayland() override;
|
||||
|
||||
@@ -206,6 +207,7 @@ class GHOST_WindowWayland : public GHOST_Window {
|
||||
GHOST_SystemWayland *system_;
|
||||
struct GWL_Window *window_;
|
||||
bool is_debug_context_;
|
||||
GHOST_GPUDevice preferred_device_;
|
||||
|
||||
/**
|
||||
* \param type: The type of rendering context create.
|
||||
|
||||
@@ -61,7 +61,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
||||
bool alphaBackground,
|
||||
GHOST_WindowWin32 *parentwindow,
|
||||
bool is_debug,
|
||||
bool dialog)
|
||||
bool dialog,
|
||||
const GHOST_GPUDevice &preferred_device)
|
||||
: GHOST_Window(width, height, state, wantStereoVisual, false),
|
||||
m_mousePresent(false),
|
||||
m_inLiveResize(false),
|
||||
@@ -82,7 +83,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
||||
m_user32(::LoadLibrary("user32.dll")),
|
||||
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
|
||||
m_directManipulationHelper(nullptr),
|
||||
m_debug_context(is_debug)
|
||||
m_debug_context(is_debug),
|
||||
m_preferred_device(preferred_device)
|
||||
{
|
||||
DWORD style = parentwindow ?
|
||||
WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX :
|
||||
@@ -620,7 +622,8 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
|
||||
switch (type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 2, m_debug_context);
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
false, m_hWnd, 1, 2, m_debug_context, m_preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
* \param state: The state the window is initially opened with.
|
||||
* \param type: The type of drawing context installed in this window.
|
||||
* \param wantStereoVisual: Stereo visual for quad buffered stereo.
|
||||
* \param preferred_device: Preferred device to use when new device will be created.
|
||||
* \param parentWindowHwnd: TODO.
|
||||
*/
|
||||
GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
||||
@@ -74,12 +75,13 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
|
||||
bool wantStereoVisual = false,
|
||||
bool alphaBackground = false,
|
||||
GHOST_WindowWin32 *parentWindow = 0,
|
||||
bool is_debug = false,
|
||||
bool dialog = false);
|
||||
GHOST_TDrawingContextType type,
|
||||
bool wantStereoVisual,
|
||||
bool alphaBackground,
|
||||
GHOST_WindowWin32 *parentWindow,
|
||||
bool is_debug,
|
||||
bool dialog,
|
||||
const GHOST_GPUDevice &preferred_device);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -384,6 +386,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
HDC m_hDC;
|
||||
|
||||
bool m_isDialog;
|
||||
GHOST_GPUDevice m_preferred_device;
|
||||
|
||||
/** Flag for if window has captured the mouse. */
|
||||
bool m_hasMouseCaptured;
|
||||
|
||||
@@ -112,7 +112,8 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
|
||||
const bool is_dialog,
|
||||
const bool stereoVisual,
|
||||
const bool exclusive,
|
||||
const bool is_debug)
|
||||
const bool is_debug,
|
||||
const GHOST_GPUDevice &preferred_device)
|
||||
: GHOST_Window(width, height, state, stereoVisual, exclusive),
|
||||
m_display(display),
|
||||
m_visualInfo(nullptr),
|
||||
@@ -132,7 +133,8 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
|
||||
m_xic(nullptr),
|
||||
#endif
|
||||
m_valid_setup(false),
|
||||
m_is_debug_context(is_debug)
|
||||
m_is_debug_context(is_debug),
|
||||
m_preferred_device(preferred_device)
|
||||
{
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
if (type == GHOST_kDrawingContextTypeOpenGL) {
|
||||
@@ -1198,7 +1200,8 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
|
||||
nullptr,
|
||||
1,
|
||||
2,
|
||||
m_is_debug_context);
|
||||
m_is_debug_context,
|
||||
m_preferred_device);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ class GHOST_WindowX11 : public GHOST_Window {
|
||||
* \param parentWindow: Parent (embedder) window.
|
||||
* \param type: The type of drawing context installed in this window.
|
||||
* \param stereoVisual: Stereo visual for quad buffered stereo.
|
||||
* \param preferred_device: Preferred device to use when new device will be created.
|
||||
*/
|
||||
GHOST_WindowX11(GHOST_SystemX11 *system,
|
||||
Display *display,
|
||||
@@ -57,11 +58,12 @@ class GHOST_WindowX11 : public GHOST_Window {
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_WindowX11 *parentWindow,
|
||||
GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
|
||||
const bool is_dialog = false,
|
||||
const bool stereoVisual = false,
|
||||
const bool exclusive = false,
|
||||
const bool is_debug = false);
|
||||
GHOST_TDrawingContextType type,
|
||||
const bool is_dialog,
|
||||
const bool stereoVisual,
|
||||
const bool exclusive,
|
||||
const bool is_debug,
|
||||
const GHOST_GPUDevice &preferred_device);
|
||||
|
||||
bool getValid() const override;
|
||||
|
||||
@@ -250,6 +252,7 @@ class GHOST_WindowX11 : public GHOST_Window {
|
||||
|
||||
bool m_valid_setup;
|
||||
bool m_is_debug_context;
|
||||
GHOST_GPUDevice m_preferred_device;
|
||||
|
||||
void icccmSetState(int state);
|
||||
int icccmGetState() const;
|
||||
|
||||
@@ -696,6 +696,10 @@ class USERPREF_PT_system_display_graphics(SystemPanel, CenterAlignMixIn, Panel):
|
||||
if system.gpu_backend != gpu.platform.backend_type_get():
|
||||
layout.label(text="A restart of Blender is required", icon="INFO")
|
||||
|
||||
if gpu.platform.backend_type_get() == 'VULKAN':
|
||||
col = layout.column()
|
||||
col.prop(system, "gpu_preferred_device")
|
||||
|
||||
if system.gpu_backend == 'VULKAN':
|
||||
col = layout.column()
|
||||
col.label(text="The Vulkan backend is experimental:", icon="INFO")
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -63,6 +66,14 @@ enum GPUArchitectureType {
|
||||
GPU_ARCHITECTURE_TBDR = 1,
|
||||
};
|
||||
|
||||
typedef struct GPUDevice {
|
||||
std::string identifier;
|
||||
int index;
|
||||
uint32_t vendor_id;
|
||||
uint32_t device_id;
|
||||
std::string name;
|
||||
} GPUDevice;
|
||||
|
||||
/* GPU Types */
|
||||
/* TODO: Verify all use-cases of GPU_type_matches to determine which graphics API it should apply
|
||||
* to, and replace with `GPU_type_matches_ex` where appropriate. */
|
||||
@@ -79,3 +90,4 @@ const char *GPU_platform_version();
|
||||
const char *GPU_platform_support_level_key();
|
||||
const char *GPU_platform_gpu_name();
|
||||
GPUArchitectureType GPU_platform_architecture();
|
||||
blender::Span<GPUDevice> GPU_platform_devices_list();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
@@ -172,4 +173,9 @@ bool GPU_type_matches_ex(eGPUDeviceType device,
|
||||
(GPG.backend & backend);
|
||||
}
|
||||
|
||||
blender::Span<GPUDevice> GPU_platform_devices_list()
|
||||
{
|
||||
return GPG.devices.as_span();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
@@ -26,6 +28,7 @@ class GPUPlatformGlobal {
|
||||
char *gpu_name = nullptr;
|
||||
eGPUBackendType backend = GPU_BACKEND_NONE;
|
||||
GPUArchitectureType architecture_type = GPU_ARCHITECTURE_IMR;
|
||||
Vector<GPUDevice> devices;
|
||||
|
||||
public:
|
||||
void init(eGPUDeviceType gpu_device,
|
||||
|
||||
@@ -38,6 +38,84 @@ static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static Vector<StringRefNull> missing_capabilities_get(VkPhysicalDevice vk_physical_device)
|
||||
{
|
||||
Vector<StringRefNull> missing_capabilities;
|
||||
/* Check device features. */
|
||||
VkPhysicalDeviceFeatures2 features = {};
|
||||
VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering = {};
|
||||
|
||||
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
dynamic_rendering.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
|
||||
VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT
|
||||
dynamic_rendering_unused_attachments = {};
|
||||
dynamic_rendering_unused_attachments.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT;
|
||||
features.pNext = &dynamic_rendering;
|
||||
dynamic_rendering.pNext = &dynamic_rendering_unused_attachments;
|
||||
|
||||
vkGetPhysicalDeviceFeatures2(vk_physical_device, &features);
|
||||
#ifndef __APPLE__
|
||||
if (features.features.geometryShader == VK_FALSE) {
|
||||
missing_capabilities.append("geometry shaders");
|
||||
}
|
||||
if (features.features.logicOp == VK_FALSE) {
|
||||
missing_capabilities.append("logical operations");
|
||||
}
|
||||
#endif
|
||||
if (features.features.dualSrcBlend == VK_FALSE) {
|
||||
missing_capabilities.append("dual source blending");
|
||||
}
|
||||
if (features.features.imageCubeArray == VK_FALSE) {
|
||||
missing_capabilities.append("image cube array");
|
||||
}
|
||||
if (features.features.multiDrawIndirect == VK_FALSE) {
|
||||
missing_capabilities.append("multi draw indirect");
|
||||
}
|
||||
if (features.features.multiViewport == VK_FALSE) {
|
||||
missing_capabilities.append("multi viewport");
|
||||
}
|
||||
if (features.features.shaderClipDistance == VK_FALSE) {
|
||||
missing_capabilities.append("shader clip distance");
|
||||
}
|
||||
if (features.features.drawIndirectFirstInstance == VK_FALSE) {
|
||||
missing_capabilities.append("draw indirect first instance");
|
||||
}
|
||||
if (features.features.fragmentStoresAndAtomics == VK_FALSE) {
|
||||
missing_capabilities.append("fragment stores and atomics");
|
||||
}
|
||||
if (dynamic_rendering.dynamicRendering == VK_FALSE) {
|
||||
missing_capabilities.append("dynamic rendering");
|
||||
}
|
||||
|
||||
/* Check device extensions. */
|
||||
uint32_t vk_extension_count;
|
||||
vkEnumerateDeviceExtensionProperties(vk_physical_device, nullptr, &vk_extension_count, nullptr);
|
||||
|
||||
Array<VkExtensionProperties> vk_extensions(vk_extension_count);
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
vk_physical_device, nullptr, &vk_extension_count, vk_extensions.data());
|
||||
Set<StringRefNull> extensions;
|
||||
for (VkExtensionProperties &vk_extension : vk_extensions) {
|
||||
extensions.add(vk_extension.extensionName);
|
||||
}
|
||||
|
||||
if (!extensions.contains(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
return missing_capabilities;
|
||||
}
|
||||
|
||||
bool VKBackend::is_supported()
|
||||
{
|
||||
CLG_logref_init(&LOG);
|
||||
@@ -71,86 +149,11 @@ bool VKBackend::is_supported()
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, vk_physical_devices.data());
|
||||
|
||||
for (VkPhysicalDevice vk_physical_device : vk_physical_devices) {
|
||||
Vector<StringRefNull> missing_capabilities = missing_capabilities_get(vk_physical_device);
|
||||
|
||||
/* Check minimum device property limits. */
|
||||
VkPhysicalDeviceProperties vk_properties = {};
|
||||
vkGetPhysicalDeviceProperties(vk_physical_device, &vk_properties);
|
||||
|
||||
Vector<StringRefNull> missing_capabilities;
|
||||
|
||||
/* Check device features. */
|
||||
VkPhysicalDeviceFeatures2 features = {};
|
||||
VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering = {};
|
||||
|
||||
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
dynamic_rendering.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
|
||||
VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT
|
||||
dynamic_rendering_unused_attachments = {};
|
||||
dynamic_rendering_unused_attachments.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT;
|
||||
features.pNext = &dynamic_rendering;
|
||||
dynamic_rendering.pNext = &dynamic_rendering_unused_attachments;
|
||||
|
||||
vkGetPhysicalDeviceFeatures2(vk_physical_device, &features);
|
||||
#ifndef __APPLE__
|
||||
if (features.features.geometryShader == VK_FALSE) {
|
||||
missing_capabilities.append("geometry shaders");
|
||||
}
|
||||
if (features.features.logicOp == VK_FALSE) {
|
||||
missing_capabilities.append("logical operations");
|
||||
}
|
||||
#endif
|
||||
if (features.features.dualSrcBlend == VK_FALSE) {
|
||||
missing_capabilities.append("dual source blending");
|
||||
}
|
||||
if (features.features.imageCubeArray == VK_FALSE) {
|
||||
missing_capabilities.append("image cube array");
|
||||
}
|
||||
if (features.features.multiDrawIndirect == VK_FALSE) {
|
||||
missing_capabilities.append("multi draw indirect");
|
||||
}
|
||||
if (features.features.multiViewport == VK_FALSE) {
|
||||
missing_capabilities.append("multi viewport");
|
||||
}
|
||||
if (features.features.shaderClipDistance == VK_FALSE) {
|
||||
missing_capabilities.append("shader clip distance");
|
||||
}
|
||||
if (features.features.drawIndirectFirstInstance == VK_FALSE) {
|
||||
missing_capabilities.append("draw indirect first instance");
|
||||
}
|
||||
if (features.features.fragmentStoresAndAtomics == VK_FALSE) {
|
||||
missing_capabilities.append("fragment stores and atomics");
|
||||
}
|
||||
if (dynamic_rendering.dynamicRendering == VK_FALSE) {
|
||||
missing_capabilities.append("dynamic rendering");
|
||||
}
|
||||
|
||||
/* Check device extensions. */
|
||||
uint32_t vk_extension_count;
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
vk_physical_device, nullptr, &vk_extension_count, nullptr);
|
||||
|
||||
Array<VkExtensionProperties> vk_extensions(vk_extension_count);
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
vk_physical_device, nullptr, &vk_extension_count, vk_extensions.data());
|
||||
Set<StringRefNull> extensions;
|
||||
for (VkExtensionProperties &vk_extension : vk_extensions) {
|
||||
extensions.add(vk_extension.extensionName);
|
||||
}
|
||||
|
||||
if (!extensions.contains(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
/* Report result. */
|
||||
if (missing_capabilities.is_empty()) {
|
||||
/* This device meets minimum requirements. */
|
||||
@@ -206,6 +209,53 @@ void VKBackend::platform_init()
|
||||
"",
|
||||
"",
|
||||
GPU_ARCHITECTURE_IMR);
|
||||
|
||||
/* Query for all compatible devices */
|
||||
VkApplicationInfo vk_application_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
||||
vk_application_info.pApplicationName = "Blender";
|
||||
vk_application_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
vk_application_info.pEngineName = "Blender";
|
||||
vk_application_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
vk_application_info.apiVersion = VK_API_VERSION_1_2;
|
||||
|
||||
const char *instance_extensions[] = {VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME};
|
||||
|
||||
VkInstanceCreateInfo vk_instance_info = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||
vk_instance_info.pApplicationInfo = &vk_application_info;
|
||||
vk_instance_info.enabledExtensionCount = 1;
|
||||
vk_instance_info.ppEnabledExtensionNames = instance_extensions;
|
||||
|
||||
VkInstance vk_instance = VK_NULL_HANDLE;
|
||||
vkCreateInstance(&vk_instance_info, nullptr, &vk_instance);
|
||||
BLI_assert(vk_instance != VK_NULL_HANDLE);
|
||||
|
||||
uint32_t physical_devices_count = 0;
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, nullptr);
|
||||
Array<VkPhysicalDevice> vk_physical_devices(physical_devices_count);
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, vk_physical_devices.data());
|
||||
int index = 0;
|
||||
for (VkPhysicalDevice vk_physical_device : vk_physical_devices) {
|
||||
if (missing_capabilities_get(vk_physical_device).is_empty()) {
|
||||
VkPhysicalDeviceProperties vk_properties = {};
|
||||
vkGetPhysicalDeviceProperties(vk_physical_device, &vk_properties);
|
||||
std::stringstream identifier;
|
||||
identifier << std::hex << vk_properties.vendorID << "/" << vk_properties.deviceID << "/"
|
||||
<< index;
|
||||
GPG.devices.append({identifier.str(),
|
||||
index,
|
||||
vk_properties.vendorID,
|
||||
vk_properties.deviceID,
|
||||
std::string(vk_properties.deviceName)});
|
||||
}
|
||||
index++;
|
||||
}
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
std::sort(GPG.devices.begin(), GPG.devices.end(), [&](const GPUDevice &a, const GPUDevice &b) {
|
||||
if (a.name == b.name) {
|
||||
return a.index < b.index;
|
||||
}
|
||||
return a.name < b.name;
|
||||
});
|
||||
}
|
||||
|
||||
void VKBackend::platform_init(const VKDevice &device)
|
||||
|
||||
@@ -1002,6 +1002,11 @@ typedef struct UserDef {
|
||||
/** Seconds to zoom around current frame. */
|
||||
float view_frame_seconds;
|
||||
|
||||
/** Preferred device/vendor for GPU device selection. */
|
||||
int gpu_preferred_index;
|
||||
uint32_t gpu_preferred_vendor_id;
|
||||
uint32_t gpu_preferred_device_id;
|
||||
char _pad16[4];
|
||||
/** #eGPUBackendType */
|
||||
short gpu_backend;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
@@ -174,6 +175,11 @@ static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
|
||||
{GPU_BACKEND_VULKAN, "VULKAN", 0, "Vulkan (experimental)", "Use Vulkan backend"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
static const EnumPropertyItem rna_enum_preference_gpu_preferred_device_items[] = {
|
||||
{0, "AUTO", 0, "Auto", "Auto detect best GPU for running Blender"},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem rna_enum_preferences_extension_repo_source_type_items[] = {
|
||||
{USER_EXTENSION_REPO_SOURCE_USER,
|
||||
@@ -1421,6 +1427,71 @@ static const EnumPropertyItem *rna_preference_gpu_backend_itemf(bContext * /*C*/
|
||||
return result;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_preference_gpu_preferred_device_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
int totitem = 0;
|
||||
EnumPropertyItem *result = nullptr;
|
||||
|
||||
for (int i = 0; rna_enum_preference_gpu_preferred_device_items[i].identifier != nullptr; i++) {
|
||||
const EnumPropertyItem *item = &rna_enum_preference_gpu_preferred_device_items[i];
|
||||
RNA_enum_item_add(&result, &totitem, item);
|
||||
}
|
||||
int index = 1;
|
||||
for (const GPUDevice &gpu_device : GPU_platform_devices_list()) {
|
||||
EnumPropertyItem item = {};
|
||||
item.value = index;
|
||||
item.identifier = gpu_device.identifier.c_str();
|
||||
item.name = gpu_device.name.c_str();
|
||||
item.description = gpu_device.name.c_str();
|
||||
RNA_enum_item_add(&result, &totitem, &item);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&result, &totitem);
|
||||
*r_free = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rna_preference_gpu_preferred_device_get(PointerRNA *ptr)
|
||||
{
|
||||
UserDef *preferences = (UserDef *)ptr->data;
|
||||
int index = 1;
|
||||
for (const GPUDevice &gpu_device : GPU_platform_devices_list()) {
|
||||
if (gpu_device.index == preferences->gpu_preferred_index &&
|
||||
gpu_device.vendor_id == preferences->gpu_preferred_vendor_id &&
|
||||
gpu_device.device_id == preferences->gpu_preferred_device_id)
|
||||
{
|
||||
/* Offset by one as first item in the list is always autodetection. */
|
||||
return index;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rna_preference_gpu_preferred_device_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
UserDef *preferences = (UserDef *)ptr->data;
|
||||
if (value > 0) {
|
||||
value -= 1;
|
||||
blender::Span<GPUDevice> devices = GPU_platform_devices_list();
|
||||
if (value < devices.size()) {
|
||||
const GPUDevice &device = devices[value];
|
||||
preferences->gpu_preferred_index = device.index;
|
||||
preferences->gpu_preferred_vendor_id = device.vendor_id;
|
||||
preferences->gpu_preferred_device_id = device.device_id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
preferences->gpu_preferred_index = 0;
|
||||
preferences->gpu_preferred_vendor_id = 0u;
|
||||
preferences->gpu_preferred_device_id = 0u;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \
|
||||
@@ -6259,6 +6330,18 @@ static void rna_def_userdef_system(BlenderRNA *brna)
|
||||
"GPU Backend",
|
||||
"GPU backend to use (requires restarting Blender for changes to take effect)");
|
||||
|
||||
prop = RNA_def_property(srna, "gpu_preferred_device", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_preference_gpu_preferred_device_items);
|
||||
RNA_def_property_enum_funcs(prop,
|
||||
"rna_preference_gpu_preferred_device_get",
|
||||
"rna_preference_gpu_preferred_device_set",
|
||||
"rna_preference_gpu_preferred_device_itemf");
|
||||
RNA_def_property_enum_default(prop, 0);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Device",
|
||||
"Preferred device to select during detection (requires restarting "
|
||||
"Blender for changes to take effect)");
|
||||
|
||||
prop = RNA_def_property(srna, "max_shader_compilation_subprocesses", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0, INT16_MAX);
|
||||
RNA_def_property_ui_text(prop,
|
||||
|
||||
@@ -1583,6 +1583,9 @@ static GHOST_WindowHandle playanim_window_open(
|
||||
GHOST_GPUSettings gpusettings = {0};
|
||||
const eGPUBackendType gpu_backend = GPU_backend_type_selection_get();
|
||||
gpusettings.context_type = wm_ghost_drawing_context_type(gpu_backend);
|
||||
gpusettings.preferred_device.index = U.gpu_preferred_index;
|
||||
gpusettings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
|
||||
gpusettings.preferred_device.device_id = U.gpu_preferred_device_id;
|
||||
|
||||
{
|
||||
bool screen_size_valid = false;
|
||||
|
||||
@@ -736,6 +736,9 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
|
||||
|
||||
eGPUBackendType gpu_backend = GPU_backend_type_selection_get();
|
||||
gpuSettings.context_type = wm_ghost_drawing_context_type(gpu_backend);
|
||||
gpuSettings.preferred_device.index = U.gpu_preferred_index;
|
||||
gpuSettings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
|
||||
gpuSettings.preferred_device.device_id = U.gpu_preferred_device_id;
|
||||
|
||||
int posx = 0;
|
||||
int posy = 0;
|
||||
@@ -3039,6 +3042,10 @@ void *WM_system_gpu_context_create()
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
gpuSettings.flags |= GHOST_gpuDebugContext;
|
||||
}
|
||||
gpuSettings.preferred_device.index = U.gpu_preferred_index;
|
||||
gpuSettings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
|
||||
gpuSettings.preferred_device.device_id = U.gpu_preferred_device_id;
|
||||
|
||||
return GHOST_CreateGPUContext(g_system, gpuSettings);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user