1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
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 OpenGL ES context wrapper that uses FBO as default framebuffer.
22 *//*--------------------------------------------------------------------*/
23
24#include "gluFboRenderContext.hpp"
25#include "gluContextFactory.hpp"
26#include "gluRenderConfig.hpp"
27#include "glwEnums.hpp"
28#include "glwFunctions.hpp"
29#include "tcuCommandLine.hpp"
30#include "gluTextureUtil.hpp"
31#include "tcuTextureUtil.hpp"
32
33#include <sstream>
34
35namespace glu
36{
37
38static tcu::PixelFormat getPixelFormat (deUint32 colorFormat)
39{
40	const tcu::IVec4 bits = tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(colorFormat));
41	return tcu::PixelFormat(bits[0], bits[1], bits[2], bits[3]);
42}
43
44static void getDepthStencilBits (deUint32 depthStencilFormat, int* depthBits, int* stencilBits)
45{
46	const tcu::IVec4 bits = tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(depthStencilFormat));
47	*depthBits		= bits[0];
48	*stencilBits	= bits[3];
49}
50
51deUint32 chooseColorFormat (const glu::RenderConfig& config)
52{
53	static const deUint32 s_formats[] =
54	{
55		GL_RGBA8,
56		GL_RGB8,
57		GL_RG8,
58		GL_R8,
59		GL_RGBA4,
60		GL_RGB5_A1,
61		GL_RGB565,
62		GL_RGB5
63	};
64
65	for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(s_formats); fmtNdx++)
66	{
67		const deUint32		format	= s_formats[fmtNdx];
68		const tcu::IVec4	bits	= tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(format));
69
70		if (config.redBits != glu::RenderConfig::DONT_CARE &&
71			config.redBits != bits[0])
72			continue;
73
74		if (config.greenBits != glu::RenderConfig::DONT_CARE &&
75			config.greenBits != bits[1])
76			continue;
77
78		if (config.blueBits != glu::RenderConfig::DONT_CARE &&
79			config.blueBits != bits[2])
80			continue;
81
82		if (config.alphaBits != glu::RenderConfig::DONT_CARE &&
83			config.alphaBits != bits[3])
84			continue;
85
86		return format;
87	}
88
89	return 0;
90}
91
92deUint32 chooseDepthStencilFormat (const glu::RenderConfig& config)
93{
94	static const deUint32 s_formats[] =
95	{
96		GL_DEPTH32F_STENCIL8,
97		GL_DEPTH24_STENCIL8,
98		GL_DEPTH_COMPONENT32F,
99		GL_DEPTH_COMPONENT24,
100		GL_DEPTH_COMPONENT16,
101		GL_STENCIL_INDEX8
102	};
103
104	for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(s_formats); fmtNdx++)
105	{
106		const deUint32		format	= s_formats[fmtNdx];
107		const tcu::IVec4	bits	= tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(format));
108
109		if (config.depthBits != glu::RenderConfig::DONT_CARE &&
110			config.depthBits != bits[0])
111			continue;
112
113		if (config.stencilBits != glu::RenderConfig::DONT_CARE &&
114			config.stencilBits != bits[3])
115			continue;
116
117		return format;
118	}
119
120	return 0;
121}
122
123FboRenderContext::FboRenderContext (RenderContext* context, const RenderConfig& config)
124	: m_context				(context)
125	, m_framebuffer			(0)
126	, m_colorBuffer			(0)
127	, m_depthStencilBuffer	(0)
128	, m_renderTarget		()
129{
130	try
131	{
132		createFramebuffer(config);
133	}
134	catch (...)
135	{
136		destroyFramebuffer();
137		throw;
138	}
139}
140
141FboRenderContext::FboRenderContext (const ContextFactory& factory, const RenderConfig& config, const tcu::CommandLine& cmdLine)
142	: m_context				(DE_NULL)
143	, m_framebuffer			(0)
144	, m_colorBuffer			(0)
145	, m_depthStencilBuffer	(0)
146	, m_renderTarget		()
147{
148	try
149	{
150		RenderConfig nativeRenderConfig;
151		nativeRenderConfig.type				= config.type;
152		nativeRenderConfig.windowVisibility	= config.windowVisibility;
153		// \note All other properties are defaults, mostly DONT_CARE
154		m_context = factory.createContext(nativeRenderConfig, cmdLine);
155		createFramebuffer(config);
156	}
157	catch (...)
158	{
159		delete m_context;
160		throw;
161	}
162}
163
164FboRenderContext::~FboRenderContext (void)
165{
166	// \todo [2013-04-08 pyry] Do we want to destry FBO before destroying context?
167	delete m_context;
168}
169
170void FboRenderContext::postIterate (void)
171{
172	// \todo [2012-11-27 pyry] Blit to default framebuffer in ES3?
173	m_context->getFunctions().finish();
174}
175
176void FboRenderContext::createFramebuffer (const RenderConfig& config)
177{
178	DE_ASSERT(m_framebuffer == 0 && m_colorBuffer == 0 && m_depthStencilBuffer == 0);
179
180	const glw::Functions&	gl					= m_context->getFunctions();
181	const deUint32			colorFormat			= chooseColorFormat(config);
182	const deUint32			depthStencilFormat	= chooseDepthStencilFormat(config);
183	int						width				= config.width;
184	int						height				= config.height;
185	tcu::PixelFormat		pixelFormat;
186	int						depthBits			= 0;
187	int						stencilBits			= 0;
188
189	if (config.numSamples > 0 && !gl.renderbufferStorageMultisample)
190		throw tcu::NotSupportedError("Multisample FBO is not supported");
191
192	if (colorFormat == 0)
193		throw tcu::NotSupportedError("Unsupported color attachment format");
194
195	if (width == glu::RenderConfig::DONT_CARE || height == glu::RenderConfig::DONT_CARE)
196	{
197		int maxSize = 0;
198		gl.getIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxSize);
199
200		width	= (width	== glu::RenderConfig::DONT_CARE) ? maxSize : width;
201		height	= (height	== glu::RenderConfig::DONT_CARE) ? maxSize : height;
202	}
203
204	{
205		pixelFormat = getPixelFormat(colorFormat);
206
207		gl.genRenderbuffers(1, &m_colorBuffer);
208		gl.bindRenderbuffer(GL_RENDERBUFFER, m_colorBuffer);
209
210		if (config.numSamples > 0)
211			gl.renderbufferStorageMultisample(GL_RENDERBUFFER, config.numSamples, colorFormat, width, height);
212		else
213			gl.renderbufferStorage(GL_RENDERBUFFER, colorFormat, width, height);
214
215		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
216		GLU_EXPECT_NO_ERROR(gl.getError(), "Creating color renderbuffer");
217	}
218
219	if (depthStencilFormat != GL_NONE)
220	{
221		getDepthStencilBits(depthStencilFormat, &depthBits, &stencilBits);
222
223		gl.genRenderbuffers(1, &m_depthStencilBuffer);
224		gl.bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
225
226		if (config.numSamples > 0)
227			gl.renderbufferStorageMultisample(GL_RENDERBUFFER, config.numSamples, depthStencilFormat, width, height);
228		else
229			gl.renderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, width, height);
230
231		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
232		GLU_EXPECT_NO_ERROR(gl.getError(), "Creating depth / stencil renderbuffer");
233	}
234
235	gl.genFramebuffers(1, &m_framebuffer);
236	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
237
238	if (m_colorBuffer)
239		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorBuffer);
240
241	if (m_depthStencilBuffer)
242	{
243		if (depthBits > 0)
244			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
245
246		if (stencilBits > 0)
247			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
248	}
249
250	GLU_EXPECT_NO_ERROR(gl.getError(), "Creating framebuffer");
251
252	if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
253		throw tcu::NotSupportedError("Framebuffer is not complete");
254
255	// Set up correct viewport for first test case.
256	gl.viewport(0, 0, width, height);
257
258	m_renderTarget = tcu::RenderTarget(width, height, pixelFormat, depthBits, stencilBits, config.numSamples);
259}
260
261void FboRenderContext::destroyFramebuffer (void)
262{
263	const glw::Functions& gl = m_context->getFunctions();
264
265	if (m_framebuffer)
266	{
267		gl.deleteFramebuffers(1, &m_framebuffer);
268		m_framebuffer = 0;
269	}
270
271	if (m_depthStencilBuffer)
272	{
273		gl.deleteRenderbuffers(1, &m_depthStencilBuffer);
274		m_depthStencilBuffer = 0;
275	}
276
277	if (m_colorBuffer)
278	{
279		gl.deleteRenderbuffers(1, &m_colorBuffer);
280		m_colorBuffer = 0;
281	}
282}
283
284} // glu
285