SF: Add WindowDisconnector for screenshot code
Adds a simple WindowDisconnector class that holds an ANativeWindow* and
disconnects from it when it goes out of scope. This allows us to
drastically reduce the nesting of the captureScreenImplLocked method
of SurfaceFlinger.
Bug: 62257775
Test: SurfaceFlinger_test and manually verified that screenshots still
work
Change-Id: I55123a7a6b2036158d0959328b0e6f8b206cce5d
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c10493b..4b6ddef 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4335,6 +4335,16 @@
hw->setViewportAndProjection();
}
+// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
+class WindowDisconnector {
+public:
+ WindowDisconnector(ANativeWindow* window, int api) : mWindow(window), mApi(api) {}
+ ~WindowDisconnector() { native_window_api_disconnect(mWindow, mApi); }
+
+private:
+ ANativeWindow* const mWindow;
+ const int mApi;
+};
status_t SurfaceFlinger::captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
@@ -4395,114 +4405,119 @@
ANativeWindow* window = sur.get();
status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
- if (result == NO_ERROR) {
- uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ if (result != NO_ERROR) {
+ return result;
+ }
- int err = 0;
- err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
- err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
- err |= native_window_set_usage(window, usage);
+ // This will disconnect from the window whenever we leave this method
+ WindowDisconnector disconnector(window, NATIVE_WINDOW_API_EGL);
- if (err == NO_ERROR) {
- ANativeWindowBuffer* buffer;
- /* TODO: Once we have the sync framework everywhere this can use
- * server-side waits on the fence that dequeueBuffer returns.
- */
- result = native_window_dequeue_buffer_and_wait(window, &buffer);
- if (result == NO_ERROR) {
- int syncFd = -1;
- // create an EGLImage from the buffer so we can later
- // turn it into a texture
- EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, buffer, NULL);
- if (image != EGL_NO_IMAGE_KHR) {
- // this binds the given EGLImage as a framebuffer for the
- // duration of this scope.
- RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image);
- if (imageBond.getStatus() == NO_ERROR) {
- // this will in fact render into our dequeued buffer
- // via an FBO, which means we didn't have to create
- // an EGLSurface and therefore we're not
- // dependent on the context's EGLConfig.
- renderScreenImplLocked(
- hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
- useIdentityTransform, rotation);
+ uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-#ifdef USE_HWC2
- if (hasWideColorDisplay) {
- native_window_set_buffers_data_space(window,
- getRenderEngine().usesWideColor() ?
- HAL_DATASPACE_DISPLAY_P3 : HAL_DATASPACE_V0_SRGB);
- }
-#endif
+ int err = 0;
+ err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
+ err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+ err |= native_window_set_usage(window, usage);
- // Attempt to create a sync khr object that can produce a sync point. If that
- // isn't available, create a non-dupable sync object in the fallback path and
- // wait on it directly.
- EGLSyncKHR sync;
- if (!DEBUG_SCREENSHOTS) {
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
- // native fence fd will not be populated until flush() is done.
- getRenderEngine().flush();
- } else {
- sync = EGL_NO_SYNC_KHR;
- }
- if (sync != EGL_NO_SYNC_KHR) {
- // get the sync fd
- syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
- if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("captureScreen: failed to dup sync khr object");
- syncFd = -1;
- }
- eglDestroySyncKHR(mEGLDisplay, sync);
- } else {
- // fallback path
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
- if (sync != EGL_NO_SYNC_KHR) {
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/);
- EGLint eglErr = eglGetError();
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("captureScreen: fence wait timed out");
- } else {
- ALOGW_IF(eglErr != EGL_SUCCESS,
- "captureScreen: error waiting on EGL fence: %#x", eglErr);
- }
- eglDestroySyncKHR(mEGLDisplay, sync);
- } else {
- ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError());
- }
- }
- if (DEBUG_SCREENSHOTS) {
- uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
- getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
- checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
- hw, minLayerZ, maxLayerZ);
- delete [] pixels;
- }
+ if (err != NO_ERROR) {
+ return BAD_VALUE;
+ }
- } else {
- ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
- result = INVALID_OPERATION;
- window->cancelBuffer(window, buffer, syncFd);
- buffer = NULL;
- }
- // destroy our image
- eglDestroyImageKHR(mEGLDisplay, image);
- } else {
- result = BAD_VALUE;
- }
- if (buffer) {
- // queueBuffer takes ownership of syncFd
- result = window->queueBuffer(window, buffer, syncFd);
- }
- }
- } else {
- result = BAD_VALUE;
+ ANativeWindowBuffer* buffer;
+ /* TODO: Once we have the sync framework everywhere this can use
+ * server-side waits on the fence that dequeueBuffer returns.
+ */
+ result = native_window_dequeue_buffer_and_wait(window, &buffer);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ int syncFd = -1;
+ // create an EGLImage from the buffer so we can later
+ // turn it into a texture
+ EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, buffer, NULL);
+ if (image == EGL_NO_IMAGE_KHR) {
+ return BAD_VALUE;
+ }
+
+ // this binds the given EGLImage as a framebuffer for the
+ // duration of this scope.
+ RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image);
+ if (imageBond.getStatus() == NO_ERROR) {
+ // this will in fact render into our dequeued buffer
+ // via an FBO, which means we didn't have to create
+ // an EGLSurface and therefore we're not
+ // dependent on the context's EGLConfig.
+ renderScreenImplLocked(
+ hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
+ useIdentityTransform, rotation);
+
+ if (hasWideColorDisplay) {
+ native_window_set_buffers_data_space(window,
+ getRenderEngine().usesWideColor() ?
+ HAL_DATASPACE_DISPLAY_P3 : HAL_DATASPACE_V0_SRGB);
}
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+
+ // Attempt to create a sync khr object that can produce a sync point. If that
+ // isn't available, create a non-dupable sync object in the fallback path and
+ // wait on it directly.
+ EGLSyncKHR sync;
+ if (!DEBUG_SCREENSHOTS) {
+ sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+ // native fence fd will not be populated until flush() is done.
+ getRenderEngine().flush();
+ } else {
+ sync = EGL_NO_SYNC_KHR;
+ }
+ if (sync != EGL_NO_SYNC_KHR) {
+ // get the sync fd
+ syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
+ if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("captureScreen: failed to dup sync khr object");
+ syncFd = -1;
+ }
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ } else {
+ // fallback path
+ sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ if (sync != EGL_NO_SYNC_KHR) {
+ EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/);
+ EGLint eglErr = eglGetError();
+ if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGW("captureScreen: fence wait timed out");
+ } else {
+ ALOGW_IF(eglErr != EGL_SUCCESS,
+ "captureScreen: error waiting on EGL fence: %#x", eglErr);
+ }
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ } else {
+ ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError());
+ }
+ }
+ if (DEBUG_SCREENSHOTS) {
+ uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
+ getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
+ checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
+ hw, minLayerZ, maxLayerZ);
+ delete [] pixels;
+ }
+
+ } else {
+ ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
+ result = INVALID_OPERATION;
+ window->cancelBuffer(window, buffer, syncFd);
+ buffer = NULL;
+ }
+ // destroy our image
+ eglDestroyImageKHR(mEGLDisplay, image);
+
+ if (buffer) {
+ // queueBuffer takes ownership of syncFd
+ result = window->queueBuffer(window, buffer, syncFd);
}
return result;