blob: 280a340b5fb4860fd32907452073efa6bf5357f6 [file] [log] [blame]
// Copyright 2015 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/cast/surface_factory_cast.h"
#include <EGL/egl.h>
#include <dlfcn.h>
#include <utility>
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "chromecast/base/chromecast_switches.h"
#include "chromecast/public/cast_egl_platform.h"
#include "chromecast/public/graphics_types.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/geometry/quad_f.h"
#include "ui/gfx/vsync_provider.h"
#include "ui/ozone/platform/cast/gl_surface_cast.h"
#include "ui/ozone/public/native_pixmap.h"
#include "ui/ozone/public/surface_ozone_canvas.h"
using chromecast::CastEglPlatform;
namespace ui {
namespace {
typedef EGLDisplay (*EGLGetDisplayFn)(NativeDisplayType);
typedef EGLBoolean (*EGLTerminateFn)(EGLDisplay);
chromecast::Size FromGfxSize(const gfx::Size& size) {
return chromecast::Size(size.width(), size.height());
}
// Display resolution, set in browser process and passed by switches.
gfx::Size GetDisplaySize() {
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
int width, height;
if (base::StringToInt(
cmd_line->GetSwitchValueASCII(switches::kCastInitialScreenWidth),
&width) &&
base::StringToInt(
cmd_line->GetSwitchValueASCII(switches::kCastInitialScreenHeight),
&height)) {
return gfx::Size(width, height);
}
LOG(WARNING) << "Unable to get initial screen resolution from command line,"
<< "using default 720p";
return gfx::Size(1280, 720);
}
class DummySurface : public SurfaceOzoneCanvas {
public:
DummySurface() {}
~DummySurface() override {}
// SurfaceOzoneCanvas implementation:
sk_sp<SkSurface> GetSurface() override { return surface_; }
void ResizeCanvas(const gfx::Size& viewport_size) override {
surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(
viewport_size.width(), viewport_size.height()));
}
void PresentCanvas(const gfx::Rect& damage) override {}
std::unique_ptr<gfx::VSyncProvider> CreateVSyncProvider() override {
return nullptr;
}
private:
sk_sp<SkSurface> surface_;
DISALLOW_COPY_AND_ASSIGN(DummySurface);
};
} // namespace
SurfaceFactoryCast::SurfaceFactoryCast() : SurfaceFactoryCast(nullptr) {}
SurfaceFactoryCast::SurfaceFactoryCast(
std::unique_ptr<CastEglPlatform> egl_platform)
: state_(kUninitialized),
display_type_(0),
have_display_type_(false),
window_(0),
display_size_(GetDisplaySize()),
egl_platform_(std::move(egl_platform)),
overlay_count_(0),
previous_frame_overlay_count_(0) {}
SurfaceFactoryCast::~SurfaceFactoryCast() {
ShutdownHardware();
}
void SurfaceFactoryCast::InitializeHardware() {
if (state_ == kInitialized) {
return;
}
CHECK_EQ(state_, kUninitialized);
if (egl_platform_->InitializeHardware()) {
state_ = kInitialized;
} else {
ShutdownHardware();
state_ = kFailed;
}
}
void SurfaceFactoryCast::TerminateDisplay() {
void* egl_lib_handle = egl_platform_->GetEglLibrary();
if (!egl_lib_handle)
return;
EGLGetDisplayFn get_display =
reinterpret_cast<EGLGetDisplayFn>(dlsym(egl_lib_handle, "eglGetDisplay"));
EGLTerminateFn terminate =
reinterpret_cast<EGLTerminateFn>(dlsym(egl_lib_handle, "eglTerminate"));
DCHECK(get_display);
DCHECK(terminate);
EGLDisplay display = get_display(GetNativeDisplay());
DCHECK_NE(display, EGL_NO_DISPLAY);
EGLBoolean terminate_result = terminate(display);
DCHECK_EQ(terminate_result, static_cast<EGLBoolean>(EGL_TRUE));
}
void SurfaceFactoryCast::ShutdownHardware() {
if (state_ != kInitialized)
return;
DestroyDisplayTypeAndWindow();
egl_platform_->ShutdownHardware();
state_ = kUninitialized;
}
void SurfaceFactoryCast::OnSwapBuffers() {
DCHECK(overlay_count_ == 0 || overlay_count_ == 1);
// Logging for overlays to help diagnose bugs when nothing is visible on
// screen. Logging this every frame would be overwhelming, so we just
// log on the transitions from 0 overlays -> 1 overlay and vice versa.
if (overlay_count_ == 0 && previous_frame_overlay_count_ != 0) {
LOG(INFO) << "Overlays deactivated";
} else if (overlay_count_ != 0 && previous_frame_overlay_count_ == 0) {
LOG(INFO) << "Overlays activated: " << overlay_bounds_.ToString();
} else if (overlay_count_ == previous_frame_overlay_count_ &&
overlay_bounds_ != previous_frame_overlay_bounds_) {
LOG(INFO) << "Overlay geometry changed to " << overlay_bounds_.ToString();
}
previous_frame_overlay_count_ = overlay_count_;
previous_frame_overlay_bounds_ = overlay_bounds_;
overlay_count_ = 0;
}
void SurfaceFactoryCast::OnOverlayScheduled(const gfx::Rect& display_bounds) {
++overlay_count_;
overlay_bounds_ = display_bounds;
}
scoped_refptr<gl::GLSurface> SurfaceFactoryCast::CreateViewGLSurface(
gl::GLImplementation implementation,
gfx::AcceleratedWidget widget) {
if (implementation != gl::kGLImplementationEGLGLES2) {
NOTREACHED();
return nullptr;
}
// Verify requested widget dimensions match our current display size.
DCHECK_EQ(widget >> 16, display_size_.width());
DCHECK_EQ(widget & 0xffff, display_size_.height());
return gl::InitializeGLSurface(new GLSurfaceCast(widget, this));
}
scoped_refptr<gl::GLSurface> SurfaceFactoryCast::CreateOffscreenGLSurface(
gl::GLImplementation implementation,
const gfx::Size& size) {
if (implementation != gl::kGLImplementationEGLGLES2) {
NOTREACHED();
return nullptr;
}
return gl::InitializeGLSurface(new gl::PbufferGLSurfaceEGL(size));
}
std::unique_ptr<SurfaceOzoneCanvas> SurfaceFactoryCast::CreateCanvasForWidget(
gfx::AcceleratedWidget widget) {
// Software canvas support only in headless mode
if (egl_platform_)
return nullptr;
return base::WrapUnique<SurfaceOzoneCanvas>(new DummySurface());
}
intptr_t SurfaceFactoryCast::GetNativeDisplay() {
CreateDisplayTypeAndWindowIfNeeded();
return reinterpret_cast<intptr_t>(display_type_);
}
void SurfaceFactoryCast::CreateDisplayTypeAndWindowIfNeeded() {
if (state_ == kUninitialized) {
InitializeHardware();
}
DCHECK_EQ(state_, kInitialized);
if (!have_display_type_) {
chromecast::Size create_size = FromGfxSize(display_size_);
display_type_ = egl_platform_->CreateDisplayType(create_size);
have_display_type_ = true;
}
if (!window_) {
chromecast::Size create_size = FromGfxSize(display_size_);
window_ = egl_platform_->CreateWindow(display_type_, create_size);
if (!window_) {
DestroyDisplayTypeAndWindow();
state_ = kFailed;
LOG(FATAL) << "Create EGLNativeWindowType(" << display_size_.ToString()
<< ") failed.";
}
}
}
intptr_t SurfaceFactoryCast::GetNativeWindow() {
CreateDisplayTypeAndWindowIfNeeded();
return reinterpret_cast<intptr_t>(window_);
}
bool SurfaceFactoryCast::ResizeDisplay(gfx::Size size) {
DCHECK_EQ(size.width(), display_size_.width());
DCHECK_EQ(size.height(), display_size_.height());
return true;
}
void SurfaceFactoryCast::DestroyWindow() {
if (window_) {
egl_platform_->DestroyWindow(window_);
window_ = 0;
}
}
void SurfaceFactoryCast::DestroyDisplayTypeAndWindow() {
DestroyWindow();
if (have_display_type_) {
egl_platform_->DestroyDisplayType(display_type_);
display_type_ = 0;
have_display_type_ = false;
}
}
void SurfaceFactoryCast::ChildDestroyed() {
if (egl_platform_->MultipleSurfaceUnsupported())
DestroyWindow();
}
scoped_refptr<NativePixmap> SurfaceFactoryCast::CreateNativePixmap(
gfx::AcceleratedWidget widget,
gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage) {
class CastPixmap : public NativePixmap {
public:
explicit CastPixmap(SurfaceFactoryCast* parent) : parent_(parent) {}
void* GetEGLClientBuffer() const override {
// TODO(halliwell): try to implement this through CastEglPlatform.
return nullptr;
}
bool AreDmaBufFdsValid() const override { return false; }
size_t GetDmaBufFdCount() const override { return 0; }
int GetDmaBufFd(size_t plane) const override { return -1; }
int GetDmaBufPitch(size_t plane) const override { return 0; }
int GetDmaBufOffset(size_t plane) const override { return 0; }
uint64_t GetDmaBufModifier(size_t plane) const override { return 0; }
gfx::BufferFormat GetBufferFormat() const override {
return gfx::BufferFormat::BGRA_8888;
}
gfx::Size GetBufferSize() const override { return gfx::Size(); }
bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
int plane_z_order,
gfx::OverlayTransform plane_transform,
const gfx::Rect& display_bounds,
const gfx::RectF& crop_rect) override {
parent_->OnOverlayScheduled(display_bounds);
return true;
}
void SetProcessingCallback(
const ProcessingCallback& processing_callback) override {}
gfx::NativePixmapHandle ExportHandle() override {
return gfx::NativePixmapHandle();
}
private:
~CastPixmap() override {}
SurfaceFactoryCast* parent_;
DISALLOW_COPY_AND_ASSIGN(CastPixmap);
};
return make_scoped_refptr(new CastPixmap(this));
}
bool SurfaceFactoryCast::LoadEGLGLES2Bindings() {
if (state_ != kInitialized) {
InitializeHardware();
if (state_ != kInitialized) {
return false;
}
}
void* lib_egl = egl_platform_->GetEglLibrary();
void* lib_gles2 = egl_platform_->GetGles2Library();
gl::GLGetProcAddressProc gl_proc = egl_platform_->GetGLProcAddressProc();
if (!lib_egl || !lib_gles2 || !gl_proc) {
return false;
}
gl::SetGLGetProcAddressProc(gl_proc);
gl::AddGLNativeLibrary(lib_egl);
gl::AddGLNativeLibrary(lib_gles2);
return true;
}
} // namespace ui