blender/intern/cycles/util/system.cpp
Brecht Van Lommel 7b6485f549 Core: Increase maximum number of open file handles
On Windows and macOS, the default maximum number of stdio file handles
is 512 and 256 respectively. On Linux it's usually some very high
number. This increases that limit on startup to 8192, to better handle
some production files and have more uniform limits across platforms.
This is commonly done in VFX software.

Some code in Blender keeps file handles open for a long time,
particularly the Cycles texture cache and Alembic cache file datablocks.
This helps improve performance when reading tiles or frames on demand,
without having to reopen the file each time.

Cycles limits itself to 128 file handles, but there is no limit on cache
file datablocks. The production file in #160383 has 480 open file
handles for Alembic, so it was already near the limit on Windows, and
then a few more pushed it over.

By increasing the limit, at a minimum all production files that worked
before should continue to work when enabling the texture cache.
Improving the cache file datablock to use fewer handles is left for
another time.

Fix #160383: Cycles texture missing images in production file

Pull Request: https://projects.blender.org/blender/blender/pulls/160488
2026-06-23 12:29:51 +02:00

311 lines
7.8 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "util/system.h"
#include "util/log.h"
#include "util/string.h"
#include <algorithm>
#include <cstring>
#ifdef _WIN32
# include <cstdio>
# if (!defined(FREE_WINDOWS))
# include <intrin.h>
# endif
# include "util/windows.h"
#elif defined(__APPLE__)
# include <cstdint>
# include <sys/ioctl.h>
# include <sys/resource.h>
# include <sys/sysctl.h>
# include <sys/types.h>
# include <unistd.h>
#else
# include <cstdint>
# include <sys/ioctl.h>
# include <sys/resource.h>
# include <unistd.h>
#endif
CCL_NAMESPACE_BEGIN
int system_console_width()
{
int columns = 0;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
columns = csbi.dwSize.X;
}
#else
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
columns = w.ws_col;
}
#endif
return (columns > 0) ? columns : 80;
}
/* Equivalent of Windows __cpuid for x86 processors on other platforms. */
#if (!defined(_WIN32) || defined(FREE_WINDOWS)) && (defined(__x86_64__) || defined(__i386__))
static void __cpuid(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));
# elif defined(__i386__)
asm("pushl %%ebx \n\t"
"cpuid \n\t"
"movl %%ebx, %1 \n\t"
"popl %%ebx \n\t"
: "=a"(data[0]), "=r"(data[1]), "=c"(data[2]), "=d"(data[3])
: "a"(selector)
: "ebx");
# else
data[0] = data[1] = data[2] = data[3] = 0;
# endif
}
#endif
string system_cpu_brand_string()
{
#if defined(__APPLE__)
/* Get from system on macOS. */
char modelname[512] = "";
size_t bufferlen = 512;
if (sysctlbyname("machdep.cpu.brand_string", &modelname, &bufferlen, nullptr, 0) == 0) {
return modelname;
}
#elif (defined(WIN32) || defined(__x86_64__) || defined(__i386__)) && !defined(_M_ARM64)
/* Get from intrinsics on Windows and x86. */
char buf[49] = {0};
int result[4] = {0};
__cpuid(result, 0x80000000);
if (result[0] != 0 && result[0] >= (int)0x80000004) {
__cpuid((int *)(buf + 0), 0x80000002);
__cpuid((int *)(buf + 16), 0x80000003);
__cpuid((int *)(buf + 32), 0x80000004);
string brand = buf;
/* Make it a bit more presentable. */
brand = string_remove_trademark(brand);
brand = string_remove_gpu_from_cpu_name(brand);
return brand;
}
#elif defined(_M_ARM64)
DWORD processorNameStringLength = 255;
char processorNameString[255];
if (RegGetValueA(HKEY_LOCAL_MACHINE,
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"ProcessorNameString",
RRF_RT_REG_SZ,
nullptr,
&processorNameString,
&processorNameStringLength) == ERROR_SUCCESS)
{
return processorNameString;
}
#else
/* Get from /proc/cpuinfo on Unix systems. */
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
if (cpuinfo != nullptr) {
char cpuinfo_buf[513] = "";
fread(cpuinfo_buf, sizeof(cpuinfo_buf) - 1, 1, cpuinfo);
fclose(cpuinfo);
char *modelname = strstr(cpuinfo_buf, "model name");
if (modelname != nullptr) {
modelname = strchr(modelname, ':');
if (modelname != nullptr) {
modelname += 2;
char *modelname_end = strchr(modelname, '\n');
if (modelname_end != nullptr) {
*modelname_end = '\0';
return modelname;
}
}
}
}
#endif
return "Unknown CPU";
}
int system_cpu_bits()
{
return (sizeof(void *) * 8);
}
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
struct CPUCapabilities {
bool sse42;
bool avx2;
bool f16c;
};
static CPUCapabilities &system_cpu_capabilities()
{
static CPUCapabilities caps = {};
static bool caps_init = false;
if (!caps_init) {
int result[4], num;
__cpuid(result, 0);
num = result[0];
if (num >= 1) {
__cpuid(result, 0x00000001);
const bool sse = (result[3] & ((int)1 << 25)) != 0;
const bool sse2 = (result[3] & ((int)1 << 26)) != 0;
const bool sse3 = (result[2] & ((int)1 << 0)) != 0;
const bool ssse3 = (result[2] & ((int)1 << 9)) != 0;
const bool sse41 = (result[2] & ((int)1 << 19)) != 0;
const bool sse42 = (result[2] & ((int)1 << 20)) != 0;
const bool fma3 = (result[2] & ((int)1 << 12)) != 0;
const bool os_uses_xsave_xrestore = (result[2] & ((int)1 << 27)) != 0;
const bool cpu_avx_support = (result[2] & ((int)1 << 28)) != 0;
/* Simplify to combined capabilities for which we specialize kernels. */
caps.sse42 = sse && sse2 && sse3 && ssse3 && sse41 && sse42;
if (os_uses_xsave_xrestore && cpu_avx_support) {
// Check if the OS will save the YMM registers
uint32_t xcr_feature_mask;
# if defined(__GNUC__)
int edx; /* not used */
/* actual opcode for xgetbv */
__asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr_feature_mask), "=d"(edx) : "c"(0));
# elif defined(_MSC_VER) && defined(_XCR_XFEATURE_ENABLED_MASK)
/* Minimum VS2010 SP1 compiler is required. */
xcr_feature_mask = (uint32_t)_xgetbv(_XCR_XFEATURE_ENABLED_MASK);
# else
xcr_feature_mask = 0;
# endif
const bool avx = (xcr_feature_mask & 0x6) == 0x6;
const bool f16c = (result[2] & ((int)1 << 29)) != 0;
caps.f16c = avx && f16c;
__cpuid(result, 0x00000007);
bool bmi1 = (result[1] & ((int)1 << 3)) != 0;
bool bmi2 = (result[1] & ((int)1 << 8)) != 0;
bool avx2 = (result[1] & ((int)1 << 5)) != 0;
caps.avx2 = sse && sse2 && sse3 && ssse3 && sse41 && sse42 && avx && f16c && avx2 &&
fma3 && bmi1 && bmi2;
}
}
caps_init = true;
}
return caps;
}
bool system_cpu_support_sse42()
{
CPUCapabilities &caps = system_cpu_capabilities();
return caps.sse42;
}
bool system_cpu_support_avx2()
{
/* F16C is considered part of AVX2 for our purpose, as all physical CPUs with
* AVX2 support also support it so there is no point having a separate kernel.
*
* Some cases where it might be missing is Rosetta or virtual machines, so we
* check for it just to be safe. */
CPUCapabilities &caps = system_cpu_capabilities();
return caps.avx2 && caps.f16c;
}
#else
bool system_cpu_support_sse42()
{
return false;
}
bool system_cpu_support_avx2()
{
return false;
}
#endif
size_t system_physical_ram()
{
#ifdef _WIN32
MEMORYSTATUSEX ram;
ram.dwLength = sizeof(ram);
GlobalMemoryStatusEx(&ram);
return ram.ullTotalPhys;
#elif defined(__APPLE__)
uint64_t ram = 0;
size_t len = sizeof(ram);
if (sysctlbyname("hw.memsize", &ram, &len, nullptr, 0) == 0) {
return ram;
}
return 0;
#else
size_t ps = sysconf(_SC_PAGESIZE);
size_t pn = sysconf(_SC_PHYS_PAGES);
return ps * pn;
#endif
}
uint64_t system_self_process_id()
{
#ifdef _WIN32
return GetCurrentProcessId();
#else
return getpid();
#endif
}
size_t system_max_open_files()
{
#if defined(_WIN32)
return _getmaxstdio();
#else
struct rlimit limit = {};
return (getrlimit(RLIMIT_NOFILE, &limit) == 0) ? limit.rlim_cur : SIZE_MAX;
#endif
}
void system_max_open_files_ensure()
{
/* The Windows maximum is 8192 open files. */
constexpr int max_open_files = 8192;
bool ok = true;
#if defined(_WIN32)
if (_getmaxstdio() < max_open_files) {
ok = _setmaxstdio(max_open_files) == max_open_files;
}
#else
struct rlimit limit = {};
ok = getrlimit(RLIMIT_NOFILE, &limit) == 0;
if (ok && limit.rlim_cur < rlim_t(max_open_files)) {
limit.rlim_cur = std::min(rlim_t(max_open_files), limit.rlim_max);
ok = setrlimit(RLIMIT_NOFILE, &limit) == 0;
}
#endif
if (!ok) {
LOG_DEBUG << "Failed to ensure max open files is at least " << max_open_files << ": "
<< strerror(errno);
}
}
CCL_NAMESPACE_END