Index: ui/gfx/ozone/impl/software_surface_factory_ozone.cc |
diff --git a/ui/gfx/ozone/impl/software_surface_factory_ozone.cc b/ui/gfx/ozone/impl/software_surface_factory_ozone.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..12dd45cd15b115861ca95fc10815ed6d39459463 |
--- /dev/null |
+++ b/ui/gfx/ozone/impl/software_surface_factory_ozone.cc |
@@ -0,0 +1,285 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/gfx/ozone/impl/software_surface_factory_ozone.h" |
+ |
+#include <drm.h> |
+#include <errno.h> |
+#include <xf86drm.h> |
+ |
+#include "base/message_loop/message_loop.h" |
+#include "third_party/skia/include/core/SkBitmap.h" |
+#include "third_party/skia/include/core/SkDevice.h" |
+#include "ui/gfx/native_widget_types.h" |
+#include "ui/gfx/ozone/impl/drm_skbitmap_ozone.h" |
+#include "ui/gfx/ozone/impl/drm_wrapper_ozone.h" |
+#include "ui/gfx/ozone/impl/hardware_display_controller_ozone.h" |
+#include "ui/gfx/ozone/impl/software_surface_ozone.h" |
+ |
+namespace gfx { |
+ |
+namespace { |
+ |
+const char kDefaultGraphicsCardPath[] = "/dev/dri/card0"; |
+ |
+const gfx::AcceleratedWidget kDefaultWidgetHandle = 1; |
+ |
+// DRM callback on page flip events. This callback is triggered after the |
+// page flip has happened and the backbuffer is now the new frontbuffer |
+// The old frontbuffer is no longer used by the hardware and can be used for |
+// future draw operations. |
+// |
+// |device| will contain a reference to the |SoftwareSurfaceOzone| object which |
+// the event belongs to. |
+// |
+// TODO(dnicoara) When we have a FD handler for the DRM calls in the message |
+// loop, we can move this function in the handler. |
+void HandlePageFlipEvent(int fd, |
+ unsigned int frame, |
+ unsigned int seconds, |
+ unsigned int useconds, |
+ void* controller) { |
+ static_cast<HardwareDisplayControllerOzone*>(controller)->get_surface() |
+ ->SwapBuffers(); |
+} |
+ |
+uint32_t GetCrtc(int fd, drmModeRes* resources, drmModeConnector* connector) { |
+ // If the connector already has an encoder try to re-use. |
+ if (connector->encoder_id) { |
+ drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoder_id); |
+ if (encoder) { |
+ if (encoder->crtc_id) { |
+ uint32_t crtc = encoder->crtc_id; |
+ drmModeFreeEncoder(encoder); |
+ return crtc; |
+ } |
+ drmModeFreeEncoder(encoder); |
+ } |
+ } |
+ |
+ // Try to find an encoder for the connector. |
+ for (int i = 0; i < connector->count_encoders; ++i) { |
+ drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoders[i]); |
+ if (!encoder) |
+ continue; |
+ |
+ for (int j = 0; j < resources->count_crtcs; ++j) { |
+ // Check if the encoder is compatible with this CRTC |
+ if (!(encoder->possible_crtcs & (1 << j))) |
+ continue; |
+ |
+ drmModeFreeEncoder(encoder); |
+ return resources->crtcs[j]; |
+ } |
+ } |
+ |
+ return 0; |
+} |
+ |
+} // namespace |
+ |
+SoftwareSurfaceFactoryOzone::SoftwareSurfaceFactoryOzone() |
+ : drm_(), |
+ state_(UNINITIALIZED), |
+ controller_() { |
+} |
+ |
+SoftwareSurfaceFactoryOzone::~SoftwareSurfaceFactoryOzone() { |
+ if (state_ == INITIALIZED) |
+ ShutdownHardware(); |
+} |
+ |
+SurfaceFactoryOzone::HardwareState |
+SoftwareSurfaceFactoryOzone::InitializeHardware() { |
+ CHECK(state_ == UNINITIALIZED); |
+ |
+ // TODO(dnicoara): Short-cut right now. What we want is to look at all the |
+ // graphics devices available and select the primary one. |
+ drm_.reset(CreateWrapper()); |
+ if (drm_->get_fd() < 0) { |
+ LOG(ERROR) << "Cannot open graphics card '" |
+ << kDefaultGraphicsCardPath << "': " << strerror(errno); |
+ state_ = FAILED; |
+ return state_; |
+ } |
+ |
+ state_ = INITIALIZED; |
+ return state_; |
+} |
+ |
+void SoftwareSurfaceFactoryOzone::ShutdownHardware() { |
+ CHECK(state_ == INITIALIZED); |
+ |
+ controller_.reset(); |
+ drm_.reset(); |
+ |
+ state_ = UNINITIALIZED; |
+} |
+ |
+gfx::AcceleratedWidget SoftwareSurfaceFactoryOzone::GetAcceleratedWidget() { |
+ CHECK(state_ != FAILED); |
+ |
+ // TODO(dnicoara) When there's more information on which display we want, |
+ // then we can return the widget associated with the display. |
+ // For now just assume we have 1 display device and return it. |
+ if (!controller_.get()) |
+ controller_.reset(new HardwareDisplayControllerOzone()); |
+ |
+ // TODO(dnicoara) We only have 1 display for now, so only 1 AcceleratedWidget. |
+ // When we'll support multiple displays this needs to be changed to return a |
+ // different handle for every display. |
+ return kDefaultWidgetHandle; |
+} |
+ |
+gfx::AcceleratedWidget SoftwareSurfaceFactoryOzone::RealizeAcceleratedWidget( |
+ gfx::AcceleratedWidget w) { |
+ CHECK(state_ == INITIALIZED); |
+ // TODO(dnicoara) Once we can handle multiple displays this needs to be |
+ // changed. |
+ CHECK(w == kDefaultWidgetHandle); |
+ |
+ CHECK(controller_->get_state() == |
+ HardwareDisplayControllerOzone::UNASSOCIATED); |
+ |
+ // Until now the controller is just a stub. Initializing it will link it to a |
+ // hardware display. |
+ if (!InitializeControllerForPrimaryDisplay(drm_.get(), controller_.get())) { |
+ LOG(ERROR) << "Failed to initialize controller"; |
+ return gfx::kNullAcceleratedWidget; |
+ } |
+ |
+ // Create a surface suitable for the current controller. |
+ scoped_ptr<SoftwareSurfaceOzone> surface(CreateSurface(controller_.get())); |
+ |
+ if (!surface->Initialize()) { |
+ LOG(ERROR) << "Failed to initialize surface"; |
+ return gfx::kNullAcceleratedWidget; |
+ } |
+ |
+ // Bind the surface to the controller. This will register the backing buffers |
+ // with the hardware CRTC such that we can show the buffers. The controller |
+ // takes ownership of the surface. |
+ if (!controller_->BindSurfaceToController(surface.Pass())) { |
+ LOG(ERROR) << "Failed to bind surface to controller"; |
+ return gfx::kNullAcceleratedWidget; |
+ } |
+ |
+ return reinterpret_cast<gfx::AcceleratedWidget>( |
+ controller_->get_surface()->GetDrawableForWidget()); |
+} |
+ |
+bool SoftwareSurfaceFactoryOzone::LoadEGLGLES2Bindings() { |
+ return false; |
+} |
+ |
+bool SoftwareSurfaceFactoryOzone::AttemptToResizeAcceleratedWidget( |
+ gfx::AcceleratedWidget w, |
+ const gfx::Rect& bounds) { |
+ return false; |
+} |
+ |
+bool SoftwareSurfaceFactoryOzone::SchedulePageFlip(gfx::AcceleratedWidget w) { |
+ CHECK(state_ == INITIALIZED); |
+ // TODO(dnicoara) Change this CHECK once we're running with the threaded |
+ // compositor. |
+ CHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); |
+ |
+ // TODO(dnicoara) Once we can handle multiple displays this needs to be |
+ // changed. |
+ CHECK(w == kDefaultWidgetHandle); |
+ |
+ if (!controller_->SchedulePageFlip()) |
+ return false; |
+ |
+ // Only wait for the page flip event to finish if it was properly scheduled. |
+ // |
+ // TODO(dnicoara) The following call will wait for the page flip event to |
+ // complete. This means that it will block until the next VSync. Ideally the |
+ // wait should happen in the message loop. The message loop would then |
+ // schedule the next draw event. Alternatively, the VSyncProvider could be |
+ // used to schedule the next draw. Unfortunately, at this point, |
+ // SoftwareOutputDevice does not provide any means to use any of the above |
+ // solutions. Note that if the DRM callback does not schedule the next draw, |
+ // then some sort of synchronization needs to take place since starting a new |
+ // draw before the page flip happened is considered an error. However we can |
+ // not use any lock constructs unless we're using the threaded compositor. |
+ // Note that the following call does not use any locks, so it is safe to be |
+ // made on the UI thread (thought not ideal). |
+ WaitForPageFlipEvent(drm_->get_fd()); |
+ |
+ return true; |
+} |
+ |
+gfx::VSyncProvider* SoftwareSurfaceFactoryOzone::GetVSyncProvider( |
+ gfx::AcceleratedWidget w) { |
+ return NULL; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// SoftwareSurfaceFactoryOzone private |
+ |
+SoftwareSurfaceOzone* SoftwareSurfaceFactoryOzone::CreateSurface( |
+ HardwareDisplayControllerOzone* controller) { |
+ return new SoftwareSurfaceOzone(controller); |
+} |
+ |
+DrmWrapperOzone* SoftwareSurfaceFactoryOzone::CreateWrapper() { |
+ return new DrmWrapperOzone(kDefaultGraphicsCardPath); |
+} |
+ |
+bool SoftwareSurfaceFactoryOzone::InitializeControllerForPrimaryDisplay( |
+ DrmWrapperOzone* drm, |
+ HardwareDisplayControllerOzone* controller) { |
+ CHECK(state_ == SurfaceFactoryOzone::INITIALIZED); |
+ |
+ drmModeRes* resources = drmModeGetResources(drm->get_fd()); |
+ |
+ // Search for an active connector. |
+ for (int i = 0; i < resources->count_connectors; ++i) { |
+ drmModeConnector* connector = drmModeGetConnector( |
+ drm->get_fd(), |
+ resources->connectors[i]); |
+ |
+ if (!connector) |
+ continue; |
+ |
+ if (connector->connection != DRM_MODE_CONNECTED || |
+ connector->count_modes == 0) { |
+ drmModeFreeConnector(connector); |
+ continue; |
+ } |
+ |
+ uint32_t crtc = GetCrtc(drm->get_fd(), resources, connector); |
+ |
+ if (!crtc) |
+ continue; |
+ |
+ // TODO(dnicoara) Select one mode for now. In the future we may need to |
+ // save all the modes and allow the user to choose a specific mode. Or |
+ // even some fullscreen applications may need to change the mode. |
+ controller->SetControllerInfo( |
+ drm, |
+ connector->connector_id, |
+ crtc, |
+ connector->modes[0]); |
+ |
+ drmModeFreeConnector(connector); |
+ |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+void SoftwareSurfaceFactoryOzone::WaitForPageFlipEvent(int fd) { |
+ drmEventContext drm_event; |
+ drm_event.version = DRM_EVENT_CONTEXT_VERSION; |
+ drm_event.page_flip_handler = HandlePageFlipEvent; |
+ drm_event.vblank_handler = NULL; |
+ |
+ // Wait for the page-flip to complete. |
+ drmHandleEvent(fd, &drm_event); |
+} |
+ |
+} // namespace gfx |