1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2014-2018 Esteban Tovagliari, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28
29 // Interface header.
30 #include "shadergroup.h"
31
32 // appleseed.renderer headers.
33 #include "renderer/global/globallogger.h"
34 #include "renderer/kernel/shading/oslshadingsystem.h"
35 #include "renderer/modeling/project/project.h"
36 #include "renderer/modeling/shadergroup/shader.h"
37 #include "renderer/modeling/shadergroup/shaderconnection.h"
38 #include "renderer/utility/paramarray.h"
39
40 // appleseed.foundation headers.
41 #include "foundation/utility/api/apistring.h"
42 #include "foundation/utility/api/specializedapiarrays.h"
43 #include "foundation/utility/job/abortswitch.h"
44 #include "foundation/utility/uid.h"
45
46 // Boost headers.
47 #include "boost/unordered/unordered_map.hpp"
48
49 // Standard headers.
50 #include <exception>
51 #include <utility>
52
53 using namespace foundation;
54 using namespace std;
55
56 namespace renderer
57 {
58
59 //
60 // ShaderGroup class implementation.
61 //
62
63 namespace
64 {
65 const UniqueID g_class_uid = new_guid();
66
67 const OIIO::ustring g_emission_str("emission");
68 const OIIO::ustring g_transparent_str("transparent");
69 const OIIO::ustring g_subsurface_str("as_subsurface");
70 const OIIO::ustring g_randomwalk_glass_str("as_randomwalk_glass");
71 const OIIO::ustring g_holdout_str("holdout");
72 const OIIO::ustring g_matte_str("as_matte");
73 const OIIO::ustring g_debug_str("debug");
74
75 const OIIO::ustring g_npr_shading_str("as_npr_shading");
76 const OIIO::ustring g_npr_contour_str("as_npr_contour");
77
78 const OIIO::ustring g_dPdtime_str("dPdtime");
79
is_subsurface_closure(const OIIO::ustring & closure_name)80 bool is_subsurface_closure(const OIIO::ustring& closure_name)
81 {
82 return
83 closure_name == g_subsurface_str ||
84 closure_name == g_randomwalk_glass_str;
85 }
86 }
87
88 struct ShaderGroup::Impl
89 {
90 typedef pair<const AssemblyInstance*, const ObjectInstance*> SurfaceAreaKey;
91 typedef boost::unordered_map<SurfaceAreaKey, float> SurfaceAreaMap;
92
93 ShaderContainer m_shaders;
94 ShaderConnectionContainer m_connections;
95 mutable OSL::ShaderGroupRef m_shader_group_ref;
96 mutable SurfaceAreaMap m_surface_areas;
97 };
98
ShaderGroup(const char * name)99 ShaderGroup::ShaderGroup(const char* name)
100 : ConnectableEntity(g_class_uid, ParamArray())
101 , impl(new Impl())
102 {
103 set_name(name);
104 clear();
105 }
106
~ShaderGroup()107 ShaderGroup::~ShaderGroup()
108 {
109 delete impl;
110 }
111
release()112 void ShaderGroup::release()
113 {
114 delete this;
115 }
116
get_model() const117 const char* ShaderGroup::get_model() const
118 {
119 return ShaderGroupFactory::get_model();
120 }
121
clear()122 void ShaderGroup::clear()
123 {
124 impl->m_shaders.clear();
125 impl->m_connections.clear();
126 impl->m_shader_group_ref.reset();
127 m_flags = 0;
128 }
129
add_shader(const char * type,const char * name,const char * layer,const ParamArray & params)130 void ShaderGroup::add_shader(
131 const char* type,
132 const char* name,
133 const char* layer,
134 const ParamArray& params)
135 {
136 auto_release_ptr<Shader> shader(
137 new Shader(
138 type,
139 name,
140 layer,
141 params));
142
143 impl->m_shaders.insert(shader);
144
145 RENDERER_LOG_DEBUG("created shader %s, layer = %s.", name, layer);
146 }
147
add_source_shader(const char * type,const char * name,const char * layer,const char * source,const ParamArray & params)148 void ShaderGroup::add_source_shader(
149 const char* type,
150 const char* name,
151 const char* layer,
152 const char* source,
153 const ParamArray& params)
154 {
155 auto_release_ptr<Shader> shader(
156 new Shader(
157 type,
158 name,
159 layer,
160 source,
161 params));
162
163 impl->m_shaders.insert(shader);
164
165 RENDERER_LOG_DEBUG("created source shader %s, layer = %s.", name, layer);
166 }
167
add_connection(const char * src_layer,const char * src_param,const char * dst_layer,const char * dst_param)168 void ShaderGroup::add_connection(
169 const char* src_layer,
170 const char* src_param,
171 const char* dst_layer,
172 const char* dst_param)
173 {
174 auto_release_ptr<ShaderConnection> connection(
175 new ShaderConnection(
176 src_layer,
177 src_param,
178 dst_layer,
179 dst_param));
180
181 impl->m_connections.insert(connection);
182
183 RENDERER_LOG_DEBUG(
184 "created shader connection: src_layer = %s, src_param = %s, dst_layer = %s, dst_param = %s.",
185 src_layer,
186 src_param,
187 dst_layer,
188 dst_param);
189 }
190
create_optimized_osl_shader_group(OSLShadingSystem & shading_system,const ShaderCompiler * shader_compiler,IAbortSwitch * abort_switch)191 bool ShaderGroup::create_optimized_osl_shader_group(
192 OSLShadingSystem& shading_system,
193 const ShaderCompiler* shader_compiler,
194 IAbortSwitch* abort_switch)
195 {
196 if (is_valid())
197 return true;
198
199 RENDERER_LOG_DEBUG("setting up shader group \"%s\"...", get_path().c_str());
200
201 if (!compile_source_shaders(shader_compiler))
202 return false;
203
204 try
205 {
206 OSL::ShaderGroupRef shader_group_ref = shading_system.ShaderGroupBegin(get_name());
207
208 if (shader_group_ref.get() == nullptr)
209 {
210 RENDERER_LOG_ERROR("failed to setup shader group \"%s\": ShaderGroupBegin() call failed.", get_path().c_str());
211 return false;
212 }
213
214 for (Shader& shader : impl->m_shaders)
215 {
216 if (is_aborted(abort_switch))
217 {
218 shading_system.ShaderGroupEnd();
219 return true;
220 }
221
222 if (!shader.add(shading_system))
223 return false;
224 }
225
226 for (ShaderConnection& connection : impl->m_connections)
227 {
228 if (is_aborted(abort_switch))
229 {
230 shading_system.ShaderGroupEnd();
231 return true;
232 }
233
234 if (!connection.add(shading_system))
235 return false;
236 }
237
238 if (!shading_system.ShaderGroupEnd())
239 {
240 RENDERER_LOG_ERROR("failed to setup shader group \"%s\": ShaderGroupEnd() call failed.", get_path().c_str());
241 return false;
242 }
243
244 impl->m_shader_group_ref = shader_group_ref;
245
246 get_shadergroup_closures_info(shading_system);
247 report_has_closure("bsdf", HasBSDFs);
248 report_has_closure(g_emission_str.c_str(), HasEmission);
249 report_has_closure(g_transparent_str.c_str(), HasTransparency);
250 report_has_closure(g_subsurface_str.c_str(), HasSubsurface);
251 report_has_closure(g_debug_str.c_str(), HasDebug);
252
253 report_has_closure("NPR", HasNPR);
254 report_has_closure(g_matte_str.c_str(), HasMatte);
255
256 get_shadergroup_globals_info(shading_system);
257 report_uses_global("dPdtime", UsesdPdTime);
258
259 return true;
260 }
261 catch (const exception& e)
262 {
263 RENDERER_LOG_ERROR("failed to setup shader group \"%s\": %s.", get_path().c_str(), e.what());
264 return false;
265 }
266 }
267
release_optimized_osl_shader_group()268 void ShaderGroup::release_optimized_osl_shader_group()
269 {
270 impl->m_shader_group_ref.reset();
271 }
272
shaders() const273 const ShaderContainer& ShaderGroup::shaders() const
274 {
275 return impl->m_shaders;
276 }
277
shader_connections() const278 const ShaderConnectionContainer& ShaderGroup::shader_connections() const
279 {
280 return impl->m_connections;
281 }
282
is_valid() const283 bool ShaderGroup::is_valid() const
284 {
285 return impl->m_shader_group_ref.get() != nullptr;
286 }
287
get_surface_area(const AssemblyInstance * assembly_instance,const ObjectInstance * object_instance) const288 float ShaderGroup::get_surface_area(
289 const AssemblyInstance* assembly_instance,
290 const ObjectInstance* object_instance) const
291 {
292 assert(has_emission());
293 return impl->m_surface_areas[Impl::SurfaceAreaKey(assembly_instance, object_instance)];
294 }
295
osl_shader_group() const296 void* ShaderGroup::osl_shader_group() const
297 {
298 return impl->m_shader_group_ref.get();
299 }
300
compile_source_shaders(const ShaderCompiler * compiler)301 bool ShaderGroup::compile_source_shaders(const ShaderCompiler* compiler)
302 {
303 for (Shader& shader : impl->m_shaders)
304 {
305 const bool success = shader.compile_shader(compiler);
306
307 if (!success)
308 return false;
309 }
310
311 return true;
312 }
313
get_shadergroup_closures_info(OSLShadingSystem & shading_system)314 void ShaderGroup::get_shadergroup_closures_info(OSLShadingSystem& shading_system)
315 {
316 // Assume the shader group has all closure types.
317 m_flags |= HasAllClosures;
318
319 int num_unknown_closures = 0;
320 if (!shading_system.getattribute(
321 impl->m_shader_group_ref.get(),
322 "unknown_closures_needed",
323 num_unknown_closures))
324 {
325 RENDERER_LOG_WARNING(
326 "getattribute: unknown_closures_needed call failed for shader group \"%s\"; "
327 "assuming shader group has all kinds of closures.",
328 get_path().c_str());
329 return;
330 }
331
332 if (num_unknown_closures != 0)
333 {
334 RENDERER_LOG_WARNING(
335 "shader group \"%s\" has unknown closures; "
336 "assuming shader group has all kinds of closures.",
337 get_path().c_str());
338 return;
339 }
340
341 int num_closures = 0;
342 if (!shading_system.getattribute(
343 impl->m_shader_group_ref.get(),
344 "num_closures_needed",
345 num_closures))
346 {
347 RENDERER_LOG_WARNING(
348 "getattribute: num_closures_needed call failed for shader group \"%s\"; "
349 "assuming shader group has all kinds of closures.",
350 get_path().c_str());
351 }
352
353 if (num_closures != 0)
354 {
355 OIIO::ustring* closures = nullptr;
356 if (!shading_system.getattribute(
357 impl->m_shader_group_ref.get(),
358 "closures_needed",
359 OIIO::TypeDesc::PTR,
360 &closures))
361 {
362 RENDERER_LOG_WARNING(
363 "getattribute: closures_needed call failed for shader group \"%s\"; "
364 "assuming shader group has all kinds of closures.",
365 get_path().c_str());
366 return;
367 }
368
369 // Clear all closure flags.
370 m_flags &= ~HasAllClosures;
371
372 // Set the closure flags.
373 for (int i = 0; i < num_closures; ++i)
374 {
375 if (closures[i] == g_emission_str)
376 m_flags |= HasEmission;
377 else if (closures[i] == g_transparent_str)
378 m_flags |= HasTransparency;
379 else if (is_subsurface_closure(closures[i]))
380 m_flags |= HasSubsurface;
381 else if (closures[i] == g_debug_str)
382 m_flags |= HasDebug;
383 else if (closures[i] == g_npr_shading_str)
384 m_flags |= HasNPR;
385 else if (closures[i] == g_npr_contour_str)
386 m_flags |= HasNPR;
387 else if (
388 closures[i] == g_matte_str ||
389 closures[i] == g_holdout_str)
390 m_flags |= HasMatte;
391 else
392 m_flags |= HasBSDFs;
393 }
394 }
395 else
396 {
397 // Shader group uses no closures.
398 m_flags &= ~HasAllClosures;
399 }
400 }
401
report_has_closure(const char * closure_name,const Flags flag) const402 void ShaderGroup::report_has_closure(const char* closure_name, const Flags flag) const
403 {
404 if (m_flags & flag)
405 {
406 RENDERER_LOG_DEBUG(
407 "shader group \"%s\" has %s closures.",
408 get_path().c_str(),
409 closure_name);
410 }
411 else
412 {
413 RENDERER_LOG_DEBUG(
414 "shader group \"%s\" does not have %s closures.",
415 get_path().c_str(),
416 closure_name);
417 }
418 }
419
get_shadergroup_globals_info(OSLShadingSystem & shading_system)420 void ShaderGroup::get_shadergroup_globals_info(OSLShadingSystem& shading_system)
421 {
422 // Assume the shader group uses all globals.
423 m_flags |= UsesAllGlobals;
424
425 int num_globals = 0;
426 if (!shading_system.getattribute(
427 impl->m_shader_group_ref.get(),
428 "num_globals_needed",
429 num_globals))
430 {
431 RENDERER_LOG_WARNING(
432 "getattribute: num_globals_needed call failed for shader group \"%s\"; "
433 "assuming shader group uses all globals.",
434 get_path().c_str());
435 return;
436 }
437
438 if (num_globals != 0)
439 {
440 OIIO::ustring* globals = nullptr;
441 if (!shading_system.getattribute(
442 impl->m_shader_group_ref.get(),
443 "globals_needed",
444 OIIO::TypeDesc::PTR,
445 &globals))
446 {
447 RENDERER_LOG_WARNING(
448 "getattribute: globals_needed call failed for shader group \"%s\"; "
449 "assuming shader group uses all globals.",
450 get_path().c_str());
451 return;
452 }
453
454 // Clear all globals flags.
455 m_flags &= ~UsesAllGlobals;
456
457 // Set the globals flags.
458 for (int i = 0; i < num_globals; ++i)
459 {
460 if (globals[i] == g_dPdtime_str)
461 m_flags |= UsesdPdTime;
462 }
463 }
464 else
465 {
466 // The shader group uses no globals.
467 m_flags &= ~UsesAllGlobals;
468 }
469 }
470
report_uses_global(const char * global_name,const Flags flag) const471 void ShaderGroup::report_uses_global(const char* global_name, const Flags flag) const
472 {
473 if (m_flags & flag)
474 {
475 RENDERER_LOG_DEBUG(
476 "shader group \"%s\" uses the %s global.",
477 get_path().c_str(),
478 global_name);
479 }
480 else
481 {
482 RENDERER_LOG_DEBUG(
483 "shader group \"%s\" does not use the %s global.",
484 get_path().c_str(),
485 global_name);
486 }
487 }
488
set_surface_area(const AssemblyInstance * assembly_instance,const ObjectInstance * object_instance,const float area) const489 void ShaderGroup::set_surface_area(
490 const AssemblyInstance* assembly_instance,
491 const ObjectInstance* object_instance,
492 const float area) const
493 {
494 assert(has_emission());
495 impl->m_surface_areas[Impl::SurfaceAreaKey(assembly_instance, object_instance)] = area;
496 }
497
498
499 //
500 // ShaderGroupFactory class implementation.
501 //
502
get_model()503 const char* ShaderGroupFactory::get_model()
504 {
505 return "shader_group";
506 }
507
get_input_metadata()508 DictionaryArray ShaderGroupFactory::get_input_metadata()
509 {
510 DictionaryArray metadata;
511 return metadata;
512 }
513
create(const char * name)514 auto_release_ptr<ShaderGroup> ShaderGroupFactory::create(const char* name)
515 {
516 return auto_release_ptr<ShaderGroup>(new ShaderGroup(name));
517 }
518
create(const char * name,const ParamArray & params)519 auto_release_ptr<ShaderGroup> ShaderGroupFactory::create(
520 const char* name,
521 const ParamArray& params)
522 {
523 return create(name);
524 }
525
526 } // namespace renderer
527