blob: a59f388a665393c9b31f15d6822e30f69d4b03b9 [file] [log] [blame]
Marissa Wall5a240aa2016-12-15 12:34:06 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <mutex>
18#include <array>
19#include <sstream>
20#include <algorithm>
21
22#include <gui/Surface.h>
23#include <gui/BufferItemConsumer.h>
24#include <gui/GraphicBufferAlloc.h>
25
26#include <ui/GraphicBuffer.h>
27#include <ui/vec4.h>
28
29#include <GLES3/gl3.h>
30
31#include "Hwc2TestBuffer.h"
32#include "Hwc2TestLayers.h"
33
34using namespace android;
35
36/* Returns a fence from egl */
37typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
38
39/* Returns fence to fence generator */
40static void setFence(int32_t fence, void* fenceGenerator);
41
42
43/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
44 * away. The fences are sent to the requester via a callback */
45class Hwc2TestSurfaceManager {
46public:
47 /* Listens for a new frame, detaches the buffer and returns the fence
48 * through saved callback. */
49 class BufferListener : public ConsumerBase::FrameAvailableListener {
50 public:
51 BufferListener(sp<IGraphicBufferConsumer> consumer,
52 FenceCallback callback, void* callbackArgs)
53 : mConsumer(consumer),
54 mCallback(callback),
55 mCallbackArgs(callbackArgs) { }
56
57 void onFrameAvailable(const BufferItem& /*item*/)
58 {
59 BufferItem item;
60
61 if (mConsumer->acquireBuffer(&item, 0))
62 return;
63 if (mConsumer->detachBuffer(item.mSlot))
64 return;
65
66 mCallback(item.mFence->dup(), mCallbackArgs);
67 }
68
69 private:
70 sp<IGraphicBufferConsumer> mConsumer;
71 FenceCallback mCallback;
72 void* mCallbackArgs;
73 };
74
75 /* Creates a buffer listener that waits on a new frame from the buffer
76 * queue. */
77 void initialize(const Area& bufferArea, android_pixel_format_t format,
78 FenceCallback callback, void* callbackArgs)
79 {
80 sp<IGraphicBufferProducer> producer;
81 sp<IGraphicBufferConsumer> consumer;
82 BufferQueue::createBufferQueue(&producer, &consumer);
83
84 consumer->setDefaultBufferSize(bufferArea.width, bufferArea.height);
85 consumer->setDefaultBufferFormat(format);
86
87 mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
88
89 mListener = new BufferListener(consumer, callback, callbackArgs);
90 mBufferItemConsumer->setFrameAvailableListener(mListener);
91
92 mSurface = new Surface(producer, true);
93 }
94
95 /* Used by Egl manager. The surface is never displayed. */
96 sp<Surface> getSurface() const
97 {
98 return mSurface;
99 }
100
101private:
102 sp<BufferItemConsumer> mBufferItemConsumer;
103 sp<BufferListener> mListener;
104 /* Used by Egl manager. The surface is never displayed */
105 sp<Surface> mSurface;
106};
107
108
109/* Used to generate valid fences. It is not possible to create a dummy sync
110 * fence for testing. Egl can generate buffers along with a valid fence.
111 * The buffer cannot be guaranteed to be the same format across all devices so
112 * a CPU filled buffer is used instead. The Egl fence is used along with the
113 * CPU filled buffer. */
114class Hwc2TestEglManager {
115public:
116 Hwc2TestEglManager()
117 : mEglDisplay(EGL_NO_DISPLAY),
118 mEglSurface(EGL_NO_SURFACE),
119 mEglContext(EGL_NO_CONTEXT) { }
120
121 ~Hwc2TestEglManager()
122 {
123 cleanup();
124 }
125
126 int initialize(sp<Surface> surface)
127 {
128 mSurface = surface;
129
130 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
131 if (mEglDisplay == EGL_NO_DISPLAY) return false;
132
133 EGLint major;
134 EGLint minor;
135 if (!eglInitialize(mEglDisplay, &major, &minor)) {
136 ALOGW("Could not initialize EGL");
137 return false;
138 }
139
140 /* We're going to use a 1x1 pbuffer surface later on
141 * The configuration distance doesn't really matter for what we're
142 * trying to do */
143 EGLint configAttrs[] = {
144 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
145 EGL_RED_SIZE, 8,
146 EGL_GREEN_SIZE, 8,
147 EGL_BLUE_SIZE, 8,
148 EGL_ALPHA_SIZE, 0,
149 EGL_DEPTH_SIZE, 24,
150 EGL_STENCIL_SIZE, 0,
151 EGL_NONE
152 };
153
154 EGLConfig configs[1];
155 EGLint configCnt;
156 if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1,
157 &configCnt)) {
158 ALOGW("Could not select EGL configuration");
159 eglReleaseThread();
160 eglTerminate(mEglDisplay);
161 return false;
162 }
163
164 if (configCnt <= 0) {
165 ALOGW("Could not find EGL configuration");
166 eglReleaseThread();
167 eglTerminate(mEglDisplay);
168 return false;
169 }
170
171 /* These objects are initialized below but the default "null" values are
172 * used to cleanup properly at any point in the initialization sequence */
173 EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
174 mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT,
175 attrs);
176 if (mEglContext == EGL_NO_CONTEXT) {
177 ALOGW("Could not create EGL context");
178 cleanup();
179 return false;
180 }
181
182 EGLint surfaceAttrs[] = { EGL_NONE };
183 mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0],
184 mSurface.get(), surfaceAttrs);
185 if (mEglSurface == EGL_NO_SURFACE) {
186 ALOGW("Could not create EGL surface");
187 cleanup();
188 return false;
189 }
190
191 if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
192 ALOGW("Could not change current EGL context");
193 cleanup();
194 return false;
195 }
196
197 return true;
198 }
199
200 void makeCurrent() const
201 {
202 eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
203 }
204
205 void present() const
206 {
207 eglSwapBuffers(mEglDisplay, mEglSurface);
208 }
209
210private:
211 void cleanup()
212 {
213 if (mEglDisplay == EGL_NO_DISPLAY)
214 return;
215 if (mEglSurface != EGL_NO_SURFACE)
216 eglDestroySurface(mEglDisplay, mEglSurface);
217 if (mEglContext != EGL_NO_CONTEXT)
218 eglDestroyContext(mEglDisplay, mEglContext);
219
220 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
221 EGL_NO_CONTEXT);
222 eglReleaseThread();
223 eglTerminate(mEglDisplay);
224 }
225
226 sp<Surface> mSurface;
227 EGLDisplay mEglDisplay;
228 EGLSurface mEglSurface;
229 EGLContext mEglContext;
230};
231
232
233static const std::array<vec2, 4> triangles = {{
234 { 1.0f, 1.0f },
235 { -1.0f, 1.0f },
236 { 1.0f, -1.0f },
237 { -1.0f, -1.0f },
238}};
239
240class Hwc2TestFenceGenerator {
241public:
242
243 Hwc2TestFenceGenerator()
244 {
245 mSurfaceManager.initialize({1, 1}, HAL_PIXEL_FORMAT_RGBA_8888,
246 setFence, this);
247
248 if (!mEglManager.initialize(mSurfaceManager.getSurface()))
249 return;
250
251 mEglManager.makeCurrent();
252
253 glClearColor(0.0, 0.0, 0.0, 1.0);
254 glEnableVertexAttribArray(0);
255 }
256
257 ~Hwc2TestFenceGenerator()
258 {
259 if (mFence >= 0)
260 close(mFence);
261 mFence = -1;
262
263 mEglManager.makeCurrent();
264 }
265
266 /* It is not possible to simply generate a fence. The easiest way is to
267 * generate a buffer using egl and use the associated fence. The buffer
268 * cannot be guaranteed to be a certain format across all devices using this
269 * method. Instead the buffer is generated using the CPU */
270 int32_t get()
271 {
272 if (mFence >= 0) {
273 return dup(mFence);
274 }
275
276 std::unique_lock<std::mutex> lock(mMutex);
277
278 /* If the pending is still set to false and times out, we cannot recover.
279 * Set an error and return */
280 while (mPending != false) {
281 if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
282 return -ETIME;
283 }
284
285 /* Generate a fence. The fence will be returned through the setFence
286 * callback */
287 mEglManager.makeCurrent();
288
289 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, triangles.data());
290 glClear(GL_COLOR_BUFFER_BIT);
291
292 mEglManager.present();
293
294 /* Wait for the setFence callback */
295 while (mPending != true) {
296 if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
297 return -ETIME;
298 }
299
300 mPending = false;
301
302 return dup(mFence);
303 }
304
305 /* Callback that sets the fence */
306 void set(int32_t fence)
307 {
308 mFence = fence;
309 mPending = true;
310
311 mCv.notify_all();
312 }
313
314private:
315
316 Hwc2TestSurfaceManager mSurfaceManager;
317 Hwc2TestEglManager mEglManager;
318
319 std::mutex mMutex;
320 std::condition_variable mCv;
321
322 int32_t mFence = -1;
323 bool mPending = false;
324};
325
326
327static void setFence(int32_t fence, void* fenceGenerator)
328{
329 static_cast<Hwc2TestFenceGenerator*>(fenceGenerator)->set(fence);
330}
331
332
Marissa Wallf18cfb02017-02-21 14:01:05 -0800333/* Sets the pixel of a buffer given the location, format, stride and color.
334 * Currently only supports RGBA_8888 */
335static void setColor(int32_t x, int32_t y,
336 android_pixel_format_t format, uint32_t stride, uint8_t* img, uint8_t r,
337 uint8_t g, uint8_t b, uint8_t a)
338{
339 switch (format) {
340 case HAL_PIXEL_FORMAT_RGBA_8888:
341 img[(y * stride + x) * 4 + 0] = r;
342 img[(y * stride + x) * 4 + 1] = g;
343 img[(y * stride + x) * 4 + 2] = b;
344 img[(y * stride + x) * 4 + 3] = a;
345 break;
346 default:
347 break;
348 }
349}
350
Marissa Wall5a240aa2016-12-15 12:34:06 -0800351Hwc2TestBuffer::Hwc2TestBuffer()
352 : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
353
354Hwc2TestBuffer::~Hwc2TestBuffer() = default;
355
356/* When the buffer changes sizes, save the new size and invalidate the current
357 * buffer */
358void Hwc2TestBuffer::updateBufferArea(const Area& bufferArea)
359{
360 if (mBufferArea.width == bufferArea.width
361 && mBufferArea.height == bufferArea.height)
362 return;
363
364 mBufferArea.width = bufferArea.width;
365 mBufferArea.height = bufferArea.height;
366
367 mValidBuffer = false;
368}
369
370/* Returns a valid buffer handle and fence. The handle is filled using the CPU
371 * to ensure the correct format across all devices. The fence is created using
372 * egl. */
373int Hwc2TestBuffer::get(buffer_handle_t* outHandle, int32_t* outFence)
374{
375 if (mBufferArea.width == -1 || mBufferArea.height == -1)
376 return -EINVAL;
377
378 /* If the current buffer is valid, the previous buffer can be reused.
379 * Otherwise, create new buffer */
380 if (!mValidBuffer) {
381 int ret = generateBuffer();
382 if (ret)
383 return ret;
384 }
385
386 *outFence = mFenceGenerator->get();
387 *outHandle = mHandle;
388
389 mValidBuffer = true;
390
391 return 0;
392}
393
394/* CPU fills a buffer to guarantee the correct buffer format across all
395 * devices */
396int Hwc2TestBuffer::generateBuffer()
397{
398 int ret;
399
400 /* Create new graphic buffer with correct dimensions */
401 mGraphicBuffer = mGraphicBufferAlloc.createGraphicBuffer(
402 mBufferArea.width, mBufferArea.height, mFormat,
403 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
404 "hwc2_test_buffer", &ret);
405 if (ret)
406 return ret;
407
408 /* Locks the buffer for writing */
409 uint8_t* img;
410 mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
411
412 uint32_t stride = mGraphicBuffer->getStride();
413
414 /* Iterate from the top row of the buffer to the bottom row */
415 for (int32_t y = 0; y < mBufferArea.height; y++) {
416
417 /* Will be used as R, G and B values for pixel colors */
418 uint8_t max = 255;
419 uint8_t min = 0;
420
421 /* Divide the rows into 3 sections. The first section will contain
422 * the lighest colors. The last section will contain the darkest
423 * colors. */
424 if (y < mBufferArea.height * 1.0 / 3.0) {
425 min = 255 / 2;
426 } else if (y >= mBufferArea.height * 2.0 / 3.0) {
427 max = 255 / 2;
428 }
429
430 /* Divide the columns into 3 sections. The first section is red,
431 * the second is green and the third is blue */
432 int32_t x = 0;
433 for (; x < mBufferArea.width / 3; x++) {
434 setColor(x, y, mFormat, stride, img, max, min, min, 255);
435 }
436
437 for (; x < mBufferArea.width * 2 / 3; x++) {
438 setColor(x, y, mFormat, stride, img, min, max, min, 255);
439 }
440
441 for (; x < mBufferArea.width; x++) {
442 setColor(x, y, mFormat, stride, img, min, min, max, 255);
443 }
444 }
445
446 /* Unlock the buffer for reading */
447 mGraphicBuffer->unlock();
448
449 mHandle = mGraphicBuffer->handle;
450
451 return 0;
452}
453
Marissa Wallf18cfb02017-02-21 14:01:05 -0800454
455Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer()
456 : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
457
458Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
459
460/* Generates a client target buffer using the layers assigned for client
461 * composition. Takes into account the individual layer properties such as
462 * transform, blend mode, source crop, etc. */
463int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
464 int32_t* outFence, const Area& bufferArea,
465 const Hwc2TestLayers* testLayers,
466 const std::set<hwc2_layer_t>* clientLayers,
467 const std::set<hwc2_layer_t>* clearLayers)
Marissa Wall5a240aa2016-12-15 12:34:06 -0800468{
Marissa Wallf18cfb02017-02-21 14:01:05 -0800469 int err;
470
471 /* Create new graphic buffer with updated size */
472 mGraphicBuffer = mGraphicBufferAlloc.createGraphicBuffer(bufferArea.width,
473 bufferArea.height, mFormat,
474 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
475 "hwc2_test_buffer", &err);
476 if (err)
477 return err;
478
479 uint8_t* img;
480 mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
481
482 uint32_t stride = mGraphicBuffer->getStride();
483
484 float bWDiv3 = bufferArea.width / 3;
485 float bW2Div3 = bufferArea.width * 2 / 3;
486 float bHDiv3 = bufferArea.height / 3;
487 float bH2Div3 = bufferArea.height * 2 / 3;
488
489 /* Cycle through every pixel in the buffer and determine what color it
490 * should be. */
491 for (int32_t y = 0; y < bufferArea.height; y++) {
492 for (int32_t x = 0; x < bufferArea.width; x++) {
493
494 uint8_t r = 0, g = 0, b = 0;
495 float a = 0.0f;
496
497 /* Cycle through each client layer from back to front and
498 * update the pixel color. */
499 for (auto layer = clientLayers->rbegin();
500 layer != clientLayers->rend(); ++layer) {
501
502 const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
503
504 float dfL = df.left;
505 float dfT = df.top;
506 float dfR = df.right;
507 float dfB = df.bottom;
508
509 /* If the pixel location falls outside of the layer display
510 * frame, skip the layer. */
511 if (x < dfL || x >= dfR || y < dfT || y >= dfB)
512 continue;
513
514 /* If the device has requested the layer be clear, clear
515 * the pixel and continue. */
516 if (clearLayers->count(*layer) != 0) {
517 r = 0;
518 g = 0;
519 b = 0;
520 a = 0.0f;
521 continue;
522 }
523
524 float planeAlpha = testLayers->getPlaneAlpha(*layer);
525
526 /* If the layer is a solid color, fill the color and
527 * continue. */
528 if (testLayers->getComposition(*layer)
529 == HWC2_COMPOSITION_SOLID_COLOR) {
530 const auto color = testLayers->getColor(*layer);
531 r = color.r;
532 g = color.g;
533 b = color.b;
534 a = color.a * planeAlpha;
535 continue;
536 }
537
538 float xPos = x;
539 float yPos = y;
540
541 hwc_transform_t transform = testLayers->getTransform(*layer);
542
543 float dfW = dfR - dfL;
544 float dfH = dfB - dfT;
545
546 /* If a layer has a transform, find which location on the
547 * layer will end up in the current pixel location. We
548 * can calculate the color of the current pixel using that
549 * location. */
550 if (transform > 0) {
551 /* Change origin to be the center of the layer. */
552 xPos = xPos - dfL - dfW / 2.0;
553 yPos = yPos - dfT - dfH / 2.0;
554
555 /* Flip Horizontal by reflecting across the y axis. */
556 if (transform & HWC_TRANSFORM_FLIP_H)
557 xPos = -xPos;
558
559 /* Flip vertical by reflecting across the x axis. */
560 if (transform & HWC_TRANSFORM_FLIP_V)
561 yPos = -yPos;
562
563 /* Rotate 90 by using a basic linear algebra rotation
564 * and scaling the result so the display frame remains
565 * the same. For example, a buffer of size 100x50 should
566 * rotate 90 degress but remain the same dimension
567 * (100x50) at the end of the transformation. */
568 if (transform & HWC_TRANSFORM_ROT_90) {
569 float tmp = xPos;
570 xPos = -yPos * dfW / dfH;
571 yPos = tmp * dfH / dfW;
572 }
573
574 /* Change origin back to the top left corner of the
575 * layer. */
576 xPos = xPos + dfL + dfW / 2.0;
577 yPos = yPos + dfT + dfH / 2.0;
578 }
579
580 hwc_frect_t sc = testLayers->getSourceCrop(*layer);
581 float scL = sc.left, scT = sc.top;
582
583 float dfWDivScW = dfW / (sc.right - scL);
584 float dfHDivScH = dfH / (sc.bottom - scT);
585
586 float max = 255, min = 0;
587
588 /* Choose the pixel color. Similar to generateBuffer,
589 * each layer will be divided into 3x3 colors. Because
590 * both the source crop and display frame must be taken into
591 * account, the formulas are more complicated.
592 *
593 * If the source crop and display frame were not taken into
594 * account, we would simply divide the buffer into three
595 * sections by height. Each section would get one color.
596 * For example the formula for the first section would be:
597 *
598 * if (yPos < bufferArea.height / 3)
599 * //Select first section color
600 *
601 * However the pixel color is chosen based on the source
602 * crop and displayed based on the display frame.
603 *
604 * If the display frame top was 0 and the source crop height
605 * and display frame height were the same. The only factor
606 * would be the source crop top. To calculate the new
607 * section boundary, the section boundary would be moved up
608 * by the height of the source crop top. The formula would
609 * be:
610 * if (yPos < (bufferArea.height / 3 - sourceCrop.top)
611 * //Select first section color
612 *
613 * If the display frame top could also vary but source crop
614 * and display frame heights were the same, the formula
615 * would be:
616 * if (yPos < (bufferArea.height / 3 - sourceCrop.top
617 * + displayFrameTop)
618 * //Select first section color
619 *
620 * If the heights were not the same, the conversion between
621 * the source crop and display frame dimensions must be
622 * taken into account. The formula would be:
623 * if (yPos < ((bufferArea.height / 3) - sourceCrop.top)
624 * * displayFrameHeight / sourceCropHeight
625 * + displayFrameTop)
626 * //Select first section color
627 */
628 if (yPos < ((bHDiv3) - scT) * dfHDivScH + dfT) {
629 min = 255 / 2;
630 } else if (yPos >= ((bH2Div3) - scT) * dfHDivScH + dfT) {
631 max = 255 / 2;
632 }
633
634 uint8_t rCur = min, gCur = min, bCur = min;
635 float aCur = 1.0f;
636
637 /* This further divides the color sections from 3 to 3x3.
638 * The math behind it follows the same logic as the previous
639 * comment */
640 if (xPos < ((bWDiv3) - scL) * (dfWDivScW) + dfL) {
641 rCur = max;
642 } else if (xPos < ((bW2Div3) - scL) * (dfWDivScW) + dfL) {
643 gCur = max;
644 } else {
645 bCur = max;
646 }
647
648
649 /* Blend the pixel color with the previous layers' pixel
650 * colors using the plane alpha and blend mode. The final
651 * pixel color is chosen using the plane alpha and blend
652 * mode formulas found in hwcomposer2.h */
653 hwc2_blend_mode_t blendMode = testLayers->getBlendMode(*layer);
654
655 if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
656 rCur *= planeAlpha;
657 gCur *= planeAlpha;
658 bCur *= planeAlpha;
659 }
660
661 aCur *= planeAlpha;
662
663 if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
664 r = rCur + r * (1.0 - aCur);
665 g = gCur + g * (1.0 - aCur);
666 b = bCur + b * (1.0 - aCur);
667 a = aCur + a * (1.0 - aCur);
668 } else if (blendMode == HWC2_BLEND_MODE_COVERAGE) {
669 r = rCur * aCur + r * (1.0 - aCur);
670 g = gCur * aCur + g * (1.0 - aCur);
671 b = bCur * aCur + b * (1.0 - aCur);
672 a = aCur * aCur + a * (1.0 - aCur);
673 } else {
674 r = rCur;
675 g = gCur;
676 b = bCur;
677 a = aCur;
678 }
679 }
680
681 /* Set the pixel color */
682 setColor(x, y, mFormat, stride, img, r, g, b, a * 255);
683 }
684 }
685
686 mGraphicBuffer->unlock();
687
688 *outFence = mFenceGenerator->get();
689 *outHandle = mGraphicBuffer->handle;
690
691 return 0;
Marissa Wall5a240aa2016-12-15 12:34:06 -0800692}