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
37namespace deqp
38{
39namespace gles3
40{
41namespace Functional
42{
43
44using std::string;
45using tcu::TestLog;
46using tcu::Vec2;
47using tcu::Vec3;
48using tcu::Vec4;
49using tcu::IVec2;
50using tcu::IVec3;
51using tcu::IVec4;
52using tcu::UVec4;
53using namespace FboTestUtil;
54
55class BasicFboMultisampleCase : public FboTestCase
56{
57public:
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
67protected:
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
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
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
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
243private:
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
253class RenderbufferResizeCase : public ApiCase
254{
255public:
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
263protected:
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
337private:
338	bool m_multisampled1;
339	bool m_multisampled2;
340};
341
342FboMultisampleTests::FboMultisampleTests (Context& context)
343	: TestCaseGroup(context, "msaa", "Multisample FBO tests")
344{
345}
346
347FboMultisampleTests::~FboMultisampleTests (void)
348{
349}
350
351void 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