blob: 17208fd25270a8f2e93f480aca4f66b1af410709 [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.
//
#include <gtest/gtest.h>
#include <vector>
#include "common/debug.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_configs.h"
#include "test_utils/angle_test_instantiate.h"
#include "util/OSWindow.h"
#include "util/random_utils.h"
using namespace angle;
namespace
{
// The only configs with 16-bits for each of red, green, blue, and alpha is GL_RGBA16F
bool IsRGBA16FConfig(EGLDisplay display, EGLConfig config)
{
EGLint red, green, blue, alpha;
eglGetConfigAttrib(display, config, EGL_RED_SIZE, &red);
eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &green);
eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alpha);
return ((red == 16) && (green == 16) && (blue == 16) && (alpha == 16));
}
bool IsRGB10_A2Config(EGLDisplay display, EGLConfig config)
{
EGLint red, green, blue, alpha;
eglGetConfigAttrib(display, config, EGL_RED_SIZE, &red);
eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &green);
eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alpha);
return ((red == 10) && (green == 10) && (blue == 10) && (alpha == 2));
}
// Queries EGL config to determine if multisampled or not
bool IsMultisampledConfig(EGLDisplay display, EGLConfig config)
{
EGLint samples = 0;
eglGetConfigAttrib(display, config, EGL_SAMPLES, &samples);
return (samples > 1);
}
bool ShouldSkipConfig(EGLDisplay display, EGLConfig config, bool windowSurfaceTest)
{
// Skip multisampled configurations due to test instability.
if (IsMultisampledConfig(display, config))
return true;
// Disable RGBA16F/RGB10_A2 on Android due to OSWindow on Android not providing compatible
// windows (http://anglebug.com/3156)
if (IsAndroid())
{
if (IsRGB10_A2Config(display, config))
return true;
if (IsRGBA16FConfig(display, config))
return windowSurfaceTest;
}
return false;
}
std::vector<EGLConfig> GetConfigs(EGLDisplay display)
{
int nConfigs = 0;
if (eglGetConfigs(display, nullptr, 0, &nConfigs) != EGL_TRUE)
{
std::cerr << "EGLContextCompatiblityTest: eglGetConfigs error\n";
return {};
}
if (nConfigs == 0)
{
std::cerr << "EGLContextCompatiblityTest: no configs\n";
return {};
}
std::vector<EGLConfig> configs;
int nReturnedConfigs = 0;
configs.resize(nConfigs);
if (eglGetConfigs(display, configs.data(), nConfigs, &nReturnedConfigs) != EGL_TRUE)
{
std::cerr << "EGLContextCompatiblityTest: eglGetConfigs error\n";
return {};
}
if (nConfigs != nReturnedConfigs)
{
std::cerr << "EGLContextCompatiblityTest: eglGetConfigs returned wrong count\n";
return {};
}
return configs;
}
PlatformParameters FromRenderer(EGLint renderer)
{
return WithNoFixture(PlatformParameters(2, 0, EGLPlatformParameters(renderer)));
}
std::string EGLConfigName(EGLDisplay display, EGLConfig config)
{
EGLint red;
eglGetConfigAttrib(display, config, EGL_RED_SIZE, &red);
EGLint green;
eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &green);
EGLint blue;
eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blue);
EGLint alpha;
eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alpha);
EGLint depth;
eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depth);
EGLint stencil;
eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencil);
EGLint samples;
eglGetConfigAttrib(display, config, EGL_SAMPLES, &samples);
std::stringstream strstr;
if (red > 0)
{
strstr << "R" << red;
}
if (green > 0)
{
strstr << "G" << green;
}
if (blue > 0)
{
strstr << "B" << blue;
}
if (alpha > 0)
{
strstr << "A" << alpha;
}
if (depth > 0)
{
strstr << "D" << depth;
}
if (stencil > 0)
{
strstr << "S" << stencil;
}
if (samples > 0)
{
strstr << "MS" << samples;
}
return strstr.str();
}
const std::array<EGLint, 3> kContextAttribs = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
class EGLContextCompatibilityTest : public ANGLETestBase, public testing::Test
{
public:
EGLContextCompatibilityTest(EGLint renderer)
: ANGLETestBase(FromRenderer(renderer)), mRenderer(renderer)
{}
void SetUp() final
{
ANGLETestBase::ANGLETestSetUp();
ASSERT_TRUE(eglGetPlatformDisplayEXT != nullptr);
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, mRenderer, 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 TearDown() final
{
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mDisplay);
ANGLETestBase::ANGLETestTearDown();
}
protected:
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);
EGLContext context =
eglCreateContext(mDisplay, contextConfig, EGL_NO_CONTEXT, kContextAttribs.data());
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, kContextAttribs.data());
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 = EGL_NO_DISPLAY;
EGLint mRenderer = 0;
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.
class EGLContextCompatibilityTest_WindowSameConfig : public EGLContextCompatibilityTest
{
public:
EGLContextCompatibilityTest_WindowSameConfig(EGLint renderer, size_t configIndex)
: EGLContextCompatibilityTest(renderer), mConfigIndex(configIndex)
{}
void TestBody() override
{
EGLConfig config = mConfigs[mConfigIndex];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
ANGLE_SKIP_TEST_IF((surfaceType & EGL_WINDOW_BIT) == 0);
testWindowCompatibility(config, config, true);
}
EGLint mConfigIndex;
};
// Basic test checking contexts and pbuffers created with the
// same config can render.
class EGLContextCompatibilityTest_PbufferSameConfig : public EGLContextCompatibilityTest
{
public:
EGLContextCompatibilityTest_PbufferSameConfig(EGLint renderer, size_t configIndex)
: EGLContextCompatibilityTest(renderer), mConfigIndex(configIndex)
{}
void TestBody() override
{
EGLConfig config = mConfigs[mConfigIndex];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
ANGLE_SKIP_TEST_IF((surfaceType & EGL_PBUFFER_BIT) == 0);
testPbufferCompatibility(config, config, true);
}
EGLint mConfigIndex;
};
// Check that a context rendering to a window with a different
// config works or errors according to the EGL compatibility rules
class EGLContextCompatibilityTest_WindowDifferentConfig : public EGLContextCompatibilityTest
{
public:
EGLContextCompatibilityTest_WindowDifferentConfig(EGLint renderer,
size_t configIndexA,
size_t configIndexB)
: EGLContextCompatibilityTest(renderer),
mConfigIndexA(configIndexA),
mConfigIndexB(configIndexB)
{}
void TestBody() override
{
EGLConfig config1 = mConfigs[mConfigIndexA];
EGLConfig config2 = mConfigs[mConfigIndexB];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config1, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
ANGLE_SKIP_TEST_IF((surfaceType & EGL_WINDOW_BIT) == 0);
testWindowCompatibility(config1, config2,
areConfigsCompatible(config1, config2, EGL_WINDOW_BIT));
}
EGLint mConfigIndexA;
EGLint mConfigIndexB;
};
// Check that a context rendering to a pbuffer with a different
// config works or errors according to the EGL compatibility rules
class EGLContextCompatibilityTest_PbufferDifferentConfig : public EGLContextCompatibilityTest
{
public:
EGLContextCompatibilityTest_PbufferDifferentConfig(EGLint renderer,
size_t configIndexA,
size_t configIndexB)
: EGLContextCompatibilityTest(renderer),
mConfigIndexA(configIndexA),
mConfigIndexB(configIndexB)
{}
void TestBody() override
{
EGLConfig config1 = mConfigs[mConfigIndexA];
EGLConfig config2 = mConfigs[mConfigIndexB];
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config1, EGL_SURFACE_TYPE, &surfaceType);
ASSERT_EGL_SUCCESS();
ANGLE_SKIP_TEST_IF((surfaceType & EGL_PBUFFER_BIT) == 0);
testPbufferCompatibility(config1, config2,
areConfigsCompatible(config1, config2, EGL_PBUFFER_BIT));
}
EGLint mConfigIndexA;
EGLint mConfigIndexB;
};
} // namespace
void RegisterContextCompatibilityTests()
{
std::vector<EGLint> renderers = {{
EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE,
}};
LoadEntryPointsWithUtilLoader(angle::GLESDriverType::AngleEGL);
if (eglGetPlatformDisplayEXT == nullptr)
{
std::cerr << "EGLContextCompatiblityTest: missing eglGetPlatformDisplayEXT\n";
return;
}
for (EGLint renderer : renderers)
{
PlatformParameters params = FromRenderer(renderer);
if (IsPlatformAvailable(params))
continue;
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, renderer, EGL_NONE};
EGLDisplay display = eglGetPlatformDisplayEXT(
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
if (display == EGL_NO_DISPLAY)
{
std::cerr << "EGLContextCompatiblityTest: eglGetPlatformDisplayEXT error\n";
return;
}
if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE)
{
std::cerr << "EGLContextCompatiblityTest: eglInitialize error\n";
return;
}
std::vector<EGLConfig> configs = GetConfigs(display);
std::vector<std::string> configNames;
std::string rendererName = GetRendererName(renderer);
for (EGLConfig config : configs)
{
configNames.push_back(EGLConfigName(display, config));
}
for (size_t configIndex = 0; configIndex < configs.size(); ++configIndex)
{
if (ShouldSkipConfig(display, configs[configIndex], true))
continue;
std::stringstream nameStr;
nameStr << "WindowSameConfig/" << rendererName << "_" << configNames[configIndex];
std::string name = nameStr.str();
testing::RegisterTest(
"EGLContextCompatibilityTest", name.c_str(), nullptr, nullptr, __FILE__, __LINE__,
[renderer, configIndex]() -> EGLContextCompatibilityTest * {
return new EGLContextCompatibilityTest_WindowSameConfig(renderer, configIndex);
});
}
for (size_t configIndex = 0; configIndex < configs.size(); ++configIndex)
{
if (ShouldSkipConfig(display, configs[configIndex], false))
continue;
std::stringstream nameStr;
nameStr << "PbufferSameConfig/" << rendererName << "_" << configNames[configIndex];
std::string name = nameStr.str();
testing::RegisterTest(
"EGLContextCompatibilityTest", name.c_str(), nullptr, nullptr, __FILE__, __LINE__,
[renderer, configIndex]() -> EGLContextCompatibilityTest * {
return new EGLContextCompatibilityTest_PbufferSameConfig(renderer, configIndex);
});
}
// Because there are so many permutations, we skip some configs randomly.
// Attempt to run at most 100 tests per renderer.
RNG rng(0);
constexpr uint32_t kMaximumTestsPerRenderer = 100;
const uint32_t kTestCount = static_cast<uint32_t>(configs.size() * configs.size());
const float kSkipP =
1.0f - (static_cast<float>(std::min(kMaximumTestsPerRenderer, kTestCount)) /
static_cast<float>(kTestCount));
for (size_t configIndexA = 0; configIndexA < configs.size(); ++configIndexA)
{
if (ShouldSkipConfig(display, configs[configIndexA], true))
continue;
std::string configNameA = configNames[configIndexA];
for (size_t configIndexB = 0; configIndexB < configs.size(); ++configIndexB)
{
if (ShouldSkipConfig(display, configs[configIndexB], true))
continue;
if (rng.randomFloat() < kSkipP)
continue;
std::string configNameB = configNames[configIndexB];
std::stringstream nameStr;
nameStr << "WindowDifferentConfig/" << rendererName << "_" << configNameA << "_"
<< configNameB;
std::string name = nameStr.str();
testing::RegisterTest(
"EGLContextCompatibilityTest", name.c_str(), nullptr, nullptr, __FILE__,
__LINE__,
[renderer, configIndexA, configIndexB]() -> EGLContextCompatibilityTest * {
return new EGLContextCompatibilityTest_WindowDifferentConfig(
renderer, configIndexA, configIndexB);
});
}
}
for (size_t configIndexA = 0; configIndexA < configs.size(); ++configIndexA)
{
if (ShouldSkipConfig(display, configs[configIndexA], false))
continue;
std::string configNameA = configNames[configIndexA];
for (size_t configIndexB = 0; configIndexB < configs.size(); ++configIndexB)
{
if (ShouldSkipConfig(display, configs[configIndexB], false))
continue;
if (rng.randomFloat() < kSkipP)
continue;
std::string configNameB = configNames[configIndexB];
std::stringstream nameStr;
nameStr << "PbufferDifferentConfig/" << rendererName << "_" << configNameA << "_"
<< configNameB;
std::string name = nameStr.str();
testing::RegisterTest(
"EGLContextCompatibilityTest", name.c_str(), nullptr, nullptr, __FILE__,
__LINE__,
[renderer, configIndexA, configIndexB]() -> EGLContextCompatibilityTest * {
return new EGLContextCompatibilityTest_PbufferDifferentConfig(
renderer, configIndexA, configIndexB);
});
}
}
if (eglTerminate(display) == EGL_FALSE)
{
std::cerr << "EGLContextCompatiblityTest: eglTerminate error\n";
return;
}
}
}