From 85beca6b6705cf954f0cd3e03870a8b13cfa39b6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 23 May 2018 16:00:27 -0700 Subject: [PATCH] tweak to dynamic workload regions regulation --- interface/src/workload/GameWorkload.cpp | 103 ++++++++++++++++++++ interface/src/workload/GameWorkload.h | 122 ++++++++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/interface/src/workload/GameWorkload.cpp b/interface/src/workload/GameWorkload.cpp index 87bf093224..a7d3a9ab52 100644 --- a/interface/src/workload/GameWorkload.cpp +++ b/interface/src/workload/GameWorkload.cpp @@ -15,6 +15,109 @@ #include "PhysicsBoundary.h" +const glm::vec2 DEFAULT_R1_BACK_FRONT = { 50.0f, 100.0f }; +const glm::vec2 DEFAULT_R2_BACK_FRONT = { 75.0f, 150.0f }; +const glm::vec2 DEFAULT_R3_BACK_FRONT = { 100.0f, 250.0f }; + +ControlViews::ControlViews() { + regionBackFronts[0] = DEFAULT_R1_BACK_FRONT; + regionBackFronts[1] = DEFAULT_R2_BACK_FRONT; + regionBackFronts[2] = DEFAULT_R3_BACK_FRONT; + + const int32_t TIME_BUDGET_MSEC = 2; + const float MIN_BACK_FRONT_SCALE_FACTOR = 0.05f; + const float MAX_BACK_FRONT_SCALE_FACTOR = 2.0f; + const float MIN_NUM_STEPS_DOWN = 250.0f; + const float MIN_NUM_STEPS_UP = 375.0f; + + for (int32_t i = 0; i < workload::Region::NUM_VIEW_REGIONS; ++i) { + glm::vec2 minBackFront = MIN_BACK_FRONT_SCALE_FACTOR * regionBackFronts[i]; + glm::vec2 maxBackFront = MAX_BACK_FRONT_SCALE_FACTOR * regionBackFronts[i]; + glm::vec2 stepDown = (maxBackFront - minBackFront) / MIN_NUM_STEPS_DOWN; + glm::vec2 stepUp = (maxBackFront - minBackFront) / MIN_NUM_STEPS_UP; + regionRegulators[i] = Regulator(std::chrono::milliseconds(TIME_BUDGET_MSEC), minBackFront, maxBackFront, stepDown, stepUp); + } +} + +void ControlViews::configure(const Config& config) { + _data = config.data; +} + +void ControlViews::run(const workload::WorkloadContextPointer& runContext, const Input& inputs, Output& outputs) { + const auto& inViews = inputs.get0(); + const auto& inTimings = inputs.get1(); + auto& outViews = outputs; + outViews.clear(); + outViews = inViews; + + if (_data.regulateViewRanges && inTimings.size()) { + regulateViews(outViews, inTimings); + + auto config = std::static_pointer_cast(runContext->jobConfig); + config->dataExport = _dataExport; + config->emitDirty(); + } +} + +glm::vec2 Regulator::run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current) { + // Regulate next value based on current moving toward the goal budget + float error_ms = std::chrono::duration(_budget - measured).count(); + float coef = error_ms / std::chrono::duration(regulationDuration).count(); + return current + coef * (error_ms < 0.0f ? _stepDown : _stepUp); +} + +glm::vec2 Regulator::clamp(const glm::vec2& backFront) const { + return glm::clamp(backFront, _minRange, _maxRange); +} + +void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) { + + for (auto& outView : outViews) { + for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) { + outView.regionBackFronts[r] = regionBackFronts[r]; + } + } + + auto loopDuration = std::chrono::nanoseconds{ std::chrono::milliseconds(16) }; + regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[0], regionBackFronts[workload::Region::R1]); + regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[0], regionBackFronts[workload::Region::R2]); + regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]); + + enforceRegionContainment(); + + _dataExport.ranges[workload::Region::R1] = regionBackFronts[workload::Region::R1]; + _dataExport.ranges[workload::Region::R2] = regionBackFronts[workload::Region::R2]; + _dataExport.ranges[workload::Region::R3] = regionBackFronts[workload::Region::R3]; + + _dataExport.timings[workload::Region::R1] = std::chrono::duration(timings[0]).count(); + _dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1]; + _dataExport.timings[workload::Region::R3] = std::chrono::duration(timings[1]).count(); + + for (auto& outView : outViews) { + outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1]; + outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2]; + outView.regionBackFronts[workload::Region::R3] = regionBackFronts[workload::Region::R3]; + + workload::View::updateRegionsFromBackFronts(outView); + } +} + +void ControlViews::enforceRegionContainment() { + // inner regions should never overreach outer + // and each region should never exceed its min/max limits + const glm::vec2 MIN_REGION_GAP = { 1.0f, 2.0f }; + // enforce outside --> in + for (int32_t i = workload::Region::NUM_VIEW_REGIONS - 2; i >= 0; --i) { + int32_t j = i + 1; + regionBackFronts[i] = regionRegulators[i].clamp(glm::min(regionBackFronts[i], regionBackFronts[j] - MIN_REGION_GAP)); + } + // enforce inside --> out + for (int32_t i = 1; i < workload::Region::NUM_VIEW_REGIONS; ++i) { + int32_t j = i - 1; + regionBackFronts[i] = regionRegulators[i].clamp(glm::max(regionBackFronts[i], regionBackFronts[j] + MIN_REGION_GAP)); + } +} + class WorkloadEngineBuilder { public: public: diff --git a/interface/src/workload/GameWorkload.h b/interface/src/workload/GameWorkload.h index 1f835686f8..fc25e6681c 100644 --- a/interface/src/workload/GameWorkload.h +++ b/interface/src/workload/GameWorkload.h @@ -43,4 +43,126 @@ public: workload::EnginePointer _engine; }; +class ControlViewsConfig : public workload::Job::Config { + Q_OBJECT + Q_PROPERTY(bool regulateViewRanges READ regulateViewRanges WRITE setRegulateViewRanges NOTIFY dirty) + + Q_PROPERTY(float r1Timing READ r1Timing NOTIFY dirty) + Q_PROPERTY(float r2Timing READ r2Timing NOTIFY dirty) + Q_PROPERTY(float r3Timing READ r3Timing NOTIFY dirty) + + Q_PROPERTY(float r1RangeBack READ r1RangeBack NOTIFY dirty) + Q_PROPERTY(float r2RangeBack READ r2RangeBack NOTIFY dirty) + Q_PROPERTY(float r3RangeBack READ r3RangeBack NOTIFY dirty) + + Q_PROPERTY(float r1RangeFront READ r1RangeFront NOTIFY dirty) + Q_PROPERTY(float r2RangeFront READ r2RangeFront NOTIFY dirty) + Q_PROPERTY(float r3RangeFront READ r3RangeFront NOTIFY dirty) +/* + Q_PROPERTY(float r1MinRangeBack READ r1MinRangeBack WRITE setR1MinRangeBack NOTIFY dirty) + Q_PROPERTY(float r2MinRangeBack READ r2MinRangeBack WRITE setR2MinRangeBack NOTIFY dirty) + Q_PROPERTY(float r3MinRangeBack READ r3MinRangeBack WRITE setR3MinRangeBack NOTIFY dirty) + + Q_PROPERTY(float r1MinRangeFront READ r1MinRangeFront WRITE setR1MinRangeFront NOTIFY dirty) + Q_PROPERTY(float r2MinRangeFront READ r2MinRangeFront WRITE setR2MinRangeFront NOTIFY dirty) + Q_PROPERTY(float r3MinRangeFront READ r3MinRangeFront WRITE setR3MinRangeFront NOTIFY dirty) + + Q_PROPERTY(float r1MaxRangeBack READ r1MaxRangeBack WRITE setR1MaxRangeBack NOTIFY dirty) + Q_PROPERTY(float r2MaxRangeBack READ r2MaxRangeBack WRITE setR2MaxRangeBack NOTIFY dirty) + Q_PROPERTY(float r3MaxRangeBack READ r3MaxRangeBack WRITE setR3MaxRangeBack NOTIFY dirty) + + Q_PROPERTY(float r1MaxRangeFront READ r1MaxRangeFront WRITE setR1MaxRangeFront NOTIFY dirty) + Q_PROPERTY(float r2MaxRangeFront READ r2MaxRangeFront WRITE setR2MaxRangeFront NOTIFY dirty) + Q_PROPERTY(float r3MaxRangeFront READ r3MaxRangeFront WRITE setR3MaxRangeFront NOTIFY dirty) + + Q_PROPERTY(float r1SpeedDownBack READ r1SpeedDownBack WRITE setR1SpeedDownBack NOTIFY dirty) + Q_PROPERTY(float r2SpeedDownBack READ r2SpeedDownBack WRITE setR2SpeedDownBack NOTIFY dirty) + Q_PROPERTY(float r3SpeedDownBack READ r3SpeedDownBack WRITE setR3SpeedDownBack NOTIFY dirty) + + Q_PROPERTY(float r1SpeedDownFront READ r1SpeedDownFront WRITE setR1SpeedDownFront NOTIFY dirty) + Q_PROPERTY(float r2SpeedDownFront READ r2SpeedDownFront WRITE setR2SpeedDownFront NOTIFY dirty) + Q_PROPERTY(float r3SpeedDownFront READ r3SpeedDownFront WRITE setR3SpeedDownFront NOTIFY dirty) + + Q_PROPERTY(float r1SpeedUpBack READ r1SpeedUpBack WRITE setR1SpeedUpBack NOTIFY dirty) + Q_PROPERTY(float r2SpeedUpBack READ r2SpeedUpBack WRITE setR2SpeedUpBack NOTIFY dirty) + Q_PROPERTY(float r3SpeedUpBack READ r3SpeedUpBack WRITE setR3SpeedUpBack NOTIFY dirty) + + Q_PROPERTY(float r1SpeedUpFront READ r1SpeedUpFront WRITE setR1SpeedUpFront NOTIFY dirty) + Q_PROPERTY(float r2SpeedUpFront READ r2SpeedUpFront WRITE setR2SpeedUpFront NOTIFY dirty) + Q_PROPERTY(float r3SpeedUpFront READ r3SpeedUpFront WRITE setR3SpeedUpFront NOTIFY dirty)*/ + +public: + + bool regulateViewRanges() const { return data.regulateViewRanges; } + void setRegulateViewRanges(bool use) { data.regulateViewRanges = use; emit dirty(); } + + float r1Timing() const { return dataExport.timings[workload::Region::R1]; } + float r2Timing() const { return dataExport.timings[workload::Region::R2]; } + float r3Timing() const { return dataExport.timings[workload::Region::R3]; } + + float r1RangeBack() const { return dataExport.ranges[workload::Region::R1].x; } + float r2RangeBack() const { return dataExport.ranges[workload::Region::R2].x; } + float r3RangeBack() const { return dataExport.ranges[workload::Region::R3].x; } + + float r1RangeFront() const { return dataExport.ranges[workload::Region::R1].y; } + float r2RangeFront() const { return dataExport.ranges[workload::Region::R2].y; } + float r3RangeFront() const { return dataExport.ranges[workload::Region::R3].y; } + + struct Data { + bool regulateViewRanges{ true }; + } data; + + struct DataExport { + static const int SIZE{ workload::Region::NUM_VIEW_REGIONS }; + float timings[SIZE]; + glm::vec2 ranges[SIZE]; + } dataExport; + + void emitDirty() { emit dirty(); } +signals: + void dirty(); +}; + +struct Regulator { + using Timing_ns = std::chrono::nanoseconds; + Timing_ns _budget{ std::chrono::milliseconds(2) }; + glm::vec2 _minRange{ 2.0f, 5.0f }; + glm::vec2 _maxRange{ 50.0f, 100.0f }; + + glm::vec2 _stepDown{ 0.2f }; + glm::vec2 _stepUp{ 0.1f }; + + + Regulator() {} + Regulator(const Timing_ns& budget_ns, const glm::vec2& minRange, const glm::vec2& maxRange, const glm::vec2& stepDown, const glm::vec2& speedUp) : + _budget(budget_ns), _minRange(minRange), _maxRange(maxRange), _stepDown(stepDown), _stepUp(speedUp) {} + + glm::vec2 run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current); + glm::vec2 clamp(const glm::vec2& backFront) const; +}; + +class ControlViews { +public: + using Config = ControlViewsConfig; + using Input = workload::VaryingSet2; + using Output = workload::Views; + using JobModel = workload::Job::ModelIO; + + ControlViews(); + + void configure(const Config& config); + void run(const workload::WorkloadContextPointer& runContext, const Input& inputs, Output& outputs); + + std::array regionBackFronts; + std::array regionRegulators; + + void regulateViews(workload::Views& views, const workload::Timings& timings); + +protected: + void enforceRegionContainment(); + + Config::Data _data; + Config::DataExport _dataExport; +}; + #endif // hifi_GameWorkload_h