| 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
|
|
|