1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "SampleApplication.h"
16
17 #include "base/GLObjectCounter.h"
18 #include "base/ConditionVariable.h"
19 #include "base/Lock.h"
20 #include "base/System.h"
21 #include "base/testing/TestSystem.h"
22 #include "base/FunctorThread.h"
23 #include "host-common/AndroidAgentFactory.h"
24 #include "host-common/multi_display_agent.h"
25 #include "host-common/MultiDisplay.h"
26 #include "Standalone.h"
27
28 #include <EGL/egl.h>
29 #include <EGL/eglext.h>
30 #include <GLES3/gl3.h>
31
32 using android::base::AutoLock;
33 using android::base::ConditionVariable;
34 using android::base::FunctorThread;
35 using android::base::Lock;
36 using android::base::MessageChannel;
37 using android::base::TestSystem;
38
39 namespace emugl {
40
41 // Class holding the persistent test window.
42 class TestWindow {
43 public:
TestWindow()44 TestWindow() {
45 window = CreateOSWindow();
46 }
47
~TestWindow()48 ~TestWindow() {
49 if (window) {
50 window->destroy();
51 }
52 }
53
setRect(int xoffset,int yoffset,int width,int height)54 void setRect(int xoffset, int yoffset, int width, int height) {
55 if (mFirstResize) {
56 initializeWithRect(xoffset, yoffset, width, height);
57 } else {
58 resizeWithRect(xoffset, yoffset, width, height);
59 }
60 }
61
62 // Check on initialization if windows are available.
initializeWithRect(int xoffset,int yoffset,int width,int height)63 bool initializeWithRect(int xoffset, int yoffset, int width, int height) {
64 if (!window->initialize("libOpenglRender test", width, height)) {
65 window->destroy();
66 window = nullptr;
67 return false;
68 }
69 window->setVisible(true);
70 window->setPosition(xoffset, yoffset);
71 window->messageLoop();
72 mFirstResize = false;
73 return true;
74 }
75
resizeWithRect(int xoffset,int yoffset,int width,int height)76 void resizeWithRect(int xoffset, int yoffset, int width, int height) {
77 if (!window) return;
78
79 window->setPosition(xoffset, yoffset);
80 window->resize(width, height);
81 window->messageLoop();
82 }
83
84 OSWindow* window = nullptr;
85 private:
86 bool mFirstResize = true;
87 };
88
sTestWindow()89 static TestWindow* sTestWindow() {
90 static TestWindow* w = new TestWindow;
91 return w;
92 }
93
shouldUseHostGpu()94 bool shouldUseHostGpu() {
95 bool useHost = android::base::getEnvironmentVariable("ANDROID_EMU_TEST_WITH_HOST_GPU") == "1";
96
97 // Also set the global emugl renderer accordingly.
98 if (useHost) {
99 emugl::setRenderer(SELECTED_RENDERER_HOST);
100 } else {
101 emugl::setRenderer(SELECTED_RENDERER_SWIFTSHADER_INDIRECT);
102 }
103
104 return useHost;
105 }
106
shouldUseWindow()107 bool shouldUseWindow() {
108 bool useWindow = android::base::getEnvironmentVariable("ANDROID_EMU_TEST_WITH_WINDOW") == "1";
109 return useWindow;
110 }
111
createOrGetTestWindow(int xoffset,int yoffset,int width,int height)112 OSWindow* createOrGetTestWindow(int xoffset, int yoffset, int width, int height) {
113 if (!shouldUseWindow()) return nullptr;
114
115 sTestWindow()->setRect(xoffset, yoffset, width, height);
116 return sTestWindow()->window;
117 }
118
119 class Vsync {
120 public:
Vsync(int refreshRate=60)121 Vsync(int refreshRate = 60) :
122 mRefreshRate(refreshRate),
123 mRefreshIntervalUs(1000000ULL / mRefreshRate),
124 mThread([this] {
125 while (true) {
126 if (mShouldStop) return 0;
127 android::base::sleepUs(mRefreshIntervalUs);
128 AutoLock lock(mLock);
129 mSync = 1;
130 mCv.signal();
131 }
132 return 0;
133 }) {
134 mThread.start();
135 }
136
~Vsync()137 ~Vsync() {
138 mShouldStop = true;
139 }
140
waitUntilNextVsync()141 void waitUntilNextVsync() {
142 AutoLock lock(mLock);
143 mSync = 0;
144 while (!mSync) {
145 mCv.wait(&mLock);
146 }
147 }
148
149 private:
150 int mShouldStop = false;
151 int mRefreshRate = 60;
152 uint64_t mRefreshIntervalUs;
153 volatile int mSync = 0;
154
155 Lock mLock;
156 ConditionVariable mCv;
157
158 FunctorThread mThread;
159 };
160
161 // app -> SF queue: separate storage, bindTexture blits
162 // SF queue -> HWC: shared storage
163 class ColorBufferQueue { // Note: we could have called this BufferQueue but there is another
164 // class of name BufferQueue that does something totally different
165
166 public:
167 static constexpr int kCapacity = 3;
168 class Item {
169 public:
Item(unsigned int cb=0,FenceSync * s=nullptr)170 Item(unsigned int cb = 0, FenceSync* s = nullptr) : colorBuffer(cb), sync(s) { }
171 unsigned int colorBuffer = 0;
172 FenceSync* sync = nullptr;
173 };
174
175 ColorBufferQueue() = default;
176
queueBuffer(const Item & item)177 void queueBuffer(const Item& item) {
178 mQueue.send(item);
179 }
180
dequeueBuffer(Item * outItem)181 void dequeueBuffer(Item* outItem) {
182 mQueue.receive(outItem);
183 }
184
185 private:
186 MessageChannel<Item, kCapacity> mQueue;
187 };
188
189 class AutoComposeDevice {
190 public:
AutoComposeDevice(uint32_t targetCb,uint32_t layerCnt=2)191 AutoComposeDevice(uint32_t targetCb, uint32_t layerCnt = 2) :
192 mData(sizeof(ComposeDevice) + layerCnt * sizeof(ComposeLayer))
193 {
194 mComposeDevice = reinterpret_cast<ComposeDevice*>(mData.data());
195 mComposeDevice->version = 1;
196 mComposeDevice->targetHandle = targetCb;
197 mComposeDevice->numLayers = layerCnt;
198 }
199
get()200 ComposeDevice* get() {
201 return mComposeDevice;
202 }
203
getSize()204 uint32_t getSize() {
205 return mData.size();
206 }
207
configureLayer(uint32_t layerId,unsigned int cb,hwc2_composition_t composeMode,hwc_rect_t displayFrame,hwc_frect_t crop,hwc2_blend_mode_t blendMode,float alpha,hwc_color_t color)208 void configureLayer(uint32_t layerId, unsigned int cb,
209 hwc2_composition_t composeMode,
210 hwc_rect_t displayFrame,
211 hwc_frect_t crop,
212 hwc2_blend_mode_t blendMode,
213 float alpha,
214 hwc_color_t color
215 ) {
216 mComposeDevice->layer[layerId].cbHandle = cb;
217 mComposeDevice->layer[layerId].composeMode = composeMode;
218 mComposeDevice->layer[layerId].displayFrame = displayFrame;
219 mComposeDevice->layer[layerId].crop = crop;
220 mComposeDevice->layer[layerId].blendMode = blendMode;
221 mComposeDevice->layer[layerId].alpha = alpha;
222 mComposeDevice->layer[layerId].color = color;
223 mComposeDevice->layer[layerId].transform = HWC_TRANSFORM_FLIP_H;
224 }
225
226 private:
227 std::vector<uint8_t> mData;
228 ComposeDevice* mComposeDevice;
229 };
230
231 extern "C" const QAndroidMultiDisplayAgent* const gMockQAndroidMultiDisplayAgent;
232
233 // SampleApplication implementation/////////////////////////////////////////////
SampleApplication(int windowWidth,int windowHeight,int refreshRate,GLESApi glVersion,bool compose)234 SampleApplication::SampleApplication(int windowWidth, int windowHeight, int refreshRate, GLESApi glVersion, bool compose) :
235 mWidth(windowWidth), mHeight(windowHeight), mRefreshRate(refreshRate), mIsCompose(compose) {
236
237 // setupStandaloneLibrarySearchPaths();
238 emugl::setGLObjectCounter(android::base::GLObjectCounter::get());
239 emugl::set_emugl_window_operations(*getConsoleAgents()->emu);;
240 emugl::set_emugl_multi_display_operations(*getConsoleAgents()->multi_display);
241 LazyLoadedEGLDispatch::get();
242 if (glVersion == GLESApi_CM) LazyLoadedGLESv1Dispatch::get();
243 LazyLoadedGLESv2Dispatch::get();
244
245 bool useHostGpu = shouldUseHostGpu();
246 mWindow = createOrGetTestWindow(mXOffset, mYOffset, mWidth, mHeight);
247 mUseSubWindow = mWindow != nullptr;
248
249 FrameBuffer::initialize(
250 mWidth, mHeight,
251 mUseSubWindow,
252 !useHostGpu /* egl2egl */);
253 mFb.reset(FrameBuffer::getFB());
254
255 if (mUseSubWindow) {
256 mFb->setupSubWindow(
257 (FBNativeWindowType)(uintptr_t)
258 mWindow->getFramebufferNativeWindow(),
259 0, 0,
260 mWidth, mHeight, mWidth, mHeight,
261 mWindow->getDevicePixelRatio(), 0, false, false);
262 mWindow->messageLoop();
263 }
264
265 mRenderThreadInfo.reset(new RenderThreadInfo());
266
267 mColorBuffer = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE);
268 mContext = mFb->createRenderContext(0, 0, glVersion);
269 mSurface = mFb->createWindowSurface(0, mWidth, mHeight);
270
271 mFb->bindContext(mContext, mSurface, mSurface);
272 mFb->setWindowSurfaceColorBuffer(mSurface, mColorBuffer);
273
274 if (mIsCompose && mTargetCb == 0) {
275 mTargetCb = mFb->createColorBuffer(mFb->getWidth(),
276 mFb->getHeight(),
277 GL_RGBA,
278 FRAMEWORK_FORMAT_GL_COMPATIBLE);
279 mFb->openColorBuffer(mTargetCb);
280 }
281 }
282
~SampleApplication()283 SampleApplication::~SampleApplication() {
284 if (mFb) {
285 if (mTargetCb) {
286 mFb->closeColorBuffer(mTargetCb);
287 }
288 mFb->bindContext(0, 0, 0);
289 mFb->closeColorBuffer(mColorBuffer);
290 mFb->DestroyWindowSurface(mSurface);
291 mFb->finalize();
292 }
293 }
294
rebind()295 void SampleApplication::rebind() {
296 mFb->bindContext(mContext, mSurface, mSurface);
297 }
298
drawLoop()299 void SampleApplication::drawLoop() {
300 this->initialize();
301
302 Vsync vsync(mRefreshRate);
303
304 while (true) {
305 this->draw();
306 mFb->flushWindowSurfaceColorBuffer(mSurface);
307 vsync.waitUntilNextVsync();
308 if (mUseSubWindow) {
309 mFb->post(mColorBuffer);
310 mWindow->messageLoop();
311 }
312 }
313 }
314
getFenceSync()315 FenceSync* SampleApplication::getFenceSync() {
316 auto gl = getGlDispatch();
317 FenceSync* sync = new FenceSync(false, false);
318 gl->glFlush();
319 return sync;
320 }
321
drawWorkerWithCompose(ColorBufferQueue & app2sfQueue,ColorBufferQueue & sf2appQueue)322 void SampleApplication::drawWorkerWithCompose(ColorBufferQueue& app2sfQueue,
323 ColorBufferQueue& sf2appQueue) {
324 ColorBufferQueue::Item appItem = {};
325 AutoComposeDevice autoComposeDevice(mTargetCb);
326 hwc_rect_t displayFrame = {0, mHeight/2, mWidth, mHeight};
327 hwc_frect_t crop = {0.0, 0.0, 0.0, 0.0};
328 hwc_color_t color = {200, 0, 0, 255};
329 autoComposeDevice.configureLayer(0, 0,
330 HWC2_COMPOSITION_SOLID_COLOR,
331 displayFrame,
332 crop,
333 HWC2_BLEND_MODE_NONE,
334 1.0,
335 color);
336
337 while (true) {
338 app2sfQueue.dequeueBuffer(&appItem);
339 if (appItem.sync) { appItem.sync->wait(EGL_FOREVER_KHR); }
340
341 hwc_rect_t displayFrame = {0, 0, mWidth, mHeight/2};
342 hwc_frect_t crop = {0.0, 0.0, (float)mWidth, (float)mHeight};
343 hwc_color_t color = {0, 0, 0, 0};
344 autoComposeDevice.configureLayer(1,
345 appItem.colorBuffer,
346 HWC2_COMPOSITION_DEVICE,
347 displayFrame,
348 crop,
349 HWC2_BLEND_MODE_PREMULTIPLIED,
350 0.8,
351 color);
352 mFb->compose(autoComposeDevice.getSize(), autoComposeDevice.get());
353
354 if (appItem.sync) { appItem.sync->decRef(); }
355 sf2appQueue.queueBuffer(ColorBufferQueue::Item(appItem.colorBuffer, getFenceSync()));
356 }
357 }
358
drawWorker(ColorBufferQueue & app2sfQueue,ColorBufferQueue & sf2appQueue,ColorBufferQueue & sf2hwcQueue,ColorBufferQueue & hwc2sfQueue)359 void SampleApplication::drawWorker(ColorBufferQueue& app2sfQueue,
360 ColorBufferQueue& sf2appQueue,
361 ColorBufferQueue& sf2hwcQueue,
362 ColorBufferQueue& hwc2sfQueue) {
363 RenderThreadInfo* tInfo = new RenderThreadInfo;
364 unsigned int sfContext = mFb->createRenderContext(0, 0, GLESApi_3_0);
365 unsigned int sfSurface = mFb->createWindowSurface(0, mWidth, mHeight);
366 mFb->bindContext(sfContext, sfSurface, sfSurface);
367
368 auto gl = getGlDispatch();
369
370 static constexpr char blitVshaderSrc[] = R"(#version 300 es
371 precision highp float;
372 layout (location = 0) in vec2 pos;
373 layout (location = 1) in vec2 texcoord;
374 out vec2 texcoord_varying;
375 void main() {
376 gl_Position = vec4(pos, 0.0, 1.0);
377 texcoord_varying = texcoord;
378 })";
379
380 static constexpr char blitFshaderSrc[] = R"(#version 300 es
381 precision highp float;
382 uniform sampler2D tex;
383 in vec2 texcoord_varying;
384 out vec4 fragColor;
385 void main() {
386 fragColor = texture(tex, texcoord_varying);
387 })";
388
389 GLint blitProgram =
390 compileAndLinkShaderProgram(
391 blitVshaderSrc, blitFshaderSrc);
392
393 GLint samplerLoc = gl->glGetUniformLocation(blitProgram, "tex");
394
395 GLuint blitVbo;
396 gl->glGenBuffers(1, &blitVbo);
397 gl->glBindBuffer(GL_ARRAY_BUFFER, blitVbo);
398 const float attrs[] = {
399 -1.0f, -1.0f, 0.0f, 1.0f,
400 1.0f, -1.0f, 1.0f, 1.0f,
401 1.0f, 1.0f, 1.0f, 0.0f,
402 -1.0f, -1.0f, 0.0f, 1.0f,
403 1.0f, 1.0f, 1.0f, 0.0f,
404 -1.0f, 1.0f, 0.0f, 0.0f,
405 };
406 gl->glBufferData(GL_ARRAY_BUFFER, sizeof(attrs), attrs, GL_STATIC_DRAW);
407 gl->glEnableVertexAttribArray(0);
408 gl->glEnableVertexAttribArray(1);
409
410 gl->glVertexAttribPointer(
411 0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
412 gl->glVertexAttribPointer(
413 1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
414 (GLvoid*)(uintptr_t)(2 * sizeof(GLfloat)));
415
416 GLuint blitTexture;
417 gl->glActiveTexture(GL_TEXTURE0);
418 gl->glGenTextures(1, &blitTexture);
419 gl->glBindTexture(GL_TEXTURE_2D, blitTexture);
420
421 gl->glUseProgram(blitProgram);
422 gl->glUniform1i(samplerLoc, 0);
423
424 ColorBufferQueue::Item appItem = {};
425 ColorBufferQueue::Item hwcItem = {};
426
427 while (true) {
428 hwc2sfQueue.dequeueBuffer(&hwcItem);
429 if (hwcItem.sync) { hwcItem.sync->wait(EGL_FOREVER_KHR); }
430
431 mFb->setWindowSurfaceColorBuffer(sfSurface, hwcItem.colorBuffer);
432
433 {
434 app2sfQueue.dequeueBuffer(&appItem);
435
436 mFb->bindColorBufferToTexture(appItem.colorBuffer);
437
438 gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
439
440 gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
441 gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
442
443 if (appItem.sync) { appItem.sync->wait(EGL_FOREVER_KHR); }
444
445 gl->glDrawArrays(GL_TRIANGLES, 0, 6);
446
447 if (appItem.sync) { appItem.sync->decRef(); }
448 sf2appQueue.queueBuffer(ColorBufferQueue::Item(appItem.colorBuffer, getFenceSync()));
449 }
450
451 mFb->flushWindowSurfaceColorBuffer(sfSurface);
452
453 if (hwcItem.sync) { hwcItem.sync->decRef(); }
454 sf2hwcQueue.queueBuffer(ColorBufferQueue::Item(hwcItem.colorBuffer, getFenceSync()));
455 }
456 delete tInfo;
457 }
458
surfaceFlingerComposerLoop()459 void SampleApplication::surfaceFlingerComposerLoop() {
460 ColorBufferQueue app2sfQueue;
461 ColorBufferQueue sf2appQueue;
462 ColorBufferQueue sf2hwcQueue;
463 ColorBufferQueue hwc2sfQueue;
464
465 std::vector<unsigned int> sfColorBuffers;
466 std::vector<unsigned int> hwcColorBuffers;
467
468 for (int i = 0; i < ColorBufferQueue::kCapacity; i++) {
469 sfColorBuffers.push_back(mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE));
470 hwcColorBuffers.push_back(mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE));
471 }
472
473 for (int i = 0; i < ColorBufferQueue::kCapacity; i++) {
474 mFb->openColorBuffer(sfColorBuffers[i]);
475 mFb->openColorBuffer(hwcColorBuffers[i]);
476 }
477
478 // prime the queue
479 for (int i = 0; i < ColorBufferQueue::kCapacity; i++) {
480 sf2appQueue.queueBuffer(ColorBufferQueue::Item(sfColorBuffers[i], nullptr));
481 hwc2sfQueue.queueBuffer(ColorBufferQueue::Item(hwcColorBuffers[i], nullptr));
482 }
483
484 FunctorThread appThread([&]() {
485 RenderThreadInfo* tInfo = new RenderThreadInfo;
486 unsigned int appContext = mFb->createRenderContext(0, 0, GLESApi_3_0);
487 unsigned int appSurface = mFb->createWindowSurface(0, mWidth, mHeight);
488 mFb->bindContext(appContext, appSurface, appSurface);
489
490 ColorBufferQueue::Item sfItem = {};
491
492 sf2appQueue.dequeueBuffer(&sfItem);
493 mFb->setWindowSurfaceColorBuffer(appSurface, sfItem.colorBuffer);
494 if (sfItem.sync) { sfItem.sync->wait(EGL_FOREVER_KHR); sfItem.sync->decRef(); }
495
496 this->initialize();
497
498 while (true) {
499 this->draw();
500 mFb->flushWindowSurfaceColorBuffer(appSurface);
501 app2sfQueue.queueBuffer(ColorBufferQueue::Item(sfItem.colorBuffer, getFenceSync()));
502
503 sf2appQueue.dequeueBuffer(&sfItem);
504 mFb->setWindowSurfaceColorBuffer(appSurface, sfItem.colorBuffer);
505 if (sfItem.sync) { sfItem.sync->wait(EGL_FOREVER_KHR); sfItem.sync->decRef(); }
506 }
507
508 delete tInfo;
509 });
510
511 FunctorThread sfThread([&]() {
512 if (mIsCompose) {
513 drawWorkerWithCompose(app2sfQueue, sf2appQueue);
514 }
515 else {
516 drawWorker(app2sfQueue, sf2appQueue, sf2hwcQueue, hwc2sfQueue);
517 }
518 });
519
520 sfThread.start();
521 appThread.start();
522
523 Vsync vsync(mRefreshRate);
524 ColorBufferQueue::Item sfItem = {};
525 if (!mIsCompose) {
526 while (true) {
527 sf2hwcQueue.dequeueBuffer(&sfItem);
528 if (sfItem.sync) { sfItem.sync->wait(EGL_FOREVER_KHR); sfItem.sync->decRef(); }
529 vsync.waitUntilNextVsync();
530 mFb->post(sfItem.colorBuffer);
531 if (mUseSubWindow) {
532 mWindow->messageLoop();
533 }
534 hwc2sfQueue.queueBuffer(ColorBufferQueue::Item(sfItem.colorBuffer, getFenceSync()));
535 }
536 }
537
538 appThread.wait();
539 sfThread.wait();
540 }
541
drawOnce()542 void SampleApplication::drawOnce() {
543 this->initialize();
544 this->draw();
545 mFb->flushWindowSurfaceColorBuffer(mSurface);
546 if (mUseSubWindow) {
547 mFb->post(mColorBuffer);
548 mWindow->messageLoop();
549 }
550 }
551
getGlDispatch()552 const GLESv2Dispatch* SampleApplication::getGlDispatch() {
553 return LazyLoadedGLESv2Dispatch::get();
554 }
555
556 } // namespace emugl
557