Loading libs/vr/libdisplay/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ LOCAL_PATH := $(call my-dir) sourceFiles := \ native_window.cpp \ native_buffer_queue.cpp \ display_client.cpp \ display_manager_client.cpp \ Loading libs/vr/libdisplay/graphics.cpp +19 −0 Original line number Diff line number Diff line Loading @@ -483,6 +483,25 @@ extern "C" int dvrGetNativeDisplayDimensions(int* native_width, return 0; } extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height, int* format) { ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win); int w, h, f; nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w); nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h); nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f); if (width) *width = w; if (height) *height = h; if (format) *format = f; return 0; } struct DvrGraphicsContext : public android::ANativeObjectBase< ANativeWindow, DvrGraphicsContext, android::LightRefBase<DvrGraphicsContext>> { Loading libs/vr/libdisplay/include/dvr/graphics.h +40 −10 Original line number Diff line number Diff line Loading @@ -21,6 +21,11 @@ typedef struct float32x4x4_t { float32x4_t val[4]; }; __BEGIN_DECLS // Create a stereo surface that will be lens-warped by the system. EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width, int* display_height); EGLNativeWindowType dvrCreateDisplaySurface(void); // Display surface parameters used to specify display surface options. enum { DVR_SURFACE_PARAMETER_NONE = 0, Loading Loading @@ -158,16 +163,9 @@ struct DvrLensInfo { float right_fov[4]; }; int dvrGetNativeDisplayDimensions(int* native_width, int* native_height); // Opaque struct that represents a graphics context, the texture swap chain, // and surfaces. typedef struct DvrGraphicsContext DvrGraphicsContext; // Create the graphics context. with the given parameters. The list of // parameters is terminated with an entry where key == // DVR_SURFACE_PARAMETER_NONE. For example, the parameters array could be built // as follows: // Creates a display surface with the given parameters. The list of parameters // is terminated with an entry where key == DVR_SURFACE_PARAMETER_NONE. // For example, the parameters array could be built as follows: // int display_width = 0, display_height = 0; // int surface_width = 0, surface_height = 0; // float inter_lens_meters = 0.0f; Loading @@ -185,6 +183,38 @@ typedef struct DvrGraphicsContext DvrGraphicsContext; // DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov), // DVR_SURFACE_PARAMETER_LIST_END, // }; EGLNativeWindowType dvrCreateDisplaySurfaceExtended( struct DvrSurfaceParameter* parameters); int dvrGetNativeDisplayDimensions(int* native_width, int* native_height); int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height, int* format); // NOTE: Only call the functions below on windows created with the API above. // Sets the display surface visible based on the boolean evaluation of // |visible|. void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible); // Sets the application z-order of the display surface. Higher values display on // top of lower values. void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order); // Post the next buffer early. This allows the application to race with either // the async EDS process or the scanline for applications that are not using // system distortion. When this is called, the next buffer in the queue is // posted for display. It is up to the application to kick its GPU rendering // work in time. If the rendering is incomplete there will be significant, // undesirable tearing artifacts. // It is not recommended to use this feature with system distortion. void dvrDisplayPostEarly(EGLNativeWindowType window); // Opaque struct that represents a graphics context, the texture swap chain, // and surfaces. typedef struct DvrGraphicsContext DvrGraphicsContext; // Create the graphics context. int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters, DvrGraphicsContext** return_graphics_context); Loading libs/vr/libdisplay/native_window.cpp 0 → 100644 +458 −0 Original line number Diff line number Diff line #include <EGL/egl.h> #include <android/native_window.h> #include <cutils/native_handle.h> #include <errno.h> #include <pthread.h> #include <semaphore.h> #include <stdarg.h> #include <string.h> #include <sys/timerfd.h> #include <system/window.h> #include <time.h> #include <ui/ANativeObjectBase.h> #include <utils/Errors.h> #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Trace.h> #include <log/log.h> #include <memory> #include <mutex> #include <dvr/graphics.h> #include <private/dvr/clock_ns.h> #include <private/dvr/display_client.h> #include <private/dvr/native_buffer.h> #include <private/dvr/native_buffer_queue.h> namespace { constexpr int kDefaultDisplaySurfaceUsage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888; constexpr int kWarpedDisplaySurfaceFlags = 0; constexpr int kUnwarpedDisplaySurfaceFlags = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS | DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION | DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC; constexpr int kDefaultBufferCount = 4; } // anonymous namespace namespace android { namespace dvr { // NativeWindow is an implementation of ANativeWindow. This class interacts with // displayd through the DisplaySurfaceClient and NativeBufferQueue. class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow, LightRefBase<NativeWindow> > { public: explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface); void SetVisible(bool visible); void SetZOrder(int z_order); void PostEarly(); private: friend class LightRefBase<NativeWindow>; void Post(sp<NativeBufferProducer> buffer, int fence_fd); static int SetSwapInterval(ANativeWindow* window, int interval); static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fence_fd); static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd); static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd); static int Query(const ANativeWindow* window, int what, int* value); static int Perform(ANativeWindow* window, int operation, ...); static int DequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); static int CancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int QueueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int LockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); std::shared_ptr<DisplaySurfaceClient> surface_; std::mutex lock_; NativeBufferQueue buffer_queue_; sp<NativeBufferProducer> next_post_buffer_; bool next_buffer_already_posted_; NativeWindow(const NativeWindow&) = delete; void operator=(NativeWindow&) = delete; }; NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface) : surface_(surface), buffer_queue_(surface, kDefaultBufferCount), next_post_buffer_(nullptr), next_buffer_already_posted_(false) { ANativeWindow::setSwapInterval = SetSwapInterval; ANativeWindow::dequeueBuffer = DequeueBuffer; ANativeWindow::cancelBuffer = CancelBuffer; ANativeWindow::queueBuffer = QueueBuffer; ANativeWindow::query = Query; ANativeWindow::perform = Perform; ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED; ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED; ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED; ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED; } void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); } void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); } void NativeWindow::PostEarly() { ATRACE_NAME("NativeWindow::PostEarly"); ALOGI_IF(TRACE, "NativeWindow::PostEarly"); std::lock_guard<std::mutex> autolock(lock_); if (!next_buffer_already_posted_) { next_buffer_already_posted_ = true; if (!next_post_buffer_.get()) { next_post_buffer_ = buffer_queue_.Dequeue(); } ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id()); Post(next_post_buffer_, -1); } } void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) { ATRACE_NAME(__PRETTY_FUNCTION__); ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d", buffer->buffer()->id(), fence_fd); ALOGW_IF(!surface_->visible(), "NativeWindow::Post: Posting buffer on invisible surface!!!"); buffer->Post(fence_fd, 0); } int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) { ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval); return 0; } int NativeWindow::DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fence_fd) { ATRACE_NAME(__PRETTY_FUNCTION__); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); if (!self->next_post_buffer_.get()) { self->next_post_buffer_ = self->buffer_queue_.Dequeue(); } ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id()); *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release(); *buffer = self->next_post_buffer_.get(); ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd); return 0; } int NativeWindow::QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd) { ATRACE_NAME("NativeWindow::QueueBuffer"); ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); NativeBufferProducer* native_buffer = static_cast<NativeBufferProducer*>(buffer); ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id()); bool do_post = true; if (self->next_buffer_already_posted_) { // Check that the buffer is the one we expect, but handle it if this happens // in production by allowing this buffer to post on top of the previous one. LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get()); if (native_buffer == self->next_post_buffer_.get()) { do_post = false; if (fence_fd >= 0) close(fence_fd); } } if (do_post) { ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id()); self->Post(native_buffer, fence_fd); } self->next_buffer_already_posted_ = false; self->next_post_buffer_ = nullptr; return NO_ERROR; } int NativeWindow::CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd) { ATRACE_NAME("NativeWindow::CancelBuffer"); ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); NativeBufferProducer* native_buffer = static_cast<NativeBufferProducer*>(buffer); ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id()); ATRACE_INT("CancelBuffer", native_buffer->buffer()->id()); bool do_enqueue = true; if (self->next_buffer_already_posted_) { // Check that the buffer is the one we expect, but handle it if this happens // in production by returning this buffer to the buffer queue. LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get()); if (native_buffer == self->next_post_buffer_.get()) { do_enqueue = false; } } if (do_enqueue) { self->buffer_queue_.Enqueue(native_buffer); } if (fence_fd >= 0) close(fence_fd); self->next_buffer_already_posted_ = false; self->next_post_buffer_ = nullptr; return NO_ERROR; } int NativeWindow::Query(const ANativeWindow* window, int what, int* value) { NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window)); std::lock_guard<std::mutex> autolock(self->lock_); switch (what) { case NATIVE_WINDOW_WIDTH: *value = self->surface_->width(); return NO_ERROR; case NATIVE_WINDOW_HEIGHT: *value = self->surface_->height(); return NO_ERROR; case NATIVE_WINDOW_FORMAT: *value = self->surface_->format(); return NO_ERROR; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = 1; return NO_ERROR; case NATIVE_WINDOW_CONCRETE_TYPE: *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: *value = 1; return NO_ERROR; case NATIVE_WINDOW_DEFAULT_WIDTH: *value = self->surface_->width(); return NO_ERROR; case NATIVE_WINDOW_DEFAULT_HEIGHT: *value = self->surface_->height(); return NO_ERROR; case NATIVE_WINDOW_TRANSFORM_HINT: *value = 0; return NO_ERROR; } *value = 0; return BAD_VALUE; } int NativeWindow::Perform(ANativeWindow* window, int operation, ...) { NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); va_list args; va_start(args, operation); // TODO(eieio): The following operations are not used at this time. They are // included here to help document which operations may be useful and what // parameters they take. switch (operation) { case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: { int w = va_arg(args, int); int h = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_FORMAT: { int format = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: { int transform = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d", transform); return NO_ERROR; } case NATIVE_WINDOW_SET_USAGE: { int usage = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage); return NO_ERROR; } case NATIVE_WINDOW_CONNECT: case NATIVE_WINDOW_DISCONNECT: case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: case NATIVE_WINDOW_API_CONNECT: case NATIVE_WINDOW_API_DISCONNECT: // TODO(eieio): we should implement these return NO_ERROR; case NATIVE_WINDOW_SET_BUFFER_COUNT: { int buffer_count = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d", buffer_count); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: { android_dataspace_t data_space = static_cast<android_dataspace_t>(va_arg(args, int)); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d", data_space); return NO_ERROR; } case NATIVE_WINDOW_SET_SCALING_MODE: { int mode = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode); return NO_ERROR; } case NATIVE_WINDOW_LOCK: case NATIVE_WINDOW_UNLOCK_AND_POST: case NATIVE_WINDOW_SET_CROP: case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: return INVALID_OPERATION; } return NAME_NOT_FOUND; } int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer) { int fence_fd = -1; int ret = DequeueBuffer(window, buffer, &fence_fd); // wait for fence if (ret == NO_ERROR && fence_fd != -1) close(fence_fd); return ret; } int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { return CancelBuffer(window, buffer, -1); } int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { return QueueBuffer(window, buffer, -1); } int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/, ANativeWindowBuffer* /*buffer*/) { return NO_ERROR; } } // namespace dvr } // namespace android static EGLNativeWindowType CreateDisplaySurface(int* display_width, int* display_height, int format, int usage, int flags) { auto client = android::dvr::DisplayClient::Create(); if (!client) { ALOGE("Failed to create display client!"); return nullptr; } // TODO(eieio,jbates): Consider passing flags and other parameters to get // metrics based on specific surface requirements. android::dvr::SystemDisplayMetrics metrics; const int ret = client->GetDisplayMetrics(&metrics); if (ret < 0) { ALOGE("Failed to get display metrics: %s", strerror(-ret)); return nullptr; } int width, height; if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) { width = metrics.display_native_width; height = metrics.display_native_height; } else { width = metrics.distorted_width; height = metrics.distorted_height; } std::shared_ptr<android::dvr::DisplaySurfaceClient> surface = client->CreateDisplaySurface(width, height, format, usage, flags); if (display_width) *display_width = metrics.display_native_width; if (display_height) *display_height = metrics.display_native_height; // Set the surface visible by default. // TODO(eieio,jbates): Remove this from here and set visible somewhere closer // to the application to account for situations where the application wants to // create surfaces that will be used later or shouldn't be visible yet. surface->SetVisible(true); return new android::dvr::NativeWindow(surface); } std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient( struct DvrSurfaceParameter* parameters, /*out*/ android::dvr::SystemDisplayMetrics* metrics); extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended( struct DvrSurfaceParameter* parameters) { android::dvr::SystemDisplayMetrics metrics; auto surface = CreateDisplaySurfaceClient(parameters, &metrics); if (!surface) { ALOGE("Failed to create display surface client"); return nullptr; } return new android::dvr::NativeWindow(surface); } extern "C" EGLNativeWindowType dvrCreateDisplaySurface() { return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat, kDefaultDisplaySurfaceUsage, kUnwarpedDisplaySurfaceFlags); } extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface( int* display_width, int* display_height) { return CreateDisplaySurface( display_width, display_height, kDefaultDisplaySurfaceFormat, kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags); } extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->SetVisible(visible); } extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->SetZOrder(z_order); } extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->PostEarly(); } libs/vr/libdisplay/native_window_test.cpp 0 → 100644 +43 −0 Original line number Diff line number Diff line #include <iostream> #include <memory> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <dvr/graphics.h> #include <private/dvr/display_client.h> #include <cpp_free_mock/cpp_free_mock.h> // Checks querying the VSync of the device on display surface creation. TEST(CreateDisplaySurface, QueryVSyncPeriod) { using ::testing::_; const uint64_t kExpectedVSync = 123456; // We only care about the expected VSync value android::dvr::DisplayMetrics metrics; metrics.vsync_period_ns = kExpectedVSync; uint64_t outPeriod; DvrSurfaceParameter display_params[] = { DVR_SURFACE_PARAMETER_IN(WIDTH, 256), DVR_SURFACE_PARAMETER_IN(HEIGHT, 256), DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod), DVR_SURFACE_PARAMETER_LIST_END, }; // inject the mocking code to the target method auto mocked_function = MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics); // instrument the mock function to return our custom metrics EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _)) .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics), ::testing::Return(0))); ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params)); EXPECT_EQ(kExpectedVSync, outPeriod); } Loading
libs/vr/libdisplay/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ LOCAL_PATH := $(call my-dir) sourceFiles := \ native_window.cpp \ native_buffer_queue.cpp \ display_client.cpp \ display_manager_client.cpp \ Loading
libs/vr/libdisplay/graphics.cpp +19 −0 Original line number Diff line number Diff line Loading @@ -483,6 +483,25 @@ extern "C" int dvrGetNativeDisplayDimensions(int* native_width, return 0; } extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height, int* format) { ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win); int w, h, f; nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w); nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h); nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f); if (width) *width = w; if (height) *height = h; if (format) *format = f; return 0; } struct DvrGraphicsContext : public android::ANativeObjectBase< ANativeWindow, DvrGraphicsContext, android::LightRefBase<DvrGraphicsContext>> { Loading
libs/vr/libdisplay/include/dvr/graphics.h +40 −10 Original line number Diff line number Diff line Loading @@ -21,6 +21,11 @@ typedef struct float32x4x4_t { float32x4_t val[4]; }; __BEGIN_DECLS // Create a stereo surface that will be lens-warped by the system. EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width, int* display_height); EGLNativeWindowType dvrCreateDisplaySurface(void); // Display surface parameters used to specify display surface options. enum { DVR_SURFACE_PARAMETER_NONE = 0, Loading Loading @@ -158,16 +163,9 @@ struct DvrLensInfo { float right_fov[4]; }; int dvrGetNativeDisplayDimensions(int* native_width, int* native_height); // Opaque struct that represents a graphics context, the texture swap chain, // and surfaces. typedef struct DvrGraphicsContext DvrGraphicsContext; // Create the graphics context. with the given parameters. The list of // parameters is terminated with an entry where key == // DVR_SURFACE_PARAMETER_NONE. For example, the parameters array could be built // as follows: // Creates a display surface with the given parameters. The list of parameters // is terminated with an entry where key == DVR_SURFACE_PARAMETER_NONE. // For example, the parameters array could be built as follows: // int display_width = 0, display_height = 0; // int surface_width = 0, surface_height = 0; // float inter_lens_meters = 0.0f; Loading @@ -185,6 +183,38 @@ typedef struct DvrGraphicsContext DvrGraphicsContext; // DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov), // DVR_SURFACE_PARAMETER_LIST_END, // }; EGLNativeWindowType dvrCreateDisplaySurfaceExtended( struct DvrSurfaceParameter* parameters); int dvrGetNativeDisplayDimensions(int* native_width, int* native_height); int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height, int* format); // NOTE: Only call the functions below on windows created with the API above. // Sets the display surface visible based on the boolean evaluation of // |visible|. void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible); // Sets the application z-order of the display surface. Higher values display on // top of lower values. void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order); // Post the next buffer early. This allows the application to race with either // the async EDS process or the scanline for applications that are not using // system distortion. When this is called, the next buffer in the queue is // posted for display. It is up to the application to kick its GPU rendering // work in time. If the rendering is incomplete there will be significant, // undesirable tearing artifacts. // It is not recommended to use this feature with system distortion. void dvrDisplayPostEarly(EGLNativeWindowType window); // Opaque struct that represents a graphics context, the texture swap chain, // and surfaces. typedef struct DvrGraphicsContext DvrGraphicsContext; // Create the graphics context. int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters, DvrGraphicsContext** return_graphics_context); Loading
libs/vr/libdisplay/native_window.cpp 0 → 100644 +458 −0 Original line number Diff line number Diff line #include <EGL/egl.h> #include <android/native_window.h> #include <cutils/native_handle.h> #include <errno.h> #include <pthread.h> #include <semaphore.h> #include <stdarg.h> #include <string.h> #include <sys/timerfd.h> #include <system/window.h> #include <time.h> #include <ui/ANativeObjectBase.h> #include <utils/Errors.h> #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Trace.h> #include <log/log.h> #include <memory> #include <mutex> #include <dvr/graphics.h> #include <private/dvr/clock_ns.h> #include <private/dvr/display_client.h> #include <private/dvr/native_buffer.h> #include <private/dvr/native_buffer_queue.h> namespace { constexpr int kDefaultDisplaySurfaceUsage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888; constexpr int kWarpedDisplaySurfaceFlags = 0; constexpr int kUnwarpedDisplaySurfaceFlags = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS | DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION | DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC; constexpr int kDefaultBufferCount = 4; } // anonymous namespace namespace android { namespace dvr { // NativeWindow is an implementation of ANativeWindow. This class interacts with // displayd through the DisplaySurfaceClient and NativeBufferQueue. class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow, LightRefBase<NativeWindow> > { public: explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface); void SetVisible(bool visible); void SetZOrder(int z_order); void PostEarly(); private: friend class LightRefBase<NativeWindow>; void Post(sp<NativeBufferProducer> buffer, int fence_fd); static int SetSwapInterval(ANativeWindow* window, int interval); static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fence_fd); static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd); static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd); static int Query(const ANativeWindow* window, int what, int* value); static int Perform(ANativeWindow* window, int operation, ...); static int DequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); static int CancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int QueueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int LockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); std::shared_ptr<DisplaySurfaceClient> surface_; std::mutex lock_; NativeBufferQueue buffer_queue_; sp<NativeBufferProducer> next_post_buffer_; bool next_buffer_already_posted_; NativeWindow(const NativeWindow&) = delete; void operator=(NativeWindow&) = delete; }; NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface) : surface_(surface), buffer_queue_(surface, kDefaultBufferCount), next_post_buffer_(nullptr), next_buffer_already_posted_(false) { ANativeWindow::setSwapInterval = SetSwapInterval; ANativeWindow::dequeueBuffer = DequeueBuffer; ANativeWindow::cancelBuffer = CancelBuffer; ANativeWindow::queueBuffer = QueueBuffer; ANativeWindow::query = Query; ANativeWindow::perform = Perform; ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED; ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED; ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED; ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED; } void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); } void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); } void NativeWindow::PostEarly() { ATRACE_NAME("NativeWindow::PostEarly"); ALOGI_IF(TRACE, "NativeWindow::PostEarly"); std::lock_guard<std::mutex> autolock(lock_); if (!next_buffer_already_posted_) { next_buffer_already_posted_ = true; if (!next_post_buffer_.get()) { next_post_buffer_ = buffer_queue_.Dequeue(); } ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id()); Post(next_post_buffer_, -1); } } void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) { ATRACE_NAME(__PRETTY_FUNCTION__); ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d", buffer->buffer()->id(), fence_fd); ALOGW_IF(!surface_->visible(), "NativeWindow::Post: Posting buffer on invisible surface!!!"); buffer->Post(fence_fd, 0); } int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) { ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval); return 0; } int NativeWindow::DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fence_fd) { ATRACE_NAME(__PRETTY_FUNCTION__); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); if (!self->next_post_buffer_.get()) { self->next_post_buffer_ = self->buffer_queue_.Dequeue(); } ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id()); *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release(); *buffer = self->next_post_buffer_.get(); ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd); return 0; } int NativeWindow::QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd) { ATRACE_NAME("NativeWindow::QueueBuffer"); ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); NativeBufferProducer* native_buffer = static_cast<NativeBufferProducer*>(buffer); ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id()); bool do_post = true; if (self->next_buffer_already_posted_) { // Check that the buffer is the one we expect, but handle it if this happens // in production by allowing this buffer to post on top of the previous one. LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get()); if (native_buffer == self->next_post_buffer_.get()) { do_post = false; if (fence_fd >= 0) close(fence_fd); } } if (do_post) { ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id()); self->Post(native_buffer, fence_fd); } self->next_buffer_already_posted_ = false; self->next_post_buffer_ = nullptr; return NO_ERROR; } int NativeWindow::CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fence_fd) { ATRACE_NAME("NativeWindow::CancelBuffer"); ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd); NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); NativeBufferProducer* native_buffer = static_cast<NativeBufferProducer*>(buffer); ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id()); ATRACE_INT("CancelBuffer", native_buffer->buffer()->id()); bool do_enqueue = true; if (self->next_buffer_already_posted_) { // Check that the buffer is the one we expect, but handle it if this happens // in production by returning this buffer to the buffer queue. LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get()); if (native_buffer == self->next_post_buffer_.get()) { do_enqueue = false; } } if (do_enqueue) { self->buffer_queue_.Enqueue(native_buffer); } if (fence_fd >= 0) close(fence_fd); self->next_buffer_already_posted_ = false; self->next_post_buffer_ = nullptr; return NO_ERROR; } int NativeWindow::Query(const ANativeWindow* window, int what, int* value) { NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window)); std::lock_guard<std::mutex> autolock(self->lock_); switch (what) { case NATIVE_WINDOW_WIDTH: *value = self->surface_->width(); return NO_ERROR; case NATIVE_WINDOW_HEIGHT: *value = self->surface_->height(); return NO_ERROR; case NATIVE_WINDOW_FORMAT: *value = self->surface_->format(); return NO_ERROR; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = 1; return NO_ERROR; case NATIVE_WINDOW_CONCRETE_TYPE: *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: *value = 1; return NO_ERROR; case NATIVE_WINDOW_DEFAULT_WIDTH: *value = self->surface_->width(); return NO_ERROR; case NATIVE_WINDOW_DEFAULT_HEIGHT: *value = self->surface_->height(); return NO_ERROR; case NATIVE_WINDOW_TRANSFORM_HINT: *value = 0; return NO_ERROR; } *value = 0; return BAD_VALUE; } int NativeWindow::Perform(ANativeWindow* window, int operation, ...) { NativeWindow* self = getSelf(window); std::lock_guard<std::mutex> autolock(self->lock_); va_list args; va_start(args, operation); // TODO(eieio): The following operations are not used at this time. They are // included here to help document which operations may be useful and what // parameters they take. switch (operation) { case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: { int w = va_arg(args, int); int h = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_FORMAT: { int format = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: { int transform = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d", transform); return NO_ERROR; } case NATIVE_WINDOW_SET_USAGE: { int usage = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage); return NO_ERROR; } case NATIVE_WINDOW_CONNECT: case NATIVE_WINDOW_DISCONNECT: case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: case NATIVE_WINDOW_API_CONNECT: case NATIVE_WINDOW_API_DISCONNECT: // TODO(eieio): we should implement these return NO_ERROR; case NATIVE_WINDOW_SET_BUFFER_COUNT: { int buffer_count = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d", buffer_count); return NO_ERROR; } case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: { android_dataspace_t data_space = static_cast<android_dataspace_t>(va_arg(args, int)); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d", data_space); return NO_ERROR; } case NATIVE_WINDOW_SET_SCALING_MODE: { int mode = va_arg(args, int); ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode); return NO_ERROR; } case NATIVE_WINDOW_LOCK: case NATIVE_WINDOW_UNLOCK_AND_POST: case NATIVE_WINDOW_SET_CROP: case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: return INVALID_OPERATION; } return NAME_NOT_FOUND; } int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer) { int fence_fd = -1; int ret = DequeueBuffer(window, buffer, &fence_fd); // wait for fence if (ret == NO_ERROR && fence_fd != -1) close(fence_fd); return ret; } int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { return CancelBuffer(window, buffer, -1); } int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { return QueueBuffer(window, buffer, -1); } int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/, ANativeWindowBuffer* /*buffer*/) { return NO_ERROR; } } // namespace dvr } // namespace android static EGLNativeWindowType CreateDisplaySurface(int* display_width, int* display_height, int format, int usage, int flags) { auto client = android::dvr::DisplayClient::Create(); if (!client) { ALOGE("Failed to create display client!"); return nullptr; } // TODO(eieio,jbates): Consider passing flags and other parameters to get // metrics based on specific surface requirements. android::dvr::SystemDisplayMetrics metrics; const int ret = client->GetDisplayMetrics(&metrics); if (ret < 0) { ALOGE("Failed to get display metrics: %s", strerror(-ret)); return nullptr; } int width, height; if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) { width = metrics.display_native_width; height = metrics.display_native_height; } else { width = metrics.distorted_width; height = metrics.distorted_height; } std::shared_ptr<android::dvr::DisplaySurfaceClient> surface = client->CreateDisplaySurface(width, height, format, usage, flags); if (display_width) *display_width = metrics.display_native_width; if (display_height) *display_height = metrics.display_native_height; // Set the surface visible by default. // TODO(eieio,jbates): Remove this from here and set visible somewhere closer // to the application to account for situations where the application wants to // create surfaces that will be used later or shouldn't be visible yet. surface->SetVisible(true); return new android::dvr::NativeWindow(surface); } std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient( struct DvrSurfaceParameter* parameters, /*out*/ android::dvr::SystemDisplayMetrics* metrics); extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended( struct DvrSurfaceParameter* parameters) { android::dvr::SystemDisplayMetrics metrics; auto surface = CreateDisplaySurfaceClient(parameters, &metrics); if (!surface) { ALOGE("Failed to create display surface client"); return nullptr; } return new android::dvr::NativeWindow(surface); } extern "C" EGLNativeWindowType dvrCreateDisplaySurface() { return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat, kDefaultDisplaySurfaceUsage, kUnwarpedDisplaySurfaceFlags); } extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface( int* display_width, int* display_height) { return CreateDisplaySurface( display_width, display_height, kDefaultDisplaySurfaceFormat, kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags); } extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->SetVisible(visible); } extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->SetZOrder(z_order); } extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) { auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window); native_window->PostEarly(); }
libs/vr/libdisplay/native_window_test.cpp 0 → 100644 +43 −0 Original line number Diff line number Diff line #include <iostream> #include <memory> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <dvr/graphics.h> #include <private/dvr/display_client.h> #include <cpp_free_mock/cpp_free_mock.h> // Checks querying the VSync of the device on display surface creation. TEST(CreateDisplaySurface, QueryVSyncPeriod) { using ::testing::_; const uint64_t kExpectedVSync = 123456; // We only care about the expected VSync value android::dvr::DisplayMetrics metrics; metrics.vsync_period_ns = kExpectedVSync; uint64_t outPeriod; DvrSurfaceParameter display_params[] = { DVR_SURFACE_PARAMETER_IN(WIDTH, 256), DVR_SURFACE_PARAMETER_IN(HEIGHT, 256), DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod), DVR_SURFACE_PARAMETER_LIST_END, }; // inject the mocking code to the target method auto mocked_function = MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics); // instrument the mock function to return our custom metrics EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _)) .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics), ::testing::Return(0))); ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params)); EXPECT_EQ(kExpectedVSync, outPeriod); }