1// Copyright 2014 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/ozone/platform/dri/gbm_surface.h"
6
7#include <gbm.h>
8
9#include "base/logging.h"
10#include "ui/ozone/platform/dri/dri_buffer.h"
11#include "ui/ozone/platform/dri/dri_window_delegate.h"
12#include "ui/ozone/platform/dri/dri_wrapper.h"
13#include "ui/ozone/platform/dri/gbm_buffer_base.h"
14#include "ui/ozone/platform/dri/hardware_display_controller.h"
15#include "ui/ozone/platform/dri/scanout_buffer.h"
16
17namespace ui {
18
19namespace {
20
21class GbmSurfaceBuffer : public GbmBufferBase {
22 public:
23  static scoped_refptr<GbmSurfaceBuffer> CreateBuffer(DriWrapper* dri,
24                                                      gbm_bo* buffer);
25  static scoped_refptr<GbmSurfaceBuffer> GetBuffer(gbm_bo* buffer);
26
27 private:
28  GbmSurfaceBuffer(DriWrapper* dri, gbm_bo* bo);
29  virtual ~GbmSurfaceBuffer();
30
31  static void Destroy(gbm_bo* buffer, void* data);
32
33  // This buffer is special and is released by GBM at any point in time (as
34  // long as it isn't being used). Since GBM should be the only one to
35  // release this buffer, keep a self-reference in order to keep this alive.
36  // When GBM calls Destroy(..) the self-reference will dissapear and this will
37  // be destroyed.
38  scoped_refptr<GbmSurfaceBuffer> self_;
39
40  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceBuffer);
41};
42
43GbmSurfaceBuffer::GbmSurfaceBuffer(DriWrapper* dri, gbm_bo* bo)
44  : GbmBufferBase(dri, bo, true) {
45  if (GetFramebufferId()) {
46    self_ = this;
47    gbm_bo_set_user_data(bo, this, GbmSurfaceBuffer::Destroy);
48  }
49}
50
51GbmSurfaceBuffer::~GbmSurfaceBuffer() {}
52
53// static
54scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::CreateBuffer(
55    DriWrapper* dri, gbm_bo* buffer) {
56  scoped_refptr<GbmSurfaceBuffer> scoped_buffer(new GbmSurfaceBuffer(dri,
57                                                                     buffer));
58  if (!scoped_buffer->GetFramebufferId())
59    return NULL;
60
61  return scoped_buffer;
62}
63
64// static
65scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::GetBuffer(gbm_bo* buffer) {
66  return scoped_refptr<GbmSurfaceBuffer>(
67      static_cast<GbmSurfaceBuffer*>(gbm_bo_get_user_data(buffer)));
68}
69
70// static
71void GbmSurfaceBuffer::Destroy(gbm_bo* buffer, void* data) {
72  GbmSurfaceBuffer* scoped_buffer = static_cast<GbmSurfaceBuffer*>(data);
73  scoped_buffer->self_ = NULL;
74}
75
76}  // namespace
77
78GbmSurface::GbmSurface(DriWindowDelegate* window_delegate,
79                       gbm_device* device,
80                       DriWrapper* dri)
81    : GbmSurfaceless(window_delegate),
82      gbm_device_(device),
83      dri_(dri),
84      native_surface_(NULL),
85      current_buffer_(NULL) {
86}
87
88GbmSurface::~GbmSurface() {
89  if (current_buffer_)
90    gbm_surface_release_buffer(native_surface_, current_buffer_);
91
92  if (native_surface_)
93    gbm_surface_destroy(native_surface_);
94}
95
96bool GbmSurface::Initialize() {
97  // If we're initializing the surface without a controller (possible on startup
98  // where the surface creation can happen before the native window delegate
99  // IPCs arrive), initialize the size to a valid value such that surface
100  // creation doesn't fail.
101  gfx::Size size(1, 1);
102  if (window_delegate_->GetController()) {
103    size = window_delegate_->GetController()->GetModeSize();
104  }
105  // TODO(dnicoara) Check underlying system support for pixel format.
106  native_surface_ =
107      gbm_surface_create(gbm_device_,
108                         size.width(),
109                         size.height(),
110                         GBM_BO_FORMAT_XRGB8888,
111                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
112
113  if (!native_surface_)
114    return false;
115
116  size_ = size;
117  return true;
118}
119
120intptr_t GbmSurface::GetNativeWindow() {
121  DCHECK(native_surface_);
122  return reinterpret_cast<intptr_t>(native_surface_);
123}
124
125bool GbmSurface::ResizeNativeWindow(const gfx::Size& viewport_size) {
126  if (size_ == viewport_size)
127    return true;
128
129  return false;
130}
131
132bool GbmSurface::OnSwapBuffers() {
133  DCHECK(native_surface_);
134
135  gbm_bo* pending_buffer = gbm_surface_lock_front_buffer(native_surface_);
136  scoped_refptr<GbmSurfaceBuffer> primary =
137      GbmSurfaceBuffer::GetBuffer(pending_buffer);
138  if (!primary.get()) {
139    primary = GbmSurfaceBuffer::CreateBuffer(dri_, pending_buffer);
140    if (!primary.get()) {
141      LOG(ERROR) << "Failed to associate the buffer with the controller";
142      return false;
143    }
144  }
145
146  // The primary buffer is a special case.
147  if (window_delegate_->GetController())
148    window_delegate_->GetController()->QueueOverlayPlane(OverlayPlane(primary));
149
150  if (!GbmSurfaceless::OnSwapBuffers())
151    return false;
152
153  // If there was a frontbuffer, it is no longer active. Release it back to GBM.
154  if (current_buffer_)
155    gbm_surface_release_buffer(native_surface_, current_buffer_);
156
157  current_buffer_ = pending_buffer;
158  return true;
159}
160
161}  // namespace ui
162