1 
2 
3 #include "stdfx/shaderfx.h"
4 
5 // TnzStdfx includes
6 #include "stdfx.h"
7 #include "stdfx/shaderinterface.h"
8 #include "stdfx/shadingcontext.h"
9 
10 // TnzBase includes
11 #include "tfxparam.h"
12 #include "tparamset.h"
13 #include "trenderresourcemanager.h"
14 #include "trenderer.h"
15 
16 // TnzCore includes
17 #include "tthread.h"
18 #include "tfilepath.h"
19 #include "tstream.h"
20 #include "tfunctorinvoker.h"
21 #include "tmsgcore.h"
22 
23 // Qt includes
24 #include <QDir>
25 #include <QOpenGLShaderProgram>
26 #include <QCoreApplication>
27 #include <QOffscreenSurface>
28 
29 // Glew include
30 #include <GL/glew.h>
31 
32 // Boost includes
33 #include <boost/any.hpp>
34 #include <boost/iterator/transform_iterator.hpp>
35 #include <boost/ptr_container/ptr_vector.hpp>
36 
37 // Diagnostics include
38 //#define DIAGNOSTICS
39 #ifdef DIAGNOSTICS
40 #include "diagnostics.h"
41 #endif
42 
43 //===================================================
44 
45 //    Forward Declarations
46 
47 class ShaderFxDeclaration;
48 
49 //===================================================
50 
51 //****************************************************************************
52 //    Local Namespace  stuff
53 //****************************************************************************
54 
55 namespace {
56 
57 // Classes
58 
59 struct ContextLocker {
60   ShadingContext &m_ctx;
61   bool m_locked;
62 
63 public:
ContextLocker__anon027714730111::ContextLocker64   ContextLocker(ShadingContext &ctx) : m_ctx(ctx), m_locked(false) { relock(); }
~ContextLocker__anon027714730111::ContextLocker65   ~ContextLocker() {
66     if (m_locked) unlock();
67   }
68 
relock__anon027714730111::ContextLocker69   void relock() {
70     assert(!m_locked), m_locked = true;
71     m_ctx.makeCurrent();
72   }
73 
unlock__anon027714730111::ContextLocker74   void unlock() {
75     assert(m_locked), m_locked = false;
76     m_ctx.doneCurrent();
77   }
78 };
79 
80 struct ProgramBinder {
81   QOpenGLShaderProgram *m_prog;
82 
83 public:
ProgramBinder__anon027714730111::ProgramBinder84   ProgramBinder(QOpenGLShaderProgram *prog) : m_prog(prog) { m_prog->bind(); }
~ProgramBinder__anon027714730111::ProgramBinder85   ~ProgramBinder() {
86     glUseProgram(0);  // m_prog->release();
87   }
88 };
89 
90 struct RectF {
91   GLfloat m_val[4];
RectF__anon027714730111::RectF92   RectF(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1) {
93     m_val[0] = x0, m_val[1] = y0, m_val[2] = x1, m_val[3] = y1;
94   }
RectF__anon027714730111::RectF95   RectF(const TRectD &rect) {
96     m_val[0] = rect.x0, m_val[1] = rect.y0, m_val[2] = rect.x1,
97     m_val[3] = rect.y1;
98   }
99 
operator TRectD__anon027714730111::RectF100   operator TRectD() const {
101     return TRectD(m_val[0], m_val[1], m_val[2], m_val[3]);
102   }
operator ==__anon027714730111::RectF103   bool operator==(const RectF &rect) const {
104     return (memcmp(m_val, rect.m_val, sizeof(this)) == 0);
105   }
106 };
107 
108 struct AffineF {
109   GLfloat m_val[9];
operator TAffine__anon027714730111::AffineF110   operator TAffine() const {
111     return TAffine(m_val[0], m_val[3], m_val[6], m_val[1], m_val[4], m_val[7]);
112   }
113   // Observe that mat3 from GLSL stores elements column-wise; this explains the
114   // weird indexing
115 };
116 
117 // Global Variables
118 
119 typedef std::map<QString, ShaderFxDeclaration *> FxDeclarationsMap;
120 FxDeclarationsMap l_shaderFxDeclarations;
121 
122 enum Measures { NONE, PERCENT, LENGTH, ANGLE, MEASURESCOUNT };
123 
124 static const std::string l_measureNames[MEASURESCOUNT] = {"", "percentage",
125                                                           "fxLength", "angle"};
126 
127 static const TParamUIConcept::Type
128     l_conceptTypes[ShaderInterface::CONCEPTSCOUNT -
129                    ShaderInterface::UI_CONCEPTS] = {
130         TParamUIConcept::RADIUS,  TParamUIConcept::WIDTH,
131         TParamUIConcept::ANGLE,   TParamUIConcept::POINT,
132         TParamUIConcept::POINT_2, TParamUIConcept::VECTOR,
133         TParamUIConcept::POLAR,   TParamUIConcept::SIZE,
134         TParamUIConcept::QUAD,    TParamUIConcept::RECT};
135 
136 // Functions
137 
isObsolete(const TFilePath & fp,const QDateTime & lastModified)138 inline bool isObsolete(const TFilePath &fp, const QDateTime &lastModified) {
139   QFileInfo fInfo(QString::fromStdWString(fp.getWideString()));
140   return (lastModified != fInfo.lastModified());
141 }
142 
tileRect(const TTile & tile)143 inline TRectD tileRect(const TTile &tile) {
144   const TDimension &dim = tile.getRaster()->getSize();
145   return TRectD(tile.m_pos, TDimensionD(dim.lx, dim.ly));
146 }
147 
ceilRect(TRectD & rect)148 inline void ceilRect(TRectD &rect) {
149   rect.x0 = tfloor(rect.x0), rect.y0 = tfloor(rect.y0);
150   rect.x1 = tceil(rect.x1), rect.y1 = tceil(rect.y1);
151 }
152 
153 }  // namespace
154 
155 //****************************************************************************
156 //    Shader Fx  declaration
157 //****************************************************************************
158 
159 class ShaderFx final : public TStandardZeraryFx {
160   FX_PLUGIN_DECLARATION(ShaderFx)
161 
162   const ShaderInterface *m_shaderInterface;  //!< Shader fx 'description'.
163   std::vector<boost::any>
164       m_params;  //!< Parameters for the shader fx. The actual parameter
165                  //!< type depends on the shader interface declaration.
166   std::vector<TParamUIConcept>
167       m_uiConcepts;  //!< UI concepts related to m_params.
168   boost::ptr_vector<TRasterFxPort>
169       m_inputPorts;  //!< Input ports for the shader fx.
170 
171 public:
ShaderFx()172   ShaderFx() : m_shaderInterface() {
173     assert(false);
174   }  // Necessary due to TPersist inheritance, but must NOT be used
ShaderFx(const ShaderInterface * shaderInterface)175   ShaderFx(const ShaderInterface *shaderInterface)
176       : m_shaderInterface(shaderInterface) {
177     initialize();
178   }
179 
180   // void setShaderInterface(const ShaderInterface& shaderInterface);
181   void initialize();
182 
183   void getParamUIs(TParamUIConcept *&params, int &length) override;
184   bool doGetBBox(double frame, TRectD &bBox,
185                  const TRenderSettings &info) override;
186   bool canHandle(const TRenderSettings &info, double frame) override;
187 
188   void doDryCompute(TRectD &rect, double frame,
189                     const TRenderSettings &ri) override;
190   void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override;
191 
192 private:
193   QOpenGLShaderProgram *touchShaderProgram(
194       const ShaderInterface::ShaderData &sd, ShadingContext &context,
195       int varyingsCount = 0, const GLchar **varyings = 0);
196 
197   void bindParameters(QOpenGLShaderProgram *shaderProgram, double frame);
198 
199   void bindWorldTransform(QOpenGLShaderProgram *shaderProgram,
200                           const TAffine &worldToDst);
201 
202   void getInputData(const TRectD &rect, double frame, const TRenderSettings &ri,
203                     std::vector<TRectD> &inputRects,
204                     std::vector<TAffine> &inputAffines,
205                     ShadingContext &context);
206 };
207 
208 //****************************************************************************
209 //    ShaderFxDeclaration  definition
210 //****************************************************************************
211 
212 class ShaderFxDeclaration final : public TFxDeclaration {
213   ShaderInterface m_shaderInterface;
214 
215 public:
ShaderFxDeclaration(const ShaderInterface & shaderInterface)216   ShaderFxDeclaration(const ShaderInterface &shaderInterface)
217       : TFxDeclaration(
218             TFxInfo(shaderInterface.mainShader().m_name.toStdString(), false))
219       , m_shaderInterface(shaderInterface) {}
220 
create() const221   TPersist *create() const override { return new ShaderFx(&m_shaderInterface); }
222 };
223 
224 //****************************************************************************
225 //    ShadingContextManager  definition
226 //****************************************************************************
227 
228 class ShadingContextManager final : public QObject {
229   mutable QMutex m_mutex;
230 
231   std::unique_ptr<ShadingContext> m_shadingContext;
232   TAtomicVar m_activeRenderInstances;
233   std::unique_ptr<QOffscreenSurface> m_surface;
234 
235 public:
ShadingContextManager()236   ShadingContextManager() {
237     /*
238 The ShadingContext's QGLPixelBuffer must be destroyed *before* the global
239 QApplication
240 is. So, we will attach to a suitable parent object whose lifespan is shorter.
241 
242 FYI - yes, this approach was adopted after a long and PAINFUL wrestling session
243 with Qt.
244 Suggestions are welcome as this is a tad beyond ridiculous...
245 */
246 
247     QObject *mainScopeBoundObject =
248         QCoreApplication::instance()->findChild<QObject *>("mainScope");
249 
250     assert(thread() ==
251            mainScopeBoundObject
252                ->thread());  // Parent object must be in the same thread,
253     // setParent(mainScopeBoundObject);  // otherwise reparenting fails
254     m_surface.reset(new QOffscreenSurface());
255     m_surface->create();
256     m_shadingContext.reset(new ShadingContext(m_surface.get()));
257   }
258 
instance()259   static ShadingContextManager *instance() {
260     static ShadingContextManager *theManager = new ShadingContextManager;
261     return theManager;
262   }
263 
mutex() const264   QMutex *mutex() const { return &m_mutex; }
265 
shadingContext() const266   const ShadingContext &shadingContext() const { return *m_shadingContext; }
shadingContext()267   ShadingContext &shadingContext() { return *m_shadingContext; }
268 
onRenderInstanceStart()269   void onRenderInstanceStart() { ++m_activeRenderInstances; }
270 
onRenderInstanceEnd()271   void onRenderInstanceEnd() {
272     if (--m_activeRenderInstances == 0) {
273       QMutexLocker mLocker(&m_mutex);
274 
275       // Release the shading context's output buffer
276       ::ContextLocker cLocker(*m_shadingContext);
277       m_shadingContext->resize(0, 0);
278 
279 #ifdef DIAGNOSTICS
280       DIAGNOSTICS_DUMP("ShaderLogs");
281       DIAGNOSTICS_CLEAR;
282 #endif
283     }
284   }
285 
touchSupport()286   ShadingContext::Support touchSupport() {
287     struct {
288       ShadingContextManager *m_this;
289       ShadingContext::Support support() {
290         QMutexLocker mLocker(&m_this->m_mutex);
291         ::ContextLocker cLocker(*m_this->m_shadingContext);
292 
293         return ShadingContext::support();
294       }
295     } locals = {this};
296 
297     static ShadingContext::Support sup = locals.support();
298 
299     static bool sentMsg = false;
300     if (!sentMsg) {
301       switch (sup) {
302       case ShadingContext::NO_PIXEL_BUFFER:
303         DVGui::warning(QOpenGLShaderProgram::tr(
304             "This system configuration does not support OpenGL Pixel Buffers. "
305             "Shader Fxs will not be able to render."));
306         break;
307 
308       case ShadingContext::NO_SHADERS:
309         DVGui::warning(QOpenGLShaderProgram::tr(
310             "This system configuration does not support OpenGL Shader "
311             "Programs. Shader Fxs will not be able to render."));
312         break;
313       default:
314         break;
315       }
316 
317       sentMsg = true;
318     }
319 
320     return sup;
321   }
322 
getSurface()323   QOffscreenSurface *getSurface() { return m_surface.get(); }
324 };
325 
326 template class DV_EXPORT_API TFxDeclarationT<ShaderFx>;
327 
328 //****************************************************************************
329 //    ShadingContextManagerDelegate  definition
330 //****************************************************************************
331 
332 class MessageCreateContext final : public TThread::Message {
333   ShadingContextManager *man;
334 
335 public:
MessageCreateContext(ShadingContextManager * ctx)336   MessageCreateContext(ShadingContextManager *ctx) : man(ctx) {}
337 
onDeliver()338   void onDeliver() override { man->onRenderInstanceEnd(); }
339 
clone() const340   TThread::Message *clone() const override {
341     return new MessageCreateContext(*this);
342   }
343 };
344 
345 class SCMDelegate final : public TRenderResourceManager {
346   T_RENDER_RESOURCE_MANAGER
347 
onRenderInstanceStart(unsigned long id)348   void onRenderInstanceStart(unsigned long id) override {
349     ShadingContextManager::instance()->onRenderInstanceStart();
350   }
351 
onRenderInstanceEnd(unsigned long id)352   void onRenderInstanceEnd(unsigned long id) override {
353     if (!TThread::isMainThread()) {
354       /* tofflinegl のときとは逆で main thread に dispatch する */
355       MessageCreateContext(ShadingContextManager::instance()).sendBlocking();
356     } else {
357       ShadingContextManager::instance()->onRenderInstanceEnd();
358     }
359   }
360 };
361 
362 //-------------------------------------------------------------------
363 
364 class SCMDelegateGenerator final : public TRenderResourceManagerGenerator {
365 public:
SCMDelegateGenerator()366   SCMDelegateGenerator() : TRenderResourceManagerGenerator(false) {
367     /*
368 Again, this has to do with the manager's lifetime issue.
369 The SCM must be created in the MAIN THREAD, but NOT BEFORE the
370 QCoreApplication itself has been created. The easiest way to do so
371 is scheduling a slot to be executed as soon as event processing starts.
372 */
373 
374     struct InstanceSCM final : public TFunctorInvoker::BaseFunctor {
375       void operator()() override { ShadingContextManager::instance(); }
376     };
377 
378     TFunctorInvoker::instance()->invokeQueued(new InstanceSCM);
379   }
380 
operator ()()381   TRenderResourceManager *operator()() override { return new SCMDelegate; }
382 };
383 
MANAGER_FILESCOPE_DECLARATION(SCMDelegate,SCMDelegateGenerator)384 MANAGER_FILESCOPE_DECLARATION(SCMDelegate, SCMDelegateGenerator)
385 
386 //****************************************************************************
387 //    Shader Fx  implementation
388 //****************************************************************************
389 
390 void ShaderFx::initialize() {
391   struct {
392     ShaderFx *m_this;
393 
394     inline void addUiConcept(const ShaderInterface::Parameter &siParam,
395                              const TParamP &param) {
396       if (siParam.m_concept.m_type >= ShaderInterface::UI_CONCEPTS &&
397           siParam.m_concept.m_type < ShaderInterface::CONCEPTSCOUNT) {
398         m_this->m_uiConcepts.push_back(TParamUIConcept());
399 
400         TParamUIConcept &uiConcept = m_this->m_uiConcepts.back();
401         uiConcept.m_type           = ::l_conceptTypes[siParam.m_concept.m_type -
402                                             ShaderInterface::UI_CONCEPTS];
403         uiConcept.m_label = siParam.m_concept.m_label.toStdString();
404         uiConcept.m_params.push_back(param);
405       }
406     }
407 
408     inline void addUiConcept(const ShaderInterface::ParameterConcept &concept) {
409       if (!concept.isUI() || concept.m_parameterNames.empty()) return;
410 
411       TParamUIConcept uiConcept = {
412           ::l_conceptTypes[concept.m_type - ShaderInterface::UI_CONCEPTS],
413           concept.m_label.toStdString()};
414 
415       int n, nCount = int(concept.m_parameterNames.size());
416       for (n = 0; n != nCount; ++n) {
417         TParam *param = m_this->getParams()->getParam(
418             concept.m_parameterNames[n].toStdString());
419         if (!param) break;
420 
421         uiConcept.m_params.push_back(param);
422       }
423 
424       if (uiConcept.m_params.size() == concept.m_parameterNames.size())
425         m_this->m_uiConcepts.push_back(uiConcept);
426     }
427 
428   } locals = {this};
429 
430   assert(m_params.empty());  // Interfaces should not be re-set
431 
432   // Allocate parameters following the specified interface
433   const std::vector<ShaderInterface::Parameter> &siParams =
434       m_shaderInterface->parameters();
435 
436   int p, pCount = int(siParams.size());
437   m_params.reserve(pCount);
438 
439   for (p = 0; p != pCount; ++p) {
440     const ShaderInterface::Parameter &siParam = siParams[p];
441 
442     switch (siParam.m_type) {
443     case ShaderInterface::BOOL: {
444       TBoolParamP param(siParam.m_default.m_bool);
445 
446       m_params.push_back(param);
447       bindParam(this, siParam.m_name.toStdString(),
448                 *boost::unsafe_any_cast<TBoolParamP>(&m_params.back()));
449 
450       break;
451     }
452 
453     case ShaderInterface::FLOAT: {
454       TDoubleParamP param(siParam.m_default.m_float);
455       param->setValueRange(siParam.m_range[0].m_float,
456                            siParam.m_range[1].m_float);
457 
458       locals.addUiConcept(siParam, param);
459 
460       switch (siParam.m_concept.m_type) {
461       case ShaderInterface::PERCENT:
462         param->setMeasureName(l_measureNames[PERCENT]);
463         break;
464 
465       case ShaderInterface::LENGTH:
466       case ShaderInterface::RADIUS_UI:
467       case ShaderInterface::WIDTH_UI:
468       case ShaderInterface::SIZE_UI:
469         param->setMeasureName(l_measureNames[LENGTH]);
470         break;
471 
472       case ShaderInterface::ANGLE:
473       case ShaderInterface::ANGLE_UI:
474         param->setMeasureName(l_measureNames[ANGLE]);
475         break;
476       default:
477         break;
478       }
479 
480       m_params.push_back(param);
481       bindParam(this, siParam.m_name.toStdString(),
482                 *boost::unsafe_any_cast<TDoubleParamP>(&m_params.back()));
483       break;
484     }
485 
486     case ShaderInterface::VEC2: {
487       TPointParamP param(
488           TPointD(siParam.m_default.m_vec2[0], siParam.m_default.m_vec2[1]));
489 
490       param->getX()->setValueRange(siParam.m_range[0].m_vec2[0],
491                                    siParam.m_range[1].m_vec2[0]);
492       param->getY()->setValueRange(siParam.m_range[0].m_vec2[1],
493                                    siParam.m_range[1].m_vec2[1]);
494 
495       locals.addUiConcept(siParam, param);
496 
497       switch (siParam.m_concept.m_type) {
498       case ShaderInterface::PERCENT:
499         param->getX()->setMeasureName(l_measureNames[PERCENT]);
500         param->getY()->setMeasureName(l_measureNames[PERCENT]);
501         break;
502 
503       case ShaderInterface::LENGTH:
504       case ShaderInterface::POINT:
505       case ShaderInterface::POINT_UI:
506       case ShaderInterface::VECTOR_UI:
507       case ShaderInterface::WIDTH_UI:
508       case ShaderInterface::SIZE_UI:
509         param->getX()->setMeasureName(l_measureNames[LENGTH]);
510         param->getY()->setMeasureName(l_measureNames[LENGTH]);
511         break;
512 
513       case ShaderInterface::ANGLE:
514       case ShaderInterface::ANGLE_UI:
515         param->getX()->setMeasureName(l_measureNames[ANGLE]);
516         param->getY()->setMeasureName(l_measureNames[ANGLE]);
517         break;
518       default:
519         break;
520       }
521 
522       m_params.push_back(param);
523       bindParam(this, siParam.m_name.toStdString(),
524                 *boost::unsafe_any_cast<TPointParamP>(&m_params.back()));
525       break;
526     }
527 
528     case ShaderInterface::INT: {
529       TIntParamP param(siParam.m_default.m_int);
530       param->setValueRange(siParam.m_range[0].m_int, siParam.m_range[1].m_int);
531 
532       m_params.push_back(param);
533       bindParam(this, siParam.m_name.toStdString(),
534                 *boost::unsafe_any_cast<TIntParamP>(&m_params.back()));
535       break;
536     }
537 
538     case ShaderInterface::RGBA: {
539       TPixelParamP param(
540           TPixel32(siParam.m_default.m_rgba[0], siParam.m_default.m_rgba[1],
541                    siParam.m_default.m_rgba[2], siParam.m_default.m_rgba[3]));
542 
543       m_params.push_back(param);
544       bindParam(this, siParam.m_name.toStdString(),
545                 *boost::unsafe_any_cast<TPixelParamP>(&m_params.back()));
546       break;
547     }
548 
549     case ShaderInterface::RGB: {
550       TPixelParamP param(TPixel32(siParam.m_default.m_rgb[0],
551                                   siParam.m_default.m_rgb[1],
552                                   siParam.m_default.m_rgb[2]));
553 
554       param->enableMatte(false);
555 
556       m_params.push_back(param);
557       bindParam(this, siParam.m_name.toStdString(),
558                 *boost::unsafe_any_cast<TPixelParamP>(&m_params.back()));
559       break;
560     }
561     default:
562       break;
563     }
564   }
565 
566   // Add composite UI concepts
567   const std::vector<ShaderInterface::ParameterConcept> &parConcepts =
568       m_shaderInterface->m_parConcepts;
569 
570   int c, cCount = int(parConcepts.size());
571   for (c = 0; c != cCount; ++c) locals.addUiConcept(parConcepts[c]);
572 
573   // Add input ports
574   const std::vector<QString> &inputPorts = m_shaderInterface->inputPorts();
575 
576   int i, iCount = int(inputPorts.size());
577   m_inputPorts.reserve(iCount);
578 
579   for (i = 0; i != iCount; ++i) {
580     m_inputPorts.push_back(new TRasterFxPort);
581     addInputPort(inputPorts[i].toStdString(), m_inputPorts[i]);
582   }
583 }
584 
585 //-------------------------------------------------------------------
586 
getParamUIs(TParamUIConcept * & params,int & length)587 void ShaderFx::getParamUIs(TParamUIConcept *&params, int &length) {
588   length = int(m_uiConcepts.size());
589   params = new TParamUIConcept[length];
590 
591   std::copy(m_uiConcepts.begin(), m_uiConcepts.end(), params);
592 }
593 
594 //-------------------------------------------------------------------
595 
doGetBBox(double frame,TRectD & bbox,const TRenderSettings & info)596 bool ShaderFx::doGetBBox(double frame, TRectD &bbox,
597                          const TRenderSettings &info) {
598   static const ::RectF infiniteRectF(-(std::numeric_limits<GLfloat>::max)(),
599                                      -(std::numeric_limits<GLfloat>::max)(),
600                                      (std::numeric_limits<GLfloat>::max)(),
601                                      (std::numeric_limits<GLfloat>::max)());
602 
603   bbox = TConsts::infiniteRectD;
604 
605   const ShaderInterface::ShaderData &sd = m_shaderInterface->bboxShader();
606   if (!sd.isValid()) return true;
607 
608   ShadingContextManager *manager = ShadingContextManager::instance();
609   if (manager->touchSupport() != ShadingContext::OK) return true;
610 
611   // Remember: info.m_affine MUST NOT BE CONSIDERED in doGetBBox's
612   // implementation
613   ::RectF bboxF(infiniteRectF);
614 
615   QMutexLocker mLocker(manager->mutex());
616 
617   // ShadingContext& context = manager->shadingContext();
618   std::shared_ptr<ShadingContext> shadingContextPtr(
619       new ShadingContext(manager->getSurface()));
620   ShadingContext &context = *shadingContextPtr.get();
621 
622   ::ContextLocker cLocker(context);
623 
624   // Build the varyings data
625   QOpenGLShaderProgram *prog = 0;
626   {
627     const GLchar *varyingNames[] = {"outputBBox"};
628     prog = touchShaderProgram(sd, context, 1, &varyingNames[0]);
629   }
630 
631   int pCount = getInputPortCount();
632 
633   std::vector<RectF> inputBBoxes(pCount, ::RectF(TRectD()));
634 
635   for (int p = 0; p != pCount; ++p) {
636     TRasterFxPort &port = m_inputPorts[p];
637     if (port.isConnected()) {
638       TRectD inputBBox;
639 
640       cLocker.unlock();
641       mLocker.unlock();
642 
643       if (port->doGetBBox(frame, inputBBox, info))
644         inputBBoxes[p] = (inputBBox == TConsts::infiniteRectD)
645                              ? infiniteRectF
646                              : ::RectF(inputBBox);
647 
648       mLocker.relock();
649       cLocker.relock();
650     }
651   }
652 
653   {
654     ProgramBinder progBinder(prog);
655 
656     // Bind uniform parameters
657     bindParameters(prog, frame);
658 
659     prog->setUniformValue("infiniteRect", infiniteRectF.m_val[0],
660                           infiniteRectF.m_val[1], infiniteRectF.m_val[2],
661                           infiniteRectF.m_val[3]);
662 
663     prog->setUniformValueArray("inputBBox", inputBBoxes[0].m_val,
664                                int(inputBBoxes.size()), 4);
665 
666     // Perform transform feedback
667     const GLsizeiptr varyingSizes[] = {sizeof(::RectF)};
668     GLvoid *bufs[]                  = {bboxF.m_val};
669 
670     context.transformFeedback(1, varyingSizes, bufs);
671   }
672 
673   // Finalize output
674   bbox = (bboxF == infiniteRectF) ? TConsts::infiniteRectD : TRectD(bboxF);
675   return true;
676 }
677 
678 //-------------------------------------------------------------------
679 
canHandle(const TRenderSettings & info,double frame)680 bool ShaderFx::canHandle(const TRenderSettings &info, double frame) {
681   return (m_shaderInterface->hwtType() == ShaderInterface::ANY)
682              ? true
683              : isAlmostIsotropic(info.m_affine);
684 }
685 
686 //-------------------------------------------------------------------
687 
touchShaderProgram(const ShaderInterface::ShaderData & sd,ShadingContext & context,int varyingsCount,const GLchar ** varyings)688 QOpenGLShaderProgram *ShaderFx::touchShaderProgram(
689     const ShaderInterface::ShaderData &sd, ShadingContext &context,
690     int varyingsCount, const GLchar **varyings) {
691   typedef std::pair<QOpenGLShaderProgram *, QDateTime> CompiledShader;
692 
693   struct locals {
694     inline static void logCompilation(QOpenGLShaderProgram *program) {
695       // Log shaders - observe that we'll look into the program's *children*,
696       // not its
697       // shaders. This is necessary as uncompiled shaders are not added to the
698       // program.
699       const QObjectList &children = program->children();
700 
701       int c, cCount = children.size();
702       for (c = 0; c != cCount; ++c) {
703         if (QOpenGLShader *shader =
704                 dynamic_cast<QOpenGLShader *>(children[c])) {
705           const QString &log = shader->log();
706           if (!log.isEmpty()) DVGui::info(log);
707         }
708       }
709 
710       // ShaderProgram linking logs
711       const QString &log = program->log();
712       if (!log.isEmpty()) DVGui::info(log);
713     }
714   };  // locals
715 
716   // ShadingContext& context =
717   // ShadingContextManager::instance()->shadingContext();
718 
719   CompiledShader cs = context.shaderData(sd.m_name);
720   if (!cs.first || ::isObsolete(sd.m_path, cs.second)) {
721     cs = m_shaderInterface->makeProgram(sd, varyingsCount, varyings);
722     context.addShaderProgram(sd.m_name, cs.first, cs.second);
723 
724     locals::logCompilation(cs.first);
725   }
726 
727   assert(cs.first);
728   return cs.first;
729 }
730 
731 //-------------------------------------------------------------------
732 
bindParameters(QOpenGLShaderProgram * program,double frame)733 void ShaderFx::bindParameters(QOpenGLShaderProgram *program, double frame) {
734   // Bind fx parameters
735   const std::vector<ShaderInterface::Parameter> &siParams =
736       m_shaderInterface->parameters();
737 
738   assert(siParams.size() == m_params.size());
739 
740   int p, pCount = int(siParams.size());
741   for (p = 0; p != pCount; ++p) {
742     const ShaderInterface::Parameter &siParam = siParams[p];
743 
744     switch (siParam.m_type) {
745     case ShaderInterface::BOOL: {
746       const TBoolParamP &param =
747           *boost::unsafe_any_cast<TBoolParamP>(&m_params[p]);
748       program->setUniformValue(siParam.m_name.toUtf8().data(),
749                                (GLboolean)param->getValue());
750       break;
751     }
752 
753     case ShaderInterface::FLOAT: {
754       const TDoubleParamP &param =
755           *boost::unsafe_any_cast<TDoubleParamP>(&m_params[p]);
756       program->setUniformValue(siParam.m_name.toUtf8().data(),
757                                (GLfloat)param->getValue(frame));
758       break;
759     }
760 
761     case ShaderInterface::VEC2: {
762       const TPointParamP &param =
763           *boost::unsafe_any_cast<TPointParamP>(&m_params[p]);
764 
765       const TPointD &value = param->getValue(frame);
766       program->setUniformValue(siParam.m_name.toUtf8().data(), (GLfloat)value.x,
767                                (GLfloat)value.y);
768       break;
769     }
770 
771     case ShaderInterface::INT: {
772       const TIntParamP &param =
773           *boost::unsafe_any_cast<TIntParamP>(&m_params[p]);
774       program->setUniformValue(siParam.m_name.toUtf8().data(),
775                                (GLint)param->getValue());
776       break;
777     }
778 
779     case ShaderInterface::RGBA:
780     case ShaderInterface::RGB: {
781       const TPixelParamP &param =
782           *boost::unsafe_any_cast<TPixelParamP>(&m_params[p]);
783 
784       const TPixel32 &value = param->getValue(frame);
785       program->setUniformValue(
786           siParam.m_name.toUtf8().data(), (GLfloat)value.r / 255.0f,
787           (GLfloat)value.g / 255.0f, (GLfloat)value.b / 255.0f,
788           (GLfloat)value.m / 255.0f);
789       break;
790     }
791     default:
792       break;
793     }
794   }
795 }
796 
797 //-------------------------------------------------------------------
798 
bindWorldTransform(QOpenGLShaderProgram * program,const TAffine & worldToDst)799 void ShaderFx::bindWorldTransform(QOpenGLShaderProgram *program,
800                                   const TAffine &worldToDst) {
801 // Bind transformation affine
802 #if QT_VERSION >= 0x050500
803   float qwToD[9] = {static_cast<float>(worldToDst.a11),
804                     static_cast<float>(worldToDst.a12),
805                     static_cast<float>(worldToDst.a13),
806                     static_cast<float>(worldToDst.a21),
807                     static_cast<float>(worldToDst.a22),
808                     static_cast<float>(worldToDst.a23),
809                     0.0f,
810                     0.0f,
811                     1.0f};
812 #else
813   qreal qwToD[9] = {worldToDst.a11,
814                     worldToDst.a12,
815                     worldToDst.a13,
816                     worldToDst.a21,
817                     worldToDst.a22,
818                     worldToDst.a23,
819                     0.0,
820                     0.0,
821                     1.0};
822 #endif
823   program->setUniformValue("worldToOutput", QMatrix3x3(qwToD));
824 
825   const TAffine &dToW = worldToDst.inv();
826 #if QT_VERSION >= 0x050500
827   float qdToW[9] = {static_cast<float>(dToW.a11),
828                     static_cast<float>(dToW.a12),
829                     static_cast<float>(dToW.a13),
830                     static_cast<float>(dToW.a21),
831                     static_cast<float>(dToW.a22),
832                     static_cast<float>(dToW.a23),
833                     0.0f,
834                     0.0f,
835                     1.0f};
836 #else
837   qreal qdToW[9] = {dToW.a11, dToW.a12, dToW.a13, dToW.a21, dToW.a22,
838                     dToW.a23, 0.0,      0.0,      1.0};
839 #endif
840   program->setUniformValue("outputToWorld", QMatrix3x3(qdToW));
841 }
842 
843 //-------------------------------------------------------------------
844 
getInputData(const TRectD & rect,double frame,const TRenderSettings & ri,std::vector<TRectD> & inputRects,std::vector<TAffine> & inputAffines,ShadingContext & context)845 void ShaderFx::getInputData(const TRectD &rect, double frame,
846                             const TRenderSettings &ri,
847                             std::vector<TRectD> &inputRects,
848                             std::vector<TAffine> &inputAffines,
849                             ShadingContext &context) {
850   struct locals {
851     static inline void addNames(std::vector<std::string> &names,
852                                 const char *prefix, int pCount) {
853       for (int p = 0; p != pCount; ++p)
854         names.push_back((prefix + QString("[%1]").arg(p)).toStdString());
855     }
856   };
857 
858   const ShaderInterface::ShaderData &sd = m_shaderInterface->inputPortsShader();
859   if (!sd.isValid()) {
860     inputRects.resize(getInputPortCount());
861     std::fill(inputRects.begin(), inputRects.end(), rect);
862 
863     inputAffines.resize(getInputPortCount());
864     std::fill(inputAffines.begin(), inputAffines.end(), ri.m_affine);
865 
866     return;
867   }
868 
869   // ShadingContext& context =
870   // ShadingContextManager::instance()->shadingContext();
871 
872   std::vector<GLfloat> buf;
873   int pCount = getInputPortCount();
874 
875   // Build the varyings data
876   QOpenGLShaderProgram *prog = 0;
877   {
878     // Unsubscripted varying arrays on transform feedback seems to be
879     // unsupported
880     // on ATI cards. We have to declare EACH array name - e.g. inputRect[0],
881     // intputRect[1], etc..
882 
883     const GLchar *varyingPrefixes[] = {"inputRect", "worldToInput"};
884     const int varyingsCount = sizeof(varyingPrefixes) / sizeof(GLchar *);
885 
886     std::vector<std::string> varyingStrings;
887     varyingStrings.reserve(varyingsCount);
888 
889     for (int v = 0; v != varyingsCount; ++v)
890       locals::addNames(varyingStrings, varyingPrefixes[v], pCount);
891 
892 #if defined(__APPLE_CC__)
893     /* OSX10.8 の clang -stdlib=libc++ だと link 時 &std::string::c_str が
894      * undefined になってしまう */
895     std::vector<const GLchar *> varyingNames(varyingStrings.size());
896     auto conv = [](const std::string &i) { return i.c_str(); };
897     std::transform(varyingStrings.begin(), varyingStrings.end(),
898                    varyingNames.begin(), conv);
899 #else
900     std::vector<const GLchar *> varyingNames(
901         boost::make_transform_iterator(varyingStrings.begin(),
902                                        std::mem_fun_ref(&std::string::c_str)),
903         boost::make_transform_iterator(varyingStrings.end(),
904                                        std::mem_fun_ref(&std::string::c_str)));
905 #endif
906     prog = touchShaderProgram(sd, context, int(varyingNames.size()),
907                               &varyingNames[0]);
908   }
909 
910   {
911     ProgramBinder progBinder(prog);
912 
913     // Build varying buffers
914     int bufFloatsCount =
915         pCount * (sizeof(RectF) + sizeof(AffineF)) / sizeof(GLfloat);
916     buf.resize(bufFloatsCount);
917 
918     // Bind uniform parameters
919     bindParameters(prog, frame);
920     bindWorldTransform(prog, ri.m_affine);
921 
922     prog->setUniformValue("outputRect", (GLfloat)rect.x0, (GLfloat)rect.y0,
923                           (GLfloat)rect.x1, (GLfloat)rect.y1);
924 
925     // Perform transform feedback
926     const GLsizeiptr varyingSizes[] = {
927         static_cast<GLsizeiptr>(bufFloatsCount * sizeof(GLfloat))};
928     GLvoid *bufs[] = {&buf[0]};
929 
930     context.transformFeedback(1, varyingSizes, bufs);
931 
932 #ifdef TRANSFORM_FEEDBACK_COUT
933     std::cout << "trFeedback: ";
934     for (int f = 0; f != bufFloatsCount; ++f) std::cout << buf[f] << " ";
935     std::cout << "\n" << std::endl;
936 #endif
937   }
938 
939   // Finalize output
940   const RectF *rBufBegin(reinterpret_cast<const RectF *>(&buf[0])),
941       *rBufEnd(rBufBegin + pCount);
942   std::copy(rBufBegin, rBufEnd, &inputRects[0]);
943 
944   const AffineF *aBufBegin(reinterpret_cast<const AffineF *>(rBufEnd)),
945       *aBufEnd(aBufBegin + pCount);
946   std::copy(aBufBegin, aBufEnd, &inputAffines[0]);
947 }
948 
949 //-------------------------------------------------------------------
950 
doCompute(TTile & tile,double frame,const TRenderSettings & info)951 void ShaderFx::doCompute(TTile &tile, double frame,
952                          const TRenderSettings &info) {
953   struct locals {
954     struct TexturesStorage {
955       ShadingContext &m_ctx;
956       std::vector<GLuint> m_texIds;
957 
958       TexturesStorage(ShadingContext &ctx, int pCount) : m_ctx(ctx) {
959         m_texIds.reserve(pCount);
960       }
961 
962       ~TexturesStorage() {
963         for (auto const &texId : m_texIds) {
964           m_ctx.unloadTexture(texId);
965         }
966       }
967 
968       void load(const TRasterP &ras, GLuint texUnit) {
969         if (ras) m_texIds.push_back(m_ctx.loadTexture(ras, texUnit));
970       }
971     };
972 
973     inline static QOpenGLFramebufferObjectFormat makeFormat(int bpp) {
974       QOpenGLFramebufferObjectFormat fmt;
975       if (bpp == 64) fmt.setInternalTextureFormat(GL_RGBA16);
976       return fmt;
977     }
978 
979     inline static void touchOutputSize(ShadingContext &context,
980                                        const TDimension &size, int bpp) {
981       const QOpenGLFramebufferObjectFormat &fmt = makeFormat(bpp);
982 
983       const TDimension &currentSize                    = context.size();
984       const QOpenGLFramebufferObjectFormat &currentFmt = context.format();
985 
986       if (currentSize.lx < size.lx || currentSize.ly < size.ly ||
987           currentFmt != fmt)
988         context.resize(std::max(size.lx, currentSize.lx),
989                        std::max(size.ly, currentSize.ly), fmt);
990     }
991   };  // locals
992 
993   ShadingContextManager *manager = ShadingContextManager::instance();
994   if (manager->touchSupport() != ShadingContext::OK) return;
995 
996   QMutexLocker mLocker(
997       manager->mutex());  // As GPU access can be considered sequential anyway,
998                           // lock the full-scale mutex
999   std::shared_ptr<ShadingContext> shadingContextPtr(
1000       new ShadingContext(manager->getSurface()));
1001   ShadingContext &context = *shadingContextPtr.get();
1002   // ShadingContext& context = manager->shadingContext();
1003 
1004   int pCount = getInputPortCount();
1005 
1006   const TRectD &tileRect = ::tileRect(tile);
1007 
1008   std::vector<TRectD> inputRects(pCount);
1009   std::vector<TAffine> inputAffines(pCount);
1010 
1011   // Calculate input tiles
1012   ::ContextLocker cLocker(context);
1013 
1014   std::unique_ptr<TTile[]> inTiles(
1015       new TTile[pCount]);  // NOTE: Input tiles must be STORED - they cannot
1016   // be passed immediately to OpenGL, since *other shader
1017   if (pCount > 0)  // fxs*, with the very same host context, could lie
1018   {                // inside this fx's input branches...
1019     getInputData(tileRect, frame, info, inputRects, inputAffines, context);
1020 
1021     // Release context and mutex
1022     cLocker.unlock();
1023     mLocker.unlock();
1024 
1025     for (int p = 0; p != pCount; ++p) {
1026       TRasterFxPort &port = m_inputPorts[p];
1027       if (port.isConnected()) {
1028         // Compute input tile
1029         TRectD &inRect = inputRects[p];
1030         if (inRect.getLx() > 0.0 && inRect.getLy() > 0.0) {
1031           ::ceilRect(inRect);
1032 
1033           TRenderSettings inputInfo(info);
1034           inputInfo.m_affine = inputAffines[p];
1035 
1036 #ifdef TRANSFORM_FEEDBACK_COUT
1037           const TAffine &inAff = inputAffines[p];
1038           std::cout << "inRect " << p << ": " << inRect.x0 << " " << inRect.y0
1039                     << " " << inRect.x1 << " " << inRect.y1 << "\n";
1040           std::cout << "inAff  " << p << ": " << inAff.a11 << " " << inAff.a12
1041                     << " " << inAff.a13 << "\n";
1042           std::cout << "          " << inAff.a21 << " " << inAff.a22 << " "
1043                     << inAff.a23 << "\n"
1044                     << std::endl;
1045 #endif
1046 
1047           port->allocateAndCompute(
1048               inTiles[p], inRect.getP00(),
1049               TDimension(tround(inRect.getLx()), tround(inRect.getLy())),
1050               tile.getRaster(), frame, inputInfo);
1051         }
1052       }
1053     }
1054 
1055     // Load input tiles on the GPU as textures
1056     mLocker.relock();
1057     cLocker.relock();
1058 
1059     // Input tiles are NOT supplied to OpenGL here - but rather just before
1060     // drawing.
1061     // It's probably beacuse a uniform integer variable must have already been
1062     // bound
1063     // to prepare the associated sampler variable in the linkes program...
1064   }
1065 
1066   // Perform the actual fragment shading
1067   {
1068     locals::touchOutputSize(context, tile.getRaster()->getSize(), info.m_bpp);
1069 
1070     QOpenGLShaderProgram *program =
1071         touchShaderProgram(m_shaderInterface->mainShader(), context);
1072     {
1073       ProgramBinder binder(program);
1074 
1075       // Bind parameters and textures
1076       bindParameters(program, frame);
1077       bindWorldTransform(program, TTranslation(-tile.m_pos) * info.m_affine);
1078 
1079       // Setup input data, if any
1080       locals::TexturesStorage texStorage(context, pCount);
1081 
1082       if (pCount > 0) {
1083         std::vector<GLint> inputs(pCount);
1084         std::vector<QMatrix3x3> screenToInput(pCount);
1085         std::vector<QMatrix3x3> inputToScreen(pCount);
1086 
1087         for (int p = 0; p != pCount; ++p) {
1088           TAffine iToS(
1089               TTranslation(-tile.m_pos) *             // Output to Screen
1090               info.m_affine *                         // World to Output
1091               inputAffines[p].inv() *                 // Input to World
1092               TTranslation(inputRects[p].getP00()) *  // Texture to Input
1093               TScale(inputRects[p].getLx(), inputRects[p].getLy()));  //
1094 
1095           TAffine sToI(iToS.inv());
1096 
1097 #if QT_VERSION >= 0x050500
1098           float qiToS[9] = {static_cast<float>(iToS.a11),
1099                             static_cast<float>(iToS.a12),
1100                             static_cast<float>(iToS.a13),
1101                             static_cast<float>(iToS.a21),
1102                             static_cast<float>(iToS.a22),
1103                             static_cast<float>(iToS.a23),
1104                             0.0f,
1105                             0.0f,
1106                             1.0f};
1107           float qsToI[9] = {static_cast<float>(sToI.a11),
1108                             static_cast<float>(sToI.a12),
1109                             static_cast<float>(sToI.a13),
1110                             static_cast<float>(sToI.a21),
1111                             static_cast<float>(sToI.a22),
1112                             static_cast<float>(sToI.a23),
1113                             0.0f,
1114                             0.0f,
1115                             1.0f};
1116 #else
1117           qreal qiToS[9] = {iToS.a11, iToS.a12, iToS.a13, iToS.a21, iToS.a22,
1118                             iToS.a23, 0.0,      0.0,      1.0};
1119 
1120           qreal qsToI[9] = {sToI.a11, sToI.a12, sToI.a13, sToI.a21, sToI.a22,
1121                             sToI.a23, 0.0,      0.0,      1.0};
1122 #endif
1123           inputs[p] = p, screenToInput[p] = QMatrix3x3(qsToI),
1124           inputToScreen[p] = QMatrix3x3(qiToS);
1125         }
1126 
1127         program->setUniformValueArray("inputImage", &inputs[0], pCount);
1128         program->setUniformValueArray("outputToInput", &screenToInput[0],
1129                                       pCount);
1130         program->setUniformValueArray("inputToOutput", &inputToScreen[0],
1131                                       pCount);
1132 
1133         // Load textures
1134         for (int p = 0; p != pCount; ++p)
1135           texStorage.load(inTiles[p].getRaster(), p);
1136       }
1137 
1138 #ifdef DIAGNOSTICS
1139       DIAGNOSTICS_TIMER("Shader Overall Times | " +
1140                         m_shaderInterface->m_mainShader.m_name);
1141 #endif
1142 
1143       context.draw(tile.getRaster());
1144     }
1145   }
1146 }
1147 
1148 //-------------------------------------------------------------------
1149 
doDryCompute(TRectD & rect,double frame,const TRenderSettings & info)1150 void ShaderFx::doDryCompute(TRectD &rect, double frame,
1151                             const TRenderSettings &info) {
1152   ShadingContextManager *manager = ShadingContextManager::instance();
1153   if (manager->touchSupport() != ShadingContext::OK) return;
1154 
1155   QMutexLocker mLocker(manager->mutex());
1156 
1157   // ShadingContext& context = manager->shadingContext();
1158   std::shared_ptr<ShadingContext> shadingContextPtr(
1159       new ShadingContext(manager->getSurface()));
1160   ShadingContext &context = *shadingContextPtr.get();
1161 
1162   int pCount = getInputPortCount();
1163   if (pCount > 0) {
1164     ::ContextLocker cLocker(context);
1165 
1166     std::vector<TRectD> inputRects(pCount);
1167     std::vector<TAffine> inputAffines(pCount);
1168 
1169     getInputData(rect, frame, info, inputRects, inputAffines, context);
1170 
1171     for (int p = 0; p != pCount; ++p) {
1172       TRasterFxPort &port = m_inputPorts[p];
1173       if (port.isConnected()) {
1174         TRectD &inRect = inputRects[p];
1175         if (inRect.getLx() > 0.0 && inRect.getLy() > 0.0) {
1176           ::ceilRect(inRect);
1177 
1178           TRenderSettings inputInfo(info);
1179           inputInfo.m_affine = inputAffines[p];
1180 
1181           cLocker.unlock();
1182           mLocker.unlock();
1183 
1184           port->dryCompute(inRect, frame, inputInfo);
1185 
1186           mLocker.relock();
1187           cLocker.relock();
1188         }
1189       }
1190     }
1191   }
1192 }
1193 
1194 //------------------------------------------------------------------
1195 
getDeclaration() const1196 const TPersistDeclaration *ShaderFx::getDeclaration() const {
1197   FxDeclarationsMap::iterator it =
1198       ::l_shaderFxDeclarations.find(m_shaderInterface->mainShader().m_name);
1199 
1200   return (it == ::l_shaderFxDeclarations.end()) ? 0 : it->second;
1201 }
1202 
1203 //****************************************************************************
1204 //    Shader Interfaces  loading function
1205 //****************************************************************************
1206 
loadShaderInterfaces(const TFilePath & shadersFolder)1207 void loadShaderInterfaces(const TFilePath &shadersFolder) {
1208   // Scan the shaders folder for xml (shader interface) files
1209   QDir shadersDir(QString::fromStdWString(shadersFolder.getWideString()));
1210 
1211   QStringList namesFilter("*.xml");
1212   QStringList files = shadersDir.entryList(namesFilter, QDir::Files,
1213                                            QDir::Name | QDir::LocaleAware);
1214 
1215   int f, fCount = files.size();
1216   for (f = 0; f != fCount; ++f) {
1217     TIStream is(shadersFolder + TFilePath(files[f].toStdWString()));
1218 
1219     // Try to load a ShaderInterface instance for the file
1220     ShaderInterface shaderInterface;
1221     is >> shaderInterface;
1222 
1223     if (shaderInterface.isValid()) {
1224       // Store a ShaderFx factory for the interface
1225       ::l_shaderFxDeclarations.insert(
1226           std::make_pair(shaderInterface.mainShader().m_name,
1227                          new ShaderFxDeclaration(shaderInterface)));
1228     }
1229   }
1230 }
1231