Home | History | Annotate | Download | only in glshared
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL (ES) Module
      3  * -----------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Shader performance measurer; handles calibration and measurement
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "glsShaderPerformanceMeasurer.hpp"
     25 #include "gluDefs.hpp"
     26 #include "tcuTestLog.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "deStringUtil.hpp"
     29 #include "deMath.h"
     30 #include "deClock.h"
     31 
     32 #include "glwFunctions.hpp"
     33 #include "glwEnums.hpp"
     34 
     35 #include <algorithm>
     36 
     37 using tcu::Vec4;
     38 using std::string;
     39 using std::vector;
     40 using tcu::TestLog;
     41 using namespace glw; // GL types
     42 
     43 namespace deqp
     44 {
     45 namespace gls
     46 {
     47 
     48 static inline float triangleInterpolate (float v0, float v1, float v2, float x, float y)
     49 {
     50 	return v0 + (v2-v0)*x + (v1-v0)*y;
     51 }
     52 
     53 static inline float triQuadInterpolate (float x, float y, const tcu::Vec4& quad)
     54 {
     55 	// \note Top left fill rule.
     56 	if (x + y < 1.0f)
     57 		return triangleInterpolate(quad.x(), quad.y(), quad.z(), x, y);
     58 	else
     59 		return triangleInterpolate(quad.w(), quad.z(), quad.y(), 1.0f-x, 1.0f-y);
     60 }
     61 
     62 static inline int getNumVertices (int gridSizeX, int gridSizeY)
     63 {
     64 	return (gridSizeX + 1) * (gridSizeY + 1);
     65 }
     66 
     67 static inline int getNumIndices (int gridSizeX, int gridSizeY)
     68 {
     69 	return gridSizeX*gridSizeY*6;
     70 }
     71 
     72 static inline deUint16 getVtxIndex (int x, int y, int gridSizeX)
     73 {
     74 	return (deUint16)(y*(gridSizeX+1) + x);
     75 }
     76 
     77 static void generateVertices (std::vector<float>& dst, int gridSizeX, int gridSizeY, const AttribSpec& spec)
     78 {
     79 	const int numComponents = 4;
     80 
     81 	DE_ASSERT((gridSizeX + 1)*(gridSizeY + 1) <= (1<<16)); // Must fit into 16-bit indices.
     82 	DE_ASSERT(gridSizeX >= 1 && gridSizeY >= 1);
     83 	dst.resize((gridSizeX + 1) * (gridSizeY + 1) * 4);
     84 
     85 	for (int y = 0; y <= gridSizeY; y++)
     86 	{
     87 		for (int x = 0; x <= gridSizeX; x++)
     88 		{
     89 			float	xf	= (float)x / (float)gridSizeX;
     90 			float	yf	= (float)y / (float)gridSizeY;
     91 
     92 			for (int compNdx = 0; compNdx < numComponents; compNdx++)
     93 				dst[getVtxIndex(x, y, gridSizeX)*numComponents + compNdx] = triQuadInterpolate(xf, yf, tcu::Vec4(spec.p00[compNdx], spec.p01[compNdx], spec.p10[compNdx], spec.p11[compNdx]));
     94 		}
     95 	}
     96 }
     97 
     98 static void generateIndices (std::vector<deUint16>& dst, int gridSizeX, int gridSizeY)
     99 {
    100 	const int	numIndicesPerQuad	= 6;
    101 	int			numIndices			= gridSizeX * gridSizeY * numIndicesPerQuad;
    102 	dst.resize(numIndices);
    103 
    104 	for (int y = 0; y < gridSizeY; y++)
    105 	{
    106 		for (int x = 0; x < gridSizeX; x++)
    107 		{
    108 			int quadNdx = y*gridSizeX + x;
    109 
    110 			dst[quadNdx*numIndicesPerQuad + 0] = getVtxIndex(x+0, y+0, gridSizeX);
    111 			dst[quadNdx*numIndicesPerQuad + 1] = getVtxIndex(x+1, y+0, gridSizeX);
    112 			dst[quadNdx*numIndicesPerQuad + 2] = getVtxIndex(x+0, y+1, gridSizeX);
    113 
    114 			dst[quadNdx*numIndicesPerQuad + 3] = getVtxIndex(x+0, y+1, gridSizeX);
    115 			dst[quadNdx*numIndicesPerQuad + 4] = getVtxIndex(x+1, y+0, gridSizeX);
    116 			dst[quadNdx*numIndicesPerQuad + 5] = getVtxIndex(x+1, y+1, gridSizeX);
    117 		}
    118 	}
    119 }
    120 
    121 ShaderPerformanceMeasurer::ShaderPerformanceMeasurer (const glu::RenderContext& renderCtx, PerfCaseType measureType)
    122 	: m_renderCtx		(renderCtx)
    123 	, m_gridSizeX		(measureType == CASETYPE_FRAGMENT	? 1		: 255)
    124 	, m_gridSizeY		(measureType == CASETYPE_FRAGMENT	? 1		: 255)
    125 	, m_viewportWidth	(measureType == CASETYPE_VERTEX		? 32	: renderCtx.getRenderTarget().getWidth())
    126 	, m_viewportHeight	(measureType == CASETYPE_VERTEX		? 32	: renderCtx.getRenderTarget().getHeight())
    127 	, m_state			(STATE_UNINITIALIZED)
    128 	, m_result			(-1.0f, -1.0f)
    129 	, m_indexBuffer		(0)
    130 	, m_vao				(0)
    131 {
    132 }
    133 
    134 void ShaderPerformanceMeasurer::logParameters (TestLog& log) const
    135 {
    136 	log << TestLog::Message << "Grid size: " << m_gridSizeX << "x" << m_gridSizeY << TestLog::EndMessage
    137 		<< TestLog::Message << "Viewport: " << m_viewportWidth << "x" << m_viewportHeight << TestLog::EndMessage;
    138 }
    139 
    140 void ShaderPerformanceMeasurer::init (deUint32 program, const vector<AttribSpec>& attributes, int calibratorInitialNumCalls)
    141 {
    142 	DE_ASSERT(m_state == STATE_UNINITIALIZED);
    143 
    144 	const glw::Functions&	gl		= m_renderCtx.getFunctions();
    145 	const bool				useVAO	= glu::isContextTypeGLCore(m_renderCtx.getType());
    146 
    147 	if (useVAO)
    148 	{
    149 		DE_ASSERT(!m_vao);
    150 		gl.genVertexArrays(1, &m_vao);
    151 		gl.bindVertexArray(m_vao);
    152 		GLU_EXPECT_NO_ERROR(gl.getError(), "Create VAO");
    153 	}
    154 
    155 	// Validate that we have sane grid and viewport setup.
    156 
    157 	DE_ASSERT(de::inBounds(m_gridSizeX, 1, 256) && de::inBounds(m_gridSizeY, 1, 256));
    158 
    159 	{
    160 		bool widthTooSmall		= m_renderCtx.getRenderTarget().getWidth() < m_viewportWidth;
    161 		bool heightTooSmall		= m_renderCtx.getRenderTarget().getHeight() < m_viewportHeight;
    162 
    163 		if (widthTooSmall || heightTooSmall)
    164 			throw tcu::NotSupportedError("Render target too small (" +
    165 											 (widthTooSmall  ?									   "width must be at least "  + de::toString(m_viewportWidth)  : "") +
    166 											 (heightTooSmall ? string(widthTooSmall ? ", " : "") + "height must be at least " + de::toString(m_viewportHeight) : "") +
    167 											 ")");
    168 	}
    169 
    170 	TCU_CHECK_INTERNAL(de::inRange(m_viewportWidth,		1, m_renderCtx.getRenderTarget().getWidth()) &&
    171 					   de::inRange(m_viewportHeight,	1, m_renderCtx.getRenderTarget().getHeight()));
    172 
    173 	// Insert a_position to attributes.
    174 	m_attributes = attributes;
    175 	m_attributes.push_back(AttribSpec("a_position",
    176 									  Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
    177 									  Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
    178 									  Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
    179 									  Vec4( 1.0f,  1.0f, 0.0f, 1.0f)));
    180 
    181 	// Generate indices.
    182 	{
    183 		std::vector<deUint16> indices;
    184 		generateIndices(indices, m_gridSizeX, m_gridSizeY);
    185 
    186 		gl.genBuffers(1, &m_indexBuffer);
    187 		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
    188 		gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(indices.size()*sizeof(deUint16)), &indices[0], GL_STATIC_DRAW);
    189 
    190 		GLU_EXPECT_NO_ERROR(gl.getError(), "Upload index data");
    191 	}
    192 
    193 	// Generate vertices.
    194 	m_attribBuffers.resize(m_attributes.size(), 0);
    195 	gl.genBuffers((GLsizei)m_attribBuffers.size(), &m_attribBuffers[0]);
    196 
    197 	for (int attribNdx = 0; attribNdx < (int)m_attributes.size(); attribNdx++)
    198 	{
    199 		std::vector<float> vertices;
    200 		generateVertices(vertices, m_gridSizeX, m_gridSizeY, m_attributes[attribNdx]);
    201 
    202 		gl.bindBuffer(GL_ARRAY_BUFFER, m_attribBuffers[attribNdx]);
    203 		gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(vertices.size()*sizeof(float)), &vertices[0], GL_STATIC_DRAW);
    204 	}
    205 
    206 	GLU_EXPECT_NO_ERROR(gl.getError(), "Upload vertex data");
    207 
    208 	// Setup attribute bindings.
    209 	for (int attribNdx = 0; attribNdx < (int)m_attributes.size(); attribNdx++)
    210 	{
    211 		int location = gl.getAttribLocation(program, m_attributes[attribNdx].name.c_str());
    212 
    213 		if (location >= 0)
    214 		{
    215 			gl.enableVertexAttribArray(location);
    216 			gl.bindBuffer(GL_ARRAY_BUFFER, m_attribBuffers[attribNdx]);
    217 			gl.vertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
    218 		}
    219 
    220 		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup vertex attribute state");
    221 	}
    222 
    223 	gl.useProgram(program);
    224 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
    225 
    226 	m_state = STATE_MEASURING;
    227 	m_isFirstIteration = true;
    228 
    229 	m_calibrator.clear(CalibratorParameters(calibratorInitialNumCalls, 10 /* calibrate iteration frames */, 2000.0f /* calibrate iteration shortcut threshold (ms) */, 16 /* max calibrate iterations */,
    230 											1000.0f/30.0f /* frame time (ms) */, 1000.0f/60.0f /* frame time cap (ms) */, 1000.0f /* target measure duration (ms) */));
    231 }
    232 
    233 void ShaderPerformanceMeasurer::deinit (void)
    234 {
    235 	const glw::Functions& gl = m_renderCtx.getFunctions();
    236 
    237 	if (m_indexBuffer)
    238 	{
    239 		gl.deleteBuffers(1, &m_indexBuffer);
    240 		m_indexBuffer = 0;
    241 	}
    242 
    243 	if (m_vao)
    244 	{
    245 		gl.deleteVertexArrays(1, &m_vao);
    246 		m_vao = 0;
    247 	}
    248 
    249 	if (!m_attribBuffers.empty())
    250 	{
    251 		gl.deleteBuffers((GLsizei)m_attribBuffers.size(), &m_attribBuffers[0]);
    252 		m_attribBuffers.clear();
    253 	}
    254 
    255 	m_state = STATE_UNINITIALIZED;
    256 }
    257 
    258 void ShaderPerformanceMeasurer::render (int numDrawCalls)
    259 {
    260 	const glw::Functions&	gl			= m_renderCtx.getFunctions();
    261 	GLsizei					numIndices	= (GLsizei)getNumIndices(m_gridSizeX, m_gridSizeY);
    262 
    263 	gl.viewport(0, 0, m_viewportWidth, m_viewportHeight);
    264 
    265 	for (int callNdx = 0; callNdx < numDrawCalls; callNdx++)
    266 		gl.drawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, DE_NULL);
    267 }
    268 
    269 void ShaderPerformanceMeasurer::iterate (void)
    270 {
    271 	DE_ASSERT(m_state == STATE_MEASURING);
    272 
    273 	deUint64 renderStartTime = deGetMicroseconds();
    274 	render(m_calibrator.getCallCount()); // Always render. This gives more stable performance behavior.
    275 
    276 	TheilSenCalibrator::State calibratorState = m_calibrator.getState();
    277 
    278 	if (calibratorState == TheilSenCalibrator::STATE_RECOMPUTE_PARAMS)
    279 	{
    280 		m_calibrator.recomputeParameters();
    281 
    282 		m_isFirstIteration = true;
    283 		m_prevRenderStartTime = renderStartTime;
    284 	}
    285 	else if (calibratorState == TheilSenCalibrator::STATE_MEASURE)
    286 	{
    287 		if (!m_isFirstIteration)
    288 			m_calibrator.recordIteration(renderStartTime - m_prevRenderStartTime);
    289 
    290 		m_isFirstIteration = false;
    291 		m_prevRenderStartTime = renderStartTime;
    292 	}
    293 	else
    294 	{
    295 		DE_ASSERT(calibratorState == TheilSenCalibrator::STATE_FINISHED);
    296 
    297 		GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "End of rendering");
    298 
    299 		const MeasureState& measureState = m_calibrator.getMeasureState();
    300 
    301 		// Compute result.
    302 		deUint64	totalTime			= measureState.getTotalTime();
    303 		int			numFrames			= (int)measureState.frameTimes.size();
    304 		deInt64		numQuadGrids		= measureState.numDrawCalls * numFrames;
    305 		deInt64		numPixels			= (deInt64)m_viewportWidth * (deInt64)m_viewportHeight * numQuadGrids;
    306 		deInt64		numVertices			= (deInt64)getNumVertices(m_gridSizeX, m_gridSizeY) * numQuadGrids;
    307 		double		mfragPerSecond		= (double)numPixels / (double)totalTime;
    308 		double		mvertPerSecond		= (double)numVertices / (double)totalTime;
    309 
    310 		m_result = Result((float)mvertPerSecond, (float)mfragPerSecond);
    311 		m_state = STATE_FINISHED;
    312 	}
    313 }
    314 
    315 void ShaderPerformanceMeasurer::logMeasurementInfo (TestLog& log) const
    316 {
    317 	DE_ASSERT(m_state == STATE_FINISHED);
    318 
    319 	const MeasureState& measureState(m_calibrator.getMeasureState());
    320 
    321 	// Compute totals.
    322 	deUint64	totalTime			= measureState.getTotalTime();
    323 	int			numFrames			= (int)measureState.frameTimes.size();
    324 	deInt64		numQuadGrids		= measureState.numDrawCalls * numFrames;
    325 	deInt64		numPixels			= (deInt64)m_viewportWidth * (deInt64)m_viewportHeight * numQuadGrids;
    326 	deInt64		numVertices			= (deInt64)getNumVertices(m_gridSizeX, m_gridSizeY) * numQuadGrids;
    327 	double		mfragPerSecond		= (double)numPixels / (double)totalTime;
    328 	double		mvertPerSecond		= (double)numVertices / (double)totalTime;
    329 	double		framesPerSecond		= (double)numFrames / ((double)totalTime / 1000000.0);
    330 
    331 	logCalibrationInfo(log, m_calibrator);
    332 
    333 	log << TestLog::Float("FramesPerSecond",		"Frames per second in measurement",	"Frames/s",				QP_KEY_TAG_PERFORMANCE,	(float)framesPerSecond)
    334 		<< TestLog::Float("FragmentsPerVertices",	"Vertex-fragment ratio",			"Fragments/Vertices",	QP_KEY_TAG_NONE,		(float)numPixels / (float)numVertices)
    335 		<< TestLog::Float("FragmentPerf",			"Fragment performance",				"MPix/s",				QP_KEY_TAG_PERFORMANCE, (float)mfragPerSecond)
    336 		<< TestLog::Float("VertexPerf",				"Vertex performance",				"MVert/s",				QP_KEY_TAG_PERFORMANCE, (float)mvertPerSecond);
    337 }
    338 
    339 void ShaderPerformanceMeasurer::setGridSize (int gridW, int gridH)
    340 {
    341 	DE_ASSERT(m_state == STATE_UNINITIALIZED);
    342 	DE_ASSERT(de::inBounds(gridW, 1, 256) && de::inBounds(gridH, 1, 256));
    343 	m_gridSizeX		= gridW;
    344 	m_gridSizeY		= gridH;
    345 }
    346 
    347 void ShaderPerformanceMeasurer::setViewportSize (int width, int height)
    348 {
    349 	DE_ASSERT(m_state == STATE_UNINITIALIZED);
    350 	DE_ASSERT(de::inRange(width,	1, m_renderCtx.getRenderTarget().getWidth()) &&
    351 			  de::inRange(height,	1, m_renderCtx.getRenderTarget().getHeight()));
    352 	m_viewportWidth		= width;
    353 	m_viewportHeight	= height;
    354 }
    355 
    356 } // gls
    357 } // deqp
    358