• 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 "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