blob: ef42f1aa39d381a7dec19a5118790bfbc917352b [file] [log] [blame]
//
// Copyright (c) 2017 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.
//
// EGLSurfacelessContextTest.cpp:
// Tests for the EGL_KHR_surfaceless_context and GL_OES_surfaceless_context
#include <gtest/gtest.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_configs.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
class EGLSurfacelessContextTest : public ANGLETest
{
public:
EGLSurfacelessContextTest() : mDisplay(0) {}
void SetUp() override
{
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
eglGetProcAddress("eglGetPlatformDisplayEXT"));
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_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
int nConfigs = 0;
ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs));
ASSERT_TRUE(nConfigs != 0);
int nReturnedConfigs = 0;
std::vector<EGLConfig> configs(nConfigs);
ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, configs.data(), nConfigs, &nReturnedConfigs));
ASSERT_TRUE(nConfigs == nReturnedConfigs);
for (auto config : configs)
{
EGLint surfaceType;
eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
if (surfaceType & EGL_PBUFFER_BIT)
{
mConfig = config;
break;
}
}
ASSERT_NE(nullptr, mConfig);
}
void TearDown() override
{
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (mContext != EGL_NO_CONTEXT)
{
eglDestroyContext(mDisplay, mContext);
}
if (mContext != EGL_NO_SURFACE)
{
eglDestroySurface(mDisplay, mPbuffer);
}
eglTerminate(mDisplay);
}
protected:
EGLContext createContext()
{
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs);
EXPECT_TRUE(mContext != EGL_NO_CONTEXT);
return mContext;
}
EGLSurface createPbuffer(int width, int height)
{
const EGLint pbufferAttribs[] = {
EGL_WIDTH, 500, EGL_HEIGHT, 500, EGL_NONE,
};
mPbuffer = eglCreatePbufferSurface(mDisplay, mConfig, pbufferAttribs);
EXPECT_TRUE(mPbuffer != EGL_NO_SURFACE);
return mPbuffer;
}
void createFramebuffer(GLFramebuffer *fbo, GLTexture *tex) const
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo->get());
glBindTexture(GL_TEXTURE_2D, tex->get());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 500, 500, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->get(), 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
bool checkExtension(bool verbose = true) const
{
if (!ANGLETest::eglDisplayExtensionEnabled(mDisplay, "EGL_KHR_surfaceless_context"))
{
if (verbose)
{
std::cout << "Test skipped because EGL_KHR_surfaceless_context is not available."
<< std::endl;
}
return false;
}
return true;
}
EGLContext mContext = EGL_NO_CONTEXT;
EGLSurface mPbuffer = EGL_NO_SURFACE;
EGLConfig mConfig = 0;
EGLDisplay mDisplay = EGL_NO_DISPLAY;
};
// Test surfaceless MakeCurrent returns the correct value.
TEST_P(EGLSurfacelessContextTest, MakeCurrentSurfaceless)
{
EGLContext context = createContext();
if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context))
{
ASSERT_TRUE(checkExtension(false));
}
else
{
// The extension allows EGL_BAD_MATCH with the extension too, but ANGLE
// shouldn't do that.
ASSERT_FALSE(checkExtension(false));
}
}
// Test that the scissor and viewport are set correctly
TEST_P(EGLSurfacelessContextTest, DefaultScissorAndViewport)
{
if (!checkExtension())
{
return;
}
EGLContext context = createContext();
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
GLint scissor[4] = {1, 2, 3, 4};
glGetIntegerv(GL_SCISSOR_BOX, scissor);
ASSERT_GL_NO_ERROR();
ASSERT_EQ(0, scissor[0]);
ASSERT_EQ(0, scissor[1]);
ASSERT_EQ(0, scissor[2]);
ASSERT_EQ(0, scissor[3]);
GLint viewport[4] = {1, 2, 3, 4};
glGetIntegerv(GL_VIEWPORT, viewport);
ASSERT_GL_NO_ERROR();
ASSERT_EQ(0, viewport[0]);
ASSERT_EQ(0, viewport[1]);
ASSERT_EQ(0, viewport[2]);
ASSERT_EQ(0, viewport[3]);
}
// Test the CheckFramebufferStatus returns the correct value.
TEST_P(EGLSurfacelessContextTest, CheckFramebufferStatus)
{
if (!checkExtension())
{
return;
}
EGLContext context = createContext();
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_UNDEFINED_OES, glCheckFramebufferStatus(GL_FRAMEBUFFER));
GLFramebuffer fbo;
GLTexture tex;
createFramebuffer(&fbo, &tex);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
// Test that clearing and readpixels work when done in an FBO.
TEST_P(EGLSurfacelessContextTest, ClearReadPixelsInFBO)
{
if (!checkExtension())
{
return;
}
EGLContext context = createContext();
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
GLFramebuffer fbo;
GLTexture tex;
createFramebuffer(&fbo, &tex);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test clear+readpixels in an FBO in surfaceless and in the default FBO in a pbuffer
TEST_P(EGLSurfacelessContextTest, Switcheroo)
{
if (!checkExtension())
{
return;
}
EGLContext context = createContext();
EGLSurface pbuffer = createPbuffer(500, 500);
// We need to make the context current to do the one time setup of the FBO
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
GLFramebuffer fbo;
GLTexture tex;
createFramebuffer(&fbo, &tex);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
for (int i = 0; i < 4; ++i)
{
// Clear to red in the FBO in surfaceless mode
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
ASSERT_GL_NO_ERROR();
// Clear to green in the pbuffer
ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, pbuffer, pbuffer, context));
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::green);
ASSERT_GL_NO_ERROR();
}
}
} // anonymous namespace
ANGLE_INSTANTIATE_TEST(EGLSurfacelessContextTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
ES2_VULKAN());