move switch logic around

This commit is contained in:
SamGondelman 2019-04-29 14:01:09 -07:00
parent 8c032ea83f
commit 4a02ab4fe2
5 changed files with 187 additions and 94 deletions

View file

@ -10,16 +10,34 @@
// //
#include "RenderViewTask.h" #include "RenderViewTask.h"
#include "AssembleLightingStageTask.h"
#include "RenderShadowTask.h" #include "RenderShadowTask.h"
#include "RenderDeferredTask.h" #include "RenderDeferredTask.h"
#include "RenderForwardTask.h" #include "RenderForwardTask.h"
#include <DisableDeferred.h> #include <DisableDeferred.h>
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<DeferredForwardSwitchJob::Input>(0);
const auto lightingModel = input.getN<DeferredForwardSwitchJob::Input>(1);
const auto lightingStageFramesAndZones = input.getN<DeferredForwardSwitchJob::Input>(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<AssembleLightingStageTask::Output>().get0()[0], lightingModel).asVarying();
const auto shadowTaskOut = task.addJob<RenderShadowTask>("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask);
const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying();
task.addJob<RenderDeferredTask>("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>("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask);
task.addBranch<RenderForwardTask>("RenderForwardTask", 1, input);
}
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { 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<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask); const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
assert(items.canCast<RenderFetchCullSortTask::Output>());
// Issue the lighting model, aka the big global settings for the view // Issue the lighting model, aka the big global settings for the view
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel"); const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
@ -28,16 +46,8 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
const auto lightingStageFramesAndZones = task.addJob<AssembleLightingStageTask>("AssembleStages", items); const auto lightingStageFramesAndZones = task.addJob<AssembleLightingStageTask>("AssembleStages", items);
if (!DISABLE_DEFERRED) { if (!DISABLE_DEFERRED) {
// Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
// is performed, then casters not in the view frustum will be removed, which is not what we wish. task.addJob<DeferredForwardSwitchJob>("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask);
const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get<AssembleLightingStageTask::Output>().get0()[0], lightingModel).asVarying();
const auto shadowTaskOut = task.addSwitchJob<RenderShadowTask>("RenderShadowTask", 0, shadowTaskIn, cullFunctor, tagBits, tagMask);
const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying();
task.addSwitchJob<RenderDeferredTask>("RenderDeferredTask", 0, renderDeferredInput);
const auto renderForwardInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addSwitchJob<RenderForwardTask>("RenderForwardTask", 1, renderForwardInput);
} else { } else {
const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addJob<RenderForwardTask>("RenderForwardTask", renderInput); task.addJob<RenderForwardTask>("RenderForwardTask", renderInput);

View file

@ -15,15 +15,38 @@
#include <render/Engine.h> #include <render/Engine.h>
#include <render/RenderFetchCullSortTask.h> #include <render/RenderFetchCullSortTask.h>
#include "AssembleLightingStageTask.h"
class RenderShadowsAndDeferredTask {
public:
using Input = render::VaryingSet3<RenderFetchCullSortTask::Output, LightingModelPointer, AssembleLightingStageTask::Output>;
using JobModel = render::Task::ModelI<RenderShadowsAndDeferredTask, Input>;
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<RenderFetchCullSortTask::Output, LightingModelPointer, AssembleLightingStageTask::Output>;
using JobModel = render::Switch::ModelI<DeferredForwardSwitchJob, Input>;
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 { class RenderViewTask {
public: public:
using Input = RenderFetchCullSortTask::Output; using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelIS<RenderViewTask, Input>; using JobModel = render::Task::ModelI<RenderViewTask, Input>;
RenderViewTask() {} 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); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00);
}; };

View file

