Gut RenderContext

This commit is contained in:
Zach Pomerantz 2016-01-22 17:47:13 -08:00
parent e8b8f4d535
commit f3265db298
19 changed files with 117 additions and 142 deletions

View file

@ -3768,22 +3768,12 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
PerformanceTimer perfTimer("EngineRun");
renderArgs->_viewFrustum = getDisplayViewFrustum();
auto renderContext = _renderEngine->getRenderContext();
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);
_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();
//zzmp renderInterface->setJobGPUTimes(engineContext->getAmbientOcclusion().gpuTime);
}
activeRenderingThread = nullptr;

View file

@ -21,6 +21,7 @@
#include <gpu/StandardShaderLib.h>
#include "RenderUtilsLogging.h"
#include "DeferredLightingEffect.h"
#include "AmbientOcclusionEffect.h"
#include "TextureCache.h"
#include "FramebufferCache.h"
@ -139,6 +140,8 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() {
}
void AmbientOcclusionEffect::configure(const Config& configuration) {
DependencyManager::get<DeferredLightingEffect>()->setAmbientOcclusionEnabled(configuration.enabled);
bool shouldUpdateGaussion = false;
const double RADIUS_POWER = 6.0;
@ -318,10 +321,10 @@ void AmbientOcclusionEffect::updateGaussianDistribution() {
}
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<FramebufferCache>();
auto depthBuffer = framebufferCache->getPrimaryDepthTexture();

View file

@ -21,6 +21,7 @@ class AmbientOcclusionEffectConfig : public render::Job::Config {
public:
AmbientOcclusionEffectConfig() : render::Job::Config(false) {}
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty)
Q_PROPERTY(float radius MEMBER radius WRITE setRadius NOTIFY dirty)
Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel NOTIFY dirty)
Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias NOTIFY dirty)

View file

@ -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);

View file

@ -16,13 +16,20 @@
#include "render/DrawTask.h"
class AntiAliasingConfig : public render::Job::Config {
Q_OBJECT
public:
AntiAliasingConfig() : render::Job::Config(false) {}
};
class Antialiasing {
public:
using Config = AntiAliasingConfig;
using JobModel = render::Job::Model<Antialiasing, Config>;
Antialiasing();
void configure(const Config& configuration) {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
using JobModel = render::Job::Model<Antialiasing>;
const gpu::PipelinePointer& getAntialiasingPipeline();
const gpu::PipelinePointer& getBlendPipeline();

View file

@ -231,9 +231,9 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str
void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const 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) {
const auto geometryBuffer = DependencyManager::get<GeometryCache>();

View file

@ -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());

View file

@ -55,12 +55,15 @@ public:
const LightStage& getLightStage() { return _lightStage; }
void setShadowMapStatus(bool enable) { _shadowMapStatus = enable; };
void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; }
private:
DeferredLightingEffect() = default;
LightStage _lightStage;
bool _shadowMapStatus{ false };
DeferredLightingEffect() = default;
bool _ambientOcclusionEnabled{ false };
model::MeshPointer _spotLightMesh;
model::MeshPointer getSpotLightMesh();

View file

@ -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;

View file

