Merge pull request #13896 from AndrewMeadows/workload-feedback

Filter timings used in feedback logic for workload region sizes
This commit is contained in:
Sam Gateau 2018-08-30 19:11:49 -07:00 committed by GitHub
commit a7fd35747d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 30 deletions

View file

@ -5820,15 +5820,13 @@ void Application::update(float deltaTime) {
auto t5 = std::chrono::high_resolution_clock::now(); auto t5 = std::chrono::high_resolution_clock::now();
workload::Timings timings(6); workload::Timings timings(6);
timings[0] = (t4 - t0); timings[0] = t1 - t0; // prePhysics entities
timings[1] = (t5 - t4); timings[1] = t2 - t1; // prePhysics avatars
timings[2] = (t4 - t3); timings[2] = t3 - t2; // stepPhysics
timings[3] = (t3 - t2); timings[3] = t4 - t3; // postPhysics
timings[4] = (t2 - t1); timings[4] = t5 - t4; // non-physical kinematics
timings[5] = (t1 - t0); timings[5] = workload::Timing_ns((int32_t)(NSECS_PER_SECOND * deltaTime)); // game loop duration
_gameWorkload.updateSimulationTimings(timings); _gameWorkload.updateSimulationTimings(timings);
} }
} }
} else { } else {

View file

@ -36,6 +36,7 @@ const float METERS_PER_MILLIMETER = 0.001f;
const float MILLIMETERS_PER_METER = 1000.0f; const float MILLIMETERS_PER_METER = 1000.0f;
const quint64 NSECS_PER_USEC = 1000; const quint64 NSECS_PER_USEC = 1000;
const quint64 NSECS_PER_MSEC = 1000000; const quint64 NSECS_PER_MSEC = 1000000;
const quint64 NSECS_PER_SECOND = 1e9;
const quint64 USECS_PER_MSEC = 1000; const quint64 USECS_PER_MSEC = 1000;
const quint64 MSECS_PER_SECOND = 1000; const quint64 MSECS_PER_SECOND = 1000;
const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;

View file

@ -70,7 +70,8 @@ private:
using SpacePointer = std::shared_ptr<Space>; using SpacePointer = std::shared_ptr<Space>;
using Changes = std::vector<Space::Change>; using Changes = std::vector<Space::Change>;
using IndexVectors = std::vector<IndexVector>; using IndexVectors = std::vector<IndexVector>;
using Timings = std::vector<std::chrono::nanoseconds>; using Timing_ns = std::chrono::nanoseconds;
using Timings = std::vector<Timing_ns>;
} // namespace workload } // namespace workload

View file

