1 #ifndef _ESEXTCGPUSHADER5FMAACCURACY_HPP 2 #define _ESEXTCGPUSHADER5FMAACCURACY_HPP 3 /*------------------------------------------------------------------------- 4 * OpenGL Conformance Test Suite 5 * ----------------------------- 6 * 7 * Copyright (c) 2014-2016 The Khronos Group Inc. 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 */ /*! 22 * \file 23 * \brief 24 */ /*-------------------------------------------------------------------*/ 25 26 /*! 27 * \file esextcGPUShader5FmaAccuracy.hpp 28 * \brief gpu_shader5 extenstion - fma accuracy test (Test 7) 29 */ /*-------------------------------------------------------------------*/ 30 31 #include "../esextcTestCaseBase.hpp" 32 33 namespace glcts 34 { 35 /** Implementation of "Test 7" from CTS_EXT_gpu_shader5. Test description follows: 36 * 37 * Check the accuracy of the function fma() in comparison to a*b + c. 38 * The fma() should be at least as accurate as a*b+c. 39 * 40 * Category: API, 41 * Functional Test. 42 * 43 * Write two vertex shaders that using Euler method compute the approximate 44 * value of y(1) for a differential equation y'(t) = y(t) with 45 * border case y(0) = 1. First shader should use the fma function and 46 * the second one standard "a*b + c" expression in each iteration of 47 * the Euler method. Both shaders should store the result in 48 * out float Result variable. 49 * 50 * Explanation: 51 * 52 * For a differential equation y'(t) = y(t) with edge case y(0) = 1 53 * the exact solution is y(t) = et . It means that for y(0) we get 1 54 * and for y(1) we get e ~ 2.71828. 55 * 56 * Sometimes we can't solve differential equation for exact solution 57 * (get the actual y function), but we can still get an approximate value of 58 * y(t) for a chosen t, by solving the equation numerically. The simplest 59 * numerical method that can be applied to the problem is called Euler method. 60 * 61 * This method start from the border case y(0) = 1 (t0 = 0, y0 = 1) and 62 * in n sub steps converges slowly to the value of y(1) (tn = 1, yn = ?). 63 * It does that by computing in a loop the equation 64 * y(tx+1) = y(tx) + 1/n * y(tx) for x = 0..n-1. 65 * After the last iteration the value of y(tn) is the approximate value of 66 * y(1) and should be close to e ~ 2.71828. The Euler method not always 67 * converges to the solution but for the differential equation y'(t) = y(t) 68 * it should converge without any problems. 69 * 70 * The equation y(tx+1) = y(tx) + 1/n * y(tx) is ideal to be implemented 71 * using fma function in the following way: 72 * 73 * y(tx+1) = fma(1/n, y(tx), y(tx)). 74 * 75 * The shaders should be configurable by a uint uniform variable n 76 * in the number of subintervals the interval [0,1] is divided into 77 * while computing the value of y(1). 78 * 79 * Write a boilerplate fragment shader. 80 * 81 * Create a program from the first vertex shader and fragment shader and 82 * a second program from the second vertex shader and fragment shader. 83 * 84 * Use the first program. 85 * 86 * Configure transform feedback to capture the value of Result. 87 * 88 * Execute a draw call glDrawArrays(GL_POINTS, 0, 1) five times in a row. 89 * Before each execution, double the value of n (n should first be 90 * set to 10). 91 * 92 * Copy the captured results from the buffer object bound to transform 93 * feedback binding point to resultsFmaArray. 94 * 95 * Use the second program. 96 * 97 * Configure transform feedback to capture the value of Result. 98 * 99 * Execute a draw call glDrawArrays(GL_POINTS, 0, 1) 10 times in a row. 100 * Before each execution double the value of n (n should first be set 101 * to 10). 102 * 103 * Copy the captured results from the buffer object bound to transform 104 * feedback to resultsNotFmaArray. 105 * 106 * For each of the values stored in the array resultsFmaArray compute 107 * a relative error of the value with correspondence to a reference value 108 * of y(1) which is e ~ 2.71828. Sum up those relative errors to a variable 109 * precise float totalRelativeErrorFma. 110 * 111 * Do the same for the array resultsNotFmaArray, this time storing the sum 112 * in precise float totalRelativeErrorNotFma. 113 * 114 * The test passes if the absolute value of the totalRelativeErrorFma 115 * is smaller or equal to totalRelativeErrorNotFma. 116 **/ 117 class GPUShader5FmaAccuracyTest : public TestCaseBase 118 { 119 public: 120 /* Public methods */ 121 GPUShader5FmaAccuracyTest(Context& context, const ExtParameters& extParams, const char* name, 122 const char* description); 123 ~GPUShader5FmaAccuracyTest(void)124 virtual ~GPUShader5FmaAccuracyTest(void) 125 { 126 } 127 128 virtual void deinit(void); 129 virtual IterateResult iterate(void); 130 131 private: 132 /* Private methods */ 133 void calculateRelativeError(glw::GLfloat result, glw::GLfloat expected_result, glw::GLfloat& relative_error); 134 void executePass(glw::GLuint program_object_id, glw::GLfloat* results); 135 glw::GLuint getNumberOfStepsForIndex(glw::GLuint index); 136 void initTest(void); 137 void logArray(const char* description, glw::GLfloat* data, glw::GLuint length); 138 139 /* Private variables */ 140 /* Program and shader ids */ 141 glw::GLuint m_fragment_shader_id; 142 glw::GLuint m_program_object_id_for_float_pass; 143 glw::GLuint m_program_object_id_for_fma_pass; 144 glw::GLuint m_vertex_shader_id_for_float_pass; 145 glw::GLuint m_vertex_shader_id_for_fma_pass; 146 147 /* Buffer object used for transform feedback */ 148 glw::GLuint m_buffer_object_id; 149 150 /* Vertex array object */ 151 glw::GLuint m_vertex_array_object_id; 152 153 /* Size of buffer used for transform feedback */ 154 static const glw::GLuint m_buffer_size; 155 156 /* Expected solution */ 157 static const glw::GLfloat m_expected_result; 158 159 /* Number of draw call executions */ 160 static const glw::GLuint m_n_draw_call_executions; 161 162 /* Shaders' code */ 163 static const glw::GLchar* const m_fragment_shader_code; 164 static const glw::GLchar* const m_vertex_shader_code_for_fma_pass; 165 static const glw::GLchar* const m_vertex_shader_code_for_float_pass; 166 }; 167 168 } /* glcts */ 169 170 #endif // _ESEXTCGPUSHADER5FMAACCURACY_HPP 171