Path: blob/main_old/src/tests/egl_tests/EGLMultiContextTest.cpp
1693 views
//1// Copyright 2021 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// EGLMultiContextTest.cpp:6// Tests relating to multiple non-shared Contexts.78#include <gtest/gtest.h>910#include "test_utils/ANGLETest.h"11#include "test_utils/MultiThreadSteps.h"12#include "test_utils/angle_test_configs.h"13#include "test_utils/gl_raii.h"14#include "util/EGLWindow.h"1516using namespace angle;1718namespace19{2021EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)22{23EGLBoolean result = EGL_TRUE;24if (context != EGL_NO_CONTEXT)25{26result = eglDestroyContext(display, context);27context = EGL_NO_CONTEXT;28}29return result;30}3132class EGLMultiContextTest : public ANGLETest33{34public:35EGLMultiContextTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}3637void testTearDown() override38{39glDeleteTextures(1, &mTexture);4041EGLDisplay display = getEGLWindow()->getDisplay();4243if (display != EGL_NO_DISPLAY)44{45for (auto &context : mContexts)46{47SafeDestroyContext(display, context);48}49}5051// Set default test state to not give an error on shutdown.52getEGLWindow()->makeCurrent();53}5455EGLContext mContexts[2];56GLuint mTexture;57};5859// Test that calling eglDeleteContext on a context that is not current succeeds.60TEST_P(EGLMultiContextTest, TestContextDestroySimple)61{62EGLWindow *window = getEGLWindow();63EGLDisplay dpy = window->getDisplay();6465EGLContext context1 = window->createContext(EGL_NO_CONTEXT);66EGLContext context2 = window->createContext(EGL_NO_CONTEXT);6768EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, context1));69EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));70EXPECT_EGL_SUCCESS();7172// Cleanup73EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));74EXPECT_EGL_TRUE(eglDestroyContext(dpy, context1));75EXPECT_EGL_SUCCESS();76}7778// Test that a compute shader running in one thread will still work when rendering is happening in79// another thread (with non-shared contexts). The non-shared context will still share a Vulkan80// command buffer.81TEST_P(EGLMultiContextTest, ComputeShaderOkayWithRendering)82{83ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());84ANGLE_SKIP_TEST_IF(!isVulkanRenderer());85ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 || getClientMinorVersion() < 1);8687// Initialize contexts88EGLWindow *window = getEGLWindow();89EGLDisplay dpy = window->getDisplay();90EGLConfig config = window->getConfig();9192constexpr size_t kThreadCount = 2;93EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};94EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};9596EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};9798for (size_t t = 0; t < kThreadCount; ++t)99{100surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);101EXPECT_EGL_SUCCESS();102103ctx[t] = window->createContext(EGL_NO_CONTEXT);104EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);105}106107// Synchronization tools to ensure the two threads are interleaved as designed by this test.108std::mutex mutex;109std::condition_variable condVar;110111enum class Step112{113Thread0Start,114Thread0DispatchedCompute,115Thread1Drew,116Thread0DispatchedComputeAgain,117Finish,118Abort,119};120Step currentStep = Step::Thread0Start;121122// This first thread dispatches a compute shader. It immediately starts.123std::thread deletingThread = std::thread([&]() {124ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);125126EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));127EXPECT_EGL_SUCCESS();128129// Potentially wait to be signalled to start.130ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Start));131132// Wake up and do next step: Create, detach, and dispatch a compute shader program.133constexpr char kCS[] = R"(#version 310 es134layout(local_size_x=1) in;135void main()136{137})";138GLuint computeProgram = glCreateProgram();139GLuint cs = CompileShader(GL_COMPUTE_SHADER, kCS);140EXPECT_NE(0u, cs);141142glAttachShader(computeProgram, cs);143glDeleteShader(cs);144glLinkProgram(computeProgram);145GLint linkStatus;146glGetProgramiv(computeProgram, GL_LINK_STATUS, &linkStatus);147EXPECT_GL_TRUE(linkStatus);148glDetachShader(computeProgram, cs);149EXPECT_GL_NO_ERROR();150glUseProgram(computeProgram);151152glDispatchCompute(8, 4, 2);153EXPECT_GL_NO_ERROR();154155// Signal the second thread and wait for it to draw and flush.156threadSynchronization.nextStep(Step::Thread0DispatchedCompute);157ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Drew));158159// Wake up and do next step: Dispatch the same compute shader again.160glDispatchCompute(8, 4, 2);161162// Signal the second thread and wait for it to draw and flush again.163threadSynchronization.nextStep(Step::Thread0DispatchedComputeAgain);164ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));165166// Wake up and do next step: Dispatch the same compute shader again, and force flush the167// underlying command buffer.168glDispatchCompute(8, 4, 2);169glFinish();170171// Clean-up and exit this thread.172EXPECT_GL_NO_ERROR();173EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));174EXPECT_EGL_SUCCESS();175});176177// This second thread renders. It starts once the other thread does its first nextStep()178std::thread continuingThread = std::thread([&]() {179ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);180181EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));182EXPECT_EGL_SUCCESS();183184// Wait for first thread to create and dispatch a compute shader.185ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedCompute));186187// Wake up and do next step: Create graphics resources, draw, and force flush the188// underlying command buffer.189GLTexture texture;190glBindTexture(GL_TEXTURE_2D, texture);191glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);192glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);193glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);194195GLRenderbuffer renderbuffer;196GLFramebuffer fbo;197glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);198constexpr int kRenderbufferSize = 4;199glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);200glBindFramebuffer(GL_FRAMEBUFFER, fbo);201glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,202renderbuffer);203glBindTexture(GL_TEXTURE_2D, texture);204205GLProgram graphicsProgram;206graphicsProgram.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());207ASSERT_TRUE(graphicsProgram.valid());208209drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);210glFinish();211212// Signal the first thread and wait for it to dispatch a compute shader again.213threadSynchronization.nextStep(Step::Thread1Drew);214ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedComputeAgain));215216// Wake up and do next step: Draw and force flush the underlying command buffer again.217drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);218glFinish();219220// Signal the first thread and wait exit this thread.221threadSynchronization.nextStep(Step::Finish);222223EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));224EXPECT_EGL_SUCCESS();225});226227deletingThread.join();228continuingThread.join();229230ASSERT_NE(currentStep, Step::Abort);231232// Clean up233EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));234for (size_t t = 0; t < kThreadCount; ++t)235{236eglDestroySurface(dpy, surface[t]);237eglDestroyContext(dpy, ctx[t]);238}239}240} // anonymous namespace241242GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLMultiContextTest);243ANGLE_INSTANTIATE_TEST_ES31(EGLMultiContextTest);244245246