1#include "Rendering/IContext.h"
2#include "Rendering/IGraphicsDevice.h"
3#include "Rendering/Statistics.h"
5#include "Foundation/Logging/Logger.h"
8#include "Services/Variables.h"
10#include "QualityService.h"
22 const Cogs::StringView qualityFrameTimeAlphaName =
"quality.frameTime.alpha";
23 const Cogs::StringView qualityFrameTimeTargetName =
"quality.frameTimeTarget";
26 const Setting qualityScalingSpeed = {
"quality.scaling.speed", 5e-2f };
28 const Setting qualityAssetSystemToleranceScaleCoarse = {
"quality.assetSystem.toleranceScale.coarse", 10.f };
29 const Setting qualityAssetSystemToleranceScaleDetailed = {
"quality.assetSystem.toleranceScale.detailed", 0.25f };
31 const Setting qualityPotreeSystemToleranceScaleCoarse = {
"quality.potreeSystem.toleranceScale.coarse", 3.f };
32 const Setting qualityPotreeSystemToleranceScaleDetailed = {
"quality.potreeSystem.toleranceScale.detailed", 0.25f };
33 const Setting qualityPotreeSystemChunkCountScaleCoarse = {
"quality.potreeSystem.chunkCountScale.coarse", 1e-3f };
34 const Setting qualityPotreeSystemChunkCountScaleDetailed = {
"quality.potreeSystem.chunkCountScale.detailed", 2.f };
36 const Setting qualityOGC3DTilesSystemToleranceScaleCoarse = {
"quality.ogc3DTilesSystem.toleranceScale.coarse", 3.f };
37 const Setting qualityOGC3DTilesSystemToleranceScaleDetailed = {
"quality.ogc3DTilesSystem.toleranceScale.detailed", 0.25f };
38 const Setting qualityOGC3DTilesSystemCacheControlCoarse = {
"quality.ogc3DTilesSystem.cacheControl.coarse", 1e-3f };
39 const Setting qualityOGC3DTilesSystemCacheControlDetailed = {
"quality.ogc3DTilesSystem.cacheControl.detailed", 2.f };
41 float mapDecreasing(
Context* context,
const Setting& coarseSetting,
const Setting& detailedSetting,
float quality)
44 if (coarseVar->isEmpty()) {
45 coarseVar->setFloat(coarseSetting.defaultValue);
49 if (detailedVar->isEmpty()) {
50 detailedVar->setFloat(detailedSetting.defaultValue);
53 float coarse = std::max(1.f, coarseVar->getFloat());
54 float detailed = std::min(1.f, std::max(detailedVar->getFloat(), std::numeric_limits<float>::min()));
58 float a = coarse - 1.f;
59 float b = 1.f - detailed;
60 float c = glm::min(0.5f * (a + b), glm::min(a, b));
65 const float x = quality;
66 rv = coarse * (1.f - x) * (1.f - x) + 2.f * (1.f + c) * (1.f - x) * x + x * x;
69 const float x = quality - 1.f;
70 rv = (1.f - x) * (1.f - x) + 2.f * (1.f - c) * (1.f - x) * x + detailed * x * x;
72 return std::isfinite(rv) ? rv : 1.f;
75 float mapIncreasing(
Context* context,
const Setting& coarseSetting,
const Setting& detailedSetting,
float quality)
78 if (coarseVar->isEmpty()) {
79 coarseVar->setFloat(coarseSetting.defaultValue);
83 if (detailedVar->isEmpty()) {
84 detailedVar->setFloat(detailedSetting.defaultValue);
87 float coarse = std::min(1.f, std::max(coarseVar->getFloat(), std::numeric_limits<float>::min()));
88 float detailed = std::max(1.f, detailedVar->getFloat());
92 float a = coarse - 1.f;
93 float b = 1.f - detailed;
94 float c = glm::max(0.5f * (a + b), glm::min(a, b));
99 const float x = quality;
100 rv = coarse * (1.f - x) * (1.f - x) + 2.f * (1.f + c) * (1.f - x) * x + x * x;
103 const float x = quality - 1.f;
104 rv = (1.f - x) * (1.f - x) + 2.f * (1.f - c) * (1.f - x) * x + detailed * x * x;
106 return std::isfinite(rv) ? rv : 1.f;
110Cogs::Core::QualityService::QualityService(
class Context* context)
114 for(
size_t i=0; i<(size_t)MetricType::MetricTypeCount; i++){
115 for(
size_t j=0; j<TIMES_COUNT; j++){
121void Cogs::Core::QualityService::beginFrame()
124 end(MetricType::Idle);
125 times_idx = (times_idx+1)%TIMES_COUNT;
130 for(
size_t i=0; i<(size_t)MetricType::MetricTypeCount; i++){
133 for(
size_t i=0; i<(size_t)MetricType::MetricTypeCount; i++){
134 times[times_idx][i] = 0.0f;
139 bufferUploadSize[times_idx] = upload_stats.bufferUploadSize;
140 textureUploadSize[times_idx] = upload_stats.textureUploadSize;
143 assert(stack_idx == 0);
144 begin(MetricType::Frame);
146 if (
Variable* var = context->
variables->get(qualityVariableName); !var->isEmpty()) {
147 qualitySetting = var->getFloat(qualitySetting);
148 if (qualitySetting < 0.0f) { qualitySetting = 0.f; }
149 if (200.f < qualitySetting || !std::isfinite(qualitySetting)) { qualitySetting = 200.f; }
150 var->setFloat(qualitySetting);
153 context->
variables->set(qualityVariableName, qualitySetting);
156 if (
Variable* var = context->
variables->get(qualityFrameTimeTargetName); !var->isEmpty()) {
157 frameTimeTarget = std::max(0.f, var->getFloat());
160 context->
variables->set(qualityFrameTimeTargetName, frameTimeTarget);
163 if (
Variable* var = context->
variables->get(GPUMemTargetMBName); !var->isEmpty()) {
164 gpuMemTargetMB =
static_cast<uint32_t
>(std::max(0, var->getInt()));
167 context->
variables->set(GPUMemTargetMBName,
static_cast<int>(gpuMemTargetMB));
170 float maxQuality = 1e-2f * qualitySetting;
171 if ((0.f < frameTimeTarget) || (0 < gpuMemTargetMB)) {
172 float speed = qualityScalingSpeed.defaultValue;
173 if (
Variable* var = context->
variables->get(qualityScalingSpeed.name); !var->isEmpty()) {
174 speed = std::max(std::numeric_limits<float>::min(), var->getFloat());
177 context->
variables->set(qualityScalingSpeed.name, speed);
180 speed = speed * 1e-3f * avgFrameTime;
182 currentQuality = glm::clamp(currentQuality - 10.f * speed, 0.f, maxQuality);
183 resetFrameTime =
true;
185 else if (throttleDown) {
186 currentQuality = glm::clamp(currentQuality - speed, 0.f, maxQuality);
189 currentQuality = std::min(currentQuality + speed, maxQuality);
192 currentQuality = std::min(currentQuality, maxQuality);
196 currentQuality = maxQuality;
200 Variable* coarse = context->
variables->get(qualityAssetSystemToleranceScaleCoarse.name);
201 if (coarse->isEmpty()) {
202 coarse->setFloat(qualityAssetSystemToleranceScaleCoarse.defaultValue);
205 Variable* detailed = context->
variables->get(qualityAssetSystemToleranceScaleDetailed.name);
206 if (detailed->isEmpty()) {
207 detailed->setFloat(qualityAssetSystemToleranceScaleDetailed.defaultValue);
211 assetSystemToleranceScale = mapDecreasing(context,
212 qualityAssetSystemToleranceScaleCoarse,
213 qualityAssetSystemToleranceScaleDetailed,
216 potreeSystemToleranceScale = mapDecreasing(context,
217 qualityPotreeSystemToleranceScaleCoarse,
218 qualityPotreeSystemToleranceScaleDetailed,
221 potreeSystemChunkCountScale = mapIncreasing(context,
222 qualityPotreeSystemChunkCountScaleCoarse,
223 qualityPotreeSystemChunkCountScaleDetailed,
226 ogc3DTilesSystemToleranceScale = mapDecreasing(context,
227 qualityOGC3DTilesSystemToleranceScaleCoarse,
228 qualityOGC3DTilesSystemToleranceScaleDetailed,
231 ogc3DTilesSystemCacheControl = mapIncreasing(context,
232 qualityOGC3DTilesSystemCacheControlCoarse,
233 qualityOGC3DTilesSystemCacheControlDetailed,
235 rapidBackoff =
false;
236 throttleDown =
false;
240void Cogs::Core::QualityService::endFrame()
242 end(MetricType::Frame);
244 float frameTime = times[times_idx][(size_t)MetricType::Frame];
245 if (avgFrameTime == 0.f || resetFrameTime) {
246 resetFrameTime =
false;
247 avgFrameTime = frameTime;
251 if (
Variable* var = context->
variables->get(qualityFrameTimeAlphaName); !var->isEmpty()) {
252 alpha = glm::clamp(var->getFloat(), std::numeric_limits<float>::min(), 1.f);
255 context->
variables->set(qualityFrameTimeAlphaName, alpha);
257 avgFrameTime = (1.f - alpha) * avgFrameTime + alpha * frameTime;
260 bool hasSlack =
true;
261 bool hasLimits =
false;
262 if (0 < gpuMemTargetMB) {
265 float gpuMemTarget = float(gpuMemTargetMB);
266 float gpuMemValue = float(resourceStats.memoryConsumption()) / float(1024 * 1024);
268 if (1.5f * gpuMemTarget < gpuMemValue) {
269 requestQualityChange(QualityRequest::RapidBackoff);
273 if (gpuMemTarget < gpuMemValue) {
274 requestQualityChange(QualityRequest::ThrottleDown);
278 if (0.8f * gpuMemTarget < gpuMemValue) {
284 if (0.f < frameTimeTarget) {
285 if (1.5f * frameTimeTarget < avgFrameTime) {
286 requestQualityChange(QualityRequest::RapidBackoff);
290 if (frameTimeTarget < avgFrameTime) {
291 requestQualityChange(QualityRequest::ThrottleDown);
295 if (0.8f * frameTimeTarget < avgFrameTime) {
301 if (hasLimits && !hasSlack) {
302 requestQualityChange(QualityRequest::AtTarget);
306 requestQualityChange(QualityRequest::HasSlack);
308 begin(MetricType::Idle);
311void Cogs::Core::QualityService::requestQualityChange(QualityRequest request)
314 case QualityRequest::AtTarget:
317 case QualityRequest::HasSlack:
319 case QualityRequest::ThrottleDown:
322 case QualityRequest::RapidBackoff:
326 assert(
false &&
"Illegal enum value");
331void QualityService::begin(MetricType metric)
337void QualityService::end(MetricType metric)
339 times[times_idx][(size_t)metric] += (
float)(perfTimeToSeconds(
Cogs::perfTime()-time[(
size_t)metric]));
345 size_t idx = (times_idx+TIMES_COUNT-1)%TIMES_COUNT;
346 float frameTime = times[idx][(size_t)MetricType::Frame];
347 float idleTime = times[idx][(size_t)MetricType::Idle];
348 float presentTime = times[idx][(size_t)MetricType::Present];
350 .frameTime = frameTime + idleTime,
351 .cogsTime = frameTime,
352 .cpuTime = frameTime - presentTime,
353 .presentTime = presentTime,
358float QualityService::getMetric(MetricType metric)
360 size_t idx = (times_idx+TIMES_COUNT-1)%TIMES_COUNT;
361 return times[idx][(size_t)metric];
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Variables > variables
Variables service instance.
FrameTimings getFrameTimings() const
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
COGSFOUNDATION_API TimePerf perfTime()
High resolution performance timer. Returns an implementation defined absolute timestamp,...
Runtime control variable.