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