void ShaderInstance::copy_code_from_master () { ASSERT (m_instops.empty() && m_instargs.empty()); // reserve with enough room for a few insertions m_instops.reserve (master()->m_ops.size()+10); m_instargs.reserve (master()->m_args.size()+10); m_instops = master()->m_ops; m_instargs = master()->m_args; // We already have the symbols on [0,lastparam). Now copy the rest. off_t symmem = vectorbytes(m_instsymbols); ASSERT (m_master->m_lastparam < 0 || m_instsymbols.size() == (size_t)m_master->m_lastparam); ASSERT (m_instsymbols.size() <= m_master->m_symbols.size()); m_instsymbols.reserve (m_master->m_symbols.size()); for (size_t i = m_instsymbols.size(), e = m_master->m_symbols.size(); i < e; ++i) m_instsymbols.push_back (m_master->m_symbols[i]); ASSERT (m_instsymbols.size() == m_master->m_symbols.size()); // adjust stats symmem = vectorbytes(m_instsymbols) - symmem; // just the new mem { spin_lock lock (shadingsys().m_stat_mutex); shadingsys().m_stat_mem_inst_syms += symmem; shadingsys().m_stat_mem_inst += symmem; shadingsys().m_stat_memory += symmem; } }
void ShadingContext::process_errors () const { size_t nerrors = m_buffered_errors.size(); if (! nerrors) return; // Use a mutex to make sure output from different threads stays // together, at least for one shader invocation, rather than being // interleaved with other threads. lock_guard lock (buffered_errors_mutex); for (size_t i = 0; i < nerrors; ++i) { switch (m_buffered_errors[i].first) { case ErrorHandler::EH_MESSAGE : case ErrorHandler::EH_DEBUG : shadingsys().message (m_buffered_errors[i].second); break; case ErrorHandler::EH_INFO : shadingsys().info (m_buffered_errors[i].second); break; case ErrorHandler::EH_WARNING : shadingsys().warning (m_buffered_errors[i].second); break; case ErrorHandler::EH_ERROR : case ErrorHandler::EH_SEVERE : shadingsys().error (m_buffered_errors[i].second); break; default: break; } } m_buffered_errors.clear(); }
void ShaderInstance::compute_run_lazily (const ShaderGroup &group) { if (shadingsys().m_lazylayers) { // lazylayers option turned on: unconditionally run shaders with no // outgoing connections ("root" nodes, including the last in the // group) or shaders that alter global variables (unless // 'lazyglobals' is turned on). if (shadingsys().m_lazyglobals) { if (group[group.nlayers()-1] == this) run_lazily (false); // force run of last group else run_lazily ((outgoing_connections() && ! renderer_outputs()) || empty_instance() || merged_unused()); } else run_lazily (outgoing_connections() && ! writes_globals() && ! renderer_outputs()); #if 0 // Suggested warning below... but are there use cases where people // want these to run (because they will extract the results they // want from output params)? if (! outgoing_connections() && ! empty_instance() && ! writes_globals() && ! renderer_outputs()) shadingsys().warning ("Layer \"%s\" (shader %s) will run even though it appears to have no used results", layername(), shadername()); #endif } else { // lazylayers option turned off: never run lazily run_lazily (false); } }
void ShaderInstance::copy_code_from_master (ShaderGroup &group) { ASSERT (m_instops.empty() && m_instargs.empty()); // reserve with enough room for a few insertions m_instops.reserve (master()->m_ops.size()+10); m_instargs.reserve (master()->m_args.size()+10); m_instops = master()->m_ops; m_instargs = master()->m_args; // Copy the symbols from the master ASSERT (m_instsymbols.size() == 0 && "should not have copied m_instsymbols yet"); m_instsymbols = m_master->m_symbols; // Copy the instance override data // Also set the renderer_output flags where needed. ASSERT (m_instoverrides.size() == (size_t)std::max(0,lastparam())); ASSERT (m_instsymbols.size() >= (size_t)std::max(0,lastparam())); if (m_instoverrides.size()) { for (size_t i = 0, e = lastparam(); i < e; ++i) { Symbol *si = &m_instsymbols[i]; if (m_instoverrides[i].valuesource() == Symbol::DefaultVal) { // Fix the length of any default-value variable length array // parameters. if (si->typespec().is_unsized_array()) si->arraylen (si->initializers()); } else { if (m_instoverrides[i].arraylen()) si->arraylen (m_instoverrides[i].arraylen()); si->valuesource (m_instoverrides[i].valuesource()); si->connected_down (m_instoverrides[i].connected_down()); si->lockgeom (m_instoverrides[i].lockgeom()); si->data (param_storage(i)); } if (shadingsys().is_renderer_output (layername(), si->name(), &group)) { si->renderer_output (true); renderer_outputs (true); } } } evaluate_writes_globals_and_userdata_params (); off_t symmem = vectorbytes(m_instsymbols) - vectorbytes(m_instoverrides); SymOverrideInfoVec().swap (m_instoverrides); // free it // adjust stats { spin_lock lock (shadingsys().m_stat_mutex); shadingsys().m_stat_mem_inst_syms += symmem; shadingsys().m_stat_mem_inst += symmem; shadingsys().m_stat_memory += symmem; } }
void ShaderInstance::add_connection (int srclayer, const ConnectedParam &srccon, const ConnectedParam &dstcon) { off_t oldmem = vectorbytes(m_connections); m_connections.push_back (Connection (srclayer, srccon, dstcon)); // adjust stats off_t mem = vectorbytes(m_connections) - oldmem; { spin_lock lock (shadingsys().m_stat_mutex); shadingsys().m_stat_mem_inst_connections += mem; shadingsys().m_stat_mem_inst += mem; shadingsys().m_stat_memory += mem; } }
int ShadingContext::dict_find (int nodeID, ustring query) { if (! m_dictionary) { m_dictionary = new Dictionary (shadingsys()); } return m_dictionary->dict_find (nodeID, query); }
int ShadingContext::dict_find (ustring dictionaryname, ustring query) { if (! m_dictionary) { m_dictionary = new Dictionary (shadingsys()); } return m_dictionary->dict_find (dictionaryname, query); }
void ShadingContext::record_error (ErrorHandler::ErrCode code, const std::string &text) const { m_buffered_errors.push_back (ErrorItem(code,text)); // If we aren't buffering, just process immediately if (! shadingsys().m_buffer_printf) process_errors (); }
void ShaderInstance::add_connection (int srclayer, const ConnectedParam &srccon, const ConnectedParam &dstcon) { // specialize symbol in case of dstcon is an unsized array if (dstcon.type.is_unsized_array()) { SymOverrideInfo *so = &m_instoverrides[dstcon.param]; so->arraylen(srccon.type.arraylength()); const TypeDesc& type = srccon.type.simpletype(); // Skip structs for now, they're just placeholders /*if (t.is_structure()) { } else*/ if (type.basetype == TypeDesc::FLOAT) { so->dataoffset((int) m_fparams.size()); expand (m_fparams,type.size()); } else if (type.basetype == TypeDesc::INT) { so->dataoffset((int) m_iparams.size()); expand (m_iparams, type.size()); } else if (type.basetype == TypeDesc::STRING) { so->dataoffset((int) m_sparams.size()); expand (m_sparams, type.size()); }/* else if (t.is_closure()) { // Closures are pointers, so we allocate a string default taking // adventage of their default being NULL as well. so->dataoffset((int) m_sparams.size()); expand (m_sparams, type.size()); }*/ else { ASSERT (0 && "unexpected type"); } } off_t oldmem = vectorbytes(m_connections); m_connections.push_back (Connection (srclayer, srccon, dstcon)); // adjust stats off_t mem = vectorbytes(m_connections) - oldmem; { spin_lock lock (shadingsys().m_stat_mutex); shadingsys().m_stat_mem_inst_connections += mem; shadingsys().m_stat_mem_inst += mem; shadingsys().m_stat_memory += mem; } }
void ShaderInstance::make_symbol_room (size_t moresyms) { size_t oldsize = m_instsymbols.capacity(); if (oldsize < m_instsymbols.size()+moresyms) { // Allocate a bit more than we need, so that most times we don't // need to reallocate. But don't be wasteful by doubling or // anything like that, since we only expect a few to be added. const size_t extra_room = 10; size_t newsize = m_instsymbols.size() + moresyms + extra_room; m_instsymbols.reserve (newsize); // adjust stats spin_lock lock (shadingsys().m_stat_mutex); size_t mem = (newsize-oldsize) * sizeof(Symbol); shadingsys().m_stat_mem_inst_syms += mem; shadingsys().m_stat_mem_inst += mem; shadingsys().m_stat_memory += mem; } }
bool ShadingContext::execute (ShaderUse use, ShaderGroup &sgroup, ShaderGlobals &ssg, bool run) { DASSERT (use == ShadUseSurface); // FIXME m_curuse = use; m_attribs = &sgroup; // Optimize if we haven't already if (sgroup.nlayers()) { sgroup.start_running (); if (! sgroup.optimized()) { shadingsys().optimize_group (sgroup); if (shadingsys().m_greedyjit && shadingsys().m_groups_to_compile_count) { // If we are greedily JITing, optimize/JIT everything now shadingsys().optimize_all_groups (); } } if (sgroup.does_nothing()) return false; } else { // empty shader - nothing to do! return false; } // Allocate enough space on the heap size_t heap_size_needed = sgroup.llvm_groupdata_size(); if (heap_size_needed > m_heap.size()) { if (shadingsys().debug()) shadingsys().info (" ShadingContext %p growing heap to %llu", this, (unsigned long long) heap_size_needed); m_heap.resize (heap_size_needed); } // Zero out the heap memory we will be using if (shadingsys().m_clearmemory) memset (&m_heap[0], 0, heap_size_needed); // Set up closure storage m_closure_pool.clear(); // Clear the message blackboard m_messages.clear (); // Clear miscellaneous scratch space m_scratch_pool.clear (); if (run) { ssg.context = this; ssg.Ci = NULL; RunLLVMGroupFunc run_func = sgroup.llvm_compiled_version(); DASSERT (run_func); DASSERT (sgroup.llvm_groupdata_size() <= m_heap.size()); run_func (&ssg, &m_heap[0]); } return true; }
void ShaderInstance::copy_code_from_master () { ASSERT (m_instops.empty() && m_instargs.empty()); // reserve with enough room for a few insertions m_instops.reserve (master()->m_ops.size()+10); m_instargs.reserve (master()->m_args.size()+10); m_instops = master()->m_ops; m_instargs = master()->m_args; // Copy the symbols from the master ASSERT (m_instsymbols.size() == 0 && "should not have copied m_instsymbols yet"); m_instsymbols = m_master->m_symbols; // Copy the instance override data ASSERT (m_instoverrides.size() == (size_t)std::max(0,lastparam())); ASSERT (m_instsymbols.size() >= (size_t)std::max(0,lastparam())); if (m_instoverrides.size()) { for (size_t i = 0, e = lastparam(); i < e; ++i) { if (m_instoverrides[i].valuesource() != Symbol::DefaultVal) { Symbol *si = &m_instsymbols[i]; si->data (param_storage(i)); si->valuesource (m_instoverrides[i].valuesource()); si->connected_down (m_instoverrides[i].connected_down()); si->lockgeom (m_instoverrides[i].lockgeom()); } } } off_t symmem = vectorbytes(m_instsymbols) - vectorbytes(m_instoverrides); SymOverrideInfoVec().swap (m_instoverrides); // free it // adjust stats { spin_lock lock (shadingsys().m_stat_mutex); shadingsys().m_stat_mem_inst_syms += symmem; shadingsys().m_stat_mem_inst += symmem; shadingsys().m_stat_memory += symmem; } }
bool ShadingContext::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) { #if 0 // Change the #if's below if you want to OIIO::Timer timer; #endif bool ok; for (int i = 0; i < FAILED_ATTRIBS; ++i) { if ((obj_name || m_failed_attribs[i].objdata == objdata) && m_failed_attribs[i].attr_name == attr_name && m_failed_attribs[i].obj_name == obj_name && m_failed_attribs[i].attr_type == attr_type && m_failed_attribs[i].array_lookup == array_lookup && m_failed_attribs[i].index == index && m_failed_attribs[i].objdata) { #if 0 double time = timer(); shadingsys().m_stat_getattribute_time += time; shadingsys().m_stat_getattribute_fail_time += time; shadingsys().m_stat_getattribute_calls += 1; #endif return false; } } if (array_lookup) ok = renderer()->get_array_attribute (sg, dest_derivs, obj_name, attr_type, attr_name, index, attr_dest); else ok = renderer()->get_attribute (sg, dest_derivs, obj_name, attr_type, attr_name, attr_dest); if (!ok) { int i = m_next_failed_attrib; m_failed_attribs[i].objdata = objdata; m_failed_attribs[i].obj_name = obj_name; m_failed_attribs[i].attr_name = attr_name; m_failed_attribs[i].attr_type = attr_type; m_failed_attribs[i].array_lookup = array_lookup; m_failed_attribs[i].index = index; m_next_failed_attrib = (i == FAILED_ATTRIBS-1) ? 0 : (i+1); } #if 0 double time = timer(); shadingsys().m_stat_getattribute_time += time; if (!ok) shadingsys().m_stat_getattribute_fail_time += time; shadingsys().m_stat_getattribute_calls += 1; #endif // std::cout << "getattribute! '" << obj_name << "' " << attr_name << ' ' << attr_type.c_str() << " ok=" << ok << ", objdata was " << objdata << "\n"; return ok; }
ShaderMaster::~ShaderMaster () { // Adjust statistics size_t opmem = vectorbytes (m_ops); size_t argmem = vectorbytes (m_args); size_t symmem = vectorbytes (m_symbols); size_t defaultmem = vectorbytes (m_idefaults) + vectorbytes (m_fdefaults) + vectorbytes (m_sdefaults); size_t constmem = vectorbytes (m_iconsts) + vectorbytes (m_fconsts) + vectorbytes (m_sconsts); size_t totalmem = (opmem + argmem + symmem + defaultmem + constmem + sizeof(ShaderMaster)); { ShadingSystemImpl &ss (shadingsys()); OIIO::spin_lock lock (ss.m_stat_mutex); ss.m_stat_mem_master_ops -= opmem; ss.m_stat_mem_master_args -= argmem; ss.m_stat_mem_master_syms -= symmem; ss.m_stat_mem_master_defaults -= defaultmem; ss.m_stat_mem_master_consts -= constmem; ss.m_stat_mem_master -= totalmem; ss.m_stat_memory -= totalmem; } }
bool ShadingContext::execute (ShaderGroup &sgroup, ShaderGlobals &ssg, bool run) { m_attribs = &sgroup; // Optimize if we haven't already if (sgroup.nlayers()) { sgroup.start_running (); if (! sgroup.optimized()) { shadingsys().optimize_group (sgroup); if (shadingsys().m_greedyjit && shadingsys().m_groups_to_compile_count) { // If we are greedily JITing, optimize/JIT everything now shadingsys().optimize_all_groups (); } } if (sgroup.does_nothing()) return false; } else { // empty shader - nothing to do! return false; } int profile = shadingsys().m_profile; OIIO::Timer timer (profile); // Allocate enough space on the heap size_t heap_size_needed = sgroup.llvm_groupdata_size(); if (heap_size_needed > m_heap.size()) { if (shadingsys().debug()) info (" ShadingContext %p growing heap to %llu", this, (unsigned long long) heap_size_needed); m_heap.resize (heap_size_needed); } // Zero out the heap memory we will be using if (shadingsys().m_clearmemory) memset (&m_heap[0], 0, heap_size_needed); // Set up closure storage m_closure_pool.clear(); // Clear the message blackboard m_messages.clear (); // Clear miscellaneous scratch space m_scratch_pool.clear (); if (run) { ssg.context = this; ssg.renderer = renderer(); ssg.Ci = NULL; RunLLVMGroupFunc run_func = sgroup.llvm_compiled_version(); DASSERT (run_func); DASSERT (sgroup.llvm_groupdata_size() <= m_heap.size()); run_func (&ssg, &m_heap[0]); } // Process any queued up error messages, warnings, printfs from shaders process_errors (); if (profile) { long long ticks = timer.ticks(); shadingsys().m_stat_total_shading_time_ticks += ticks; sgroup.m_stat_total_shading_time_ticks += ticks; } return true; }
bool ShaderInstance::mergeable (const ShaderInstance &b, const ShaderGroup &g) const { // Must both be instances of the same master -- very fast early-out // for most potential pair comparisons. if (master() != b.master()) return false; // If the shaders haven't been optimized yet, they don't yet have // their own symbol tables and instructions (they just refer to // their unoptimized master), but they may have an "instance // override" vector that describes which parameters have // instance-specific values or connections. bool optimized = (m_instsymbols.size() != 0 || m_instops.size() != 0); // Same instance overrides if (m_instoverrides.size() || b.m_instoverrides.size()) { ASSERT (! optimized); // should not be post-opt ASSERT (m_instoverrides.size() == b.m_instoverrides.size()); for (size_t i = 0, e = m_instoverrides.size(); i < e; ++i) { if ((m_instoverrides[i].valuesource() == Symbol::DefaultVal || m_instoverrides[i].valuesource() == Symbol::InstanceVal) && (b.m_instoverrides[i].valuesource() == Symbol::DefaultVal || b.m_instoverrides[i].valuesource() == Symbol::InstanceVal)) { // If both params are defaults or instances, let the // instance parameter value checking below handle // things. No need to reject default-vs-instance // mismatches if the actual values turn out to be the // same later. continue; } if (! (equivalent(m_instoverrides[i], b.m_instoverrides[i]))) { const Symbol *sym = mastersymbol(i); // remember, it's pre-opt const Symbol *bsym = b.mastersymbol(i); if (! sym->everused_in_group() && ! bsym->everused_in_group()) continue; return false; } // But still, if they differ in their lockgeom'edness, we can't // merge the instances. if (m_instoverrides[i].lockgeom() != b.m_instoverrides[i].lockgeom()) { return false; } } } // Make sure that the two nodes have the same parameter values. If // the group has already been optimized, it's got an // instance-specific symbol table to check; but if it hasn't been // optimized, we check the symbol table in the master. for (int i = firstparam(); i < lastparam(); ++i) { const Symbol *sym = optimized ? symbol(i) : mastersymbol(i); if (! sym->everused_in_group()) continue; if (sym->typespec().is_closure()) continue; // Closures can't have instance override values if ((sym->valuesource() == Symbol::InstanceVal || sym->valuesource() == Symbol::DefaultVal) && memcmp (param_storage(i), b.param_storage(i), sym->typespec().simpletype().size())) { return false; } } if (m_run_lazily != b.m_run_lazily) { return false; } // The connection list need to be the same for the two shaders. if (m_connections.size() != b.m_connections.size()) { return false; } if (m_connections != b.m_connections) { return false; } // Make sure system didn't ask for instances that query userdata to be // immune from instance merging. if (! shadingsys().m_opt_merge_instances_with_userdata && (userdata_params() || b.userdata_params())) { return false; } // If there are no "local" ops or symbols, this instance hasn't been // optimized yet. In that case, we've already done enough checking, // since the masters being the same and having the same instance // params and connections is all it takes. The rest (below) only // comes into play after instances are more fully elaborated from // their masters in order to be optimized. if (!optimized) { return true; } // Same symbol table if (! equivalent (m_instsymbols, b.m_instsymbols)) { return false; } // Same opcodes to run if (! equivalent (m_instops, b.m_instops)) { return false; } // Same arguments to the ops if (m_instargs != b.m_instargs) { return false; } // Parameter and code ranges if (m_firstparam != b.m_firstparam || m_lastparam != b.m_lastparam || m_maincodebegin != b.m_maincodebegin || m_maincodeend != b.m_maincodeend || m_Psym != b.m_Psym || m_Nsym != b.m_Nsym) { return false; } // Nothing left to check, they must be identical! return true; }