Chromium Code Reviews| 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..01904072883eec057c7ea533b7cbbef585251a63 |
| --- /dev/null |
| +++ b/ui/gfx/ozone/impl/software_surface_factory_ozone.cc |
| @@ -0,0 +1,282 @@ |
| +// 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. |
| + SoftwareSurfaceOzone* surface = CreateSurface(controller_.get()); |
| + |
| + if (!surface->Initialize()) { |
| + delete surface; |
| + 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. On success the |
| + // controller takes ownership of the surface. |
|
rjkroege
2013/10/16 21:42:13
In this kind of complicated ownership transferring
dnicoara
2013/10/17 14:26:19
Done. Thank you for the tip.
|
| + if (!controller_->BindSurfaceToController(surface)) { |
| + delete surface; |
| + LOG(ERROR) << "Failed to bind surface to controller"; |
| + return gfx::kNullAcceleratedWidget; |
| + } |
| + |
| + return reinterpret_cast<gfx::AcceleratedWidget>( |
| + 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) This requires the threaded compositor. |
| + // 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. |
|
rjkroege
2013/10/16 21:42:13
You still say that WaitForPageFlipEvent needs thre
dnicoara
2013/10/17 14:26:19
Oh, I see where the lack of clarity is. Added more
|
| + 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 |