You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
563 lines
17 KiB
563 lines
17 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program EGL Module
|
|
* ---------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Tests for resizing the native window of a surface.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "teglResizeTests.hpp"
|
|
|
|
#include "teglSimpleConfigCase.hpp"
|
|
|
|
#include "tcuImageCompare.hpp"
|
|
#include "tcuSurface.hpp"
|
|
#include "tcuPlatform.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuInterval.hpp"
|
|
#include "tcuTextureUtil.hpp"
|
|
#include "tcuResultCollector.hpp"
|
|
|
|
#include "egluNativeDisplay.hpp"
|
|
#include "egluNativeWindow.hpp"
|
|
#include "egluNativePixmap.hpp"
|
|
#include "egluUnique.hpp"
|
|
#include "egluUtil.hpp"
|
|
|
|
#include "eglwLibrary.hpp"
|
|
#include "eglwEnums.hpp"
|
|
|
|
#include "gluDefs.hpp"
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuVector.hpp"
|
|
|
|
#include "deThread.h"
|
|
#include "deUniquePtr.hpp"
|
|
|
|
#include <sstream>
|
|
|
|
namespace deqp
|
|
{
|
|
namespace egl
|
|
{
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
using std::ostringstream;
|
|
using de::MovePtr;
|
|
using tcu::CommandLine;
|
|
using tcu::ConstPixelBufferAccess;
|
|
using tcu::Interval;
|
|
using tcu::IVec2;
|
|
using tcu::Vec3;
|
|
using tcu::Vec4;
|
|
using tcu::UVec4;
|
|
using tcu::ResultCollector;
|
|
using tcu::Surface;
|
|
using tcu::TestLog;
|
|
using eglu::AttribMap;
|
|
using eglu::NativeDisplay;
|
|
using eglu::NativeWindow;
|
|
using eglu::ScopedCurrentContext;
|
|
using eglu::UniqueSurface;
|
|
using eglu::UniqueContext;
|
|
using eglu::NativeWindowFactory;
|
|
using eglu::WindowParams;
|
|
using namespace eglw;
|
|
|
|
typedef eglu::WindowParams::Visibility Visibility;
|
|
typedef TestCase::IterateResult IterateResult;
|
|
|
|
struct ResizeParams
|
|
{
|
|
string name;
|
|
string description;
|
|
IVec2 oldSize;
|
|
IVec2 newSize;
|
|
};
|
|
|
|
class ResizeTest : public TestCase
|
|
{
|
|
public:
|
|
ResizeTest (EglTestContext& eglTestCtx,
|
|
const ResizeParams& params)
|
|
: TestCase (eglTestCtx,
|
|
params.name.c_str(),
|
|
params.description.c_str())
|
|
, m_oldSize (params.oldSize)
|
|
, m_newSize (params.newSize)
|
|
, m_display (EGL_NO_DISPLAY)
|
|
, m_config (DE_NULL)
|
|
, m_log (m_testCtx.getLog())
|
|
, m_status (m_log) {}
|
|
|
|
void init (void);
|
|
void deinit (void);
|
|
|
|
protected:
|
|
virtual EGLenum surfaceType (void) const { return EGL_WINDOW_BIT; }
|
|
void resize (IVec2 size);
|
|
|
|
const IVec2 m_oldSize;
|
|
const IVec2 m_newSize;
|
|
EGLDisplay m_display;
|
|
EGLConfig m_config;
|
|
MovePtr<NativeWindow> m_nativeWindow;
|
|
MovePtr<UniqueSurface> m_surface;
|
|
MovePtr<UniqueContext> m_context;
|
|
TestLog& m_log;
|
|
ResultCollector m_status;
|
|
glw::Functions m_gl;
|
|
};
|
|
|
|
EGLConfig getEGLConfig (const Library& egl, const EGLDisplay eglDisplay, EGLenum surfaceType)
|
|
{
|
|
AttribMap attribMap;
|
|
|
|
attribMap[EGL_SURFACE_TYPE] = surfaceType;
|
|
attribMap[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
|
|
|
|
return eglu::chooseSingleConfig(egl, eglDisplay, attribMap);
|
|
}
|
|
|
|
void ResizeTest::init (void)
|
|
{
|
|
TestCase::init();
|
|
|
|
const Library& egl = m_eglTestCtx.getLibrary();
|
|
const CommandLine& cmdLine = m_testCtx.getCommandLine();
|
|
const EGLDisplay eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
|
|
const EGLConfig eglConfig = getEGLConfig(egl, eglDisplay, surfaceType());
|
|
const EGLint ctxAttribs[] =
|
|
{
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE
|
|
};
|
|
EGLContext eglContext = egl.createContext(eglDisplay,
|
|
eglConfig,
|
|
EGL_NO_CONTEXT,
|
|
ctxAttribs);
|
|
EGLU_CHECK_MSG(egl, "eglCreateContext()");
|
|
MovePtr<UniqueContext> context (new UniqueContext(egl, eglDisplay, eglContext));
|
|
const EGLint configId = eglu::getConfigAttribInt(egl,
|
|
eglDisplay,
|
|
eglConfig,
|
|
EGL_CONFIG_ID);
|
|
const Visibility visibility = eglu::parseWindowVisibility(cmdLine);
|
|
NativeDisplay& nativeDisplay = m_eglTestCtx.getNativeDisplay();
|
|
const NativeWindowFactory& windowFactory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(),
|
|
cmdLine);
|
|
|
|
const WindowParams windowParams (m_oldSize.x(), m_oldSize.y(), visibility);
|
|
MovePtr<NativeWindow> nativeWindow (windowFactory.createWindow(&nativeDisplay,
|
|
eglDisplay,
|
|
eglConfig,
|
|
DE_NULL,
|
|
windowParams));
|
|
const EGLSurface eglSurface = eglu::createWindowSurface(nativeDisplay,
|
|
*nativeWindow,
|
|
eglDisplay,
|
|
eglConfig,
|
|
DE_NULL);
|
|
MovePtr<UniqueSurface> surface (new UniqueSurface(egl, eglDisplay, eglSurface));
|
|
|
|
m_log << TestLog::Message
|
|
<< "Chose EGLConfig with id " << configId << ".\n"
|
|
<< "Created initial surface with size " << m_oldSize
|
|
<< TestLog::EndMessage;
|
|
|
|
m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
|
|
m_config = eglConfig;
|
|
m_surface = surface;
|
|
m_context = context;
|
|
m_display = eglDisplay;
|
|
m_nativeWindow = nativeWindow;
|
|
EGLU_CHECK_MSG(egl, "init");
|
|
}
|
|
|
|
void ResizeTest::deinit (void)
|
|
{
|
|
m_config = DE_NULL;
|
|
m_context.clear();
|
|
m_surface.clear();
|
|
m_nativeWindow.clear();
|
|
|
|
if (m_display != EGL_NO_DISPLAY)
|
|
{
|
|
m_eglTestCtx.getLibrary().terminate(m_display);
|
|
m_display = EGL_NO_DISPLAY;
|
|
}
|
|
}
|
|
|
|
void ResizeTest::resize (IVec2 size)
|
|
{
|
|
m_nativeWindow->setSurfaceSize(size);
|
|
m_testCtx.getPlatform().processEvents();
|
|
m_log << TestLog::Message
|
|
<< "Resized surface to size " << size
|
|
<< TestLog::EndMessage;
|
|
}
|
|
|
|
class ChangeSurfaceSizeCase : public ResizeTest
|
|
{
|
|
public:
|
|
ChangeSurfaceSizeCase (EglTestContext& eglTestCtx,
|
|
const ResizeParams& params)
|
|
: ResizeTest(eglTestCtx, params) {}
|
|
|
|
IterateResult iterate (void);
|
|
};
|
|
|
|
void drawRectangle (const glw::Functions& gl, IVec2 pos, IVec2 size, Vec3 color)
|
|
{
|
|
gl.clearColor(color.x(), color.y(), color.z(), 1.0);
|
|
gl.scissor(pos.x(), pos.y(), size.x(), size.y());
|
|
gl.enable(GL_SCISSOR_TEST);
|
|
gl.clear(GL_COLOR_BUFFER_BIT);
|
|
gl.disable(GL_SCISSOR_TEST);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(),
|
|
"Rectangle drawing with glScissor and glClear failed.");
|
|
}
|
|
|
|
void initSurface (const glw::Functions& gl, IVec2 oldSize)
|
|
{
|
|
const Vec3 frameColor (0.0f, 0.0f, 1.0f);
|
|
const Vec3 fillColor (1.0f, 0.0f, 0.0f);
|
|
const Vec3 markColor (0.0f, 1.0f, 0.0f);
|
|
|
|
drawRectangle(gl, IVec2(0, 0), oldSize, frameColor);
|
|
drawRectangle(gl, IVec2(2, 2), oldSize - IVec2(4, 4), fillColor);
|
|
|
|
drawRectangle(gl, IVec2(0, 0), IVec2(8, 4), markColor);
|
|
drawRectangle(gl, oldSize - IVec2(16, 16), IVec2(8, 4), markColor);
|
|
drawRectangle(gl, IVec2(0, oldSize.y() - 16), IVec2(8, 4), markColor);
|
|
drawRectangle(gl, IVec2(oldSize.x() - 16, 0), IVec2(8, 4), markColor);
|
|
}
|
|
|
|
bool compareRectangles (const ConstPixelBufferAccess& rectA,
|
|
const ConstPixelBufferAccess& rectB)
|
|
{
|
|
const int width = rectA.getWidth();
|
|
const int height = rectA.getHeight();
|
|
const int depth = rectA.getDepth();
|
|
|
|
if (rectB.getWidth() != width || rectB.getHeight() != height || rectB.getDepth() != depth)
|
|
return false;
|
|
|
|
for (int z = 0; z < depth; ++z)
|
|
for (int y = 0; y < height; ++y)
|
|
for (int x = 0; x < width; ++x)
|
|
if (rectA.getPixel(x, y, z) != rectB.getPixel(x, y, z))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check whether `oldSurface` and `newSurface` share a common corner.
|
|
bool compareCorners (const Surface& oldSurface, const Surface& newSurface)
|
|
{
|
|
const int oldWidth = oldSurface.getWidth();
|
|
const int oldHeight = oldSurface.getHeight();
|
|
const int newWidth = newSurface.getWidth();
|
|
const int newHeight = newSurface.getHeight();
|
|
const int minWidth = de::min(oldWidth, newWidth);
|
|
const int minHeight = de::min(oldHeight, newHeight);
|
|
|
|
for (int xCorner = 0; xCorner < 2; ++xCorner)
|
|
{
|
|
const int oldX = xCorner == 0 ? 0 : oldWidth - minWidth;
|
|
const int newX = xCorner == 0 ? 0 : newWidth - minWidth;
|
|
|
|
for (int yCorner = 0; yCorner < 2; ++yCorner)
|
|
{
|
|
const int oldY = yCorner == 0 ? 0 : oldHeight - minHeight;
|
|
const int newY = yCorner == 0 ? 0 : newHeight - minHeight;
|
|
ConstPixelBufferAccess oldAccess =
|
|
getSubregion(oldSurface.getAccess(), oldX, oldY, minWidth, minHeight);
|
|
ConstPixelBufferAccess newAccess =
|
|
getSubregion(newSurface.getAccess(), newX, newY, minWidth, minHeight);
|
|
|
|
if (compareRectangles(oldAccess, newAccess))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Surface readSurface (const glw::Functions& gl, IVec2 size)
|
|
{
|
|
Surface ret (size.x(), size.y());
|
|
gl.readPixels(0, 0, size.x(), size.y(), GL_RGBA, GL_UNSIGNED_BYTE,
|
|
ret.getAccess().getDataPtr());
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
|
|
return ret;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool hasBits (T bitSet, T requiredBits)
|
|
{
|
|
return (bitSet & requiredBits) == requiredBits;
|
|
}
|
|
|
|
IVec2 getNativeSurfaceSize (const NativeWindow& nativeWindow,
|
|
IVec2 reqSize)
|
|
{
|
|
if (hasBits(nativeWindow.getCapabilities(), NativeWindow::CAPABILITY_GET_SURFACE_SIZE))
|
|
return nativeWindow.getSurfaceSize();
|
|
return reqSize; // assume we got the requested size
|
|
}
|
|
|
|
IVec2 checkSurfaceSize (const Library& egl,
|
|
EGLDisplay eglDisplay,
|
|
EGLSurface eglSurface,
|
|
const NativeWindow& nativeWindow,
|
|
IVec2 reqSize,
|
|
ResultCollector& status)
|
|
{
|
|
const IVec2 nativeSize = getNativeSurfaceSize(nativeWindow, reqSize);
|
|
IVec2 eglSize = eglu::getSurfaceSize(egl, eglDisplay, eglSurface);
|
|
ostringstream oss;
|
|
|
|
oss << "Size of EGL surface " << eglSize
|
|
<< " differs from size of native window " << nativeSize;
|
|
status.check(eglSize == nativeSize, oss.str());
|
|
|
|
return eglSize;
|
|
}
|
|
|
|
IterateResult ChangeSurfaceSizeCase::iterate (void)
|
|
{
|
|
const Library& egl = m_eglTestCtx.getLibrary();
|
|
ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
|
|
egl.swapBuffers(m_display, **m_surface);
|
|
IVec2 oldEglSize = checkSurfaceSize(egl,
|
|
m_display,
|
|
**m_surface,
|
|
*m_nativeWindow,
|
|
m_oldSize,
|
|
m_status);
|
|
|
|
initSurface(m_gl, oldEglSize);
|
|
|
|
this->resize(m_newSize);
|
|
|
|
egl.swapBuffers(m_display, **m_surface);
|
|
EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
|
|
checkSurfaceSize(egl, m_display, **m_surface, *m_nativeWindow, m_newSize, m_status);
|
|
|
|
m_status.setTestContextResult(m_testCtx);
|
|
return STOP;
|
|
}
|
|
|
|
class PreserveBackBufferCase : public ResizeTest
|
|
{
|
|
public:
|
|
PreserveBackBufferCase (EglTestContext& eglTestCtx,
|
|
const ResizeParams& params)
|
|
: ResizeTest(eglTestCtx, params) {}
|
|
|
|
IterateResult iterate (void);
|
|
EGLenum surfaceType (void) const;
|
|
};
|
|
|
|
EGLenum PreserveBackBufferCase::surfaceType (void) const
|
|
{
|
|
return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
|
|
}
|
|
|
|
IterateResult PreserveBackBufferCase::iterate (void)
|
|
{
|
|
const Library& egl = m_eglTestCtx.getLibrary();
|
|
ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
|
|
|
|
EGLU_CHECK_CALL(egl, surfaceAttrib(m_display, **m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
|
|
|
|
GLU_EXPECT_NO_ERROR(m_gl.getError(), "GL state erroneous upon initialization!");
|
|
|
|
{
|
|
const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
|
|
initSurface(m_gl, oldEglSize);
|
|
|
|
m_gl.finish();
|
|
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFinish() failed");
|
|
|
|
{
|
|
const Surface oldSurface = readSurface(m_gl, oldEglSize);
|
|
|
|
egl.swapBuffers(m_display, **m_surface);
|
|
this->resize(m_newSize);
|
|
egl.swapBuffers(m_display, **m_surface);
|
|
EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
|
|
|
|
{
|
|
const IVec2 newEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
|
|
const Surface newSurface = readSurface(m_gl, newEglSize);
|
|
|
|
m_log << TestLog::ImageSet("Corner comparison",
|
|
"Comparing old and new surfaces at all corners")
|
|
<< TestLog::Image("Before resizing", "Before resizing", oldSurface)
|
|
<< TestLog::Image("After resizing", "After resizing", newSurface)
|
|
<< TestLog::EndImageSet;
|
|
|
|
m_status.checkResult(compareCorners(oldSurface, newSurface),
|
|
QP_TEST_RESULT_QUALITY_WARNING,
|
|
"Resizing the native window changed the contents of "
|
|
"the EGL surface");
|
|
}
|
|
}
|
|
}
|
|
|
|
m_status.setTestContextResult(m_testCtx);
|
|
return STOP;
|
|
}
|
|
|
|
typedef tcu::Vector<Interval, 2> IvVec2;
|
|
|
|
class UpdateResolutionCase : public ResizeTest
|
|
{
|
|
public:
|
|
UpdateResolutionCase (EglTestContext& eglTestCtx,
|
|
const ResizeParams& params)
|
|
: ResizeTest(eglTestCtx, params) {}
|
|
|
|
IterateResult iterate (void);
|
|
|
|
private:
|
|
IvVec2 getNativePixelsPerInch (void);
|
|
};
|
|
|
|
IvVec2 ivVec2 (const IVec2& vec)
|
|
{
|
|
return IvVec2(double(vec.x()), double(vec.y()));
|
|
}
|
|
|
|
Interval approximateInt (int i)
|
|
{
|
|
const Interval margin(-1.0, 1.0); // The resolution may be rounded
|
|
|
|
return (Interval(i) + margin) & Interval(0.0, TCU_INFINITY);
|
|
}
|
|
|
|
IvVec2 UpdateResolutionCase::getNativePixelsPerInch (void)
|
|
{
|
|
const Library& egl = m_eglTestCtx.getLibrary();
|
|
const int inchPer10km = 254 * EGL_DISPLAY_SCALING;
|
|
const IVec2 bufSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
|
|
const IVec2 winSize = m_nativeWindow->getScreenSize();
|
|
const IVec2 bufPp10km = eglu::getSurfaceResolution(egl, m_display, **m_surface);
|
|
const IvVec2 bufPpiI = (IvVec2(approximateInt(bufPp10km.x()),
|
|
approximateInt(bufPp10km.y()))
|
|
/ Interval(inchPer10km));
|
|
const IvVec2 winPpiI = ivVec2(winSize) * bufPpiI / ivVec2(bufSize);
|
|
const IVec2 winPpi (int(winPpiI.x().midpoint()), int(winPpiI.y().midpoint()));
|
|
|
|
m_log << TestLog::Message
|
|
<< "EGL surface size: " << bufSize << "\n"
|
|
<< "EGL surface pixel density (pixels / 10 km): " << bufPp10km << "\n"
|
|
<< "Native window size: " << winSize << "\n"
|
|
<< "Native pixel density (ppi): " << winPpi << "\n"
|
|
<< TestLog::EndMessage;
|
|
|
|
m_status.checkResult(bufPp10km.x() >= 1 && bufPp10km.y() >= 1,
|
|
QP_TEST_RESULT_QUALITY_WARNING,
|
|
"Surface pixel density is less than one pixel per 10 km. "
|
|
"Is the surface really visible from space?");
|
|
|
|
return winPpiI;
|
|
}
|
|
|
|
IterateResult UpdateResolutionCase::iterate (void)
|
|
{
|
|
const Library& egl = m_eglTestCtx.getLibrary();
|
|
ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
|
|
|
|
if (!hasBits(m_nativeWindow->getCapabilities(),
|
|
NativeWindow::CAPABILITY_GET_SCREEN_SIZE))
|
|
TCU_THROW(NotSupportedError, "Unable to determine surface size in screen pixels");
|
|
|
|
{
|
|
const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
|
|
initSurface(m_gl, oldEglSize);
|
|
}
|
|
{
|
|
const IvVec2 oldPpi = this->getNativePixelsPerInch();
|
|
this->resize(m_newSize);
|
|
EGLU_CHECK_CALL(egl, swapBuffers(m_display, **m_surface));
|
|
{
|
|
const IvVec2 newPpi = this->getNativePixelsPerInch();
|
|
m_status.check(oldPpi.x().intersects(newPpi.x()) &&
|
|
oldPpi.y().intersects(newPpi.y()),
|
|
"Window PPI differs after resizing");
|
|
}
|
|
}
|
|
|
|
m_status.setTestContextResult(m_testCtx);
|
|
return STOP;
|
|
}
|
|
|
|
ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
|
|
: TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
|
|
{
|
|
}
|
|
|
|
template <class Case>
|
|
TestCaseGroup* createCaseGroup(EglTestContext& eglTestCtx,
|
|
const string& name,
|
|
const string& desc)
|
|
{
|
|
const ResizeParams params[] =
|
|
{
|
|
{ "shrink", "Shrink in both dimensions",
|
|
IVec2(128, 128), IVec2(32, 32) },
|
|
{ "grow", "Grow in both dimensions",
|
|
IVec2(32, 32), IVec2(128, 128) },
|
|
{ "stretch_width", "Grow horizontally, shrink vertically",
|
|
IVec2(32, 128), IVec2(128, 32) },
|
|
{ "stretch_height", "Grow vertically, shrink horizontally",
|
|
IVec2(128, 32), IVec2(32, 128) },
|
|
};
|
|
TestCaseGroup* const group =
|
|
new TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str());
|
|
|
|
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(params); ++ndx)
|
|
group->addChild(new Case(eglTestCtx, params[ndx]));
|
|
|
|
return group;
|
|
}
|
|
|
|
void ResizeTests::init (void)
|
|
{
|
|
addChild(createCaseGroup<ChangeSurfaceSizeCase>(m_eglTestCtx,
|
|
"surface_size",
|
|
"EGL surface size update"));
|
|
addChild(createCaseGroup<PreserveBackBufferCase>(m_eglTestCtx,
|
|
"back_buffer",
|
|
"Back buffer contents"));
|
|
addChild(createCaseGroup<UpdateResolutionCase>(m_eglTestCtx,
|
|
"pixel_density",
|
|
"Pixel density"));
|
|
}
|
|
|
|
} // egl
|
|
} // deqp
|