diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 61823cf01c..a38769165b 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -10,16 +10,34 @@ // #include "RenderViewTask.h" -#include "AssembleLightingStageTask.h" #include "RenderShadowTask.h" #include "RenderDeferredTask.h" #include "RenderForwardTask.h" #include +void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { + const auto items = input.getN(0); + const auto lightingModel = input.getN(1); + const auto lightingStageFramesAndZones = input.getN(2); + + // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling + // is performed, then casters not in the view frustum will be removed, which is not what we wish. + const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); + const auto shadowTaskOut = task.addJob("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); + + const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); + task.addJob("RenderDeferredTask", renderDeferredInput); +} + +void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { + task.addBranch("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask); + + task.addBranch("RenderForwardTask", 1, input); +} + void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); - assert(items.canCast()); // Issue the lighting model, aka the big global settings for the view const auto lightingModel = task.addJob("LightingModel"); @@ -28,16 +46,8 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: const auto lightingStageFramesAndZones = task.addJob("AssembleStages", items); if (!DISABLE_DEFERRED) { - // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling - // is performed, then casters not in the view frustum will be removed, which is not what we wish. - const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); - const auto shadowTaskOut = task.addSwitchJob("RenderShadowTask", 0, shadowTaskIn, cullFunctor, tagBits, tagMask); - - const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); - task.addSwitchJob("RenderDeferredTask", 0, renderDeferredInput); - - const auto renderForwardInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); - task.addSwitchJob("RenderForwardTask", 1, renderForwardInput); + const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); + task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); } else { const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("RenderForwardTask", renderInput); diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index b4d9326944..cdb56a2189 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -15,15 +15,38 @@ #include #include +#include "AssembleLightingStageTask.h" + +class RenderShadowsAndDeferredTask { +public: + using Input = render::VaryingSet3; + using JobModel = render::Task::ModelI; + + RenderShadowsAndDeferredTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); + +}; + +class DeferredForwardSwitchJob { +public: + using Input = render::VaryingSet3; + using JobModel = render::Switch::ModelI; + + DeferredForwardSwitchJob() {} + + void configure(const render::SwitchConfig& config) {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); + +}; class RenderViewTask { public: using Input = RenderFetchCullSortTask::Output; - using JobModel = render::Task::ModelIS; + using JobModel = render::Task::ModelI; RenderViewTask() {} - void configure(const render::SwitchConfig& configuration) {} void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/task/src/task/Config.cpp b/libraries/task/src/task/Config.cpp index c836d06717..79e0b9f7fb 100644 --- a/libraries/task/src/task/Config.cpp +++ b/libraries/task/src/task/Config.cpp @@ -38,7 +38,7 @@ void JobConfig::setPresetList(const QJsonObject& object) { } } -void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::string& name) { +void JobConfig::connectChildConfig(std::shared_ptr childConfig, const std::string& name) { childConfig->setParent(this); childConfig->setObjectName(name.c_str()); @@ -52,7 +52,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin } } -void TaskConfig::transferChildrenConfigs(QConfigPointer source) { +void JobConfig::transferChildrenConfigs(std::shared_ptr source) { if (!source) { return; } @@ -70,13 +70,13 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) { } } -void TaskConfig::refresh() { +void JobConfig::refresh() { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "refresh"); return; } - _task->applyConfiguration(); + _jobConcept->applyConfiguration(); } TaskConfig* TaskConfig::getRootConfig(const std::string& jobPath, std::string& jobName) const { @@ -134,9 +134,9 @@ JobConfig* TaskConfig::getJobConfig(const std::string& jobPath) const { } } -void SwitchConfig::setSwitchIndex(uint8_t index) { - if (_switchIndex != index) { - _switchIndex = index; +void SwitchConfig::setBranch(uint8_t branch) { + if (_branch != branch) { + _branch = branch; // We can re-use this signal here emit dirtyEnabled(); } diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index d158e16eec..891970006d 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -154,6 +154,11 @@ public: */ Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; } + void connectChildConfig(std::shared_ptr childConfig, const std::string& name); + void transferChildrenConfigs(std::shared_ptr source); + + JobConcept* _jobConcept; + public slots: /**jsdoc @@ -162,6 +167,11 @@ public slots: */ void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } + /**jsdoc + * @function Render.refresh + */ + void refresh(); + signals: /**jsdoc @@ -248,30 +258,18 @@ public: auto subs = getSubConfigs(); return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]); } - - void connectChildConfig(QConfigPointer childConfig, const std::string& name); - void transferChildrenConfigs(QConfigPointer source); - - JobConcept* _task; - -public slots: - - /**jsdoc - * @function Render.refresh - */ - void refresh(); }; -class SwitchConfig : public TaskConfig { +class SwitchConfig : public JobConfig { Q_OBJECT - Q_PROPERTY(bool switchIndex READ getSwitchIndex WRITE setSwitchIndex NOTIFY dirtyEnabled) + Q_PROPERTY(bool branch READ getBranch WRITE setBranch NOTIFY dirtyEnabled) public: - uint8_t getSwitchIndex() const { return _switchIndex; } - void setSwitchIndex(uint8_t index); + uint8_t getBranch() const { return _branch; } + void setBranch(uint8_t index); protected: - uint8_t _switchIndex { 0 }; + uint8_t _branch { 0 }; }; } diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index e4f7e789f4..87fb21d59b 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -177,6 +177,7 @@ public: template using ModelO = Model; template using ModelIO = Model; + Job() {} Job(const ConceptPointer& concept) : _concept(concept) {} virtual ~Job() = default; @@ -304,7 +305,7 @@ public: // swap Concept::_config = config; // Capture this - std::static_pointer_cast(Concept::_config)->_task = this; + Concept::_config->_jobConcept = this; } QConfigPointer& getConfiguration() override { @@ -314,7 +315,7 @@ public: return Concept::_config; } - virtual void applyConfiguration() override { + void applyConfiguration() override { TimeProfiler probe("configure::" + JobConcept::getName()); jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); for (auto& job : TaskConcept::_jobs) { @@ -322,7 +323,7 @@ public: } } - virtual void run(const ContextPointer& jobContext) override { + void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { for (auto job : TaskConcept::_jobs) { @@ -340,17 +341,86 @@ public: template using ModelO = TaskModel; template using ModelIO = TaskModel; - template class SwitchTaskModel : public TaskModel { + // Create a new job in the Task's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, A&&... args) { + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + } + template const Varying addJob(std::string name, A&&... args) { + const auto input = Varying(typename T::JobModel::Input()); + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + } + + std::shared_ptr getConfiguration() { + return std::static_pointer_cast(JobType::_concept->getConfiguration()); + } +}; + + +// A Switch is a specialized job to run a collection of other jobs and switch between different branches at run time +// It can be created on any type T by aliasing the type JobModel in the class T +// using JobModel = Switch::Model +// The class T is expected to have a "build" method acting as a constructor. +// The build method is where child Jobs can be added internally to the branches of the switch +// where the input of the switch can be setup to feed the child jobs +// and where the output of the switch is defined +template +class Switch : public Job { +public: + using Context = JC; + using TimeProfiler = TP; + using ContextPointer = std::shared_ptr; + using Config = SwitchConfig; + using JobType = Job; + using None = typename JobType::None; + using Concept = typename JobType::Concept; + using ConceptPointer = typename JobType::ConceptPointer; + using Branches = std::unordered_map; + + Switch(ConceptPointer concept) : JobType(concept) {} + + class SwitchConcept : public Concept { public: + Varying _input; + Varying _output; + Branches _branches; + + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } + Varying& editInput() override { return _input; } + + SwitchConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {} + + template const Varying addBranch(std::string name, uint8_t index, const Varying& input, NA&&... args) { + auto& branch = _branches[index]; + branch = JobType(NT::JobModel::create(name, input, std::forward(args)...)); + + // Conect the child config to this task's config + std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(branch.getConfiguration(), name); + + return branch.getOutput(); + } + template const Varying addBranch(std::string name, uint8_t index, NA&&... args) { + const auto input = Varying(typename NT::JobModel::Input()); + return addBranch(name, index, input, std::forward(args)...); + } + }; + + template class SwitchModel : public SwitchConcept { + public: + using Data = T; using Input = I; + using Output = O; - std::unordered_map _jobsSwitch; + Data _data; - SwitchTaskModel(const std::string& name, const Varying& input, QConfigPointer config) : TaskModel(name, input, config) {} + SwitchModel(const std::string& name, const Varying& input, QConfigPointer config) : + SwitchConcept(name, input, config), + _data(Data()) { + } template - static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { - auto model = std::make_shared(name, input, std::make_shared()); + static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + auto model = std::make_shared(name, input, std::make_shared()); { TimeProfiler probe("build::" + model->getName()); @@ -364,78 +434,69 @@ public: } template - static std::shared_ptr create(const std::string& name, A&&... args) { + static std::shared_ptr create(const std::string& name, A&&... args) { const auto input = Varying(Input()); return create(name, input, std::forward(args)...); } - void applyConfiguration() override { - TaskModel::applyConfiguration(); + void createConfiguration() { + // A brand new config + auto config = std::make_shared(); + // Make sure we transfer the former children configs to the new config + config->transferChildrenConfigs(Concept::_config); + // swap + Concept::_config = config; + // Capture this + Concept::_config->_jobConcept = this; + } - for (auto& jobs : _jobsSwitch) { - for (auto& job : jobs.second) { - job.applyConfiguration(); - } + QConfigPointer& getConfiguration() override { + if (!Concept::_config) { + createConfiguration(); + } + return Concept::_config; + } + + void applyConfiguration() override { + TimeProfiler probe("configure::" + JobConcept::getName()); + jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); + for (auto& branch : _branches) { + branch.second.applyConfiguration(); } } void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { - // First we run all the setup jobs - TaskModel::run(jobContext); - - // Then we run the branching jobs - auto jobsIt = _jobsSwitch.find(config->getSwitchIndex()); - if (jobsIt != _jobsSwitch.end()) { - for (auto job : jobsIt->second) { - job.run(jobContext); - if (jobContext->taskFlow.doAbortTask()) { - jobContext->taskFlow.reset(); - return; - } + auto jobsIt = _branches.find(config->getBranch()); + if (jobsIt != _branches.end()) { + jobsIt->second.run(jobContext); + if (jobContext->taskFlow.doAbortTask()) { + jobContext->taskFlow.reset(); + return; } } } } - - template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, NA&&... args) { - auto& jobs = _jobsSwitch[index]; - jobs.emplace_back((NT::JobModel::create(name, input, std::forward(args)...))); - - // Conect the child config to this task's config - std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(jobs.back().getConfiguration(), name); - - return jobs.back().getOutput(); - } - template const Varying addSwitchJob(std::string name, uint8_t index, NA&&... args) { - const auto input = Varying(typename NT::JobModel::Input()); - return addJob(name, index, input, std::forward(args)...); - } }; - template using ModelIS = SwitchTaskModel; + template using Model = SwitchModel; + template using ModelI = SwitchModel; + // TODO: Switches don't support Outputs yet + //template using ModelO = SwitchModel; + //template using ModelIO = SwitchModel; - // Create a new job in the Task's queue; returns the job's output - template const Varying addJob(std::string name, const Varying& input, A&&... args) { - return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + // Create a new job in the Switches' branches; returns the job's output + template const Varying addBranch(std::string name, uint8_t index, const Varying& input, A&&... args) { + return std::static_pointer_cast(JobType::_concept)->template addBranch(name, index, input, std::forward(args)...); } - template const Varying addJob(std::string name, A&&... args) { + template const Varying addBranch(std::string name, uint8_t index, A&&... args) { const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); - } - template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, A&&... args) { - return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); - } - template const Varying addSwitchJob(std::string name, uint8_t index, A&&... args) { - const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); + return std::static_pointer_cast(JobType::_concept)->template addBranch(name, index, input, std::forward(args)...); } std::shared_ptr getConfiguration() { return std::static_pointer_cast(JobType::_concept->getConfiguration()); } - -protected: }; template @@ -475,6 +536,7 @@ protected: using SwitchConfig = task::SwitchConfig; \ template using PersistentConfig = task::PersistentConfig; \ using Job = task::Job; \ + using Switch = task::Switch; \ using Task = task::Task; \ using Engine = task::Engine; \ using Varying = task::Varying; \