1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 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 GLSL ES 1.0 gl_FragData[] tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es3fShaderFragDataTests.hpp"
25
26 #include "glsShaderLibrary.hpp"
27
28 #include "gluRenderContext.hpp"
29 #include "gluShaderProgram.hpp"
30 #include "gluDrawUtil.hpp"
31 #include "gluPixelTransfer.hpp"
32 #include "gluObjectWrapper.hpp"
33
34 #include "tcuRenderTarget.hpp"
35 #include "tcuStringTemplate.hpp"
36 #include "tcuTestLog.hpp"
37 #include "tcuSurface.hpp"
38
39 #include "glwFunctions.hpp"
40 #include "glwEnums.hpp"
41
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Functional
47 {
48
49 using std::string;
50 using tcu::TestLog;
51
52 namespace
53 {
54
55 enum IndexExprType
56 {
57 INDEX_EXPR_STATIC = 0,
58 INDEX_EXPR_UNIFORM,
59 INDEX_EXPR_DYNAMIC,
60
61 INDEX_EXPR_TYPE_LAST
62 };
63
isExtensionSupported(const glu::RenderContext & renderCtx,const std::string & extension)64 static bool isExtensionSupported(const glu::RenderContext &renderCtx, const std::string &extension)
65 {
66 const glw::Functions &gl = renderCtx.getFunctions();
67 int numExts = 0;
68
69 gl.getIntegerv(GL_NUM_EXTENSIONS, &numExts);
70
71 for (int ndx = 0; ndx < numExts; ndx++)
72 {
73 const char *curExt = (const char *)gl.getStringi(GL_EXTENSIONS, ndx);
74
75 if (extension == curExt)
76 return true;
77 }
78
79 return false;
80 }
81
compareSingleColor(tcu::TestLog & log,const tcu::Surface & surface,tcu::RGBA expectedColor,tcu::RGBA threshold)82 static bool compareSingleColor(tcu::TestLog &log, const tcu::Surface &surface, tcu::RGBA expectedColor,
83 tcu::RGBA threshold)
84 {
85 const int maxPrints = 10;
86 int numFailedPixels = 0;
87
88 log << TestLog::Message << "Expecting " << expectedColor << " with threshold " << threshold << TestLog::EndMessage;
89
90 for (int y = 0; y < surface.getHeight(); y++)
91 {
92 for (int x = 0; x < surface.getWidth(); x++)
93 {
94 const tcu::RGBA resultColor = surface.getPixel(x, y);
95 const bool isOk = compareThreshold(resultColor, expectedColor, threshold);
96
97 if (!isOk)
98 {
99 if (numFailedPixels < maxPrints)
100 log << TestLog::Message << "ERROR: Got " << resultColor << " at (" << x << ", " << y << ")!"
101 << TestLog::EndMessage;
102 else if (numFailedPixels == maxPrints)
103 log << TestLog::Message << "..." << TestLog::EndMessage;
104
105 numFailedPixels += 1;
106 }
107 }
108 }
109
110 if (numFailedPixels > 0)
111 {
112 log << TestLog::Message << "Found " << numFailedPixels << " invalid pixels, comparison FAILED!"
113 << TestLog::EndMessage;
114 log << TestLog::Image("ResultImage", "Result Image", surface);
115 return false;
116 }
117 else
118 {
119 log << TestLog::Message << "Image comparison passed." << TestLog::EndMessage;
120 return true;
121 }
122 }
123
124 class FragDataIndexingCase : public TestCase
125 {
126 public:
FragDataIndexingCase(Context & context,const char * name,const char * description,IndexExprType indexExprType)127 FragDataIndexingCase(Context &context, const char *name, const char *description, IndexExprType indexExprType)
128 : TestCase(context, name, description)
129 , m_indexExprType(indexExprType)
130 {
131 }
132
genSources(const IndexExprType indexExprType)133 static glu::ProgramSources genSources(const IndexExprType indexExprType)
134 {
135 const char *const fragIndexExpr = indexExprType == INDEX_EXPR_STATIC ? "0" :
136 indexExprType == INDEX_EXPR_UNIFORM ? "u_index" :
137 indexExprType == INDEX_EXPR_DYNAMIC ? "int(v_index)" :
138 DE_NULL;
139 glu::ProgramSources sources;
140
141 DE_ASSERT(fragIndexExpr);
142
143 sources << glu::VertexSource("attribute highp vec4 a_position;\n"
144 "attribute highp float a_index;\n"
145 "attribute highp vec4 a_color;\n"
146 "varying mediump float v_index;\n"
147 "varying mediump vec4 v_color;\n"
148 "void main (void)\n"
149 "{\n"
150 " gl_Position = a_position;\n"
151 " v_color = a_color;\n"
152 " v_index = a_index;\n"
153 "}\n");
154
155 sources << glu::FragmentSource(string("varying mediump vec4 v_color;\n"
156 "varying mediump float v_index;\n"
157 "uniform mediump int u_index;\n"
158 "void main (void)\n"
159 "{\n"
160 " gl_FragData[") +
161 fragIndexExpr +
162 "] = v_color;\n"
163 "}\n");
164
165 return sources;
166 }
167
iterate(void)168 IterateResult iterate(void)
169 {
170 const glu::RenderContext &renderCtx = m_context.getRenderContext();
171 const glw::Functions &gl = renderCtx.getFunctions();
172 const glu::ShaderProgram program(renderCtx, genSources(m_indexExprType));
173 const int viewportW = de::min(renderCtx.getRenderTarget().getWidth(), 128);
174 const int viewportH = de::min(renderCtx.getRenderTarget().getHeight(), 128);
175
176 const float positions[] = {-1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f};
177 const float colors[] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
178 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
179 const float indexValues[] = {0.0f, 0.0f, 0.0f, 0.0f};
180 const uint8_t indices[] = {0, 1, 2, 2, 1, 3};
181
182 const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 2, 4, 0, &positions[0]),
183 glu::va::Float("a_color", 4, 4, 0, &colors[0]),
184 glu::va::Float("a_index", 1, 4, 0, &indexValues[0])};
185
186 m_testCtx.getLog() << program;
187
188 if (!program.isOk())
189 {
190 if (m_indexExprType == INDEX_EXPR_STATIC)
191 TCU_FAIL("Compile failed");
192 else
193 throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
194 }
195
196 gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
197 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
198
199 gl.viewport(0, 0, viewportW, viewportH);
200 gl.useProgram(program.getProgram());
201 gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_index"), 0);
202
203 glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
204 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
205 GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
206
207 {
208 tcu::Surface result(viewportW, viewportH);
209 const tcu::RGBA threshold =
210 renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1, 1, 1, 1);
211 bool isOk;
212
213 glu::readPixels(renderCtx, 0, 0, result.getAccess());
214 GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
215
216 isOk = compareSingleColor(m_testCtx.getLog(), result, tcu::RGBA::green(), threshold);
217
218 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
219 isOk ? "Pass" : "Image comparison failed");
220 }
221
222 return STOP;
223 }
224
225 private:
226 const IndexExprType m_indexExprType;
227 };
228
229 class FragDataDrawBuffersCase : public TestCase
230 {
231 public:
FragDataDrawBuffersCase(Context & context)232 FragDataDrawBuffersCase(Context &context)
233 : TestCase(context, "draw_buffers", "gl_FragData[] and glDrawBuffers() interaction")
234 {
235 }
236
iterate(void)237 IterateResult iterate(void)
238 {
239 const glu::RenderContext &renderCtx = m_context.getRenderContext();
240
241 int num_test_attachment = 2;
242 if (!isExtensionSupported(renderCtx, "GL_EXT_draw_buffers") &&
243 !isExtensionSupported(renderCtx, "GL_NV_draw_buffers"))
244 num_test_attachment = 1;
245
246 std::string extensionString;
247 if (isExtensionSupported(renderCtx, "GL_EXT_draw_buffers"))
248 extensionString = "#extension GL_EXT_draw_buffers : require\n";
249 else if (isExtensionSupported(renderCtx, "GL_NV_draw_buffers"))
250 extensionString = "#extension GL_NV_draw_buffers : require\n";
251
252 const glu::ShaderProgram program(renderCtx, glu::ProgramSources()
253 << glu::VertexSource("attribute highp vec4 a_position;\n"
254 "attribute highp vec4 a_color;\n"
255 "varying mediump vec4 v_color;\n"
256 "void main (void)\n"
257 "{\n"
258 " gl_Position = a_position;\n"
259 " v_color = a_color;\n"
260 "}\n")
261 << glu::FragmentSource(extensionString +
262 "varying mediump vec4 v_color;\n"
263 "uniform mediump int u_index;\n"
264 "void main (void)\n"
265 "{\n"
266 " gl_FragData[u_index] = v_color;\n"
267 "}\n"));
268 const glw::Functions &gl = renderCtx.getFunctions();
269 const int width = 128;
270 const int height = 128;
271 const int indexLoc = program.isOk() ? gl.getUniformLocation(program.getProgram(), "u_index") : -1;
272 const glu::Framebuffer fbo(renderCtx);
273 const glu::Renderbuffer colorBuf0(renderCtx);
274 const glu::Renderbuffer colorBuf1(renderCtx);
275
276 const float positions[] = {-1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f};
277 const float colors[] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
278 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
279 const uint8_t indices[] = {0, 1, 2, 2, 1, 3};
280
281 const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 2, 4, 0, &positions[0]),
282 glu::va::Float("a_color", 4, 4, 0, &colors[0])};
283
284 m_testCtx.getLog() << program;
285
286 if (!program.isOk())
287 throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
288
289 gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
290 for (int ndx = 0; ndx < num_test_attachment; ndx++)
291 {
292 const uint32_t rbo = ndx == 0 ? *colorBuf0 : *colorBuf1;
293
294 gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
295 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
296 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + ndx, GL_RENDERBUFFER, rbo);
297 }
298 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
299
300 {
301 const uint32_t drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
302 gl.drawBuffers(DE_LENGTH_OF_ARRAY(drawBuffers), &drawBuffers[0]);
303 }
304
305 gl.viewport(0, 0, width, height);
306 gl.useProgram(program.getProgram());
307
308 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
309
310 bool allOk = true;
311 const tcu::RGBA threshold =
312 renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1, 1, 1, 1);
313
314 for (int ndx = 0; ndx < num_test_attachment; ndx++)
315 {
316 gl.clearBufferfv(GL_COLOR, 0, tcu::RGBA::red().toVec().getPtr());
317 gl.clearBufferfv(GL_COLOR, 1, tcu::RGBA::red().toVec().getPtr());
318
319 m_testCtx.getLog() << TestLog::Message << "Drawing to attachments " << ndx << TestLog::EndMessage;
320
321 gl.uniform1i(indexLoc, ndx);
322 glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
323 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
324
325 GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
326
327 {
328 tcu::Surface result(width, height);
329
330 m_testCtx.getLog() << TestLog::Message << "Verifying attachment " << ndx << "..."
331 << TestLog::EndMessage;
332
333 gl.readBuffer(GL_COLOR_ATTACHMENT0 + ndx);
334 glu::readPixels(renderCtx, 0, 0, result.getAccess());
335 GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
336
337 if (!compareSingleColor(m_testCtx.getLog(), result, tcu::RGBA::green(), threshold))
338 allOk = false;
339 }
340 }
341
342 m_testCtx.setTestResult(allOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
343 allOk ? "Pass" : "Image comparison failed");
344
345 return STOP;
346 }
347 };
348
349 } // namespace
350
ShaderFragDataTests(Context & context)351 ShaderFragDataTests::ShaderFragDataTests(Context &context) : TestCaseGroup(context, "fragdata", "gl_FragData[] Tests")
352 {
353 }
354
~ShaderFragDataTests(void)355 ShaderFragDataTests::~ShaderFragDataTests(void)
356 {
357 }
358
init(void)359 void ShaderFragDataTests::init(void)
360 {
361 addChild(new FragDataIndexingCase(m_context, "valid_static_index",
362 "Valid gl_FragData[] assignment using static index", INDEX_EXPR_STATIC));
363 addChild(new FragDataIndexingCase(m_context, "valid_uniform_index",
364 "Valid gl_FragData[] assignment using uniform index", INDEX_EXPR_UNIFORM));
365 addChild(new FragDataIndexingCase(m_context, "valid_dynamic_index",
366 "Valid gl_FragData[] assignment using dynamic index", INDEX_EXPR_DYNAMIC));
367 addChild(new FragDataDrawBuffersCase(m_context));
368
369 // Negative cases.
370 {
371 gls::ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
372 std::vector<tcu::TestNode *> negativeCases = library.loadShaderFile("shaders/fragdata.test");
373
374 for (std::vector<tcu::TestNode *>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++)
375 addChild(*i);
376 }
377 }
378
379 } // namespace Functional
380 } // namespace gles3
381 } // namespace deqp
382