13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry/*-------------------------------------------------------------------------
23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * drawElements Quality Program OpenGL (ES) Module
33c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * -----------------------------------------------
43c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Copyright 2014 The Android Open Source Project
63c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Licensed under the Apache License, Version 2.0 (the "License");
83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * you may not use this file except in compliance with the License.
93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * You may obtain a copy of the License at
103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *      http://www.apache.org/licenses/LICENSE-2.0
123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Unless required by applicable law or agreed to in writing, software
143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * distributed under the License is distributed on an "AS IS" BASIS,
153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * See the License for the specific language governing permissions and
173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * limitations under the License.
183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *//*!
203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * \file
213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * \brief Single-program test case wrapper for ShaderPerformanceMeasurer.
223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *//*--------------------------------------------------------------------*/
233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "glsShaderPerformanceCase.hpp"
253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "tcuRenderTarget.hpp"
263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "deStringUtil.hpp"
273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "deMath.h"
283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "glwFunctions.hpp"
303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "glwEnums.hpp"
313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
323c827367444ee418f129b2c238299f49d3264554Jarkko Poyryusing tcu::Vec4;
333c827367444ee418f129b2c238299f49d3264554Jarkko Poyryusing tcu::TestLog;
343c827367444ee418f129b2c238299f49d3264554Jarkko Poyryusing namespace glw; // GL types
353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
363c827367444ee418f129b2c238299f49d3264554Jarkko Poyrynamespace deqp
373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
383c827367444ee418f129b2c238299f49d3264554Jarkko Poyrynamespace gls
393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
413c827367444ee418f129b2c238299f49d3264554Jarkko PoyryShaderPerformanceCase::ShaderPerformanceCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, PerfCaseType caseType)
423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	: tcu::TestCase		(testCtx, tcu::NODETYPE_PERFORMANCE, name, description)
433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	, m_renderCtx		(renderCtx)
443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	, m_caseType		(caseType)
453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	, m_program			(DE_NULL)
463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	, m_measurer		(renderCtx, caseType)
473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
503c827367444ee418f129b2c238299f49d3264554Jarkko PoyryShaderPerformanceCase::~ShaderPerformanceCase (void)
513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	ShaderPerformanceCase::deinit();
533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
553c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::setGridSize (int gridW, int gridH)
563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_measurer.setGridSize(gridW, gridH);
583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
603c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::setViewportSize (int width, int height)
613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_measurer.setViewportSize(width, height);
633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
653c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::setVertexFragmentRatio (float fragmentsPerVertices)
663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	const float	eps			= 0.01f;
683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			gridW		= 255;
693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			gridH		= 255;
703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			viewportW	= m_renderCtx.getRenderTarget().getWidth();
713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			viewportH	= m_renderCtx.getRenderTarget().getHeight();
723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int i = 0; i < 10; i++)
743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		int		numVert	= (gridW+1)*(gridH+1);
763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		int		numFrag	= viewportW*viewportH;
773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		float	ratio	= (float)numFrag / (float)numVert;
783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if (de::abs(ratio - fragmentsPerVertices) < eps)
803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			break;
813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		else if (ratio < fragmentsPerVertices)
823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{
833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			// Not enough fragments.
843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			numVert = deRoundFloatToInt32((float)numFrag / fragmentsPerVertices);
853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			while ((gridW+1)*(gridH+1) > numVert)
873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			{
883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				if (gridW > gridH)
893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry					gridW -= 1;
903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				else
913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry					gridH -= 1;
923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			}
933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		}
943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		else
953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{
963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			// Not enough vertices.
973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			numFrag = deRoundFloatToInt32((float)numVert * fragmentsPerVertices);
983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			while (viewportW*viewportH > numFrag)
1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			{
1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				if (viewportW > viewportH)
1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry					viewportW -= 1;
1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				else
1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry					viewportH -= 1;
1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			}
1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		}
1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	float finalRatio = (float)(viewportW*viewportH) / (float)((gridW+1)*(gridH+1));
1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_testCtx.getLog() << TestLog::Message << "Requested fragment/vertex-ratio: " << de::floatToString(fragmentsPerVertices, 2) << "\n"
1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry										   << "Computed fragment/vertex-ratio: " << de::floatToString(finalRatio, 2)
1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry					   << TestLog::EndMessage;
1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	setGridSize(gridW, gridH);
1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	setViewportSize(viewportW, viewportH);
1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic void logRenderTargetInfo (TestLog& log, const tcu::RenderTarget& renderTarget)
1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	log << TestLog::Section("RenderTarget", "Render target")
1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		<< TestLog::Message << "size: " << renderTarget.getWidth() << "x" << renderTarget.getHeight() << TestLog::EndMessage
1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		<< TestLog::Message << "bits:"
1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " R" << renderTarget.getPixelFormat().redBits
1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " G" << renderTarget.getPixelFormat().greenBits
1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " B" << renderTarget.getPixelFormat().blueBits
1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " A" << renderTarget.getPixelFormat().alphaBits
1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " D" << renderTarget.getDepthBits()
1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< " S" << renderTarget.getStencilBits()
1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry							<< TestLog::EndMessage;
1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (renderTarget.getNumSamples() != 0)
1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		log << TestLog::Message << renderTarget.getNumSamples() << "x MSAA" << TestLog::EndMessage;
1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	else
1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		log << TestLog::Message << "No MSAA" << TestLog::EndMessage;
1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	log << TestLog::EndSection;
1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::init (void)
1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	tcu::TestLog& log = m_testCtx.getLog();
1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(m_vertShaderSource, m_fragShaderSource));
1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (m_program->isOk())
1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		const int initialCallCount = m_initialCalibration ? m_initialCalibration->initialNumCalls : 1;
1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		logRenderTargetInfo(log, m_renderCtx.getRenderTarget());
1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		m_measurer.init(m_program->getProgram(), m_attributes, initialCallCount);
1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		m_measurer.logParameters(log);
1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		log << *m_program;
1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	else
1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		log << *m_program;
1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		return; // Skip rest of init.
1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	setupProgram(m_program->getProgram());
1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	setupRenderState();
1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::deinit (void)
1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	delete m_program;
1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_program = DE_NULL;
1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_measurer.deinit();
1703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1723c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::setupProgram (deUint32 program)
1733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	DE_UNREF(program);
1753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1773c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::setupRenderState (void)
1783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1813c827367444ee418f129b2c238299f49d3264554Jarkko PoyryShaderPerformanceCase::IterateResult ShaderPerformanceCase::iterate (void)
1823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	DE_ASSERT(m_program);
1843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (!m_program->isOk()) // This happens when compilation failed in init().
1863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		return STOP;
1873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_measurer.iterate();
1893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (m_measurer.isFinished())
1913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		m_measurer.logMeasurementInfo(m_testCtx.getLog());
1933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if (m_initialCalibration)
1953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			m_initialCalibration->initialNumCalls = de::max(1, m_measurer.getFinalCallCount());
1963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		const ShaderPerformanceMeasurer::Result result = m_measurer.getResult();
1983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		reportResult(result.megaVertPerSec, result.megaFragPerSec);
1993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		return STOP;
2003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
2013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	else
2023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		return CONTINUE;
2033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2053c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCase::reportResult (float mvertPerSecond, float mfragPerSecond)
2063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	float result = 0.0f;
2083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	switch (m_caseType)
2093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
2103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case CASETYPE_VERTEX:	result = mvertPerSecond;	break;
2113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case CASETYPE_FRAGMENT:	result = mfragPerSecond;	break;
2123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case CASETYPE_BALANCED:	result = mfragPerSecond;	break;
2133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		default:
2143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			DE_ASSERT(false);
2153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
2163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(result, 2).c_str());
2183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2203c827367444ee418f129b2c238299f49d3264554Jarkko PoyryShaderPerformanceCaseGroup::ShaderPerformanceCaseGroup (tcu::TestContext& testCtx, const char* name, const char* description)
2213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	: TestCaseGroup					(testCtx, name, description)
2223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	, m_initialCalibrationStorage	(new ShaderPerformanceCase::InitialCalibration)
2233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2263c827367444ee418f129b2c238299f49d3264554Jarkko Poyryvoid ShaderPerformanceCaseGroup::addChild (ShaderPerformanceCase* perfCase)
2273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	perfCase->setCalibrationInitialParamStorage(m_initialCalibrationStorage);
2293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	TestCaseGroup::addChild(perfCase);
2303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} // gls
2333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} // deqp
234