Home | History | Annotate | Download | only in dri
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/gfx/ozone/dri/dri_surface_factory.h"
      6 
      7 #include <drm.h>
      8 #include <errno.h>
      9 #include <xf86drm.h>
     10 
     11 #include "base/message_loop/message_loop.h"
     12 #include "third_party/skia/include/core/SkBitmap.h"
     13 #include "third_party/skia/include/core/SkDevice.h"
     14 #include "ui/gfx/native_widget_types.h"
     15 #include "ui/gfx/ozone/dri/dri_skbitmap.h"
     16 #include "ui/gfx/ozone/dri/dri_surface.h"
     17 #include "ui/gfx/ozone/dri/dri_vsync_provider.h"
     18 #include "ui/gfx/ozone/dri/dri_wrapper.h"
     19 #include "ui/gfx/ozone/dri/hardware_display_controller.h"
     20 
     21 namespace gfx {
     22 
     23 namespace {
     24 
     25 const char kDefaultGraphicsCardPath[] = "/dev/dri/card0";
     26 const char kDPMSProperty[] = "DPMS";
     27 
     28 const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
     29 
     30 // DRM callback on page flip events. This callback is triggered after the
     31 // page flip has happened and the backbuffer is now the new frontbuffer
     32 // The old frontbuffer is no longer used by the hardware and can be used for
     33 // future draw operations.
     34 //
     35 // |device| will contain a reference to the |DriSurface| object which
     36 // the event belongs to.
     37 //
     38 // TODO(dnicoara) When we have a FD handler for the DRM calls in the message
     39 // loop, we can move this function in the handler.
     40 void HandlePageFlipEvent(int fd,
     41                          unsigned int frame,
     42                          unsigned int seconds,
     43                          unsigned int useconds,
     44                          void* controller) {
     45   static_cast<HardwareDisplayController*>(controller)
     46       ->OnPageFlipEvent(frame, seconds, useconds);
     47 }
     48 
     49 uint32_t GetDriProperty(int fd, drmModeConnector* connector, const char* name) {
     50   for (int i = 0; i < connector->count_props; ++i) {
     51     drmModePropertyPtr property = drmModeGetProperty(fd, connector->props[i]);
     52     if (!property)
     53       continue;
     54 
     55     if (strcmp(property->name, name) == 0) {
     56       uint32_t id = property->prop_id;
     57       drmModeFreeProperty(property);
     58       return id;
     59     }
     60 
     61     drmModeFreeProperty(property);
     62   }
     63   return 0;
     64 }
     65 
     66 uint32_t GetCrtc(int fd, drmModeRes* resources, drmModeConnector* connector) {
     67   // If the connector already has an encoder try to re-use.
     68   if (connector->encoder_id) {
     69     drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoder_id);
     70     if (encoder) {
     71       if (encoder->crtc_id) {
     72         uint32_t crtc = encoder->crtc_id;
     73         drmModeFreeEncoder(encoder);
     74         return crtc;
     75       }
     76       drmModeFreeEncoder(encoder);
     77     }
     78   }
     79 
     80   // Try to find an encoder for the connector.
     81   for (int i = 0; i < connector->count_encoders; ++i) {
     82     drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoders[i]);
     83     if (!encoder)
     84       continue;
     85 
     86     for (int j = 0; j < resources->count_crtcs; ++j) {
     87       // Check if the encoder is compatible with this CRTC
     88       if (!(encoder->possible_crtcs & (1 << j)))
     89         continue;
     90 
     91       drmModeFreeEncoder(encoder);
     92       return resources->crtcs[j];
     93     }
     94   }
     95 
     96   return 0;
     97 }
     98 
     99 }  // namespace
    100 
    101 DriSurfaceFactory::DriSurfaceFactory()
    102     : drm_(),
    103       state_(UNINITIALIZED),
    104       controller_() {
    105 }
    106 
    107 DriSurfaceFactory::~DriSurfaceFactory() {
    108   if (state_ == INITIALIZED)
    109     ShutdownHardware();
    110 }
    111 
    112 SurfaceFactoryOzone::HardwareState
    113 DriSurfaceFactory::InitializeHardware() {
    114   CHECK(state_ == UNINITIALIZED);
    115 
    116   // TODO(dnicoara): Short-cut right now. What we want is to look at all the
    117   // graphics devices available and select the primary one.
    118   drm_.reset(CreateWrapper());
    119   if (drm_->get_fd() < 0) {
    120     LOG(ERROR) << "Cannot open graphics card '"
    121                << kDefaultGraphicsCardPath << "': " << strerror(errno);
    122     state_ = FAILED;
    123     return state_;
    124   }
    125 
    126   state_ = INITIALIZED;
    127   return state_;
    128 }
    129 
    130 void DriSurfaceFactory::ShutdownHardware() {
    131   CHECK(state_ == INITIALIZED);
    132 
    133   controller_.reset();
    134   drm_.reset();
    135 
    136   state_ = UNINITIALIZED;
    137 }
    138 
    139 gfx::AcceleratedWidget DriSurfaceFactory::GetAcceleratedWidget() {
    140   CHECK(state_ != FAILED);
    141 
    142   // TODO(dnicoara) When there's more information on which display we want,
    143   // then we can return the widget associated with the display.
    144   // For now just assume we have 1 display device and return it.
    145   if (!controller_.get())
    146     controller_.reset(new HardwareDisplayController());
    147 
    148   // TODO(dnicoara) We only have 1 display for now, so only 1 AcceleratedWidget.
    149   // When we'll support multiple displays this needs to be changed to return a
    150   // different handle for every display.
    151   return kDefaultWidgetHandle;
    152 }
    153 
    154 gfx::AcceleratedWidget DriSurfaceFactory::RealizeAcceleratedWidget(
    155     gfx::AcceleratedWidget w) {
    156   CHECK(state_ == INITIALIZED);
    157   // TODO(dnicoara) Once we can handle multiple displays this needs to be
    158   // changed.
    159   CHECK(w == kDefaultWidgetHandle);
    160 
    161   CHECK(controller_->get_state() ==
    162         HardwareDisplayController::UNASSOCIATED);
    163 
    164   // Until now the controller is just a stub. Initializing it will link it to a
    165   // hardware display.
    166   if (!InitializeControllerForPrimaryDisplay(drm_.get(), controller_.get())) {
    167     LOG(ERROR) << "Failed to initialize controller";
    168     return gfx::kNullAcceleratedWidget;
    169   }
    170 
    171   // Create a surface suitable for the current controller.
    172   scoped_ptr<DriSurface> surface(CreateSurface(controller_.get()));
    173 
    174   if (!surface->Initialize()) {
    175     LOG(ERROR) << "Failed to initialize surface";
    176     return gfx::kNullAcceleratedWidget;
    177   }
    178 
    179   // Bind the surface to the controller. This will register the backing buffers
    180   // with the hardware CRTC such that we can show the buffers. The controller
    181   // takes ownership of the surface.
    182   if (!controller_->BindSurfaceToController(surface.Pass())) {
    183     LOG(ERROR) << "Failed to bind surface to controller";
    184     return gfx::kNullAcceleratedWidget;
    185   }
    186 
    187   return reinterpret_cast<gfx::AcceleratedWidget>(controller_->get_surface());
    188 }
    189 
    190 bool DriSurfaceFactory::LoadEGLGLES2Bindings(
    191       AddGLLibraryCallback add_gl_library,
    192       SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
    193   return false;
    194 }
    195 
    196 bool DriSurfaceFactory::AttemptToResizeAcceleratedWidget(
    197     gfx::AcceleratedWidget w,
    198     const gfx::Rect& bounds) {
    199   return false;
    200 }
    201 
    202 bool DriSurfaceFactory::SchedulePageFlip(gfx::AcceleratedWidget w) {
    203   CHECK(state_ == INITIALIZED);
    204   // TODO(dnicoara) Change this CHECK once we're running with the threaded
    205   // compositor.
    206   CHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
    207 
    208   // TODO(dnicoara) Once we can handle multiple displays this needs to be
    209   // changed.
    210   CHECK(w == kDefaultWidgetHandle);
    211 
    212   if (!controller_->SchedulePageFlip())
    213     return false;
    214 
    215   // Only wait for the page flip event to finish if it was properly scheduled.
    216   //
    217   // TODO(dnicoara) The following call will wait for the page flip event to
    218   // complete. This means that it will block until the next VSync. Ideally the
    219   // wait should happen in the message loop. The message loop would then
    220   // schedule the next draw event. Alternatively, the VSyncProvider could be
    221   // used to schedule the next draw. Unfortunately, at this point,
    222   // DriOutputDevice does not provide any means to use any of the above
    223   // solutions. Note that if the DRM callback does not schedule the next draw,
    224   // then some sort of synchronization needs to take place since starting a new
    225   // draw before the page flip happened is considered an error. However we can
    226   // not use any lock constructs unless we're using the threaded compositor.
    227   // Note that the following call does not use any locks, so it is safe to be
    228   // made on the UI thread (thought not ideal).
    229   WaitForPageFlipEvent(drm_->get_fd());
    230 
    231   return true;
    232 }
    233 
    234 SkCanvas* DriSurfaceFactory::GetCanvasForWidget(
    235     gfx::AcceleratedWidget w) {
    236   CHECK(state_ == INITIALIZED);
    237   return reinterpret_cast<DriSurface*>(w)->GetDrawableForWidget();
    238 }
    239 
    240 gfx::VSyncProvider* DriSurfaceFactory::GetVSyncProvider(
    241     gfx::AcceleratedWidget w) {
    242   CHECK(state_ == INITIALIZED);
    243   return new DriVSyncProvider(controller_.get());
    244 }
    245 
    246 ////////////////////////////////////////////////////////////////////////////////
    247 // DriSurfaceFactory private
    248 
    249 DriSurface* DriSurfaceFactory::CreateSurface(
    250     HardwareDisplayController* controller) {
    251   return new DriSurface(controller);
    252 }
    253 
    254 DriWrapper* DriSurfaceFactory::CreateWrapper() {
    255   return new DriWrapper(kDefaultGraphicsCardPath);
    256 }
    257 
    258 bool DriSurfaceFactory::InitializeControllerForPrimaryDisplay(
    259     DriWrapper* drm,
    260     HardwareDisplayController* controller) {
    261   CHECK(state_ == SurfaceFactoryOzone::INITIALIZED);
    262 
    263   drmModeRes* resources = drmModeGetResources(drm->get_fd());
    264 
    265   // Search for an active connector.
    266   for (int i = 0; i < resources->count_connectors; ++i) {
    267     drmModeConnector* connector = drmModeGetConnector(
    268         drm->get_fd(),
    269         resources->connectors[i]);
    270 
    271     if (!connector)
    272       continue;
    273 
    274     if (connector->connection != DRM_MODE_CONNECTED ||
    275         connector->count_modes == 0) {
    276       drmModeFreeConnector(connector);
    277       continue;
    278     }
    279 
    280     uint32_t crtc = GetCrtc(drm->get_fd(), resources, connector);
    281 
    282     if (!crtc)
    283       continue;
    284 
    285     uint32_t dpms_property_id = GetDriProperty(drm->get_fd(),
    286                                                connector,
    287                                                kDPMSProperty);
    288 
    289     // TODO(dnicoara) Select one mode for now. In the future we may need to
    290     // save all the modes and allow the user to choose a specific mode. Or
    291     // even some fullscreen applications may need to change the mode.
    292     controller->SetControllerInfo(
    293         drm,
    294         connector->connector_id,
    295         crtc,
    296         dpms_property_id,
    297         connector->modes[0]);
    298 
    299     drmModeFreeConnector(connector);
    300 
    301     return true;
    302   }
    303 
    304   return false;
    305 }
    306 
    307 void DriSurfaceFactory::WaitForPageFlipEvent(int fd) {
    308   drmEventContext drm_event;
    309   drm_event.version = DRM_EVENT_CONTEXT_VERSION;
    310   drm_event.page_flip_handler = HandlePageFlipEvent;
    311   drm_event.vblank_handler = NULL;
    312 
    313   // Wait for the page-flip to complete.
    314   drmHandleEvent(fd, &drm_event);
    315 }
    316 
    317 }  // namespace gfx
    318