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 FBO multisample tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es3fApiCase.hpp"
25 #include "es3fFboMultisampleTests.hpp"
26 #include "es3fFboTestCase.hpp"
27 #include "es3fFboTestUtil.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "tcuImageCompare.hpp"
30 #include "tcuTextureUtil.hpp"
31 #include "tcuTestLog.hpp"
32 #include "deStringUtil.hpp"
33 #include "deRandom.hpp"
34 #include "sglrContextUtil.hpp"
35 #include "glwEnums.hpp"
36
37 namespace deqp
38 {
39 namespace gles3
40 {
41 namespace Functional
42 {
43
44 using std::string;
45 using tcu::TestLog;
46 using tcu::Vec2;
47 using tcu::Vec3;
48 using tcu::Vec4;
49 using tcu::IVec2;
50 using tcu::IVec3;
51 using tcu::IVec4;
52 using tcu::UVec4;
53 using namespace FboTestUtil;
54
55 class BasicFboMultisampleCase : public FboTestCase
56 {
57 public:
BasicFboMultisampleCase(Context & context,const char * name,const char * desc,deUint32 colorFormat,deUint32 depthStencilFormat,const IVec2 & size,int numSamples)58 BasicFboMultisampleCase (Context& context, const char* name, const char* desc, deUint32 colorFormat, deUint32 depthStencilFormat, const IVec2& size, int numSamples)
59 : FboTestCase (context, name, desc)
60 , m_colorFormat (colorFormat)
61 , m_depthStencilFormat (depthStencilFormat)
62 , m_size (size)
63 , m_numSamples (numSamples)
64 {
65 }
66
67 protected:
preCheck(void)68 void preCheck (void)
69 {
70 checkFormatSupport (m_colorFormat);
71 checkSampleCount (m_colorFormat, m_numSamples);
72
73 if (m_depthStencilFormat != GL_NONE)
74 {
75 checkFormatSupport (m_depthStencilFormat);
76 checkSampleCount (m_depthStencilFormat, m_numSamples);
77 }
78 }
79
render(tcu::Surface & dst)80 void render (tcu::Surface& dst)
81 {
82 tcu::TextureFormat colorFmt = glu::mapGLInternalFormat(m_colorFormat);
83 tcu::TextureFormat depthStencilFmt = m_depthStencilFormat != GL_NONE ? glu::mapGLInternalFormat(m_depthStencilFormat) : tcu::TextureFormat();
84 tcu::TextureFormatInfo colorFmtInfo = tcu::getTextureFormatInfo(colorFmt);
85 bool depth = depthStencilFmt.order == tcu::TextureFormat::D || depthStencilFmt.order == tcu::TextureFormat::DS;
86 bool stencil = depthStencilFmt.order == tcu::TextureFormat::S || depthStencilFmt.order == tcu::TextureFormat::DS;
87 GradientShader gradShader (getFragmentOutputType(colorFmt));
88 FlatColorShader flatShader (getFragmentOutputType(colorFmt));
89 deUint32 gradShaderID = getCurrentContext()->createProgram(&gradShader);
90 deUint32 flatShaderID = getCurrentContext()->createProgram(&flatShader);
91 deUint32 msaaFbo = 0;
92 deUint32 resolveFbo = 0;
93 deUint32 msaaColorRbo = 0;
94 deUint32 resolveColorRbo = 0;
95 deUint32 msaaDepthStencilRbo = 0;
96 deUint32 resolveDepthStencilRbo = 0;
97
98 // Create framebuffers.
99 for (int ndx = 0; ndx < 2; ndx++)
100 {
101 deUint32& fbo = ndx ? resolveFbo : msaaFbo;
102 deUint32& colorRbo = ndx ? resolveColorRbo : msaaColorRbo;
103 deUint32& depthStencilRbo = ndx ? resolveDepthStencilRbo : msaaDepthStencilRbo;
104 int samples = ndx ? 0 : m_numSamples;
105
106 glGenRenderbuffers(1, &colorRbo);
107 glBindRenderbuffer(GL_RENDERBUFFER, colorRbo);
108 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, m_colorFormat, m_size.x(), m_size.y());
109
110 if (depth || stencil)
111 {
112 glGenRenderbuffers(1, &depthStencilRbo);
113 glBindRenderbuffer(GL_RENDERBUFFER, depthStencilRbo);
114 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, m_depthStencilFormat, m_size.x(), m_size.y());
115 }
116
117 glGenFramebuffers(1, &fbo);
118 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
119 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRbo);
120 if (depth)
121 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthStencilRbo);
122 if (stencil)
123 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilRbo);
124
125 checkError();
126 checkFramebufferStatus(GL_FRAMEBUFFER);
127 }
128
129 glBindFramebuffer(GL_FRAMEBUFFER, msaaFbo);
130 glViewport(0, 0, m_size.x(), m_size.y());
131
132 // Clear depth and stencil buffers.
133 glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0);
134
135 // Fill MSAA fbo with gradient, depth = [-1..1]
136 glEnable(GL_DEPTH_TEST);
137 gradShader.setGradient(*getCurrentContext(), gradShaderID, colorFmtInfo.valueMin, colorFmtInfo.valueMax);
138 sglr::drawQuad(*getCurrentContext(), gradShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f));
139
140 // Render random-colored quads.
141 {
142 const int numQuads = 8;
143 de::Random rnd (9);
144
145 glDepthFunc(GL_ALWAYS);
146 glEnable(GL_STENCIL_TEST);
147 glStencilFunc(GL_ALWAYS, 0, 0xffu);
148 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
149
150 for (int ndx = 0; ndx < numQuads; ndx++)
151 {
152 float r = rnd.getFloat();
153 float g = rnd.getFloat();
154 float b = rnd.getFloat();
155 float a = rnd.getFloat();
156 float x0 = rnd.getFloat(-1.0f, 1.0f);
157 float y0 = rnd.getFloat(-1.0f, 1.0f);
158 float z0 = rnd.getFloat(-1.0f, 1.0f);
159 float x1 = rnd.getFloat(-1.0f, 1.0f);
160 float y1 = rnd.getFloat(-1.0f, 1.0f);
161 float z1 = rnd.getFloat(-1.0f, 1.0f);
162
163 flatShader.setColor(*getCurrentContext(), flatShaderID, Vec4(r,g,b,a) * (colorFmtInfo.valueMax-colorFmtInfo.valueMin) + colorFmtInfo.valueMin);
164 sglr::drawQuad(*getCurrentContext(), flatShaderID, Vec3(x0, y0, z0), Vec3(x1, y1, z1));
165 }
166 }
167
168 glDisable(GL_DEPTH_TEST);
169 glDisable(GL_STENCIL_TEST);
170 checkError();
171
172 // Resolve using glBlitFramebuffer().
173 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
174 glBlitFramebuffer(0, 0, m_size.x(), m_size.y(), 0, 0, m_size.x(), m_size.y(), GL_COLOR_BUFFER_BIT | (depth ? GL_DEPTH_BUFFER_BIT : 0) | (stencil ? GL_STENCIL_BUFFER_BIT : 0), GL_NEAREST);
175
176 glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
177
178 if (depth)
179 {
180 // Visualize depth.
181 const int numSteps = 8;
182 const float step = 2.0f / (float)numSteps;
183
184 glEnable(GL_DEPTH_TEST);
185 glDepthFunc(GL_LESS);
186 glDepthMask(GL_FALSE);
187 glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
188
189 for (int ndx = 0; ndx < numSteps; ndx++)
190 {
191 float d = -1.0f + step*(float)ndx;
192 float c = (float)ndx / (float)(numSteps-1);
193
194 flatShader.setColor(*getCurrentContext(), flatShaderID, Vec4(0.0f, 0.0f, c, 1.0f) * (colorFmtInfo.valueMax-colorFmtInfo.valueMin) + colorFmtInfo.valueMin);
195 sglr::drawQuad(*getCurrentContext(), flatShaderID, Vec3(-1.0f, -1.0f, d), Vec3(1.0f, 1.0f, d));
196 }
197
198 glDisable(GL_DEPTH_TEST);
199 }
200
201 if (stencil)
202 {
203 // Visualize stencil.
204 const int numSteps = 4;
205 const int step = 1;
206
207 glEnable(GL_STENCIL_TEST);
208 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
209 glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
210
211 for (int ndx = 0; ndx < numSteps; ndx++)
212 {
213 int s = step*ndx;
214 float c = (float)ndx / (float)(numSteps-1);
215
216 glStencilFunc(GL_EQUAL, s, 0xffu);
217
218 flatShader.setColor(*getCurrentContext(), flatShaderID, Vec4(0.0f, c, 0.0f, 1.0f) * (colorFmtInfo.valueMax-colorFmtInfo.valueMin) + colorFmtInfo.valueMin);
219 sglr::drawQuad(*getCurrentContext(), flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
220 }
221
222 glDisable(GL_STENCIL_TEST);
223 }
224
225 readPixels(dst, 0, 0, m_size.x(), m_size.y(), colorFmt, colorFmtInfo.lookupScale, colorFmtInfo.lookupBias);
226 }
227
colorCompare(const tcu::Surface & reference,const tcu::Surface & result)228 bool colorCompare (const tcu::Surface& reference, const tcu::Surface& result)
229 {
230 const tcu::RGBA threshold (tcu::max(getFormatThreshold(m_colorFormat), tcu::RGBA(12, 12, 12, 12)));
231
232 return tcu::bilinearCompare(m_testCtx.getLog(), "Result", "Image comparison result", reference.getAccess(), result.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
233 }
234
compare(const tcu::Surface & reference,const tcu::Surface & result)235 bool compare (const tcu::Surface& reference, const tcu::Surface& result)
236 {
237 if (m_depthStencilFormat != GL_NONE)
238 return FboTestCase::compare(reference, result);
239 else
240 return colorCompare(reference, result);
241 }
242
243 private:
244 deUint32 m_colorFormat;
245 deUint32 m_depthStencilFormat;
246 IVec2 m_size;
247 int m_numSamples;
248 };
249
250 // Ported from WebGL [1], originally written to test a Qualcomm driver bug [2].
251 // [1] https://github.com/KhronosGroup/WebGL/blob/master/sdk/tests/conformance2/renderbuffers/multisampled-renderbuffer-initialization.html
252 // [2] http://crbug.com/696126
253 class RenderbufferResizeCase : public ApiCase
254 {
255 public:
RenderbufferResizeCase(Context & context,const char * name,const char * desc,bool multisampled1,bool multisampled2)256 RenderbufferResizeCase (Context& context, const char* name, const char* desc, bool multisampled1, bool multisampled2)
257 : ApiCase (context, name, desc)
258 , m_multisampled1(multisampled1)
259 , m_multisampled2(multisampled2)
260 {
261 }
262
263 protected:
test()264 void test ()
265 {
266 glDisable(GL_DEPTH_TEST);
267
268 int maxSamples = 0;
269 glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, 1, &maxSamples);
270 deUint32 samp1 = m_multisampled1 ? maxSamples : 0;
271 deUint32 samp2 = m_multisampled2 ? maxSamples : 0;
272
273 static const deUint32 W1 = 10, H1 = 10;
274 static const deUint32 W2 = 40, H2 = 40;
275
276 // Set up non-multisampled buffer to blit to and read back from.
277 deUint32 fboResolve = 0;
278 deUint32 rboResolve = 0;
279 {
280 glGenFramebuffers(1, &fboResolve);
281 glBindFramebuffer(GL_FRAMEBUFFER, fboResolve);
282 glGenRenderbuffers(1, &rboResolve);
283 glBindRenderbuffer(GL_RENDERBUFFER, rboResolve);
284 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, W2, H2);
285 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboResolve);
286 TCU_CHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
287 glClearBufferfv(GL_COLOR, 0, Vec4(1.0f, 0.0f, 0.0f, 1.0f).getPtr());
288 }
289 expectError(GL_NO_ERROR);
290
291 // Set up multisampled buffer to test.
292 deUint32 fboMultisampled = 0;
293 deUint32 rboMultisampled = 0;
294 {
295 glGenFramebuffers(1, &fboMultisampled);
296 glBindFramebuffer(GL_FRAMEBUFFER, fboMultisampled);
297 glGenRenderbuffers(1, &rboMultisampled);
298 glBindRenderbuffer(GL_RENDERBUFFER, rboMultisampled);
299 // Allocate,
300 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samp1, GL_RGBA8, W1, H1);
301 // attach,
302 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboMultisampled);
303 TCU_CHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
304 glClearBufferfv(GL_COLOR, 0, Vec4(0.0f, 0.0f, 1.0f, 1.0f).getPtr());
305 // and allocate again with different parameters.
306 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samp2, GL_RGBA8, W2, H2);
307 TCU_CHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
308 glClearBufferfv(GL_COLOR, 0, Vec4(0.0f, 1.0f, 0.0f, 1.0f).getPtr());
309 }
310 expectError(GL_NO_ERROR);
311
312 // This is a blit from the multisampled buffer to the non-multisampled buffer.
313 glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMultisampled);
314 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboResolve);
315 // Blit color from fboMultisampled (should be green) to fboResolve (should currently be red).
316 glBlitFramebuffer(0, 0, W2, H2, 0, 0, W2, H2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
317 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
318 expectError(GL_NO_ERROR);
319
320 // fboResolve should now be green.
321 glBindFramebuffer(GL_READ_FRAMEBUFFER, fboResolve);
322 deUint32 pixels[W2 * H2] = {};
323 glReadPixels(0, 0, W2, H2, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
324 expectError(GL_NO_ERROR);
325
326 const tcu::RGBA threshold (tcu::max(getFormatThreshold(GL_RGBA8), tcu::RGBA(12, 12, 12, 12)));
327 for (deUint32 y = 0; y < H2; ++y)
328 {
329 for (deUint32 x = 0; x < W2; ++x)
330 {
331 tcu::RGBA color(pixels[y * W2 + x]);
332 TCU_CHECK(compareThreshold(color, tcu::RGBA::green(), threshold));
333 }
334 }
335 }
336
337 private:
338 bool m_multisampled1;
339 bool m_multisampled2;
340 };
341
FboMultisampleTests(Context & context)342 FboMultisampleTests::FboMultisampleTests (Context& context)
343 : TestCaseGroup(context, "msaa", "Multisample FBO tests")
344 {
345 }
346
~FboMultisampleTests(void)347 FboMultisampleTests::~FboMultisampleTests (void)
348 {
349 }
350
init(void)351 void FboMultisampleTests::init (void)
352 {
353 static const deUint32 colorFormats[] =
354 {
355 // RGBA formats
356 GL_RGBA8,
357 GL_SRGB8_ALPHA8,
358 GL_RGB10_A2,
359 GL_RGBA4,
360 GL_RGB5_A1,
361
362 // RGB formats
363 GL_RGB8,
364 GL_RGB565,
365
366 // RG formats
367 GL_RG8,
368
369 // R formats
370 GL_R8,
371
372 // GL_EXT_color_buffer_float
373 GL_RGBA32F,
374 GL_RGBA16F,
375 GL_R11F_G11F_B10F,
376 GL_RG32F,
377 GL_RG16F,
378 GL_R32F,
379 GL_R16F
380 };
381
382 static const deUint32 depthStencilFormats[] =
383 {
384 GL_DEPTH_COMPONENT32F,
385 GL_DEPTH_COMPONENT24,
386 GL_DEPTH_COMPONENT16,
387 GL_DEPTH32F_STENCIL8,
388 GL_DEPTH24_STENCIL8,
389 GL_STENCIL_INDEX8
390 };
391
392 static const int sampleCounts[] = { 2, 4, 8 };
393
394 for (int sampleCntNdx = 0; sampleCntNdx < DE_LENGTH_OF_ARRAY(sampleCounts); sampleCntNdx++)
395 {
396 int samples = sampleCounts[sampleCntNdx];
397 tcu::TestCaseGroup* sampleCountGroup = new tcu::TestCaseGroup(m_testCtx, (de::toString(samples) + "_samples").c_str(), "");
398 addChild(sampleCountGroup);
399
400 // Color formats.
401 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++)
402 sampleCountGroup->addChild(new BasicFboMultisampleCase(m_context, getFormatName(colorFormats[fmtNdx]), "", colorFormats[fmtNdx], GL_NONE, IVec2(119, 131), samples));
403
404 // Depth/stencil formats.
405 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++)
406 sampleCountGroup->addChild(new BasicFboMultisampleCase(m_context, getFormatName(depthStencilFormats[fmtNdx]), "", GL_RGBA8, depthStencilFormats[fmtNdx], IVec2(119, 131), samples));
407 }
408
409 // .renderbuffer_resize
410 {
411 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(m_testCtx, "renderbuffer_resize", "Multisample renderbuffer resize");
412 addChild(group);
413
414 {
415 group->addChild(new RenderbufferResizeCase(m_context, "nonms_to_nonms", "", false, false));
416 group->addChild(new RenderbufferResizeCase(m_context, "nonms_to_ms", "", false, true));
417 group->addChild(new RenderbufferResizeCase(m_context, "ms_to_nonms", "", true, false));
418 group->addChild(new RenderbufferResizeCase(m_context, "ms_to_ms", "", true, true));
419 }
420 }
421 }
422
423 } // Functional
424 } // gles3
425 } // deqp
426