// // Task.h // render/src/task // // Created by Zach Pomerantz on 1/6/2016. // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #ifndef hifi_task_Task_h #define hifi_task_Task_h #include "Config.h" #include "Varying.h" #include "SettingHandle.h" #include "Logging.h" #include #include namespace task { class JobConcept; template class JobT; template class TaskT; class JobNoIO {}; class JobContext { public: virtual ~JobContext() {} std::shared_ptr jobConfig { nullptr }; }; using JobContextPointer = std::shared_ptr; // The guts of a job class JobConcept { public: using Config = JobConfig; JobConcept(QConfigPointer config) : _config(config) {} virtual ~JobConcept() = default; virtual const Varying getInput() const { return Varying(); } virtual const Varying getOutput() const { return Varying(); } virtual QConfigPointer& getConfiguration() { return _config; } virtual void applyConfiguration() = 0; void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } QConfigPointer _config; protected: }; template void jobConfigure(T& data, const C& configuration) { data.configure(configuration); } template void jobConfigure(T&, const JobConfig&) { // nop, as the default JobConfig was used, so the data does not need a configure method } template void jobConfigure(T&, const TaskConfig&) { // nop, as the default TaskConfig was used, so the data does not need a configure method } template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, JobNoIO& output) { data.run(renderContext); } template void jobRun(T& data, const RC& renderContext, const I& input, JobNoIO& output) { data.run(renderContext, input); } template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, O& output) { data.run(renderContext, output); } template void jobRun(T& data, const RC& renderContext, const I& input, O& output) { data.run(renderContext, input, output); } template class Job { public: using Context = RC; using ContextPointer = std::shared_ptr; using Config = JobConfig; using None = JobNoIO; class Concept : public JobConcept { public: Concept(QConfigPointer config) : JobConcept(config) {} virtual ~Concept() = default; virtual void run(const ContextPointer& renderContext) = 0; }; using ConceptPointer = std::shared_ptr; template class Model : public Concept { public: using Data = T; using Input = I; using Output = O; Data _data; Varying _input; Varying _output; const Varying getInput() const override { return _input; } const Varying getOutput() const override { return _output; } template Model(const Varying& input, QConfigPointer config, A&&... args) : Concept(config), _data(Data(std::forward(args)...)), _input(input), _output(Output()) { applyConfiguration(); } template static std::shared_ptr create(const Varying& input, A&&... args) { return std::make_shared(input, std::make_shared(), std::forward(args)...); } void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); } void run(const ContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(Concept::_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { jobRun(_data, renderContext, _input.get(), _output.edit()); } renderContext->jobConfig.reset(); } }; template using ModelI = Model; template using ModelO = Model; template using ModelIO = Model; Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); } void applyConfiguration() { return _concept->applyConfiguration(); } template T& edit() { auto concept = std::static_pointer_cast(_concept); assert(concept); return concept->_data; } virtual void run(const ContextPointer& renderContext) { PerformanceTimer perfTimer(_name.c_str()); PROFILE_RANGE(render, _name.c_str()); auto start = usecTimestampNow(); _concept->run(renderContext); _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); } protected: ConceptPointer _concept; std::string _name = ""; }; // A task is a specialized job to run a collection of other jobs // It can be created on any type T by aliasing the type JobModel in the class T // using JobModel = Task::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 task // where the input of the task can be setup to feed the child jobs // and where the output of the task is defined template class Task : public Job { public: using Context = RC; using ContextPointer = std::shared_ptr; using Config = TaskConfig; using JobType = Job; using None = typename JobType::None; using Concept = typename JobType::Concept; using ConceptPointer = typename JobType::ConceptPointer; using Jobs = std::vector; Task(std::string name, ConceptPointer concept) : JobType(name, concept) {} class TaskConcept : public Concept { public: Varying _input; Varying _output; Jobs _jobs; const Varying getInput() const override { return _input; } const Varying getOutput() const override { return _output; } TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} // Create a new job in the container's queue; returns the job's output template const Varying addJob(std::string name, const Varying& input, NA&&... args) { _jobs.emplace_back(name, (NT::JobModel::create(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 addJob(std::string name, NA&&... args) { const auto input = Varying(typename NT::JobModel::Input()); return addJob(name, input, std::forward(args)...); } }; template class TaskModel : public TaskConcept { public: using Data = T; using Input = I; using Output = O; Data _data; TaskModel(const Varying& input, QConfigPointer config) : TaskConcept(input, config), _data(Data()) {} template static std::shared_ptr create(const Varying& input, A&&... args) { auto model = std::make_shared(input, std::make_shared()); model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); // Recreate the Config to use the templated type model->createConfiguration(); model->applyConfiguration(); return model; } template static std::shared_ptr create(A&&... args) { const auto input = Varying(Input()); return create(input, std::forward(args)...); } 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 std::static_pointer_cast(Concept::_config)->_task = this; } QConfigPointer& getConfiguration() override { if (!Concept::_config) { createConfiguration(); } return Concept::_config; } void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); for (auto& job : TaskConcept::_jobs) { job.applyConfiguration(); } } void run(const ContextPointer& renderContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->alwaysEnabled || config->enabled) { for (auto job : TaskConcept::_jobs) { job.run(renderContext); } } } }; template using Model = TaskModel; template using ModelI = TaskModel; template using ModelO = TaskModel; template using ModelIO = 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()); } protected: }; } #define Task_DeclareTypeAliases(ContextType) \ using JobConfig = task::JobConfig; \ using TaskConfig = task::TaskConfig; \ template using PersistentConfig = task::PersistentConfig; \ using Job = task::Job; \ using Task = task::Task; \ using Varying = task::Varying; \ template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2; \ template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3; \ template < typename T0, typename T1, typename T2, typename T3 > using VaryingSet4 = task::VaryingSet4; \ template < typename T0, typename T1, typename T2, typename T3, typename T4 > using VaryingSet5 = task::VaryingSet5; \ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5 > using VaryingSet6 = task::VaryingSet6; \ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > using VaryingSet7 = task::VaryingSet7; \ template < class T, int NUM > using VaryingArray = task::VaryingArray; #endif // hifi_task_Task_h