@ -102,9 +102,16 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const
// Export the ranges and timings for debuging // Export the ranges and timings for debuging
if (inTimings.size()) { if (inTimings.size()) {
_dataExport.timings[workload::Region::R1] = std::chrono::duration<float, std::milli>(inTimings[0]).count(); // NOTE for reference:
// inTimings[0] = prePhysics entities
// inTimings[1] = prePhysics avatars
// inTimings[2] = stepPhysics
// inTimings[3] = postPhysics
// inTimings[4] = non-physical kinematics
// inTimings[5] = game loop
_dataExport.timings[workload::Region::R1] = std::chrono::duration<float, std::milli>(inTimings[2] + inTimings[3]).count();
_dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1]; _dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1];
_dataExport.timings[workload::Region::R3] = std::chrono::duration<float, std::milli>(inTimings[1]).count(); _dataExport.timings[workload::Region::R3] = std::chrono::duration<float, std::milli>(inTimings[4]).count();
doExport = true; doExport = true;
} }
@ -115,11 +122,29 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const
} }
} }
glm::vec2 Regulator::run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current) { glm::vec2 Regulator::run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack) {
// Regulate next value based on current moving toward the goal budget // measure signal: average and noise
float error_ms = std::chrono::duration<float, std::milli>(_budget - measured).count(); const float FILTER_TIMESCALE = 0.5f * (float)NSECS_PER_SECOND;
float coef = glm::clamp(error_ms / std::chrono::duration<float, std::milli>(regulationDuration).count(), -1.0f, 1.0f); float del = deltaTime.count() / FILTER_TIMESCALE;
return current * (1.0f + coef * (error_ms < 0.0f ? _relativeStepDown : _relativeStepUp)); if (del > 1.0f) {
del = 1.0f; // clamp for stability
}
_measuredTimeAverage = (1.0f - del) * _measuredTimeAverage + del * measuredTime.count();
float diff = measuredTime.count() - _measuredTimeAverage;
_measuredTimeNoiseSquared = (1.0f - del) * _measuredTimeNoiseSquared + del * diff * diff;
float noise = sqrtf(_measuredTimeNoiseSquared);
// check budget
float offsetFromTarget = _budget.count() - _measuredTimeAverage;
if (fabsf(offsetFromTarget) < noise) {
// budget is within the noise --> do nothing
return currentFrontBack;
}
// compute response
glm::vec2 stepDelta = offsetFromTarget < 0.0f ? -_relativeStepDown : _relativeStepUp;
stepDelta *= glm::min(1.0f, (fabsf(offsetFromTarget) - noise) / noise); // ease out of "do nothing"
return currentFrontBack * (1.0f + stepDelta);
} }
glm::vec2 Regulator::clamp(const glm::vec2& backFront) const { glm::vec2 Regulator::clamp(const glm::vec2& backFront) const {
@ -128,17 +153,24 @@ glm::vec2 Regulator::clamp(const glm::vec2& backFront) const {
} }
void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) { void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) {
for (auto& outView : outViews) { for (auto& outView : outViews) {
for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) { for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) {
outView.regionBackFronts[r] = regionBackFronts[r]; outView.regionBackFronts[r] = regionBackFronts[r];
} }
} }
auto loopDuration = std::chrono::nanoseconds{ std::chrono::milliseconds(16) }; // Note: for reference:
regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[0], regionBackFronts[workload::Region::R1]); // timings[0] = prePhysics entities
regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[0], regionBackFronts[workload::Region::R2]); // timings[1] = prePhysics avatars
regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]); // timings[2] = stepPhysics
// timings[3] = postPhysics
// timings[4] = non-physical kinematics
// timings[5] = game loop
auto loopDuration = timings[5];
regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R1]);
regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R2]);
regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[4], regionBackFronts[workload::Region::R3]);
enforceRegionContainment(); enforceRegionContainment();
for (auto& outView : outViews) { for (auto& outView : outViews) {

View file

@ -37,8 +37,8 @@ namespace workload {
{ 250.0f, 16000.0f } { 250.0f, 16000.0f }
}; };
const float RELATIVE_STEP_DOWN = 0.05f; const float RELATIVE_STEP_DOWN = 0.11f;
const float RELATIVE_STEP_UP = 0.04f; const float RELATIVE_STEP_UP = 0.09f;
class SetupViewsConfig : public Job::Config{ class SetupViewsConfig : public Job::Config{
Q_OBJECT Q_OBJECT
@ -212,20 +212,31 @@ namespace workload {
}; };
struct Regulator { struct Regulator {
using Timing_ns = std::chrono::nanoseconds;
Timing_ns _budget{ std::chrono::milliseconds(2) };
glm::vec2 _minRange{ MIN_VIEW_BACK_FRONTS[0] }; glm::vec2 _minRange{ MIN_VIEW_BACK_FRONTS[0] };
glm::vec2 _maxRange{ MAX_VIEW_BACK_FRONTS[0] }; glm::vec2 _maxRange{ MAX_VIEW_BACK_FRONTS[0] };
glm::vec2 _relativeStepDown{ RELATIVE_STEP_DOWN }; glm::vec2 _relativeStepDown{ RELATIVE_STEP_DOWN };
glm::vec2 _relativeStepUp{ RELATIVE_STEP_UP }; glm::vec2 _relativeStepUp{ RELATIVE_STEP_UP };
Timing_ns _budget{ std::chrono::milliseconds(2) };
float _measuredTimeAverage { 0.0f };
float _measuredTimeNoiseSquared { 0.0f };
Regulator() {} Regulator() {}
Regulator(const Timing_ns& budget_ns, const glm::vec2& minRange, const glm::vec2& maxRange, const glm::vec2& relativeStepDown, const glm::vec2& relativeStepUp) : Regulator(const Timing_ns& budget_ns,
_budget(budget_ns), _minRange(minRange), _maxRange(maxRange), _relativeStepDown(relativeStepDown), _relativeStepUp(relativeStepUp) {} const glm::vec2& minRange,
const glm::vec2& maxRange,
const glm::vec2& relativeStepDown,
const glm::vec2& relativeStepUp) :
_minRange(minRange),
_maxRange(maxRange),
_relativeStepDown(relativeStepDown),
_relativeStepUp(relativeStepUp),
_budget(budget_ns),
_measuredTimeAverage(budget_ns.count()),
_measuredTimeNoiseSquared(0.0f)
{}
glm::vec2 run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current); void setBudget(const Timing_ns& budget) { _budget = budget; }
glm::vec2 run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack);
glm::vec2 clamp(const glm::vec2& backFront) const; glm::vec2 clamp(const glm::vec2& backFront) const;
}; };