blob: 00c88373b15a795b86d8ab6f26ddb86af8a56522 [file] [log] [blame]
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLContextCompatibilityTest.cpp:
// This test will try to use all combinations of context configs and
// surface configs. If the configs are compatible, it checks that simple
// rendering works, otherwise it checks an error is generated one MakeCurrent.
//
// Only run the EGLContextCompatibilityTest on release builds. The execution time of this test
// scales with the square of the number of configs exposed and can time out in some debug builds.
// http://anglebug.com/2121
#include <gtest/gtest.h>
#include <vector>
#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_configs.h"
#include "test_utils/angle_test_instantiate.h"
#include "util/OSWindow.h"
using namespace angle;
namespace
{
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
}
class EGLContextCompatibilityTest : public ANGLETest
{
public:
EGLContextCompatibilityTest() : mDisplay(0) {}
void testSetUp() override
{
ASSERT_TRUE(eglGetPlatformDisplayEXT != nullptr);
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
mDisplay = eglGetPlatformDisplayEXT(
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
int nConfigs = 0;
ASSERT_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs) == EGL_TRUE);
ASSERT_TRUE(nConfigs != 0);
int nReturnedConfigs = 0;
mConfigs.resize(nConfigs);
ASSERT_TRUE(eglGetConfigs(mDisplay, mConfigs.data(), nConfigs, &nReturnedConfigs) ==
EGL_TRUE);
ASSERT_TRUE(nConfigs == nReturnedConfigs);
}
void testTearDown() override
{
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mDisplay);
}
protected:
// Queries EGL config to determine if multisampled or not
bool isMultisampledConfig(EGLConfig config)
{
EGLint samples = 0;
eglGetConfigAttrib(mDisplay, config, EGL_SAMPLES, &samples);
return (samples > 1);
}
// The only configs with 16-bits for each of red, green, blue, and alpha is GL_RGBA16F
bool isRGBA16FConfig(EGLConfig config)
{
EGLint red, green, blue, alpha;
eglGetConfigAttrib(mDisplay, config, EGL_RED_SIZE, &red);
eglGetConfigAttrib(mDisplay, config, EGL_GREEN_SIZE, &green);
eglGetConfigAttrib(mDisplay, config, EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib(mDisplay, config, EGL_ALPHA_SIZE, &alpha);
return ((red == 16) && (green == 16) && (blue == 16) && (alpha == 16));
}
bool isRGB10_A2Config(EGLConfig config)
{
EGLint red, green, blue, alpha;
eglGetConfigAttrib(mDisplay, config, EGL_RED_SIZE, &red);
eglGetConfigAttrib(mDisplay, config, EGL_GREEN_SIZE, &green);
eglGetConfigAttrib(mDisplay, config, EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib(mDisplay, config, EGL_ALPHA_SIZE, &alpha);
return ((red == 10) && (green == 10) && (blue == 10) && (alpha == 2));
}
bool areConfigsCompatible(EGLConfig c1, EGLConfig c2, EGLint surfaceBit)
{
EGLint colorBufferType1, colorBufferType2;
EGLint red1, red2, green1, green2, blue1, blue2, alpha1, alpha2;
EGLint depth1, depth2, stencil1, stencil2;
EGLint surfaceType1, surfaceType2;
eglGetConfigAttrib(mDisplay, c1, EGL_COLOR_BUFFER_TYPE, &colorBufferType1);
eglGetConfigAttrib(mDisplay, c2, EGL_COLOR_BUFFER_TYPE, &colorBufferType2);
eglGetConfigAttrib(mDisplay, c1, EGL_RED_SIZE, &red1);
eglGetConfigAttrib(mDisplay, c2, EGL_RED_SIZE, &red2);
eglGetConfigAttrib(mDisplay, c1, EGL_GREEN_SIZE, &green1);
eglGetConfigAttrib(mDisplay, c2, EGL_GREEN_SIZE, &green2);
eglGetConfigAttrib(mDisplay, c1, EGL_BLUE_SIZE, &blue1);
eglGetConfigAttrib(mDisplay, c2, EGL_BLUE_SIZE, &blue2);
eglGetConfigAttrib(mDisplay, c1, EGL_ALPHA_SIZE, &alpha1);
eglGetConfigAttrib(mDisplay, c2, EGL_ALPHA_SIZE, &alpha2);
eglGetConfigAttrib(mDisplay, c1, EGL_DEPTH_SIZE, &depth1);
eglGetConfigAttrib(mDisplay, c2, EGL_DEPTH_SIZE, &depth2);
eglGetConfigAttrib(mDisplay, c1, EGL_STENCIL_SIZE, &stencil1);
eglGetConfigAttrib(mDisplay, c2, EGL_STENCIL_SIZE, &stencil2);
eglGetConfigAttrib(mDisplay, c1, EGL_SURFACE_TYPE, &surfaceType1);
eglGetConfigAttrib(mDisplay, c2, EGL_SURFACE_TYPE, &surfaceType2);
EGLint colorComponentType1 = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
EGLint colorComponentType2 = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
if (IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_pixel_format_float"))
{
eglGetConfigAttrib(mDisplay, c1, EGL_COLOR_COMPONENT_TYPE_EXT, &colorComponentType1);
eglGetConfigAttrib(mDisplay, c2, EGL_COLOR_COMPONENT_TYPE_EXT, &colorComponentType2);
}
EXPECT_EGL_SUCCESS();
return colorBufferType1 == colorBufferType2 && red1 == red2 && green1 == green2 &&
blue1 == blue2 && alpha1 == alpha2 && colorComponentType1 == colorComponentType2 &&
depth1 == depth2 && stencil1 == stencil2 && (surfaceType1 & surfaceBit) != 0 &&
(surfaceType2 & surfaceBit) != 0;
}
void testWindowCompatibility(EGLConfig windowConfig,
EGLConfig contextConfig,
bool compatible) const
{
OSWindow *osWindow = OSWindow::New();
ASSERT_TRUE(osWindow != nullptr);
osWindow->initialize("EGLContextCompatibilityTest", 500, 500);
osWindow->setVisible(true);
EGLContext context =
eglCreateContext(mDisplay, contextConfig, EGL_NO_CONTEXT, contextAttribs);
ASSERT_TRUE(context != EGL_NO_CONTEXT);
EGLSurface window =
eglCreateWindowSurface(mDisplay, windowConfig, osWindow->getNativeWindow(), nullptr);
ASSERT_EGL_SUCCESS();
if (compatible)
{
testClearSurface(window, windowConfig, context);
}
else
{
testMakeCurrentFails(window, context);
}
eglDestroySurface(mDisplay, window);
ASSERT_EGL_SUCCESS();
eglDestroyContext(mDisplay, context);
ASSERT_EGL_SUCCESS();
OSWindow::Delete(&osWindow);
}
void testPbufferCompatibility(EGLConfig pbufferConfig,
EGLConfig contextConfig,
bool compatible) const
{
EGLContext context =
eglCreateContext(mDisplay, contextConfig, EGL_NO_CONTEXT, contextAttribs);
ASSERT_TRUE(context != EGL_NO_CONTEXT);
const EGLint pBufferAttribs[] = {
EGL_WIDTH, 500, EGL_HEIGHT, 500, EGL_NONE,
};
EGLSurface pbuffer = eglCreatePbufferSurface(mDisplay, pbufferConfig, pBufferAttribs);
ASSERT_TRUE(pbuffer != EGL_NO_SURFACE);
if (compatible)
{
testClearSurface(pbuffer, pbufferConfig, context);
}
else
{
testMakeCurrentFails(pbuffer, context);
}
eglDestroySurface(mDisplay, pbuffer);
ASSERT_EGL_SUCCESS();
eglDestroyContext(mDisplay, context);
ASSERT_EGL_SUCCESS();
}
std::vector<EGLConfig> mConfigs;
EGLDisplay mDisplay;
private:
void testClearSurface(EGLSurface surface, EGLConfig surfaceConfig, EGLContext context) const
{
eglMakeCurrent(mDisplay, surface, surface, context);
ASSERT_EGL_SUCCESS();
glViewport(0, 0, 500, 500);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EGLint surfaceCompontentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
if (IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_pixel_format_float"))
{
eglGetConfigAttrib(mDisplay, surfaceConfig, EGL_COLOR_COMPONENT_TYPE_EXT,
&surfaceCompontentType);
}
if (surfaceCompontentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT)
{
EXPECT_PIXEL_EQ(250, 250, 0, 0, 255, 255);
}
else
{
EXPECT_PIXEL_32F_EQ(250, 250, 0, 0, 1.0f, 1.0f);
}
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
ASSERT_EGL_SUCCESS();
}
void testMakeCurrentFails(EGLSurface surface, EGLContext context) const
{
eglMakeCurrent(mDisplay, surface, surface, context);
EXPECT_EGL_ERROR(EGL_BAD_MATCH);
}
};
// The test is split in several subtest so that simple cases
// are tested separately. Also each surface types are not tested
// together.
// Basic test checking contexts and windows created with the
// same config can render.
TEST_P(EGLContextCompatibilityTest, WindowSameConfig)
{
ANGLE_SKIP_TEST_IF(IsDebug()); // http://anglebug.com/2121
for (size_t i = 0; i < mConfigs.size(); i++)
{
EGLConfig config = mConfigs[i];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
if ((surfaceType & EGL_WINDOW_BIT) != 0)
{
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGBA16F/RGB10_A2 on Android due to OSWindow on Android not providing
// compatible windows (anglebug.com/3156)
if (isMultisampledConfig(config) ||
(IsAndroid() && (isRGB10_A2Config(config) || isRGBA16FConfig(config))))
{
continue;
}
testWindowCompatibility(config, config, true);
}
}
}
// Basic test checking contexts and pbuffers created with the
// same config can render.
TEST_P(EGLContextCompatibilityTest, PbufferSameConfig)
{
ANGLE_SKIP_TEST_IF(IsDebug()); // http://anglebug.com/2121
for (size_t i = 0; i < mConfigs.size(); i++)
{
EGLConfig config = mConfigs[i];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
if ((surfaceType & EGL_PBUFFER_BIT) != 0)
{
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGB10_A2 on Android due to OSWindow on Android not providing compatible
// windows (anglebug.com/3156)
if (isMultisampledConfig(config) || (IsAndroid() && isRGB10_A2Config(config)))
{
continue;
}
testPbufferCompatibility(config, config, true);
}
}
}
// Check that a context rendering to a window with a different
// config works or errors according to the EGL compatibility rules
TEST_P(EGLContextCompatibilityTest, WindowDifferentConfig)
{
ANGLE_SKIP_TEST_IF(IsDebug()); // http://anglebug.com/2121
// anglebug.com/2183
// Actually failed only on (IsIntel() && IsWindows() && IsD3D11()),
// but it's impossible to do other tests since GL_RENDERER is NULL
ANGLE_SKIP_TEST_IF(IsWindows());
for (size_t i = 0; i < mConfigs.size(); i++)
{
EGLConfig config1 = mConfigs[i];
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGBA16F/RGB10_A2 on Android due to OSWindow on Android not providing
// compatible windows (anglebug.com/3156)
if (isMultisampledConfig(config1) ||
(IsAndroid() && (isRGB10_A2Config(config1) || isRGBA16FConfig(config1))))
{
continue;
}
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config1, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
if ((surfaceType & EGL_WINDOW_BIT) == 0)
{
continue;
}
for (size_t j = 0; j < mConfigs.size(); j++)
{
EGLConfig config2 = mConfigs[j];
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGBA16F/RGB10_A2 on Android due to OSWindow on Android not providing
// compatible windows (anglebug.com/3156)
if (isMultisampledConfig(config2) ||
(IsAndroid() && (isRGB10_A2Config(config2) || isRGBA16FConfig(config2))))
{
continue;
}
testWindowCompatibility(config1, config2,
areConfigsCompatible(config1, config2, EGL_WINDOW_BIT));
}
}
}
// Check that a context rendering to a pbuffer with a different
// config works or errors according to the EGL compatibility rules
TEST_P(EGLContextCompatibilityTest, PbufferDifferentConfig)
{
ANGLE_SKIP_TEST_IF(IsDebug()); // http://anglebug.com/2121
for (size_t i = 0; i < mConfigs.size(); i++)
{
EGLConfig config1 = mConfigs[i];
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGB10_A2 on Android due to OSWindow on Android not providing compatible
// windows (anglebug.com/3156)
if (isMultisampledConfig(config1) || (IsAndroid() && isRGB10_A2Config(config1)))
{
continue;
}
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config1, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
if ((surfaceType & EGL_PBUFFER_BIT) == 0)
{
continue;
}
for (size_t j = 0; j < mConfigs.size(); j++)
{
EGLConfig config2 = mConfigs[j];
// Disabling multisampled configurations due to test instability with various graphics
// cards, and RGB10_A2 on Android due to OSWindow on Android not providing compatible
// windows (anglebug.com/3156)
if (isMultisampledConfig(config2) || (IsAndroid() && isRGB10_A2Config(config2)))
{
continue;
}
testPbufferCompatibility(config1, config2,
areConfigsCompatible(config1, config2, EGL_PBUFFER_BIT));
}
}
}
ANGLE_INSTANTIATE_TEST(EGLContextCompatibilityTest,
WithNoFixture(ES2_D3D9()),
WithNoFixture(ES2_D3D11()),
WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES2_OPENGLES()),
WithNoFixture(ES2_VULKAN()));