blob: 4e36e2ee86e3a2a22cdf703b907390e54d66c46b [file] [log] [blame]
// Copyright (c) 2012 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/gl/gl_surface.h"
#include <stdint.h>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_surface_egl_x11.h"
#include "ui/gl/gl_surface_glx.h"
#include "ui/gl/gl_surface_osmesa.h"
#include "ui/gl/gl_surface_stub.h"
namespace gfx {
// This OSMesa GL surface can use XLib to swap the contents of the buffer to a
// view.
class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa {
public:
explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window);
static bool InitializeOneOff();
// Implement a subset of GLSurface.
bool Initialize(GLSurface::Format format) override;
void Destroy() override;
bool Resize(const gfx::Size& new_size,
float scale_factor,
bool alpha) override;
bool IsOffscreen() override;
gfx::SwapResult SwapBuffers() override;
bool SupportsPostSubBuffer() override;
gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
protected:
~NativeViewGLSurfaceOSMesa() override;
private:
Display* xdisplay_;
GC window_graphics_context_;
gfx::AcceleratedWidget window_;
GC pixmap_graphics_context_;
Pixmap pixmap_;
DISALLOW_COPY_AND_ASSIGN(NativeViewGLSurfaceOSMesa);
};
bool GLSurface::InitializeOneOffInternal() {
switch (GetGLImplementation()) {
case kGLImplementationDesktopGL:
if (!GLSurfaceGLX::InitializeOneOff()) {
LOG(ERROR) << "GLSurfaceGLX::InitializeOneOff failed.";
return false;
}
break;
case kGLImplementationOSMesaGL:
if (!NativeViewGLSurfaceOSMesa::InitializeOneOff()) {
LOG(ERROR) << "NativeViewGLSurfaceOSMesa::InitializeOneOff failed.";
return false;
}
break;
case kGLImplementationEGLGLES2:
if (!GLSurfaceEGL::InitializeOneOff()) {
LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
return false;
}
break;
default:
break;
}
return true;
}
NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa(
gfx::AcceleratedWidget window)
: GLSurfaceOSMesa(SURFACE_OSMESA_BGRA, gfx::Size(1, 1)),
xdisplay_(gfx::GetXDisplay()),
window_graphics_context_(0),
window_(window),
pixmap_graphics_context_(0),
pixmap_(0) {
DCHECK(xdisplay_);
DCHECK(window_);
}
// static
bool NativeViewGLSurfaceOSMesa::InitializeOneOff() {
static bool initialized = false;
if (initialized)
return true;
if (!gfx::GetXDisplay()) {
LOG(ERROR) << "XOpenDisplay failed.";
return false;
}
initialized = true;
return true;
}
bool NativeViewGLSurfaceOSMesa::Initialize(GLSurface::Format format) {
if (!GLSurfaceOSMesa::Initialize(format))
return false;
window_graphics_context_ = XCreateGC(xdisplay_, window_, 0, NULL);
if (!window_graphics_context_) {
LOG(ERROR) << "XCreateGC failed.";
Destroy();
return false;
}
return true;
}
void NativeViewGLSurfaceOSMesa::Destroy() {
if (pixmap_graphics_context_) {
XFreeGC(xdisplay_, pixmap_graphics_context_);
pixmap_graphics_context_ = NULL;
}
if (pixmap_) {
XFreePixmap(xdisplay_, pixmap_);
pixmap_ = 0;
}
if (window_graphics_context_) {
XFreeGC(xdisplay_, window_graphics_context_);
window_graphics_context_ = NULL;
}
XSync(xdisplay_, False);
}
bool NativeViewGLSurfaceOSMesa::Resize(const gfx::Size& new_size,
float scale_factor,
bool alpha) {
if (!GLSurfaceOSMesa::Resize(new_size, scale_factor, alpha))
return false;
XWindowAttributes attributes;
if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
return false;
}
// Destroy the previous pixmap and graphics context.
if (pixmap_graphics_context_) {
XFreeGC(xdisplay_, pixmap_graphics_context_);
pixmap_graphics_context_ = NULL;
}
if (pixmap_) {
XFreePixmap(xdisplay_, pixmap_);
pixmap_ = 0;
}
// Recreate a pixmap to hold the frame.
pixmap_ = XCreatePixmap(xdisplay_,
window_,
new_size.width(),
new_size.height(),
attributes.depth);
if (!pixmap_) {
LOG(ERROR) << "XCreatePixmap failed.";
return false;
}
// Recreate a graphics context for the pixmap.
pixmap_graphics_context_ = XCreateGC(xdisplay_, pixmap_, 0, NULL);
if (!pixmap_graphics_context_) {
LOG(ERROR) << "XCreateGC failed";
return false;
}
return true;
}
bool NativeViewGLSurfaceOSMesa::IsOffscreen() {
return false;
}
gfx::SwapResult NativeViewGLSurfaceOSMesa::SwapBuffers() {
TRACE_EVENT2("gpu", "NativeViewGLSurfaceOSMesa:RealSwapBuffers",
"width", GetSize().width(),
"height", GetSize().height());
gfx::Size size = GetSize();
XWindowAttributes attributes;
if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
return gfx::SwapResult::SWAP_FAILED;
}
// Copy the frame into the pixmap.
gfx::PutARGBImage(xdisplay_, attributes.visual, attributes.depth, pixmap_,
pixmap_graphics_context_,
static_cast<const uint8_t*>(GetHandle()), size.width(),
size.height());
// Copy the pixmap to the window.
XCopyArea(xdisplay_,
pixmap_,
window_,
window_graphics_context_,
0,
0,
size.width(),
size.height(),
0,
0);
return gfx::SwapResult::SWAP_ACK;
}
bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() {
return true;
}
gfx::SwapResult NativeViewGLSurfaceOSMesa::PostSubBuffer(int x,
int y,
int width,
int height) {
gfx::Size size = GetSize();
// Move (0,0) from lower-left to upper-left
y = size.height() - y - height;
XWindowAttributes attributes;
if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
return gfx::SwapResult::SWAP_FAILED;
}
// Copy the frame into the pixmap.
gfx::PutARGBImage(xdisplay_, attributes.visual, attributes.depth, pixmap_,
pixmap_graphics_context_,
static_cast<const uint8_t*>(GetHandle()), size.width(),
size.height(), x, y, x, y, width, height);
// Copy the pixmap to the window.
XCopyArea(xdisplay_,
pixmap_,
window_,
window_graphics_context_,
x,
y,
width,
height,
x,
y);
return gfx::SwapResult::SWAP_ACK;
}
NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() {
Destroy();
}
scoped_refptr<GLSurface> GLSurface::CreateViewGLSurface(
gfx::AcceleratedWidget window) {
TRACE_EVENT0("gpu", "GLSurface::CreateViewGLSurface");
switch (GetGLImplementation()) {
case kGLImplementationOSMesaGL: {
scoped_refptr<GLSurface> surface(
new NativeViewGLSurfaceOSMesa(window));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationDesktopGL: {
scoped_refptr<GLSurface> surface(new NativeViewGLSurfaceGLX(window));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationEGLGLES2: {
DCHECK(window != gfx::kNullAcceleratedWidget);
scoped_refptr<GLSurface> surface(new NativeViewGLSurfaceEGLX11(window));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationMockGL:
return new GLSurfaceStub;
default:
NOTREACHED();
return NULL;
}
}
scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface(
const gfx::Size& size) {
TRACE_EVENT0("gpu", "GLSurface::CreateOffscreenGLSurface");
switch (GetGLImplementation()) {
case kGLImplementationOSMesaGL: {
scoped_refptr<GLSurface> surface(
new GLSurfaceOSMesa(SURFACE_OSMESA_RGBA, size));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationDesktopGL: {
scoped_refptr<GLSurface> surface(
new UnmappedNativeViewGLSurfaceGLX(size));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationEGLGLES2: {
scoped_refptr<GLSurface> surface(new PbufferGLSurfaceEGL(size));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationMockGL:
return new GLSurfaceStub;
default:
NOTREACHED();
return NULL;
}
}
EGLNativeDisplayType GetPlatformDefaultEGLNativeDisplay() {
return gfx::GetXDisplay();
}
} // namespace gfx