runtime switch for deferred/forward rendering

This commit is contained in:
SamGondelman 2019-04-26 12:56:01 -07:00
parent 164984b15d
commit ca5c7e3904
10 changed files with 129 additions and 41 deletions

View file

@ -243,11 +243,8 @@
#include "webbrowser/WebBrowserSuggestionsEngine.h" #include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h> #include <DesktopPreviewProvider.h>
#include "AboutUtil.h" #include "AboutUtil.h"
#include <DisableDeferred.h>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include <VersionHelpers.h> #include <VersionHelpers.h>
@ -2979,7 +2976,7 @@ void Application::initializeDisplayPlugins() {
void Application::initializeRenderEngine() { void Application::initializeRenderEngine() {
// FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
DeadlockWatchdogThread::withPause([&] { DeadlockWatchdogThread::withPause([&] {
_graphicsEngine.initializeRender(DISABLE_DEFERRED); _graphicsEngine.initializeRender();
DependencyManager::get<Keyboard>()->registerKeyboardHighlighting(); DependencyManager::get<Keyboard>()->registerKeyboardHighlighting();
}); });
} }

View file

@ -272,10 +272,10 @@ public:
} }
}; };
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) {
const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera"); const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera");
task.addJob<RenderViewTask>("RenderSecondView", cullFunctor, isDeferred, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); task.addJob<RenderViewTask>("RenderSecondView", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg); task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
} }

View file

@ -65,7 +65,7 @@ public:
using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>; using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>;
SecondaryCameraRenderTask() {} SecondaryCameraRenderTask() {}
void configure(const Config& config) {} void configure(const Config& config) {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor);
}; };
#endif #endif

View file

@ -65,15 +65,15 @@ void GraphicsEngine::initializeGPU(GLWidget* glwidget) {
DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext); DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext);
} }
void GraphicsEngine::initializeRender(bool disableDeferred) { void GraphicsEngine::initializeRender() {
// Set up the render engine // Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender; render::CullFunctor cullFunctor = LODManager::shouldRender;
_renderEngine->addJob<UpdateSceneTask>("UpdateScene"); _renderEngine->addJob<UpdateSceneTask>("UpdateScene");
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, !disableDeferred); _renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor);
#endif #endif
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, !disableDeferred, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); _renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0);
_renderEngine->load(); _renderEngine->load();
_renderEngine->registerScene(_renderScene); _renderEngine->registerScene(_renderScene);

View file

@ -44,7 +44,7 @@ public:
~GraphicsEngine(); ~GraphicsEngine();
void initializeGPU(GLWidget*); void initializeGPU(GLWidget*);
void initializeRender(bool disableDeferred); void initializeRender();
void startup(); void startup();
void shutdown(); void shutdown();

View file

@ -15,7 +15,9 @@
#include "RenderDeferredTask.h" #include "RenderDeferredTask.h"
#include "RenderForwardTask.h" #include "RenderForwardTask.h"
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) { #include <DisableDeferred.h>
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>()); assert(items.canCast<RenderFetchCullSortTask::Output>());
@ -25,17 +27,19 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
// Assemble the lighting stages current frames // Assemble the lighting stages current frames
const auto lightingStageFramesAndZones = task.addJob<AssembleLightingStageTask>("AssembleStages", items); const auto lightingStageFramesAndZones = task.addJob<AssembleLightingStageTask>("AssembleStages", items);
if (isDeferred) { if (!DISABLE_DEFERRED) {
// Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling // 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. // 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 shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get<AssembleLightingStageTask::Output>().get0()[0], lightingModel).asVarying();
const auto shadowTaskOut = task.addJob<RenderShadowTask>("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); const auto shadowTaskOut = task.addSwitchJob<RenderShadowTask>("RenderShadowTask", 0, shadowTaskIn, cullFunctor, tagBits, tagMask);
const auto renderInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying();
task.addJob<RenderDeferredTask>("RenderDeferredTask", renderInput); 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>("Forward", renderInput); task.addJob<RenderForwardTask>("RenderForwardTask", renderInput);
} }
} }

