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