xref: /aosp_15_r20/external/angle/samples/multiple_contexts/MultipleContexts.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Demonstrates GLES usage with two contexts, one uploading texture data.
7 
8 #include "SampleApplication.h"
9 #include "util/random_utils.h"
10 #include "util/shader_utils.h"
11 #include "util/test_utils.h"
12 
13 #include <array>
14 #include <mutex>
15 #include <queue>
16 #include <thread>
17 
18 struct TextureAndFence
19 {
20     GLuint textureID;
21     GLsync fenceSync;
22 };
23 
24 constexpr GLuint64 kTimeout         = 10000000;
25 static constexpr GLint kTextureSize = 2;
26 constexpr size_t kWindowWidth       = 400;
27 constexpr size_t kWindowHeight      = 300;
28 
UpdateThreadLoop(EGLDisplay display,EGLConfig config,EGLContext shareContext,std::mutex * updateThreadMutex,std::queue<TextureAndFence> * updateThreadQueue,std::mutex * mainThreadMutex,std::queue<TextureAndFence> * mainThreadQueue)29 void UpdateThreadLoop(EGLDisplay display,
30                       EGLConfig config,
31                       EGLContext shareContext,
32                       std::mutex *updateThreadMutex,
33                       std::queue<TextureAndFence> *updateThreadQueue,
34                       std::mutex *mainThreadMutex,
35                       std::queue<TextureAndFence> *mainThreadQueue)
36 {
37     angle::RNG rng;
38 
39     EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_NONE};
40     EGLContext context      = eglCreateContext(display, config, shareContext, contextAttribs);
41 
42     EGLint surfaceAttribs[] = {EGL_NONE};
43     EGLSurface surface      = eglCreatePbufferSurface(display, config, surfaceAttribs);
44 
45     eglMakeCurrent(display, surface, surface, context);
46 
47     for (;;)
48     {
49         bool hasUpdate = false;
50         TextureAndFence textureAndFence;
51 
52         {
53             std::lock_guard<std::mutex> lock(*updateThreadMutex);
54             if (!updateThreadQueue->empty())
55             {
56                 textureAndFence = updateThreadQueue->back();
57                 hasUpdate       = true;
58                 updateThreadQueue->pop();
59             }
60         }
61 
62         if (hasUpdate)
63         {
64             if (textureAndFence.textureID == 0)
65             {
66                 // Signal from the main thread to stop execution.
67                 break;
68             }
69 
70             glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
71             glDeleteSync(textureAndFence.fenceSync);
72             glBindTexture(GL_TEXTURE_2D, textureAndFence.textureID);
73 
74             std::vector<uint8_t> bytes(3, 0);
75             FillVectorWithRandomUBytes(&rng, &bytes);
76             bytes.push_back(255);
77 
78             std::vector<uint8_t> textureData;
79             for (GLint x = 0; x < kTextureSize; ++x)
80             {
81                 for (GLint y = 0; y < kTextureSize; ++y)
82                 {
83                     textureData.insert(textureData.end(), bytes.begin(), bytes.end());
84                 }
85             }
86 
87             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTextureSize, kTextureSize, GL_RGBA,
88                             GL_UNSIGNED_BYTE, textureData.data());
89 
90             GLsync mainThreadSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
91 
92             {
93                 std::lock_guard<std::mutex> lock(*mainThreadMutex);
94                 mainThreadQueue->push({textureAndFence.textureID, mainThreadSync});
95             }
96         }
97 
98         angle::Sleep(200);
99     }
100 
101     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
102     eglDestroySurface(display, surface);
103     eglDestroyContext(display, context);
104 }
105 
106 class MultipleContextsSample : public SampleApplication
107 {
108   public:
MultipleContextsSample(int argc,char ** argv)109     MultipleContextsSample(int argc, char **argv)
110         : SampleApplication("MultipleContexts",
111                             argc,
112                             argv,
113                             ClientType::ES3_0,
114                             kWindowWidth,
115                             kWindowHeight)
116     {}
117 
initialize()118     bool initialize() override
119     {
120         // Initialize some textures and send them to the update thread.
121         glGenTextures(kNumTextures, mTextures.data());
122 
123         for (GLuint texture : mTextures)
124         {
125             glBindTexture(GL_TEXTURE_2D, texture);
126             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
127                          GL_UNSIGNED_BYTE, nullptr);
128             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
129             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
130             glBindTexture(GL_TEXTURE_2D, 0);
131             GLsync fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
132 
133             mUpdateThreadQueue.push({texture, fenceSync});
134         }
135 
136         mUpdateThread.reset(new std::thread(UpdateThreadLoop, getDisplay(), getConfig(),
137                                             getContext(), &mUpdateThreadMutex, &mUpdateThreadQueue,
138                                             &mMainThreadMutex, &mMainThreadQueue));
139 
140         mProgram = CompileProgram(angle::essl1_shaders::vs::Texture2D(),
141                                   angle::essl1_shaders::fs::Texture2D());
142         glUseProgram(mProgram);
143         glEnableVertexAttribArray(0);
144 
145         glGenBuffers(1, &mVertexBuffer);
146         glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
147         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * 6, nullptr, GL_DYNAMIC_DRAW);
148 
149         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
150 
151         return true;
152     }
153 
destroy()154     void destroy() override
155     {
156         {
157             // Signal the worker thread to stop execution.
158             std::lock_guard<std::mutex> lock(mUpdateThreadMutex);
159             mUpdateThreadQueue.push({0, 0});
160         }
161 
162         for (;;)
163         {
164             {
165                 std::lock_guard<std::mutex> mainLock(mMainThreadMutex);
166                 if (mMainThreadQueue.empty())
167                 {
168                     std::lock_guard<std::mutex> updateLock(mUpdateThreadMutex);
169                     if (mUpdateThreadQueue.empty())
170                     {
171                         break;
172                     }
173                 }
174                 else
175                 {
176                     TextureAndFence textureAndFence = mMainThreadQueue.back();
177                     mMainThreadQueue.pop();
178                     glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
179                     glDeleteSync(textureAndFence.fenceSync);
180                 }
181             }
182         }
183 
184         glDeleteTextures(kNumTextures, mTextures.data());
185         glDeleteProgram(mProgram);
186         glDeleteBuffers(1, &mVertexBuffer);
187     }
188 
draw()189     void draw() override
190     {
191         bool hasUpdate = false;
192         TextureAndFence textureAndFence;
193         while (!hasUpdate)
194         {
195             {
196                 std::lock_guard<std::mutex> lock(mMainThreadMutex);
197                 if (!mMainThreadQueue.empty())
198                 {
199                     hasUpdate       = true;
200                     textureAndFence = mMainThreadQueue.back();
201                     mMainThreadQueue.pop();
202                 }
203             }
204         }
205 
206         glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
207         glDeleteSync(textureAndFence.fenceSync);
208         glBindTexture(GL_TEXTURE_2D, textureAndFence.textureID);
209 
210         constexpr size_t kNumRows    = 3;
211         constexpr size_t kNumCols    = 4;
212         constexpr size_t kTileHeight = kWindowHeight / kNumRows;
213         constexpr size_t kTileWidth  = kWindowWidth / kNumCols;
214 
215         size_t tileX = mDrawCount % kNumCols;
216         size_t tileY = (mDrawCount / kNumCols) % kNumRows;
217 
218         mDrawCount++;
219 
220         GLfloat tileX0 = static_cast<float>(tileX) / static_cast<float>(kNumCols);
221         GLfloat tileY0 = static_cast<float>(tileY) / static_cast<float>(kNumRows);
222         GLfloat tileX1 = tileX0 + static_cast<float>(kTileWidth) / static_cast<float>(kWindowWidth);
223         GLfloat tileY1 =
224             tileY0 + static_cast<float>(kTileHeight) / static_cast<float>(kWindowHeight);
225 
226         tileX0 = tileX0 * 2.0f - 1.0f;
227         tileX1 = tileX1 * 2.0f - 1.0f;
228         tileY0 = tileY0 * 2.0f - 1.0f;
229         tileY1 = tileY1 * 2.0f - 1.0f;
230 
231         std::vector<GLfloat> vertices = {tileX0, tileY0, tileX0, tileY1, tileX1, tileY0,
232                                          tileX1, tileY0, tileX1, tileY1, tileX0, tileY1};
233 
234         glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices[0]) * vertices.size(), vertices.data());
235 
236         glClear(GL_COLOR_BUFFER_BIT);
237         glDrawArrays(GL_TRIANGLES, 0, 6);
238 
239         GLsync drawSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
240         {
241             std::lock_guard<std::mutex> lock(mUpdateThreadMutex);
242             mUpdateThreadQueue.push({textureAndFence.textureID, drawSync});
243         }
244     }
245 
246   private:
247     std::unique_ptr<std::thread> mUpdateThread;
248 
249     static constexpr GLuint kNumTextures = 4;
250     std::array<GLuint, kNumTextures> mTextures;
251 
252     GLuint mProgram      = 0;
253     GLuint mVertexBuffer = 0;
254 
255     std::mutex mUpdateThreadMutex;
256     std::queue<TextureAndFence> mUpdateThreadQueue;
257     std::mutex mMainThreadMutex;
258     std::queue<TextureAndFence> mMainThreadQueue;
259     size_t mDrawCount = 0;
260 };
261 
main(int argc,char ** argv)262 int main(int argc, char **argv)
263 {
264     MultipleContextsSample app(argc, argv);
265     return app.run();
266 }
267