1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 #include <vector>
6 #include <string>
7 #include <cstdio>
8 
9 #include <OpenImageIO/sysutil.h>
10 #include <OpenImageIO/timer.h>
11 #include <OpenImageIO/thread.h>
12 
13 #include "oslexec_pvt.h"
14 
15 OSL_NAMESPACE_ENTER
16 
17 static mutex buffered_errors_mutex;
18 
19 
20 
ShadingContext(ShadingSystemImpl & shadingsys,PerThreadInfo * threadinfo)21 ShadingContext::ShadingContext (ShadingSystemImpl &shadingsys,
22                                 PerThreadInfo *threadinfo)
23     : m_shadingsys(shadingsys), m_renderer(m_shadingsys.renderer()),
24       m_group(NULL), m_max_warnings(shadingsys.max_warnings_per_thread()), m_dictionary(NULL)
25 {
26     m_shadingsys.m_stat_contexts += 1;
27     m_threadinfo = threadinfo ? threadinfo : shadingsys.get_perthread_info ();
28     m_texture_thread_info = NULL;
29 }
30 
31 
32 
~ShadingContext()33 ShadingContext::~ShadingContext ()
34 {
35     process_errors ();
36     m_shadingsys.m_stat_contexts -= 1;
37     free_dict_resources ();
38 }
39 
40 
41 
42 bool
execute_init(ShaderGroup & sgroup,ShaderGlobals & ssg,bool run)43 ShadingContext::execute_init (ShaderGroup &sgroup, ShaderGlobals &ssg, bool run)
44 {
45     if (m_group)
46         execute_cleanup ();
47     m_group = &sgroup;
48     m_ticks = 0;
49 
50     // Optimize if we haven't already
51     if (sgroup.nlayers()) {
52         sgroup.start_running ();
53         if (! sgroup.jitted()) {
54             auto ctx = shadingsys().get_context(thread_info());
55             shadingsys().optimize_group (sgroup, ctx, true /*do_jit*/);
56             if (shadingsys().m_greedyjit && shadingsys().m_groups_to_compile_count) {
57                 // If we are greedily JITing, optimize/JIT everything now
58                 shadingsys().optimize_all_groups ();
59             }
60             shadingsys().release_context(ctx);
61         }
62         if (sgroup.does_nothing())
63             return false;
64     } else {
65        // empty shader - nothing to do!
66        return false;
67     }
68 
69     int profile = shadingsys().m_profile;
70     OIIO::Timer timer (profile ? OIIO::Timer::StartNow : OIIO::Timer::DontStartNow);
71 
72     // Allocate enough space on the heap
73     size_t heap_size_needed = sgroup.llvm_groupdata_size();
74     reserve_heap(heap_size_needed);
75     // Zero out the heap memory we will be using
76     if (shadingsys().m_clearmemory)
77         memset (m_heap.get(), 0, heap_size_needed);
78 
79     // Set up closure storage
80     m_closure_pool.clear();
81 
82     // Clear the message blackboard
83     m_messages.clear ();
84 
85     // Clear miscellaneous scratch space
86     m_scratch_pool.clear ();
87 
88     // Zero out stats for this execution
89     clear_runtime_stats ();
90 
91     if (run) {
92         RunLLVMGroupFunc run_func = sgroup.llvm_compiled_init();
93         if (!run_func)
94             return false;
95         ssg.context = this;
96         ssg.renderer = renderer();
97         ssg.Ci = NULL;
98         run_func (&ssg, m_heap.get());
99     }
100 
101     if (profile)
102         m_ticks += timer.ticks();
103     return true;
104 }
105 
106 
107 
108 bool
execute_layer(ShaderGlobals & ssg,int layernumber)109 ShadingContext::execute_layer (ShaderGlobals &ssg, int layernumber)
110 {
111     if (!group() || group()->nlayers() == 0 || group()->does_nothing())
112         return false;
113     OSL_DASSERT(ssg.context == this && ssg.renderer == renderer());
114 
115     int profile = shadingsys().m_profile;
116     OIIO::Timer timer (profile ? OIIO::Timer::StartNow : OIIO::Timer::DontStartNow);
117 
118     RunLLVMGroupFunc run_func = group()->llvm_compiled_layer (layernumber);
119     if (! run_func)
120         return false;
121 
122     run_func (&ssg, m_heap.get());
123 
124     if (profile)
125         m_ticks += timer.ticks();
126 
127     return true;
128 }
129 
130 
131 
132 bool
execute_cleanup()133 ShadingContext::execute_cleanup ()
134 {
135     if (! group()) {
136         errorf("execute_cleanup called again on a cleaned-up context");
137         return false;
138     }
139 
140     // Process any queued up error messages, warnings, printfs from shaders
141     process_errors ();
142 
143     if (shadingsys().m_profile) {
144         record_runtime_stats ();   // Transfer runtime stats to the shadingsys
145         shadingsys().m_stat_total_shading_time_ticks += m_ticks;
146         group()->m_stat_total_shading_time_ticks += m_ticks;
147     }
148 
149     return true;
150 }
151 
152 
153 
154 bool
execute(ShaderGroup & sgroup,ShaderGlobals & ssg,bool run)155 ShadingContext::execute (ShaderGroup &sgroup, ShaderGlobals &ssg, bool run)
156 {
157     int n = sgroup.m_exec_repeat;
158     Vec3 Psave, Nsave;   // for repeats
159     bool repeat = (n > 1);
160     if (repeat) {
161         // If we're going to repeat more than once, we need to save any
162         // globals that might get modified.
163         Psave = ssg.P;
164         Nsave = ssg.N;
165         if (! run)
166             n = 1;
167     }
168 
169     bool result = true;
170     while (1) {
171         if (! execute_init (sgroup, ssg, run))
172             return false;
173         if (run && n)
174             execute_layer (ssg, group()->nlayers()-1);
175         result = execute_cleanup ();
176         if (--n < 1)
177             break;   // done
178         if (repeat) {
179             // Going around for another pass... restore things as best as we
180             // can.
181             ssg.P = Psave;
182             ssg.N = Nsave;
183             ssg.Ci = NULL;
184         }
185     }
186     return result;
187 }
188 
189 
190 
191 void
record_error(ErrorHandler::ErrCode code,const std::string & text) const192 ShadingContext::record_error (ErrorHandler::ErrCode code,
193                               const std::string &text) const
194 {
195     m_buffered_errors.emplace_back(code,text);
196     // If we aren't buffering, just process immediately
197     if (! shadingsys().m_buffer_printf)
198         process_errors ();
199 }
200 
201 
202 
203 void
process_errors() const204 ShadingContext::process_errors () const
205 {
206     size_t nerrors = m_buffered_errors.size();
207     if (! nerrors)
208         return;
209 
210     // Use a mutex to make sure output from different threads stays
211     // together, at least for one shader invocation, rather than being
212     // interleaved with other threads.
213     lock_guard lock (buffered_errors_mutex);
214 
215     for (size_t i = 0;  i < nerrors;  ++i) {
216         switch (m_buffered_errors[i].first) {
217         case ErrorHandler::EH_MESSAGE :
218         case ErrorHandler::EH_DEBUG :
219            shadingsys().message (m_buffered_errors[i].second);
220             break;
221         case ErrorHandler::EH_INFO :
222             shadingsys().info (m_buffered_errors[i].second);
223             break;
224         case ErrorHandler::EH_WARNING :
225             shadingsys().warning (m_buffered_errors[i].second);
226             break;
227         case ErrorHandler::EH_ERROR :
228         case ErrorHandler::EH_SEVERE :
229             shadingsys().error (m_buffered_errors[i].second);
230             break;
231         default:
232             break;
233         }
234     }
235     m_buffered_errors.clear();
236 }
237 
238 
239 
240 const Symbol *
symbol(ustring layername,ustring symbolname) const241 ShadingContext::symbol (ustring layername, ustring symbolname) const
242 {
243     return group()->find_symbol (layername, symbolname);
244 }
245 
246 
247 
248 const void *
symbol_data(const Symbol & sym) const249 ShadingContext::symbol_data (const Symbol &sym) const
250 {
251     const ShaderGroup &sgroup (*group());
252     if (! sgroup.optimized())
253         return NULL;   // can't retrieve symbol if we didn't optimize it
254 
255     if (sym.dataoffset() >= 0 && (int)m_heapsize > sym.dataoffset()) {
256         // lives on the heap
257         return m_heap.get() + sym.dataoffset();
258     }
259 
260     // doesn't live on the heap
261     if ((sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) &&
262         (sym.valuesource() == Symbol::DefaultVal || sym.valuesource() == Symbol::InstanceVal)) {
263         return sym.data();
264     }
265 
266     return NULL;  // not something we can retrieve
267 }
268 
269 
270 
271 const regex &
find_regex(ustring r)272 ShadingContext::find_regex (ustring r)
273 {
274     RegexMap::const_iterator found = m_regex_map.find (r);
275     if (found != m_regex_map.end())
276         return *found->second;
277     // otherwise, it wasn't found, add it
278     m_regex_map[r].reset (new regex(r.c_str()));
279     m_shadingsys.m_stat_regexes += 1;
280     // std::cerr << "Made new regex for " << r << "\n";
281     return *m_regex_map[r];
282 }
283 
284 
285 
286 bool
osl_get_attribute(ShaderGlobals * sg,void * objdata,int dest_derivs,ustring obj_name,ustring attr_name,int array_lookup,int index,TypeDesc attr_type,void * attr_dest)287 ShadingContext::osl_get_attribute (ShaderGlobals *sg, void *objdata,
288                                    int dest_derivs,
289                                    ustring obj_name, ustring attr_name,
290                                    int array_lookup, int index,
291                                    TypeDesc attr_type, void *attr_dest)
292 {
293 #if 0
294     // Change the #if's below if you want to
295     OIIO::Timer timer;
296 #endif
297     bool ok;
298 
299     if (array_lookup)
300         ok = renderer()->get_array_attribute (sg, dest_derivs,
301                                               obj_name, attr_type,
302                                               attr_name, index, attr_dest);
303     else
304         ok = renderer()->get_attribute (sg, dest_derivs,
305                                         obj_name, attr_type,
306                                         attr_name, attr_dest);
307 
308 #if 0
309     double time = timer();
310     shadingsys().m_stat_getattribute_time += time;
311     if (!ok)
312         shadingsys().m_stat_getattribute_fail_time += time;
313     shadingsys().m_stat_getattribute_calls += 1;
314 #endif
315 //    std::cout << "getattribute! '" << obj_name << "' " << attr_name << ' ' << attr_type.c_str() << " ok=" << ok << ", objdata was " << objdata << "\n";
316     return ok;
317 }
318 
319 
320 
321 OSL_SHADEOP void
osl_incr_layers_executed(ShaderGlobals * sg)322 osl_incr_layers_executed (ShaderGlobals *sg)
323 {
324     ShadingContext *ctx = (ShadingContext *)sg->context;
325     ctx->incr_layers_executed ();
326 }
327 
328 
329 OSL_NAMESPACE_EXIT
330