// 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 #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 { class GbmSurfaceBuffer : public GbmBufferBase { public: static scoped_refptr CreateBuffer( const scoped_refptr& drm, gbm_bo* buffer); static scoped_refptr GetBuffer(gbm_bo* buffer); private: GbmSurfaceBuffer(const scoped_refptr& 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 self_; DISALLOW_COPY_AND_ASSIGN(GbmSurfaceBuffer); }; GbmSurfaceBuffer::GbmSurfaceBuffer(const scoped_refptr& 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::CreateBuffer( const scoped_refptr& drm, gbm_bo* buffer) { scoped_refptr scoped_buffer( new GbmSurfaceBuffer(drm, buffer)); if (!scoped_buffer->GetFramebufferId()) return NULL; return scoped_buffer; } // static scoped_refptr GbmSurfaceBuffer::GetBuffer(gbm_bo* buffer) { return scoped_refptr( static_cast(gbm_bo_get_user_data(buffer))); } // static void GbmSurfaceBuffer::Destroy(gbm_bo* buffer, void* data) { GbmSurfaceBuffer* scoped_buffer = static_cast(data); scoped_buffer->self_ = NULL; } } // namespace GbmSurface::GbmSurface(DrmWindow* window_delegate, const scoped_refptr& 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(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(&base::DoNothing)); } bool GbmSurface::OnSwapBuffersAsync(const SwapCompletionCallback& callback) { DCHECK(native_surface_); gbm_bo* pending_buffer = gbm_surface_lock_front_buffer(native_surface_); scoped_refptr 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(); 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(); return false; } return true; } void GbmSurface::OnSwapBuffersCallback(const SwapCompletionCallback& callback, gbm_bo* pending_buffer) { // 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(); } } // namespace ui