1/*------------------------------------------------------------------------- 2 * OpenGL Conformance Test Suite 3 * ----------------------------- 4 * 5 * Copyright (c) 2017 The Khronos Group Inc. 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 glcLimitTest.cpp 21 * \brief Definition of template class. 22 */ /*-------------------------------------------------------------------*/ 23 24using namespace glw; 25 26template<typename DataType> 27LimitCase<DataType>::LimitCase(deqp::Context& context, 28 const char* caseName, 29 deUint32 limitToken, 30 DataType limitBoundary, 31 bool isBoundaryMaximum, 32 const char* glslVersion, 33 const char* glslBuiltin, 34 const char* glslExtension) 35 : deqp::TestCase(context, caseName, "Token limit validation.") 36 , m_limitToken(limitToken) 37 , m_limitBoundary(limitBoundary) 38 , m_isBoundaryMaximum(isBoundaryMaximum) 39 , m_glslVersion(glslVersion) 40 , m_glslBuiltin(glslBuiltin) 41 , m_glslExtension(glslExtension) 42{ 43 // GL_MAX_FRAGMENT_INTERPOLATION_OFFSET is special in that its limit is dependent on 44 // GL_FRAGMENT_INTERPOLATION_OFFSET_BITS. Adjust the limit automatically here. 45 adjustBoundaryForMaxFragmentInterpolationOffset(); 46} 47 48template<typename DataType> 49LimitCase<DataType>::~LimitCase(void) 50{ 51} 52 53template<typename DataType> 54void LimitCase<DataType>::adjustBoundaryForMaxFragmentInterpolationOffset() 55{ 56} 57 58template<> 59void LimitCase<GLfloat>::adjustBoundaryForMaxFragmentInterpolationOffset() 60{ 61 if (m_limitToken == GL_MAX_FRAGMENT_INTERPOLATION_OFFSET) 62 { 63 const Functions& gl = m_context.getRenderContext().getFunctions(); 64 65 GLfloat fragmentInterpolationOffsetBits = 0; 66 gl.getFloatv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &fragmentInterpolationOffsetBits); 67 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv"); 68 69 GLfloat ULP = 1.0f / powf(2, fragmentInterpolationOffsetBits); 70 m_limitBoundary -= ULP; 71 } 72} 73 74template<typename DataType> 75tcu::TestNode::IterateResult LimitCase<DataType>::iterate(void) 76{ 77 DataType limitValue = DataType(); 78 const Functions& gl = m_context.getRenderContext().getFunctions(); 79 80 // make sure that limit or builtin was specified 81 DE_ASSERT(m_limitToken || !m_glslBuiltin.empty()); 82 83 // check if limit was specified 84 if (m_limitToken) 85 { 86 // check if limit is not smaller or greater then boundary defined in specification 87 limitValue = getLimitValue(gl); 88 if (!isWithinBoundary(limitValue)) 89 { 90 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 91 return STOP; 92 } 93 94 // if glsl builtin wasn't defined then test already passed 95 if (m_glslBuiltin.empty()) 96 { 97 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 98 return STOP; 99 } 100 } 101 102 // create compute shader to check glsl builtin 103 std::string shaderSource = createShader(); 104 const GLchar* source = shaderSource.c_str(); 105 const GLuint program = gl.createProgram(); 106 GLuint shader = gl.createShader(GL_COMPUTE_SHADER); 107 gl.attachShader(program, shader); 108 gl.deleteShader(shader); 109 gl.shaderSource(shader, 1, &source, NULL); 110 gl.compileShader(shader); 111 gl.linkProgram(program); 112 113 GLint status; 114 gl.getProgramiv(program, GL_LINK_STATUS, &status); 115 GLint length; 116 gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &length); 117 if (length > 1) 118 { 119 std::vector<GLchar> log(length); 120 gl.getProgramInfoLog(program, length, NULL, &log[0]); 121 m_testCtx.getLog() << tcu::TestLog::Message 122 << &log[0] 123 << tcu::TestLog::EndMessage; 124 } 125 if (status == GL_FALSE) 126 { 127 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 128 return STOP; 129 } 130 131 gl.useProgram(program); 132 133 GLuint buffer; 134 gl.genBuffers(1, &buffer); 135 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer); 136 gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(DataType), NULL, GL_DYNAMIC_DRAW); 137 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 138 139 gl.dispatchCompute(1, 1, 1); 140 141 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); 142 gl.memoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); 143 DataType* data = static_cast<DataType*>(gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(DataType), GL_MAP_READ_BIT)); 144 DataType builtinValue = data[0]; 145 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER); 146 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 147 148 if (m_limitToken) 149 { 150 // limit token was specified - compare builtin to it 151 if (isEqual(limitValue, builtinValue)) 152 { 153 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 154 } 155 else 156 { 157 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 158 m_testCtx.getLog() << tcu::TestLog::Message 159 << "Shader builtin has value: " 160 << builtinValue 161 << " which is different from the value of corresponding limit: " 162 << limitValue 163 << tcu::TestLog::EndMessage; 164 } 165 } 166 else 167 { 168 // limit token was not specified - compare builtin to the boundary 169 if (isWithinBoundary(builtinValue, true)) 170 { 171 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 172 } 173 else 174 { 175 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 176 m_testCtx.getLog() << tcu::TestLog::Message 177 << "Shader builtin value is: " 178 << builtinValue 179 << " which is outside of specified boundary." 180 << tcu::TestLog::EndMessage; 181 } 182 } 183 184 return STOP; 185} 186 187template<typename DataType> 188bool LimitCase<DataType>::isWithinBoundary(DataType value, bool isBuiltin) const 189{ 190 if (m_isBoundaryMaximum) 191 { 192 // value should be smaller or euqual to boundary 193 if (isGreater(value, m_limitBoundary)) 194 { 195 m_testCtx.getLog() << tcu::TestLog::Message 196 << (isBuiltin ? "Builtin" : "Limit") 197 << " value is: " 198 << value 199 << " when it should not be greater than " 200 << m_limitBoundary 201 << tcu::TestLog::EndMessage; 202 return false; 203 } 204 } 205 else 206 { 207 // value should be greater or euqual to boundary 208 if (isSmaller(value, m_limitBoundary)) 209 { 210 m_testCtx.getLog() << tcu::TestLog::Message 211 << (isBuiltin ? "Builtin" : "Limit") 212 << " value is: " 213 << value 214 << "when it should not be smaller than " 215 << m_limitBoundary 216 << tcu::TestLog::EndMessage; 217 return false; 218 } 219 } 220 221 return true; 222} 223 224template<typename DataType> 225std::string LimitCase<DataType>::createShader() const 226{ 227 std::stringstream shader; 228 shader << "#version " << m_glslVersion << "\n"; 229 if (!m_glslExtension.empty()) 230 shader << "#extension " + m_glslExtension + " : require\n"; 231 shader << "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" 232 "layout(std430) buffer Output {\n" 233 << getGLSLDataType() <<" data; } g_out;" 234 "void main() { " 235 "g_out.data = " << m_glslBuiltin << "; }"; 236 return shader.str(); 237} 238 239template<typename DataType> 240std::string LimitCase<DataType>::getGLSLDataType() const 241{ 242 return "int"; 243} 244 245template<> 246std::string LimitCase<GLfloat>::getGLSLDataType() const 247{ 248 return "float"; 249} 250 251template<> 252std::string LimitCase<tcu::IVec3>::getGLSLDataType() const 253{ 254 return "ivec3"; 255} 256 257template<typename DataType> 258bool LimitCase<DataType>::isEqual(DataType a, DataType b) const 259{ 260 return a == b; 261} 262 263template<> 264bool LimitCase<tcu::IVec3>::isEqual(tcu::IVec3 a, tcu::IVec3 b) const 265{ 266 tcu::BVec3 bVec = tcu::equal(a, b); 267 return tcu::boolAll(bVec); 268} 269 270template<typename DataType> 271bool LimitCase<DataType>::isGreater(DataType a, DataType b) const 272{ 273 return a > b; 274} 275 276template<> 277bool LimitCase<tcu::IVec3>::isGreater(tcu::IVec3 a, tcu::IVec3 b) const 278{ 279 tcu::BVec3 bVec = tcu::greaterThan(a, b); 280 return tcu::boolAll(bVec); 281} 282 283template<typename DataType> 284bool LimitCase<DataType>::isSmaller(DataType a, DataType b) const 285{ 286 return a < b; 287} 288 289template<> 290bool LimitCase<tcu::IVec3>::isSmaller(tcu::IVec3 a, tcu::IVec3 b) const 291{ 292 tcu::BVec3 bVec = tcu::lessThan(a, b); 293 return tcu::boolAll(bVec); 294} 295 296template<typename DataType> 297DataType LimitCase<DataType>::getLimitValue(const Functions&) const 298{ 299 return DataType(); 300} 301 302template<> 303GLint LimitCase<GLint>::getLimitValue(const Functions& gl) const 304{ 305 GLint value = -1; 306 gl.getIntegerv(m_limitToken, &value); 307 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv"); 308 return value; 309} 310 311template<> 312GLint64 LimitCase<GLint64>::getLimitValue(const Functions& gl) const 313{ 314 GLint64 value = -1; 315 gl.getInteger64v(m_limitToken, &value); 316 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v"); 317 return value; 318} 319 320template<> 321GLuint64 LimitCase<GLuint64>::getLimitValue(const Functions& gl) const 322{ 323 GLint64 value = -1; 324 gl.getInteger64v(m_limitToken, &value); 325 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v"); 326 return static_cast<GLuint64>(value); 327} 328 329template<> 330GLfloat LimitCase<GLfloat>::getLimitValue(const Functions& gl) const 331{ 332 GLfloat value = -1; 333 gl.getFloatv(m_limitToken, &value); 334 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv"); 335 return value; 336} 337 338template<> 339tcu::IVec3 LimitCase<tcu::IVec3>::getLimitValue(const Functions& gl) const 340{ 341 tcu::IVec3 value(-1); 342 for (int i = 0; i < 3; i++) 343 gl.getIntegeri_v(m_limitToken, (GLuint)i, &value[i]); 344 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegeri_v"); 345 return value; 346} 347 348