Cycles: move some Scene related methods out of Session
This moves `Session::get_requested_device_features`, `Session::load_kernels`, and `Session::update_scene` out of `Session` and into `Scene`, as mentioned in D8544. Reviewed By: brecht Differential Revision: https://developer.blender.org/D8590
This commit is contained in:
@@ -180,7 +180,6 @@ class DeviceRequestedFeatures {
|
||||
DeviceRequestedFeatures()
|
||||
{
|
||||
/* TODO(sergey): Find more meaningful defaults. */
|
||||
experimental = false;
|
||||
max_nodes_group = 0;
|
||||
nodes_features = 0;
|
||||
use_hair = false;
|
||||
@@ -203,8 +202,7 @@ class DeviceRequestedFeatures {
|
||||
|
||||
bool modified(const DeviceRequestedFeatures &requested_features)
|
||||
{
|
||||
return !(experimental == requested_features.experimental &&
|
||||
max_nodes_group == requested_features.max_nodes_group &&
|
||||
return !(max_nodes_group == requested_features.max_nodes_group &&
|
||||
nodes_features == requested_features.nodes_features &&
|
||||
use_hair == requested_features.use_hair &&
|
||||
use_hair_thick == requested_features.use_hair_thick &&
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "render/osl.h"
|
||||
#include "render/particles.h"
|
||||
#include "render/scene.h"
|
||||
#include "render/session.h"
|
||||
#include "render/shader.h"
|
||||
#include "render/svm.h"
|
||||
#include "render/tables.h"
|
||||
@@ -109,6 +110,10 @@ Scene::Scene(const SceneParams ¶ms_, Device *device)
|
||||
image_manager = new ImageManager(device->info);
|
||||
particle_system_manager = new ParticleSystemManager();
|
||||
bake_manager = new BakeManager();
|
||||
kernels_loaded = false;
|
||||
|
||||
/* TODO(sergey): Check if it's indeed optimal value for the split kernel. */
|
||||
max_closure_global = 1;
|
||||
|
||||
/* OSL only works on the CPU */
|
||||
if (device->info.has_osl)
|
||||
@@ -396,4 +401,161 @@ void Scene::collect_statistics(RenderStats *stats)
|
||||
image_manager->collect_statistics(stats);
|
||||
}
|
||||
|
||||
DeviceRequestedFeatures Scene::get_requested_device_features()
|
||||
{
|
||||
DeviceRequestedFeatures requested_features;
|
||||
|
||||
shader_manager->get_requested_features(this, &requested_features);
|
||||
|
||||
/* This features are not being tweaked as often as shaders,
|
||||
* so could be done selective magic for the viewport as well.
|
||||
*/
|
||||
bool use_motion = need_motion() == Scene::MotionType::MOTION_BLUR;
|
||||
requested_features.use_hair = false;
|
||||
requested_features.use_hair_thick = (params.hair_shape == CURVE_THICK);
|
||||
requested_features.use_object_motion = false;
|
||||
requested_features.use_camera_motion = use_motion && camera->use_motion();
|
||||
foreach (Object *object, objects) {
|
||||
Geometry *geom = object->geometry;
|
||||
if (use_motion) {
|
||||
requested_features.use_object_motion |= object->use_motion() | geom->use_motion_blur;
|
||||
requested_features.use_camera_motion |= geom->use_motion_blur;
|
||||
}
|
||||
if (object->is_shadow_catcher) {
|
||||
requested_features.use_shadow_tricks = true;
|
||||
}
|
||||
if (geom->type == Geometry::MESH) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
|
||||
requested_features.use_patch_evaluation = true;
|
||||
}
|
||||
#endif
|
||||
requested_features.use_true_displacement |= mesh->has_true_displacement();
|
||||
}
|
||||
else if (geom->type == Geometry::HAIR) {
|
||||
requested_features.use_hair = true;
|
||||
}
|
||||
}
|
||||
|
||||
requested_features.use_background_light = light_manager->has_background_light(this);
|
||||
|
||||
requested_features.use_baking = bake_manager->get_baking();
|
||||
requested_features.use_integrator_branched = (integrator->method == Integrator::BRANCHED_PATH);
|
||||
if (film->denoising_data_pass) {
|
||||
requested_features.use_denoising = true;
|
||||
requested_features.use_shadow_tricks = true;
|
||||
}
|
||||
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
bool Scene::update(Progress &progress, bool &kernel_switch_needed)
|
||||
{
|
||||
/* update scene */
|
||||
if (need_update()) {
|
||||
/* Updated used shader tag so we know which features are need for the kernel. */
|
||||
shader_manager->update_shaders_used(this);
|
||||
|
||||
/* Update max_closures. */
|
||||
KernelIntegrator *kintegrator = &dscene.data.integrator;
|
||||
if (params.background) {
|
||||
kintegrator->max_closures = get_max_closure_count();
|
||||
}
|
||||
else {
|
||||
/* Currently viewport render is faster with higher max_closures, needs investigating. */
|
||||
kintegrator->max_closures = MAX_CLOSURE;
|
||||
}
|
||||
|
||||
/* Load render kernels, before device update where we upload data to the GPU. */
|
||||
bool new_kernels_needed = load_kernels(progress, false);
|
||||
|
||||
progress.set_status("Updating Scene");
|
||||
MEM_GUARDED_CALL(&progress, device_update, device, progress);
|
||||
|
||||
DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
|
||||
kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
|
||||
kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
|
||||
if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
|
||||
progress.set_kernel_status("Compiling render kernels");
|
||||
}
|
||||
if (new_kernels_needed || kernel_switch_needed) {
|
||||
progress.set_kernel_status("Compiling render kernels");
|
||||
device->wait_for_availability(loaded_kernel_features);
|
||||
progress.set_kernel_status("");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Scene::load_kernels(Progress &progress, bool lock_scene)
|
||||
{
|
||||
thread_scoped_lock scene_lock;
|
||||
if (lock_scene) {
|
||||
scene_lock = thread_scoped_lock(mutex);
|
||||
}
|
||||
|
||||
DeviceRequestedFeatures requested_features = get_requested_device_features();
|
||||
|
||||
if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) {
|
||||
progress.set_status("Loading render kernels (may take a few minutes the first time)");
|
||||
|
||||
scoped_timer timer;
|
||||
|
||||
VLOG(2) << "Requested features:\n" << requested_features;
|
||||
if (!device->load_kernels(requested_features)) {
|
||||
string message = device->error_message();
|
||||
if (message.empty())
|
||||
message = "Failed loading render kernel, see console for errors";
|
||||
|
||||
progress.set_error(message);
|
||||
progress.set_status(message);
|
||||
progress.set_update();
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.add_skip_time(timer, false);
|
||||
VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
|
||||
|
||||
kernels_loaded = true;
|
||||
loaded_kernel_features = requested_features;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Scene::get_max_closure_count()
|
||||
{
|
||||
if (shader_manager->use_osl()) {
|
||||
/* OSL always needs the maximum as we can't predict the
|
||||
* number of closures a shader might generate. */
|
||||
return MAX_CLOSURE;
|
||||
}
|
||||
|
||||
int max_closures = 0;
|
||||
for (int i = 0; i < shaders.size(); i++) {
|
||||
Shader *shader = shaders[i];
|
||||
if (shader->used) {
|
||||
int num_closures = shader->graph->get_num_closures();
|
||||
max_closures = max(max_closures, num_closures);
|
||||
}
|
||||
}
|
||||
max_closure_global = max(max_closure_global, max_closures);
|
||||
|
||||
if (max_closure_global > MAX_CLOSURE) {
|
||||
/* This is usually harmless as more complex shader tend to get many
|
||||
* closures discarded due to mixing or low weights. We need to limit
|
||||
* to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it
|
||||
* avoids excessive memory usage for split kernels. */
|
||||
VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > "
|
||||
<< MAX_CLOSURE;
|
||||
|
||||
max_closure_global = MAX_CLOSURE;
|
||||
}
|
||||
|
||||
return max_closure_global;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "render/image.h"
|
||||
#include "render/shader.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "device/device_memory.h"
|
||||
|
||||
#include "util/util_param.h"
|
||||
@@ -276,6 +277,8 @@ class Scene {
|
||||
|
||||
void collect_statistics(RenderStats *stats);
|
||||
|
||||
bool update(Progress &progress, bool &kernel_switch_needed);
|
||||
|
||||
protected:
|
||||
/* Check if some heavy data worth logging was updated.
|
||||
* Mainly used to suppress extra annoying logging.
|
||||
@@ -283,6 +286,21 @@ class Scene {
|
||||
bool need_data_update();
|
||||
|
||||
void free_memory(bool final);
|
||||
|
||||
bool kernels_loaded;
|
||||
DeviceRequestedFeatures loaded_kernel_features;
|
||||
|
||||
bool load_kernels(Progress &progress, bool lock_scene = true);
|
||||
|
||||
/* ** Split kernel routines ** */
|
||||
|
||||
DeviceRequestedFeatures get_requested_device_features();
|
||||
|
||||
/* Maximumnumber of closure during session lifetime. */
|
||||
int max_closure_global;
|
||||
|
||||
/* Get maximum number of closures to be used in kernel. */
|
||||
int get_max_closure_count();
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -90,10 +90,6 @@ Session::Session(const SessionParams ¶ms_)
|
||||
gpu_draw_ready = false;
|
||||
gpu_need_display_buffer_update = false;
|
||||
pause = false;
|
||||
kernels_loaded = false;
|
||||
|
||||
/* TODO(sergey): Check if it's indeed optimal value for the split kernel. */
|
||||
max_closure_global = 1;
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
@@ -767,95 +763,6 @@ void Session::run_cpu()
|
||||
update_progressive_refine(true);
|
||||
}
|
||||
|
||||
DeviceRequestedFeatures Session::get_requested_device_features()
|
||||
{
|
||||
/* TODO(sergey): Consider moving this to the Scene level. */
|
||||
DeviceRequestedFeatures requested_features;
|
||||
requested_features.experimental = params.experimental;
|
||||
|
||||
scene->shader_manager->get_requested_features(scene, &requested_features);
|
||||
|
||||
/* This features are not being tweaked as often as shaders,
|
||||
* so could be done selective magic for the viewport as well.
|
||||
*/
|
||||
bool use_motion = scene->need_motion() == Scene::MotionType::MOTION_BLUR;
|
||||
requested_features.use_hair = false;
|
||||
requested_features.use_hair_thick = (scene->params.hair_shape == CURVE_THICK);
|
||||
requested_features.use_object_motion = false;
|
||||
requested_features.use_camera_motion = use_motion && scene->camera->use_motion();
|
||||
foreach (Object *object, scene->objects) {
|
||||
Geometry *geom = object->geometry;
|
||||
if (use_motion) {
|
||||
requested_features.use_object_motion |= object->use_motion() | geom->use_motion_blur;
|
||||
requested_features.use_camera_motion |= geom->use_motion_blur;
|
||||
}
|
||||
if (object->is_shadow_catcher) {
|
||||
requested_features.use_shadow_tricks = true;
|
||||
}
|
||||
if (geom->type == Geometry::MESH) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
|
||||
requested_features.use_patch_evaluation = true;
|
||||
}
|
||||
#endif
|
||||
requested_features.use_true_displacement |= mesh->has_true_displacement();
|
||||
}
|
||||
else if (geom->type == Geometry::HAIR) {
|
||||
requested_features.use_hair = true;
|
||||
}
|
||||
}
|
||||
|
||||
requested_features.use_background_light = scene->light_manager->has_background_light(scene);
|
||||
|
||||
BakeManager *bake_manager = scene->bake_manager;
|
||||
requested_features.use_baking = bake_manager->get_baking();
|
||||
requested_features.use_integrator_branched = (scene->integrator->method ==
|
||||
Integrator::BRANCHED_PATH);
|
||||
if (params.denoising.use || params.denoising.store_passes) {
|
||||
requested_features.use_denoising = true;
|
||||
requested_features.use_shadow_tricks = true;
|
||||
}
|
||||
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
bool Session::load_kernels(bool lock_scene)
|
||||
{
|
||||
thread_scoped_lock scene_lock;
|
||||
if (lock_scene) {
|
||||
scene_lock = thread_scoped_lock(scene->mutex);
|
||||
}
|
||||
|
||||
DeviceRequestedFeatures requested_features = get_requested_device_features();
|
||||
|
||||
if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) {
|
||||
progress.set_status("Loading render kernels (may take a few minutes the first time)");
|
||||
|
||||
scoped_timer timer;
|
||||
|
||||
VLOG(2) << "Requested features:\n" << requested_features;
|
||||
if (!device->load_kernels(requested_features)) {
|
||||
string message = device->error_message();
|
||||
if (message.empty())
|
||||
message = "Failed loading render kernel, see console for errors";
|
||||
|
||||
progress.set_error(message);
|
||||
progress.set_status(message);
|
||||
progress.set_update();
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.add_skip_time(timer, false);
|
||||
VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
|
||||
|
||||
kernels_loaded = true;
|
||||
loaded_kernel_features = requested_features;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::run()
|
||||
{
|
||||
if (params.use_profiling && (params.device.type == DEVICE_CPU)) {
|
||||
@@ -1032,39 +939,8 @@ bool Session::update_scene()
|
||||
}
|
||||
}
|
||||
|
||||
/* update scene */
|
||||
if (scene->need_update()) {
|
||||
/* Updated used shader tag so we know which features are need for the kernel. */
|
||||
scene->shader_manager->update_shaders_used(scene);
|
||||
|
||||
/* Update max_closures. */
|
||||
KernelIntegrator *kintegrator = &scene->dscene.data.integrator;
|
||||
if (params.background) {
|
||||
kintegrator->max_closures = get_max_closure_count();
|
||||
}
|
||||
else {
|
||||
/* Currently viewport render is faster with higher max_closures, needs investigating. */
|
||||
kintegrator->max_closures = MAX_CLOSURE;
|
||||
}
|
||||
|
||||
/* Load render kernels, before device update where we upload data to the GPU. */
|
||||
bool new_kernels_needed = load_kernels(false);
|
||||
|
||||
progress.set_status("Updating Scene");
|
||||
MEM_GUARDED_CALL(&progress, scene->device_update, device, progress);
|
||||
|
||||
DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
|
||||
bool kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
|
||||
kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
|
||||
if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
|
||||
progress.set_kernel_status("Compiling render kernels");
|
||||
}
|
||||
if (new_kernels_needed || kernel_switch_needed) {
|
||||
progress.set_kernel_status("Compiling render kernels");
|
||||
device->wait_for_availability(loaded_kernel_features);
|
||||
progress.set_kernel_status("");
|
||||
}
|
||||
|
||||
bool kernel_switch_needed = false;
|
||||
if (scene->update(progress, kernel_switch_needed)) {
|
||||
if (kernel_switch_needed) {
|
||||
reset(tile_manager.params, params.samples);
|
||||
}
|
||||
@@ -1334,36 +1210,4 @@ void Session::collect_statistics(RenderStats *render_stats)
|
||||
}
|
||||
}
|
||||
|
||||
int Session::get_max_closure_count()
|
||||
{
|
||||
if (scene->shader_manager->use_osl()) {
|
||||
/* OSL always needs the maximum as we can't predict the
|
||||
* number of closures a shader might generate. */
|
||||
return MAX_CLOSURE;
|
||||
}
|
||||
|
||||
int max_closures = 0;
|
||||
for (int i = 0; i < scene->shaders.size(); i++) {
|
||||
Shader *shader = scene->shaders[i];
|
||||
if (shader->used) {
|
||||
int num_closures = shader->graph->get_num_closures();
|
||||
max_closures = max(max_closures, num_closures);
|
||||
}
|
||||
}
|
||||
max_closure_global = max(max_closure_global, max_closures);
|
||||
|
||||
if (max_closure_global > MAX_CLOSURE) {
|
||||
/* This is usually harmless as more complex shader tend to get many
|
||||
* closures discarded due to mixing or low weights. We need to limit
|
||||
* to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it
|
||||
* avoids excessive memory usage for split kernels. */
|
||||
VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > "
|
||||
<< MAX_CLOSURE;
|
||||
|
||||
max_closure_global = MAX_CLOSURE;
|
||||
}
|
||||
|
||||
return max_closure_global;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -157,7 +157,6 @@ class Session {
|
||||
void set_denoising_start_sample(int sample);
|
||||
|
||||
bool update_scene();
|
||||
bool load_kernels(bool lock_scene = true);
|
||||
|
||||
void device_free();
|
||||
|
||||
@@ -219,25 +218,12 @@ class Session {
|
||||
thread_mutex display_mutex;
|
||||
thread_condition_variable denoising_cond;
|
||||
|
||||
bool kernels_loaded;
|
||||
DeviceRequestedFeatures loaded_kernel_features;
|
||||
|
||||
double reset_time;
|
||||
double last_update_time;
|
||||
double last_display_time;
|
||||
|
||||
/* progressive refine */
|
||||
bool update_progressive_refine(bool cancel);
|
||||
|
||||
DeviceRequestedFeatures get_requested_device_features();
|
||||
|
||||
/* ** Split kernel routines ** */
|
||||
|
||||
/* Maximumnumber of closure during session lifetime. */
|
||||
int max_closure_global;
|
||||
|
||||
/* Get maximum number of closures to be used in kernel. */
|
||||
int get_max_closure_count();
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
Reference in New Issue
Block a user