blob: 08a32f44d7bf7c9c09fc83a49151deaaeb11bb5e [file] [log] [blame]
// Copyright 2014 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/ozone/platform/drm/gpu/gbm_surface.h"
#include <gbm.h>
#include "base/bind.h"
#include "base/logging.h"
#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
#include "ui/ozone/platform/drm/gpu/drm_window.h"
#include "ui/ozone/platform/drm/gpu/gbm_buffer_base.h"
#include "ui/ozone/platform/drm/gpu/gbm_device.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
namespace ui {
namespace {
void DoNothing(gfx::SwapResult) {
}
class GbmSurfaceBuffer : public GbmBufferBase {
public:
static scoped_refptr<GbmSurfaceBuffer> CreateBuffer(
const scoped_refptr<DrmDevice>& drm,
gbm_bo* buffer);
static scoped_refptr<GbmSurfaceBuffer> GetBuffer(gbm_bo* buffer);
private:
GbmSurfaceBuffer(const scoped_refptr<DrmDevice>& drm, gbm_bo* bo);
~GbmSurfaceBuffer() override;
static void Destroy(gbm_bo* buffer, void* data);
// This buffer is special and is released by GBM at any point in time (as
// long as it isn't being used). Since GBM should be the only one to
// release this buffer, keep a self-reference in order to keep this alive.
// When GBM calls Destroy(..) the self-reference will dissapear and this will
// be destroyed.
scoped_refptr<GbmSurfaceBuffer> self_;
DISALLOW_COPY_AND_ASSIGN(GbmSurfaceBuffer);
};
GbmSurfaceBuffer::GbmSurfaceBuffer(const scoped_refptr<DrmDevice>& drm,
gbm_bo* bo)
: GbmBufferBase(drm, bo, true) {
if (GetFramebufferId()) {
self_ = this;
gbm_bo_set_user_data(bo, this, GbmSurfaceBuffer::Destroy);
}
}
GbmSurfaceBuffer::~GbmSurfaceBuffer() {
}
// static
scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::CreateBuffer(
const scoped_refptr<DrmDevice>& drm,
gbm_bo* buffer) {
scoped_refptr<GbmSurfaceBuffer> scoped_buffer(
new GbmSurfaceBuffer(drm, buffer));
if (!scoped_buffer->GetFramebufferId())
return NULL;
return scoped_buffer;
}
// static
scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::GetBuffer(gbm_bo* buffer) {
return scoped_refptr<GbmSurfaceBuffer>(
static_cast<GbmSurfaceBuffer*>(gbm_bo_get_user_data(buffer)));
}
// static
void GbmSurfaceBuffer::Destroy(gbm_bo* buffer, void* data) {
GbmSurfaceBuffer* scoped_buffer = static_cast<GbmSurfaceBuffer*>(data);
scoped_buffer->self_ = NULL;
}
} // namespace
GbmSurface::GbmSurface(DrmWindow* window_delegate,
const scoped_refptr<GbmDevice>& gbm)
: GbmSurfaceless(window_delegate, NULL),
gbm_(gbm),
native_surface_(NULL),
current_buffer_(NULL),
weak_factory_(this) {
}
GbmSurface::~GbmSurface() {
if (current_buffer_)
gbm_surface_release_buffer(native_surface_, current_buffer_);
if (native_surface_)
gbm_surface_destroy(native_surface_);
}
bool GbmSurface::Initialize() {
// If we're initializing the surface without a controller (possible on startup
// where the surface creation can happen before the native window delegate
// IPCs arrive), initialize the size to a valid value such that surface
// creation doesn't fail.
gfx::Size size(1, 1);
if (window_delegate_->GetController()) {
size = window_delegate_->GetController()->GetModeSize();
}
// TODO(dnicoara) Check underlying system support for pixel format.
native_surface_ = gbm_surface_create(
gbm_->device(), size.width(), size.height(), GBM_BO_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
if (!native_surface_)
return false;
size_ = size;
return true;
}
intptr_t GbmSurface::GetNativeWindow() {
DCHECK(native_surface_);
return reinterpret_cast<intptr_t>(native_surface_);
}
bool GbmSurface::ResizeNativeWindow(const gfx::Size& viewport_size) {
if (size_ == viewport_size)
return true;
return false;
}
bool GbmSurface::OnSwapBuffers() {
return OnSwapBuffersAsync(base::Bind(&DoNothing));
}
bool GbmSurface::OnSwapBuffersAsync(const SwapCompletionCallback& callback) {
DCHECK(native_surface_);
gbm_bo* pending_buffer = gbm_surface_lock_front_buffer(native_surface_);
scoped_refptr<GbmSurfaceBuffer> primary =
GbmSurfaceBuffer::GetBuffer(pending_buffer);
if (!primary.get()) {
primary = GbmSurfaceBuffer::CreateBuffer(gbm_, pending_buffer);
if (!primary.get()) {
LOG(ERROR) << "Failed to associate the buffer with the controller";
callback.Run(gfx::SwapResult::SWAP_FAILED);
return false;
}
}
// The primary buffer is a special case.
window_delegate_->QueueOverlayPlane(OverlayPlane(primary));
if (!GbmSurfaceless::OnSwapBuffersAsync(
base::Bind(&GbmSurface::OnSwapBuffersCallback,
weak_factory_.GetWeakPtr(), callback, pending_buffer))) {
callback.Run(gfx::SwapResult::SWAP_FAILED);
return false;
}
return true;
}
void GbmSurface::OnSwapBuffersCallback(const SwapCompletionCallback& callback,
gbm_bo* pending_buffer,
gfx::SwapResult result) {
// If there was a frontbuffer, it is no longer active. Release it back to GBM.
if (current_buffer_)
gbm_surface_release_buffer(native_surface_, current_buffer_);
current_buffer_ = pending_buffer;
callback.Run(result);
}
} // namespace ui