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