#include #include #include #include #include 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 CreateFixture() { auto fixture = std::make_unique(); 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 threadAReady; std::promise threadBAttempted; std::promise 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); }