]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Technique.cxx
Fix failing BucketBox test
[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         Drawable* drawable = itr->get();
145         const BoundingBox& bb = drawable->getBound();
146         if ((drawable->getCullCallback()
147              && drawable->getCullCallback()->cull(cv, drawable,
148                                                   &cv->getRenderInfo()))
149             || (isCullingActive && cv->isCulled(bb))) {
150             depth[i] = FLT_MAX;
151             continue;
152         }
153         if (computeNearFar && bb.valid()) {
154             if (!cv->updateCalculatedNearFar(matrix, *drawable, false)) {
155                 depth[i] = FLT_MAX;
156                 continue;
157             }
158         }
159         depth[i] = (bb.valid()
160                     ? cv->getDistanceFromEyePoint(bb.center(), false)
161                     : 0.0f);
162         if (isNaN(depth[i]))
163             depth[i] = FLT_MAX;
164     }
165     EffectCullVisitor* ecv = dynamic_cast<EffectCullVisitor*>( cv );
166     EffectGeode::DrawablesIterator drawablesEnd = itr;
167     BOOST_FOREACH(ref_ptr<Pass>& pass, passes)
168     {
169         osg::ref_ptr<osg::StateSet> ss = pass;
170         if (ecv && ( ! pass->getBufferUnitList().empty() || ! pass->getPositionedUniformMap().empty() ) ) {
171             ss = static_cast<osg::StateSet*>(
172                 pass->clone( osg::CopyOp( ( ! pass->getBufferUnitList().empty() ?
173                                                         osg::CopyOp::DEEP_COPY_TEXTURES :
174                                                         osg::CopyOp::SHALLOW_COPY ) |
175                                            ( ! pass->getPositionedUniformMap().empty() ?
176                                                         osg::CopyOp::DEEP_COPY_UNIFORMS :
177                                                         osg::CopyOp::SHALLOW_COPY ) )
178                 )
179             );
180             for (Pass::BufferUnitList::const_iterator ii = pass->getBufferUnitList().begin();
181                     ii != pass->getBufferUnitList().end();
182                     ++ii) {
183                 osg::Texture2D* tex = ecv->getBuffer(ii->second);
184                 if (tex != 0)
185                     ss->setTextureAttributeAndModes( ii->first, tex );
186             }
187             for (Pass::PositionedUniformMap::const_iterator ii = pass->getPositionedUniformMap().begin();
188                     ii != pass->getPositionedUniformMap().end();
189                     ++ii) {
190                 osg::RefMatrix* mv = cv->getModelViewMatrix();
191                 osg::Vec4 v = ii->second * *mv;
192                 ss->getUniform(ii->first)->set( v );
193             }
194         }
195         cv->pushStateSet(ss);
196         int i = 0;
197         for (itr = begin; itr != drawablesEnd; ++itr, ++i) {
198             if (depth[i] != FLT_MAX)
199                 cv->addDrawableAndDepth(itr->get(), &matrix, depth[i]);
200         }
201         cv->popStateSet();
202     }
203     return drawablesEnd;
204 }
205
206 void Technique::resizeGLObjectBuffers(unsigned int maxSize)
207 {
208     if (_shadowingStateSet.valid())
209         _shadowingStateSet->resizeGLObjectBuffers(maxSize);
210     BOOST_FOREACH(ref_ptr<Pass>& pass, passes) {
211         pass->resizeGLObjectBuffers(maxSize);
212     }
213     _contextMap.resize(maxSize);
214 }
215
216 void Technique::releaseGLObjects(osg::State* state) const
217 {
218     if (_shadowingStateSet.valid())
219         _shadowingStateSet->releaseGLObjects(state);
220     BOOST_FOREACH(const ref_ptr<Pass>& pass, passes)
221     {
222         pass->releaseGLObjects(state);
223     }
224     if (state == 0) {
225         for (int i = 0; i < (int)_contextMap.size(); ++i) {
226             ContextInfo& info = _contextMap[i];
227             Status oldVal = info.valid();
228             info.valid.compareAndSwap(oldVal, UNKNOWN);
229         }
230     } else {
231         ContextInfo& info = _contextMap[state->getContextID()];
232         Status oldVal = info.valid();
233         info.valid.compareAndSwap(oldVal, UNKNOWN);
234     }
235 }
236
237 void Technique::setValidExpression(SGExpressionb* exp,
238                                    const simgear::expression
239                                    ::BindingLayout& layout)
240 {
241     using namespace simgear::expression;
242     _validExpression = exp;
243     VariableBinding binding;
244     if (layout.findBinding("__contextId", binding))
245         _contextIdLocation = binding.location;
246 }
247
248 class GLVersionExpression : public SGExpression<float>
249 {
250 public:
251     void eval(float& value, const expression::Binding*) const
252     {
253         value = getGLVersionNumber();
254     }
255 };
256
257 Expression* glVersionParser(const SGPropertyNode* exp,
258                             expression::Parser* parser)
259 {
260     return new GLVersionExpression();
261 }
262
263 expression::ExpParserRegistrar glVersionRegistrar("glversion", glVersionParser);
264
265 class ExtensionSupportedExpression
266     : public GeneralNaryExpression<bool, int>
267 {
268 public:
269     ExtensionSupportedExpression() {}
270     ExtensionSupportedExpression(const std::string& extString)
271         : _extString(extString)
272     {
273     }
274     const std::string& getExtensionString() { return _extString; }
275     void setExtensionString(const std::string& extString) { _extString = extString; }
276     void eval(bool&value, const expression::Binding* b) const
277     {
278         int contextId = getOperand(0)->getValue(b);
279         value = isGLExtensionSupported((unsigned)contextId, _extString.c_str());
280     }
281 protected:
282     std::string _extString;
283 };
284
285 Expression* extensionSupportedParser(const SGPropertyNode* exp,
286                                      expression::Parser* parser)
287 {
288     if (exp->getType() == props::STRING
289         || exp->getType() == props::UNSPECIFIED) {
290         ExtensionSupportedExpression* esp
291             = new ExtensionSupportedExpression(exp->getStringValue());
292         int location = parser->getBindingLayout().addBinding("__contextId",
293                                                              expression::INT);
294         VariableExpression<int>* contextExp
295             = new VariableExpression<int>(location);
296         esp->addOperand(contextExp);
297         return esp;
298     }
299     throw expression::ParseError("extension-supported expression has wrong type");
300 }
301
302 expression::ExpParserRegistrar
303 extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
304
305 class GLShaderLanguageExpression : public GeneralNaryExpression<float, int>
306 {
307 public:
308     void eval(float& value, const expression::Binding* b) const
309     {
310         value = 0.0f;
311         int contextId = getOperand(0)->getValue(b);
312         GL2Extensions* extensions
313             = GL2Extensions::Get(static_cast<unsigned>(contextId), true);
314         if (!extensions)
315             return;
316         if (!extensions->isGlslSupported())
317             return;
318         value = extensions->getLanguageVersion();
319     }
320 };
321
322 Expression* shaderLanguageParser(const SGPropertyNode* exp,
323                                  expression::Parser* parser)
324 {
325     GLShaderLanguageExpression* slexp = new GLShaderLanguageExpression;
326     int location = parser->getBindingLayout().addBinding("__contextId",
327                                                          expression::INT);
328     VariableExpression<int>* contextExp = new VariableExpression<int>(location);
329     slexp->addOperand(contextExp);
330     return slexp;
331 }
332
333 expression::ExpParserRegistrar shaderLanguageRegistrar("shader-language",
334                                                        shaderLanguageParser);
335
336 class GLSLSupportedExpression : public GeneralNaryExpression<bool, int>
337 {
338 public:
339    void eval(bool& value, const expression::Binding* b) const
340    {
341        value = false;
342        int contextId = getOperand(0)->getValue(b);
343        GL2Extensions* extensions
344            = GL2Extensions::Get(static_cast<unsigned>(contextId), true);
345        if (!extensions)
346            return;
347        value = extensions->isGlslSupported();
348    }
349 };
350
351 Expression* glslSupportedParser(const SGPropertyNode* exp,
352                                 expression::Parser* parser)
353 {
354    GLSLSupportedExpression* sexp = new GLSLSupportedExpression;
355    int location = parser->getBindingLayout().addBinding("__contextId",
356                                                         expression::INT);
357    VariableExpression<int>* contextExp = new VariableExpression<int>(location);
358    sexp->addOperand(contextExp);
359    return sexp;
360 }
361
362 expression::ExpParserRegistrar glslSupportedRegistrar("glsl-supported",
363                                                       glslSupportedParser);
364                                                                                                               
365 void Technique::setGLExtensionsPred(float glVersion,
366                                     const std::vector<std::string>& extensions)
367 {
368     using namespace std;
369     using namespace expression;
370     BindingLayout layout;
371     int contextLoc = layout.addBinding("__contextId", INT);
372     VariableExpression<int>* contextExp
373         = new VariableExpression<int>(contextLoc);
374     SGExpression<bool>* versionTest
375         = makePredicate<std::less_equal>(new SGConstExpression<float>(glVersion),
376                         new GLVersionExpression);
377     AndExpression* extensionsExp = 0;
378     for (vector<string>::const_iterator itr = extensions.begin(),
379              e = extensions.end();
380          itr != e;
381          ++itr) {
382         if (!extensionsExp)
383             extensionsExp = new AndExpression;
384         ExtensionSupportedExpression* supported
385             = new ExtensionSupportedExpression(*itr);
386         supported->addOperand(contextExp);
387         extensionsExp->addOperand(supported);
388     }
389     SGExpressionb* predicate = 0;
390     if (extensionsExp) {
391         OrExpression* orExp = new OrExpression;
392         orExp->addOperand(versionTest);
393         orExp->addOperand(extensionsExp);
394         predicate = orExp;
395     } else {
396         predicate = versionTest;
397     }
398     setValidExpression(predicate, layout);
399 }
400
401 void Technique::refreshValidity()
402 {
403     for (int i = 0; i < (int)_contextMap.size(); ++i) {
404         ContextInfo& info = _contextMap[i];
405         Status oldVal = info.valid();
406         // What happens if we lose the race here?
407         info.valid.compareAndSwap(oldVal, UNKNOWN);
408     }
409 }
410
411 bool Technique_writeLocalData(const Object& obj, osgDB::Output& fw)
412 {
413     const Technique& tniq = static_cast<const Technique&>(obj);
414     fw.indent() << "alwaysValid "
415                 << (tniq.getAlwaysValid() ? "TRUE\n" : "FALSE\n");
416 #if 0
417     fw.indent() << "glVersion " << tniq.getGLVersion() << "\n";
418 #endif
419     if (tniq.getShadowingStateSet()) {
420         fw.indent() << "shadowingStateSet\n";
421         fw.writeObject(*tniq.getShadowingStateSet());
422     }
423     fw.indent() << "num_passes " << tniq.passes.size() << "\n";
424     BOOST_FOREACH(const ref_ptr<Pass>& pass, tniq.passes) {
425         fw.writeObject(*pass);
426     }
427     return true;
428 }
429
430 namespace
431 {
432 osgDB::RegisterDotOsgWrapperProxy TechniqueProxy
433 (
434     new Technique,
435     "simgear::Technique",
436     "Object simgear::Technique",
437     0,
438     &Technique_writeLocalData
439     );
440 }
441 }