Files
MobileGL/MobileGL/MG_Test/EGLState/EGLStateTest.cpp
T

135 lines
6.2 KiB
C++

#include <gtest/gtest.h>
#include <MG_State/EGLState/Core.h>
#include <future>
#include <memory>
#include <thread>
namespace {
using StateContext = MobileGL::MG_State::EGLState::EGLContext;
struct EGLFixture {
StateContext State;
EGLDisplay Display = EGL_NO_DISPLAY;
EGLConfig Config = nullptr;
EGLSurface Surface = EGL_NO_SURFACE;
StateContext::EGLContextHandle Context = EGL_NO_CONTEXT;
};
std::unique_ptr<EGLFixture> CreateFixture() {
auto fixture = std::make_unique<EGLFixture>();
fixture->Display = fixture->State.GetDisplay(EGL_DEFAULT_DISPLAY);
EXPECT_NE(fixture->Display, EGL_NO_DISPLAY);
EXPECT_TRUE(fixture->State.InitializeDisplay(fixture->Display, nullptr, nullptr));
EGLint configCount = 0;
EXPECT_TRUE(fixture->State.ChooseConfig(fixture->Display, nullptr, &fixture->Config, 1, &configCount));
EXPECT_GE(configCount, 1);
EXPECT_NE(fixture->Config, nullptr);
const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE};
fixture->Surface = fixture->State.CreatePbufferSurface(fixture->Display, fixture->Config, surfaceAttribs);
EXPECT_NE(fixture->Surface, EGL_NO_SURFACE);
fixture->Context = fixture->State.CreateContext(fixture->Display, fixture->Config, EGL_NO_CONTEXT, nullptr);
EXPECT_NE(fixture->Context, EGL_NO_CONTEXT);
return fixture;
}
}
TEST(EGLStateMakeCurrent, ContextCannotBeCurrentOnTwoThreadsAtOnce) {
auto fixture = CreateFixture();
std::promise<void> threadAReady;
std::promise<void> threadBAttempted;
std::promise<void> threadAReleased;
auto threadAReadyFuture = threadAReady.get_future();
auto threadBAttemptedFuture = threadBAttempted.get_future();
auto threadAReleasedFuture = threadAReleased.get_future();
bool threadAAttachOk = false;
bool threadAReleaseOk = false;
bool threadBDenied = false;
EGLint threadBDeniedError = EGL_SUCCESS;
bool threadBAttachAfterReleaseOk = false;
EGLint threadBAttachAfterReleaseError = EGL_SUCCESS;
std::thread threadA([&] {
threadAAttachOk = fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface,
fixture->Context);
threadAReady.set_value();
threadBAttemptedFuture.wait();
threadAReleaseOk =
fixture->State.MakeCurrent(fixture->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
threadAReleased.set_value();
});
std::thread threadB([&] {
threadAReadyFuture.wait();
threadBDenied = fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface,
fixture->Context);
threadBDeniedError = fixture->State.ConsumeError();
threadBAttempted.set_value();
threadAReleasedFuture.wait();
threadBAttachAfterReleaseOk =
fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context);
threadBAttachAfterReleaseError = fixture->State.ConsumeError();
if (threadBAttachAfterReleaseOk) {
(void)fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
});
threadA.join();
threadB.join();
EXPECT_TRUE(threadAAttachOk);
EXPECT_TRUE(threadAReleaseOk);
EXPECT_FALSE(threadBDenied);
EXPECT_EQ(threadBDeniedError, EGL_BAD_ACCESS);
EXPECT_TRUE(threadBAttachAfterReleaseOk);
EXPECT_EQ(threadBAttachAfterReleaseError, EGL_SUCCESS);
}
TEST(EGLStateMakeCurrent, SameThreadRepeatedAttachReleaseDoesNotLeaveStaleOwner) {
auto fixture = CreateFixture();
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context));
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context));
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context));
EXPECT_TRUE(fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context));
EXPECT_TRUE(fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EQ(fixture->State.ConsumeError(), EGL_SUCCESS);
}
// The compatibility-profile accessor is affirmative-only (it backs GL_CONTEXT_PROFILE_MASK
// reporting): attrib-less contexts (profile mask 0) and released threads both answer "not
// compat" and therefore read as core-profile contexts.
TEST(EGLStateProfile, CompatibilityProfileRequiresExplicitCompatBit) {
auto fixture = CreateFixture();
EXPECT_FALSE(fixture->State.IsCurrentContextOpenGLCompatibilityProfile());
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context));
EXPECT_FALSE(fixture->State.IsCurrentContextOpenGLCompatibilityProfile());
const EGLint compatAttribs[] = {EGL_CONTEXT_MAJOR_VERSION,
3,
EGL_CONTEXT_MINOR_VERSION,
3,
EGL_CONTEXT_OPENGL_PROFILE_MASK,
EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
EGL_NONE};
const auto compatContext =
fixture->State.CreateContext(fixture->Display, fixture->Config, EGL_NO_CONTEXT, compatAttribs);
ASSERT_NE(compatContext, EGL_NO_CONTEXT);
EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, compatContext));
EXPECT_TRUE(fixture->State.IsCurrentContextOpenGLCompatibilityProfile());
EXPECT_FALSE(fixture->State.IsCurrentContextOpenGLCoreProfile());
EXPECT_TRUE(fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_FALSE(fixture->State.IsCurrentContextOpenGLCompatibilityProfile());
EXPECT_EQ(fixture->State.ConsumeError(), EGL_SUCCESS);
}