diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 81e4924204..c876ac6abb 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -7,7 +7,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Preferences" - showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers"] + showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Graphics"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27f9d81ab2..8e0d46bc81 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -102,7 +102,6 @@ #include #include #include -#include #include #include #include @@ -363,7 +362,6 @@ bool setupEssentials(int& argc, char** argv) { #endif DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -675,12 +673,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _offscreenContext->makeCurrent(); initializeGL(); - // Start rendering - render::CullFunctor cullFunctor = LODManager::shouldRender; - _renderEngine->addTask(make_shared(cullFunctor)); - _renderEngine->addTask(make_shared(cullFunctor)); - _renderEngine->registerScene(_main3DScene); - _offscreenContext->makeCurrent(); // Tell our entity edit sender about our known jurisdictions @@ -1151,9 +1143,17 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); + // Set up the render engine + render::CullFunctor cullFunctor = LODManager::shouldRender; + _renderEngine->addJob("RenderShadowTask", cullFunctor); + _renderEngine->addJob("RenderDeferredTask", cullFunctor); + _renderEngine->registerScene(_main3DScene); + // TODO: Load a cached config file + // The UI can't be created until the primary OpenGL // context is created, because it needs to share // texture resources + // Needs to happen AFTER the render engine initialization to access its configuration initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); _offscreenContext->makeCurrent(); @@ -1251,7 +1251,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Paths", DependencyManager::get().data()); rootContext->setContextProperty("HMD", DependencyManager::get().data()); rootContext->setContextProperty("Scene", DependencyManager::get().data()); - rootContext->setContextProperty("Render", DependencyManager::get().data()); + rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { @@ -3769,29 +3769,13 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se { PerformanceTimer perfTimer("EngineRun"); - auto renderInterface = DependencyManager::get(); - auto renderContext = renderInterface->getRenderContext(); - renderArgs->_viewFrustum = getDisplayViewFrustum(); - renderContext.setArgs(renderArgs); - - bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); - bool shadowStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugShadows); - bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); - bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned); - renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus, shadowStatus); - - _renderEngine->setRenderContext(renderContext); + _renderEngine->getRenderContext()->args = renderArgs; // Before the deferred pass, let's try to use the render engine myAvatar->startRenderRun(); _renderEngine->run(); myAvatar->endRenderRun(); - - auto engineContext = _renderEngine->getRenderContext(); - renderInterface->setItemCounts(engineContext->getItemsConfig()); - renderInterface->setJobGPUTimes(engineContext->getAmbientOcclusion().gpuTime); - } activeRenderingThread = nullptr; @@ -4200,7 +4184,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Render", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data()); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 720d8993ab..8f94ae214a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,6 +32,7 @@ #include "devices/Faceshift.h" #include "input-plugins/SpacemouseManager.h" #include "MainWindow.h" +#include "render/DrawStatus.h" #include "scripting/MenuScriptingInterface.h" #include "ui/AssetUploadDialogFactory.h" #include "ui/DialogsManager.h" @@ -336,9 +337,6 @@ Menu::Menu() { // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugShadows); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true); // Developer > Render > Ambient Light @@ -594,7 +592,11 @@ Menu::Menu() { // Developer > Physics >>> MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics"); - addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned); + { + auto drawStatusConfig = qApp->getRenderEngine()->getConfiguration()->getConfig(); + addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned, + 0, false, drawStatusConfig, SLOT(setShowNetwork(bool))); + } addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); // Developer > Display Crash Options diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 7b9cca2c63..ce53b11178 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -152,7 +152,6 @@ namespace MenuOption { const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose"; const QString AnimDebugDrawPosition= "Debug Draw Position"; - const QString Antialiasing = "Antialiasing"; const QString AssetMigration = "ATP Asset Migration"; const QString Attachments = "Attachments..."; const QString AudioNetworkStats = "Audio Network Stats"; @@ -186,8 +185,6 @@ namespace MenuOption { const QString CopyPath = "Copy Path to Clipboard"; const QString CoupleEyelids = "Couple Eyelids"; const QString CrashInterface = "Crash Interface"; - const QString DebugShadows = "Shadows"; - const QString DebugAmbientOcclusion = "Ambient Occlusion"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; @@ -248,8 +245,8 @@ namespace MenuOption { const QString OutputMenu = "Display"; const QString PackageModel = "Package Model..."; const QString Pair = "Pair"; - const QString PhysicsShowOwned = "Highlight Simulation Ownership"; const QString PhysicsShowHulls = "Draw Collision Hulls"; + const QString PhysicsShowOwned = "Highlight Simulation Ownership"; const QString PipelineWarnings = "Log Render Pipeline Warnings"; const QString Preferences = "General..."; const QString Quit = "Quit"; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7609939676..b25fe6775a 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -24,6 +24,10 @@ #include "Snapshot.h" #include "UserActivityLogger.h" +#include "AmbientOcclusionEffect.h" +#include "AntialiasingEffect.h" +#include "RenderShadowTask.h" + void setupPreferences() { auto preferences = DependencyManager::get(); @@ -310,4 +314,29 @@ void setupPreferences() { preference->setStep(1); preferences->addPreference(preference); } + + { + static const QString RENDER("Graphics"); + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + { + auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; + auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; + auto preference = new CheckPreference(RENDER, "Ambient Occlusion", getter, setter); + preferences->addPreference(preference); + } + + { + auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; + auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; + auto preference = new CheckPreference(RENDER, "Antialiasing", getter, setter); + preferences->addPreference(preference); + } + + { + auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; + auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; + auto preference = new CheckPreference(RENDER, "Shadows", getter, setter); + preferences->addPreference(preference); + } + } } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 30360fdbce..185afade03 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -21,6 +21,7 @@ #include #include "RenderUtilsLogging.h" +#include "DeferredLightingEffect.h" #include "AmbientOcclusionEffect.h" #include "TextureCache.h" #include "FramebufferCache.h" @@ -100,6 +101,82 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() { _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); } +void AmbientOcclusionEffect::configure(const Config& config) { + DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); + + bool shouldUpdateGaussian = false; + + const double RADIUS_POWER = 6.0; + const auto& radius = config.radius; + if (radius != getRadius()) { + auto& current = _parametersBuffer.edit().radiusInfo; + current.x = radius; + current.y = radius * radius; + current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER)); + } + + if (config.obscuranceLevel != getObscuranceLevel()) { + auto& current = _parametersBuffer.edit().radiusInfo; + current.w = config.obscuranceLevel; + } + + if (config.falloffBias != getFalloffBias()) { + auto& current = _parametersBuffer.edit().ditheringInfo; + current.z = config.falloffBias; + } + + if (config.edgeSharpness != getEdgeSharpness()) { + auto& current = _parametersBuffer.edit().blurInfo; + current.x = config.edgeSharpness; + } + + if (config.blurDeviation != getBlurDeviation()) { + auto& current = _parametersBuffer.edit().blurInfo; + current.z = config.blurDeviation; + shouldUpdateGaussian = true; + } + + if (config.numSpiralTurns != getNumSpiralTurns()) { + auto& current = _parametersBuffer.edit().sampleInfo; + current.z = config.numSpiralTurns; + } + + if (config.numSamples != getNumSamples()) { + auto& current = _parametersBuffer.edit().sampleInfo; + current.x = config.numSamples; + current.y = 1.0f / config.numSamples; + } + + const auto& resolutionLevel = config.resolutionLevel; + if (resolutionLevel != getResolutionLevel()) { + auto& current = _parametersBuffer.edit().resolutionInfo; + current.x = (float)resolutionLevel; + + // Communicate the change to the Framebuffer cache + DependencyManager::get()->setAmbientOcclusionResolutionLevel(resolutionLevel); + } + + if (config.blurRadius != getBlurRadius()) { + auto& current = _parametersBuffer.edit().blurInfo; + current.y = (float)config.blurRadius; + shouldUpdateGaussian = true; + } + + if (config.ditheringEnabled != isDitheringEnabled()) { + auto& current = _parametersBuffer.edit().ditheringInfo; + current.x = (float)config.ditheringEnabled; + } + + if (config.borderingEnabled != isBorderingEnabled()) { + auto& current = _parametersBuffer.edit().ditheringInfo; + current.w = (float)config.borderingEnabled; + } + + if (shouldUpdateGaussian) { + updateGaussianDistribution(); + } +} + const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() { if (!_pyramidPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); @@ -200,113 +277,16 @@ void AmbientOcclusionEffect::setDepthInfo(float nearZ, float farZ) { _frameTransformBuffer.edit().depthInfo = glm::vec4(nearZ*farZ, farZ -nearZ, -farZ, 0.0f); } -void AmbientOcclusionEffect::setResolutionLevel(int level) { - const int MAX_RESOLUTION_LEVEL = 4; - level = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); - if (level != getResolutionLevel()) { - auto& current = _parametersBuffer.edit().resolutionInfo; - current.x = (float)level; - - // Communicate the change to the Framebuffer cache - DependencyManager::get()->setAmbientOcclusionResolutionLevel(level); - } -} - -void AmbientOcclusionEffect::setRadius(float radius) { - const double RADIUS_POWER = 6.0; - radius = std::max(0.01f, radius); - if (radius != getRadius()) { - auto& current = _parametersBuffer.edit().radiusInfo; - current.x = radius; - current.y = radius * radius; - current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER)); - } -} - -void AmbientOcclusionEffect::setLevel(float level) { - level = std::max(0.01f, level); - if (level != getLevel()) { - auto& current = _parametersBuffer.edit().radiusInfo; - current.w = level; - } -} - -void AmbientOcclusionEffect::setDithering(bool enabled) { - if (enabled != isDitheringEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; - current.x = (float)enabled; - } -} - -void AmbientOcclusionEffect::setBordering(bool enabled) { - if (enabled != isBorderingEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; - current.w = (float)enabled; - } -} - -void AmbientOcclusionEffect::setFalloffBias(float bias) { - bias = std::max(0.0f, std::min(bias, 0.2f)); - if (bias != getFalloffBias()) { - auto& current = _parametersBuffer.edit().ditheringInfo; - current.z = (float)bias; - } -} - - -void AmbientOcclusionEffect::setNumSamples(int numSamples) { - numSamples = std::max(1.0f, (float) numSamples); - if (numSamples != getNumSamples()) { - auto& current = _parametersBuffer.edit().sampleInfo; - current.x = numSamples; - current.y = 1.0f / numSamples; - } -} - -void AmbientOcclusionEffect::setNumSpiralTurns(float numTurns) { - numTurns = std::max(0.0f, (float)numTurns); - if (numTurns != getNumSpiralTurns()) { - auto& current = _parametersBuffer.edit().sampleInfo; - current.z = numTurns; - } -} - -void AmbientOcclusionEffect::setEdgeSharpness(float sharpness) { - sharpness = std::max(0.0f, (float)sharpness); - if (sharpness != getEdgeSharpness()) { - auto& current = _parametersBuffer.edit().blurInfo; - current.x = sharpness; - } -} - -void AmbientOcclusionEffect::setBlurRadius(int radius) { - const int MAX_BLUR_RADIUS = 6; - radius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); - if (radius != getBlurRadius()) { - auto& current = _parametersBuffer.edit().blurInfo; - current.y = (float)radius; - updateGaussianDistribution(); - } -} - -void AmbientOcclusionEffect::setBlurDeviation(float deviation) { - deviation = std::max(0.0f, deviation); - if (deviation != getBlurDeviation()) { - auto& current = _parametersBuffer.edit().blurInfo; - current.z = deviation; - updateGaussianDistribution(); - } -} void AmbientOcclusionEffect::updateGaussianDistribution() { auto coefs = _parametersBuffer.edit()._gaussianCoefs; GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, getBlurRadius(), getBlurDeviation()); } void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; auto framebufferCache = DependencyManager::get(); auto depthBuffer = framebufferCache->getPrimaryDepthTexture(); @@ -417,6 +397,8 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext } _gpuTimer.end(batch); - }); + + // Update the timer + std::static_pointer_cast(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage(); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 9973bd9ec8..c040e31188 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -16,64 +16,78 @@ #include "render/DrawTask.h" +class AmbientOcclusionEffectConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) + Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) + Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) + Q_PROPERTY(float radius MEMBER radius WRITE setRadius) + Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) + Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) + Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) + Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) + Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) + Q_PROPERTY(int numSamples MEMBER numSamples WRITE setNumSamples) + Q_PROPERTY(int resolutionLevel MEMBER resolutionLevel WRITE setResolutionLevel) + Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) + Q_PROPERTY(double gpuTime READ getGpuTime) +public: + AmbientOcclusionEffectConfig() : render::Job::Config(false) {} + + const int MAX_RESOLUTION_LEVEL = 4; + const int MAX_BLUR_RADIUS = 6; + + void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } + void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } + void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } + void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } + void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } + void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } + void setNumSamples(int samples) { numSamples = std::max(1.0f, (float)samples); emit dirty(); } + void setResolutionLevel(int level) { resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); emit dirty(); } + void setBlurRadius(int radius) { blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); emit dirty(); } + double getGpuTime() { return gpuTime; } + + float radius{ 0.5f }; + float obscuranceLevel{ 0.5f }; // intensify or dim down the obscurance effect + float falloffBias{ 0.01f }; + float edgeSharpness{ 1.0f }; + float blurDeviation{ 2.5f }; + float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions + int numSamples{ 11 }; + int resolutionLevel{ 1 }; + int blurRadius{ 4 }; // 0 means no blurring + bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true + bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true + double gpuTime{ 0.0 }; + +signals: + void dirty(); +}; class AmbientOcclusionEffect { public: + using Config = AmbientOcclusionEffectConfig; + using JobModel = render::Job::Model; AmbientOcclusionEffect(); + void configure(const Config& config); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - void setResolutionLevel(int level); - int getResolutionLevel() const { return _parametersBuffer.get().resolutionInfo.x; } - - void setRadius(float radius); float getRadius() const { return _parametersBuffer.get().radiusInfo.x; } - - // Obscurance level which intensify or dim down the obscurance effect - void setLevel(float level); - float getLevel() const { return _parametersBuffer.get().radiusInfo.w; } - - // On to randomize the distribution of rays per pixel, should always be true - void setDithering(bool enabled); - bool isDitheringEnabled() const { return _parametersBuffer.get().ditheringInfo.x; } - - // On to avoid evaluating information from non existing pixels Out of the frame, should always be true - void setBordering(bool enabled); - bool isBorderingEnabled() const { return _parametersBuffer.get().ditheringInfo.w; } - - // Faloff Bias - void setFalloffBias(float bias); - int getFalloffBias() const { return (int)_parametersBuffer.get().ditheringInfo.z; } - - // Number of samples per pixel to evaluate the Obscurance - void setNumSamples(int numSamples); - int getNumSamples() const { return (int)_parametersBuffer.get().sampleInfo.x; } - - // Number of spiral turns defining an angle span to distribute the samples ray directions - void setNumSpiralTurns(float numTurns); - float getNumSpiralTurns() const { return _parametersBuffer.get().sampleInfo.z; } - - // Edge blurring setting - void setEdgeSharpness(float sharpness); - int getEdgeSharpness() const { return (int)_parametersBuffer.get().blurInfo.x; } - - // Blurring Radius - // 0 means no blurring - const int MAX_BLUR_RADIUS = 6; - void setBlurRadius(int radius); - int getBlurRadius() const { return (int)_parametersBuffer.get().blurInfo.y; } - - void setBlurDeviation(float deviation); + float getObscuranceLevel() const { return _parametersBuffer.get().radiusInfo.w; } + float getFalloffBias() const { return (float)_parametersBuffer.get().ditheringInfo.z; } + float getEdgeSharpness() const { return (float)_parametersBuffer.get().blurInfo.x; } float getBlurDeviation() const { return _parametersBuffer.get().blurInfo.z; } - + float getNumSpiralTurns() const { return _parametersBuffer.get().sampleInfo.z; } + int getNumSamples() const { return (int)_parametersBuffer.get().sampleInfo.x; } + int getResolutionLevel() const { return _parametersBuffer.get().resolutionInfo.x; } + int getBlurRadius() const { return (int)_parametersBuffer.get().blurInfo.y; } + bool isDitheringEnabled() const { return _parametersBuffer.get().ditheringInfo.x; } + bool isBorderingEnabled() const { return _parametersBuffer.get().ditheringInfo.w; } - double getGPUTime() const { return _gpuTimer.getAverage(); } - - using JobModel = render::Task::Job::Model; - private: - void updateGaussianDistribution(); void setDepthInfo(float nearZ, float farZ); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index f56514c7ec..6c26b16124 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -92,14 +92,15 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { } void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); - if (renderContext->getArgs()->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + RenderArgs* args = renderContext->args; + + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { return; } - RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index 51a8e3a1de..f0a32c68ff 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -16,13 +16,21 @@ #include "render/DrawTask.h" +class AntiAliasingConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled) +public: + AntiAliasingConfig() : render::Job::Config(false) {} +}; + class Antialiasing { public: + using Config = AntiAliasingConfig; + using JobModel = render::Job::Model; Antialiasing(); - + void configure(const Config& config) {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; const gpu::PipelinePointer& getAntialiasingPipeline(); const gpu::PipelinePointer& getBlendPipeline(); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index bb45d3945a..06d8709dc5 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -27,7 +27,18 @@ using namespace render; -enum Slots { +void DebugDeferredBufferConfig::setMode(int newMode) { + if (newMode == mode) { + return; + } else if (newMode > DebugDeferredBuffer::CustomMode || newMode < 0) { + mode = DebugDeferredBuffer::CustomMode; + } else { + mode = newMode; + } + emit dirty(); +} + +enum Slot { Diffuse = 0, Normal, Specular, @@ -39,6 +50,8 @@ enum Slots { AmbientOcclusionBlurred }; + + static const std::string DEFAULT_DIFFUSE_SHADER { "vec4 getFragmentColor() {" " return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" @@ -129,7 +142,7 @@ DebugDeferredBuffer::DebugDeferredBuffer() { _customPipelines.emplace(CUSTOM_FILE, pipeline); } -std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string customFile) { +std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string customFile) { switch (mode) { case DiffuseMode: return DEFAULT_DIFFUSE_SHADER; @@ -158,7 +171,7 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string cus return std::string(); } -bool DebugDeferredBuffer::pipelineNeedsUpdate(Modes mode, std::string customFile) const { +bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, std::string customFile) const { if (mode != CustomMode) { return !_pipelines[mode]; } @@ -175,7 +188,7 @@ bool DebugDeferredBuffer::pipelineNeedsUpdate(Modes mode, std::string customFile return true; } -const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::string customFile) { +const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::string customFile) { if (pipelineNeedsUpdate(mode, customFile)) { static const std::string VERTEX_SHADER { debug_deferred_buffer_vert }; static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag }; @@ -221,18 +234,15 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::st } } +void DebugDeferredBuffer::configure(const Config& config) { + _mode = (Mode)config.mode; + _size = config.size; +} void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); - - // Guard against unspecified modes - auto mode = renderContext->_deferredDebugMode; - if (mode > (int)CustomMode) { - renderContext->_deferredDebugMode = -1; - return; - } + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { const auto geometryBuffer = DependencyManager::get(); @@ -250,7 +260,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren // TODO REMOVE: Temporary until UI auto first = _customPipelines.begin()->first; - batch.setPipeline(getPipeline(Modes(renderContext->_deferredDebugMode), first)); + batch.setPipeline(getPipeline(_mode, first)); batch.setResourceTexture(Diffuse, framebufferCache->getDeferredColorTexture()); batch.setResourceTexture(Normal, framebufferCache->getDeferredNormalTexture()); @@ -263,8 +273,8 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren batch.setResourceTexture(AmbientOcclusionBlurred, framebufferCache->getOcclusionBlurredTexture()); const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); - const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y); - const glm::vec2 topRight(renderContext->_deferredDebugSize.z, renderContext->_deferredDebugSize.w); + const glm::vec2 bottomLeft(_size.x, _size.y); + const glm::vec2 topRight(_size.z, _size.w); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color); }); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index ef0fe512b2..ecc2012466 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -16,16 +16,37 @@ #include +class DebugDeferredBufferConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled) + Q_PROPERTY(int mode MEMBER mode WRITE setMode) + Q_PROPERTY(glm::vec4 size MEMBER size NOTIFY dirty) +public: + DebugDeferredBufferConfig() : render::Job::Config(false) {} + + void setMode(int newMode); + + int mode{ 0 }; + glm::vec4 size{ 0.0f, 0.0f, 0.0f, 0.0f }; +signals: + void dirty(); +}; + class DebugDeferredBuffer { public: - using JobModel = render::Task::Job::Model; + using Config = DebugDeferredBufferConfig; + using JobModel = render::Job::Model; DebugDeferredBuffer(); - + + void configure(const Config& config); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); -private: - enum Modes : uint8_t { +protected: + friend class DebugDeferredBufferConfig; + + enum Mode : uint8_t { + // Use Mode suffix to avoid collisions DiffuseMode = 0, SpecularMode, RoughnessMode, @@ -38,6 +59,11 @@ private: AmbientOcclusionBlurredMode, CustomMode // Needs to stay last }; + +private: + Mode _mode; + glm::vec4 _size; + struct CustomPipeline { gpu::PipelinePointer pipeline; mutable QFileInfo info; @@ -45,9 +71,9 @@ private: using StandardPipelines = std::array; using CustomPipelines = std::unordered_map; - bool pipelineNeedsUpdate(Modes mode, std::string customFile = std::string()) const; - const gpu::PipelinePointer& getPipeline(Modes mode, std::string customFile = std::string()); - std::string getShaderSourceCode(Modes mode, std::string customFile = std::string()); + bool pipelineNeedsUpdate(Mode mode, std::string customFile = std::string()) const; + const gpu::PipelinePointer& getPipeline(Mode mode, std::string customFile = std::string()); + std::string getShaderSourceCode(Mode mode, std::string customFile = std::string()); StandardPipelines _pipelines; CustomPipelines _customPipelines; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 723eacd6be..f81ac2efd1 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -162,7 +162,7 @@ void DeferredLightingEffect::prepare(RenderArgs* args) { } void DeferredLightingEffect::render(const render::RenderContextPointer& renderContext) { - auto args = renderContext->getArgs(); + auto args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { // Allocate the parameters buffer used by all the deferred shaders @@ -188,14 +188,14 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - // BInd the G-Buffer surfaces + // Bind the G-Buffer surfaces batch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, framebufferCache->getDeferredColorTexture()); batch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, framebufferCache->getDeferredNormalTexture()); batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, framebufferCache->getDeferredSpecularTexture()); batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, framebufferCache->getPrimaryDepthTexture()); // need to assign the white texture if ao is off - if (renderContext->getOcclusionStatus()) { + if (_ambientOcclusionEnabled) { batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture()); } else { batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture()); @@ -313,12 +313,12 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo { bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap()); - auto& program = _shadowMapStatus ? _directionalLightShadow : _directionalLight; - LightLocationsPtr locations = _shadowMapStatus ? _directionalLightShadowLocations : _directionalLightLocations; + auto& program = _shadowMapEnabled ? _directionalLightShadow : _directionalLight; + LightLocationsPtr locations = _shadowMapEnabled ? _directionalLightShadowLocations : _directionalLightLocations; // Setup the global directional pass pipeline { - if (_shadowMapStatus) { + if (_shadowMapEnabled) { if (useSkyboxCubemap) { program = _directionalSkyboxLightShadow; locations = _directionalSkyboxLightShadowLocations; diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 82b9eeadba..ee4eeb445e 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -54,14 +54,17 @@ public: void setGlobalSkybox(const model::SkyboxPointer& skybox); const LightStage& getLightStage() { return _lightStage; } - void setShadowMapStatus(bool enable) { _shadowMapStatus = enable; }; + void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; }; + void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; } private: - LightStage _lightStage; - bool _shadowMapStatus{ false }; - DeferredLightingEffect() = default; + LightStage _lightStage; + + bool _shadowMapEnabled{ false }; + bool _ambientOcclusionEnabled{ false }; + model::MeshPointer _spotLightMesh; model::MeshPointer getSpotLightMesh(); diff --git a/libraries/render-utils/src/HitEffect.cpp b/libraries/render-utils/src/HitEffect.cpp index 89f950d830..8f1e9259d4 100644 --- a/libraries/render-utils/src/HitEffect.cpp +++ b/libraries/render-utils/src/HitEffect.cpp @@ -61,9 +61,10 @@ const gpu::PipelinePointer& HitEffect::getHitEffectPipeline() { } void HitEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; diff --git a/libraries/render-utils/src/HitEffect.h b/libraries/render-utils/src/HitEffect.h index a83fb36693..5252e63726 100644 --- a/libraries/render-utils/src/HitEffect.h +++ b/libraries/render-utils/src/HitEffect.h @@ -11,13 +11,21 @@ #include +class HitEffectConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled) +public: + HitEffectConfig() : render::Job::Config(false) {} +}; + class HitEffect { public: + using Config = HitEffectConfig; + using JobModel = render::Job::Model; HitEffect(); - + void configure(const Config& config) {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; const gpu::PipelinePointer& getHitEffectPipeline(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 7afab44c6c..06fadb69b3 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -60,18 +60,28 @@ using namespace render; void initDeferredPipelines(render::ShapePlumber& plumber); void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - DependencyManager::get()->prepare(renderContext->getArgs()); + DependencyManager::get()->prepare(renderContext->args); } void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->render(renderContext); } -void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - _toneMappingEffect.render(renderContext->getArgs()); +void ToneMappingDeferred::configure(const Config& config) { + if (config.exposure >= 0.0f) { + _toneMappingEffect.setExposure(config.exposure); + } + + if (config.curve >= 0) { + _toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve); + } } -RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { +void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + _toneMappingEffect.render(renderContext->args); +} + +RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // Prepare the ShapePipelines @@ -79,19 +89,13 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { initDeferredPipelines(*shapePlumber); // CPU: Fetch the renderOpaques - auto fetchedOpaques = addJob("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) { - context->getItemsConfig().opaque.numFeed = count; - })); + auto fetchedOpaques = addJob("FetchOpaque"); auto culledOpaques = addJob>("CullOpaque", fetchedOpaques, cullFunctor); auto opaques = addJob("DepthSortOpaque", culledOpaques); // CPU only, create the list of renderedTransparents items auto fetchedTransparents = addJob("FetchTransparent", FetchItems( - ItemFilter::Builder::transparentShape().withoutLayered(), - [](const RenderContextPointer& context, int count) { - context->getItemsConfig().transparent.numFeed = count; - } - )); + ItemFilter::Builder::transparentShape().withoutLayered())); auto culledTransparents = addJob>("CullTransparent", fetchedTransparents, cullFunctor); auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); @@ -99,7 +103,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { addJob("PrepareDeferred"); // Render opaque objects in DeferredBuffer - addJob("DrawOpaqueDeferred", opaques, shapePlumber); + addJob("DrawOpaqueDeferred", opaques, shapePlumber); // Once opaque is all rendered create stencil background addJob("DrawOpaqueStencil"); @@ -109,8 +113,6 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { // AO job addJob("AmbientOcclusion"); - _jobs.back().setEnabled(false); - _occlusionJobIndex = (int)_jobs.size() - 1; // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. addJob("DrawLight", cullFunctor); @@ -120,20 +122,15 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { // AA job to be revisited addJob("Antialiasing"); - _antialiasingJobIndex = (int)_jobs.size() - 1; - enableJob(_antialiasingJobIndex, false); - - // Render transparent objects forward in LigthingBuffer - addJob("DrawTransparentDeferred", transparents, shapePlumber); + // Render transparent objects forward in LightingBuffer + addJob("DrawTransparentDeferred", transparents, shapePlumber); + // Lighting Buffer ready for tone mapping addJob("ToneMapping"); - _toneMappingJobIndex = (int)_jobs.size() - 1; // Debugging Deferred buffer job addJob("DebugDeferredBuffer"); - _drawDebugDeferredBufferIndex = (int)_jobs.size() - 1; - enableJob(_drawDebugDeferredBufferIndex, false); // Status icon rendering job { @@ -141,15 +138,11 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); - _drawStatusJobIndex = (int)_jobs.size() - 1; - enableJob(_drawStatusJobIndex, false); } addJob("DrawOverlay3D", shapePlumber); addJob("HitEffect"); - _drawHitEffectJobIndex = (int)_jobs.size() -1; - enableJob(_drawHitEffectJobIndex, false); addJob("Blit"); } @@ -163,62 +156,28 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend // Is it possible that we render without a viewFrustum ? - if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { + if (!(renderContext->args && renderContext->args->_viewFrustum)) { return; } - setDrawDebugDeferredBuffer(renderContext->_deferredDebugMode); - setDrawItemStatus(renderContext->getDrawStatus()); - setDrawHitEffect(renderContext->getDrawHitEffect()); - // TODO: turn on/off AO through menu item - setOcclusionStatus(renderContext->getOcclusionStatus()); - - if (_occlusionJobIndex >= 0) { - _jobs[_occlusionJobIndex].edit().setResolutionLevel(renderContext->getAmbientOcclusion().resolutionLevel); - _jobs[_occlusionJobIndex].edit().setRadius(renderContext->getAmbientOcclusion().radius); - _jobs[_occlusionJobIndex].edit().setLevel(renderContext->getAmbientOcclusion().level); - _jobs[_occlusionJobIndex].edit().setNumSamples(renderContext->getAmbientOcclusion().numSamples); - _jobs[_occlusionJobIndex].edit().setNumSpiralTurns(renderContext->getAmbientOcclusion().numSpiralTurns); - _jobs[_occlusionJobIndex].edit().setDithering(renderContext->getAmbientOcclusion().ditheringEnabled); - _jobs[_occlusionJobIndex].edit().setFalloffBias(renderContext->getAmbientOcclusion().falloffBias); - _jobs[_occlusionJobIndex].edit().setEdgeSharpness(renderContext->getAmbientOcclusion().edgeSharpness); - _jobs[_occlusionJobIndex].edit().setBlurRadius(renderContext->getAmbientOcclusion().blurRadius); - _jobs[_occlusionJobIndex].edit().setBlurDeviation(renderContext->getAmbientOcclusion().blurDeviation); - } - - setAntialiasingStatus(renderContext->getFxaaStatus()); - setToneMappingExposure(renderContext->getTone().exposure); - setToneMappingToneCurve(renderContext->getTone().toneCurve); - // TODO: Allow runtime manipulation of culling ShouldRenderFunctor - - // TODO: For now, lighting is controlled through a singleton, so it is distinct - DependencyManager::get()->setShadowMapStatus(renderContext->getShadowMapStatus()); - - renderContext->getArgs()->_context->syncCache(); - for (auto job : _jobs) { job.run(sceneContext, renderContext); } - - if (_occlusionJobIndex >= 0 && renderContext->getOcclusionStatus()) { - renderContext->getAmbientOcclusion().gpuTime = _jobs[_occlusionJobIndex].edit().getGPUTime(); - } else { - renderContext->getAmbientOcclusion().gpuTime = 0.0; - } }; -void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); +void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); + auto config = std::static_pointer_cast(renderContext->jobConfig); + + RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; - auto& opaque = renderContext->getItemsConfig().opaque; - opaque.numDrawn = (int)inItems.size(); + config->numDrawn = (int)inItems.size(); glm::mat4 projMat; Transform viewMat; @@ -228,33 +187,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, opaque.maxDrawn); - args->_batch = nullptr; - }); -} - -void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); - - RenderArgs* args = renderContext->getArgs(); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - args->_batch = &batch; - - auto& transparent = renderContext->getItemsConfig().transparent; - transparent.numDrawn = (int)inItems.size(); - - glm::mat4 projMat; - Transform viewMat; - args->_viewFrustum->evalProjectionMatrix(projMat); - args->_viewFrustum->evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, transparent.maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); args->_batch = nullptr; }); } @@ -278,13 +211,14 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + auto config = std::static_pointer_cast(renderContext->jobConfig); ItemIDsBounds inItems; inItems.reserve(items.size()); @@ -294,12 +228,11 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon inItems.emplace_back(id); } } - auto& overlay3D = renderContext->getItemsConfig().overlay3D; - overlay3D.numFeed = (int)inItems.size(); - overlay3D.numDrawn = (int)inItems.size(); + config->numItems = (int)inItems.size(); + config->numDrawn = (int)inItems.size(); if (!inItems.empty()) { - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call @@ -328,7 +261,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, renderContext->getItemsConfig().overlay3D.maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); }); args->_batch = nullptr; args->_whiteTexture.reset(); @@ -357,11 +290,11 @@ const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); // from the touched pixel generate the stencil buffer - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -383,8 +316,8 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren } void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -396,7 +329,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const for (auto id : items) { inItems.emplace_back(id); } - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -423,10 +356,10 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const } void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_context); + assert(renderContext->args); + assert(renderContext->args->_context); - RenderArgs* renderArgs = renderContext->getArgs(); + RenderArgs* renderArgs = renderContext->args; auto blitFbo = renderArgs->_blitFramebuffer; if (!blitFbo) { @@ -493,34 +426,6 @@ void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPoint }); } -void RenderDeferredTask::setToneMappingExposure(float exposure) { - if (_toneMappingJobIndex >= 0) { - _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setExposure(exposure); - } -} - -float RenderDeferredTask::getToneMappingExposure() const { - if (_toneMappingJobIndex >= 0) { - return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getExposure(); - } else { - return 0.0f; - } -} - -void RenderDeferredTask::setToneMappingToneCurve(int toneCurve) { - if (_toneMappingJobIndex >= 0) { - _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)toneCurve); - } -} - -int RenderDeferredTask::getToneMappingToneCurve() const { - if (_toneMappingJobIndex >= 0) { - return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getToneCurve(); - } else { - return 0.0f; - } -} - void pipelineBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { if (pipeline.locations->normalFittingMapUnit > -1) { batch.setResourceTexture(pipeline.locations->normalFittingMapUnit, diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 78327006cd..414b019b31 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -22,14 +22,14 @@ class SetupDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; }; class PrepareDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; }; @@ -37,38 +37,61 @@ class RenderDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; +}; + + +class ToneMappingConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled) + Q_PROPERTY(float exposure MEMBER exposure NOTIFY dirty); + Q_PROPERTY(int curve MEMBER curve NOTIFY dirty); +public: + ToneMappingConfig() : render::Job::Config(true) {} + + float exposure{ 0.0f }; + int curve{ 3 }; +signals: + void dirty(); }; class ToneMappingDeferred { public: + using Config = ToneMappingConfig; + using JobModel = render::Job::Model; + + void configure(const Config& config); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); ToneMappingEffect _toneMappingEffect; - - using JobModel = render::Task::Job::Model; }; -class DrawOpaqueDeferred { +class DrawConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) public: - DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); + int getNumDrawn() { return numDrawn; } - using JobModel = render::Task::Job::ModelI; - -protected: - render::ShapePlumberPointer _shapePlumber; -}; - -class DrawTransparentDeferred { -public: - DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); - - using JobModel = render::Task::Job::ModelI; + int numDrawn{ 0 }; + int maxDrawn{ -1 }; +signals: + void dirty(); +}; + +class DrawDeferred { +public: + using Config = DrawConfig; + using JobModel = render::Job::ModelI; + + DrawDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + + void configure(const Config& config) { _maxDrawn = config.maxDrawn; } + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); protected: render::ShapePlumberPointer _shapePlumber; + int _maxDrawn; // initialized by Config }; class DrawStencilDeferred { @@ -77,7 +100,7 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; protected: static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable @@ -87,64 +110,57 @@ class DrawBackgroundDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; +}; + +class DrawOverlay3DConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) +public: + int getNumItems() { return numItems; } + int getNumDrawn() { return numDrawn; } + + int numItems{ 0 }; + int numDrawn{ 0 }; + int maxDrawn{ -1 }; +signals: + void dirty(); }; class DrawOverlay3D { public: - DrawOverlay3D(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - static const gpu::PipelinePointer& getOpaquePipeline(); + using Config = DrawOverlay3DConfig; + using JobModel = render::Job::Model; + DrawOverlay3D(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + + void configure(const Config& config) { _maxDrawn = config.maxDrawn; } void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + static const gpu::PipelinePointer& getOpaquePipeline(); protected: static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable render::ShapePlumberPointer _shapePlumber; + int _maxDrawn; // initialized by Config }; class Blit { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Task::Job::Model; + using JobModel = render::Job::Model; }; class RenderDeferredTask : public render::Task { public: RenderDeferredTask(render::CullFunctor cullFunctor); - void setDrawDebugDeferredBuffer(int draw) { enableJob(_drawDebugDeferredBufferIndex, draw >= 0); } - bool doDrawDebugDeferredBuffer() const { return getEnableJob(_drawDebugDeferredBufferIndex); } - - void setDrawItemStatus(int draw) { enableJob(_drawStatusJobIndex, draw > 0); } - bool doDrawItemStatus() const { return getEnableJob(_drawStatusJobIndex); } - - void setDrawHitEffect(bool draw) { enableJob(_drawHitEffectJobIndex, draw); } - bool doDrawHitEffect() const { return getEnableJob(_drawHitEffectJobIndex); } + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - void setOcclusionStatus(bool draw) { enableJob(_occlusionJobIndex, draw); } - bool doOcclusionStatus() const { return getEnableJob(_occlusionJobIndex); } - - void setAntialiasingStatus(bool draw) { enableJob(_antialiasingJobIndex, draw); } - bool doAntialiasingStatus() const { return getEnableJob(_antialiasingJobIndex); } - - void setToneMappingExposure(float exposure); - float getToneMappingExposure() const; - - void setToneMappingToneCurve(int toneCurve); - int getToneMappingToneCurve() const; - - virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - -protected: - int _drawDebugDeferredBufferIndex; - int _drawStatusJobIndex; - int _drawHitEffectJobIndex; - int _occlusionJobIndex; - int _antialiasingJobIndex; - int _toneMappingJobIndex; + using JobModel = Model; }; #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderScriptingInterface.cpp b/libraries/render-utils/src/RenderScriptingInterface.cpp deleted file mode 100644 index 6b3fedd97f..0000000000 --- a/libraries/render-utils/src/RenderScriptingInterface.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// -// RenderScriptingInterface.cpp -// libraries/render-utils -// -// Created by Zach Pomerantz on 12/16/15. -// Copyright 2015 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 -// - -#include "RenderScriptingInterface.h" - -RenderScriptingInterface::RenderScriptingInterface() {}; - -void RenderScripting::Tone::setCurve(const QString& curve) { - if (curve == QString("None")) { - toneCurve = 0; - } else if (curve == QString("Gamma22")) { - toneCurve = 1; - } else if (curve == QString("Reinhard")) { - toneCurve = 2; - } else if (curve == QString("Filmic")) { - toneCurve = 3; - } -} - -QString RenderScripting::Tone::getCurve() const { - switch (toneCurve) { - case 0: - return QString("None"); - case 1: - return QString("Gamma22"); - case 2: - return QString("Reinhard"); - case 3: - return QString("Filmic"); - default: - return QString("Filmic"); - }; -} - -render::RenderContext RenderScriptingInterface::getRenderContext() { - render::RenderContext::ItemsConfig items{ *_opaque, *_transparent, *_overlay3D }; - return render::RenderContext{ items, *_tone, *_ambientOcclusion, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode }; -} - -void RenderScriptingInterface::setItemCounts(const render::RenderContext::ItemsConfig& items) { - _opaque->setCounts(items.opaque); - _transparent->setCounts(items.transparent); - _overlay3D->setCounts(items.overlay3D); -} - -void RenderScriptingInterface::setJobGPUTimes(double aoTime) { - _ambientOcclusion->gpuTime = aoTime; -} \ No newline at end of file diff --git a/libraries/render-utils/src/RenderScriptingInterface.h b/libraries/render-utils/src/RenderScriptingInterface.h deleted file mode 100644 index 947240fea6..0000000000 --- a/libraries/render-utils/src/RenderScriptingInterface.h +++ /dev/null @@ -1,140 +0,0 @@ -// -// RenderScriptingInterface.h -// libraries/render-utils -// -// Created by Zach Pomerantz on 12/16/15. -// Copyright 2014 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_RenderScriptingInterface_h -#define hifi_RenderScriptingInterface_h - -#include // QObject -#include // Dependency - -#include "render/Engine.h" - -namespace RenderScripting { - using State = render::RenderContext::ItemsConfig::State; - using Counter = render::RenderContext::ItemsConfig::Counter; - - class ItemCounter : public QObject, public Counter { - Q_OBJECT - - public: - Q_PROPERTY(int numFeed READ getNumFeed) - Q_PROPERTY(int numDrawn READ getNumDrawn) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn) - - protected: - int getNumFeed() const { return numFeed; } - int getNumDrawn() const { return numDrawn; } - }; - using ItemCounterPointer = std::unique_ptr; - - class ItemState : public QObject, public State { - Q_OBJECT - - public: - Q_PROPERTY(bool render MEMBER render) - Q_PROPERTY(bool cull MEMBER cull) - Q_PROPERTY(bool sort MEMBER sort) - - Q_PROPERTY(int numFeed READ getNumFeed) - Q_PROPERTY(int numDrawn READ getNumDrawn) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn) - - protected: - int getNumFeed() const { return numFeed; } - int getNumDrawn() const { return numDrawn; } - }; - using ItemStatePointer = std::unique_ptr; - - class Tone : public QObject, public render::RenderContext::Tone { - Q_OBJECT - - public: - Q_PROPERTY(float exposure MEMBER exposure) - Q_PROPERTY(QString curve READ getCurve WRITE setCurve) - - QString getCurve() const; - int getCurveValue() const { return toneCurve; } - void setCurve(const QString& curve); - }; - using TonePointer = std::unique_ptr; - - class AmbientOcclusion : public QObject, public render::RenderContext::AmbientOcclusion { - Q_OBJECT - - public: - Q_PROPERTY(int resolutionLevel MEMBER resolutionLevel) - Q_PROPERTY(float radius MEMBER radius) - Q_PROPERTY(float level MEMBER level) - Q_PROPERTY(int numSamples MEMBER numSamples) - Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns) - Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled) - Q_PROPERTY(float falloffBias MEMBER falloffBias) - Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness) - Q_PROPERTY(int blurRadius MEMBER blurRadius) - Q_PROPERTY(float blurDeviation MEMBER blurDeviation) - Q_PROPERTY(double gpuTime MEMBER gpuTime) - }; - using AmbientOcclusionPointer = std::unique_ptr; -}; - -class RenderScriptingInterface : public QObject, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - - public: - Q_PROPERTY(RenderScripting::ItemState* opaque READ getOpaque) - Q_PROPERTY(RenderScripting::ItemState* transparent READ getTransparent) - Q_PROPERTY(RenderScripting::ItemCounter* overlay3D READ getOverlay3D) - - Q_PROPERTY(RenderScripting::Tone* tone READ getTone) - Q_PROPERTY(RenderScripting::AmbientOcclusion* ambientOcclusion READ getAmbientOcclusion) - - Q_PROPERTY(int displayItemStatus MEMBER _drawStatus) - Q_PROPERTY(bool displayHitEffect MEMBER _drawHitEffect) - - Q_PROPERTY(int deferredDebugMode MEMBER _deferredDebugMode) - Q_PROPERTY(glm::vec4 deferredDebugSize MEMBER _deferredDebugSize) - - render::RenderContext getRenderContext(); - void setItemCounts(const render::RenderContext::ItemsConfig& items); - - // FIXME: It is ugly, we need a cleaner solution - void setJobGPUTimes(double aoTime); - -protected: - RenderScriptingInterface(); - ~RenderScriptingInterface() {}; - - RenderScripting::ItemState* getOpaque() const { return _opaque.get(); } - RenderScripting::ItemState* getTransparent() const { return _transparent.get(); } - RenderScripting::ItemCounter* getOverlay3D() const { return _overlay3D.get(); } - - RenderScripting::Tone* getTone() const { return _tone.get(); } - RenderScripting::AmbientOcclusion* getAmbientOcclusion() const { return _ambientOcclusion.get(); } - - RenderScripting::ItemStatePointer _opaque = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; - RenderScripting::ItemStatePointer _transparent = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; - RenderScripting::ItemCounterPointer _overlay3D = RenderScripting::ItemCounterPointer{new RenderScripting::ItemCounter{}}; - - RenderScripting::TonePointer _tone = RenderScripting::TonePointer{ new RenderScripting::Tone{} }; - - RenderScripting::AmbientOcclusionPointer _ambientOcclusion = RenderScripting::AmbientOcclusionPointer{ new RenderScripting::AmbientOcclusion{} }; - - // Options - int _drawStatus = 0; - bool _drawHitEffect = false; - - // Debugging - int _deferredDebugMode = -1; - glm::vec4 _deferredDebugSize { 0.0f, -1.0f, 1.0f, 1.0f }; -}; - -#endif // hifi_RenderScriptingInterface_h diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 3fd8e022f5..11b1753895 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -29,15 +29,15 @@ using namespace render; void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ShapesIDsBounds& inShapes) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); const auto& lightStage = DependencyManager::get()->getLightStage(); const auto globalLight = lightStage.lights[0]; const auto& shadow = globalLight->shadow; const auto& fbo = shadow.framebuffer; - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -78,7 +78,8 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const }); } -RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task() { +// The shadow task *must* use this base ctor to initialize with its own Config, see Task.h +RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task(std::make_shared()) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // Prepare the ShapePipeline @@ -119,14 +120,15 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task() { addJob("RenderShadowMap", shadowShapes, shapePlumber); } +void RenderShadowTask::configure(const Config& configuration) { + DependencyManager::get()->setShadowMapEnabled(configuration.enabled); + // This is a task, so must still propogate configure() to its Jobs + Task::configure(configuration); +} + void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { assert(sceneContext); - RenderArgs* args = renderContext->getArgs(); - - // This feature is in a debugging stage - it must be turned on explicitly - if (!renderContext->getShadowMapStatus()) { - return; - } + RenderArgs* args = renderContext->args; // sanity checks if (!sceneContext->_scene || !args) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index add021994a..e1bf983b79 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -21,19 +21,34 @@ class ViewFrustum; class RenderShadowMap { public: + using JobModel = render::Job::ModelI; + RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ShapesIDsBounds& inShapes); - using JobModel = render::Task::Job::ModelI; protected: render::ShapePlumberPointer _shapePlumber; }; +class RenderShadowTaskConfig : public render::Task::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) +public: + RenderShadowTaskConfig() : render::Task::Config(false) {} + +signals: + void dirty(); +}; + class RenderShadowTask : public render::Task { public: + using Config = RenderShadowTaskConfig; + using JobModel = Model; + RenderShadowTask(render::CullFunctor shouldRender); + void configure(const Config& configuration); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); }; diff --git a/libraries/render/src/render/Context.cpp b/libraries/render/src/render/Context.cpp deleted file mode 100644 index 0a392e366e..0000000000 --- a/libraries/render/src/render/Context.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Context.cpp -// render/src/render -// -// Created by Zach Pomerantz on 1/6/2015. -// Copyright 2015 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 -// - -#include "Context.h" - -using namespace render; - -RenderContext::RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) - : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, - _args{ nullptr }, - _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, - _items{ items }, _tone{ tone }, _ambientOcclusion{ ao } {} - -void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap) { - _occlusionStatus = occlusion; - _fxaaStatus = fxaa; - _shadowMapStatus = shadowMap; - - if (showOwned) { - _drawStatus |= render::showNetworkStatusFlag; - } -}; - diff --git a/libraries/render/src/render/Context.h b/libraries/render/src/render/Context.h index 33c4c13a4c..04cf373146 100644 --- a/libraries/render/src/render/Context.h +++ b/libraries/render/src/render/Context.h @@ -24,104 +24,14 @@ public: }; using SceneContextPointer = std::shared_ptr; -// see examples/utilities/tools/renderEngineDebug.js -const int showDisplayStatusFlag = 1; -const int showNetworkStatusFlag = 2; +class JobConfig; class RenderContext { public: - class ItemsConfig { - public: - class Counter { - public: - Counter() {} - Counter(const Counter& counter) : maxDrawn { counter.maxDrawn } {} - - void setCounts(const Counter& counter) { - numFeed = counter.numFeed; - numDrawn = counter.numDrawn; - }; - - int numFeed { 0 }; - int numDrawn { 0 }; - int maxDrawn { -1 }; - }; - - class State : public Counter { - public: - bool render { true }; - bool cull { true }; - bool sort { true }; - - Counter counter{}; - }; - - ItemsConfig(State opaqueState, State transparentState, Counter overlay3DCounter) - : opaque{ opaqueState }, transparent{ transparentState }, overlay3D{ overlay3DCounter } {} - ItemsConfig() : ItemsConfig{ {}, {}, {} } {} - - // TODO: If member count increases, store counters in a map instead of multiple members - State opaque{}; - State transparent{}; - Counter overlay3D{}; - }; - - class Tone { - public: - int toneCurve = 1; // Means just Gamma 2.2 correction - float exposure = 0.0; - }; - - class AmbientOcclusion { - public: - int resolutionLevel { 1 }; - float radius { 0.5f }; // radius in meters of the AO effect - float level { 0.5f }; // Level of the obscrance value - int numSamples { 11 }; // Num Samples per pixel - float numSpiralTurns { 7.0f }; - bool ditheringEnabled { true }; - float falloffBias { 0.01f }; - float edgeSharpness { 1.0f }; - int blurRadius { 4 }; - float blurDeviation { 2.5f}; - - double gpuTime { 0.0 }; - }; - - RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); - RenderContext() {}; - - void setArgs(RenderArgs* args) { _args = args; } - RenderArgs* getArgs() { return _args; } - ItemsConfig& getItemsConfig() { return _items; } - Tone& getTone() { return _tone; } - AmbientOcclusion& getAmbientOcclusion() { return _ambientOcclusion; } - int getDrawStatus() { return _drawStatus; } - bool getDrawHitEffect() { return _drawHitEffect; } - bool getOcclusionStatus() { return _occlusionStatus; } - bool getFxaaStatus() { return _fxaaStatus; } - bool getShadowMapStatus() { return _shadowMapStatus; } - void setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap); - - // Debugging - int _deferredDebugMode; - glm::vec4 _deferredDebugSize; - -protected: - RenderArgs* _args; - - // Options - int _drawStatus; // bitflag - bool _drawHitEffect; - bool _occlusionStatus { false }; - bool _fxaaStatus { false }; - bool _shadowMapStatus { false }; - - ItemsConfig _items; - Tone _tone; - AmbientOcclusion _ambientOcclusion; + RenderArgs* args; + std::shared_ptr jobConfig{ nullptr }; }; -typedef std::shared_ptr RenderContextPointer; +using RenderContextPointer = std::shared_ptr; } diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 3f919b3d8c..112aa0be03 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -27,7 +27,10 @@ using namespace render; - +void DrawStatusConfig::dirtyHelper() { + enabled = showNetwork || showDisplay; + emit dirty(); +} const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { if (!_drawItemBoundsPipeline) { @@ -94,12 +97,17 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const { return _statusIconMap; } +void DrawStatus::configure(const Config& config) { + _showDisplay = config.showDisplay; + _showNetwork = config.showNetwork; +} + void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; const int NUM_STATUS_VEC4_PER_ITEM = 2; const int VEC4_LENGTH = 4; @@ -179,7 +187,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const unsigned int VEC3_ADRESS_OFFSET = 3; - if ((renderContext->getDrawStatus() & showDisplayStatusFlag) > 0) { + if (_showDisplay) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); @@ -192,7 +200,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, batch.setPipeline(getDrawItemStatusPipeline()); - if ((renderContext->getDrawStatus() & showNetworkStatusFlag) > 0) { + if (_showNetwork) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index c9cf16c0cb..b00e42b13d 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -16,7 +16,47 @@ #include "gpu/Batch.h" namespace render { + class DrawStatusConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(bool showDisplay MEMBER showDisplay WRITE setShowDisplay) + Q_PROPERTY(bool showNetwork MEMBER showNetwork WRITE setShowNetwork) + public: + DrawStatusConfig() : Job::Config(false) {} + + void dirtyHelper(); + + bool showDisplay{ false }; + bool showNetwork{ false }; + + public slots: + void setShowDisplay(bool enabled) { showDisplay = enabled; dirtyHelper(); } + void setShowNetwork(bool enabled) { showNetwork = enabled; dirtyHelper(); } + + signals: + void dirty(); + }; + class DrawStatus { + public: + using Config = DrawStatusConfig; + using JobModel = Job::ModelI; + + DrawStatus() {} + DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); } + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); + + const gpu::PipelinePointer getDrawItemBoundsPipeline(); + const gpu::PipelinePointer getDrawItemStatusPipeline(); + + void setStatusIconMap(const gpu::TexturePointer& map); + const gpu::TexturePointer getStatusIconMap() const; + + protected: + bool _showDisplay; // initialized by Config + bool _showNetwork; // initialized by Config + int _drawItemBoundPosLoc = -1; int _drawItemBoundDimLoc = -1; int _drawItemStatusPosLoc = -1; @@ -30,21 +70,6 @@ namespace render { gpu::BufferPointer _itemBounds; gpu::BufferPointer _itemStatus; gpu::TexturePointer _statusIconMap; - - public: - - DrawStatus() {} - DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); - - using JobModel = Task::Job::ModelI; - - const gpu::PipelinePointer getDrawItemBoundsPipeline(); - const gpu::PipelinePointer getDrawItemStatusPipeline(); - - void setStatusIconMap(const gpu::TexturePointer& map); - const gpu::TexturePointer getStatusIconMap() const; }; } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index ed35519b9d..9bf9a9acd3 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -22,10 +22,10 @@ using namespace render; void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; ViewFrustum* frustum = args->_viewFrustum; details._considered += inItems.size(); @@ -86,11 +86,11 @@ struct BackToFrontSort { }; void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; // Allocate and simply copy @@ -127,7 +127,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; for (const auto& itemDetails : inItems) { auto& item = scene->getItem(itemDetails.id); @@ -153,7 +153,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; auto numItemsToDraw = glm::max((int)inItems.size(), maxDrawnItems); for (auto i = 0; i < numItemsToDraw; ++i) { @@ -177,9 +177,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex } } - if (_probeNumItems) { - _probeNumItems(renderContext, (int)outItems.size()); - } + std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { @@ -187,8 +185,8 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo } void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->getArgs()); - assert(renderContext->getArgs()->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); // render lights auto& scene = sceneContext->_scene; @@ -201,7 +199,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext inItems.emplace_back(ItemIDAndBounds(id, item.getBound())); } - RenderArgs* args = renderContext->getArgs(); + RenderArgs* args = renderContext->args; auto& details = args->_details.edit(RenderDetails::OTHER_ITEM); ItemIDsBounds culledItems; diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 684d8bf4ea..d2d9b0a80b 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -27,34 +27,43 @@ void depthSortItems(const SceneContextPointer& sceneContext, const RenderContext void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); -class FetchItems { +class FetchItemsConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) public: - typedef std::function ProbeNumItems; - FetchItems() {} - FetchItems(const ProbeNumItems& probe): _probeNumItems(probe) {} - FetchItems(const ItemFilter& filter, const ProbeNumItems& probe): _filter(filter), _probeNumItems(probe) {} + int getNumItems() { return numItems; } - ItemFilter _filter = ItemFilter::Builder::opaqueShape().withoutLayered(); - ProbeNumItems _probeNumItems; - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelO; + int numItems{ 0 }; }; -template +class FetchItems { +public: + using Config = FetchItemsConfig; + using JobModel = Job::ModelO; + + FetchItems() {} + FetchItems(const ItemFilter& filter) : _filter(filter) {} + + ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; + + void configure(const Config& config) {} + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems); +}; + +template class CullItems { public: CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - const auto& args = renderContext->getArgs(); + const auto& args = renderContext->args; auto& details = args->_details.edit(T); outItems.clear(); outItems.reserve(inItems.size()); render::cullItems(renderContext, _cullFunctor, details, inItems, outItems); } - using JobModel = Task::Job::ModelIO, ItemIDsBounds, ItemIDsBounds>; + using JobModel = Job::ModelIO, ItemIDsBounds, ItemIDsBounds>; protected: CullFunctor _cullFunctor; @@ -66,14 +75,14 @@ public: DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelIO; + using JobModel = Job::ModelIO; }; class DrawLight { public: DrawLight(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - using JobModel = Task::Job::Model; + using JobModel = Job::Model; protected: CullFunctor _cullFunctor; @@ -82,7 +91,7 @@ protected: class PipelineSortShapes { public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes); - using JobModel = Task::Job::ModelIO; + using JobModel = Job::ModelIO; }; class DepthSortShapes { @@ -91,7 +100,7 @@ public: DepthSortShapes(bool frontToBack = true) : _frontToBack(frontToBack) {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes); - using JobModel = Task::Job::ModelIO; + using JobModel = Job::ModelIO; }; } diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 06cfd2e78b..be0481f97c 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -17,29 +17,14 @@ using namespace render; Engine::Engine() : _sceneContext(std::make_shared()), - _renderContext(std::make_shared()) -{ -} - -void Engine::registerScene(const ScenePointer& scene) { - _sceneContext->_scene = scene; -} - -void Engine::setRenderContext(const RenderContext& renderContext) { - (*_renderContext) = renderContext; -} - -void Engine::addTask(const TaskPointer& task) { - if (task) { - _tasks.push_back(task); - } + _renderContext(std::make_shared()) { } void Engine::run() { // Sync GPU state before beginning to render - _renderContext->getArgs()->_context->syncCache(); + _renderContext->args->_context->syncCache(); - for (auto task : _tasks) { - task->run(_sceneContext, _renderContext); + for (auto job : _jobs) { + job.run(_sceneContext, _renderContext); } } diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index fa01ef6018..e94de6f266 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -17,36 +17,31 @@ namespace render { -// The root of the tasks, the Engine, should not be known from the Tasks, -// The SceneContext is what navigates from the engine down to the Tasks -class Engine { +// The render engine holds all render tasks, and is itself a render task. +// State flows through tasks to jobs via the render and scene contexts - +// the engine should not be known from its jobs. +class Engine : public Task { public: - Engine(); - ~Engine() {} + ~Engine() = default; - // Register the scene should be [art of the init phase before running the engine - void registerScene(const ScenePointer& scene); + // Register the scene + void registerScene(const ScenePointer& scene) { _sceneContext->_scene = scene; } // Push a RenderContext - void setRenderContext(const RenderContext& renderContext); + void setRenderContext(const RenderContext& renderContext) { (*_renderContext) = renderContext; } RenderContextPointer getRenderContext() const { return _renderContext; } - void addTask(const TaskPointer& task); - const Tasks& getTasks() const { return _tasks; } - - + // Render a frame + // A frame must have a scene registered and a context set to render void run(); protected: - Tasks _tasks; - SceneContextPointer _sceneContext; RenderContextPointer _renderContext; }; -typedef std::shared_ptr EnginePointer; +using EnginePointer = std::shared_ptr; } #endif // hifi_render_Engine_h - diff --git a/libraries/render/src/render/Task.cpp b/libraries/render/src/render/Task.cpp new file mode 100644 index 0000000000..0bf20c6042 --- /dev/null +++ b/libraries/render/src/render/Task.cpp @@ -0,0 +1,25 @@ +// +// Task.cpp +// render/src/render +// +// Created by Zach Pomerantz on 1/21/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 +// + +#include + +#include "Task.h" + +using namespace render; + +void TaskConfig::refresh() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "refresh", Qt::BlockingQueuedConnection); + return; + } + + _task->configure(*this); +} diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index b63cb88dcf..ac05fbe68a 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -2,8 +2,8 @@ // Task.h // render/src/render // -// Created by Zach Pomerantz on 1/6/2015. -// Copyright 2015 High Fidelity, Inc. +// 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 @@ -12,6 +12,8 @@ #ifndef hifi_render_Task_h #define hifi_render_Task_h +#include // QObject + #include "Context.h" #include "gpu/Batch.h" @@ -48,180 +50,296 @@ protected: std::shared_ptr _concept; }; -// FIXME: In c++17, use default classes of nullptr_t to combine these -template void jobRun(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - jobModel.run(sceneContext, renderContext); -} -template void jobRunI(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input) { - jobModel.run(sceneContext, renderContext, input); -} -template void jobRunO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, O& output) { - jobModel.run(sceneContext, renderContext, output); -} -template void jobRunIO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { - jobModel.run(sceneContext, renderContext, input, output); -} +class Job; +class Task; -// The base class for a task that runs on the SceneContext -class Task { +// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled) +class JobConfig : public QObject { + Q_OBJECT public: - // The guts of a task; tasks are composed of multiple Jobs that execute serially - class Job { + JobConfig() = default; + JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} + + bool isEnabled() { return alwaysEnabled || enabled; } + + bool alwaysEnabled{ true }; + bool enabled{ true }; +}; + +class TaskConfig : public JobConfig { + Q_OBJECT +public: + TaskConfig() = default ; + TaskConfig(bool enabled) : JobConfig(enabled) {} + + void init(Task* task) { _task = task; } + + template typename T::Config* getConfig(std::string job = "") const { + QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string + return findChild(name); + } + + template void setJobEnabled(bool enable = true, std::string job = "") { + assert(getConfig(job)->alwaysEnabled != true); + getConfig(job)->enabled = enable; + refresh(); // trigger a Job->configure + } + template bool isJobEnabled(bool enable = true, std::string job = "") const { + return getConfig(job)->isEnabled(); + } + +public slots: + void refresh(); + +private: + Task* _task; +}; + +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 jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + data.run(sceneContext, renderContext); +} +template void jobRunI(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input) { + data.run(sceneContext, renderContext, input); +} +template void jobRunO(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, O& output) { + data.run(sceneContext, renderContext, output); +} +template void jobRunIO(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { + data.run(sceneContext, renderContext, input, output); +} + +class Job { +public: + using Config = JobConfig; + using QConfigPointer = std::shared_ptr; + + // The guts of a job + class Concept { public: - friend class Task; + Concept(QConfigPointer config) : _config(config) {} + virtual ~Concept() = default; - // The guts of a job; jobs are composed of a concept - class Concept { - public: - Concept() = default; - virtual ~Concept() = default; + virtual const Varying getInput() const { return Varying(); } + virtual const Varying getOutput() const { return Varying(); } - bool isEnabled() const { return _isEnabled; } - void setEnabled(bool isEnabled) { _isEnabled = isEnabled; } + virtual QConfigPointer& getConfiguration() { return _config; } + virtual void applyConfiguration() = 0; - virtual const Varying getInput() const { return Varying(); } - virtual const Varying getOutput() const { return Varying(); } - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; - protected: + protected: + QConfigPointer _config; + }; + using ConceptPointer = std::shared_ptr; - bool _isEnabled = true; - }; - using ConceptPointer = std::shared_ptr; + template class Model : public Concept { + public: + using Data = T; + Data _data; - template class Model : public Concept { - public: - typedef T Data; - - Data _data; - - Model() {} - Model(Data data): _data(data) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRun(_data, sceneContext, renderContext); - } - } - }; - - template class ModelI : public Concept { - public: - typedef T Data; - typedef I Input; - - Data _data; - Varying _input; - - const Varying getInput() const { return _input; } - - ModelI(const Varying& input, Data data = Data()) : _data(data), _input(input) {} - ModelI(Data data) : _data(data) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunI(_data, sceneContext, renderContext, _input.get()); - } - } - }; - - template class ModelO : public Concept { - public: - typedef T Data; - typedef O Output; - - Data _data; - Varying _output; - - const Varying getOutput() const { return _output; } - - ModelO(Data data) : _data(data), _output(Output()) {} - ModelO() : _output(Output()) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunO(_data, sceneContext, renderContext, _output.edit()); - } - } - }; - - template class ModelIO : public Concept { - public: - typedef T Data; - typedef I Input; - typedef O Output; - - Data _data; - Varying _input; - Varying _output; - - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } - - ModelIO(const Varying& input, Data data = Data()) : _data(data), _input(input), _output(Output()) {} - ModelIO(Data data) : _data(data), _output(Output()) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); - } - } - }; - - Job(ConceptPointer concept) : _concept(concept) {} - Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} - - bool isEnabled() const { return _concept->isEnabled(); } - void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); } - - const Varying getInput() const { return _concept->getInput(); } - const Varying getOutput() const { return _concept->getOutput(); } - - template T& edit() { - auto concept = std::static_pointer_cast(_concept); - assert(concept); - return concept->_data; + Model(Data data = Data()) : Concept(std::make_shared()), _data(data) { + applyConfiguration(); } - template const T& get() const { - auto concept = std::static_pointer_cast(_concept); - assert(concept); - return concept->_data; + + void applyConfiguration() { + jobConfigure(_data, *std::static_pointer_cast(_config)); } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - PerformanceTimer perfTimer(_name.c_str()); - PROFILE_RANGE(_name.c_str()); - - _concept->run(sceneContext, renderContext); + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { + jobRun(_data, sceneContext, renderContext); + } + renderContext->jobConfig.reset(); } - protected: - ConceptPointer _concept; - std::string _name = ""; - }; + + template class ModelI : public Concept { + public: + using Data = T; + using Input = I; + + Data _data; + Varying _input; + + const Varying getInput() const { return _input; } + + ModelI(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input) { + applyConfiguration(); + } + + void applyConfiguration() { + jobConfigure(_data, *std::static_pointer_cast(_config)); + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { + jobRunI(_data, sceneContext, renderContext, _input.get()); + } + renderContext->jobConfig.reset(); + } + }; + + template class ModelO : public Concept { + public: + using Data = T; + using Output = O; + + Data _data; + Varying _output; + + const Varying getOutput() const { return _output; } + + ModelO(Data data = Data()) : Concept(std::make_shared()), _data(data), _output(Output()) { + applyConfiguration(); + } + + void applyConfiguration() { + jobConfigure(_data, *std::static_pointer_cast(_config)); + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { + jobRunO(_data, sceneContext, renderContext, _output.edit()); + } + renderContext->jobConfig.reset(); + } + }; + + template class ModelIO : public Concept { + public: + using Data = T; + using Input = I; + using Output = O; + + Data _data; + Varying _input; + Varying _output; + + const Varying getInput() const { return _input; } + const Varying getOutput() const { return _output; } + + ModelIO(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input), _output(Output()) { + applyConfiguration(); + } + + void applyConfiguration() { + jobConfigure(_data, *std::static_pointer_cast(_config)); + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { + jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); + } + renderContext->jobConfig.reset(); + } + }; + + 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; + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer(_name.c_str()); + PROFILE_RANGE(_name.c_str()); + + _concept->run(sceneContext, renderContext); + } + + protected: + ConceptPointer _concept; + std::string _name = ""; +}; + +// A task is a specialized job to run a collection of other jobs +// It is defined with JobModel = Task::Model +// +// A task with a custom config *must* use the templated constructor +class Task { +public: + using Config = TaskConfig; + using QConfigPointer = Job::QConfigPointer; + + template class Model : public Job::Concept { + public: + using Data = T; + + Data _data; + + Model(Data data = Data()) : Concept(std::make_shared()), _data(data) { + _config = _data._config; // use the data's config + std::static_pointer_cast(_config)->init(&_data); + + applyConfiguration(); + } + + void applyConfiguration() { + jobConfigure(_data, *std::static_pointer_cast(_config)); + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { + jobRun(_data, sceneContext, renderContext); + } + renderContext->jobConfig.reset(); + } + }; + using Jobs = std::vector; -public: - Task() = default; - virtual ~Task() = default; + // A task must use its Config for construction + Task() : _config{ std::make_shared() } {} + template Task(std::shared_ptr config) : _config{ config } {} - // Queue a new job to the task; returns the job's index + // Queue a new job to the container; returns the job's output template const Varying addJob(std::string name, A&&... args) { _jobs.emplace_back(name, std::make_shared(std::forward(args)...)); + QConfigPointer config = _jobs.back().getConfiguration(); + config->setParent(_config.get()); + config->setObjectName(name.c_str()); + QObject::connect(config.get(), SIGNAL(dirty()), _config.get(), SLOT(refresh())); return _jobs.back().getOutput(); } - void enableJob(size_t jobIndex, bool enable = true) { _jobs.at(jobIndex).setEnabled(enable); } - bool getEnableJob(size_t jobIndex) const { return _jobs.at(jobIndex).isEnabled(); } + std::shared_ptr getConfiguration() { + auto config = std::static_pointer_cast(_config); + // If we are here, we were not made by a Model, so we must initialize our own config + config->init(this); + return config; + } - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {} + void configure(const QObject& configuration) { + for (auto& job : _jobs) { + job.applyConfiguration(); + } + } protected: + template friend class Model; + + QConfigPointer _config; Jobs _jobs; }; -typedef std::shared_ptr TaskPointer; -typedef std::vector Tasks; }