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 *¶ms, 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 ¶m) {
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 *¶ms, 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 ¶m =
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 ¶m =
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 ¶m =
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 ¶m =
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 ¶m =
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 ¤tSize = context.size();
984 const QOpenGLFramebufferObjectFormat ¤tFmt = 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