• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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