forked from bartvdbraak/blender
1da0685076
The Pass struct is now a Node and the passes are moved from the Film class to the Scene class. The Pass Node only has `type` and `name` as sockets as those seem to be the only properties settable by exporters (other properties are implicit and depend on the pass type). This is part of T79131. Reviewed By: brecht Differential Revision: https://developer.blender.org/D8591
564 lines
18 KiB
C++
564 lines
18 KiB
C++
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "device/device.h"
|
|
#include "render/background.h"
|
|
#include "render/bake.h"
|
|
#include "render/camera.h"
|
|
#include "render/curves.h"
|
|
#include "render/film.h"
|
|
#include "render/integrator.h"
|
|
#include "render/light.h"
|
|
#include "render/mesh.h"
|
|
#include "render/object.h"
|
|
#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"
|
|
|
|
#include "util/util_foreach.h"
|
|
#include "util/util_guarded_allocator.h"
|
|
#include "util/util_logging.h"
|
|
#include "util/util_progress.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
DeviceScene::DeviceScene(Device *device)
|
|
: bvh_nodes(device, "__bvh_nodes", MEM_GLOBAL),
|
|
bvh_leaf_nodes(device, "__bvh_leaf_nodes", MEM_GLOBAL),
|
|
object_node(device, "__object_node", MEM_GLOBAL),
|
|
prim_tri_index(device, "__prim_tri_index", MEM_GLOBAL),
|
|
prim_tri_verts(device, "__prim_tri_verts", MEM_GLOBAL),
|
|
prim_type(device, "__prim_type", MEM_GLOBAL),
|
|
prim_visibility(device, "__prim_visibility", MEM_GLOBAL),
|
|
prim_index(device, "__prim_index", MEM_GLOBAL),
|
|
prim_object(device, "__prim_object", MEM_GLOBAL),
|
|
prim_time(device, "__prim_time", MEM_GLOBAL),
|
|
tri_shader(device, "__tri_shader", MEM_GLOBAL),
|
|
tri_vnormal(device, "__tri_vnormal", MEM_GLOBAL),
|
|
tri_vindex(device, "__tri_vindex", MEM_GLOBAL),
|
|
tri_patch(device, "__tri_patch", MEM_GLOBAL),
|
|
tri_patch_uv(device, "__tri_patch_uv", MEM_GLOBAL),
|
|
curves(device, "__curves", MEM_GLOBAL),
|
|
curve_keys(device, "__curve_keys", MEM_GLOBAL),
|
|
patches(device, "__patches", MEM_GLOBAL),
|
|
objects(device, "__objects", MEM_GLOBAL),
|
|
object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL),
|
|
object_motion(device, "__object_motion", MEM_GLOBAL),
|
|
object_flag(device, "__object_flag", MEM_GLOBAL),
|
|
object_volume_step(device, "__object_volume_step", MEM_GLOBAL),
|
|
camera_motion(device, "__camera_motion", MEM_GLOBAL),
|
|
attributes_map(device, "__attributes_map", MEM_GLOBAL),
|
|
attributes_float(device, "__attributes_float", MEM_GLOBAL),
|
|
attributes_float2(device, "__attributes_float2", MEM_GLOBAL),
|
|
attributes_float3(device, "__attributes_float3", MEM_GLOBAL),
|
|
attributes_uchar4(device, "__attributes_uchar4", MEM_GLOBAL),
|
|
light_distribution(device, "__light_distribution", MEM_GLOBAL),
|
|
lights(device, "__lights", MEM_GLOBAL),
|
|
light_background_marginal_cdf(device, "__light_background_marginal_cdf", MEM_GLOBAL),
|
|
light_background_conditional_cdf(device, "__light_background_conditional_cdf", MEM_GLOBAL),
|
|
particles(device, "__particles", MEM_GLOBAL),
|
|
svm_nodes(device, "__svm_nodes", MEM_GLOBAL),
|
|
shaders(device, "__shaders", MEM_GLOBAL),
|
|
lookup_table(device, "__lookup_table", MEM_GLOBAL),
|
|
sample_pattern_lut(device, "__sample_pattern_lut", MEM_GLOBAL),
|
|
ies_lights(device, "__ies", MEM_GLOBAL)
|
|
{
|
|
memset((void *)&data, 0, sizeof(data));
|
|
}
|
|
|
|
Scene::Scene(const SceneParams ¶ms_, Device *device)
|
|
: name("Scene"),
|
|
default_surface(NULL),
|
|
default_volume(NULL),
|
|
default_light(NULL),
|
|
default_background(NULL),
|
|
default_empty(NULL),
|
|
device(device),
|
|
dscene(device),
|
|
params(params_)
|
|
{
|
|
memset((void *)&dscene.data, 0, sizeof(dscene.data));
|
|
|
|
camera = new Camera();
|
|
dicing_camera = new Camera();
|
|
lookup_tables = new LookupTables();
|
|
film = new Film();
|
|
background = new Background();
|
|
light_manager = new LightManager();
|
|
geometry_manager = new GeometryManager();
|
|
object_manager = new ObjectManager();
|
|
integrator = new Integrator();
|
|
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;
|
|
|
|
film->add_default(this);
|
|
|
|
/* OSL only works on the CPU */
|
|
if (device->info.has_osl)
|
|
shader_manager = ShaderManager::create(params.shadingsystem);
|
|
else
|
|
shader_manager = ShaderManager::create(SHADINGSYSTEM_SVM);
|
|
|
|
shader_manager->add_default(this);
|
|
}
|
|
|
|
Scene::~Scene()
|
|
{
|
|
free_memory(true);
|
|
}
|
|
|
|
void Scene::free_memory(bool final)
|
|
{
|
|
foreach (Shader *s, shaders)
|
|
delete s;
|
|
foreach (Geometry *g, geometry)
|
|
delete g;
|
|
foreach (Object *o, objects)
|
|
delete o;
|
|
foreach (Light *l, lights)
|
|
delete l;
|
|
foreach (ParticleSystem *p, particle_systems)
|
|
delete p;
|
|
|
|
shaders.clear();
|
|
geometry.clear();
|
|
objects.clear();
|
|
lights.clear();
|
|
particle_systems.clear();
|
|
|
|
if (device) {
|
|
camera->device_free(device, &dscene, this);
|
|
film->device_free(device, &dscene, this);
|
|
background->device_free(device, &dscene);
|
|
integrator->device_free(device, &dscene);
|
|
|
|
object_manager->device_free(device, &dscene);
|
|
geometry_manager->device_free(device, &dscene);
|
|
shader_manager->device_free(device, &dscene, this);
|
|
light_manager->device_free(device, &dscene);
|
|
|
|
particle_system_manager->device_free(device, &dscene);
|
|
|
|
bake_manager->device_free(device, &dscene);
|
|
|
|
if (!params.persistent_data || final)
|
|
image_manager->device_free(device);
|
|
else
|
|
image_manager->device_free_builtin(device);
|
|
|
|
lookup_tables->device_free(device, &dscene);
|
|
}
|
|
|
|
if (final) {
|
|
delete lookup_tables;
|
|
delete camera;
|
|
delete dicing_camera;
|
|
delete film;
|
|
delete background;
|
|
delete integrator;
|
|
delete object_manager;
|
|
delete geometry_manager;
|
|
delete shader_manager;
|
|
delete light_manager;
|
|
delete particle_system_manager;
|
|
delete image_manager;
|
|
delete bake_manager;
|
|
}
|
|
}
|
|
|
|
void Scene::device_update(Device *device_, Progress &progress)
|
|
{
|
|
if (!device)
|
|
device = device_;
|
|
|
|
bool print_stats = need_data_update();
|
|
|
|
/* The order of updates is important, because there's dependencies between
|
|
* the different managers, using data computed by previous managers.
|
|
*
|
|
* - Image manager uploads images used by shaders.
|
|
* - Camera may be used for adaptive subdivision.
|
|
* - Displacement shader must have all shader data available.
|
|
* - Light manager needs lookup tables and final mesh data to compute emission CDF.
|
|
* - Film needs light manager to run for use_light_visibility
|
|
* - Lookup tables are done a second time to handle film tables
|
|
*/
|
|
|
|
progress.set_status("Updating Shaders");
|
|
shader_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Background");
|
|
background->device_update(device, &dscene, this);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Camera");
|
|
camera->device_update(device, &dscene, this);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
geometry_manager->device_update_preprocess(device, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Objects");
|
|
object_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Particle Systems");
|
|
particle_system_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Meshes");
|
|
geometry_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Objects Flags");
|
|
object_manager->device_update_flags(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Images");
|
|
image_manager->device_update(device, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Camera Volume");
|
|
camera->device_update_volume(device, &dscene, this);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Lookup Tables");
|
|
lookup_tables->device_update(device, &dscene);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Lights");
|
|
light_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Integrator");
|
|
integrator->device_update(device, &dscene, this);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Film");
|
|
film->device_update(device, &dscene, this);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Lookup Tables");
|
|
lookup_tables->device_update(device, &dscene);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
progress.set_status("Updating Baking");
|
|
bake_manager->device_update(device, &dscene, this, progress);
|
|
|
|
if (progress.get_cancel() || device->have_error())
|
|
return;
|
|
|
|
if (device->have_error() == false) {
|
|
progress.set_status("Updating Device", "Writing constant memory");
|
|
device->const_copy_to("__data", &dscene.data, sizeof(dscene.data));
|
|
}
|
|
|
|
if (print_stats) {
|
|
size_t mem_used = util_guarded_get_mem_used();
|
|
size_t mem_peak = util_guarded_get_mem_peak();
|
|
|
|
VLOG(1) << "System memory statistics after full device sync:\n"
|
|
<< " Usage: " << string_human_readable_number(mem_used) << " ("
|
|
<< string_human_readable_size(mem_used) << ")\n"
|
|
<< " Peak: " << string_human_readable_number(mem_peak) << " ("
|
|
<< string_human_readable_size(mem_peak) << ")";
|
|
}
|
|
}
|
|
|
|
Scene::MotionType Scene::need_motion()
|
|
{
|
|
if (integrator->motion_blur)
|
|
return MOTION_BLUR;
|
|
else if (Pass::contains(passes, PASS_MOTION))
|
|
return MOTION_PASS;
|
|
else
|
|
return MOTION_NONE;
|
|
}
|
|
|
|
float Scene::motion_shutter_time()
|
|
{
|
|
if (need_motion() == Scene::MOTION_PASS)
|
|
return 2.0f;
|
|
else
|
|
return camera->shuttertime;
|
|
}
|
|
|
|
bool Scene::need_global_attribute(AttributeStandard std)
|
|
{
|
|
if (std == ATTR_STD_UV)
|
|
return Pass::contains(passes, PASS_UV);
|
|
else if (std == ATTR_STD_MOTION_VERTEX_POSITION)
|
|
return need_motion() != MOTION_NONE;
|
|
else if (std == ATTR_STD_MOTION_VERTEX_NORMAL)
|
|
return need_motion() == MOTION_BLUR;
|
|
|
|
return false;
|
|
}
|
|
|
|
void Scene::need_global_attributes(AttributeRequestSet &attributes)
|
|
{
|
|
for (int std = ATTR_STD_NONE; std < ATTR_STD_NUM; std++)
|
|
if (need_global_attribute((AttributeStandard)std))
|
|
attributes.add((AttributeStandard)std);
|
|
}
|
|
|
|
bool Scene::need_update()
|
|
{
|
|
return (need_reset() || film->need_update);
|
|
}
|
|
|
|
bool Scene::need_data_update()
|
|
{
|
|
return (background->need_update || image_manager->need_update || object_manager->need_update ||
|
|
geometry_manager->need_update || light_manager->need_update ||
|
|
lookup_tables->need_update || integrator->need_update || shader_manager->need_update ||
|
|
particle_system_manager->need_update || bake_manager->need_update || film->need_update);
|
|
}
|
|
|
|
bool Scene::need_reset()
|
|
{
|
|
return need_data_update() || camera->need_update;
|
|
}
|
|
|
|
void Scene::reset()
|
|
{
|
|
shader_manager->reset(this);
|
|
shader_manager->add_default(this);
|
|
|
|
/* ensure all objects are updated */
|
|
camera->tag_update();
|
|
dicing_camera->tag_update();
|
|
film->tag_update(this);
|
|
background->tag_update(this);
|
|
integrator->tag_update(this);
|
|
object_manager->tag_update(this);
|
|
geometry_manager->tag_update(this);
|
|
light_manager->tag_update(this);
|
|
particle_system_manager->tag_update(this);
|
|
}
|
|
|
|
void Scene::device_free()
|
|
{
|
|
free_memory(false);
|
|
}
|
|
|
|
void Scene::collect_statistics(RenderStats *stats)
|
|
{
|
|
geometry_manager->collect_statistics(this, 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
|