teglImageUtil.cpp revision 7f2e8aa43396923cff333c4b4e17773589e19c17
1/*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL 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 Common utilities for EGL images.
22 *//*--------------------------------------------------------------------*/
23
24
25#include "teglImageUtil.hpp"
26
27#include "tcuTexture.hpp"
28#include "tcuTextureUtil.hpp"
29
30#include "egluGLUtil.hpp"
31#include "egluNativeWindow.hpp"
32#include "egluNativePixmap.hpp"
33
34#include "eglwLibrary.hpp"
35#include "eglwEnums.hpp"
36
37#include "glwEnums.hpp"
38
39#include "gluObjectWrapper.hpp"
40#include "gluTextureUtil.hpp"
41
42namespace deqp
43{
44namespace egl
45{
46namespace Image
47{
48
49using std::string;
50using std::vector;
51
52using de::UniquePtr;
53using de::MovePtr;
54
55using tcu::TextureFormat;
56using tcu::Texture2D;
57using tcu::Vec4;
58
59using glu::Framebuffer;
60using glu::Texture;
61
62using eglu::AttribMap;
63using eglu::UniqueSurface;
64using eglu::NativeDisplay;
65using eglu::NativeWindow;
66using eglu::NativePixmap;
67using eglu::NativeDisplayFactory;
68using eglu::NativeWindowFactory;
69using eglu::NativePixmapFactory;
70using eglu::WindowParams;
71
72using namespace glw;
73using namespace eglw;
74
75enum {
76	IMAGE_WIDTH		= 64,
77	IMAGE_HEIGHT	= 64,
78};
79
80
81template <typename T>
82struct NativeSurface : public ManagedSurface
83{
84public:
85	explicit		NativeSurface	(MovePtr<UniqueSurface>	surface,
86									 MovePtr<T>				native)
87						: ManagedSurface	(surface)
88						, m_native			(native) {}
89
90private:
91	UniquePtr<T>	m_native;
92};
93
94typedef NativeSurface<NativeWindow> NativeWindowSurface;
95typedef NativeSurface<NativePixmap> NativePixmapSurface;
96
97MovePtr<ManagedSurface> createSurface (EglTestContext& eglTestCtx, EGLDisplay dpy, EGLConfig config, int width, int height)
98{
99	const Library&				egl				= eglTestCtx.getLibrary();
100	EGLint						surfaceTypeBits	= eglu::getConfigAttribInt(egl, dpy, config, EGL_SURFACE_TYPE);
101	const NativeDisplayFactory&	displayFactory	= eglTestCtx.getNativeDisplayFactory();
102	NativeDisplay&				nativeDisplay	= eglTestCtx.getNativeDisplay();
103
104	if (surfaceTypeBits & EGL_PBUFFER_BIT)
105	{
106		static const EGLint attribs[]	= { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE };
107		const EGLSurface	surface		= egl.createPbufferSurface(dpy, config, attribs);
108
109		EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()");
110
111		return de::newMovePtr<ManagedSurface>(MovePtr<UniqueSurface>(new UniqueSurface(egl, dpy, surface)));
112	}
113	else if (surfaceTypeBits & EGL_WINDOW_BIT)
114	{
115		const NativeWindowFactory&	windowFactory	= selectNativeWindowFactory(displayFactory, eglTestCtx.getTestContext().getCommandLine());
116
117		MovePtr<NativeWindow>		window	(windowFactory.createWindow(&nativeDisplay, dpy, config, DE_NULL, WindowParams(width, height, WindowParams::VISIBILITY_DONT_CARE)));
118		const EGLSurface			surface	= eglu::createWindowSurface(nativeDisplay, *window, dpy, config, DE_NULL);
119
120		return MovePtr<ManagedSurface>(new NativeWindowSurface(MovePtr<UniqueSurface>(new UniqueSurface(egl, dpy, surface)), window));
121	}
122	else if (surfaceTypeBits & EGL_PIXMAP_BIT)
123	{
124		const NativePixmapFactory&	pixmapFactory	= selectNativePixmapFactory(displayFactory, eglTestCtx.getTestContext().getCommandLine());
125
126		MovePtr<NativePixmap>	pixmap	(pixmapFactory.createPixmap(&nativeDisplay, dpy, config, DE_NULL, width, height));
127		const EGLSurface		surface	= eglu::createPixmapSurface(eglTestCtx.getNativeDisplay(), *pixmap, dpy, config, DE_NULL);
128
129		return MovePtr<ManagedSurface>(new NativePixmapSurface(MovePtr<UniqueSurface>(new UniqueSurface(egl, dpy, surface)), pixmap));
130	}
131	else
132		TCU_FAIL("No valid surface types supported in config");
133}
134
135class GLClientBuffer : public ClientBuffer
136{
137	EGLClientBuffer	get		(void) const { return reinterpret_cast<EGLClientBuffer>(static_cast<deUintptr>(getName())); }
138
139protected:
140	virtual GLuint	getName	(void) const = 0;
141};
142
143class TextureClientBuffer : public GLClientBuffer
144{
145public:
146						TextureClientBuffer	(const glw::Functions& gl) : m_texture (gl) {}
147	GLuint				getName				(void) const { return *m_texture; }
148
149private:
150	glu::Texture		m_texture;
151};
152
153class GLImageSource : public ImageSource
154{
155public:
156	EGLImageKHR			createImage			(const Library& egl, EGLDisplay dpy, EGLContext ctx, EGLClientBuffer clientBuffer) const;
157
158protected:
159	virtual AttribMap	getCreateAttribs	(void) const = 0;
160	virtual EGLenum		getSource			(void) const = 0;
161};
162
163EGLImageKHR GLImageSource::createImage (const Library& egl, EGLDisplay dpy, EGLContext ctx, EGLClientBuffer clientBuffer) const
164{
165	AttribMap				attribMap	= getCreateAttribs();
166
167	attribMap[EGL_IMAGE_PRESERVED_KHR] = EGL_TRUE;
168
169	{
170		const vector<EGLint> 	attribs	= eglu::attribMapToList(attribMap);
171		const EGLImageKHR		image 	= egl.createImageKHR(dpy, ctx, getSource(),
172															 clientBuffer, &attribs.front());
173		EGLU_CHECK_MSG(egl, "eglCreateImageKHR()");
174		return image;
175	}
176}
177
178class TextureImageSource : public GLImageSource
179{
180public:
181							TextureImageSource	(GLenum format, GLenum type, bool useTexLevel0) : m_format(format), m_type(type), m_useTexLevel0(useTexLevel0) {}
182	MovePtr<ClientBuffer>	createBuffer		(const glw::Functions& gl, Texture2D* reference) const;
183	GLenum					getFormat			(void) const { return m_format; }
184
185protected:
186	AttribMap				getCreateAttribs	(void) const;
187	virtual void			initTexture			(const glw::Functions& gl) const = 0;
188	virtual GLenum			getGLTarget			(void) const = 0;
189
190	GLenum					m_format;
191	GLenum					m_type;
192	bool					m_useTexLevel0;
193};
194
195AttribMap TextureImageSource::getCreateAttribs (void) const
196{
197	AttribMap ret;
198
199	ret[EGL_GL_TEXTURE_LEVEL_KHR] = 0;
200
201	return ret;
202}
203
204MovePtr<ClientBuffer> TextureImageSource::createBuffer (const glw::Functions& gl, Texture2D* ref) const
205{
206	MovePtr<TextureClientBuffer>	clientBuffer	(new TextureClientBuffer(gl));
207	const GLuint					texture			= clientBuffer->getName();
208	const GLenum					target			= getGLTarget();
209
210	GLU_CHECK_GLW_CALL(gl, bindTexture(target, texture));
211	initTexture(gl);
212
213	if (!m_useTexLevel0)
214	{
215		// Set minification filter to linear. This makes the texture complete.
216		GLU_CHECK_GLW_CALL(gl, texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
217	}
218
219	if (ref != DE_NULL)
220	{
221		GLenum		imgTarget	= eglu::getImageGLTarget(getSource());
222
223		*ref = Texture2D(glu::mapGLTransferFormat(m_format, m_type), IMAGE_WIDTH, IMAGE_HEIGHT);
224		ref->allocLevel(0);
225		tcu::fillWithComponentGradients(ref->getLevel(0),
226										tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f),
227										tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
228
229		GLU_CHECK_GLW_CALL(gl, texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
230		GLU_CHECK_GLW_CALL(gl, texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
231		GLU_CHECK_GLW_CALL(gl, texParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
232		GLU_CHECK_GLW_CALL(gl, texParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
233
234		GLU_CHECK_GLW_CALL(gl, texImage2D(imgTarget, 0, m_format, IMAGE_WIDTH, IMAGE_HEIGHT,
235										  0, m_format, m_type, ref->getLevel(0).getDataPtr()));
236	}
237	GLU_CHECK_GLW_CALL(gl, bindTexture(target, 0));
238	return MovePtr<ClientBuffer>(clientBuffer);
239}
240
241class Texture2DImageSource : public TextureImageSource
242{
243public:
244					Texture2DImageSource	(GLenum format, GLenum type, bool useTexLevel0) : TextureImageSource(format, type, useTexLevel0) {}
245	EGLenum			getSource				(void) const { return EGL_GL_TEXTURE_2D_KHR; }
246	string			getRequiredExtension	(void) const { return "EGL_KHR_gl_texture_2D_image"; }
247	GLenum			getGLTarget				(void) const { return GL_TEXTURE_2D; }
248
249protected:
250	void			initTexture				(const glw::Functions& gl) const;
251};
252
253void Texture2DImageSource::initTexture (const glw::Functions& gl) const
254{
255	// Specify mipmap level 0
256	GLU_CHECK_CALL_ERROR(gl.texImage2D(GL_TEXTURE_2D, 0, m_format, IMAGE_WIDTH, IMAGE_HEIGHT, 0, m_format, m_type, DE_NULL),
257						 gl.getError());
258}
259
260class TextureCubeMapImageSource : public TextureImageSource
261{
262public:
263					TextureCubeMapImageSource	(EGLenum source, GLenum format, GLenum type, bool useTexLevel0) : TextureImageSource(format, type, useTexLevel0), m_source(source) {}
264	EGLenum			getSource					(void) const { return m_source; }
265	string			getRequiredExtension		(void) const { return "EGL_KHR_gl_texture_cubemap_image"; }
266	GLenum			getGLTarget					(void) const { return GL_TEXTURE_CUBE_MAP; }
267
268protected:
269	void			initTexture					(const glw::Functions& gl) const;
270
271	EGLenum			m_source;
272};
273
274void TextureCubeMapImageSource::initTexture (const glw::Functions& gl) const
275{
276	// Specify mipmap level 0 for all faces
277	static const GLenum faces[] =
278	{
279		GL_TEXTURE_CUBE_MAP_POSITIVE_X,
280		GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
281		GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
282		GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
283		GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
284		GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
285	};
286
287	for (int faceNdx = 0; faceNdx < DE_LENGTH_OF_ARRAY(faces); faceNdx++)
288		GLU_CHECK_GLW_CALL(gl, texImage2D(faces[faceNdx], 0, m_format, IMAGE_WIDTH, IMAGE_HEIGHT, 0, m_format, m_type, DE_NULL));
289}
290
291class RenderbufferClientBuffer : public GLClientBuffer
292{
293public:
294						RenderbufferClientBuffer	(const glw::Functions& gl) : m_rbo (gl) {}
295	GLuint				getName						(void) const { return *m_rbo; }
296
297private:
298	glu::Renderbuffer	m_rbo;
299};
300
301class RenderbufferImageSource : public GLImageSource
302{
303public:
304							RenderbufferImageSource	(GLenum format) : m_format(format) {}
305
306	string					getRequiredExtension	(void) const 	{ return "EGL_KHR_gl_renderbuffer_image"; }
307	MovePtr<ClientBuffer>	createBuffer			(const glw::Functions& gl, Texture2D* reference) const;
308	GLenum					getFormat				(void) const { return m_format; }
309
310protected:
311	EGLenum					getSource				(void) const	{ return EGL_GL_RENDERBUFFER_KHR; }
312	AttribMap				getCreateAttribs		(void) const	{ return AttribMap(); }
313
314	GLenum					m_format;
315};
316
317void initializeStencilRbo(const glw::Functions& gl, GLuint rbo, Texture2D& ref)
318{
319	static const deUint32 stencilValues[] =
320	{
321		0xBF688C11u,
322		0xB43D2922u,
323		0x055D5FFBu,
324		0x9300655Eu,
325		0x63BE0DF2u,
326		0x0345C13Bu,
327		0x1C184832u,
328		0xD107040Fu,
329		0x9B91569Fu,
330		0x0F0CFDC7u,
331	};
332
333	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
334												   GL_RENDERBUFFER, rbo));
335	GLU_CHECK_GLW_CALL(gl, clearStencil(0));
336	GLU_CHECK_GLW_CALL(gl, clear(GL_STENCIL_BUFFER_BIT));
337	tcu::clearStencil(ref.getLevel(0), 0);
338
339	// create a pattern
340	GLU_CHECK_GLW_CALL(gl, enable(GL_SCISSOR_TEST));
341	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilValues); ++ndx)
342	{
343		const tcu::IVec2	size	= tcu::IVec2((int)((DE_LENGTH_OF_ARRAY(stencilValues) - ndx) * (ref.getWidth() / float(DE_LENGTH_OF_ARRAY(stencilValues)))),
344												 (int)((DE_LENGTH_OF_ARRAY(stencilValues) - ndx) * (ref.getHeight() / float(DE_LENGTH_OF_ARRAY(stencilValues) + 4)))); // not symmetric
345
346		if (size.x() == 0 || size.y() == 0)
347			break;
348
349		GLU_CHECK_GLW_CALL(gl, scissor(0, 0, size.x(), size.y()));
350		GLU_CHECK_GLW_CALL(gl, clearStencil(stencilValues[ndx]));
351		GLU_CHECK_GLW_CALL(gl, clear(GL_STENCIL_BUFFER_BIT));
352
353		tcu::clearStencil(tcu::getSubregion(ref.getLevel(0), 0, 0, size.x(), size.y()), stencilValues[ndx]);
354	}
355
356	GLU_CHECK_GLW_CALL(gl, disable(GL_SCISSOR_TEST));
357	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
358												   GL_RENDERBUFFER, 0));
359}
360
361void initializeDepthRbo(const glw::Functions& gl, GLuint rbo, Texture2D& ref)
362{
363	const int NUM_STEPS = 13;
364
365	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
366												   GL_RENDERBUFFER, rbo));
367
368	GLU_CHECK_GLW_CALL(gl, clearDepthf(0.0f));
369	GLU_CHECK_GLW_CALL(gl, clear(GL_DEPTH_BUFFER_BIT));
370	tcu::clearDepth(ref.getLevel(0), 0.0f);
371
372	// create a pattern
373	GLU_CHECK_GLW_CALL(gl, enable(GL_SCISSOR_TEST));
374	for (int ndx = 0; ndx < NUM_STEPS; ++ndx)
375	{
376		const float			depth	= ndx / float(NUM_STEPS);
377		const tcu::IVec2	size	= tcu::IVec2((int)((NUM_STEPS - ndx) * (ref.getWidth() / float(NUM_STEPS))),
378												 (int)((NUM_STEPS - ndx) * (ref.getHeight() / float(NUM_STEPS + 4)))); // not symmetric
379
380		if (size.x() == 0 || size.y() == 0)
381			break;
382
383		GLU_CHECK_GLW_CALL(gl, scissor(0, 0, size.x(), size.y()));
384		GLU_CHECK_GLW_CALL(gl, clearDepthf(depth));
385		GLU_CHECK_GLW_CALL(gl, clear(GL_DEPTH_BUFFER_BIT));
386
387		tcu::clearDepth(tcu::getSubregion(ref.getLevel(0), 0, 0, size.x(), size.y()), depth);
388	}
389
390	GLU_CHECK_GLW_CALL(gl, disable(GL_SCISSOR_TEST));
391	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
392												   GL_RENDERBUFFER, 0));
393
394}
395
396void initializeColorRbo(const glw::Functions& gl, GLuint rbo, Texture2D& ref)
397{
398	static const tcu::Vec4 colorValues[] =
399	{
400		tcu::Vec4(0.9f, 0.5f, 0.65f, 1.0f),
401		tcu::Vec4(0.5f, 0.7f, 0.65f, 1.0f),
402		tcu::Vec4(0.2f, 0.5f, 0.65f, 1.0f),
403		tcu::Vec4(0.3f, 0.1f, 0.5f, 1.0f),
404		tcu::Vec4(0.8f, 0.2f, 0.3f, 1.0f),
405		tcu::Vec4(0.9f, 0.4f, 0.8f, 1.0f),
406	};
407
408	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
409												   GL_RENDERBUFFER, rbo));
410	GLU_CHECK_GLW_CALL(gl, clearColor(1.0f, 1.0f, 0.0f, 1.0f));
411	GLU_CHECK_GLW_CALL(gl, clear(GL_COLOR_BUFFER_BIT));
412	tcu::clear(ref.getLevel(0), Vec4(1.0f, 1.0f, 0.0f, 1.0f));
413
414	// create a pattern
415	GLU_CHECK_GLW_CALL(gl, enable(GL_SCISSOR_TEST));
416	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(colorValues); ++ndx)
417	{
418		const tcu::IVec2	size	= tcu::IVec2((int)((DE_LENGTH_OF_ARRAY(colorValues) - ndx) * (ref.getWidth() / float(DE_LENGTH_OF_ARRAY(colorValues)))),
419												 (int)((DE_LENGTH_OF_ARRAY(colorValues) - ndx) * (ref.getHeight() / float(DE_LENGTH_OF_ARRAY(colorValues) + 4)))); // not symmetric
420
421		if (size.x() == 0 || size.y() == 0)
422			break;
423
424		GLU_CHECK_GLW_CALL(gl, scissor(0, 0, size.x(), size.y()));
425		GLU_CHECK_GLW_CALL(gl, clearColor(colorValues[ndx].x(), colorValues[ndx].y(), colorValues[ndx].z(), colorValues[ndx].w()));
426		GLU_CHECK_GLW_CALL(gl, clear(GL_COLOR_BUFFER_BIT));
427
428		tcu::clear(tcu::getSubregion(ref.getLevel(0), 0, 0, size.x(), size.y()), colorValues[ndx]);
429	}
430
431	GLU_CHECK_GLW_CALL(gl, disable(GL_SCISSOR_TEST));
432	GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
433												   GL_RENDERBUFFER, 0));
434}
435
436MovePtr<ClientBuffer> RenderbufferImageSource::createBuffer (const glw::Functions& gl, Texture2D* ref) const
437{
438	MovePtr<RenderbufferClientBuffer>	buffer	(new RenderbufferClientBuffer(gl));
439	const GLuint						rbo		= buffer->getName();
440
441	GLU_CHECK_CALL_ERROR(gl.bindRenderbuffer(GL_RENDERBUFFER, rbo), gl.getError());
442
443	// Specify storage.
444	GLU_CHECK_CALL_ERROR(gl.renderbufferStorage(GL_RENDERBUFFER, m_format, 64, 64), gl.getError());
445
446	if (ref != DE_NULL)
447	{
448		Framebuffer			fbo			(gl);
449		const TextureFormat	texFormat	= glu::mapGLInternalFormat(m_format);
450
451		*ref = tcu::Texture2D(texFormat, 64, 64);
452		ref->allocLevel(0);
453
454		gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
455		switch (m_format)
456		{
457			case GL_STENCIL_INDEX8:
458				initializeStencilRbo(gl, rbo, *ref);
459				break;
460			case GL_DEPTH_COMPONENT16:
461				initializeDepthRbo(gl, rbo, *ref);
462				break;
463			case GL_RGBA4:
464				initializeColorRbo(gl, rbo, *ref);
465				break;
466			case GL_RGB5_A1:
467				initializeColorRbo(gl, rbo, *ref);
468				break;
469			case GL_RGB565:
470				initializeColorRbo(gl, rbo, *ref);
471				break;
472			default:
473				DE_ASSERT(!"Impossible");
474		}
475
476		gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
477	}
478
479	return MovePtr<ClientBuffer>(buffer);
480}
481
482class UnsupportedImageSource : public ImageSource
483{
484public:
485							UnsupportedImageSource	(const string& message, GLenum format) : m_message(message), m_format(format) {}
486	string					getRequiredExtension	(void) const { fail(); return ""; }
487	MovePtr<ClientBuffer>	createBuffer			(const glw::Functions&, tcu::Texture2D*) const { fail(); return de::MovePtr<ClientBuffer>(); }
488	EGLImageKHR				createImage				(const Library& egl, EGLDisplay dpy, EGLContext ctx, EGLClientBuffer clientBuffer) const;
489	GLenum					getFormat				(void) const { return m_format; }
490
491private:
492	const string			m_message;
493	GLenum					m_format;
494
495	void					fail					(void) const { TCU_THROW(NotSupportedError, m_message.c_str()); }
496};
497
498EGLImageKHR	UnsupportedImageSource::createImage (const Library&, EGLDisplay, EGLContext, EGLClientBuffer) const
499{
500	fail();
501	return EGL_NO_IMAGE_KHR;
502}
503
504MovePtr<ImageSource> createTextureImageSource (EGLenum source, GLenum format, GLenum type, bool useTexLevel0)
505{
506	if (source == EGL_GL_TEXTURE_2D_KHR)
507		return MovePtr<ImageSource>(new Texture2DImageSource(format, type, useTexLevel0));
508	else
509		return MovePtr<ImageSource>(new TextureCubeMapImageSource(source, format, type, useTexLevel0));
510}
511
512MovePtr<ImageSource> createRenderbufferImageSource (GLenum format)
513{
514	return MovePtr<ImageSource>(new RenderbufferImageSource(format));
515}
516
517MovePtr<ImageSource> createUnsupportedImageSource (const string& message, GLenum format)
518{
519	return MovePtr<ImageSource>(new UnsupportedImageSource(message, format));
520}
521
522} // Image
523} // egl
524} // deqp
525