| |
| |
| #include "stdfx/shaderfx.h" |
| |
| |
| #include "stdfx.h" |
| #include "stdfx/shaderinterface.h" |
| #include "stdfx/shadingcontext.h" |
| |
| |
| #include "tfxparam.h" |
| #include "tparamset.h" |
| #include "trenderresourcemanager.h" |
| #include "trenderer.h" |
| |
| |
| #include "tthread.h" |
| #include "tfilepath.h" |
| #include "tstream.h" |
| #include "tfunctorinvoker.h" |
| #include "tmsgcore.h" |
| |
| |
| #include <QDir> |
| #include <QGLShaderProgram> |
| #include <QCoreApplication> |
| |
| |
| #include <GL/glew.h> |
| |
| |
| #include "tcg/tcg_function_types.h" |
| #include "tcg/tcg_unique_ptr.h" |
| |
| |
| #include <boost/any.hpp> |
| #include <boost/iterator/transform_iterator.hpp> |
| #include <boost/ptr_container/ptr_vector.hpp> |
| |
| |
| |
| #ifdef DIAGNOSTICS |
| #include "diagnostics.h" |
| #endif |
| |
| |
| |
| |
| |
| class ShaderFxDeclaration; |
| |
| |
| |
| |
| |
| |
| |
| namespace |
| { |
| |
| |
| |
| struct ContextLocker { |
| ShadingContext &m_ctx; |
| bool m_locked; |
| |
| public: |
| ContextLocker(ShadingContext &ctx) |
| : m_ctx(ctx), m_locked(false) { relock(); } |
| ~ContextLocker() |
| { |
| if (m_locked) |
| unlock(); |
| } |
| |
| void relock() |
| { |
| assert(!m_locked), m_locked = true; |
| m_ctx.makeCurrent(); |
| } |
| |
| void unlock() |
| { |
| assert(m_locked), m_locked = false; |
| m_ctx.doneCurrent(); |
| } |
| }; |
| |
| struct ProgramBinder { |
| QGLShaderProgram *m_prog; |
| |
| public: |
| ProgramBinder(QGLShaderProgram *prog) : m_prog(prog) { m_prog->bind(); } |
| ~ProgramBinder() { m_prog->release(); } |
| }; |
| |
| struct RectF { |
| GLfloat m_val[4]; |
| RectF(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1) { m_val[0] = x0, m_val[1] = y0, m_val[2] = x1, m_val[3] = y1; } |
| RectF(const TRectD &rect) { m_val[0] = rect.x0, m_val[1] = rect.y0, m_val[2] = rect.x1, m_val[3] = rect.y1; } |
| |
| operator TRectD() const { return TRectD(m_val[0], m_val[1], m_val[2], m_val[3]); } |
| bool operator==(const RectF &rect) const { return (memcmp(m_val, rect.m_val, sizeof(this)) == 0); } |
| }; |
| |
| struct AffineF { |
| GLfloat m_val[9]; |
| operator TAffine() const { return TAffine(m_val[0], m_val[3], m_val[6], m_val[1], m_val[4], m_val[7]); } |
| |
| }; |
| |
| |
| |
| typedef std::map<QString, ShaderFxDeclaration *> FxDeclarationsMap; |
| FxDeclarationsMap l_shaderFxDeclarations; |
| |
| enum Measures { NONE, |
| PERCENT, |
| LENGTH, |
| ANGLE, |
| MEASURESCOUNT }; |
| |
| static const std::string l_measureNames[MEASURESCOUNT] = { |
| "", "percentage", "fxLength", "angle"}; |
| |
| static const TParamUIConcept::Type l_conceptTypes[ShaderInterface::CONCEPTSCOUNT - ShaderInterface::UI_CONCEPTS] = { |
| TParamUIConcept::RADIUS, TParamUIConcept::WIDTH, TParamUIConcept::ANGLE, TParamUIConcept::POINT, |
| TParamUIConcept::POINT_2, TParamUIConcept::VECTOR, TParamUIConcept::POLAR, TParamUIConcept::SIZE, |
| TParamUIConcept::QUAD, TParamUIConcept::RECT}; |
| |
| |
| |
| inline bool isObsolete(const TFilePath &fp, const QDateTime &lastModified) |
| { |
| QFileInfo fInfo(QString::fromStdWString(fp.getWideString())); |
| return (lastModified != fInfo.lastModified()); |
| } |
| |
| inline TRectD tileRect(const TTile &tile) |
| { |
| const TDimension &dim = tile.getRaster()->getSize(); |
| return TRectD(tile.m_pos, TDimensionD(dim.lx, dim.ly)); |
| } |
| |
| inline void ceilRect(TRectD &rect) |
| { |
| rect.x0 = tfloor(rect.x0), rect.y0 = tfloor(rect.y0); |
| rect.x1 = tceil(rect.x1), rect.y1 = tceil(rect.y1); |
| } |
| |
| } |
| |
| |
| |
| |
| |
| class ShaderFx : public TStandardZeraryFx |
| { |
| FX_PLUGIN_DECLARATION(ShaderFx) |
| |
| const ShaderInterface *m_shaderInterface; |
| std::vector<boost::any> m_params; |
| |
| std::vector<TParamUIConcept> m_uiConcepts; |
| boost::ptr_vector<TRasterFxPort> m_inputPorts; |
| |
| public: |
| ShaderFx() : m_shaderInterface() { assert(false); } |
| ShaderFx(const ShaderInterface *shaderInterface) |
| : m_shaderInterface(shaderInterface) { initialize(); } |
| |
| |
| void initialize(); |
| |
| void getParamUIs(TParamUIConcept *¶ms, int &length); |
| bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info); |
| bool canHandle(const TRenderSettings &info, double frame); |
| |
| void doDryCompute(TRectD &rect, double frame, const TRenderSettings &ri); |
| void doCompute(TTile &tile, double frame, const TRenderSettings &ri); |
| |
| private: |
| QGLShaderProgram *touchShaderProgram( |
| const ShaderInterface::ShaderData &sd, |
| ShadingContext &context, |
| int varyingsCount = 0, const GLchar **varyings = 0); |
| |
| void bindParameters(QGLShaderProgram *shaderProgram, double frame); |
| |
| void bindWorldTransform(QGLShaderProgram *shaderProgram, const TAffine &worldToDst); |
| |
| void getInputData(const TRectD &rect, double frame, const TRenderSettings &ri, |
| std::vector<TRectD> &inputRects, std::vector<TAffine> &inputAffines, |
| ShadingContext &context); |
| }; |
| |
| |
| |
| |
| |
| class ShaderFxDeclaration : public TFxDeclaration |
| { |
| ShaderInterface m_shaderInterface; |
| |
| public: |
| ShaderFxDeclaration(const ShaderInterface &shaderInterface) |
| : TFxDeclaration(TFxInfo( |
| shaderInterface.mainShader().m_name.toStdString(), false)), |
| m_shaderInterface(shaderInterface) {} |
| |
| TPersist *create() const |
| { |
| return new ShaderFx(&m_shaderInterface); |
| } |
| }; |
| |
| |
| |
| |
| |
| class ShadingContextManager : public QObject |
| { |
| mutable QMutex m_mutex; |
| |
| ShadingContext m_shadingContext; |
| TAtomicVar m_activeRenderInstances; |
| |
| public: |
| ShadingContextManager() |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| QObject *mainScopeBoundObject = QCoreApplication::instance()->findChild<QObject *>("mainScope"); |
| |
| assert(thread() == mainScopeBoundObject->thread()); |
| setParent(mainScopeBoundObject); |
| } |
| |
| static ShadingContextManager *instance() |
| { |
| static ShadingContextManager *theManager = new ShadingContextManager; |
| return theManager; |
| } |
| |
| QMutex *mutex() const { return &m_mutex; } |
| |
| const ShadingContext &shadingContext() const { return m_shadingContext; } |
| ShadingContext &shadingContext() { return m_shadingContext; } |
| |
| void onRenderInstanceStart() |
| { |
| ++m_activeRenderInstances; |
| } |
| |
| void onRenderInstanceEnd() |
| { |
| if (--m_activeRenderInstances == 0) { |
| QMutexLocker mLocker(&m_mutex); |
| |
| |
| ::ContextLocker cLocker(m_shadingContext); |
| m_shadingContext.resize(0, 0); |
| |
| #ifdef DIAGNOSTICS |
| DIAGNOSTICS_DUMP("ShaderLogs"); |
| DIAGNOSTICS_CLEAR; |
| #endif |
| } |
| } |
| |
| ShadingContext::Support touchSupport() |
| { |
| struct { |
| ShadingContextManager *m_this; |
| ShadingContext::Support support() |
| { |
| QMutexLocker mLocker(&m_this->m_mutex); |
| ::ContextLocker cLocker(m_this->m_shadingContext); |
| |
| return ShadingContext::support(); |
| } |
| } locals = {this}; |
| |
| static ShadingContext::Support sup = locals.support(); |
| |
| static bool sentMsg = false; |
| if (!sentMsg) { |
| switch (sup) { |
| case ShadingContext::NO_PIXEL_BUFFER: |
| DVGui::warning(QGLShaderProgram::tr( |
| "This system configuration does not support OpenGL Pixel Buffers. Shader Fxs will not be able to render.")); |
| break; |
| |
| case ShadingContext::NO_SHADERS: |
| DVGui::warning(QGLShaderProgram::tr( |
| "This system configuration does not support OpenGL Shader Programs. Shader Fxs will not be able to render.")); |
| break; |
| } |
| |
| sentMsg = true; |
| } |
| |
| return sup; |
| } |
| }; |
| |
| template class DV_EXPORT_API TFxDeclarationT<ShaderFx>; |
| |
| |
| |
| |
| |
| class MessageCreateContext : public TThread::Message |
| { |
| ShadingContextManager *man; |
| |
| public: |
| MessageCreateContext(ShadingContextManager *ctx) |
| : man(ctx) {} |
| |
| void onDeliver() |
| { |
| man->onRenderInstanceEnd(); |
| } |
| |
| TThread::Message *clone() const |
| { |
| return new MessageCreateContext(*this); |
| } |
| }; |
| |
| class SCMDelegate : public TRenderResourceManager |
| { |
| T_RENDER_RESOURCE_MANAGER |
| |
| void onRenderInstanceStart(unsigned long id) |
| { |
| ShadingContextManager::instance()->onRenderInstanceStart(); |
| } |
| |
| void onRenderInstanceEnd(unsigned long id) |
| { |
| if (!TThread::isMainThread()) { |
| |
| MessageCreateContext(ShadingContextManager::instance()).sendBlocking(); |
| } else { |
| ShadingContextManager::instance()->onRenderInstanceEnd(); |
| } |
| } |
| }; |
| |
| |
| |
| class SCMDelegateGenerator : public TRenderResourceManagerGenerator |
| { |
| public: |
| SCMDelegateGenerator() : TRenderResourceManagerGenerator(false) |
| { |
| |
| |
| |
| |
| |
| |
| |
| struct InstanceSCM : public TFunctorInvoker::BaseFunctor { |
| void operator()() { ShadingContextManager::instance(); } |
| }; |
| |
| TFunctorInvoker::instance()->invokeQueued(new InstanceSCM); |
| } |
| |
| TRenderResourceManager *operator()() { return new SCMDelegate; } |
| }; |
| |
| MANAGER_FILESCOPE_DECLARATION(SCMDelegate, SCMDelegateGenerator) |
| |
| |
| |
| |
| |
| void ShaderFx::initialize() |
| { |
| struct { |
| ShaderFx *m_this; |
| |
| inline void addUiConcept(const ShaderInterface::Parameter &siParam, const TParamP ¶m) |
| { |
| if (siParam.m_concept.m_type >= ShaderInterface::UI_CONCEPTS && |
| siParam.m_concept.m_type < ShaderInterface::CONCEPTSCOUNT) { |
| m_this->m_uiConcepts.push_back(TParamUIConcept()); |
| |
| TParamUIConcept &uiConcept = m_this->m_uiConcepts.back(); |
| uiConcept.m_type = ::l_conceptTypes[siParam.m_concept.m_type - ShaderInterface::UI_CONCEPTS]; |
| uiConcept.m_label = siParam.m_concept.m_label.toStdString(); |
| uiConcept.m_params.push_back(param); |
| } |
| } |
| |
| inline void addUiConcept(const ShaderInterface::ParameterConcept &concept) |
| { |
| if (!concept.isUI() || concept.m_parameterNames.empty()) |
| return; |
| |
| TParamUIConcept uiConcept = { |
| ::l_conceptTypes[concept.m_type - ShaderInterface::UI_CONCEPTS], |
| concept.m_label.toStdString()}; |
| |
| int n, nCount = int(concept.m_parameterNames.size()); |
| for (n = 0; n != nCount; ++n) { |
| TParam *param = m_this->getParams()->getParam(concept.m_parameterNames[n].toStdString()); |
| if (!param) |
| break; |
| |
| uiConcept.m_params.push_back(param); |
| } |
| |
| if (uiConcept.m_params.size() == concept.m_parameterNames.size()) |
| m_this->m_uiConcepts.push_back(uiConcept); |
| } |
| |
| } locals = {this}; |
| |
| assert(m_params.empty()); |
| |
| |
| const std::vector<ShaderInterface::Parameter> &siParams = |
| m_shaderInterface->parameters(); |
| |
| int p, pCount = int(siParams.size()); |
| m_params.reserve(pCount); |
| |
| for (p = 0; p != pCount; ++p) { |
| const ShaderInterface::Parameter &siParam = siParams[p]; |
| |
| switch (siParam.m_type) { |
| case ShaderInterface::BOOL: { |
| TBoolParamP param(siParam.m_default.m_bool); |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TBoolParamP>(&m_params.back())); |
| |
| break; |
| } |
| |
| case ShaderInterface::FLOAT: { |
| TDoubleParamP param(siParam.m_default.m_float); |
| param->setValueRange(siParam.m_range[0].m_float, siParam.m_range[1].m_float); |
| |
| locals.addUiConcept(siParam, param); |
| |
| switch (siParam.m_concept.m_type) { |
| case ShaderInterface::PERCENT: |
| param->setMeasureName(l_measureNames[PERCENT]); |
| break; |
| |
| case ShaderInterface::LENGTH: |
| case ShaderInterface::RADIUS_UI: |
| case ShaderInterface::WIDTH_UI: |
| case ShaderInterface::SIZE_UI: |
| param->setMeasureName(l_measureNames[LENGTH]); |
| break; |
| |
| case ShaderInterface::ANGLE: |
| case ShaderInterface::ANGLE_UI: |
| param->setMeasureName(l_measureNames[ANGLE]); |
| break; |
| } |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TDoubleParamP>(&m_params.back())); |
| break; |
| } |
| |
| case ShaderInterface::VEC2: { |
| TPointParamP param(TPointD( |
| siParam.m_default.m_vec2[0], siParam.m_default.m_vec2[1])); |
| |
| param->getX()->setValueRange(siParam.m_range[0].m_vec2[0], siParam.m_range[1].m_vec2[0]); |
| param->getY()->setValueRange(siParam.m_range[0].m_vec2[1], siParam.m_range[1].m_vec2[1]); |
| |
| locals.addUiConcept(siParam, param); |
| |
| switch (siParam.m_concept.m_type) { |
| case ShaderInterface::PERCENT: |
| param->getX()->setMeasureName(l_measureNames[PERCENT]); |
| param->getY()->setMeasureName(l_measureNames[PERCENT]); |
| break; |
| |
| case ShaderInterface::LENGTH: |
| case ShaderInterface::POINT: |
| case ShaderInterface::POINT_UI: |
| case ShaderInterface::VECTOR_UI: |
| case ShaderInterface::WIDTH_UI: |
| case ShaderInterface::SIZE_UI: |
| param->getX()->setMeasureName(l_measureNames[LENGTH]); |
| param->getY()->setMeasureName(l_measureNames[LENGTH]); |
| break; |
| |
| case ShaderInterface::ANGLE: |
| case ShaderInterface::ANGLE_UI: |
| param->getX()->setMeasureName(l_measureNames[ANGLE]); |
| param->getY()->setMeasureName(l_measureNames[ANGLE]); |
| break; |
| } |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TPointParamP>(&m_params.back())); |
| break; |
| } |
| |
| case ShaderInterface::INT: { |
| TIntParamP param(siParam.m_default.m_int); |
| param->setValueRange(siParam.m_range[0].m_int, siParam.m_range[1].m_int); |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TIntParamP>(&m_params.back())); |
| break; |
| } |
| |
| case ShaderInterface::RGBA: { |
| TPixelParamP param(TPixel32( |
| siParam.m_default.m_rgba[0], siParam.m_default.m_rgba[1], |
| siParam.m_default.m_rgba[2], siParam.m_default.m_rgba[3])); |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TPixelParamP>(&m_params.back())); |
| break; |
| } |
| |
| case ShaderInterface::RGB: { |
| TPixelParamP param(TPixel32( |
| siParam.m_default.m_rgb[0], siParam.m_default.m_rgb[1], |
| siParam.m_default.m_rgb[2])); |
| |
| param->enableMatte(false); |
| |
| m_params.push_back(param); |
| bindParam(this, siParam.m_name.toStdString(), |
| *boost::unsafe_any_cast<TPixelParamP>(&m_params.back())); |
| break; |
| } |
| } |
| } |
| |
| |
| const std::vector<ShaderInterface::ParameterConcept> &parConcepts = m_shaderInterface->m_parConcepts; |
| |
| int c, cCount = int(parConcepts.size()); |
| for (c = 0; c != cCount; ++c) |
| locals.addUiConcept(parConcepts[c]); |
| |
| |
| const std::vector<QString> &inputPorts = m_shaderInterface->inputPorts(); |
| |
| int i, iCount = int(inputPorts.size()); |
| m_inputPorts.reserve(iCount); |
| |
| for (i = 0; i != iCount; ++i) { |
| m_inputPorts.push_back(new TRasterFxPort); |
| addInputPort(inputPorts[i].toStdString(), m_inputPorts[i]); |
| } |
| } |
| |
| |
| |
| void ShaderFx::getParamUIs(TParamUIConcept *¶ms, int &length) |
| { |
| length = int(m_uiConcepts.size()); |
| params = new TParamUIConcept[length]; |
| |
| std::copy(m_uiConcepts.begin(), m_uiConcepts.end(), params); |
| } |
| |
| |
| |
| bool ShaderFx::doGetBBox(double frame, TRectD &bbox, const TRenderSettings &info) |
| { |
| static const ::RectF infiniteRectF( |
| -(std::numeric_limits<GLfloat>::max)(), -(std::numeric_limits<GLfloat>::max)(), |
| (std::numeric_limits<GLfloat>::max)(), (std::numeric_limits<GLfloat>::max)()); |
| |
| bbox = TConsts::infiniteRectD; |
| |
| const ShaderInterface::ShaderData &sd = m_shaderInterface->bboxShader(); |
| if (!sd.isValid()) |
| return true; |
| |
| ShadingContextManager *manager = ShadingContextManager::instance(); |
| if (manager->touchSupport() != ShadingContext::OK) |
| return true; |
| |
| |
| ::RectF bboxF(infiniteRectF); |
| |
| QMutexLocker mLocker(manager->mutex()); |
| |
| |
| std::shared_ptr<ShadingContext> shadingContextPtr(new ShadingContext); |
| ShadingContext &context = *shadingContextPtr.get(); |
| |
| ::ContextLocker cLocker(context); |
| |
| |
| QGLShaderProgram *prog = 0; |
| { |
| const GLchar *varyingNames[] = {"outputBBox"}; |
| prog = touchShaderProgram(sd, context, 1, &varyingNames[0]); |
| } |
| |
| int pCount = getInputPortCount(); |
| |
| std::vector<RectF> inputBBoxes(pCount, ::RectF(TRectD())); |
| |
| for (int p = 0; p != pCount; ++p) { |
| TRasterFxPort &port = m_inputPorts[p]; |
| if (port.isConnected()) { |
| TRectD inputBBox; |
| |
| cLocker.unlock(); |
| mLocker.unlock(); |
| |
| if (port->doGetBBox(frame, inputBBox, info)) |
| inputBBoxes[p] = (inputBBox == TConsts::infiniteRectD) ? infiniteRectF : ::RectF(inputBBox); |
| |
| mLocker.relock(); |
| cLocker.relock(); |
| } |
| } |
| |
| { |
| ProgramBinder progBinder(prog); |
| |
| |
| bindParameters(prog, frame); |
| |
| prog->setUniformValue("infiniteRect", |
| infiniteRectF.m_val[0], infiniteRectF.m_val[1], infiniteRectF.m_val[2], infiniteRectF.m_val[3]); |
| |
| prog->setUniformValueArray("inputBBox", inputBBoxes[0].m_val, |
| int(inputBBoxes.size()), 4); |
| |
| |
| const GLsizeiptr varyingSizes[] = {sizeof(::RectF)}; |
| GLvoid *bufs[] = {bboxF.m_val}; |
| |
| context.transformFeedback(1, varyingSizes, bufs); |
| } |
| |
| |
| bbox = (bboxF == infiniteRectF) ? TConsts::infiniteRectD : TRectD(bboxF); |
| return true; |
| } |
| |
| |
| |
| bool ShaderFx::canHandle(const TRenderSettings &info, double frame) |
| { |
| return (m_shaderInterface->hwtType() == ShaderInterface::ANY) ? true : isAlmostIsotropic(info.m_affine); |
| } |
| |
| |
| |
| QGLShaderProgram *ShaderFx::touchShaderProgram( |
| const ShaderInterface::ShaderData &sd, |
| ShadingContext &context, |
| int varyingsCount, const GLchar **varyings) |
| { |
| typedef std::pair<QGLShaderProgram *, QDateTime> CompiledShader; |
| |
| struct locals { |
| inline static void logCompilation(QGLShaderProgram *program) |
| { |
| |
| |
| const QObjectList &children = program->children(); |
| |
| int c, cCount = children.size(); |
| for (c = 0; c != cCount; ++c) { |
| if (QGLShader *shader = dynamic_cast<QGLShader *>(children[c])) { |
| const QString &log = shader->log(); |
| if (!log.isEmpty()) |
| DVGui::info(log); |
| } |
| } |
| |
| |
| const QString &log = program->log(); |
| if (!log.isEmpty()) |
| DVGui::info(log); |
| } |
| }; |
| |
| |
| |
| CompiledShader cs = context.shaderData(sd.m_name); |
| if (!cs.first || ::isObsolete(sd.m_path, cs.second)) { |
| cs = m_shaderInterface->makeProgram(sd, varyingsCount, varyings); |
| context.addShaderProgram(sd.m_name, cs.first, cs.second); |
| |
| locals::logCompilation(cs.first); |
| } |
| |
| assert(cs.first); |
| return cs.first; |
| } |
| |
| |
| |
| void ShaderFx::bindParameters( |
| QGLShaderProgram *program, double frame) |
| { |
| |
| const std::vector<ShaderInterface::Parameter> &siParams = |
| m_shaderInterface->parameters(); |
| |
| assert(siParams.size() == m_params.size()); |
| |
| int p, pCount = int(siParams.size()); |
| for (p = 0; p != pCount; ++p) { |
| const ShaderInterface::Parameter &siParam = siParams[p]; |
| |
| switch (siParam.m_type) { |
| case ShaderInterface::BOOL: { |
| const TBoolParamP ¶m = *boost::unsafe_any_cast<TBoolParamP>(&m_params[p]); |
| program->setUniformValue(siParam.m_name.toUtf8().data(), (GLboolean)param->getValue()); |
| break; |
| } |
| |
| case ShaderInterface::FLOAT: { |
| const TDoubleParamP ¶m = *boost::unsafe_any_cast<TDoubleParamP>(&m_params[p]); |
| program->setUniformValue(siParam.m_name.toUtf8().data(), |
| (GLfloat)param->getValue(frame)); |
| break; |
| } |
| |
| case ShaderInterface::VEC2: { |
| const TPointParamP ¶m = *boost::unsafe_any_cast<TPointParamP>(&m_params[p]); |
| |
| const TPointD &value = param->getValue(frame); |
| program->setUniformValue(siParam.m_name.toUtf8().data(), |
| (GLfloat)value.x, (GLfloat)value.y); |
| break; |
| } |
| |
| case ShaderInterface::INT: { |
| const TIntParamP ¶m = *boost::unsafe_any_cast<TIntParamP>(&m_params[p]); |
| program->setUniformValue(siParam.m_name.toUtf8().data(), (GLint)param->getValue()); |
| break; |
| } |
| |
| case ShaderInterface::RGBA: |
| case ShaderInterface::RGB: { |
| const TPixelParamP ¶m = *boost::unsafe_any_cast<TPixelParamP>(&m_params[p]); |
| |
| const TPixel32 &value = param->getValue(frame); |
| program->setUniformValue(siParam.m_name.toUtf8().data(), |
| (GLfloat)value.r / 255.0f, (GLfloat)value.g / 255.0f, |
| (GLfloat)value.b / 255.0f, (GLfloat)value.m / 255.0f); |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| |
| void ShaderFx::bindWorldTransform(QGLShaderProgram *program, const TAffine &worldToDst) |
| { |
| |
| #if QT_VERSION >= 0x050500 |
| float qwToD[9] = {static_cast<float>(worldToDst.a11), static_cast<float>(worldToDst.a12), static_cast<float>(worldToDst.a13), |
| static_cast<float>(worldToDst.a21), static_cast<float>(worldToDst.a22), static_cast<float>(worldToDst.a23), |
| 0.0f, 0.0f, 1.0f}; |
| #else |
| qreal qwToD[9] = {worldToDst.a11, worldToDst.a12, worldToDst.a13, |
| worldToDst.a21, worldToDst.a22, worldToDst.a23, |
| 0.0, 0.0, 1.0}; |
| #endif |
| program->setUniformValue("worldToOutput", QMatrix3x3(qwToD)); |
| |
| const TAffine &dToW = worldToDst.inv(); |
| #if QT_VERSION >= 0x050500 |
| float qdToW[9] = {static_cast<float>(dToW.a11), static_cast<float>(dToW.a12), static_cast<float>(dToW.a13), |
| static_cast<float>(dToW.a21), static_cast<float>(dToW.a22), static_cast<float>(dToW.a23), |
| 0.0f, 0.0f, 1.0f}; |
| #else |
| qreal qdToW[9] = {dToW.a11, dToW.a12, dToW.a13, |
| dToW.a21, dToW.a22, dToW.a23, |
| 0.0, 0.0, 1.0}; |
| #endif |
| program->setUniformValue("outputToWorld", QMatrix3x3(qdToW)); |
| } |
| |
| |
| |
| void ShaderFx::getInputData(const TRectD &rect, double frame, const TRenderSettings &ri, |
| std::vector<TRectD> &inputRects, std::vector<TAffine> &inputAffines, |
| ShadingContext &context) |
| { |
| struct locals { |
| static inline void addNames(std::vector<std::string> &names, const char *prefix, int pCount) |
| { |
| for (int p = 0; p != pCount; ++p) |
| names.push_back((prefix + QString("[%1]").arg(p)).toStdString()); |
| } |
| }; |
| |
| const ShaderInterface::ShaderData &sd = m_shaderInterface->inputPortsShader(); |
| if (!sd.isValid()) { |
| inputRects.resize(getInputPortCount()); |
| std::fill(inputRects.begin(), inputRects.end(), rect); |
| |
| inputAffines.resize(getInputPortCount()); |
| std::fill(inputAffines.begin(), inputAffines.end(), ri.m_affine); |
| |
| return; |
| } |
| |
| |
| |
| std::vector<GLfloat> buf; |
| int pCount = getInputPortCount(); |
| |
| |
| QGLShaderProgram *prog = 0; |
| { |
| |
| |
| |
| const GLchar *varyingPrefixes[] = {"inputRect", "worldToInput"}; |
| const int varyingsCount = sizeof(varyingPrefixes) / sizeof(GLchar *); |
| |
| std::vector<std::string> varyingStrings; |
| varyingStrings.reserve(varyingsCount); |
| |
| for (int v = 0; v != varyingsCount; ++v) |
| locals::addNames(varyingStrings, varyingPrefixes[v], pCount); |
| |
| #if defined(__APPLE_CC__) |
| |
| std::vector<const GLchar *> varyingNames(varyingStrings.size()); |
| auto conv = [](const std::string &i) { return i.c_str(); }; |
| std::transform(varyingStrings.begin(), varyingStrings.end(), varyingNames.begin(), conv); |
| #else |
| std::vector<const GLchar *> varyingNames( |
| boost::make_transform_iterator(varyingStrings.begin(), std::mem_fun_ref(&std::string::c_str)), |
| boost::make_transform_iterator(varyingStrings.end(), std::mem_fun_ref(&std::string::c_str))); |
| #endif |
| prog = touchShaderProgram(sd, context, int(varyingNames.size()), &varyingNames[0]); |
| } |
| |
| { |
| ProgramBinder progBinder(prog); |
| |
| |
| int bufFloatsCount = pCount * (sizeof(RectF) + sizeof(AffineF)) / sizeof(GLfloat); |
| buf.resize(bufFloatsCount); |
| |
| |
| bindParameters(prog, frame); |
| bindWorldTransform(prog, ri.m_affine); |
| |
| prog->setUniformValue("outputRect", |
| (GLfloat)rect.x0, (GLfloat)rect.y0, (GLfloat)rect.x1, (GLfloat)rect.y1); |
| |
| |
| const GLsizeiptr varyingSizes[] = {static_cast<GLsizeiptr>(bufFloatsCount * sizeof(GLfloat))}; |
| GLvoid *bufs[] = {&buf[0]}; |
| |
| context.transformFeedback(1, varyingSizes, bufs); |
| |
| #ifdef TRANSFORM_FEEDBACK_COUT |
| std::cout << "trFeedback: "; |
| for (int f = 0; f != bufFloatsCount; ++f) |
| std::cout << buf[f] << " "; |
| std::cout << "\n" << std::endl; |
| #endif |
| } |
| |
| |
| const RectF *rBufBegin(reinterpret_cast<const RectF *>(&buf[0])), *rBufEnd(rBufBegin + pCount); |
| std::copy(rBufBegin, rBufEnd, &inputRects[0]); |
| |
| const AffineF *aBufBegin(reinterpret_cast<const AffineF *>(rBufEnd)), *aBufEnd(aBufBegin + pCount); |
| std::copy(aBufBegin, aBufEnd, &inputAffines[0]); |
| } |
| |
| |
| |
| void ShaderFx::doCompute(TTile &tile, double frame, const TRenderSettings &info) |
| { |
| struct locals { |
| |
| struct TexturesStorage { |
| ShadingContext &m_ctx; |
| std::vector<GLuint> m_texIds; |
| |
| TexturesStorage(ShadingContext &ctx, int pCount) |
| : m_ctx(ctx) |
| { |
| m_texIds.reserve(pCount); |
| } |
| |
| ~TexturesStorage() |
| { |
| typedef tcg::function<void (ShadingContext::*)(GLuint), |
| &ShadingContext::unloadTexture> UnloadFunc; |
| |
| std::for_each(m_texIds.begin(), m_texIds.end(), |
| tcg::bind1st(UnloadFunc(), m_ctx)); |
| } |
| |
| void load(const TRasterP &ras, GLuint texUnit) |
| { |
| if (ras) |
| m_texIds.push_back(m_ctx.loadTexture(ras, texUnit)); |
| } |
| }; |
| |
| inline static QGLFramebufferObjectFormat makeFormat(int bpp) |
| { |
| QGLFramebufferObjectFormat fmt; |
| if (bpp == 64) |
| fmt.setInternalTextureFormat(GL_RGBA16); |
| return fmt; |
| } |
| |
| inline static void touchOutputSize(ShadingContext &context, const TDimension &size, int bpp) |
| { |
| const QGLFramebufferObjectFormat &fmt = makeFormat(bpp); |
| |
| const TDimension ¤tSize = context.size(); |
| const QGLFramebufferObjectFormat ¤tFmt = context.format(); |
| |
| if (currentSize.lx < size.lx || currentSize.ly < size.ly || |
| currentFmt != fmt) |
| context.resize(std::max(size.lx, currentSize.lx), std::max(size.ly, currentSize.ly), fmt); |
| } |
| }; |
| |
| ShadingContextManager *manager = ShadingContextManager::instance(); |
| if (manager->touchSupport() != ShadingContext::OK) |
| return; |
| |
| QMutexLocker mLocker(manager->mutex()); |
| |
| |
| std::shared_ptr<ShadingContext> shadingContextPtr(new ShadingContext); |
| ShadingContext &context = *shadingContextPtr.get(); |
| |
| |
| int pCount = getInputPortCount(); |
| |
| const TRectD &tileRect = ::tileRect(tile); |
| |
| std::vector<TRectD> inputRects(pCount); |
| std::vector<TAffine> inputAffines(pCount); |
| |
| |
| ::ContextLocker cLocker(context); |
| |
| tcg::unique_ptr<TTile[]> inTiles(new TTile[pCount]); |
| |
| if (pCount > 0) |
| { |
| getInputData(tileRect, frame, info, inputRects, inputAffines, context); |
| |
| |
| cLocker.unlock(); |
| mLocker.unlock(); |
| |
| for (int p = 0; p != pCount; ++p) { |
| TRasterFxPort &port = m_inputPorts[p]; |
| if (port.isConnected()) { |
| |
| TRectD &inRect = inputRects[p]; |
| if (inRect.getLx() > 0.0 && inRect.getLy() > 0.0) { |
| ::ceilRect(inRect); |
| |
| TRenderSettings inputInfo(info); |
| inputInfo.m_affine = inputAffines[p]; |
| |
| #ifdef TRANSFORM_FEEDBACK_COUT |
| const TAffine &inAff = inputAffines[p]; |
| std::cout << "inRect " << p << ": " << inRect.x0 << " " << inRect.y0 << " " << inRect.x1 << " " << inRect.y1 << "\n"; |
| std::cout << "inAff " << p << ": " << inAff.a11 << " " << inAff.a12 << " " << inAff.a13 << "\n"; |
| std::cout << " " << inAff.a21 << " " << inAff.a22 << " " << inAff.a23 << "\n" << std::endl; |
| #endif |
| |
| port->allocateAndCompute(inTiles[p], inRect.getP00(), |
| TDimension(tround(inRect.getLx()), tround(inRect.getLy())), tile.getRaster(), frame, inputInfo); |
| } |
| } |
| } |
| |
| |
| mLocker.relock(); |
| cLocker.relock(); |
| |
| |
| |
| |
| } |
| |
| |
| { |
| locals::touchOutputSize(context, tile.getRaster()->getSize(), info.m_bpp); |
| |
| QGLShaderProgram *program = touchShaderProgram(m_shaderInterface->mainShader(), context); |
| { |
| ProgramBinder binder(program); |
| |
| |
| bindParameters(program, frame); |
| bindWorldTransform(program, TTranslation(-tile.m_pos) * info.m_affine); |
| |
| |
| locals::TexturesStorage texStorage(context, pCount); |
| |
| if (pCount > 0) { |
| std::vector<GLint> inputs(pCount); |
| std::vector<QMatrix3x3> screenToInput(pCount); |
| std::vector<QMatrix3x3> inputToScreen(pCount); |
| |
| for (int p = 0; p != pCount; ++p) { |
| TAffine iToS( |
| TTranslation(-tile.m_pos) * |
| info.m_affine * |
| inputAffines[p].inv() * |
| TTranslation(inputRects[p].getP00()) * |
| TScale(inputRects[p].getLx(), inputRects[p].getLy())); |
| |
| TAffine sToI(iToS.inv()); |
| |
| #if QT_VERSION >= 0x050500 |
| float qiToS[9] = {static_cast<float>(iToS.a11), static_cast<float>(iToS.a12), static_cast<float>(iToS.a13), |
| static_cast<float>(iToS.a21), static_cast<float>(iToS.a22), static_cast<float>(iToS.a23), |
| 0.0f, 0.0f, 1.0f}; |
| float qsToI[9] = {static_cast<float>(sToI.a11), static_cast<float>(sToI.a12), static_cast<float>(sToI.a13), |
| static_cast<float>(sToI.a21), static_cast<float>(sToI.a22), static_cast<float>(sToI.a23), |
| 0.0f, 0.0f, 1.0f}; |
| #else |
| qreal qiToS[9] = {iToS.a11, iToS.a12, iToS.a13, |
| iToS.a21, iToS.a22, iToS.a23, |
| 0.0, 0.0, 1.0}; |
| |
| qreal qsToI[9] = {sToI.a11, sToI.a12, sToI.a13, |
| sToI.a21, sToI.a22, sToI.a23, |
| 0.0, 0.0, 1.0}; |
| #endif |
| inputs[p] = p, screenToInput[p] = QMatrix3x3(qsToI), inputToScreen[p] = QMatrix3x3(qiToS); |
| } |
| |
| program->setUniformValueArray("inputImage", &inputs[0], pCount); |
| program->setUniformValueArray("outputToInput", &screenToInput[0], pCount); |
| program->setUniformValueArray("inputToOutput", &inputToScreen[0], pCount); |
| |
| |
| for (int p = 0; p != pCount; ++p) |
| texStorage.load(inTiles[p].getRaster(), p); |
| } |
| |
| #ifdef DIAGNOSTICS |
| DIAGNOSTICS_TIMER("Shader Overall Times | " + m_shaderInterface->m_mainShader.m_name); |
| #endif |
| |
| context.draw(tile.getRaster()); |
| } |
| } |
| } |
| |
| |
| |
| void ShaderFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) |
| { |
| ShadingContextManager *manager = ShadingContextManager::instance(); |
| if (manager->touchSupport() != ShadingContext::OK) |
| return; |
| |
| QMutexLocker mLocker(manager->mutex()); |
| |
| |
| std::shared_ptr<ShadingContext> shadingContextPtr(new ShadingContext); |
| ShadingContext &context = *shadingContextPtr.get(); |
| |
| int pCount = getInputPortCount(); |
| if (pCount > 0) { |
| ::ContextLocker cLocker(context); |
| |
| std::vector<TRectD> inputRects(pCount); |
| std::vector<TAffine> inputAffines(pCount); |
| |
| getInputData(rect, frame, info, inputRects, inputAffines, context); |
| |
| for (int p = 0; p != pCount; ++p) { |
| TRasterFxPort &port = m_inputPorts[p]; |
| if (port.isConnected()) { |
| TRectD &inRect = inputRects[p]; |
| if (inRect.getLx() > 0.0 && inRect.getLy() > 0.0) { |
| ::ceilRect(inRect); |
| |
| TRenderSettings inputInfo(info); |
| inputInfo.m_affine = inputAffines[p]; |
| |
| cLocker.unlock(); |
| mLocker.unlock(); |
| |
| port->dryCompute(inRect, frame, inputInfo); |
| |
| mLocker.relock(); |
| cLocker.relock(); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| |
| const TPersistDeclaration *ShaderFx::getDeclaration() const |
| { |
| FxDeclarationsMap::iterator it = ::l_shaderFxDeclarations.find( |
| m_shaderInterface->mainShader().m_name); |
| |
| return (it == ::l_shaderFxDeclarations.end()) ? 0 : it->second; |
| } |
| |
| |
| |
| |
| |
| void loadShaderInterfaces(const TFilePath &shadersFolder) |
| { |
| |
| QDir shadersDir(QString::fromStdWString(shadersFolder.getWideString())); |
| |
| QStringList namesFilter("*.xml"); |
| QStringList files = shadersDir.entryList(namesFilter, QDir::Files, QDir::Name | QDir::LocaleAware); |
| |
| int f, fCount = files.size(); |
| for (f = 0; f != fCount; ++f) { |
| TIStream is(shadersFolder + TFilePath(files[f].toStdWString())); |
| |
| |
| ShaderInterface shaderInterface; |
| is >> shaderInterface; |
| |
| if (shaderInterface.isValid()) { |
| |
| ::l_shaderFxDeclarations.insert(std::make_pair( |
| shaderInterface.mainShader().m_name, new ShaderFxDeclaration(shaderInterface))); |
| } |
| } |
| } |
| |