@ -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<JobConfig> childConfig, const std::string& name) {
childConfig->setParent(this); childConfig->setParent(this);
childConfig->setObjectName(name.c_str()); 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<JobConfig> source) {
if (!source) { if (!source) {
return; return;
} }
@ -70,13 +70,13 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) {
} }
} }
void TaskConfig::refresh() { void JobConfig::refresh() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "refresh"); BLOCKING_INVOKE_METHOD(this, "refresh");
return; return;
} }
_task->applyConfiguration(); _jobConcept->applyConfiguration();
} }
TaskConfig* TaskConfig::getRootConfig(const std::string& jobPath, std::string& jobName) const { 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) { void SwitchConfig::setBranch(uint8_t branch) {
if (_switchIndex != index) { if (_branch != branch) {
_switchIndex = index; _branch = branch;
// We can re-use this signal here // We can re-use this signal here
emit dirtyEnabled(); emit dirtyEnabled();
} }

View file

@ -154,6 +154,11 @@ public:
*/ */
Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; } Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; }
void connectChildConfig(std::shared_ptr<JobConfig> childConfig, const std::string& name);
void transferChildrenConfigs(std::shared_ptr<JobConfig> source);
JobConcept* _jobConcept;
public slots: public slots:
/**jsdoc /**jsdoc
@ -162,6 +167,11 @@ public slots:
*/ */
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
/**jsdoc
* @function Render.refresh
*/
void refresh();
signals: signals:
/**jsdoc /**jsdoc
@ -248,30 +258,18 @@ public:
auto subs = getSubConfigs(); auto subs = getSubConfigs();
return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]); 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_OBJECT
Q_PROPERTY(bool switchIndex READ getSwitchIndex WRITE setSwitchIndex NOTIFY dirtyEnabled) Q_PROPERTY(bool branch READ getBranch WRITE setBranch NOTIFY dirtyEnabled)
public: public:
uint8_t getSwitchIndex() const { return _switchIndex; } uint8_t getBranch() const { return _branch; }
void setSwitchIndex(uint8_t index); void setBranch(uint8_t index);
protected: protected:
uint8_t _switchIndex { 0 }; uint8_t _branch { 0 };
}; };
} }

View file

@ -177,6 +177,7 @@ public:
template <class T, class O, class C = Config> using ModelO = Model<T, C, None, O>; template <class T, class O, class C = Config> using ModelO = Model<T, C, None, O>;
template <class T, class I, class O, class C = Config> using ModelIO = Model<T, C, I, O>; template <class T, class I, class O, class C = Config> using ModelIO = Model<T, C, I, O>;
Job() {}
Job(const ConceptPointer& concept) : _concept(concept) {} Job(const ConceptPointer& concept) : _concept(concept) {}
virtual ~Job() = default; virtual ~Job() = default;
@ -304,7 +305,7 @@ public:
// swap // swap
Concept::_config = config; Concept::_config = config;
// Capture this // Capture this
std::static_pointer_cast<C>(Concept::_config)->_task = this; Concept::_config->_jobConcept = this;
} }
QConfigPointer& getConfiguration() override { QConfigPointer& getConfiguration() override {
@ -314,7 +315,7 @@ public:
return Concept::_config; return Concept::_config;
} }
virtual void applyConfiguration() override { void applyConfiguration() override {
TimeProfiler probe("configure::" + JobConcept::getName()); TimeProfiler probe("configure::" + JobConcept::getName());
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config)); jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
for (auto& job : TaskConcept::_jobs) { 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<C>(Concept::_config); auto config = std::static_pointer_cast<C>(Concept::_config);
if (config->isEnabled()) { if (config->isEnabled()) {
for (auto job : TaskConcept::_jobs) { for (auto job : TaskConcept::_jobs) {
@ -340,17 +341,86 @@ public:
template <class T, class O, class C = Config> using ModelO = TaskModel<T, C, None, O>; template <class T, class O, class C = Config> using ModelO = TaskModel<T, C, None, O>;
template <class T, class I, class O, class C = Config> using ModelIO = TaskModel<T, C, I, O>; template <class T, class I, class O, class C = Config> using ModelIO = TaskModel<T, C, I, O>;
template <class T, class C = SwitchConfig, class I = None, class O = None> class SwitchTaskModel : public TaskModel<T, C, I, O> { // Create a new job in the Task's queue; returns the job's output
template <class T, class... A> const Varying addJob(std::string name, const Varying& input, A&&... args) {
return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, input, std::forward<A>(args)...);
}
template <class T, class... A> const Varying addJob(std::string name, A&&... args) {
const auto input = Varying(typename T::JobModel::Input());
return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, input, std::forward<A>(args)...);
}
std::shared_ptr<Config> getConfiguration() {
return std::static_pointer_cast<Config>(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<T>
// 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 JC, class TP>
class Switch : public Job<JC, TP> {
public:
using Context = JC;
using TimeProfiler = TP;
using ContextPointer = std::shared_ptr<Context>;
using Config = SwitchConfig;
using JobType = Job<JC, TP>;
using None = typename JobType::None;
using Concept = typename JobType::Concept;
using ConceptPointer = typename JobType::ConceptPointer;
using Branches = std::unordered_map<uint8_t, JobType>;
Switch(ConceptPointer concept) : JobType(concept) {}
class SwitchConcept : public Concept {
public: 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 <class NT, class... NA> 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<NA>(args)...));
// Conect the child config to this task's config
std::static_pointer_cast<SwitchConfig>(Concept::getConfiguration())->connectChildConfig(branch.getConfiguration(), name);
return branch.getOutput();
}
template <class NT, class... NA> const Varying addBranch(std::string name, uint8_t index, NA&&... args) {
const auto input = Varying(typename NT::JobModel::Input());
return addBranch<NT>(name, index, input, std::forward<NA>(args)...);
}
};
template <class T, class C = SwitchConfig, class I = None, class O = None> class SwitchModel : public SwitchConcept {
public:
using Data = T;
using Input = I; using Input = I;
using Output = O;
std::unordered_map<uint8_t, Jobs> _jobsSwitch; Data _data;
SwitchTaskModel(const std::string& name, const Varying& input, QConfigPointer config) : TaskModel<T, C, I, O>(name, input, config) {} SwitchModel(const std::string& name, const Varying& input, QConfigPointer config) :
SwitchConcept(name, input, config),
_data(Data()) {
}
template <class... A> template <class... A>
static std::shared_ptr<SwitchTaskModel> create(const std::string& name, const Varying& input, A&&... args) { static std::shared_ptr<SwitchModel> create(const std::string& name, const Varying& input, A&&... args) {
auto model = std::make_shared<SwitchTaskModel>(name, input, std::make_shared<C>()); auto model = std::make_shared<SwitchModel>(name, input, std::make_shared<C>());
{ {
TimeProfiler probe("build::" + model->getName()); TimeProfiler probe("build::" + model->getName());
@ -364,32 +434,43 @@ public:
} }
template <class... A> template <class... A>
static std::shared_ptr<SwitchTaskModel> create(const std::string& name, A&&... args) { static std::shared_ptr<SwitchModel> create(const std::string& name, A&&... args) {
const auto input = Varying(Input()); const auto input = Varying(Input());
return create(name, input, std::forward<A>(args)...); return create(name, input, std::forward<A>(args)...);
} }
void applyConfiguration() override { void createConfiguration() {
TaskModel<T, C, I, O>::applyConfiguration(); // A brand new config
auto config = std::make_shared<C>();
for (auto& jobs : _jobsSwitch) { // Make sure we transfer the former children configs to the new config
for (auto& job : jobs.second) { config->transferChildrenConfigs(Concept::_config);
job.applyConfiguration(); // swap
Concept::_config = config;
// Capture this
Concept::_config->_jobConcept = this;
} }
QConfigPointer& getConfiguration() override {
if (!Concept::_config) {
createConfiguration();
}
return Concept::_config;
}
void applyConfiguration() override {
TimeProfiler probe("configure::" + JobConcept::getName());
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
for (auto& branch : _branches) {
branch.second.applyConfiguration();
} }
} }
void run(const ContextPointer& jobContext) override { void run(const ContextPointer& jobContext) override {
auto config = std::static_pointer_cast<C>(Concept::_config); auto config = std::static_pointer_cast<C>(Concept::_config);
if (config->isEnabled()) { if (config->isEnabled()) {
// First we run all the setup jobs auto jobsIt = _branches.find(config->getBranch());
TaskModel<T, C, I, O>::run(jobContext); if (jobsIt != _branches.end()) {
jobsIt->second.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()) { if (jobContext->taskFlow.doAbortTask()) {
jobContext->taskFlow.reset(); jobContext->taskFlow.reset();
return; return;
@ -397,45 +478,25 @@ public:
} }
} }
} }
}
template <class NT, class... NA> 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<NA>(args)...)));
// Conect the child config to this task's config
std::static_pointer_cast<TaskConfig>(Concept::getConfiguration())->connectChildConfig(jobs.back().getConfiguration(), name);
return jobs.back().getOutput();
}
template <class NT, class... NA> const Varying addSwitchJob(std::string name, uint8_t index, NA&&... args) {
const auto input = Varying(typename NT::JobModel::Input());
return addJob<NT>(name, index, input, std::forward<NA>(args)...);
}
}; };
template <class T, class I, class C = SwitchConfig> using ModelIS = SwitchTaskModel<T, C, I, None>; template <class T, class C = SwitchConfig> using Model = SwitchModel<T, C, None, None>;
template <class T, class I, class C = SwitchConfig> using ModelI = SwitchModel<T, C, I, None>;
// TODO: Switches don't support Outputs yet
//template <class T, class O, class C = SwitchConfig> using ModelO = SwitchModel<T, C, None, O>;
//template <class T, class I, class O, class C = SwitchConfig> using ModelIO = SwitchModel<T, C, I, O>;
// Create a new job in the Task's queue; returns the job's output // Create a new job in the Switches' branches; returns the job's output
template <class T, class... A> const Varying addJob(std::string name, const Varying& input, A&&... args) { template <class T, class... A> const Varying addBranch(std::string name, uint8_t index, const Varying& input, A&&... args) {
return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, input, std::forward<A>(args)...); return std::static_pointer_cast<SwitchConcept>(JobType::_concept)->template addBranch<T>(name, index, input, std::forward<A>(args)...);
} }
template <class T, class... A> const Varying addJob(std::string name, A&&... args) { template <class T, class... A> const Varying addBranch(std::string name, uint8_t index, A&&... args) {
const auto input = Varying(typename T::JobModel::Input()); const auto input = Varying(typename T::JobModel::Input());
return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, input, std::forward<A>(args)...); return std::static_pointer_cast<SwitchConcept>(JobType::_concept)->template addBranch<T>(name, index, input, std::forward<A>(args)...);
}
template <class T, class... A> const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, A&&... args) {
return std::static_pointer_cast<SwitchTaskModel>(JobType::_concept)->template addSwitchJob<T>(name, index, input, std::forward<A>(args)...);
}
template <class T, class... A> const Varying addSwitchJob(std::string name, uint8_t index, A&&... args) {
const auto input = Varying(typename T::JobModel::Input());
return std::static_pointer_cast<SwitchTaskModel>(JobType::_concept)->template addSwitchJob<T>(name, index, input, std::forward<A>(args)...);
} }
std::shared_ptr<Config> getConfiguration() { std::shared_ptr<Config> getConfiguration() {
return std::static_pointer_cast<Config>(JobType::_concept->getConfiguration()); return std::static_pointer_cast<Config>(JobType::_concept->getConfiguration());
} }
protected:
}; };
template <class JC, class TP> template <class JC, class TP>
@ -475,6 +536,7 @@ protected:
using SwitchConfig = task::SwitchConfig; \ using SwitchConfig = task::SwitchConfig; \
template <class T> using PersistentConfig = task::PersistentConfig<T>; \ template <class T> using PersistentConfig = task::PersistentConfig<T>; \
using Job = task::Job<ContextType, TimeProfiler>; \ using Job = task::Job<ContextType, TimeProfiler>; \
using Switch = task::Switch<ContextType, TimeProfiler>; \
using Task = task::Task<ContextType, TimeProfiler>; \ using Task = task::Task<ContextType, TimeProfiler>; \
using Engine = task::Engine<ContextType, TimeProfiler>; \ using Engine = task::Engine<ContextType, TimeProfiler>; \
using Varying = task::Varying; \ using Varying = task::Varying; \