summaryrefslogtreecommitdiff
path: root/stream-servers/FrameBuffer.h
blob: 37951cf8b5ff23ae9f493b4d05bf18d007802c7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
/*
 * Copyright (C) 2011-2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef _LIBRENDER_FRAMEBUFFER_H
#define _LIBRENDER_FRAMEBUFFER_H

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdint.h>

#include <functional>
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>

#include "ColorBuffer.h"
#include "DisplayVk.h"
#include "FbConfig.h"
#include "GLESVersionDetector.h"
#include "Hwc2.h"
#include "PostCommands.h"
#include "PostWorker.h"
#include "ReadbackWorker.h"
#include "RenderContext.h"
#include "Renderer.h"
#include "TextureDraw.h"
#include "WindowSurface.h"
#include "base/Lock.h"
#include "base/MessageChannel.h"
#include "base/Stream.h"
#include "base/Thread.h"
#include "base/WorkerThread.h"
#include "render_api.h"
#include "snapshot/common.h"
#include "vulkan/vk_util.h"
#include "virtio_gpu_ops.h"

struct ColorBufferRef {
    ColorBufferPtr cb;
    uint32_t refcount;  // number of client-side references

    // Tracks whether opened at least once. In O+,
    // color buffers can be created/closed immediately,
    // but then registered (opened) afterwards.
    bool opened;

    // Tracks the time when this buffer got a close request while not being
    // opened yet.
    uint64_t closedTs;
};

struct BufferRef {
    BufferPtr buffer;
};

typedef std::unordered_map<HandleType, std::pair<WindowSurfacePtr, HandleType>>
    WindowSurfaceMap;
typedef std::unordered_set<HandleType> WindowSurfaceSet;
typedef std::unordered_map<uint64_t, WindowSurfaceSet> ProcOwnedWindowSurfaces;

typedef std::unordered_map<HandleType, RenderContextPtr> RenderContextMap;
typedef std::unordered_set<HandleType> RenderContextSet;
typedef std::unordered_map<uint64_t, RenderContextSet> ProcOwnedRenderContexts;

typedef std::unordered_map<HandleType, ColorBufferRef> ColorBufferMap;
typedef std::unordered_multiset<HandleType> ColorBufferSet;
typedef std::unordered_map<uint64_t, ColorBufferSet> ProcOwnedColorBuffers;

typedef std::unordered_map<HandleType, BufferRef> BufferMap;
typedef std::unordered_multiset<HandleType> BufferSet;
typedef std::unordered_map<uint64_t, BufferSet> ProcOwnedBuffers;

typedef std::unordered_set<HandleType> EGLImageSet;
typedef std::unordered_map<uint64_t, EGLImageSet> ProcOwnedEGLImages;

typedef std::unordered_map<void*, std::function<void()>> CallbackMap;
typedef std::unordered_map<uint64_t, CallbackMap> ProcOwnedCleanupCallbacks;

typedef std::unordered_map<uint64_t, uint32_t*> ProcOwnedSequenceNumbers;

// A structure used to list the capabilities of the underlying EGL
// implementation that the FrameBuffer instance depends on.
// |has_eglimage_texture_2d| is true iff the EGL_KHR_gl_texture_2D_image
// extension is supported.
// |has_eglimage_renderbuffer| is true iff the EGL_KHR_gl_renderbuffer_image
// extension is supported.
// |eglMajor| and |eglMinor| are the major and minor version numbers of
// the underlying EGL implementation.
struct FrameBufferCaps {
    bool has_eglimage_texture_2d;
    bool has_eglimage_renderbuffer;
    EGLint eglMajor;
    EGLint eglMinor;
};

// The FrameBuffer class holds the global state of the emulation library on
// top of the underlying EGL/GLES implementation. It should probably be
// named "Display" instead of "FrameBuffer".
//
// There is only one global instance, that can be retrieved with getFB(),
// and which must be previously setup by calling initialize().
//
class FrameBuffer {
   public:
    // Initialize the global instance.
    // |width| and |height| are the dimensions of the emulator GPU display
    // in pixels. |useSubWindow| is true to indicate that the caller
    // will use setupSubWindow() to let EmuGL display the GPU content in its
    // own sub-windows. If false, this means the caller will use
    // setPostCallback() instead to retrieve the content.
    // Returns true on success, false otherwise.
    static bool initialize(int width, int height, bool useSubWindow,
                           bool egl2egl);

    // Setup a sub-window to display the content of the emulated GPU
    // on-top of an existing UI window. |p_window| is the platform-specific
    // parent window handle. |wx|, |wy|, |ww| and |wh| are the
    // dimensions in pixels of the sub-window, relative to the parent window's
    // coordinate. |fbw| and |fbh| are the dimensions used to initialize
    // the framebuffer, which may be different from the dimensions of the
    // sub-window (in which case scaling will be applied automatically).
    // |dpr| is the device pixel ratio of the monitor, which is needed for
    // proper panning on high-density displays (like retina)
    // |zRot| is a rotation angle in degrees, (clockwise in the Y-upwards GL
    // coordinate space).
    //
    // If a sub-window already exists, this function updates the subwindow
    // and framebuffer properties to match the given values.
    //
    // Return true on success, false otherwise.
    //
    // NOTE: This can return false for software-only EGL engines like OSMesa.
    bool setupSubWindow(FBNativeWindowType p_window, int wx, int wy, int ww,
                        int wh, int fbw, int fbh, float dpr, float zRot,
                        bool deleteExisting, bool hideWindow);

    // Remove the sub-window created by setupSubWindow(), if any.
    // Return true on success, false otherwise.
    bool removeSubWindow();

    // Finalize the instance.
    void finalize();

    // Return a pointer to the global instance. initialize() must be called
    // previously, or this will return NULL.
    static FrameBuffer* getFB() { return s_theFrameBuffer; }

    // Wait for a FrameBuffer instance to be initialized and ready to use.
    // This function blocks the caller until there is a valid initialized
    // object in getFB() and
    static void waitUntilInitialized();

    // Return the capabilities of the underlying display.
    const FrameBufferCaps& getCaps() const { return m_caps; }

    // Return the emulated GPU display width in pixels.
    int getWidth() const { return m_framebufferWidth; }

    // Return the emulated GPU display height in pixels.
    int getHeight() const { return m_framebufferHeight; }

    // Return the list of configs available from this display.
    const FbConfigList* getConfigs() const { return m_configs; }

    // Set a callback that will be called each time the emulated GPU content
    // is updated. This can be relatively slow with host-based GPU emulation,
    // so only do this when you need to.
    void setPostCallback(emugl::Renderer::OnPostCallback onPost,
                         void* onPostContext, uint32_t displayId,
                         bool useBgraReadback = false);

    // Retrieve the GL strings of the underlying EGL/GLES implementation.
    // On return, |*vendor|, |*renderer| and |*version| will point to strings
    // that are owned by the instance (and must not be freed by the caller).
    void getGLStrings(const char** vendor, const char** renderer,
                      const char** version) const {
        *vendor = m_glVendor.c_str();
        *renderer = m_glRenderer.c_str();
        *version = m_glVersion.c_str();
    }

    // Create a new RenderContext instance for this display instance.
    // |p_config| is the index of one of the configs returned by getConfigs().
    // |p_share| is either EGL_NO_CONTEXT or the handle of a shared context.
    // |version| specifies the GLES version as a GLESApi enum.
    // Return a new handle value, which will be 0 in case of error.
    HandleType createRenderContext(int p_config, HandleType p_share,
                                   GLESApi version = GLESApi_CM);

    // Create a new WindowSurface instance from this display instance.
    // |p_config| is the index of one of the configs returned by getConfigs().
    // |p_width| and |p_height| are the window dimensions in pixels.
    // Return a new handle value, or 0 in case of error.
    HandleType createWindowSurface(int p_config, int p_width, int p_height);

    // Create a new ColorBuffer instance from this display instance.
    // |p_width| and |p_height| are its dimensions in pixels.
    // |p_internalFormat| is the OpenGL format of this color buffer.
    // |p_frameworkFormat| describes the Android frameework format of this
    // color buffer, if differing from |p_internalFormat|.
    // See ColorBuffer::create() for
    // list of valid values. Note that ColorBuffer instances are reference-
    // counted. Use openColorBuffer / closeColorBuffer to operate on the
    // internal count.
    HandleType createColorBuffer(int p_width, int p_height,
                                 GLenum p_internalFormat,
                                 FrameworkFormat p_frameworkFormat);
    // Variant of createColorBuffer except with a particular
    // handle already assigned. This is for use with
    // virtio-gpu's RESOURCE_CREATE ioctl.
    void createColorBufferWithHandle(int p_width, int p_height,
                                     GLenum p_internalFormat,
                                     FrameworkFormat p_frameworkFormat,
                                     HandleType handle);

    // Create a new data Buffer instance from this display instance.
    // The buffer will be backed by a VkBuffer and VkDeviceMemory (if Vulkan
    // is available).
    // |size| is the requested size of Buffer in bytes.
    // |memoryProperty| is the requested memory property bits of the device
    // memory.
    HandleType createBuffer(uint64_t size, uint32_t memoryProperty);

    // Call this function when a render thread terminates to destroy all
    // the remaining contexts it created. Necessary to avoid leaking host
    // contexts when a guest application crashes, for example.
    void drainRenderContext();

    // Call this function when a render thread terminates to destroy all
    // remaining window surfqce it created. Necessary to avoid leaking
    // host buffers when a guest application crashes, for example.
    void drainWindowSurface();

    // Destroy a given RenderContext instance. |p_context| is its handle
    // value as returned by createRenderContext().
    void DestroyRenderContext(HandleType p_context);

    // Destroy a given WindowSurface instance. |p_surcace| is its handle
    // value as returned by createWindowSurface().
    void DestroyWindowSurface(HandleType p_surface);
    // Returns the set of ColorBuffers destroyed (for further cleanup)
    std::vector<HandleType> DestroyWindowSurfaceLocked(HandleType p_surface);

    // Increment the reference count associated with a given ColorBuffer
    // instance. |p_colorbuffer| is its handle value as returned by
    // createColorBuffer().
    int openColorBuffer(HandleType p_colorbuffer);

    // Decrement the reference count associated with a given ColorBuffer
    // instance. |p_colorbuffer| is its handle value as returned by
    // createColorBuffer(). Note that if the reference count reaches 0,
    // the instance is destroyed automatically.
    void closeColorBuffer(HandleType p_colorbuffer);

    // Destroy a Buffer created previously. |p_buffer| is its handle value as
    // returned by createBuffer().
    void closeBuffer(HandleType p_colorbuffer);

    void cleanupProcGLObjects(uint64_t puid);

    // Equivalent for eglMakeCurrent() for the current display.
    // |p_context|, |p_drawSurface| and |p_readSurface| are the handle values
    // of the context, the draw surface and the read surface, respectively.
    // Returns true on success, false on failure.
    // Note: if all handle values are 0, this is an unbind operation.
    bool bindContext(HandleType p_context, HandleType p_drawSurface,
                     HandleType p_readSurface);

    // Return a render context pointer from its handle
    RenderContextPtr getContext_locked(HandleType p_context);

    // Return a color buffer pointer from its handle
    ColorBufferPtr getColorBuffer_locked(HandleType p_colorBuffer);

    // Return a color buffer pointer from its handle
    WindowSurfacePtr getWindowSurface_locked(HandleType p_windowsurface);

    // Attach a ColorBuffer to a WindowSurface instance.
    // See the documentation for WindowSurface::setColorBuffer().
    // |p_surface| is the target WindowSurface's handle value.
    // |p_colorbuffer| is the ColorBuffer handle value.
    // Returns true on success, false otherwise.
    bool setWindowSurfaceColorBuffer(HandleType p_surface,
                                     HandleType p_colorbuffer);

    // Copy the content of a WindowSurface's Pbuffer to its attached
    // ColorBuffer. See the documentation for WindowSurface::flushColorBuffer()
    // |p_surface| is the target WindowSurface's handle value.
    // Returns true on success, false on failure.
    bool flushWindowSurfaceColorBuffer(HandleType p_surface);

    // Retrieves the color buffer handle associated with |p_surface|.
    // Returns 0 if there is no such handle.
    HandleType getWindowSurfaceColorBufferHandle(HandleType p_surface);

    // Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer
    // instance's EGLImage. This is intended to implement
    // glEGLImageTargetTexture2DOES() for all GLES versions.
    // |p_colorbuffer| is the ColorBuffer's handle value.
    // Returns true on success, false on failure.
    bool bindColorBufferToTexture(HandleType p_colorbuffer);
    bool bindColorBufferToTexture2(HandleType p_colorbuffer);

    // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
    // ColorBuffer's EGLImage. This is intended to implement
    // glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
    // |p_colorbuffer| is the ColorBuffer's handle value.
    // Returns true on success, false on failure.
    bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);

    // Read the content of a given ColorBuffer into client memory.
    // |p_colorbuffer| is the ColorBuffer's handle value. Similar
    // to glReadPixels(), this can be a slow operation.
    // |x|, |y|, |width| and |height| are the position and dimensions of
    // a rectangle whose pixel values will be transfered to the host.
    // |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA.
    // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
    // |pixels| is the address of a caller-provided buffer that will be filled
    // with the pixel data.
    void readColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
                         int height, GLenum format, GLenum type, void* pixels);

    // Read the content of a given YUV420_888 ColorBuffer into client memory.
    // |p_colorbuffer| is the ColorBuffer's handle value. Similar
    // to glReadPixels(), this can be a slow operation.
    // |x|, |y|, |width| and |height| are the position and dimensions of
    // a rectangle whose pixel values will be transfered to the host.
    // |pixels| is the address of a caller-provided buffer that will be filled
    // with the pixel data.
    // |pixles_size| is the size of buffer
    void readColorBufferYUV(HandleType p_colorbuffer, int x, int y, int width,
                            int height, void* pixels, uint32_t pixels_size);

    // create a Y texture and a UV texture with width and height, the created
    // texture ids are stored in textures respectively
    void createYUVTextures(uint32_t type, uint32_t count, int width, int height,
                           uint32_t* output);
    void destroyYUVTextures(uint32_t type, uint32_t count, uint32_t* textures);
    void updateYUVTextures(uint32_t type, uint32_t* textures, void* privData,
                           void* func);
    void swapTexturesAndUpdateColorBuffer(uint32_t colorbufferhandle, int x,
                                          int y, int width, int height,
                                          uint32_t format, uint32_t type,
                                          uint32_t texture_type,
                                          uint32_t* textures);

    // Update the content of a given ColorBuffer from client data.
    // |p_colorbuffer| is the ColorBuffer's handle value. Similar
    // to glReadPixels(), this can be a slow operation.
    // |x|, |y|, |width| and |height| are the position and dimensions of
    // a rectangle whose pixel values will be transfered to the GPU
    // |format| indicates the format of the OpenGL buffer, e.g. GL_RGB or
    // GL_RGBA. |frameworkFormat| indicates the format of the pixel data; if
    // FRAMEWORK_FORMAT_GL_COMPATIBLE, |format| (OpenGL format) is used.
    // Otherwise, explicit conversion to |format| is needed.
    // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
    // |pixels| is the address of a buffer containing the new pixel data.
    // Returns true on success, false otherwise.
    bool updateColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
                           int height, GLenum format, GLenum type,
                           void* pixels);
    // Replaces contents completely using the color buffer's current format,
    // with row length equal to width of a row in bytes.
    // The number of bytes is passed as a check.
    bool replaceColorBufferContents(HandleType p_colorbuffer,
                                    const void* pixels, size_t numBytes);
    // Reads back the raw color buffer to |pixels|
    // if |pixels| is not null.
    // Always returns in |numBytes| how many bytes were
    // planned to be transmitted.
    // |numBytes| is not an input parameter;
    // fewer or more bytes cannot be specified.
    // If the framework format is YUV, it will read
    // back as raw YUV data.
    bool readColorBufferContents(HandleType p_colorbuffer, size_t* numBytes,
                                 void* pixels);

    bool getColorBufferInfo(HandleType p_colorbuffer, int* width, int* height,
                            GLint* internalformat,
                            FrameworkFormat* frameworkFormat = nullptr);
    bool getBufferInfo(HandleType p_buffer, int* size);

    // Display the content of a given ColorBuffer into the framebuffer's
    // sub-window. |p_colorbuffer| is a handle value.
    // |needLockAndBind| is used to indicate whether the operation requires
    // acquiring/releasing the FrameBuffer instance's lock and binding the
    // contexts. It should be |false| only when called internally.
    bool post(HandleType p_colorbuffer, bool needLockAndBind = true);
    bool hasGuestPostedAFrame() { return m_guestPostedAFrame; }
    void resetGuestPostedAFrame() { m_guestPostedAFrame = false; }

    // Runs the post callback with |pixels| (good for when the readback
    // happens in a separate place)
    void doPostCallback(void* pixels, uint32_t displayId);

    void getPixels(void* pixels, uint32_t bytes, uint32_t displayId);
    void flushReadPipeline(int displayId);
    void ensureReadbackWorker();

    bool asyncReadbackSupported();
    emugl::Renderer::ReadPixelsCallback getReadPixelsCallback();
    emugl::Renderer::FlushReadPixelPipeline getFlushReadPixelPipeline();

    // Re-post the last ColorBuffer that was displayed through post().
    // This is useful if you detect that the sub-window content needs to
    // be re-displayed for any reason.
    bool repost(bool needLockAndBind = true);

    // Return the host EGLDisplay used by this instance.
    EGLDisplay getDisplay() const { return m_eglDisplay; }
    EGLSurface getWindowSurface() const { return m_eglSurface; }
    EGLContext getContext() const { return m_eglContext; }
    EGLConfig getConfig() const { return m_eglConfig; }

    // Change the rotation of the displayed GPU sub-window.
    void setDisplayRotation(float zRot) {
        if (zRot != m_zRot) {
            m_zRot = zRot;
            repost();
        }
    }

    // Changes what coordinate of this framebuffer will be displayed at the
    // corner of the GPU sub-window. Specifically, |px| and |py| = 0 means
    // align the bottom-left of the framebuffer with the bottom-left of the
    // sub-window, and |px| and |py| = 1 means align the top right of the
    // framebuffer with the top right of the sub-window. Intermediate values
    // interpolate between these states.
    void setDisplayTranslation(float px, float py) {
        // Sanity check the values to ensure they are between 0 and 1
        const float x = px > 1.f ? 1.f : (px < 0.f ? 0.f : px);
        const float y = py > 1.f ? 1.f : (py < 0.f ? 0.f : py);
        if (x != m_px || y != m_py) {
            m_px = x;
            m_py = y;
            repost();
        }
    }

    // Return a TextureDraw instance that can be used with this surfaces
    // and windows created by this instance.
    TextureDraw* getTextureDraw() const { return m_textureDraw; }

    // Create an eglImage and return its handle.  Reference:
    // https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_image_base.txt
    HandleType createClientImage(HandleType context, EGLenum target,
                                 GLuint buffer);
    // Call the implementation of eglDestroyImageKHR, return if succeeds or
    // not. Reference:
    // https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_image_base.txt
    EGLBoolean destroyClientImage(HandleType image);

    // Used internally.
    bool bind_locked();
    bool unbind_locked();

    void lockContextStructureRead() { m_contextStructureLock.lockRead(); }
    void unlockContextStructureRead() { m_contextStructureLock.unlockRead(); }

    // For use with sync threads and otherwise, any time we need a GL context
    // not specifically for drawing, but to obtain certain things about
    // GL state.
    // It can be unsafe / leaky to change the structure of contexts
    // outside the facilities the FrameBuffer class provides.
    void createTrivialContext(HandleType shared, HandleType* contextOut,
                              HandleType* surfOut);
    // createAndBindTrivialSharedContext(), but with a m_pbufContext
    // as shared, and not adding itself to the context map at all.
    void createAndBindTrivialSharedContext(EGLContext* contextOut,
                                           EGLSurface* surfOut);
    void unbindAndDestroyTrivialSharedContext(EGLContext context,
                                              EGLSurface surf);

    void setShuttingDown() { m_shuttingDown = true; }
    bool isShuttingDown() const { return m_shuttingDown; }
    bool compose(uint32_t bufferSize, void* buffer, bool post = true);

    ~FrameBuffer();

    void onSave(android::base::Stream* stream,
                const android::snapshot::ITextureSaverPtr& textureSaver);
    bool onLoad(android::base::Stream* stream,
                const android::snapshot::ITextureLoaderPtr& textureLoader);

    // lock and unlock handles (RenderContext, ColorBuffer, WindowSurface)
    void lock();
    void unlock();

    static void setMaxGLESVersion(GLESDispatchMaxVersion version);
    static GLESDispatchMaxVersion getMaxGLESVersion();

    float getDpr() const { return m_dpr; }
    int windowWidth() const { return m_windowWidth; }
    int windowHeight() const { return m_windowHeight; }
    float getPx() const { return m_px; }
    float getPy() const { return m_py; }
    int getZrot() const { return m_zRot; }

    bool isFastBlitSupported() const { return m_fastBlitSupported; }
    bool isVulkanInteropSupported() const { return m_vulkanInteropSupported; }
    bool importMemoryToColorBuffer(
#ifdef _WIN32
        void* handle,
#else
        int handle,
#endif
        uint64_t size, bool dedicated, bool linearTiling, bool vulkanOnly,
        uint32_t colorBufferHandle, VkImage, VkFormat);
    void setColorBufferInUse(uint32_t colorBufferHandle, bool inUse);

    // Used during tests to disable fast blit.
    void disableFastBlit();

    // Fill GLES usage protobuf
    void fillGLESUsages(android_studio::EmulatorGLESUsages*);
    // Save a screenshot of the previous frame.
    // nChannels should be 3 (RGB) or 4 (RGBA).
    // Note: swiftshader_indirect does not work with 3 channels
    void getScreenshot(unsigned int nChannels, unsigned int* width,
                       unsigned int* height, std::vector<unsigned char>& pixels,
                       int displayId, int desiredWidth, int desiredHeight,
                       int desiredRotation);
    void onLastColorBufferRef(uint32_t handle);
    ColorBuffer::Helper* getColorBufferHelper() { return m_colorBufferHelper; }
    ColorBufferPtr findColorBuffer(HandleType p_colorbuffer);

    void registerProcessCleanupCallback(void* key,
                                        std::function<void()> callback);
    void unregisterProcessCleanupCallback(void* key);

    void registerProcessSequenceNumberForPuid(uint64_t puid);
    uint32_t* getProcessSequenceNumberPtr(uint64_t puid);

    int createDisplay(uint32_t *displayId);
    int createDisplay(uint32_t displayId);
    int destroyDisplay(uint32_t displayId);
    int setDisplayColorBuffer(uint32_t displayId, uint32_t colorBuffer);
    int getDisplayColorBuffer(uint32_t displayId, uint32_t* colorBuffer);
    int getColorBufferDisplay(uint32_t colorBuffer, uint32_t* displayId);
    int getDisplayPose(uint32_t displayId, int32_t* x, int32_t* y, uint32_t* w,
                       uint32_t* h);
    int setDisplayPose(uint32_t displayId, int32_t x, int32_t y, uint32_t w,
                       uint32_t h, uint32_t dpi = 0);
    void getCombinedDisplaySize(int* w, int* h);
    struct DisplayInfo {
        uint32_t cb;
        int32_t pos_x;
        int32_t pos_y;
        uint32_t width;
        uint32_t height;
        uint32_t dpi;
        DisplayInfo()
            : cb(0), pos_x(0), pos_y(0), width(0), height(0), dpi(0){};
        DisplayInfo(uint32_t cb, int32_t x, int32_t y, uint32_t w, uint32_t h,
                    uint32_t d)
            : cb(cb), pos_x(x), pos_y(y), width(w), height(h), dpi(d) {}
    };
    // Inline with MultiDisplay::s_invalidIdMultiDisplay
    static const uint32_t s_invalidIdMultiDisplay = 0xFFFFFFAB;
    static const uint32_t s_maxNumMultiDisplay = 11;

    EGLContext getGlobalEGLContext() { return m_pbufContext; }
    HandleType getLastPostedColorBuffer() { return m_lastPostedColorBuffer; }
    void waitForGpu(uint64_t eglsync);
    void waitForGpuVulkan(uint64_t deviceHandle, uint64_t fenceHandle);
    void asyncWaitForGpuWithCb(uint64_t eglsync, FenceCompletionCallback cb);
    void asyncWaitForGpuVulkanWithCb(uint64_t deviceHandle, uint64_t fenceHandle, FenceCompletionCallback cb);
    void asyncWaitForGpuVulkanQsriWithCb(uint64_t image, FenceCompletionCallback cb);
    void waitForGpuVulkanQsri(uint64_t image);

    void setGuestManagedColorBufferLifetime(bool guestManaged);

    VkImageLayout getVkImageLayoutForCompose() const;

   private:
    FrameBuffer(int p_width, int p_height, bool useSubWindow);
    HandleType genHandle_locked();

    bool bindSubwin_locked();
    bool bindFakeWindow_locked();
    bool removeSubWindow_locked();
    // Returns the set of ColorBuffers destroyed (for further cleanup)
    std::vector<HandleType> cleanupProcGLObjects_locked(uint64_t puid,
                                                        bool forced = false);

    void markOpened(ColorBufferRef* cbRef);
    // Returns true if the color buffer was erased.
    bool closeColorBufferLocked(HandleType p_colorbuffer, bool forced = false);
    // Returns true if this was the last ref and we need to destroy stuff.
    bool decColorBufferRefCountLocked(HandleType p_colorbuffer);
    // Close all expired color buffers for real.
    // Treat all delayed color buffers as expired if forced=true
    void performDelayedColorBufferCloseLocked(bool forced = false);
    void eraseDelayedCloseColorBufferLocked(HandleType cb, uint64_t ts);

    bool postImpl(HandleType p_colorbuffer, bool needLockAndBind = true,
                  bool repaint = false);
    void setGuestPostedAFrame() { m_guestPostedAFrame = true; }
    HandleType createColorBufferLocked(int p_width, int p_height,
                                       GLenum p_internalFormat,
                                       FrameworkFormat p_frameworkFormat);
    HandleType createColorBufferWithHandleLocked(
        int p_width, int p_height, GLenum p_internalFormat,
        FrameworkFormat p_frameworkFormat, HandleType handle);
    HandleType createBufferLocked(int p_size);
    HandleType createBufferWithHandleLocked(int p_size, HandleType handle);

    void recomputeLayout();
    void setDisplayPoseInSkinUI(int totalHeight);
    void sweepColorBuffersLocked();

   private:
    static FrameBuffer* s_theFrameBuffer;
    static HandleType s_nextHandle;
    int m_x = 0;
    int m_y = 0;
    int m_framebufferWidth = 0;
    int m_framebufferHeight = 0;
    int m_windowWidth = 0;
    int m_windowHeight = 0;
    float m_dpr = 0;

    bool m_useSubWindow = false;
    bool m_eglContextInitialized = false;

    bool m_fpsStats = false;
    bool m_perfStats = false;
    int m_statsNumFrames = 0;
    long long m_statsStartTime = 0;

    android::base::Thread* m_perfThread;
    android::base::Lock m_lock;
    android::base::ReadWriteLock m_contextStructureLock;
    FbConfigList* m_configs = nullptr;
    FBNativeWindowType m_nativeWindow = 0;
    FrameBufferCaps m_caps = {};
    EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
    RenderContextMap m_contexts;
    WindowSurfaceMap m_windows;
    ColorBufferMap m_colorbuffers;
    BufferMap m_buffers;
    std::unordered_map<HandleType, HandleType> m_windowSurfaceToColorBuffer;

    // A collection of color buffers that were closed without any usages
    // (|opened| == false).
    //
    // If a buffer reached |refcount| == 0 while not being |opened|, instead of
    // deleting it we remember the timestamp when this happened. Later, we
    // check if the buffer stayed unopened long enough and if it did, we delete
    // it permanently. On the other hand, if the color buffer was used then
    // we don't care about timestamps anymore.
    //
    // Note: this collection is ordered by |ts| field.
    struct ColorBufferCloseInfo {
        uint64_t ts;          // when we got the close request.
        HandleType cbHandle;  // 0 == already closed, do nothing
    };
    using ColorBufferDelayedClose = std::vector<ColorBufferCloseInfo>;
    ColorBufferDelayedClose m_colorBufferDelayedCloseList;

    ColorBuffer::Helper* m_colorBufferHelper = nullptr;

    EGLSurface m_eglSurface = EGL_NO_SURFACE;
    EGLContext m_eglContext = EGL_NO_CONTEXT;
    EGLSurface m_pbufSurface = EGL_NO_SURFACE;
    EGLContext m_pbufContext = EGL_NO_CONTEXT;

    EGLSurface m_eglFakeWindowSurface = EGL_NO_SURFACE;
    EGLContext m_eglFakeWindowContext = EGL_NO_CONTEXT;

    EGLContext m_prevContext = EGL_NO_CONTEXT;
    EGLSurface m_prevReadSurf = EGL_NO_SURFACE;
    EGLSurface m_prevDrawSurf = EGL_NO_SURFACE;
    EGLNativeWindowType m_subWin = {};
    TextureDraw* m_textureDraw = nullptr;
    EGLConfig m_eglConfig = nullptr;
    HandleType m_lastPostedColorBuffer = 0;
    // With Vulkan swapchain, compose also means to post to the WSI surface.
    // In this case, don't do anything in the subsequent resource flush.
    bool m_justVkComposed = false;
    float m_zRot = 0;
    float m_px = 0;
    float m_py = 0;

    // Async readback
    enum class ReadbackCmd {
        Init = 0,
        GetPixels = 1,
        AddRecordDisplay = 2,
        DelRecordDisplay = 3,
        Exit = 4,
    };
    struct Readback {
        ReadbackCmd cmd;
        uint32_t displayId;
        GLuint bufferId;
        void* pixelsOut;
        uint32_t bytes;
        uint32_t width;
        uint32_t height;
    };
    android::base::WorkerProcessingResult sendReadbackWorkerCmd(
        const Readback& readback);
    bool m_asyncReadbackSupported = true;
    bool m_guestPostedAFrame = false;

    struct onPost {
        emugl::Renderer::OnPostCallback cb;
        void* context;
        uint32_t displayId;
        uint32_t width;
        uint32_t height;
        unsigned char* img = nullptr;
        bool readBgra;
        ~onPost() {
            if (img) {
                delete[] img;
                img = nullptr;
            }
        }
    };
    std::map<uint32_t, onPost> m_onPost;
    std::unique_ptr<ReadbackWorker> m_readbackWorker;
    android::base::WorkerThread<Readback> m_readbackThread;

    std::string m_glVendor;
    std::string m_glRenderer;
    std::string m_glVersion;

    // The host associates color buffers with guest processes for memory
    // cleanup. Guest processes are identified with a host generated unique ID.
    ProcOwnedWindowSurfaces m_procOwnedWindowSurfaces;
    ProcOwnedColorBuffers m_procOwnedColorBuffers;
    ProcOwnedEGLImages m_procOwnedEGLImages;
    ProcOwnedRenderContexts m_procOwnedRenderContext;
    ProcOwnedCleanupCallbacks m_procOwnedCleanupCallbacks;
    ProcOwnedSequenceNumbers m_procOwnedSequenceNumbers;

    // Flag set when emulator is shutting down.
    bool m_shuttingDown = false;

    // When this feature is enabled, open/close operations from gralloc in guest
    // will no longer control the reference counting of color buffers on host.
    // Instead, it will be managed by a file descriptor in the guest kernel. In
    // case all the native handles in guest are destroyed, the pipe will be
    // automatically closed by the kernel. We only need to do reference counting
    // for color buffers attached in window surface.
    bool m_refCountPipeEnabled = false;

    // When this feature is enabled, and m_refCountPipeEnabled == false, color
    // buffer close operations will immediately close the color buffer if host
    // refcount hits 0. This is for use with guest kernels where the color
    // buffer is already tied to a file descriptor in the guest kernel.
    bool m_noDelayCloseColorBufferEnabled = false;

    std::unique_ptr<PostWorker> m_postWorker = {};
    android::base::WorkerThread<Post> m_postThread;
    android::base::WorkerProcessingResult postWorkerFunc(const Post& post);
    void sendPostWorkerCmd(Post post);

    bool m_fastBlitSupported = false;
    bool m_vulkanInteropSupported = false;
    bool m_guestUsesAngle = false;
    // Whether the guest manages ColorBuffer lifetime
    // so we don't need refcounting on the host side.
    bool m_guestManagedColorBufferLifetime = false;

    android::base::MessageChannel<HandleType, 1024>
        mOutstandingColorBufferDestroys;

    // The implementation for Vulkan native swapchain. Only initialized when
    // useVulkan is set when calling FrameBuffer::initialize().
    std::unique_ptr<DisplayVk> m_displayVk;
    VkInstance m_vkInstance = VK_NULL_HANDLE;
    VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE;

    // UUIDs of physical devices for Vulkan and GLES, respectively.  In most
    // cases, this determines whether we can support zero-copy interop.
    uint8_t m_vulkanUUID[VK_UUID_SIZE];
    uint8_t m_glesUUID[GL_UUID_SIZE_EXT];
    static_assert(VK_UUID_SIZE == GL_UUID_SIZE_EXT);
};
#endif