@ -60,7 +60,7 @@ using namespace render;
void initDeferredPipelines(render::ShapePlumber& plumber);
void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
DependencyManager::get<DeferredLightingEffect>()->prepare(renderContext->getArgs());
DependencyManager::get<DeferredLightingEffect>()->prepare(renderContext->args);
}
void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
@ -78,7 +78,7 @@ void ToneMappingDeferred::configure(const Config& configuration) {
}
void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
_toneMappingEffect.render(renderContext->getArgs());
_toneMappingEffect.render(renderContext->args);
}
RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
@ -167,17 +167,14 @@ 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;
}
setAntialiasingStatus(renderContext->getFxaaStatus());
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor
// TODO: For now, lighting is controlled through a singleton, so it is distinct
DependencyManager::get<DeferredLightingEffect>()->setShadowMapStatus(renderContext->getShadowMapStatus());
renderContext->getArgs()->_context->syncCache();
renderContext->args->_context->syncCache();
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
@ -185,12 +182,12 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
};
void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& config = std::static_pointer_cast<Config>(renderContext->jobConfig);
RenderArgs* args = renderContext->getArgs();
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
@ -230,8 +227,8 @@ 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;
@ -251,7 +248,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
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
@ -309,11 +306,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;
@ -335,8 +332,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;
@ -348,7 +345,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;
@ -375,10 +372,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) {

View file

@ -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<DeferredLightingEffect>()->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;
@ -121,7 +121,7 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) {
void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
assert(sceneContext);
RenderArgs* args = renderContext->getArgs();
RenderArgs* args = renderContext->args;
// This feature is in a debugging stage - it must be turned on explicitly
if (!renderContext->getShadowMapStatus()) {

View file

@ -1,30 +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(int drawStatus)
: _args{ nullptr },
_drawStatus{ drawStatus } {
}
void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap) {
_occlusionStatus = occlusion;
_fxaaStatus = fxaa;
_shadowMapStatus = shadowMap;
if (showOwned) {
_drawStatus |= render::showNetworkStatusFlag;
}
};

View file

@ -24,33 +24,13 @@ public:
};
using SceneContextPointer = std::shared_ptr<SceneContext>;
// see examples/utilities/tools/renderEngineDebug.js
const int showDisplayStatusFlag = 1;
const int showNetworkStatusFlag = 2;
class JobConfig;
class RenderContext {
public:
RenderContext(int drawStatus);
RenderContext() {};
void setArgs(RenderArgs* args) { _args = args; }
RenderArgs* getArgs() { return _args; }
int getDrawStatus() { return _drawStatus; }
bool getOcclusionStatus() { return _occlusionStatus; }
bool getFxaaStatus() { return _fxaaStatus; }
bool getShadowMapStatus() { return _shadowMapStatus; }
void setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap);
RenderArgs* args;
std::shared_ptr<JobConfig> jobConfig{ nullptr };
protected:
RenderArgs* _args;
// Options
int _drawStatus; // bitflag
bool _occlusionStatus { false };
bool _fxaaStatus { false };
// TODO: gut this
bool _shadowMapStatus { false };
};
typedef std::shared_ptr<RenderContext> RenderContextPointer;

View file

@ -94,12 +94,17 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const {
return _statusIconMap;
}
void DrawStatus::configure(const Config& configuration) {
_showDisplay = configuration.showDisplay;
_showNetwork = configuration.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 +184,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 +197,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);

View file

@ -16,7 +16,38 @@
#include "gpu/Batch.h"
namespace render {
class DrawStatusConfig : public Job::Config {
Q_OBJECT
public:
Q_PROPERTY(bool showDisplay MEMBER showDisplay NOTIFY dirty)
Q_PROPERTY(bool showNetwork MEMBER showDisplay NOTIFY dirty)
bool showDisplay{ false };
bool showNetwork{ false };
signals:
void dirty();
};
class DrawStatus {
public:
using Config = DrawStatusConfig;
using JobModel = Job::ModelI<DrawStatus, ItemIDsBounds, Config>;
DrawStatus() {}
DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); }
void configure(const Config& configuration);
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{ false };
bool _showNetwork{ false };
int _drawItemBoundPosLoc = -1;
int _drawItemBoundDimLoc = -1;
int _drawItemStatusPosLoc = -1;
@ -30,21 +61,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 = Job::ModelI<DrawStatus, ItemIDsBounds>;
const gpu::PipelinePointer getDrawItemBoundsPipeline();
const gpu::PipelinePointer getDrawItemStatusPipeline();
void setStatusIconMap(const gpu::TexturePointer& map);
const gpu::TexturePointer getStatusIconMap() const;
};
}

View file

@ -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) {
@ -185,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;
@ -199,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;

View file

@ -55,7 +55,7 @@ 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());

View file

@ -23,7 +23,7 @@ Engine::Engine() :
void Engine::run() {
// Sync GPU state before beginning to render
_renderContext->getArgs()->_context->syncCache();
_renderContext->args->_context->syncCache();
for (auto job : _jobs) {
job.run(_sceneContext, _renderContext);

View file

@ -60,7 +60,8 @@ public:
JobConfig() : alwaysEnabled{ true }, enabled{ true } {}
JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}
Q_PROPERTY(bool enabled MEMBER enabled)
Q_PROPERTY(bool enabled MEMBER enabled READ isEnabled)
bool isEnabled() { return alwaysEnabled || enabled; }
bool alwaysEnabled{ true };
bool enabled;
};