Creator: Add CPU check on startup

This adds an sse42 cpu check on startup for both linux and windows,
mac has been excluded, since there are no intel based macs that do
not support SSE42.

The way this works is, we count on the OS to initialize the shared
libraries in the order they are linked (which currently holds true)
before calling the initialization code in the main executable. This
allows us to check the CPU before running any of the code in the main
executable that might not be supported by the current isa.

Changing those build flags is for a future PR, but for now and for
future reference: blender_cpu_check must be build without optimized CPU
flags so it'll be able to run on older CPUs.

some code has been duplicated from blenlib, there's really no way around
that since we cannot link blenlib as it may be build with optimized cpu
flags.

Windows currently gives a popup to inform the user, while linux reports
to the console, there may be better ways to communicate with linux users
with perhaps some generic GUI popup, but I'm unaware of these and will
leave this for the linux platform maintainer to polish.

Part of #116592

Pull Request: https://projects.blender.org/blender/blender/pulls/118054
This commit is contained in:
Ray Molenkamp 2024-02-19 18:11:57 +01:00 committed by Ray molenkamp
parent c473a165b8
commit 0326b29899
4 changed files with 160 additions and 2 deletions

View File

@ -387,6 +387,18 @@ else()
set(WITH_SYSTEM_EIGEN3 OFF)
endif()
if((NOT WITH_PYTHON_MODULE) AND (
(WIN32 AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")) OR
((UNIX AND NOT APPLE) AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"))))
option(WITH_CPU_CHECK "\
Report when a CPU is not compatible on startup \
instead of failing to start with an inscrutable error."
ON
)
mark_as_advanced(WITH_CPU_CHECK)
else()
set(WITH_CPU_CHECK OFF)
endif()
# Modifiers
option(WITH_MOD_FLUID "Enable Mantaflow Fluid Simulation Framework" ON)

View File

@ -327,6 +327,31 @@ if(WITH_PYTHON_MODULE)
else()
add_executable(blender ${EXETYPE} ${SRC})
if(WITH_CPU_CHECK)
target_compile_definitions(blender PRIVATE WITH_CPU_CHECK)
# we cannot directly link against any blender libraries for the cpu_check module
# as they may have been build for an ISA that is unsupported by the CPU
# running this code.
add_library(blender_cpu_check SHARED
creator_cpu_check.cc
)
target_link_libraries(blender_cpu_check
PRIVATE ${PLATFORM_LINKLIBS}
)
# blender_cpu_check *NEEDS* to be linked first, there can be no exceptions
# to this, this is to ensure this will be the first code to run once the
# blender binary has been loaded by the OS.
target_link_libraries(blender PRIVATE blender_cpu_check)
if(NOT WIN32)
set(_LIB_SUB_FOLDER "lib/")
endif()
set_target_properties(blender_cpu_check
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${_LIB_SUB_FOLDER}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${_LIB_SUB_FOLDER}"
)
unset(_LIB_SUB_FOLDER)
endif()
if(WIN32)
add_executable(blender-launcher WIN32
blender_launcher_win32.c
@ -690,6 +715,13 @@ if(UNIX AND NOT APPLE)
DESTINATION "."
)
if(WITH_CPU_CHECK)
install(
TARGETS blender_cpu_check
DESTINATION "./lib"
)
endif()
install(
FILES
${CMAKE_SOURCE_DIR}/release/freedesktop/blender.desktop
@ -745,7 +777,6 @@ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libglapi.so.0.0.0)\n
TARGETS blender
DESTINATION "./bin"
)
# Misc files.
install(
FILES ${CMAKE_SOURCE_DIR}/release/freedesktop/blender.desktop
@ -1850,6 +1881,13 @@ if(WIN32 AND NOT WITH_PYTHON_MODULE)
COMPONENT Blender
DESTINATION "."
)
if(WITH_CPU_CHECK)
install(
TARGETS blender_cpu_check
COMPONENT Blender
DESTINATION "."
)
endif()
set_target_properties(
blender
PROPERTIES

View File

@ -12,6 +12,9 @@
#ifdef WIN32
# include "utfconv.hh"
# include <windows.h>
# ifdef WITH_CPU_CHECK
# pragma comment(linker, "/include:cpu_check_win32")
# endif
#endif
#if defined(WITH_TBB_MALLOC) && defined(_MSC_VER) && defined(NDEBUG)
@ -277,7 +280,6 @@ int main(int argc,
)
{
bContext *C;
#ifndef WITH_PYTHON_MODULE
bArgs *ba;
#endif

View File

@ -0,0 +1,106 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup creator
*/
#include <string>
#if defined(WIN32)
# include <Windows.h>
# include <intrin.h>
#endif
/* The code below is duplicated from system.c from bf_blenlib. This is on purpose, since bf_blenlib
* may be build with CPU flags that are not available on the current cpu so we can't link it. */
#if !defined(_WIN32)
static void __cpuid(
/* Cannot be const, because it is modified below.
* NOLINTNEXTLINE: readability-non-const-parameter. */
int data[4],
int selector)
{
# if defined(__x86_64__)
asm("cpuid" : "=a"(data[0]), "=b"(data[1]), "=c"(data[2]), "=d"(data[3]) : "a"(selector));
# else
(void)selector;
data[0] = data[1] = data[2] = data[3] = 0;
# endif
}
#endif
static int cpu_supports_sse42(void)
{
int result[4], num;
__cpuid(result, 0);
num = result[0];
if (num >= 1) {
__cpuid(result, 0x00000001);
return (result[2] & ((int)1 << 20)) != 0;
}
return 0;
}
static const char *cpu_brand_string(void)
{
static char buf[49] = {0};
int result[4] = {0};
__cpuid(result, 0x80000000);
if (result[0] >= (int)0x80000004) {
__cpuid((int *)(buf + 0), 0x80000002);
__cpuid((int *)(buf + 16), 0x80000003);
__cpuid((int *)(buf + 32), 0x80000004);
const char *buf_ptr = buf;
// Trim any leading spaces.
while (*buf_ptr == ' ') {
buf_ptr++;
}
return buf_ptr;
}
return NULL;
}
#ifdef _MSC_VER
extern "C" __declspec(dllexport) void cpu_check_win32()
{
# ifdef _M_X64
if (!cpu_supports_sse42()) {
std::string error_title = "Unsupported CPU - " + std::string(cpu_brand_string());
MessageBoxA(NULL,
"Blender requires a CPU with SSE42 support.",
error_title.c_str(),
MB_OK | MB_ICONERROR);
exit(-1);
}
# endif
}
BOOL WINAPI DllMain(HINSTANCE /* hinstDLL */, DWORD fdwReason, LPVOID /* lpvReserved */)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
cpu_check_win32();
break;
}
return TRUE;
}
#else
# include <cstdio>
# include <cstdlib>
static __attribute__((constructor)) void cpu_check(void)
{
# ifdef __x86_64
if (!cpu_supports_sse42()) {
std::string error = "Unsupported CPU - " + std::string(cpu_brand_string()) +
"\nBlender requires a CPU with SSE42 support.";
printf("%s\n", error.c_str());
exit(-1);
}
return;
# endif
}
#endif