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 "AssembleLightingStageTask.h"
#include "RenderShadowTask.h"
#include "RenderDeferredTask.h"
#include "RenderForwardTask.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) {
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
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);
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<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);
const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addJob<DeferredForwardSwitchJob>("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask);
} else {
const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addJob<RenderForwardTask>("RenderForwardTask", renderInput);

View file

@ -15,15 +15,38 @@
#include <render/Engine.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 {
public:
using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelIS<RenderViewTask, Input>;
using JobModel = render::Task::ModelI<RenderViewTask, Input>;
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);
};

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->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) {
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();
}

View file

@ -154,6 +154,11 @@ public:
*/
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:
/**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 };
};
}

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 I, class O, class C = Config> using ModelIO = Model<T, C, I, O>;
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<C>(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<C>(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<C>(Concept::_config);
if (config->isEnabled()) {
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 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:
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 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>
static std::shared_ptr<SwitchTaskModel> create(const std::string& name, const Varying& input, A&&... args) {
auto model = std::make_shared<SwitchTaskModel>(name, input, std::make_shared<C>());
static std::shared_ptr<SwitchModel> create(const std::string& name, const Varying& input, A&&... args) {
auto model = std::make_shared<SwitchModel>(name, input, std::make_shared<C>());
{
TimeProfiler probe("build::" + model->getName());
@ -364,78 +434,69 @@ public:
}
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());
return create(name, input, std::forward<A>(args)...);
}
void applyConfiguration() override {
TaskModel<T, C, I, O>::applyConfiguration();
void createConfiguration() {
// A brand new config
auto config = std::make_shared<C>();
// 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<C>(Concept::_config));
for (auto& branch : _branches) {
branch.second.applyConfiguration();
}
}
void run(const ContextPointer& jobContext) override {
auto config = std::static_pointer_cast<C>(Concept::_config);
if (config->isEnabled()) {
// First we run all the setup jobs
TaskModel<T, C, I, O>::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 <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
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)...);
// Create a new job in the Switches' branches; returns the job's output
template <class T, class... A> const Varying addBranch(std::string name, uint8_t index, const Varying& input, 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());
return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, 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)...);
return std::static_pointer_cast<SwitchConcept>(JobType::_concept)->template addBranch<T>(name, index, input, std::forward<A>(args)...);
}
std::shared_ptr<Config> getConfiguration() {
return std::static_pointer_cast<Config>(JobType::_concept->getConfiguration());
}
protected:
};
template <class JC, class TP>
@ -475,6 +536,7 @@ protected:
using SwitchConfig = task::SwitchConfig; \
template <class T> using PersistentConfig = task::PersistentConfig<T>; \
using Job = task::Job<ContextType, TimeProfiler>; \
using Switch = task::Switch<ContextType, TimeProfiler>; \
using Task = task::Task<ContextType, TimeProfiler>; \
using Engine = task::Engine<ContextType, TimeProfiler>; \
using Varying = task::Varying; \