blob: 9c61e11c0fdd03a10c235aa6e49b7bae69156994 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file glcAggressiveShaderOptimizationsTests.cpp
* \brief Conformance tests that checks if shader optimizations are not
* overly aggressive. This is done by compering result of complex
* trigonometric functions aproximation to shader buil
*/ /*-------------------------------------------------------------------*/
#include "glcAggressiveShaderOptimizationsTests.hpp"
#include "deSharedPtr.hpp"
#include "glsShaderExecUtil.hpp"
#include "gluContextInfo.hpp"
#include "gluDrawUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluShaderProgram.hpp"
#include "glwFunctions.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
using namespace glw;
namespace glcts
{
enum ShaderType
{
TEST_VERTEX_SHADER,
TEST_FRAGMENT_SHADER
};
struct TrigonometryCaseData
{
const char* testedFunction;
const char* testedType;
const char* colorComponents;
ShaderType shaderType;
};
class TrigonometryTestCase : public deqp::TestCase
{
public:
TrigonometryTestCase(deqp::Context& context, const std::string& name, const TrigonometryCaseData& data);
virtual ~TrigonometryTestCase();
IterateResult iterate(void);
protected:
glu::ProgramSources prepareSineSources(bool useBuiltin);
glu::ProgramSources prepareCosineSources(bool useBuiltin);
void renderAndGrabSurface(glu::ProgramSources sources, tcu::Surface& result) const;
private:
ShaderType m_shaderType;
const char* m_testedFunction;
std::map<std::string, std::string> m_specializationMap;
};
TrigonometryTestCase::TrigonometryTestCase(deqp::Context& context, const std::string& name,
const TrigonometryCaseData& data)
: deqp::TestCase(context, name.c_str(), ""), m_shaderType(data.shaderType), m_testedFunction(data.testedFunction)
{
glu::ContextType contextType = m_context.getRenderContext().getType();
glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType);
m_specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion);
m_specializationMap["TYPE"] = data.testedType;
m_specializationMap["COLOR_COMPONENTS"] = data.colorComponents;
if (glu::contextSupports(contextType, glu::ApiType::es(3, 0)) || glu::isContextTypeGLCore(contextType))
{
m_specializationMap["IN"] = "in";
m_specializationMap["OUT"] = "out";
m_specializationMap["ATTRIBUTE"] = "in";
m_specializationMap["FS_OUT_COLOR_NAME"] = "fragColor";
m_specializationMap["FS_OUT_COLOR_DECLARATION"] = "out vec4 fragColor;";
}
else
{
m_specializationMap["IN"] = "varying";
m_specializationMap["OUT"] = "varying";
m_specializationMap["ATTRIBUTE"] = "attribute";
m_specializationMap["FS_OUT_COLOR_NAME"] = "gl_FragColor";
m_specializationMap["FS_OUT_COLOR_DECLARATION"] = "";
}
}
TrigonometryTestCase::~TrigonometryTestCase()
{
}
glu::ProgramSources TrigonometryTestCase::prepareSineSources(bool useBuiltinSin)
{
const char* vsDefault = "${VERSION}\n"
"${ATTRIBUTE} highp vec2 position;\n"
"${ATTRIBUTE} highp vec3 baseColor;\n"
"${OUT} vec4 color;\n"
"void main (void) {\n"
" color = vec4(baseColor, 1.0);\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* vsCalculateSin = "${VERSION}\n"
"${ATTRIBUTE} highp vec2 position;\n"
"${ATTRIBUTE} highp vec3 baseColor;\n"
"${OUT} vec4 color;\n"
"${SIN_FUNCTION_DEFINITION_VS}\n"
"void main (void) {\n"
" const float M_2PI = 2.0 * 3.14159265358979323846;\n"
" ${TYPE} c = baseColor.${COLOR_COMPONENTS} * M_2PI;\n"
" ${TYPE} sin_c = ${SIN_FUNCTION_NAME}(c);\n"
" \n"
" color = vec4(0.0, 0.0, 0.0, 1.0);\n"
" color.${COLOR_COMPONENTS} = sin_c * 0.5 + 0.5;\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* fsDefault = "${VERSION}\n"
"precision mediump float;\n"
"${IN} vec4 color;\n"
"${FS_OUT_COLOR_DECLARATION}\n"
"void main (void) {\n"
" ${FS_OUT_COLOR_NAME} = color;\n"
"}\n";
const char* fsCalculateSin = "${VERSION}\n"
"precision mediump float;\n"
"${IN} vec4 color;\n"
"${FS_OUT_COLOR_DECLARATION}\n\n"
"${SIN_FUNCTION_DEFINITION_FS}\n"
"void main (void) {\n"
" const float M_2PI = 2.0 * 3.14159265358979323846;\n"
" ${TYPE} c = color.${COLOR_COMPONENTS};\n"
" ${TYPE} sin_c = ${SIN_FUNCTION_NAME}(c * M_2PI);\n"
" \n"
" ${FS_OUT_COLOR_NAME} =vec4(0.0, 0.0, 0.0, 1.0);\n"
" ${FS_OUT_COLOR_NAME}.${COLOR_COMPONENTS} = sin_c * 0.5 + 0.5;\n"
"}\n";
std::string vsTemplate;
std::string fsTemplate;
if (m_shaderType == TEST_VERTEX_SHADER)
{
vsTemplate = vsCalculateSin;
fsTemplate = fsDefault;
}
else
{
vsTemplate = vsDefault;
fsTemplate = fsCalculateSin;
}
if (useBuiltinSin)
{
m_specializationMap["SIN_FUNCTION_NAME"] = "sin";
m_specializationMap["SIN_FUNCTION_DEFINITION_VS"] = "";
m_specializationMap["SIN_FUNCTION_DEFINITION_FS"] = "";
}
else
{
std::string sinFunctionDefinitionVS = "${TYPE} ${SIN_FUNCTION_NAME}(${TYPE} c) {\n"
" ${TYPE} sin_c = ${TYPE}(0.0);\n"
" float sign = 1.0;\n"
" float fact;\n"
" float fact_of;\n"
" \n"
" // Taylors series expansion for sin \n"
" for(int i = 0; i < 12; i++) {\n"
" fact = 1.0;\n"
" for(int j = 2; j <= 23; j++)\n"
" if (j <= 2 * i + 1)\n"
" fact *= float(j);\n"
" \n"
" sin_c += sign * pow(c, ${TYPE}(2.0 * float(i) + 1.0)) / fact;\n"
" sign *= -1.0;\n"
" }\n"
" return sin_c;\n"
"}";
std::string sinFunctionDefinitionFS = "float lerpHelper(float a, float b, float weight) {\n"
" return a + (b - a) * weight;\n"
"}\n"
"float sinLerpHelper(int index, float weight) {\n"
" float sArray[17];\n"
" sArray[0] = 0.0;\n"
" sArray[1] = 0.382683;\n"
" sArray[2] = 0.707107;\n"
" sArray[3] = 0.92388;\n"
" sArray[4] = 1.0;\n"
" sArray[5] = 0.92388;\n"
" sArray[6] = 0.707107;\n"
" sArray[7] = 0.382683;\n"
" sArray[8] = 0.0;\n"
" sArray[9] = -0.382683;\n"
" sArray[10] = -0.707107;\n"
" sArray[11] = -0.92388;\n"
" sArray[12] = -1.0;\n"
" sArray[13] = -0.923879;\n"
" sArray[14] = -0.707107;\n"
" sArray[15] = -0.382683;\n"
" sArray[16] = 0.0;\n"
" \n"
" if (index == 0)\n"
" return lerpHelper(sArray[0], sArray[1], weight);\n"
" if (index == 1)\n"
" return lerpHelper(sArray[1], sArray[2], weight);\n"
" if (index == 2)\n"
" return lerpHelper(sArray[2], sArray[3], weight);\n"
" if (index == 3)\n"
" return lerpHelper(sArray[3], sArray[4], weight);\n"
" if (index == 4)\n"
" return lerpHelper(sArray[4], sArray[5], weight);\n"
" if (index == 5)\n"
" return lerpHelper(sArray[5], sArray[6], weight);\n"
" if (index == 6)\n"
" return lerpHelper(sArray[6], sArray[7], weight);\n"
" if (index == 7)\n"
" return lerpHelper(sArray[7], sArray[8], weight);\n"
" if (index == 8)\n"
" return lerpHelper(sArray[8], sArray[9], weight);\n"
" if (index == 9)\n"
" return lerpHelper(sArray[9], sArray[10], weight);\n"
" if (index == 10)\n"
" return lerpHelper(sArray[10], sArray[11], weight);\n"
" if (index == 11)\n"
" return lerpHelper(sArray[11], sArray[12], weight);\n"
" if (index == 12)\n"
" return lerpHelper(sArray[12], sArray[13], weight);\n"
" if (index == 13)\n"
" return lerpHelper(sArray[13], sArray[14], weight);\n"
" if (index == 14)\n"
" return lerpHelper(sArray[14], sArray[15], weight);\n"
" if (index == 15)\n"
" return lerpHelper(sArray[15], sArray[16], weight);\n"
" return sArray[16];\n"
"}\n"
"${TYPE} ${SIN_FUNCTION_NAME}(${TYPE} c) {\n"
" ${TYPE} arrVal = c * 2.546478971;\n"
" ${TYPE} weight = arrVal - floor(arrVal);\n"
" ${TYPE} sin_c = ${TYPE}(0.0);\n"
" ${INTERPOLATE_SIN}"
" return sin_c;\n"
"}";
if (m_specializationMap["TYPE"] == "float")
{
m_specializationMap["INTERPOLATE_SIN"] = "\n"
" int index = int(floor(arrVal));\n"
" sin_c = sinLerpHelper(index, weight);\n";
}
else if (m_specializationMap["TYPE"] == "vec2")
{
m_specializationMap["INTERPOLATE_SIN"] = "\n"
" int indexX = int(floor(arrVal.x));\n"
" sin_c.x = sinLerpHelper(indexX, weight.x);\n"
" int indexY = int(floor(arrVal.y));\n"
" sin_c.y = sinLerpHelper(indexY, weight.y);\n";
}
else if (m_specializationMap["TYPE"] == "vec3")
{
m_specializationMap["INTERPOLATE_SIN"] = "\n"
" int indexX = int(floor(arrVal.x));\n"
" sin_c.x = sinLerpHelper(indexX, weight.x);\n"
" int indexY = int(floor(arrVal.y));\n"
" sin_c.y = sinLerpHelper(indexY, weight.y);\n"
" int indexZ = int(floor(arrVal.z));\n"
" sin_c.z = sinLerpHelper(indexZ, weight.z);\n";
}
m_specializationMap["SIN_FUNCTION_NAME"] = "calculateSin";
m_specializationMap["SIN_FUNCTION_DEFINITION_VS"] =
tcu::StringTemplate(sinFunctionDefinitionVS).specialize(m_specializationMap);
m_specializationMap["SIN_FUNCTION_DEFINITION_FS"] =
tcu::StringTemplate(sinFunctionDefinitionFS).specialize(m_specializationMap);
}
// Specialize shader templates
vsTemplate = tcu::StringTemplate(vsTemplate).specialize(m_specializationMap);
fsTemplate = tcu::StringTemplate(fsTemplate).specialize(m_specializationMap);
return glu::makeVtxFragSources(vsTemplate.c_str(), fsTemplate.c_str());
}
glu::ProgramSources TrigonometryTestCase::prepareCosineSources(bool useBuiltinCos)
{
const char* vsDefault = "${VERSION}\n"
"${ATTRIBUTE} highp vec2 position;\n"
"${ATTRIBUTE} highp vec3 baseColor;\n"
"${OUT} vec4 color;\n"
"void main (void) {\n"
" color = vec4(baseColor, 1.0);\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* vsCalculateCos = "${VERSION}\n"
"${ATTRIBUTE} highp vec2 position;\n"
"${ATTRIBUTE} highp vec3 baseColor;\n"
"${OUT} vec4 color;\n"
"${COS_FUNCTION_DEFINITION_VS}\n"
"void main (void) {\n"
" const float M_2PI = 2.0 * 3.14159265358979323846;\n"
" ${TYPE} c = baseColor.${COLOR_COMPONENTS};\n"
" ${TYPE} cos_c = ${COS_FUNCTION_NAME}(c * M_2PI);\n"
" \n"
" color = vec4(0.0, 0.0, 0.0, 1.0);\n"
" color.${COLOR_COMPONENTS} = cos_c * 0.5 + 0.5;\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* fsDefault = "${VERSION}\n"
"precision mediump float;\n"
"${IN} vec4 color;\n"
"${FS_OUT_COLOR_DECLARATION}\n"
"void main (void) {\n"
" ${FS_OUT_COLOR_NAME} = color;\n"
"}\n";
const char* fsCalculateCos = "${VERSION}\n"
"precision mediump float;\n"
"${IN} vec4 color;\n"
"${FS_OUT_COLOR_DECLARATION}\n\n"
"// function definitions \n"
"${COS_FUNCTION_DEFINITION_FS}\n"
"${TYPE} preprocessColor(${TYPE} c) {\n"
" ${PREPROCESS_COLOR};\n"
" return c;\n"
"}\n\n"
"void main (void) {\n"
" const float M_2PI = 2.0 * 3.14159265358979323846;\n"
" ${TYPE} c = preprocessColor(color.${COLOR_COMPONENTS});\n"
" ${TYPE} cos_c = ${COS_FUNCTION_NAME}(c * M_2PI);\n"
" \n"
" ${FS_OUT_COLOR_NAME} = vec4(0.0, 0.0, 0.0, 1.0);\n"
" ${FS_OUT_COLOR_NAME}.${COLOR_COMPONENTS} = cos_c * 0.5 + 0.5;\n"
"}\n";
std::string vsTemplate;
std::string fsTemplate;
if (m_shaderType == TEST_VERTEX_SHADER)
{
vsTemplate = vsCalculateCos;
fsTemplate = fsDefault;
}
else
{
vsTemplate = vsDefault;
fsTemplate = fsCalculateCos;
}
if (useBuiltinCos)
{
m_specializationMap["PREPROCESS_COLOR"] = "";
m_specializationMap["COS_FUNCTION_NAME"] = "cos";
m_specializationMap["COS_FUNCTION_DEFINITION_VS"] = "";
m_specializationMap["COS_FUNCTION_DEFINITION_FS"] = "";
}
else
{
std::string cosFunctionDefinitionVS = "${TYPE} ${COS_FUNCTION_NAME}(${TYPE} c) {\n"
" ${TYPE} cos_c = ${TYPE}(1.0);\n"
" float sign = -1.0;\n"
" float fact = 1.0;\n"
" \n"
" for(int i = 2; i <= 20; i += 2) {\n"
" fact *= float(i)*float(i-1);\n"
" cos_c += sign*pow(c, ${TYPE}(float(i)))/fact;\n"
" sign = -sign;\n"
" }\n"
" return cos_c;\n"
"}";
std::string cosFunctionDefinitionFS = "${TYPE} ${COS_FUNCTION_NAME}(${TYPE} c) {\n"
" ${TYPE} cos_c = ${TYPE}(-1.0);\n"
" float sign = 1.0;\n"
" float fact_even = 1.0;\n"
" float fact_odd = 1.0;\n"
" ${TYPE} sum;\n"
" ${TYPE} exp;\n"
" \n"
" for(int i = 2; i <= 10; i += 2) {\n"
" fact_even *= float(i);\n"
" fact_odd *= float(i-1);\n"
" exp = ${TYPE}(float(i/2));\n"
" sum = sign * pow(abs(c), exp)/fact_even;\n"
" cos_c += pow(abs(c), exp)*(sum/fact_odd);\n"
" sign = -sign;\n"
" }\n"
" return cos_c;\n"
"}";
m_specializationMap["PREPROCESS_COLOR"] = "c = (fract(abs(c)) - 0.5)";
m_specializationMap["COS_FUNCTION_NAME"] = "calculateCos";
m_specializationMap["COS_FUNCTION_DEFINITION_VS"] =
tcu::StringTemplate(cosFunctionDefinitionVS).specialize(m_specializationMap);
m_specializationMap["COS_FUNCTION_DEFINITION_FS"] =
tcu::StringTemplate(cosFunctionDefinitionFS).specialize(m_specializationMap);
}
// Specialize shader templates
vsTemplate = tcu::StringTemplate(vsTemplate).specialize(m_specializationMap);
fsTemplate = tcu::StringTemplate(fsTemplate).specialize(m_specializationMap);
return glu::makeVtxFragSources(vsTemplate.c_str(), fsTemplate.c_str());
}
void TrigonometryTestCase::renderAndGrabSurface(glu::ProgramSources sources, tcu::Surface& result) const
{
static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
static const float positions[] = { -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 };
static const float baseColors[] = { 1.0, 0.0, 0.25, 0.75, 0.25, 1.0, 0.0, 1.0, 0.75, 0.25, 0.5, 0.0 };
glu::RenderContext& renderContext = m_context.getRenderContext();
const glw::Functions& gl = renderContext.getFunctions();
glu::ShaderProgram testProgram(renderContext, sources);
if (!testProgram.isOk())
{
m_testCtx.getLog() << testProgram;
TCU_FAIL("Test program compilation failed");
}
// Render
gl.useProgram(testProgram.getProgram());
const glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("position", 2, 4, 0, positions),
glu::va::Float("baseColor", 3, 4, 0, baseColors) };
glu::draw(renderContext, testProgram.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices));
// Grab surface
glu::readPixels(renderContext, 0, 0, result.getAccess());
}
tcu::TestNode::IterateResult TrigonometryTestCase::iterate(void)
{
glu::RenderContext& renderContext = m_context.getRenderContext();
const glw::Functions& gl = renderContext.getFunctions();
int renderWidth = 64;
int renderHeight = 64;
if (renderWidth > m_context.getRenderTarget().getWidth())
renderWidth = m_context.getRenderTarget().getWidth();
if (renderHeight > m_context.getRenderTarget().getHeight())
renderHeight = m_context.getRenderTarget().getHeight();
bool isSin = std::string(m_testedFunction) == "sin";
gl.viewport(0, 0, renderWidth, renderHeight);
// Use program that will call trigonometric function aproximation
tcu::Surface testSurface(renderWidth, renderHeight);
if (isSin)
renderAndGrabSurface(prepareSineSources(false), testSurface);
else
renderAndGrabSurface(prepareCosineSources(false), testSurface);
// Use reference program that will call builtin function
tcu::Surface referenceSurface(renderWidth, renderHeight);
if (isSin)
renderAndGrabSurface(prepareSineSources(true), referenceSurface);
else
renderAndGrabSurface(prepareCosineSources(true), referenceSurface);
// Compare surfaces
qpTestResult testResult = QP_TEST_RESULT_FAIL;
if (tcu::fuzzyCompare(m_testCtx.getLog(), "Result", "Image comparison result", referenceSurface, testSurface, 0.05f,
tcu::COMPARE_LOG_RESULT))
testResult = QP_TEST_RESULT_PASS;
m_testCtx.setTestResult(testResult, qpGetTestResultName(testResult));
return STOP;
}
AggressiveShaderOptimizationsTests::AggressiveShaderOptimizationsTests(deqp::Context& context)
: TestCaseGroup(context, "aggressive_optimizations", "checks if shader optimizations are not overly aggressive")
{
}
AggressiveShaderOptimizationsTests::~AggressiveShaderOptimizationsTests()
{
}
void AggressiveShaderOptimizationsTests::init(void)
{
TrigonometryCaseData trigonometryCases[] = {
{ "sin", "float", "r", TEST_VERTEX_SHADER }, { "sin", "float", "r", TEST_FRAGMENT_SHADER },
{ "sin", "vec2", "rg", TEST_VERTEX_SHADER }, { "sin", "vec2", "rg", TEST_FRAGMENT_SHADER },
{ "sin", "vec3", "rgb", TEST_VERTEX_SHADER }, { "sin", "vec3", "rgb", TEST_FRAGMENT_SHADER },
{ "cos", "float", "r", TEST_VERTEX_SHADER }, { "cos", "float", "r", TEST_FRAGMENT_SHADER },
{ "cos", "vec2", "rg", TEST_VERTEX_SHADER }, { "cos", "vec2", "rg", TEST_FRAGMENT_SHADER },
{ "cos", "vec3", "rgb", TEST_VERTEX_SHADER }, { "cos", "vec3", "rgb", TEST_FRAGMENT_SHADER },
};
for (int i = 0; i < DE_LENGTH_OF_ARRAY(trigonometryCases); ++i)
{
const TrigonometryCaseData& tcd = trigonometryCases[i];
std::string shaderType = (tcd.shaderType == TEST_VERTEX_SHADER) ? "_vert" : "_frag";
std::string name = std::string(tcd.testedFunction) + "_" + tcd.testedType + shaderType;
addChild(new TrigonometryTestCase(m_context, name, tcd));
}
}
} // glcts namespace