xref: /aosp_15_r20/external/deqp/modules/glshared/glsCalibration.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1*35238bceSAndroid Build Coastguard Worker /*-------------------------------------------------------------------------
2*35238bceSAndroid Build Coastguard Worker  * drawElements Quality Program OpenGL (ES) Module
3*35238bceSAndroid Build Coastguard Worker  * -----------------------------------------------
4*35238bceSAndroid Build Coastguard Worker  *
5*35238bceSAndroid Build Coastguard Worker  * Copyright 2014 The Android Open Source Project
6*35238bceSAndroid Build Coastguard Worker  *
7*35238bceSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
8*35238bceSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
9*35238bceSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
10*35238bceSAndroid Build Coastguard Worker  *
11*35238bceSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
12*35238bceSAndroid Build Coastguard Worker  *
13*35238bceSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
14*35238bceSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
15*35238bceSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16*35238bceSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
17*35238bceSAndroid Build Coastguard Worker  * limitations under the License.
18*35238bceSAndroid Build Coastguard Worker  *
19*35238bceSAndroid Build Coastguard Worker  *//*!
20*35238bceSAndroid Build Coastguard Worker  * \file
21*35238bceSAndroid Build Coastguard Worker  * \brief Calibration tools.
22*35238bceSAndroid Build Coastguard Worker  *//*--------------------------------------------------------------------*/
23*35238bceSAndroid Build Coastguard Worker 
24*35238bceSAndroid Build Coastguard Worker #include "glsCalibration.hpp"
25*35238bceSAndroid Build Coastguard Worker #include "tcuTestLog.hpp"
26*35238bceSAndroid Build Coastguard Worker #include "tcuVectorUtil.hpp"
27*35238bceSAndroid Build Coastguard Worker #include "deStringUtil.hpp"
28*35238bceSAndroid Build Coastguard Worker #include "deMath.h"
29*35238bceSAndroid Build Coastguard Worker #include "deClock.h"
30*35238bceSAndroid Build Coastguard Worker 
31*35238bceSAndroid Build Coastguard Worker #include <algorithm>
32*35238bceSAndroid Build Coastguard Worker #include <limits>
33*35238bceSAndroid Build Coastguard Worker 
34*35238bceSAndroid Build Coastguard Worker using std::string;
35*35238bceSAndroid Build Coastguard Worker using std::vector;
36*35238bceSAndroid Build Coastguard Worker using tcu::TestLog;
37*35238bceSAndroid Build Coastguard Worker using tcu::TestNode;
38*35238bceSAndroid Build Coastguard Worker using tcu::Vec2;
39*35238bceSAndroid Build Coastguard Worker using namespace glu;
40*35238bceSAndroid Build Coastguard Worker 
41*35238bceSAndroid Build Coastguard Worker namespace deqp
42*35238bceSAndroid Build Coastguard Worker {
43*35238bceSAndroid Build Coastguard Worker namespace gls
44*35238bceSAndroid Build Coastguard Worker {
45*35238bceSAndroid Build Coastguard Worker 
46*35238bceSAndroid Build Coastguard Worker // Reorders input arbitrarily, linear complexity and no allocations
47*35238bceSAndroid Build Coastguard Worker template <typename T>
destructiveMedian(vector<T> & data)48*35238bceSAndroid Build Coastguard Worker float destructiveMedian(vector<T> &data)
49*35238bceSAndroid Build Coastguard Worker {
50*35238bceSAndroid Build Coastguard Worker     const typename vector<T>::iterator mid = data.begin() + data.size() / 2;
51*35238bceSAndroid Build Coastguard Worker 
52*35238bceSAndroid Build Coastguard Worker     std::nth_element(data.begin(), mid, data.end());
53*35238bceSAndroid Build Coastguard Worker 
54*35238bceSAndroid Build Coastguard Worker     if (data.size() % 2 == 0) // Even number of elements, need average of two centermost elements
55*35238bceSAndroid Build Coastguard Worker         return (*mid + *std::max_element(data.begin(), mid)) *
56*35238bceSAndroid Build Coastguard Worker                0.5f; // Data is partially sorted around mid, mid is half an item after center
57*35238bceSAndroid Build Coastguard Worker     else
58*35238bceSAndroid Build Coastguard Worker         return *mid;
59*35238bceSAndroid Build Coastguard Worker }
60*35238bceSAndroid Build Coastguard Worker 
theilSenLinearRegression(const std::vector<tcu::Vec2> & dataPoints)61*35238bceSAndroid Build Coastguard Worker LineParameters theilSenLinearRegression(const std::vector<tcu::Vec2> &dataPoints)
62*35238bceSAndroid Build Coastguard Worker {
63*35238bceSAndroid Build Coastguard Worker     const float epsilon = 1e-6f;
64*35238bceSAndroid Build Coastguard Worker 
65*35238bceSAndroid Build Coastguard Worker     const int numDataPoints = (int)dataPoints.size();
66*35238bceSAndroid Build Coastguard Worker     vector<float> pairwiseCoefficients;
67*35238bceSAndroid Build Coastguard Worker     vector<float> pointwiseOffsets;
68*35238bceSAndroid Build Coastguard Worker     LineParameters result(0.0f, 0.0f);
69*35238bceSAndroid Build Coastguard Worker 
70*35238bceSAndroid Build Coastguard Worker     // Compute the pairwise coefficients.
71*35238bceSAndroid Build Coastguard Worker     for (int i = 0; i < numDataPoints; i++)
72*35238bceSAndroid Build Coastguard Worker     {
73*35238bceSAndroid Build Coastguard Worker         const Vec2 &ptA = dataPoints[i];
74*35238bceSAndroid Build Coastguard Worker 
75*35238bceSAndroid Build Coastguard Worker         for (int j = 0; j < i; j++)
76*35238bceSAndroid Build Coastguard Worker         {
77*35238bceSAndroid Build Coastguard Worker             const Vec2 &ptB = dataPoints[j];
78*35238bceSAndroid Build Coastguard Worker 
79*35238bceSAndroid Build Coastguard Worker             if (de::abs(ptA.x() - ptB.x()) > epsilon)
80*35238bceSAndroid Build Coastguard Worker                 pairwiseCoefficients.push_back((ptA.y() - ptB.y()) / (ptA.x() - ptB.x()));
81*35238bceSAndroid Build Coastguard Worker         }
82*35238bceSAndroid Build Coastguard Worker     }
83*35238bceSAndroid Build Coastguard Worker 
84*35238bceSAndroid Build Coastguard Worker     // Find the median of the pairwise coefficients.
85*35238bceSAndroid Build Coastguard Worker     // \note If there are no data point pairs with differing x values, the coefficient variable will stay zero as initialized.
86*35238bceSAndroid Build Coastguard Worker     if (!pairwiseCoefficients.empty())
87*35238bceSAndroid Build Coastguard Worker         result.coefficient = destructiveMedian(pairwiseCoefficients);
88*35238bceSAndroid Build Coastguard Worker 
89*35238bceSAndroid Build Coastguard Worker     // Compute the offsets corresponding to the median coefficient, for all data points.
90*35238bceSAndroid Build Coastguard Worker     for (int i = 0; i < numDataPoints; i++)
91*35238bceSAndroid Build Coastguard Worker         pointwiseOffsets.push_back(dataPoints[i].y() - result.coefficient * dataPoints[i].x());
92*35238bceSAndroid Build Coastguard Worker 
93*35238bceSAndroid Build Coastguard Worker     // Find the median of the offsets.
94*35238bceSAndroid Build Coastguard Worker     // \note If there are no data points, the offset variable will stay zero as initialized.
95*35238bceSAndroid Build Coastguard Worker     if (!pointwiseOffsets.empty())
96*35238bceSAndroid Build Coastguard Worker         result.offset = destructiveMedian(pointwiseOffsets);
97*35238bceSAndroid Build Coastguard Worker 
98*35238bceSAndroid Build Coastguard Worker     return result;
99*35238bceSAndroid Build Coastguard Worker }
100*35238bceSAndroid Build Coastguard Worker 
101*35238bceSAndroid Build Coastguard Worker // Sample from given values using linear interpolation at a given position as if values were laid to range [0, 1]
102*35238bceSAndroid Build Coastguard Worker template <typename T>
linearSample(const std::vector<T> & values,float position)103*35238bceSAndroid Build Coastguard Worker static float linearSample(const std::vector<T> &values, float position)
104*35238bceSAndroid Build Coastguard Worker {
105*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(position >= 0.0f);
106*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(position <= 1.0f);
107*35238bceSAndroid Build Coastguard Worker 
108*35238bceSAndroid Build Coastguard Worker     const int maxNdx     = (int)values.size() - 1;
109*35238bceSAndroid Build Coastguard Worker     const float floatNdx = (float)maxNdx * position;
110*35238bceSAndroid Build Coastguard Worker     const int lowerNdx   = (int)deFloatFloor(floatNdx);
111*35238bceSAndroid Build Coastguard Worker     const int higherNdx  = lowerNdx + (lowerNdx == maxNdx ? 0 : 1); // Use only last element if position is 1.0
112*35238bceSAndroid Build Coastguard Worker     const float interpolationFactor = floatNdx - (float)lowerNdx;
113*35238bceSAndroid Build Coastguard Worker 
114*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(lowerNdx >= 0 && lowerNdx < (int)values.size());
115*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(higherNdx >= 0 && higherNdx < (int)values.size());
116*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(interpolationFactor >= 0 && interpolationFactor < 1.0f);
117*35238bceSAndroid Build Coastguard Worker 
118*35238bceSAndroid Build Coastguard Worker     return tcu::mix((float)values[lowerNdx], (float)values[higherNdx], interpolationFactor);
119*35238bceSAndroid Build Coastguard Worker }
120*35238bceSAndroid Build Coastguard Worker 
theilSenSiegelLinearRegression(const std::vector<tcu::Vec2> & dataPoints,float reportedConfidence)121*35238bceSAndroid Build Coastguard Worker LineParametersWithConfidence theilSenSiegelLinearRegression(const std::vector<tcu::Vec2> &dataPoints,
122*35238bceSAndroid Build Coastguard Worker                                                             float reportedConfidence)
123*35238bceSAndroid Build Coastguard Worker {
124*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(!dataPoints.empty());
125*35238bceSAndroid Build Coastguard Worker 
126*35238bceSAndroid Build Coastguard Worker     // Siegel's variation
127*35238bceSAndroid Build Coastguard Worker 
128*35238bceSAndroid Build Coastguard Worker     const float epsilon     = 1e-6f;
129*35238bceSAndroid Build Coastguard Worker     const int numDataPoints = (int)dataPoints.size();
130*35238bceSAndroid Build Coastguard Worker     std::vector<float> medianSlopes;
131*35238bceSAndroid Build Coastguard Worker     std::vector<float> pointwiseOffsets;
132*35238bceSAndroid Build Coastguard Worker     LineParametersWithConfidence result;
133*35238bceSAndroid Build Coastguard Worker 
134*35238bceSAndroid Build Coastguard Worker     // Compute the median slope via each element
135*35238bceSAndroid Build Coastguard Worker     for (int i = 0; i < numDataPoints; i++)
136*35238bceSAndroid Build Coastguard Worker     {
137*35238bceSAndroid Build Coastguard Worker         const tcu::Vec2 &ptA = dataPoints[i];
138*35238bceSAndroid Build Coastguard Worker         std::vector<float> slopes;
139*35238bceSAndroid Build Coastguard Worker 
140*35238bceSAndroid Build Coastguard Worker         slopes.reserve(numDataPoints);
141*35238bceSAndroid Build Coastguard Worker 
142*35238bceSAndroid Build Coastguard Worker         for (int j = 0; j < numDataPoints; j++)
143*35238bceSAndroid Build Coastguard Worker         {
144*35238bceSAndroid Build Coastguard Worker             const tcu::Vec2 &ptB = dataPoints[j];
145*35238bceSAndroid Build Coastguard Worker 
146*35238bceSAndroid Build Coastguard Worker             if (de::abs(ptA.x() - ptB.x()) > epsilon)
147*35238bceSAndroid Build Coastguard Worker                 slopes.push_back((ptA.y() - ptB.y()) / (ptA.x() - ptB.x()));
148*35238bceSAndroid Build Coastguard Worker         }
149*35238bceSAndroid Build Coastguard Worker 
150*35238bceSAndroid Build Coastguard Worker         // Add median of slopes through point i
151*35238bceSAndroid Build Coastguard Worker         medianSlopes.push_back(destructiveMedian(slopes));
152*35238bceSAndroid Build Coastguard Worker     }
153*35238bceSAndroid Build Coastguard Worker 
154*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(!medianSlopes.empty());
155*35238bceSAndroid Build Coastguard Worker 
156*35238bceSAndroid Build Coastguard Worker     // Find the median of the pairwise coefficients.
157*35238bceSAndroid Build Coastguard Worker     std::sort(medianSlopes.begin(), medianSlopes.end());
158*35238bceSAndroid Build Coastguard Worker     result.coefficient = linearSample(medianSlopes, 0.5f);
159*35238bceSAndroid Build Coastguard Worker 
160*35238bceSAndroid Build Coastguard Worker     // Compute the offsets corresponding to the median coefficient, for all data points.
161*35238bceSAndroid Build Coastguard Worker     for (int i = 0; i < numDataPoints; i++)
162*35238bceSAndroid Build Coastguard Worker         pointwiseOffsets.push_back(dataPoints[i].y() - result.coefficient * dataPoints[i].x());
163*35238bceSAndroid Build Coastguard Worker 
164*35238bceSAndroid Build Coastguard Worker     // Find the median of the offsets.
165*35238bceSAndroid Build Coastguard Worker     std::sort(pointwiseOffsets.begin(), pointwiseOffsets.end());
166*35238bceSAndroid Build Coastguard Worker     result.offset = linearSample(pointwiseOffsets, 0.5f);
167*35238bceSAndroid Build Coastguard Worker 
168*35238bceSAndroid Build Coastguard Worker     // calculate confidence intervals
169*35238bceSAndroid Build Coastguard Worker     result.coefficientConfidenceLower = linearSample(medianSlopes, 0.5f - reportedConfidence * 0.5f);
170*35238bceSAndroid Build Coastguard Worker     result.coefficientConfidenceUpper = linearSample(medianSlopes, 0.5f + reportedConfidence * 0.5f);
171*35238bceSAndroid Build Coastguard Worker 
172*35238bceSAndroid Build Coastguard Worker     result.offsetConfidenceLower = linearSample(pointwiseOffsets, 0.5f - reportedConfidence * 0.5f);
173*35238bceSAndroid Build Coastguard Worker     result.offsetConfidenceUpper = linearSample(pointwiseOffsets, 0.5f + reportedConfidence * 0.5f);
174*35238bceSAndroid Build Coastguard Worker 
175*35238bceSAndroid Build Coastguard Worker     result.confidence = reportedConfidence;
176*35238bceSAndroid Build Coastguard Worker 
177*35238bceSAndroid Build Coastguard Worker     return result;
178*35238bceSAndroid Build Coastguard Worker }
179*35238bceSAndroid Build Coastguard Worker 
isDone(void) const180*35238bceSAndroid Build Coastguard Worker bool MeasureState::isDone(void) const
181*35238bceSAndroid Build Coastguard Worker {
182*35238bceSAndroid Build Coastguard Worker     return (int)frameTimes.size() >= maxNumFrames ||
183*35238bceSAndroid Build Coastguard Worker            (frameTimes.size() >= 2 && frameTimes[frameTimes.size() - 2] >= (uint64_t)frameShortcutTime &&
184*35238bceSAndroid Build Coastguard Worker             frameTimes[frameTimes.size() - 1] >= (uint64_t)frameShortcutTime);
185*35238bceSAndroid Build Coastguard Worker }
186*35238bceSAndroid Build Coastguard Worker 
getTotalTime(void) const187*35238bceSAndroid Build Coastguard Worker uint64_t MeasureState::getTotalTime(void) const
188*35238bceSAndroid Build Coastguard Worker {
189*35238bceSAndroid Build Coastguard Worker     uint64_t time = 0;
190*35238bceSAndroid Build Coastguard Worker     for (int i = 0; i < (int)frameTimes.size(); i++)
191*35238bceSAndroid Build Coastguard Worker         time += frameTimes[i];
192*35238bceSAndroid Build Coastguard Worker     return time;
193*35238bceSAndroid Build Coastguard Worker }
194*35238bceSAndroid Build Coastguard Worker 
clear(void)195*35238bceSAndroid Build Coastguard Worker void MeasureState::clear(void)
196*35238bceSAndroid Build Coastguard Worker {
197*35238bceSAndroid Build Coastguard Worker     maxNumFrames      = 0;
198*35238bceSAndroid Build Coastguard Worker     frameShortcutTime = std::numeric_limits<float>::infinity();
199*35238bceSAndroid Build Coastguard Worker     numDrawCalls      = 0;
200*35238bceSAndroid Build Coastguard Worker     frameTimes.clear();
201*35238bceSAndroid Build Coastguard Worker }
202*35238bceSAndroid Build Coastguard Worker 
start(int maxNumFrames_,float frameShortcutTime_,int numDrawCalls_)203*35238bceSAndroid Build Coastguard Worker void MeasureState::start(int maxNumFrames_, float frameShortcutTime_, int numDrawCalls_)
204*35238bceSAndroid Build Coastguard Worker {
205*35238bceSAndroid Build Coastguard Worker     frameTimes.clear();
206*35238bceSAndroid Build Coastguard Worker     frameTimes.reserve(maxNumFrames_);
207*35238bceSAndroid Build Coastguard Worker     maxNumFrames      = maxNumFrames_;
208*35238bceSAndroid Build Coastguard Worker     frameShortcutTime = frameShortcutTime_;
209*35238bceSAndroid Build Coastguard Worker     numDrawCalls      = numDrawCalls_;
210*35238bceSAndroid Build Coastguard Worker }
211*35238bceSAndroid Build Coastguard Worker 
TheilSenCalibrator(void)212*35238bceSAndroid Build Coastguard Worker TheilSenCalibrator::TheilSenCalibrator(void)
213*35238bceSAndroid Build Coastguard Worker     : m_params(1 /* initial calls */, 10 /* calibrate iter frames */, 2000.0f /* calibrate iter shortcut threshold */,
214*35238bceSAndroid Build Coastguard Worker                31 /* max calibration iterations */, 1000.0f / 30.0f /* target frame time */,
215*35238bceSAndroid Build Coastguard Worker                1000.0f / 60.0f /* frame time cap */, 1000.0f /* target measure duration */)
216*35238bceSAndroid Build Coastguard Worker     , m_state(INTERNALSTATE_LAST)
217*35238bceSAndroid Build Coastguard Worker {
218*35238bceSAndroid Build Coastguard Worker     clear();
219*35238bceSAndroid Build Coastguard Worker }
220*35238bceSAndroid Build Coastguard Worker 
TheilSenCalibrator(const CalibratorParameters & params)221*35238bceSAndroid Build Coastguard Worker TheilSenCalibrator::TheilSenCalibrator(const CalibratorParameters &params)
222*35238bceSAndroid Build Coastguard Worker     : m_params(params)
223*35238bceSAndroid Build Coastguard Worker     , m_state(INTERNALSTATE_LAST)
224*35238bceSAndroid Build Coastguard Worker {
225*35238bceSAndroid Build Coastguard Worker     clear();
226*35238bceSAndroid Build Coastguard Worker }
227*35238bceSAndroid Build Coastguard Worker 
~TheilSenCalibrator()228*35238bceSAndroid Build Coastguard Worker TheilSenCalibrator::~TheilSenCalibrator()
229*35238bceSAndroid Build Coastguard Worker {
230*35238bceSAndroid Build Coastguard Worker }
231*35238bceSAndroid Build Coastguard Worker 
clear(void)232*35238bceSAndroid Build Coastguard Worker void TheilSenCalibrator::clear(void)
233*35238bceSAndroid Build Coastguard Worker {
234*35238bceSAndroid Build Coastguard Worker     m_measureState.clear();
235*35238bceSAndroid Build Coastguard Worker     m_calibrateIterations.clear();
236*35238bceSAndroid Build Coastguard Worker     m_state = INTERNALSTATE_CALIBRATING;
237*35238bceSAndroid Build Coastguard Worker }
238*35238bceSAndroid Build Coastguard Worker 
clear(const CalibratorParameters & params)239*35238bceSAndroid Build Coastguard Worker void TheilSenCalibrator::clear(const CalibratorParameters &params)
240*35238bceSAndroid Build Coastguard Worker {
241*35238bceSAndroid Build Coastguard Worker     m_params = params;
242*35238bceSAndroid Build Coastguard Worker     clear();
243*35238bceSAndroid Build Coastguard Worker }
244*35238bceSAndroid Build Coastguard Worker 
getState(void) const245*35238bceSAndroid Build Coastguard Worker TheilSenCalibrator::State TheilSenCalibrator::getState(void) const
246*35238bceSAndroid Build Coastguard Worker {
247*35238bceSAndroid Build Coastguard Worker     if (m_state == INTERNALSTATE_FINISHED)
248*35238bceSAndroid Build Coastguard Worker         return STATE_FINISHED;
249*35238bceSAndroid Build Coastguard Worker     else
250*35238bceSAndroid Build Coastguard Worker     {
251*35238bceSAndroid Build Coastguard Worker         DE_ASSERT(m_state == INTERNALSTATE_CALIBRATING || !m_measureState.isDone());
252*35238bceSAndroid Build Coastguard Worker         return m_measureState.isDone() ? STATE_RECOMPUTE_PARAMS : STATE_MEASURE;
253*35238bceSAndroid Build Coastguard Worker     }
254*35238bceSAndroid Build Coastguard Worker }
255*35238bceSAndroid Build Coastguard Worker 
recordIteration(uint64_t iterationTime)256*35238bceSAndroid Build Coastguard Worker void TheilSenCalibrator::recordIteration(uint64_t iterationTime)
257*35238bceSAndroid Build Coastguard Worker {
258*35238bceSAndroid Build Coastguard Worker     DE_ASSERT((m_state == INTERNALSTATE_CALIBRATING || m_state == INTERNALSTATE_RUNNING) && !m_measureState.isDone());
259*35238bceSAndroid Build Coastguard Worker     m_measureState.frameTimes.push_back(iterationTime);
260*35238bceSAndroid Build Coastguard Worker 
261*35238bceSAndroid Build Coastguard Worker     if (m_state == INTERNALSTATE_RUNNING && m_measureState.isDone())
262*35238bceSAndroid Build Coastguard Worker         m_state = INTERNALSTATE_FINISHED;
263*35238bceSAndroid Build Coastguard Worker }
264*35238bceSAndroid Build Coastguard Worker 
recomputeParameters(void)265*35238bceSAndroid Build Coastguard Worker void TheilSenCalibrator::recomputeParameters(void)
266*35238bceSAndroid Build Coastguard Worker {
267*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(m_state == INTERNALSTATE_CALIBRATING);
268*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(m_measureState.isDone());
269*35238bceSAndroid Build Coastguard Worker 
270*35238bceSAndroid Build Coastguard Worker     // Minimum and maximum acceptable frame times.
271*35238bceSAndroid Build Coastguard Worker     const float minGoodFrameTimeUs = m_params.targetFrameTimeUs * 0.95f;
272*35238bceSAndroid Build Coastguard Worker     const float maxGoodFrameTimeUs = m_params.targetFrameTimeUs * 1.15f;
273*35238bceSAndroid Build Coastguard Worker 
274*35238bceSAndroid Build Coastguard Worker     const int numIterations = (int)m_calibrateIterations.size();
275*35238bceSAndroid Build Coastguard Worker 
276*35238bceSAndroid Build Coastguard Worker     // Record frame time.
277*35238bceSAndroid Build Coastguard Worker     if (numIterations > 0)
278*35238bceSAndroid Build Coastguard Worker     {
279*35238bceSAndroid Build Coastguard Worker         m_calibrateIterations.back().frameTime =
280*35238bceSAndroid Build Coastguard Worker             (float)((double)m_measureState.getTotalTime() / (double)m_measureState.frameTimes.size());
281*35238bceSAndroid Build Coastguard Worker 
282*35238bceSAndroid Build Coastguard Worker         // Check if we're good enough to stop calibrating.
283*35238bceSAndroid Build Coastguard Worker         {
284*35238bceSAndroid Build Coastguard Worker             bool endCalibration = false;
285*35238bceSAndroid Build Coastguard Worker 
286*35238bceSAndroid Build Coastguard Worker             // Is the maximum calibration iteration limit reached?
287*35238bceSAndroid Build Coastguard Worker             endCalibration = endCalibration || (int)m_calibrateIterations.size() >= m_params.maxCalibrateIterations;
288*35238bceSAndroid Build Coastguard Worker 
289*35238bceSAndroid Build Coastguard Worker             // Do a few past iterations have frame time in acceptable range?
290*35238bceSAndroid Build Coastguard Worker             {
291*35238bceSAndroid Build Coastguard Worker                 const int numRelevantPastIterations = 2;
292*35238bceSAndroid Build Coastguard Worker 
293*35238bceSAndroid Build Coastguard Worker                 if (!endCalibration && (int)m_calibrateIterations.size() >= numRelevantPastIterations)
294*35238bceSAndroid Build Coastguard Worker                 {
295*35238bceSAndroid Build Coastguard Worker                     const CalibrateIteration *const past =
296*35238bceSAndroid Build Coastguard Worker                         &m_calibrateIterations[m_calibrateIterations.size() - numRelevantPastIterations];
297*35238bceSAndroid Build Coastguard Worker                     bool allInGoodRange = true;
298*35238bceSAndroid Build Coastguard Worker 
299*35238bceSAndroid Build Coastguard Worker                     for (int i = 0; i < numRelevantPastIterations && allInGoodRange; i++)
300*35238bceSAndroid Build Coastguard Worker                     {
301*35238bceSAndroid Build Coastguard Worker                         const float frameTimeUs = past[i].frameTime;
302*35238bceSAndroid Build Coastguard Worker                         if (!de::inRange(frameTimeUs, minGoodFrameTimeUs, maxGoodFrameTimeUs))
303*35238bceSAndroid Build Coastguard Worker                             allInGoodRange = false;
304*35238bceSAndroid Build Coastguard Worker                     }
305*35238bceSAndroid Build Coastguard Worker 
306*35238bceSAndroid Build Coastguard Worker                     endCalibration = endCalibration || allInGoodRange;
307*35238bceSAndroid Build Coastguard Worker                 }
308*35238bceSAndroid Build Coastguard Worker             }
309*35238bceSAndroid Build Coastguard Worker 
310*35238bceSAndroid Build Coastguard Worker             // Do a few past iterations have similar-enough call counts?
311*35238bceSAndroid Build Coastguard Worker             {
312*35238bceSAndroid Build Coastguard Worker                 const int numRelevantPastIterations = 3;
313*35238bceSAndroid Build Coastguard Worker                 if (!endCalibration && (int)m_calibrateIterations.size() >= numRelevantPastIterations)
314*35238bceSAndroid Build Coastguard Worker                 {
315*35238bceSAndroid Build Coastguard Worker                     const CalibrateIteration *const past =
316*35238bceSAndroid Build Coastguard Worker                         &m_calibrateIterations[m_calibrateIterations.size() - numRelevantPastIterations];
317*35238bceSAndroid Build Coastguard Worker                     int minCallCount = std::numeric_limits<int>::max();
318*35238bceSAndroid Build Coastguard Worker                     int maxCallCount = std::numeric_limits<int>::min();
319*35238bceSAndroid Build Coastguard Worker 
320*35238bceSAndroid Build Coastguard Worker                     for (int i = 0; i < numRelevantPastIterations; i++)
321*35238bceSAndroid Build Coastguard Worker                     {
322*35238bceSAndroid Build Coastguard Worker                         minCallCount = de::min(minCallCount, past[i].numDrawCalls);
323*35238bceSAndroid Build Coastguard Worker                         maxCallCount = de::max(maxCallCount, past[i].numDrawCalls);
324*35238bceSAndroid Build Coastguard Worker                     }
325*35238bceSAndroid Build Coastguard Worker 
326*35238bceSAndroid Build Coastguard Worker                     if ((float)(maxCallCount - minCallCount) <= (float)minCallCount * 0.1f)
327*35238bceSAndroid Build Coastguard Worker                         endCalibration = true;
328*35238bceSAndroid Build Coastguard Worker                 }
329*35238bceSAndroid Build Coastguard Worker             }
330*35238bceSAndroid Build Coastguard Worker 
331*35238bceSAndroid Build Coastguard Worker             // Is call count just 1, and frame time still way too high?
332*35238bceSAndroid Build Coastguard Worker             endCalibration =
333*35238bceSAndroid Build Coastguard Worker                 endCalibration || (m_calibrateIterations.back().numDrawCalls == 1 &&
334*35238bceSAndroid Build Coastguard Worker                                    m_calibrateIterations.back().frameTime > m_params.targetFrameTimeUs * 2.0f);
335*35238bceSAndroid Build Coastguard Worker 
336*35238bceSAndroid Build Coastguard Worker             if (endCalibration)
337*35238bceSAndroid Build Coastguard Worker             {
338*35238bceSAndroid Build Coastguard Worker                 const int minFrames  = 10;
339*35238bceSAndroid Build Coastguard Worker                 const int maxFrames  = 60;
340*35238bceSAndroid Build Coastguard Worker                 int numMeasureFrames = deClamp32(
341*35238bceSAndroid Build Coastguard Worker                     deRoundFloatToInt32(m_params.targetMeasureDurationUs / m_calibrateIterations.back().frameTime),
342*35238bceSAndroid Build Coastguard Worker                     minFrames, maxFrames);
343*35238bceSAndroid Build Coastguard Worker 
344*35238bceSAndroid Build Coastguard Worker                 m_state = INTERNALSTATE_RUNNING;
345*35238bceSAndroid Build Coastguard Worker                 m_measureState.start(numMeasureFrames, m_params.calibrateIterationShortcutThreshold,
346*35238bceSAndroid Build Coastguard Worker                                      m_calibrateIterations.back().numDrawCalls);
347*35238bceSAndroid Build Coastguard Worker                 return;
348*35238bceSAndroid Build Coastguard Worker             }
349*35238bceSAndroid Build Coastguard Worker         }
350*35238bceSAndroid Build Coastguard Worker     }
351*35238bceSAndroid Build Coastguard Worker 
352*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(m_state == INTERNALSTATE_CALIBRATING);
353*35238bceSAndroid Build Coastguard Worker 
354*35238bceSAndroid Build Coastguard Worker     // Estimate new call count.
355*35238bceSAndroid Build Coastguard Worker     {
356*35238bceSAndroid Build Coastguard Worker         int newCallCount;
357*35238bceSAndroid Build Coastguard Worker 
358*35238bceSAndroid Build Coastguard Worker         if (numIterations == 0)
359*35238bceSAndroid Build Coastguard Worker             newCallCount = m_params.numInitialCalls;
360*35238bceSAndroid Build Coastguard Worker         else
361*35238bceSAndroid Build Coastguard Worker         {
362*35238bceSAndroid Build Coastguard Worker             vector<Vec2> dataPoints;
363*35238bceSAndroid Build Coastguard Worker             for (int i = 0; i < numIterations; i++)
364*35238bceSAndroid Build Coastguard Worker             {
365*35238bceSAndroid Build Coastguard Worker                 if (m_calibrateIterations[i].numDrawCalls == 1 ||
366*35238bceSAndroid Build Coastguard Worker                     m_calibrateIterations[i].frameTime >
367*35238bceSAndroid Build Coastguard Worker                         m_params.frameTimeCapUs * 1.05f) // Only account for measurements not too near the cap.
368*35238bceSAndroid Build Coastguard Worker                     dataPoints.push_back(
369*35238bceSAndroid Build Coastguard Worker                         Vec2((float)m_calibrateIterations[i].numDrawCalls, m_calibrateIterations[i].frameTime));
370*35238bceSAndroid Build Coastguard Worker             }
371*35238bceSAndroid Build Coastguard Worker 
372*35238bceSAndroid Build Coastguard Worker             if (numIterations == 1)
373*35238bceSAndroid Build Coastguard Worker                 dataPoints.push_back(
374*35238bceSAndroid Build Coastguard Worker                     Vec2(0.0f,
375*35238bceSAndroid Build Coastguard Worker                          0.0f)); // If there's just one measurement so far, this will help in getting the next estimate.
376*35238bceSAndroid Build Coastguard Worker 
377*35238bceSAndroid Build Coastguard Worker             {
378*35238bceSAndroid Build Coastguard Worker                 const float targetFrameTimeUs = m_params.targetFrameTimeUs;
379*35238bceSAndroid Build Coastguard Worker                 const float coeffEpsilon =
380*35238bceSAndroid Build Coastguard Worker                     0.001f; // Coefficient must be large enough (and positive) to be considered sensible.
381*35238bceSAndroid Build Coastguard Worker 
382*35238bceSAndroid Build Coastguard Worker                 const LineParameters estimatorLine = theilSenLinearRegression(dataPoints);
383*35238bceSAndroid Build Coastguard Worker 
384*35238bceSAndroid Build Coastguard Worker                 int prevMaxCalls = 0;
385*35238bceSAndroid Build Coastguard Worker 
386*35238bceSAndroid Build Coastguard Worker                 // Find the maximum of the past call counts.
387*35238bceSAndroid Build Coastguard Worker                 for (int i = 0; i < numIterations; i++)
388*35238bceSAndroid Build Coastguard Worker                     prevMaxCalls = de::max(prevMaxCalls, m_calibrateIterations[i].numDrawCalls);
389*35238bceSAndroid Build Coastguard Worker 
390*35238bceSAndroid Build Coastguard Worker                 if (estimatorLine.coefficient <
391*35238bceSAndroid Build Coastguard Worker                     coeffEpsilon) // Coefficient not good for sensible estimation; increase call count enough to get a reasonably different value.
392*35238bceSAndroid Build Coastguard Worker                     newCallCount = 2 * prevMaxCalls;
393*35238bceSAndroid Build Coastguard Worker                 else
394*35238bceSAndroid Build Coastguard Worker                 {
395*35238bceSAndroid Build Coastguard Worker                     // Solve newCallCount such that approximately targetFrameTime = offset + coefficient*newCallCount.
396*35238bceSAndroid Build Coastguard Worker                     newCallCount = (int)((targetFrameTimeUs - estimatorLine.offset) / estimatorLine.coefficient + 0.5f);
397*35238bceSAndroid Build Coastguard Worker 
398*35238bceSAndroid Build Coastguard Worker                     // We should generally prefer FPS counts below the target rather than above (i.e. higher frame times rather than lower).
399*35238bceSAndroid Build Coastguard Worker                     if (estimatorLine.offset + estimatorLine.coefficient * (float)newCallCount < minGoodFrameTimeUs)
400*35238bceSAndroid Build Coastguard Worker                         newCallCount++;
401*35238bceSAndroid Build Coastguard Worker                 }
402*35238bceSAndroid Build Coastguard Worker 
403*35238bceSAndroid Build Coastguard Worker                 // Make sure we have at least minimum amount of calls, and don't allow increasing call count too much in one iteration.
404*35238bceSAndroid Build Coastguard Worker                 newCallCount = de::clamp(newCallCount, 1, prevMaxCalls * 10);
405*35238bceSAndroid Build Coastguard Worker             }
406*35238bceSAndroid Build Coastguard Worker         }
407*35238bceSAndroid Build Coastguard Worker 
408*35238bceSAndroid Build Coastguard Worker         m_measureState.start(m_params.maxCalibrateIterationFrames, m_params.calibrateIterationShortcutThreshold,
409*35238bceSAndroid Build Coastguard Worker                              newCallCount);
410*35238bceSAndroid Build Coastguard Worker         m_calibrateIterations.push_back(CalibrateIteration(newCallCount, 0.0f));
411*35238bceSAndroid Build Coastguard Worker     }
412*35238bceSAndroid Build Coastguard Worker }
413*35238bceSAndroid Build Coastguard Worker 
logCalibrationInfo(tcu::TestLog & log,const TheilSenCalibrator & calibrator)414*35238bceSAndroid Build Coastguard Worker void logCalibrationInfo(tcu::TestLog &log, const TheilSenCalibrator &calibrator)
415*35238bceSAndroid Build Coastguard Worker {
416*35238bceSAndroid Build Coastguard Worker     const CalibratorParameters &params                         = calibrator.getParameters();
417*35238bceSAndroid Build Coastguard Worker     const std::vector<CalibrateIteration> &calibrateIterations = calibrator.getCalibrationInfo();
418*35238bceSAndroid Build Coastguard Worker 
419*35238bceSAndroid Build Coastguard Worker     // Write out default calibration info.
420*35238bceSAndroid Build Coastguard Worker 
421*35238bceSAndroid Build Coastguard Worker     log << TestLog::Section("CalibrationInfo", "Calibration Info") << TestLog::Message
422*35238bceSAndroid Build Coastguard Worker         << "Target frame time: " << params.targetFrameTimeUs << " us (" << 1000000 / params.targetFrameTimeUs << " fps)"
423*35238bceSAndroid Build Coastguard Worker         << TestLog::EndMessage;
424*35238bceSAndroid Build Coastguard Worker 
425*35238bceSAndroid Build Coastguard Worker     for (int iterNdx = 0; iterNdx < (int)calibrateIterations.size(); iterNdx++)
426*35238bceSAndroid Build Coastguard Worker     {
427*35238bceSAndroid Build Coastguard Worker         log << TestLog::Message << "  iteration " << iterNdx << ": " << calibrateIterations[iterNdx].numDrawCalls
428*35238bceSAndroid Build Coastguard Worker             << " calls => " << de::floatToString(calibrateIterations[iterNdx].frameTime, 2) << " us ("
429*35238bceSAndroid Build Coastguard Worker             << de::floatToString(1000000.0f / calibrateIterations[iterNdx].frameTime, 2) << " fps)"
430*35238bceSAndroid Build Coastguard Worker             << TestLog::EndMessage;
431*35238bceSAndroid Build Coastguard Worker     }
432*35238bceSAndroid Build Coastguard Worker     log << TestLog::Integer("CallCount", "Calibrated call count", "", QP_KEY_TAG_NONE,
433*35238bceSAndroid Build Coastguard Worker                             calibrator.getMeasureState().numDrawCalls)
434*35238bceSAndroid Build Coastguard Worker         << TestLog::Integer("FrameCount", "Calibrated frame count", "", QP_KEY_TAG_NONE,
435*35238bceSAndroid Build Coastguard Worker                             (int)calibrator.getMeasureState().frameTimes.size());
436*35238bceSAndroid Build Coastguard Worker     log << TestLog::EndSection;
437*35238bceSAndroid Build Coastguard Worker }
438*35238bceSAndroid Build Coastguard Worker 
439*35238bceSAndroid Build Coastguard Worker } // namespace gls
440*35238bceSAndroid Build Coastguard Worker } // namespace deqp
441