Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/render')
-rw-r--r--intern/cycles/render/alembic.cpp154
-rw-r--r--intern/cycles/render/alembic.h39
-rw-r--r--intern/cycles/render/alembic_read.cpp16
-rw-r--r--intern/cycles/render/geometry.cpp20
-rw-r--r--intern/cycles/render/graph.cpp4
-rw-r--r--intern/cycles/render/nodes.cpp20
-rw-r--r--intern/cycles/render/object.cpp3
-rw-r--r--intern/cycles/render/object.h2
-rw-r--r--intern/cycles/render/session.cpp287
-rw-r--r--intern/cycles/render/session.h45
-rw-r--r--intern/cycles/render/shader.h8
11 files changed, 397 insertions, 201 deletions
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
index 6713531c9b0..69bc0712674 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -25,6 +25,7 @@
#include "render/shader.h"
#include "util/util_foreach.h"
+#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_transform.h"
#include "util/util_vector.h"
@@ -211,6 +212,35 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
}
}
+size_t CachedData::memory_used() const
+{
+ size_t mem_used = 0;
+
+ mem_used += curve_first_key.memory_used();
+ mem_used += curve_keys.memory_used();
+ mem_used += curve_radius.memory_used();
+ mem_used += curve_shader.memory_used();
+ mem_used += num_ngons.memory_used();
+ mem_used += shader.memory_used();
+ mem_used += subd_creases_edge.memory_used();
+ mem_used += subd_creases_weight.memory_used();
+ mem_used += subd_face_corners.memory_used();
+ mem_used += subd_num_corners.memory_used();
+ mem_used += subd_ptex_offset.memory_used();
+ mem_used += subd_smooth.memory_used();
+ mem_used += subd_start_corner.memory_used();
+ mem_used += transforms.memory_used();
+ mem_used += triangles.memory_used();
+ mem_used += uv_loops.memory_used();
+ mem_used += vertices.memory_used();
+
+ for (const CachedAttribute &attr : attributes) {
+ mem_used += attr.data.memory_used();
+ }
+
+ return mem_used;
+}
+
static M44d convert_yup_zup(const M44d &mtx, float scale_mult)
{
V3d scale, shear, rotation, translation;
@@ -385,6 +415,8 @@ NODE_DEFINE(AlembicObject)
SOCKET_STRING(path, "Alembic Path", ustring());
SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", Shader::get_node_type());
+ SOCKET_BOOLEAN(ignore_subdivision, "Ignore Subdivision", true);
+
SOCKET_INT(subd_max_level, "Max Subdivision Level", 1);
SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f);
@@ -470,6 +502,33 @@ void AlembicObject::load_data_in_cache(CachedData &cached_data,
cached_data.clear();
+ if (this->get_ignore_subdivision()) {
+ PolyMeshSchemaData data;
+ data.topology_variance = schema.getTopologyVariance();
+ data.time_sampling = schema.getTimeSampling();
+ data.positions = schema.getPositionsProperty();
+ data.face_counts = schema.getFaceCountsProperty();
+ data.face_indices = schema.getFaceIndicesProperty();
+ data.num_samples = schema.getNumSamples();
+ data.velocities = schema.getVelocitiesProperty();
+ data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
+
+ read_geometry_data(proc, cached_data, data, progress);
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ /* Use the schema as the base compound property to also be able to look for top level
+ * properties. */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
+
+ cached_data.invalidate_last_loaded_time(true);
+ data_loaded = true;
+ return;
+ }
+
SubDSchemaData data;
data.time_sampling = schema.getTimeSampling();
data.num_samples = schema.getNumSamples();
@@ -677,6 +736,9 @@ NODE_DEFINE(AlembicProcedural)
SOCKET_NODE_ARRAY(objects, "Objects", AlembicObject::get_node_type());
+ SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true);
+ SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096);
+
return type;
}
@@ -781,6 +843,43 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate);
+ /* Clear the subdivision caches as the data is stored differently. */
+ for (Node *node : objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+
+ if (object->schema_type != AlembicObject::SUBD) {
+ continue;
+ }
+
+ if (object->ignore_subdivision_is_modified()) {
+ object->clear_cache();
+ }
+ }
+
+ if (use_prefetch_is_modified()) {
+ if (!use_prefetch) {
+ for (Node *node : objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+ object->clear_cache();
+ }
+ }
+ }
+
+ if (prefetch_cache_size_is_modified()) {
+ /* Check whether the current memory usage fits in the new requested size,
+ * abort the render if it is any higher. */
+ size_t memory_used = 0ul;
+ for (Node *node : objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+ memory_used += object->get_cached_data().memory_used();
+ }
+
+ if (memory_used > get_prefetch_cache_size_in_bytes()) {
+ progress.set_error("Error: Alembic Procedural memory limit reached");
+ return;
+ }
+ }
+
build_caches(progress);
foreach (Node *node, objects) {
@@ -959,14 +1058,6 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
update_attributes(mesh->attributes, cached_data, frame_time);
- /* we don't yet support arbitrary attributes, for now add vertex
- * coordinates as generated coordinates if requested */
- if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) {
- Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
- memcpy(
- attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
- }
-
if (mesh->is_modified()) {
bool need_rebuild = mesh->triangles_is_modified();
mesh->tag_update(scene_, need_rebuild);
@@ -975,13 +1066,13 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time)
{
- CachedData &cached_data = abc_object->get_cached_data();
-
- if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
- /* need to reset the current data is something changed */
- cached_data.invalidate_last_loaded_time();
+ if (abc_object->get_ignore_subdivision()) {
+ read_mesh(abc_object, frame_time);
+ return;
}
+ CachedData &cached_data = abc_object->get_cached_data();
+
/* Update sockets. */
Object *object = abc_object->get_object();
@@ -996,6 +1087,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
return;
}
+ if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
+ /* need to reset the current data is something changed */
+ cached_data.invalidate_last_loaded_time();
+ }
+
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
/* Make sure shader ids are also updated. */
@@ -1053,14 +1149,6 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
update_attributes(mesh->subd_attributes, cached_data, frame_time);
- /* we don't yet support arbitrary attributes, for now add vertex
- * coordinates as generated coordinates if requested */
- if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) {
- Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
- memcpy(
- attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
- }
-
if (mesh->is_modified()) {
bool need_rebuild = (mesh->triangles_is_modified()) ||
(mesh->subd_num_corners_is_modified()) ||
@@ -1110,17 +1198,6 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra
update_attributes(hair->attributes, cached_data, frame_time);
- /* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if
- * requested */
- if (hair->need_attribute(scene_, ATTR_STD_GENERATED)) {
- Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
- float3 *generated = attr_generated->data_float3();
-
- for (size_t i = 0; i < hair->num_curves(); i++) {
- generated[i] = hair->get_curve_keys()[hair->get_curve(i).first_key];
- }
- }
-
const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
hair->tag_update(scene_, rebuild);
}
@@ -1280,6 +1357,8 @@ void AlembicProcedural::walk_hierarchy(
void AlembicProcedural::build_caches(Progress &progress)
{
+ size_t memory_used = 0;
+
for (Node *node : objects) {
AlembicObject *object = static_cast<AlembicObject *>(node);
@@ -1333,7 +1412,18 @@ void AlembicProcedural::build_caches(Progress &progress)
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
object->setup_transform_cache(object->get_cached_data(), scale);
}
+
+ memory_used += object->get_cached_data().memory_used();
+
+ if (use_prefetch) {
+ if (memory_used > get_prefetch_cache_size_in_bytes()) {
+ progress.set_error("Error: Alembic Procedural memory limit reached");
+ return;
+ }
+ }
}
+
+ VLOG(1) << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
index 61c0e40fe4a..8e166a5ab04 100644
--- a/intern/cycles/render/alembic.h
+++ b/intern/cycles/render/alembic.h
@@ -272,6 +272,21 @@ template<typename T> class DataStore {
node->set(*socket, value);
}
+ size_t memory_used() const
+ {
+ if constexpr (is_array<T>::value) {
+ size_t mem_used = 0;
+
+ for (const T &array : data) {
+ mem_used += array.size() * sizeof(array[0]);
+ }
+
+ return mem_used;
+ }
+
+ return data.size() * sizeof(T);
+ }
+
private:
const TimeIndexPair &get_index_for_time(double time) const
{
@@ -332,6 +347,8 @@ struct CachedData {
void invalidate_last_loaded_time(bool attributes_only = false);
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling);
+
+ size_t memory_used() const;
};
/* Representation of an Alembic object for the AlembicProcedural.
@@ -353,6 +370,10 @@ class AlembicObject : public Node {
/* Shaders used for rendering. */
NODE_SOCKET_API_ARRAY(array<Node *>, used_shaders)
+ /* Treat this subdivision object as a regular polygon mesh, so no subdivision will be performed.
+ */
+ NODE_SOCKET_API(bool, ignore_subdivision)
+
/* Maximum number of subdivisions for ISubD objects. */
NODE_SOCKET_API(int, subd_max_level)
@@ -416,6 +437,11 @@ class AlembicObject : public Node {
return cached_data_.is_constant();
}
+ void clear_cache()
+ {
+ cached_data_.clear();
+ }
+
Object *object = nullptr;
bool data_loaded = false;
@@ -473,6 +499,13 @@ class AlembicProcedural : public Procedural {
* software. */
NODE_SOCKET_API(float, scale)
+ /* Cache controls */
+ NODE_SOCKET_API(bool, use_prefetch)
+
+ /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted.
+ */
+ NODE_SOCKET_API(int, prefetch_cache_size)
+
AlembicProcedural();
~AlembicProcedural();
@@ -522,6 +555,12 @@ class AlembicProcedural : public Procedural {
void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
void build_caches(Progress &progress);
+
+ size_t get_prefetch_cache_size_in_bytes() const
+ {
+ /* prefetch_cache_size is in megabytes, so convert to bytes. */
+ return static_cast<size_t>(prefetch_cache_size) * 1024 * 1024;
+ }
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
index c53ec668938..b105af63b44 100644
--- a/intern/cycles/render/alembic_read.cpp
+++ b/intern/cycles/render/alembic_read.cpp
@@ -44,9 +44,19 @@ static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
return result;
}
- // load the data for the entire animation
- const double start_frame = static_cast<double>(proc->get_start_frame());
- const double end_frame = static_cast<double>(proc->get_end_frame());
+ double start_frame;
+ double end_frame;
+
+ if (proc->get_use_prefetch()) {
+ // load the data for the entire animation
+ start_frame = static_cast<double>(proc->get_start_frame());
+ end_frame = static_cast<double>(proc->get_end_frame());
+ }
+ else {
+ // load the data for the current frame
+ start_frame = static_cast<double>(proc->get_frame());
+ end_frame = start_frame;
+ }
const double frame_rate = static_cast<double>(proc->get_frame_rate());
const double start_time = start_frame / frame_rate;
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 30b19104ccf..b01b1fe5a77 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -788,6 +788,11 @@ void GeometryManager::device_update_attributes(Device *device,
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = geom->attributes.find(req);
+ /* Vertex normals are stored in DeviceScene.tri_vnormal. */
+ if (attr && attr->std == ATTR_STD_VERTEX_NORMAL) {
+ continue;
+ }
+
update_attribute_element_size(geom,
attr,
ATTR_PRIM_GEOMETRY,
@@ -800,6 +805,11 @@ void GeometryManager::device_update_attributes(Device *device,
Mesh *mesh = static_cast<Mesh *>(geom);
Attribute *subd_attr = mesh->subd_attributes.find(req);
+ /* Vertex normals are stored in DeviceScene.tri_vnormal. */
+ if (subd_attr && subd_attr->std == ATTR_STD_VERTEX_NORMAL) {
+ continue;
+ }
+
update_attribute_element_size(mesh,
subd_attr,
ATTR_PRIM_SUBD,
@@ -854,6 +864,11 @@ void GeometryManager::device_update_attributes(Device *device,
Attribute *attr = geom->attributes.find(req);
if (attr) {
+ /* Vertex normals are stored in DeviceScene.tri_vnormal. */
+ if (attr->std == ATTR_STD_VERTEX_NORMAL) {
+ continue;
+ }
+
/* force a copy if we need to reallocate all the data */
attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
}
@@ -877,6 +892,11 @@ void GeometryManager::device_update_attributes(Device *device,
Attribute *subd_attr = mesh->subd_attributes.find(req);
if (subd_attr) {
+ /* Vertex normals are stored in DeviceScene.tri_vnormal. */
+ if (subd_attr->std == ATTR_STD_VERTEX_NORMAL) {
+ continue;
+ }
+
/* force a copy if we need to reallocate all the data */
subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)];
}
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index a290eb4de2b..9c356656f81 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -158,13 +158,13 @@ void ShaderNode::attributes(Shader *shader, AttributeRequestSet *attributes)
foreach (ShaderInput *input, inputs) {
if (!input->link) {
if (input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
- if (shader->has_surface)
+ if (shader->has_surface_link())
attributes->add(ATTR_STD_GENERATED);
if (shader->has_volume)
attributes->add(ATTR_STD_GENERATED_TRANSFORM);
}
else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
- if (shader->has_surface)
+ if (shader->has_surface_link())
attributes->add(ATTR_STD_UV);
}
}
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 2477a96bc44..7337e19a929 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -353,7 +353,7 @@ void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attribute
#ifdef WITH_PTEX
/* todo: avoid loading other texture coordinates when using ptex,
* and hide texture coordinate socket in the UI */
- if (shader->has_surface && string_endswith(filename, ".ptx")) {
+ if (shader->has_surface_link() && string_endswith(filename, ".ptx")) {
/* ptex */
attributes->add(ATTR_STD_PTEX_FACE_ID);
attributes->add(ATTR_STD_PTEX_UV);
@@ -567,7 +567,7 @@ ImageParams EnvironmentTextureNode::image_params() const
void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
#ifdef WITH_PTEX
- if (shader->has_surface && string_endswith(filename, ".ptx")) {
+ if (shader->has_surface_link() && string_endswith(filename, ".ptx")) {
/* ptex */
attributes->add(ATTR_STD_PTEX_FACE_ID);
attributes->add(ATTR_STD_PTEX_UV);
@@ -2335,7 +2335,7 @@ AnisotropicBsdfNode::AnisotropicBsdfNode() : BsdfNode(get_node_type())
void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
ShaderInput *tangent_in = input("Tangent");
if (!tangent_in->link)
@@ -2859,7 +2859,7 @@ bool PrincipledBsdfNode::has_surface_bssrdf()
void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
ShaderInput *tangent_in = input("Tangent");
if (!tangent_in->link)
@@ -3700,7 +3700,7 @@ GeometryNode::GeometryNode() : ShaderNode(get_node_type())
void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
if (!output("Tangent")->links.empty()) {
attributes->add(ATTR_STD_GENERATED);
}
@@ -3846,7 +3846,7 @@ TextureCoordinateNode::TextureCoordinateNode() : ShaderNode(get_node_type())
void TextureCoordinateNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
if (!from_dupli) {
if (!output("Generated")->links.empty())
attributes->add(ATTR_STD_GENERATED);
@@ -4404,7 +4404,7 @@ HairInfoNode::HairInfoNode() : ShaderNode(get_node_type())
void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
ShaderOutput *intercept_out = output("Intercept");
if (!intercept_out->links.empty())
@@ -6760,7 +6760,7 @@ NormalMapNode::NormalMapNode() : ShaderNode(get_node_type())
void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) {
+ if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) {
if (attribute.empty()) {
attributes->add(ATTR_STD_UV_TANGENT);
attributes->add(ATTR_STD_UV_TANGENT_SIGN);
@@ -6854,7 +6854,7 @@ TangentNode::TangentNode() : ShaderNode(get_node_type())
void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface) {
+ if (shader->has_surface_link()) {
if (direction_type == NODE_TANGENT_UVMAP) {
if (attribute.empty())
attributes->add(ATTR_STD_UV_TANGENT);
@@ -7037,7 +7037,7 @@ void VectorDisplacementNode::constant_fold(const ConstantFolder &folder)
void VectorDisplacementNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
- if (shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) {
+ if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) {
if (attribute.empty()) {
attributes->add(ATTR_STD_UV_TANGENT);
attributes->add(ATTR_STD_UV_TANGENT_SIGN);
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 5fe4e9ed57f..c88d94fe4c2 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -102,6 +102,8 @@ NODE_DEFINE(Object)
SOCKET_NODE(particle_system, "Particle System", ParticleSystem::get_node_type());
SOCKET_INT(particle_index, "Particle Index", 0);
+ SOCKET_FLOAT(ao_distance, "AO Distance", 0.0f);
+
return type;
}
@@ -428,6 +430,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.random_number = random_number;
kobject.particle_index = particle_index;
kobject.motion_offset = 0;
+ kobject.ao_distance = ob->ao_distance;
if (geom->get_use_motion_blur()) {
state->have_motion = true;
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index ebb7733c2aa..c52ddce48da 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -73,6 +73,8 @@ class Object : public Node {
NODE_SOCKET_API(ParticleSystem *, particle_system);
NODE_SOCKET_API(int, particle_index);
+ NODE_SOCKET_API(float, ao_distance)
+
Object();
~Object();
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 19d4a66353d..1b91c49f0ea 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -57,23 +57,26 @@ Session::Session(const SessionParams &params_)
stats(),
profiler()
{
- device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
+ device_use_gl_ = ((params.device.type != DEVICE_CPU) && !params.background);
TaskScheduler::init(params.threads);
- session_thread = NULL;
+ session_thread_ = NULL;
scene = NULL;
- reset_time = 0.0;
- last_update_time = 0.0;
+ reset_time_ = 0.0;
+ last_update_time_ = 0.0;
- delayed_reset.do_reset = false;
- delayed_reset.samples = 0;
+ delayed_reset_.do_reset = false;
+ delayed_reset_.samples = 0;
- display_outdated = false;
- gpu_draw_ready = false;
- gpu_need_display_buffer_update = false;
- pause = false;
+ display_outdated_ = false;
+ gpu_draw_ready_ = false;
+ gpu_need_display_buffer_update_ = false;
+
+ pause_ = false;
+ cancel_ = false;
+ new_work_added_ = false;
buffers = NULL;
display = NULL;
@@ -127,25 +130,26 @@ Session::~Session()
void Session::start()
{
- if (!session_thread) {
- session_thread = new thread(function_bind(&Session::run, this));
+ if (!session_thread_) {
+ session_thread_ = new thread(function_bind(&Session::run, this));
}
}
void Session::cancel()
{
- if (session_thread) {
+ if (session_thread_) {
/* wait for session thread to end */
progress.set_cancel("Exiting");
- gpu_need_display_buffer_update = false;
- gpu_need_display_buffer_update_cond.notify_all();
+ gpu_need_display_buffer_update_ = false;
+ gpu_need_display_buffer_update_cond_.notify_all();
{
- thread_scoped_lock pause_lock(pause_mutex);
- pause = false;
+ thread_scoped_lock pause_lock(pause_mutex_);
+ pause_ = false;
+ cancel_ = true;
}
- pause_cond.notify_all();
+ pause_cond_.notify_all();
wait();
}
@@ -153,9 +157,9 @@ void Session::cancel()
bool Session::ready_to_reset()
{
- double dt = time_dt() - reset_time;
+ double dt = time_dt() - reset_time_;
- if (!display_outdated)
+ if (!display_outdated_)
return (dt > params.reset_timeout);
else
return (dt > params.cancel_timeout);
@@ -165,48 +169,50 @@ bool Session::ready_to_reset()
void Session::reset_gpu(BufferParams &buffer_params, int samples)
{
- thread_scoped_lock pause_lock(pause_mutex);
+ thread_scoped_lock pause_lock(pause_mutex_);
/* block for buffer access and reset immediately. we can't do this
* in the thread, because we need to allocate an OpenGL buffer, and
* that only works in the main thread */
- thread_scoped_lock display_lock(display_mutex);
- thread_scoped_lock buffers_lock(buffers_mutex);
+ thread_scoped_lock display_lock(display_mutex_);
+ thread_scoped_lock buffers_lock(buffers_mutex_);
- display_outdated = true;
- reset_time = time_dt();
+ display_outdated_ = true;
+ reset_time_ = time_dt();
reset_(buffer_params, samples);
- gpu_need_display_buffer_update = false;
- gpu_need_display_buffer_update_cond.notify_all();
+ gpu_need_display_buffer_update_ = false;
+ gpu_need_display_buffer_update_cond_.notify_all();
+
+ new_work_added_ = true;
- pause_cond.notify_all();
+ pause_cond_.notify_all();
}
bool Session::draw_gpu(BufferParams &buffer_params, DeviceDrawParams &draw_params)
{
/* block for buffer access */
- thread_scoped_lock display_lock(display_mutex);
+ thread_scoped_lock display_lock(display_mutex_);
/* first check we already rendered something */
- if (gpu_draw_ready) {
+ if (gpu_draw_ready_) {
/* then verify the buffers have the expected size, so we don't
* draw previous results in a resized window */
if (buffer_params.width == display->params.width &&
buffer_params.height == display->params.height) {
/* for CUDA we need to do tone-mapping still, since we can
* only access GL buffers from the main thread. */
- if (gpu_need_display_buffer_update) {
- thread_scoped_lock buffers_lock(buffers_mutex);
+ if (gpu_need_display_buffer_update_) {
+ thread_scoped_lock buffers_lock(buffers_mutex_);
copy_to_display_buffer(tile_manager.state.sample);
- gpu_need_display_buffer_update = false;
- gpu_need_display_buffer_update_cond.notify_all();
+ gpu_need_display_buffer_update_ = false;
+ gpu_need_display_buffer_update_cond_.notify_all();
}
display->draw(device, draw_params);
- if (display_outdated && (time_dt() - reset_time) > params.text_timeout)
+ if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout)
return false;
return true;
@@ -220,9 +226,9 @@ void Session::run_gpu()
{
bool tiles_written = false;
- reset_time = time_dt();
- last_update_time = time_dt();
- last_display_time = last_update_time;
+ reset_time_ = time_dt();
+ last_update_time_ = time_dt();
+ last_display_time_ = last_update_time_;
progress.set_render_start_time();
@@ -255,7 +261,7 @@ void Session::run_gpu()
/* buffers mutex is locked entirely while rendering each
* sample, and released/reacquired on each iteration to allow
* reset and draw in between */
- thread_scoped_lock buffers_lock(buffers_mutex);
+ thread_scoped_lock buffers_lock(buffers_mutex_);
/* update status and timing */
update_status_time();
@@ -273,17 +279,17 @@ void Session::run_gpu()
/* update status and timing */
update_status_time();
- gpu_need_display_buffer_update = !delayed_denoise;
- gpu_draw_ready = true;
+ gpu_need_display_buffer_update_ = !delayed_denoise;
+ gpu_draw_ready_ = true;
progress.set_update();
/* wait for until display buffer is updated */
if (!params.background) {
- while (gpu_need_display_buffer_update) {
+ while (gpu_need_display_buffer_update_) {
if (progress.get_cancel())
break;
- gpu_need_display_buffer_update_cond.wait(buffers_lock);
+ gpu_need_display_buffer_update_cond_.wait(buffers_lock);
}
}
@@ -305,23 +311,23 @@ void Session::run_gpu()
void Session::reset_cpu(BufferParams &buffer_params, int samples)
{
- thread_scoped_lock reset_lock(delayed_reset.mutex);
- thread_scoped_lock pause_lock(pause_mutex);
+ thread_scoped_lock reset_lock(delayed_reset_.mutex);
+ thread_scoped_lock pause_lock(pause_mutex_);
- display_outdated = true;
- reset_time = time_dt();
+ display_outdated_ = true;
+ reset_time_ = time_dt();
- delayed_reset.params = buffer_params;
- delayed_reset.samples = samples;
- delayed_reset.do_reset = true;
+ delayed_reset_.params = buffer_params;
+ delayed_reset_.samples = samples;
+ delayed_reset_.do_reset = true;
device->task_cancel();
- pause_cond.notify_all();
+ pause_cond_.notify_all();
}
bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_params)
{
- thread_scoped_lock display_lock(display_mutex);
+ thread_scoped_lock display_lock(display_mutex_);
/* first check we already rendered something */
if (display->draw_ready()) {
@@ -331,7 +337,7 @@ bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_param
buffer_params.height == display->params.height) {
display->draw(device, draw_params);
- if (display_outdated && (time_dt() - reset_time) > params.text_timeout)
+ if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout)
return false;
return true;
@@ -345,46 +351,46 @@ bool Session::steal_tile(RenderTile &rtile, Device *tile_device, thread_scoped_l
{
/* Devices that can get their tiles stolen don't steal tiles themselves.
* Additionally, if there are no stealable tiles in flight, give up here. */
- if (tile_device->info.type == DEVICE_CPU || stealable_tiles == 0) {
+ if (tile_device->info.type == DEVICE_CPU || stealable_tiles_ == 0) {
return false;
}
/* Wait until no other thread is trying to steal a tile. */
- while (tile_stealing_state != NOT_STEALING && stealable_tiles > 0) {
+ while (tile_stealing_state_ != NOT_STEALING && stealable_tiles_ > 0) {
/* Someone else is currently trying to get a tile.
* Wait on the condition variable and try later. */
- tile_steal_cond.wait(tile_lock);
+ tile_steal_cond_.wait(tile_lock);
}
/* If another thread stole the last stealable tile in the meantime, give up. */
- if (stealable_tiles == 0) {
+ if (stealable_tiles_ == 0) {
return false;
}
/* There are stealable tiles in flight, so signal that one should be released. */
- tile_stealing_state = WAITING_FOR_TILE;
+ tile_stealing_state_ = WAITING_FOR_TILE;
/* Wait until a device notices the signal and releases its tile. */
- while (tile_stealing_state != GOT_TILE && stealable_tiles > 0) {
- tile_steal_cond.wait(tile_lock);
+ while (tile_stealing_state_ != GOT_TILE && stealable_tiles_ > 0) {
+ tile_steal_cond_.wait(tile_lock);
}
/* If the last stealable tile finished on its own, give up. */
- if (tile_stealing_state != GOT_TILE) {
- tile_stealing_state = NOT_STEALING;
+ if (tile_stealing_state_ != GOT_TILE) {
+ tile_stealing_state_ = NOT_STEALING;
return false;
}
/* Successfully stole a tile, now move it to the new device. */
- rtile = stolen_tile;
+ rtile = stolen_tile_;
rtile.buffers->buffer.move_device(tile_device);
rtile.buffer = rtile.buffers->buffer.device_pointer;
rtile.stealing_state = RenderTile::NO_STEALING;
rtile.num_samples -= (rtile.sample - rtile.start_sample);
rtile.start_sample = rtile.sample;
- tile_stealing_state = NOT_STEALING;
+ tile_stealing_state_ = NOT_STEALING;
/* Poke any threads which might be waiting for NOT_STEALING above. */
- tile_steal_cond.notify_one();
+ tile_steal_cond_.notify_one();
return true;
}
@@ -394,7 +400,7 @@ bool Session::get_tile_stolen()
/* If tile_stealing_state is WAITING_FOR_TILE, atomically set it to RELEASING_TILE
* and return true. */
TileStealingState expected = WAITING_FOR_TILE;
- return tile_stealing_state.compare_exchange_weak(expected, RELEASING_TILE);
+ return tile_stealing_state_.compare_exchange_weak(expected, RELEASING_TILE);
}
bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_types)
@@ -406,7 +412,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
}
}
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
/* get next tile from manager */
Tile *tile;
@@ -423,7 +429,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
/* Wait for denoising tiles to become available */
if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) {
- denoising_cond.wait(tile_lock);
+ denoising_cond_.wait(tile_lock);
continue;
}
@@ -446,7 +452,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
}
else {
if (tile_device->info.type == DEVICE_CPU) {
- stealable_tiles++;
+ stealable_tiles_++;
rtile.stealing_state = RenderTile::CAN_BE_STOLEN;
}
@@ -515,7 +521,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
/* This will read any passes needed as input for baking. */
if (tile_manager.state.sample == tile_manager.range_start_sample) {
{
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
read_bake_tile_cb(rtile);
}
rtile.buffers->buffer.copy_to_device();
@@ -533,7 +539,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
void Session::update_tile_sample(RenderTile &rtile)
{
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
if (update_render_tile_cb) {
if (params.progressive_refine == false) {
@@ -548,25 +554,25 @@ void Session::update_tile_sample(RenderTile &rtile)
void Session::release_tile(RenderTile &rtile, const bool need_denoise)
{
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
if (rtile.stealing_state != RenderTile::NO_STEALING) {
- stealable_tiles--;
+ stealable_tiles_--;
if (rtile.stealing_state == RenderTile::WAS_STOLEN) {
/* If the tile is being stolen, don't release it here - the new device will pick up where
* the old one left off. */
- assert(tile_stealing_state == RELEASING_TILE);
+ assert(tile_stealing_state_ == RELEASING_TILE);
assert(rtile.sample < rtile.start_sample + rtile.num_samples);
- tile_stealing_state = GOT_TILE;
- stolen_tile = rtile;
- tile_steal_cond.notify_all();
+ tile_stealing_state_ = GOT_TILE;
+ stolen_tile_ = rtile;
+ tile_steal_cond_.notify_all();
return;
}
- else if (stealable_tiles == 0) {
+ else if (stealable_tiles_ == 0) {
/* If this was the last stealable tile, wake up any threads still waiting for one. */
- tile_steal_cond.notify_all();
+ tile_steal_cond_.notify_all();
}
}
@@ -595,12 +601,12 @@ void Session::release_tile(RenderTile &rtile, const bool need_denoise)
update_status_time();
/* Notify denoising thread that a tile was finished. */
- denoising_cond.notify_all();
+ denoising_cond_.notify_all();
}
void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device)
{
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
const int4 image_region = make_int4(
tile_manager.state.buffer.full_x,
@@ -677,7 +683,7 @@ void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_de
void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device)
{
- thread_scoped_lock tile_lock(tile_mutex);
+ thread_scoped_lock tile_lock(tile_mutex_);
device->unmap_neighbor_tiles(tile_device, neighbors);
}
@@ -685,8 +691,8 @@ void Session::run_cpu()
{
bool tiles_written = false;
- last_update_time = time_dt();
- last_display_time = last_update_time;
+ last_update_time_ = time_dt();
+ last_display_time_ = last_update_time_;
while (!progress.get_cancel()) {
const bool no_tiles = !run_update_for_next_iteration();
@@ -718,7 +724,7 @@ void Session::run_cpu()
/* buffers mutex is locked entirely while rendering each
* sample, and released/reacquired on each iteration to allow
* reset and draw in between */
- thread_scoped_lock buffers_lock(buffers_mutex);
+ thread_scoped_lock buffers_lock(buffers_mutex_);
/* update status and timing */
update_status_time();
@@ -741,14 +747,14 @@ void Session::run_cpu()
device->task_wait();
{
- thread_scoped_lock reset_lock(delayed_reset.mutex);
- thread_scoped_lock buffers_lock(buffers_mutex);
- thread_scoped_lock display_lock(display_mutex);
+ thread_scoped_lock reset_lock(delayed_reset_.mutex);
+ thread_scoped_lock buffers_lock(buffers_mutex_);
+ thread_scoped_lock display_lock(display_mutex_);
- if (delayed_reset.do_reset) {
+ if (delayed_reset_.do_reset) {
/* reset rendering if request from main thread */
- delayed_reset.do_reset = false;
- reset_(delayed_reset.params, delayed_reset.samples);
+ delayed_reset_.do_reset = false;
+ reset_(delayed_reset_.params, delayed_reset_.samples);
}
else if (need_copy_to_display_buffer) {
/* Only copy to display_buffer if we do not reset, we don't
@@ -783,7 +789,7 @@ void Session::run()
/* reset number of rendered samples */
progress.reset_sample();
- if (device_use_gl)
+ if (device_use_gl_)
run_gpu();
else
run_cpu();
@@ -801,12 +807,12 @@ void Session::run()
bool Session::run_update_for_next_iteration()
{
thread_scoped_lock scene_lock(scene->mutex);
- thread_scoped_lock reset_lock(delayed_reset.mutex);
+ thread_scoped_lock reset_lock(delayed_reset_.mutex);
- if (delayed_reset.do_reset) {
- thread_scoped_lock buffers_lock(buffers_mutex);
- reset_(delayed_reset.params, delayed_reset.samples);
- delayed_reset.do_reset = false;
+ if (delayed_reset_.do_reset) {
+ thread_scoped_lock buffers_lock(buffers_mutex_);
+ reset_(delayed_reset_.params, delayed_reset_.samples);
+ delayed_reset_.do_reset = false;
}
const bool have_tiles = tile_manager.next();
@@ -829,35 +835,43 @@ bool Session::run_wait_for_work(bool no_tiles)
return false;
}
- thread_scoped_lock pause_lock(pause_mutex);
+ thread_scoped_lock pause_lock(pause_mutex_);
- if (!pause && !no_tiles) {
+ if (!pause_ && !no_tiles) {
+ /* Rendering is not paused and there is work to be done. No need to wait for anything. */
return false;
}
- update_status_time(pause, no_tiles);
+ update_status_time(pause_, no_tiles);
- while (true) {
+ /* Only leave the loop when rendering is not paused. But even if the current render is un-paused
+ * but there is nothing to render keep waiting until new work is added. */
+ while (!cancel_) {
scoped_timer pause_timer;
- pause_cond.wait(pause_lock);
- if (pause) {
- progress.add_skip_time(pause_timer, params.background);
+
+ if (!pause_ && (!no_tiles || new_work_added_ || delayed_reset_.do_reset)) {
+ break;
}
- update_status_time(pause, no_tiles);
- progress.set_update();
+ /* Wait for either pause state changed, or extra samples added to render. */
+ pause_cond_.wait(pause_lock);
- if (!pause) {
- break;
+ if (pause_) {
+ progress.add_skip_time(pause_timer, params.background);
}
+
+ update_status_time(pause_, no_tiles);
+ progress.set_update();
}
+ new_work_added_ = false;
+
return no_tiles;
}
bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params)
{
- if (device_use_gl)
+ if (device_use_gl_)
return draw_gpu(buffer_params, draw_params);
else
return draw_cpu(buffer_params, draw_params);
@@ -866,7 +880,7 @@ bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params)
void Session::reset_(BufferParams &buffer_params, int samples)
{
if (buffers && buffer_params.modified(tile_manager.params)) {
- gpu_draw_ready = false;
+ gpu_draw_ready_ = false;
buffers->reset(buffer_params);
if (display) {
display->reset(buffer_params);
@@ -874,8 +888,8 @@ void Session::reset_(BufferParams &buffer_params, int samples)
}
tile_manager.reset(buffer_params, samples);
- stealable_tiles = 0;
- tile_stealing_state = NOT_STEALING;
+ stealable_tiles_ = 0;
+ tile_stealing_state_ = NOT_STEALING;
progress.reset_sample();
bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX;
@@ -888,7 +902,7 @@ void Session::reset_(BufferParams &buffer_params, int samples)
void Session::reset(BufferParams &buffer_params, int samples)
{
- if (device_use_gl)
+ if (device_use_gl_)
reset_gpu(buffer_params, samples);
else
reset_cpu(buffer_params, samples);
@@ -896,30 +910,37 @@ void Session::reset(BufferParams &buffer_params, int samples)
void Session::set_samples(int samples)
{
- if (samples != params.samples) {
- params.samples = samples;
- tile_manager.set_samples(samples);
+ if (samples == params.samples) {
+ return;
+ }
+
+ params.samples = samples;
+ tile_manager.set_samples(samples);
- pause_cond.notify_all();
+ {
+ thread_scoped_lock pause_lock(pause_mutex_);
+ new_work_added_ = true;
}
+
+ pause_cond_.notify_all();
}
-void Session::set_pause(bool pause_)
+void Session::set_pause(bool pause)
{
bool notify = false;
{
- thread_scoped_lock pause_lock(pause_mutex);
+ thread_scoped_lock pause_lock(pause_mutex_);
if (pause != pause_) {
- pause = pause_;
+ pause_ = pause;
notify = true;
}
}
- if (session_thread) {
+ if (session_thread_) {
if (notify) {
- pause_cond.notify_all();
+ pause_cond_.notify_all();
}
}
else if (pause_) {
@@ -932,7 +953,7 @@ void Session::set_denoising(const DenoiseParams &denoising)
bool need_denoise = denoising.need_denoising_task();
/* Lock buffers so no denoising operation is triggered while the settings are changed here. */
- thread_scoped_lock buffers_lock(buffers_mutex);
+ thread_scoped_lock buffers_lock(buffers_mutex_);
params.denoising = denoising;
if (!(params.device.denoisers & denoising.type)) {
@@ -957,18 +978,18 @@ void Session::set_denoising_start_sample(int sample)
if (sample != params.denoising.start_sample) {
params.denoising.start_sample = sample;
- pause_cond.notify_all();
+ pause_cond_.notify_all();
}
}
void Session::wait()
{
- if (session_thread) {
- session_thread->join();
- delete session_thread;
+ if (session_thread_) {
+ session_thread_->join();
+ delete session_thread_;
}
- session_thread = NULL;
+ session_thread_ = NULL;
}
bool Session::update_scene()
@@ -1099,7 +1120,7 @@ bool Session::render_need_denoise(bool &delayed)
/* Avoid excessive denoising in viewport after reaching a certain amount of samples. */
delayed = (tile_manager.state.sample >= 20 &&
- (time_dt() - last_display_time) < params.progressive_update_timeout);
+ (time_dt() - last_display_time_) < params.progressive_update_timeout);
return !delayed;
}
@@ -1197,10 +1218,10 @@ void Session::copy_to_display_buffer(int sample)
/* set display to new size */
display->draw_set(task.w, task.h);
- last_display_time = time_dt();
+ last_display_time_ = time_dt();
}
- display_outdated = false;
+ display_outdated_ = false;
}
bool Session::update_progressive_refine(bool cancel)
@@ -1210,7 +1231,7 @@ bool Session::update_progressive_refine(bool cancel)
double current_time = time_dt();
- if (current_time - last_update_time < params.progressive_update_timeout) {
+ if (current_time - last_update_time_ < params.progressive_update_timeout) {
/* If last sample was processed, we need to write buffers anyway. */
if (!write && sample != 1)
return false;
@@ -1241,7 +1262,7 @@ bool Session::update_progressive_refine(bool cancel)
}
}
- last_update_time = current_time;
+ last_update_time_ = current_time;
return write;
}
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index bc3b8366c05..05025c10f9c 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -174,7 +174,7 @@ class Session {
bool do_reset;
BufferParams params;
int samples;
- } delayed_reset;
+ } delayed_reset_;
void run();
@@ -207,38 +207,41 @@ class Session {
void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device);
void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device);
- bool device_use_gl;
+ bool device_use_gl_;
- thread *session_thread;
+ thread *session_thread_;
- volatile bool display_outdated;
+ volatile bool display_outdated_;
- volatile bool gpu_draw_ready;
- volatile bool gpu_need_display_buffer_update;
- thread_condition_variable gpu_need_display_buffer_update_cond;
+ volatile bool gpu_draw_ready_;
+ volatile bool gpu_need_display_buffer_update_;
+ thread_condition_variable gpu_need_display_buffer_update_cond_;
- bool pause;
- thread_condition_variable pause_cond;
- thread_mutex pause_mutex;
- thread_mutex tile_mutex;
- thread_mutex buffers_mutex;
- thread_mutex display_mutex;
- thread_condition_variable denoising_cond;
- thread_condition_variable tile_steal_cond;
+ bool pause_;
+ bool cancel_;
+ bool new_work_added_;
- double reset_time;
- double last_update_time;
- double last_display_time;
+ thread_condition_variable pause_cond_;
+ thread_mutex pause_mutex_;
+ thread_mutex tile_mutex_;
+ thread_mutex buffers_mutex_;
+ thread_mutex display_mutex_;
+ thread_condition_variable denoising_cond_;
+ thread_condition_variable tile_steal_cond_;
- RenderTile stolen_tile;
+ double reset_time_;
+ double last_update_time_;
+ double last_display_time_;
+
+ RenderTile stolen_tile_;
typedef enum {
NOT_STEALING, /* There currently is no tile stealing in progress. */
WAITING_FOR_TILE, /* A device is waiting for another device to release a tile. */
RELEASING_TILE, /* A device has releasing a stealable tile. */
GOT_TILE /* A device has released a stealable tile, which is now stored in stolen_tile. */
} TileStealingState;
- std::atomic<TileStealingState> tile_stealing_state;
- int stealable_tiles;
+ std::atomic<TileStealingState> tile_stealing_state_;
+ int stealable_tiles_;
/* progressive refine */
bool update_progressive_refine(bool cancel);
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index d7c8f81ccfe..29d14700efd 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -157,6 +157,14 @@ class Shader : public Node {
void tag_update(Scene *scene);
void tag_used(Scene *scene);
+ /* Return true when either of the surface or displacement socket of the output node is linked.
+ * This should be used to ensure that surface attributes are also requested even when only the
+ * displacement socket is linked. */
+ bool has_surface_link() const
+ {
+ return has_surface || has_displacement;
+ }
+
bool need_update_geometry() const;
};