919 lines
41 KiB
C++
919 lines
41 KiB
C++
/*
|
|
* 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 <array>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "Buffer.h"
|
|
#include "Compositor.h"
|
|
#include "Display.h"
|
|
#include "DisplaySurface.h"
|
|
#include "Hwc2.h"
|
|
#include "PostCommands.h"
|
|
#include "PostWorker.h"
|
|
#include "ReadbackWorker.h"
|
|
#include "VsyncThread.h"
|
|
#include "aemu/base/AsyncResult.h"
|
|
#include "aemu/base/EventNotificationSupport.h"
|
|
#include "aemu/base/HealthMonitor.h"
|
|
#include "aemu/base/ManagedDescriptor.hpp"
|
|
#include "aemu/base/Metrics.h"
|
|
#include "aemu/base/files/Stream.h"
|
|
#include "aemu/base/synchronization/Lock.h"
|
|
#include "aemu/base/synchronization/MessageChannel.h"
|
|
#include "aemu/base/threads/Thread.h"
|
|
#include "aemu/base/threads/WorkerThread.h"
|
|
#include "gl/BufferGl.h"
|
|
#include "gl/ColorBufferGl.h"
|
|
#include "gl/CompositorGl.h"
|
|
#include "gl/DisplaySurfaceGl.h"
|
|
#include "gl/EmulatedEglConfig.h"
|
|
#include "gl/EmulatedEglContext.h"
|
|
#include "gl/EmulatedEglImage.h"
|
|
#include "gl/EmulatedEglWindowSurface.h"
|
|
#include "gl/EmulationGl.h"
|
|
#include "gl/GLESVersionDetector.h"
|
|
#include "gl/TextureDraw.h"
|
|
#include "render-utils/Renderer.h"
|
|
#include "render-utils/virtio_gpu_ops.h"
|
|
#include "render_api.h"
|
|
#include "snapshot/common.h"
|
|
#include "utils/RenderDoc.h"
|
|
#include "vulkan/vk_util.h"
|
|
|
|
namespace gfxstream {
|
|
namespace vk {
|
|
class DisplayVk;
|
|
} // namespace vk
|
|
} // namespace gfxstream
|
|
|
|
namespace gfxstream {
|
|
|
|
using android::base::CreateMetricsLogger;
|
|
using emugl::HealthMonitor;
|
|
using emugl::MetricsLogger;
|
|
|
|
struct BufferRef {
|
|
BufferPtr buffer;
|
|
};
|
|
|
|
class ProcessResources {
|
|
public:
|
|
// We only allow ProcessResources to be created on the heap, because the pointer to
|
|
// mSequenceNumber shouldn't change until ProcessResources is destroyed.
|
|
static std::unique_ptr<ProcessResources> create() {
|
|
return std::unique_ptr<ProcessResources>(new ProcessResources());
|
|
}
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ProcessResources);
|
|
|
|
~ProcessResources() = default;
|
|
std::atomic<uint32_t>* getSequenceNumberPtr() const {
|
|
return &mSequenceNumber;
|
|
}
|
|
|
|
private:
|
|
ProcessResources() : mSequenceNumber(0) {}
|
|
mutable std::atomic<uint32_t> mSequenceNumber;
|
|
};
|
|
|
|
typedef std::unordered_map<uint64_t, gl::EmulatedEglWindowSurfaceSet>
|
|
ProcOwnedEmulatedEglWindowSurfaces;
|
|
|
|
typedef std::unordered_map<uint64_t, gl::EmulatedEglContextSet> ProcOwnedEmulatedEglContexts;
|
|
|
|
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_map<uint64_t, gl::EmulatedEglImageSet> ProcOwnedEmulatedEGLImages;
|
|
|
|
typedef std::unordered_map<void*, std::function<void()>> CallbackMap;
|
|
typedef std::unordered_map<uint64_t, CallbackMap> ProcOwnedCleanupCallbacks;
|
|
|
|
// 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 android::base::EventNotificationSupport<FrameBufferChangeEvent> {
|
|
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);
|
|
|
|
// Finalize the instance.
|
|
static void finalize();
|
|
|
|
// 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();
|
|
|
|
// 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 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 gl::EmulatedEglConfigList* getConfigs() const;
|
|
|
|
// 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(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_graphicsAdapterVendor.c_str();
|
|
*renderer = m_graphicsAdapterName.c_str();
|
|
*version = m_graphicsApiVersion.c_str();
|
|
}
|
|
|
|
// Create a new EmulatedEglContext 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 createEmulatedEglContext(int p_config, HandleType p_share,
|
|
gl::GLESApi version = gl::GLESApi_CM);
|
|
|
|
// Destroy a given EmulatedEglContext instance. |p_context| is its handle
|
|
// value as returned by createEmulatedEglContext().
|
|
void destroyEmulatedEglContext(HandleType p_context);
|
|
|
|
// Create a new EmulatedEglWindowSurface 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 createEmulatedEglWindowSurface(int p_config, int p_width, int p_height);
|
|
|
|
// Destroy a given EmulatedEglWindowSurface instance. |p_surcace| is its
|
|
// handle value as returned by createEmulatedEglWindowSurface().
|
|
void destroyEmulatedEglWindowSurface(HandleType p_surface);
|
|
|
|
// Returns the set of ColorBuffers destroyed (for further cleanup)
|
|
std::vector<HandleType> destroyEmulatedEglWindowSurfaceLocked(HandleType p_surface);
|
|
|
|
void createEmulatedEglFenceSync(EGLenum type,
|
|
int destroyWhenSignaled,
|
|
uint64_t* outSync = nullptr,
|
|
uint64_t* outSyncThread = nullptr);
|
|
|
|
// 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);
|
|
|
|
// Variant of createBuffer except with a particular handle already
|
|
// assigned and using device local memory. This is for use with
|
|
// virtio-gpu's RESOURCE_CREATE ioctl for BLOB resources.
|
|
void createBufferWithHandle(uint64_t size, HandleType handle);
|
|
|
|
// Call this function when a render thread terminates to destroy all
|
|
// resources it created. Necessary to avoid leaking host resources
|
|
// when a guest application crashes, for example.
|
|
void drainGlRenderThreadResources();
|
|
|
|
// 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 drainGlRenderThreadContexts();
|
|
|
|
// Call this function when a render thread terminates to destroy all
|
|
// remaining window surface it created. Necessary to avoid leaking
|
|
// host buffers when a guest application crashes, for example.
|
|
void drainGlRenderThreadSurfaces();
|
|
|
|
// 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);
|
|
|
|
// The caller mustn't refer to this puid before this function returns, i.e. the creation of the
|
|
// host process pipe must be blocked until this function returns.
|
|
void createGraphicsProcessResources(uint64_t puid);
|
|
// The process resource is returned so that we can destroy it on a separate thread.
|
|
std::unique_ptr<ProcessResources> removeGraphicsProcessResources(uint64_t puid);
|
|
// TODO(kaiyili): retire cleanupProcGLObjects in favor of removeGraphicsProcessResources.
|
|
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
|
|
gl::EmulatedEglContextPtr getContext_locked(HandleType p_context);
|
|
|
|
// Return a color buffer pointer from its handle
|
|
gl::EmulatedEglWindowSurfacePtr getWindowSurface_locked(HandleType p_windowsurface);
|
|
|
|
// Attach a ColorBuffer to a EmulatedEglWindowSurface instance.
|
|
// See the documentation for EmulatedEglWindowSurface::setColorBuffer().
|
|
// |p_surface| is the target EmulatedEglWindowSurface's handle value.
|
|
// |p_colorbuffer| is the ColorBuffer handle value.
|
|
// Returns true on success, false otherwise.
|
|
bool setEmulatedEglWindowSurfaceColorBuffer(HandleType p_surface,
|
|
HandleType p_colorbuffer);
|
|
|
|
// Copy the content of a EmulatedEglWindowSurface's Pbuffer to its attached
|
|
// ColorBuffer. See the documentation for
|
|
// EmulatedEglWindowSurface::flushColorBuffer().
|
|
// |p_surface| is the target WindowSurface's handle value.
|
|
// Returns true on success, false on failure.
|
|
bool flushEmulatedEglWindowSurfaceColorBuffer(HandleType p_surface);
|
|
|
|
// Retrieves the color buffer handle associated with |p_surface|.
|
|
// Returns 0 if there is no such handle.
|
|
HandleType getEmulatedEglWindowSurfaceColorBufferHandle(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 Buffer into client memory.
|
|
// |p_buffer| is the Buffer's handle value.
|
|
// |offset| and |size| are the position and size of a slice of the buffer
|
|
// that will be read.
|
|
// |bytes| is the address of a caller-provided buffer that will be filled
|
|
// with the buffer data.
|
|
void readBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* bytes);
|
|
|
|
// 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 Buffer from client data.
|
|
// |p_buffer| is the Buffer's handle value.
|
|
// |offset| and |size| are the position and size of a slice of the buffer
|
|
// that will be updated.
|
|
// |bytes| is the address of a caller-provided buffer containing the new
|
|
// buffer data.
|
|
bool updateBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* pixels);
|
|
|
|
// 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);
|
|
bool updateColorBufferFromFrameworkFormat(HandleType p_colorbuffer, int x, int y, int width,
|
|
int height, FrameworkFormat fwkFormat, GLenum format,
|
|
GLenum type, void* pixels);
|
|
|
|
// 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);
|
|
// The callback will always be called; however, the callback may not be called
|
|
// until after this function has returned. If the callback is deferred, then it
|
|
// will be dispatched to run on SyncThread.
|
|
void postWithCallback(HandleType p_colorbuffer, Post::CompletionCallback callback, 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();
|
|
Renderer::ReadPixelsCallback getReadPixelsCallback();
|
|
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);
|
|
|
|
gl::EmulationGl& getEmulationGl();
|
|
bool hasEmulationGl() const { return m_emulationGl != nullptr; }
|
|
|
|
// Return the host EGLDisplay used by this instance.
|
|
EGLDisplay getDisplay() const;
|
|
EGLSurface getWindowSurface() const;
|
|
EGLContext getContext() const;
|
|
EGLConfig getConfig() const;
|
|
ContextHelper* getPbufferSurfaceContextHelper() const;
|
|
|
|
// 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.
|
|
gl::TextureDraw* getTextureDraw() const;
|
|
|
|
// Create an eglImage and return its handle. Reference:
|
|
// https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_image_base.txt
|
|
HandleType createEmulatedEglImage(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 destroyEmulatedEglImage(HandleType image);
|
|
|
|
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);
|
|
// createTrivialContext(), but with a m_pbufContext
|
|
// as shared, and not adding itself to the context map at all.
|
|
void createSharedTrivialContext(EGLContext* contextOut, EGLSurface* surfOut);
|
|
void destroySharedTrivialContext(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);
|
|
// When false is returned, the callback won't be called. The callback will
|
|
// be called on the PostWorker thread without blocking the current thread.
|
|
AsyncResult composeWithCallback(uint32_t bufferSize, void* buffer,
|
|
Post::CompletionCallback callback);
|
|
|
|
~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 (EmulatedEglContext, ColorBuffer, EmulatedEglWindowSurface)
|
|
void lock();
|
|
void unlock();
|
|
|
|
gl::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;
|
|
void disableFastBlitForTesting();
|
|
|
|
bool isVulkanInteropSupported() const { return m_vulkanInteropSupported; }
|
|
bool isVulkanEnabled() const { return m_vulkanEnabled; }
|
|
|
|
// Fill GLES usage protobuf
|
|
void fillGLESUsages(android_studio::EmulatorGLESUsages*);
|
|
|
|
// Saves a screenshot of the previous frame.
|
|
// nChannels should be 3 (RGB) or 4 (RGBA).
|
|
// You must provide a pre-allocated buffer of sufficient
|
|
// size. Returns 0 on success. In the case of failure and if *cPixels != 0
|
|
// you can call this function again with a buffer of size *cPixels. cPixels
|
|
// should usually be at at least desiredWidth * desiredHeight * nChannels.
|
|
//
|
|
// In practice the buffer should be > desiredWidth *
|
|
// desiredHeight * nChannels.
|
|
//
|
|
// Note: Do not call this function again if it fails and *cPixels == 0
|
|
// swiftshader_indirect does not work with 3 channels
|
|
//
|
|
// This function supports rectangle snipping by
|
|
// providing an |rect| parameter. The default value of {{0,0}, {0,0}}
|
|
// indicates the users wants to snip the entire screen instead of a
|
|
// partial screen.
|
|
// - |rect| represents a rectangle within the screen defined by
|
|
// desiredWidth and desiredHeight.
|
|
int getScreenshot(unsigned int nChannels, unsigned int* width, unsigned int* height,
|
|
uint8_t* pixels, size_t* cPixels, int displayId, int desiredWidth,
|
|
int desiredHeight, int desiredRotation, Rect rect = {{0, 0}, {0, 0}});
|
|
|
|
void onLastColorBufferRef(uint32_t handle);
|
|
ColorBufferPtr findColorBuffer(HandleType p_colorbuffer);
|
|
BufferPtr findBuffer(HandleType p_buffer);
|
|
|
|
void registerProcessCleanupCallback(void* key,
|
|
std::function<void()> callback);
|
|
void unregisterProcessCleanupCallback(void* key);
|
|
|
|
const ProcessResources* getProcessResources(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() const;
|
|
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);
|
|
|
|
bool platformImportResource(uint32_t handle, uint32_t info, void* resource);
|
|
void* platformCreateSharedEglContext(void);
|
|
bool platformDestroySharedEglContext(void* context);
|
|
|
|
void setGuestManagedColorBufferLifetime(bool guestManaged);
|
|
|
|
std::unique_ptr<BorrowedImageInfo> borrowColorBufferForComposition(uint32_t colorBufferHandle,
|
|
bool colorBufferIsTarget);
|
|
std::unique_ptr<BorrowedImageInfo> borrowColorBufferForDisplay(uint32_t colorBufferHandle);
|
|
|
|
HealthMonitor<>* getHealthMonitor() { return m_healthMonitor.get(); }
|
|
|
|
emugl::MetricsLogger& getMetricsLogger() {
|
|
return *m_logger;
|
|
}
|
|
|
|
void logVulkanOutOfMemory(VkResult result, const char* function, int line,
|
|
std::optional<uint64_t> allocationSize = std::nullopt);
|
|
|
|
void setVsyncHz(int vsyncHz);
|
|
void scheduleVsyncTask(VsyncThread::VsyncTask task);
|
|
void setDisplayConfigs(int configId, int w, int h, int dpiX, int dpiY);
|
|
void setDisplayActiveConfig(int configId);
|
|
const int getDisplayConfigsCount();
|
|
const int getDisplayConfigsParam(int configId, EGLint param);
|
|
const int getDisplayActiveConfig();
|
|
|
|
bool flushColorBufferFromGl(HandleType colorBufferHandle);
|
|
bool flushColorBufferFromGlLocked(HandleType colorBufferHandle);
|
|
bool flushColorBufferFromVk(HandleType colorBufferHandle);
|
|
bool flushColorBufferFromVkBytes(HandleType colorBufferHandle, const void* bytes, size_t bytesSize);
|
|
bool invalidateColorBufferForGl(HandleType colorBufferHandle);
|
|
bool invalidateColorBufferForVk(HandleType colorBufferHandle);
|
|
|
|
const gl::EGLDispatch* getEglDispatch();
|
|
const gl::GLESv2Dispatch* getGles2Dispatch();
|
|
|
|
private:
|
|
FrameBuffer(int p_width, int p_height, bool useSubWindow);
|
|
// Requires the caller to hold the m_colorBufferMapLock until the new handle is inserted into of
|
|
// the object handle maps.
|
|
HandleType genHandle_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);
|
|
// Decrease refcount but not destroy the object.
|
|
// Mainly used in post thread, when we need to destroy the object but cannot in post thread.
|
|
void decColorBufferRefCountNoDestroy(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);
|
|
|
|
AsyncResult postImpl(HandleType p_colorbuffer, Post::CompletionCallback callback,
|
|
bool needLockAndBind = true, bool repaint = false);
|
|
bool postImplSync(HandleType p_colorbuffer, bool needLockAndBind = true, bool repaint = false);
|
|
void setGuestPostedAFrame() {
|
|
m_guestPostedAFrame = true;
|
|
fireEvent({FrameBufferChange::FrameReady, mFrameNumber++});
|
|
}
|
|
HandleType createColorBufferWithHandleLocked(int p_width, int p_height, GLenum p_internalFormat,
|
|
FrameworkFormat p_frameworkFormat,
|
|
HandleType handle);
|
|
HandleType createBufferWithHandleLocked(int p_size, HandleType handle, uint32_t memoryProperty);
|
|
|
|
void recomputeLayout();
|
|
void setDisplayPoseInSkinUI(int totalHeight);
|
|
void sweepColorBuffersLocked();
|
|
|
|
std::future<void> blockPostWorker(std::future<void> continueSignal);
|
|
|
|
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;
|
|
std::atomic_int m_windowWidth = 0;
|
|
std::atomic_int m_windowHeight = 0;
|
|
float m_dpr = 0;
|
|
|
|
bool m_useSubWindow = 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;
|
|
android::base::Lock m_colorBufferMapLock;
|
|
uint64_t mFrameNumber;
|
|
FBNativeWindowType m_nativeWindow = 0;
|
|
gl::EmulatedEglContextMap m_contexts;
|
|
gl::EmulatedEglImageMap m_images;
|
|
gl::EmulatedEglWindowSurfaceMap m_windows;
|
|
ColorBufferMap m_colorbuffers;
|
|
BufferMap m_buffers;
|
|
std::unordered_map<HandleType, HandleType> m_EmulatedEglWindowSurfaceToColorBuffer;
|
|
|
|
// 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;
|
|
|
|
EGLNativeWindowType m_subWin = {};
|
|
HandleType m_lastPostedColorBuffer = 0;
|
|
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;
|
|
void* pixelsOut;
|
|
uint32_t bytes;
|
|
uint32_t width;
|
|
uint32_t height;
|
|
};
|
|
android::base::WorkerProcessingResult sendReadbackWorkerCmd(
|
|
const Readback& readback);
|
|
bool m_guestPostedAFrame = false;
|
|
|
|
struct onPost {
|
|
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;
|
|
ReadbackWorker* m_readbackWorker = nullptr;
|
|
android::base::WorkerThread<Readback> m_readbackThread;
|
|
std::atomic_bool m_readbackThreadStarted = false;
|
|
|
|
std::string m_graphicsAdapterVendor;
|
|
std::string m_graphicsAdapterName;
|
|
std::string m_graphicsApiVersion;
|
|
std::string m_graphicsApiExtensions;
|
|
std::string m_graphicsDeviceExtensions;
|
|
|
|
// The host associates color buffers with guest processes for memory
|
|
// cleanup. Guest processes are identified with a host generated unique ID.
|
|
// TODO(kaiyili): move all those resources to the ProcessResources struct.
|
|
ProcOwnedColorBuffers m_procOwnedColorBuffers;
|
|
ProcOwnedEmulatedEGLImages m_procOwnedEmulatedEglImages;
|
|
ProcOwnedEmulatedEglContexts m_procOwnedEmulatedEglContexts;
|
|
ProcOwnedEmulatedEglWindowSurfaces m_procOwnedEmulatedEglWindowSurfaces;
|
|
ProcOwnedCleanupCallbacks m_procOwnedCleanupCallbacks;
|
|
std::unordered_map<uint64_t, std::unique_ptr<ProcessResources>> m_procOwnedResources;
|
|
|
|
// 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 = {};
|
|
std::atomic_bool m_postThreadStarted = false;
|
|
android::base::WorkerThread<Post> m_postThread;
|
|
android::base::WorkerProcessingResult postWorkerFunc(Post& post);
|
|
std::future<void> sendPostWorkerCmd(Post post);
|
|
|
|
bool m_vulkanInteropSupported = false;
|
|
bool m_vulkanEnabled = 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;
|
|
|
|
std::unique_ptr<gl::EmulationGl> m_emulationGl;
|
|
gl::DisplayGl* m_displayGl = nullptr;
|
|
|
|
Compositor* m_compositor = nullptr;
|
|
bool m_useVulkanComposition = false;
|
|
|
|
vk::VkEmulation* m_emulationVk = nullptr;
|
|
// The implementation for Vulkan native swapchain. Only initialized when useVulkan is set when
|
|
// calling FrameBuffer::initialize(). DisplayVk is actually owned by VkEmulation.
|
|
vk::DisplayVk* m_displayVk = nullptr;
|
|
VkInstance m_vkInstance = VK_NULL_HANDLE;
|
|
std::unique_ptr<emugl::RenderDoc> m_renderDoc = nullptr;
|
|
|
|
// TODO(b/233939967): Refactor to create DisplayGl and DisplaySurfaceGl
|
|
// and remove usage of non-generic DisplayVk.
|
|
Display* m_display;
|
|
std::unique_ptr<DisplaySurface> m_displaySurface;
|
|
|
|
// CompositorGl.
|
|
// TODO: update RenderDoc to be a DisplaySurfaceUser.
|
|
std::vector<DisplaySurfaceUser*> m_displaySurfaceUsers;
|
|
|
|
// UUIDs of physical devices for Vulkan and GLES, respectively. In most
|
|
// cases, this determines whether we can support zero-copy interop.
|
|
using VkUuid = std::array<uint8_t, VK_UUID_SIZE>;
|
|
VkUuid m_vulkanUUID{};
|
|
static_assert(VK_UUID_SIZE == GL_UUID_SIZE_EXT);
|
|
|
|
// Tracks platform EGL contexts that have been handed out to other users,
|
|
// indexed by underlying native EGL context object.
|
|
struct PlatformEglContextInfo {
|
|
EGLContext context;
|
|
EGLSurface surface;
|
|
};
|
|
std::unordered_map<void*, PlatformEglContextInfo> m_platformEglContexts;
|
|
|
|
std::unique_ptr<MetricsLogger> m_logger;
|
|
std::unique_ptr<HealthMonitor<>> m_healthMonitor;
|
|
|
|
int m_vsyncHz = 60;
|
|
|
|
// Vsync thread.
|
|
std::unique_ptr<VsyncThread> m_vsyncThread = {};
|
|
|
|
struct DisplayConfig{
|
|
int w;
|
|
int h;
|
|
int dpiX;
|
|
int dpiY;
|
|
DisplayConfig() {}
|
|
DisplayConfig(int w, int h, int x, int y)
|
|
: w(w), h(h), dpiX(x), dpiY(y) {}
|
|
};
|
|
std::map<int, DisplayConfig> mDisplayConfigs;
|
|
int mDisplayActiveConfigId = -1;
|
|
};
|
|
|
|
} // namespace gfxstream
|
|
|
|
#endif
|