Cycles: Add command line option for overriding the compute device

The current way of setting the compute device makes sense for local
use, but for headless rendering it it a massive pain to get Cycles
to use the correct device, usually involving entire Python scripts.

Therefore, this patch adds a simple command-line option to Blender
for specifying the type of device that should be used. If the option
is present, the settings in the user preferences and the scene are
ignored, and instead all devices matching the specified type are used.

Differential Revision: https://developer.blender.org/D9086
This commit is contained in:
Lukas Stockner 2020-10-02 00:48:01 +02:00
parent 90a27d5aa9
commit cfa101c228
7 changed files with 67 additions and 4 deletions

View File

@ -70,6 +70,11 @@ def _configure_argument_parser():
parser.add_argument("--cycles-print-stats",
help="Print rendering statistics to stderr",
action='store_true')
parser.add_argument("--cycles-device",
help="Set the device to use for Cycles, overriding user preferences and the scene setting."
"Valid options are 'CPU', 'CUDA', 'OPTIX' or 'OPENCL'."
"Additionally, you can append '+CPU' to any GPU type for hybrid rendering.",
default=None)
return parser
@ -102,6 +107,10 @@ def _parse_command_line():
import _cycles
_cycles.enable_print_stats()
if args.cycles_device:
import _cycles
_cycles.set_device_override(args.cycles_device)
def init():
import bpy

View File

@ -15,6 +15,7 @@
*/
#include "blender/blender_device.h"
#include "blender/blender_session.h"
#include "blender/blender_util.h"
#include "util/util_foreach.h"
@ -42,6 +43,18 @@ int blender_device_threads(BL::Scene &b_scene)
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background)
{
if (BlenderSession::device_override != DEVICE_MASK_ALL) {
vector<DeviceInfo> devices = Device::available_devices(BlenderSession::device_override);
if (devices.empty()) {
printf("Found no Cycles device of the specified type, falling back to CPU...\n");
return Device::available_devices(DEVICE_MASK_CPU).front();
}
int threads = blender_device_threads(b_scene);
return Device::get_multi_device(devices, threads, background);
}
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
/* Default to CPU device. */

View File

@ -968,6 +968,44 @@ static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
return list;
}
static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg)
{
PyObject *override_string = PyObject_Str(arg);
string override = PyUnicode_AsUTF8(override_string);
Py_DECREF(override_string);
bool include_cpu = false;
const string cpu_suffix = "+CPU";
if (string_endswith(override, cpu_suffix)) {
include_cpu = true;
override = override.substr(0, override.length() - cpu_suffix.length());
}
if (override == "CPU") {
BlenderSession::device_override = DEVICE_MASK_CPU;
}
else if (override == "OPENCL") {
BlenderSession::device_override = DEVICE_MASK_OPENCL;
}
else if (override == "CUDA") {
BlenderSession::device_override = DEVICE_MASK_CUDA;
}
else if (override == "OPTIX") {
BlenderSession::device_override = DEVICE_MASK_OPTIX;
}
else {
printf("\nError: %s is not a valid Cycles device.\n", override.c_str());
Py_RETURN_FALSE;
}
if (include_cpu) {
BlenderSession::device_override = (DeviceTypeMask)(BlenderSession::device_override |
DEVICE_MASK_CPU);
}
Py_RETURN_TRUE;
}
static PyMethodDef methods[] = {
{"init", init_func, METH_VARARGS, ""},
{"exit", exit_func, METH_VARARGS, ""},
@ -1007,6 +1045,7 @@ static PyMethodDef methods[] = {
/* Compute Device selection */
{"get_device_types", get_device_types_func, METH_VARARGS, ""},
{"set_device_override", set_device_override_func, METH_O, ""},
{NULL, NULL, 0, NULL},
};

View File

@ -47,6 +47,7 @@
CCL_NAMESPACE_BEGIN
DeviceTypeMask BlenderSession::device_override = DEVICE_MASK_ALL;
bool BlenderSession::headless = false;
int BlenderSession::num_resumable_chunks = 0;
int BlenderSession::current_resumable_chunk = 0;

View File

@ -126,6 +126,7 @@ class BlenderSession {
/* Global state which is common for all render sessions created from Blender.
* Usually denotes command line arguments.
*/
static DeviceTypeMask device_override;
/* Blender is running from the command line, no windows are shown and some
* extra render optimization is possible (possible to free draw-only data and

View File

@ -117,14 +117,14 @@ bool string_startswith(const string &s, const char *start)
return strncmp(s.c_str(), start, len) == 0;
}
bool string_endswith(const string &s, const char *end)
bool string_endswith(const string &s, const string &end)
{
size_t len = strlen(end);
size_t len = end.length();
if (len > s.size())
return 0;
else
return strncmp(s.c_str() + s.size() - len, end, len) == 0;
return s.compare(s.length() - len, len, end) == 0;
}
string string_strip(const string &s)

View File

@ -46,7 +46,7 @@ void string_split(vector<string> &tokens,
bool skip_empty_tokens = true);
void string_replace(string &haystack, const string &needle, const string &other);
bool string_startswith(const string &s, const char *start);
bool string_endswith(const string &s, const char *end);
bool string_endswith(const string &s, const string &end);
string string_strip(const string &s);
string string_remove_trademark(const string &s);
string string_from_bool(const bool var);