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