]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Technique.cxx
Partial fix for crash in SGPropertyNode::fireValueChanged
[simgear.git] / simgear / scene / material / Technique.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <simgear_config.h>
4 #endif
5
6 #include "Technique.hxx"
7 #include "Pass.hxx"
8 #include "EffectCullVisitor.hxx"
9
10 #include <boost/foreach.hpp>
11
12 #include <iterator>
13 #include <vector>
14 #include <string>
15
16 #include <osg/GLExtensions>
17 #include <osg/GL2Extensions>
18 #include <osg/Math>
19 #include <osg/Texture2D>
20 #include <osg/CopyOp>
21 #include <osgUtil/CullVisitor>
22
23 #include <osgDB/Registry>
24 #include <osgDB/Input>
25 #include <osgDB/ParameterOutput>
26
27 #include <simgear/props/props.hxx>
28 #include <simgear/structure/OSGUtils.hxx>
29
30 namespace simgear
31 {
32 using namespace osg;
33 using namespace osgUtil;
34
35 namespace
36 {
37
38 struct ValidateOperation : GraphicsOperation
39 {
40     ValidateOperation(Technique* technique_)
41         : GraphicsOperation(opName, false), technique(technique_)
42     {
43     }
44     virtual void operator() (GraphicsContext* gc);
45     osg::ref_ptr<Technique> technique;
46     static const std::string opName;
47 };
48
49 const std::string ValidateOperation::opName("ValidateOperation");
50
51
52 void ValidateOperation::operator() (GraphicsContext* gc)
53 {
54     technique->validateInContext(gc);
55 }
56 }
57
58 Technique::Technique(bool alwaysValid)
59     : _alwaysValid(alwaysValid), _contextIdLocation(-1)
60 {
61 }
62
63 Technique::Technique(const Technique& rhs, const osg::CopyOp& copyop) :
64     osg::Object(rhs,copyop),
65     _contextMap(rhs._contextMap), _alwaysValid(rhs._alwaysValid),
66     _shadowingStateSet(copyop(rhs._shadowingStateSet.get())),
67     _validExpression(rhs._validExpression),
68     _contextIdLocation(rhs._contextIdLocation)
69 {
70     for (std::vector<ref_ptr<Pass> >::const_iterator itr = rhs.passes.begin(),
71              end = rhs.passes.end();
72          itr != end;
73          ++itr)
74         passes.push_back(static_cast<Pass*>(copyop(itr->get())));
75 }
76
77 Technique::~Technique()
78 {
79 }
80
81 Technique::Status Technique::valid(osg::RenderInfo* renderInfo)
82 {
83     if (_alwaysValid)
84         return VALID;
85     unsigned contextID = renderInfo->getContextID();
86     ContextInfo& contextInfo = _contextMap[contextID];
87     Status status = contextInfo.valid();
88     if (status != UNKNOWN)
89         return status;
90     Status newStatus = QUERY_IN_PROGRESS;
91     // lock and spawn validity check.
92     if (!contextInfo.valid.compareAndSwap(status, newStatus)) {
93         // Lost the race with another thread spawning a request
94         return contextInfo.valid();
95     }
96     ref_ptr<ValidateOperation> validOp = new ValidateOperation(this);
97     GraphicsContext* context = renderInfo->getState()->getGraphicsContext();
98     GraphicsThread* thread = context->getGraphicsThread();
99     if (thread)
100         thread->add(validOp.get());
101     else
102         context->add(validOp.get());
103     return newStatus;
104 }
105
106 Technique::Status Technique::getValidStatus(const RenderInfo* renderInfo) const
107 {
108     if (_alwaysValid)
109         return VALID;
110     ContextInfo& contextInfo = _contextMap[renderInfo->getContextID()];
111     return contextInfo.valid();
112 }
113
114 void Technique::validateInContext(GraphicsContext* gc)
115 {
116     unsigned int contextId = gc->getState()->getContextID();
117     ContextInfo& contextInfo = _contextMap[contextId];
118     Status oldVal = contextInfo.valid();
119     Status newVal = INVALID;
120     expression::FixedLengthBinding<1> binding;
121     binding.getBindings()[_contextIdLocation] = expression::Value((int) contextId);
122     if (_validExpression->getValue(&binding))
123         newVal = VALID;
124     contextInfo.valid.compareAndSwap(oldVal, newVal);
125 }
126
127 namespace
128 {
129 enum NumDrawables {NUM_DRAWABLES = 128};
130 }
131
132 EffectGeode::DrawablesIterator
133 Technique::processDrawables(const EffectGeode::DrawablesIterator& begin,
134                             const EffectGeode::DrawablesIterator& end,
135                             CullVisitor* cv,
136                             bool isCullingActive)
137 {
138     RefMatrix& matrix = *cv->getModelViewMatrix();
139     float depth[NUM_DRAWABLES];
140     EffectGeode::DrawablesIterator itr = begin;
141     bool computeNearFar
142         = cv->getComputeNearFarMode() != CullVisitor::DO_NOT_COMPUTE_NEAR_FAR;
143     for (int i = 0; i < NUM_DRAWABLES && itr != end; ++itr, ++i)
144     {
145       Drawable* drawable = itr->get();
146
147 #if OSG_VERSION_LESS_THAN(3,3,2)
148       const BoundingBox& bb = drawable->getBound();
149       osg::Drawable::CullCallback* cull = drawable->getCullCallback();
150 #else
151       const BoundingBox& bb = drawable->getBoundingBox();
152       osg::Drawable::CullCallback* cull =
153         dynamic_cast<osg::Drawable::CullCallback*>(drawable->getCullCallback());
154 #endif
155
156       if(   (cull && cull->cull(cv, drawable, &cv->getRenderInfo()))
157          || (isCullingActive && cv->isCulled(bb)) )
158       {
159         depth[i] = FLT_MAX;
160         continue;
161       }
162
163       if( computeNearFar && bb.valid() )
164       {
165         if( !cv->updateCalculatedNearFar(matrix, *drawable, false) )
166         {
167           depth[i] = FLT_MAX;
168           continue;
169         }
170       }
171
172       depth[i] = bb.valid()
173                ? cv->getDistanceFromEyePoint(bb.center(), false)
174                : 0.0f;
175       if( isNaN(depth[i]) )
176         depth[i] = FLT_MAX;
177     }
178     EffectCullVisitor* ecv = dynamic_cast<EffectCullVisitor*>( cv );
179     EffectGeode::DrawablesIterator drawablesEnd = itr;
180     BOOST_FOREACH(ref_ptr<Pass>& pass, passes)
181     {
182         osg::ref_ptr<osg::StateSet> ss = pass;
183         if (ecv && ( ! pass->getBufferUnitList().empty() || ! pass->getPositionedUniformMap().empty() ) ) {
184             ss = static_cast<osg::StateSet*>(
185                 pass->clone( osg::CopyOp( ( ! pass->getBufferUnitList().empty() ?
186                                                         osg::CopyOp::DEEP_COPY_TEXTURES :
187                                                         osg::CopyOp::SHALLOW_COPY ) |
188                                            ( ! pass->getPositionedUniformMap().empty() ?
189                                                         osg::CopyOp::DEEP_COPY_UNIFORMS :
190                                                         osg::CopyOp::SHALLOW_COPY ) )
191                 )
192             );
193             for (Pass::BufferUnitList::const_iterator ii = pass->getBufferUnitList().begin();
194                     ii != pass->getBufferUnitList().end();
195                     ++ii) {
196                 osg::Texture2D* tex = ecv->getBuffer(ii->second);
197                 if (tex != 0)
198                     ss->setTextureAttributeAndModes( ii->first, tex );
199             }
200             for (Pass::PositionedUniformMap::const_iterator ii = pass->getPositionedUniformMap().begin();
201                     ii != pass->getPositionedUniformMap().end();
202                     ++ii) {
203                 osg::RefMatrix* mv = cv->getModelViewMatrix();
204                 osg::Vec4 v = ii->second * *mv;
205                 ss->getUniform(ii->first)->set( v );
206             }
207         }
208         cv->pushStateSet(ss);
209         int i = 0;
210         for (itr = begin; itr != drawablesEnd; ++itr, ++i) {
211             if (depth[i] != FLT_MAX)
212                 cv->addDrawableAndDepth(itr->get(), &matrix, depth[i]);
213         }
214         cv->popStateSet();
215     }
216     return drawablesEnd;
217 }
218
219 void Technique::resizeGLObjectBuffers(unsigned int maxSize)
220 {
221     if (_shadowingStateSet.valid())
222         _shadowingStateSet->resizeGLObjectBuffers(maxSize);
223     BOOST_FOREACH(ref_ptr<Pass>& pass, passes) {
224         pass->resizeGLObjectBuffers(maxSize);
225     }
226     _contextMap.resize(maxSize);
227 }
228
229 void Technique::releaseGLObjects(osg::State* state) const
230 {
231     if (_shadowingStateSet.valid())
232         _shadowingStateSet->releaseGLObjects(state);
233     BOOST_FOREACH(const ref_ptr<Pass>& pass, passes)
234     {
235         pass->releaseGLObjects(state);
236     }
237     if (state == 0) {
238         for (int i = 0; i < (int)_contextMap.size(); ++i) {
239             ContextInfo& info = _contextMap[i];
240             Status oldVal = info.valid();
241             info.valid.compareAndSwap(oldVal, UNKNOWN);
242         }
243     } else {
244         ContextInfo& info = _contextMap[state->getContextID()];
245         Status oldVal = info.valid();
246         info.valid.compareAndSwap(oldVal, UNKNOWN);
247     }
248 }
249
250 void Technique::setValidExpression(SGExpressionb* exp,
251                                    const simgear::expression
252                                    ::BindingLayout& layout)
253 {
254     using namespace simgear::expression;
255     _validExpression = exp;
256     VariableBinding binding;
257     if (layout.findBinding("__contextId", binding))
258         _contextIdLocation = binding.location;
259 }
260
261 class GLVersionExpression : public SGExpression<float>
262 {
263 public:
264     void eval(float& value, const expression::Binding*) const
265     {
266         value = getGLVersionNumber();
267     }
268 };
269
270 Expression* glVersionParser(const SGPropertyNode* exp,
271                             expression::Parser* parser)
272 {
273     return new GLVersionExpression();
274 }
275
276 expression::ExpParserRegistrar glVersionRegistrar("glversion", glVersionParser);
277
278 class ExtensionSupportedExpression
279     : public GeneralNaryExpression<bool, int>
280 {
281 public:
282     ExtensionSupportedExpression() {}
283     ExtensionSupportedExpression(const std::string& extString)
284         : _extString(extString)
285     {
286     }
287     const std::string& getExtensionString() { return _extString; }
288     void setExtensionString(const std::string& extString) { _extString = extString; }
289     void eval(bool&value, const expression::Binding* b) const
290     {
291         int contextId = getOperand(0)->getValue(b);
292         value = isGLExtensionSupported((unsigned)contextId, _extString.c_str());
293     }
294 protected:
295     std::string _extString;
296 };
297
298 Expression* extensionSupportedParser(const SGPropertyNode* exp,
299                                      expression::Parser* parser)
300 {
301     if (exp->getType() == props::STRING
302         || exp->getType() == props::UNSPECIFIED) {
303         ExtensionSupportedExpression* esp
304             = new ExtensionSupportedExpression(exp->getStringValue());
305         int location = parser->getBindingLayout().addBinding("__contextId",
306                                                              expression::INT);
307         VariableExpression<int>* contextExp
308             = new VariableExpression<int>(location);
309         esp->addOperand(contextExp);
310         return esp;
311     }
312     throw expression::ParseError("extension-supported expression has wrong type");
313 }
314
315 expression::ExpParserRegistrar
316 extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
317
318 class GLShaderLanguageExpression : public GeneralNaryExpression<float, int>
319 {
320 public:
321     void eval(float& value, const expression::Binding* b) const
322     {
323         value = 0.0f;
324         int contextId = getOperand(0)->getValue(b);
325         GL2Extensions* extensions
326             = GL2Extensions::Get(static_cast<unsigned>(contextId), true);
327         if (!extensions)
328             return;
329         if (!extensions->isGlslSupported())
330             return;
331         value = extensions->getLanguageVersion();
332     }
333 };
334
335 Expression* shaderLanguageParser(const SGPropertyNode* exp,
336                                  expression::Parser* parser)
337 {
338     GLShaderLanguageExpression* slexp = new GLShaderLanguageExpression;
339     int location = parser->getBindingLayout().addBinding("__contextId",
340                                                          expression::INT);
341     VariableExpression<int>* contextExp = new VariableExpression<int>(location);
342     slexp->addOperand(contextExp);
343     return slexp;
344 }
345
346 expression::ExpParserRegistrar shaderLanguageRegistrar("shader-language",
347                                                        shaderLanguageParser);
348
349 class GLSLSupportedExpression : public GeneralNaryExpression<bool, int>
350 {
351 public:
352    void eval(bool& value, const expression::Binding* b) const
353    {
354        value = false;
355        int contextId = getOperand(0)->getValue(b);
356        GL2Extensions* extensions
357            = GL2Extensions::Get(static_cast<unsigned>(contextId), true);
358        if (!extensions)
359            return;
360        value = extensions->isGlslSupported();
361    }
362 };
363
364 Expression* glslSupportedParser(const SGPropertyNode* exp,
365                                 expression::Parser* parser)
366 {
367    GLSLSupportedExpression* sexp = new GLSLSupportedExpression;
368    int location = parser->getBindingLayout().addBinding("__contextId",
369                                                         expression::INT);
370    VariableExpression<int>* contextExp = new VariableExpression<int>(location);
371    sexp->addOperand(contextExp);
372    return sexp;
373 }
374
375 expression::ExpParserRegistrar glslSupportedRegistrar("glsl-supported",
376                                                       glslSupportedParser);
377                                                                                                               
378 void Technique::setGLExtensionsPred(float glVersion,
379                                     const std::vector<std::string>& extensions)
380 {
381     using namespace std;
382     using namespace expression;
383     BindingLayout layout;
384     int contextLoc = layout.addBinding("__contextId", INT);
385     VariableExpression<int>* contextExp
386         = new VariableExpression<int>(contextLoc);
387     SGExpression<bool>* versionTest
388         = makePredicate<std::less_equal>(new SGConstExpression<float>(glVersion),
389                         new GLVersionExpression);
390     AndExpression* extensionsExp = 0;
391     for (vector<string>::const_iterator itr = extensions.begin(),
392              e = extensions.end();
393          itr != e;
394          ++itr) {
395         if (!extensionsExp)
396             extensionsExp = new AndExpression;
397         ExtensionSupportedExpression* supported
398             = new ExtensionSupportedExpression(*itr);
399         supported->addOperand(contextExp);
400         extensionsExp->addOperand(supported);
401     }
402     SGExpressionb* predicate = 0;
403     if (extensionsExp) {
404         OrExpression* orExp = new OrExpression;
405         orExp->addOperand(versionTest);
406         orExp->addOperand(extensionsExp);
407         predicate = orExp;
408     } else {
409         predicate = versionTest;
410     }
411     setValidExpression(predicate, layout);
412 }
413
414 void Technique::refreshValidity()
415 {
416     for (int i = 0; i < (int)_contextMap.size(); ++i) {
417         ContextInfo& info = _contextMap[i];
418         Status oldVal = info.valid();
419         // What happens if we lose the race here?
420         info.valid.compareAndSwap(oldVal, UNKNOWN);
421     }
422 }
423
424 bool Technique_writeLocalData(const Object& obj, osgDB::Output& fw)
425 {
426     const Technique& tniq = static_cast<const Technique&>(obj);
427     fw.indent() << "alwaysValid "
428                 << (tniq.getAlwaysValid() ? "TRUE\n" : "FALSE\n");
429 #if 0
430     fw.indent() << "glVersion " << tniq.getGLVersion() << "\n";
431 #endif
432     if (tniq.getShadowingStateSet()) {
433         fw.indent() << "shadowingStateSet\n";
434         fw.writeObject(*tniq.getShadowingStateSet());
435     }
436     fw.indent() << "num_passes " << tniq.passes.size() << "\n";
437     BOOST_FOREACH(const ref_ptr<Pass>& pass, tniq.passes) {
438         fw.writeObject(*pass);
439     }
440     return true;
441 }
442
443 namespace
444 {
445 osgDB::RegisterDotOsgWrapperProxy TechniqueProxy
446 (
447     new Technique,
448     "simgear::Technique",
449     "Object simgear::Technique",
450     0,
451     &Technique_writeLocalData
452     );
453 }
454 }