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