View file

@ -19,11 +19,12 @@
class RenderViewTask { class RenderViewTask {
public: public:
using Input = RenderFetchCullSortTask::Output; using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelI<RenderViewTask, Input>; using JobModel = render::Task::ModelIS<RenderViewTask, Input>;
RenderViewTask() {} RenderViewTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); 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

@ -133,3 +133,11 @@ JobConfig* TaskConfig::getJobConfig(const std::string& jobPath) const {
return found; return found;
} }
} }
void SwitchConfig::setSwitchIndex(uint8_t index) {
if (_switchIndex != index) {
_switchIndex = index;
// We can re-use this signal here
emit dirtyEnabled();
}
}

View file

@ -262,6 +262,18 @@ public slots:
void refresh(); void refresh();
}; };
class SwitchConfig : public TaskConfig {
Q_OBJECT
Q_PROPERTY(bool switchIndex READ getSwitchIndex WRITE setSwitchIndex NOTIFY dirtyEnabled)
public:
uint8_t getSwitchIndex() const { return _switchIndex; }
void setSwitchIndex(uint8_t index);
protected:
uint8_t _switchIndex { 0 };
};
} }
#endif // hifi_task_Config_h #endif // hifi_task_Config_h

View file

@ -15,6 +15,8 @@
#include "Config.h" #include "Config.h"
#include "Varying.h" #include "Varying.h"
#include <unordered_map>
namespace task { namespace task {
class JobConcept; class JobConcept;
@ -244,25 +246,6 @@ public:
const Varying getOutput() const override { return _output; } const Varying getOutput() const override { return _output; }
Varying& editInput() override { return _input; } Varying& editInput() override { return _input; }
typename Jobs::iterator editJob(std::string name) {
typename Jobs::iterator jobIt;
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
if (jobIt->getName() == name) {
return jobIt;
}
}
return jobIt;
}
typename Jobs::const_iterator getJob(std::string name) const {
typename Jobs::const_iterator jobIt;
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
if (jobIt->getName() == name) {
return jobIt;
}
}
return jobIt;
}
TaskConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {} TaskConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {}
// Create a new job in the container's queue; returns the job's output // Create a new job in the container's queue; returns the job's output
@ -331,7 +314,7 @@ public:
return Concept::_config; return Concept::_config;
} }
void applyConfiguration() override { virtual 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) {
@ -339,7 +322,7 @@ public:
} }
} }
void run(const ContextPointer& jobContext) override { virtual 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) {
@ -357,6 +340,81 @@ 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> {
public:
using Input = I;
std::unordered_map<uint8_t, Jobs> _jobsSwitch;
SwitchTaskModel(const std::string& name, const Varying& input, QConfigPointer config) : TaskModel<T, C, I, O>(name, input, config) {}
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>());
{
TimeProfiler probe("build::" + model->getName());
model->_data.build(*(model), model->_input, model->_output, std::forward<A>(args)...);
}
// Recreate the Config to use the templated type
model->createConfiguration();
model->applyConfiguration();
return model;
}
template <class... A>
static std::shared_ptr<SwitchTaskModel> 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();
for (auto& jobs : _jobsSwitch) {
for (auto& job : jobs.second) {
job.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;
}
}
}
}
}
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>;
// Create a new job in the Task's queue; returns the job's output // 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) { 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)...); return std::static_pointer_cast<TaskConcept>(JobType::_concept)->template addJob<T>(name, input, std::forward<A>(args)...);
@ -365,6 +423,13 @@ public:
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<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)...);
}
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());
@ -407,6 +472,7 @@ protected:
#define Task_DeclareTypeAliases(ContextType, TimeProfiler) \ #define Task_DeclareTypeAliases(ContextType, TimeProfiler) \
using JobConfig = task::JobConfig; \ using JobConfig = task::JobConfig; \
using TaskConfig = task::TaskConfig; \ using TaskConfig = task::TaskConfig; \
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 Task = task::Task<ContextType, TimeProfiler>; \ using Task = task::Task<ContextType, TimeProfiler>; \