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 ¶ms)
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 ¶ms)
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 ¶ms = 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