blob: 29d7ce6de009e6faf1d46d3f596240bcb211821a [file] [log] [blame]
/*
* Copyright © 2010-2011 Linaro Limited
*
* This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
*
* glmark2 is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* glmark2. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexandros Frantzis (glmark2)
* Jesse Barker
*/
#include "canvas-x11.h"
#include "log.h"
#include "options.h"
#include "util.h"
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <fstream>
#include <sstream>
/******************
* Public methods *
******************/
bool
CanvasX11::reset()
{
release_fbo();
if (!reset_context())
return false;
if (!do_make_current())
return false;
if (!supports_gl2()) {
Log::error("Glmark2 needs OpenGL(ES) version >= 2.0 to run"
" (but version string is: '%s')!\n",
glGetString(GL_VERSION));
return false;
}
glViewport(0, 0, width_, height_);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
clear();
return true;
}
bool
CanvasX11::init()
{
xdpy_ = XOpenDisplay(NULL);
if (!xdpy_)
return false;
resize_no_viewport(width_, height_);
if (!xwin_)
return false;
return reset();
}
void
CanvasX11::visible(bool visible)
{
if (visible && !offscreen_)
XMapWindow(xdpy_, xwin_);
}
void
CanvasX11::clear()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
#if USE_GL
glClearDepth(1.0f);
#elif USE_GLESv2
glClearDepthf(1.0f);
#endif
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void
CanvasX11::update()
{
Options::FrameEnd m = Options::frame_end;
if (m == Options::FrameEndDefault) {
if (offscreen_)
m = Options::FrameEndFinish;
else
m = Options::FrameEndSwap;
}
switch(m) {
case Options::FrameEndSwap:
swap_buffers();
break;
case Options::FrameEndFinish:
glFinish();
break;
case Options::FrameEndReadPixels:
read_pixel(width_ / 2, height_ / 2);
break;
case Options::FrameEndNone:
default:
break;
}
}
void
CanvasX11::print_info()
{
do_make_current();
std::stringstream ss;
ss << " OpenGL Information" << std::endl;
ss << " GL_VENDOR: " << glGetString(GL_VENDOR) << std::endl;
ss << " GL_RENDERER: " << glGetString(GL_RENDERER) << std::endl;
ss << " GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
Log::info("%s", ss.str().c_str());
}
Canvas::Pixel
CanvasX11::read_pixel(int x, int y)
{
uint8_t pixel[4];
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
return Canvas::Pixel(pixel[0], pixel[1], pixel[2], pixel[3]);
}
void
CanvasX11::write_to_file(std::string &filename)
{
char *pixels = new char[width_ * height_ * 4];
for (int i = 0; i < height_; i++) {
glReadPixels(0, i, width_, 1, GL_RGBA, GL_UNSIGNED_BYTE,
&pixels[(height_ - i - 1) * width_ * 4]);
}
std::ofstream output (filename.c_str(), std::ios::out | std::ios::binary);
output.write(pixels, 4 * width_ * height_);
delete [] pixels;
}
bool
CanvasX11::should_quit()
{
XEvent event;
if (!XPending(xdpy_))
return false;
XNextEvent(xdpy_, &event);
if (event.type == KeyPress) {
if (XLookupKeysym(&event.xkey, 0) == XK_Escape)
return true;
}
else if (event.type == ClientMessage) {
/* Window Delete event from window manager */
return true;
}
return false;
}
void
CanvasX11::resize(int width, int height)
{
resize_no_viewport(width, height);
glViewport(0, 0, width_, height_);
}
unsigned int
CanvasX11::fbo()
{
return fbo_;
}
bool
CanvasX11::supports_gl2()
{
std::string gl_version_str(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
int gl_major(0);
size_t point_pos(gl_version_str.find('.'));
if (point_pos != std::string::npos) {
point_pos--;
size_t start_pos(gl_version_str.rfind(' ', point_pos));
if (start_pos == std::string::npos)
start_pos = 0;
else
start_pos++;
gl_major = Util::fromString<int>(
gl_version_str.substr(start_pos, point_pos - start_pos + 1)
);
}
return gl_major >= 2;
}
/*******************
* Private methods *
*******************/
bool
CanvasX11::ensure_x_window()
{
static const char *win_name("glmark2 "GLMARK_VERSION);
if (xwin_)
return true;
if (!xdpy_) {
Log::error("Error: X11 Display has not been initialized!\n");
return false;
}
XVisualInfo *vis_info = get_xvisualinfo();
if (!vis_info) {
Log::error("Error: Could not get a valid XVisualInfo!\n");
return false;
}
Log::debug("Creating XWindow W: %d H: %d VisualID: 0x%x\n",
width_, height_, vis_info->visualid);
/* window attributes */
XSetWindowAttributes attr;
unsigned long mask;
Window root = RootWindow(xdpy_, DefaultScreen(xdpy_));
attr.background_pixel = 0;
attr.border_pixel = 0;
attr.colormap = XCreateColormap(xdpy_, root, vis_info->visual, AllocNone);
attr.event_mask = KeyPressMask;
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
xwin_ = XCreateWindow(xdpy_, root, 0, 0, width_, height_,
0, vis_info->depth, InputOutput,
vis_info->visual, mask, &attr);
XFree(vis_info);
if (!xwin_) {
Log::error("Error: XCreateWindow() failed!\n");
return false;
}
/* set hints and properties */
if (fullscreen_) {
Atom atom = XInternAtom(xdpy_, "_NET_WM_STATE_FULLSCREEN", True);
XChangeProperty(xdpy_, xwin_,
XInternAtom(xdpy_, "_NET_WM_STATE", True),
XA_ATOM, 32, PropModeReplace,
reinterpret_cast<unsigned char*>(&atom), 1);
}
else {
XSizeHints sizehints;
sizehints.min_width = width_;
sizehints.min_height = height_;
sizehints.max_width = width_;
sizehints.max_height = height_;
sizehints.flags = PMaxSize | PMinSize;
XSetWMProperties(xdpy_, xwin_, NULL, NULL,
NULL, 0, &sizehints, NULL, NULL);
}
/* Set the window name */
XStoreName(xdpy_ , xwin_, win_name);
/* Gracefully handle Window Delete event from window manager */
Atom wmDelete = XInternAtom(xdpy_, "WM_DELETE_WINDOW", True);
XSetWMProtocols(xdpy_, xwin_, &wmDelete, 1);
return true;
}
void
CanvasX11::resize_no_viewport(int width, int height)
{
bool request_fullscreen = (width == -1 || height == -1);
/* Recreate an existing window only if it has actually been resized */
if (xwin_) {
if (width_ != width || height_ != height ||
fullscreen_ != request_fullscreen)
{
XDestroyWindow(xdpy_, xwin_);
xwin_ = 0;
}
else
{
return;
}
}
fullscreen_ = request_fullscreen;
if (fullscreen_) {
/* Get the screen (root window) size */
XWindowAttributes window_attr;
XGetWindowAttributes(xdpy_, RootWindow(xdpy_, DefaultScreen(xdpy_)),
&window_attr);
width_ = window_attr.width;
height_ = window_attr.height;
}
else {
width_ = width;
height_ = height;
}
if (!ensure_x_window())
Log::error("Error: Couldn't create X Window!\n");
if (color_renderbuffer_) {
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer_);
glRenderbufferStorage(GL_RENDERBUFFER, gl_color_format_,
width_, height_);
}
if (depth_renderbuffer_) {
glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer_);
glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format_,
width_, height_);
}
projection_ = LibMatrix::Mat4::perspective(60.0, width_ / static_cast<float>(height_),
1.0, 1024.0);
}
bool
CanvasX11::do_make_current()
{
if (!make_current())
return false;
if (offscreen_) {
if (!ensure_fbo())
return false;
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
}
return true;
}
bool
CanvasX11::ensure_gl_formats()
{
if (gl_color_format_ && gl_depth_format_)
return true;
GLVisualConfig vc;
get_glvisualconfig(vc);
gl_color_format_ = 0;
gl_depth_format_ = 0;
bool supports_rgba8(false);
bool supports_rgb8(false);
bool supports_depth24(false);
bool supports_depth32(false);
#if USE_GLESv2
if (GLExtensions::support("GL_ARM_rgba8"))
supports_rgba8 = true;
if (GLExtensions::support("GL_OES_rgb8_rgba8")) {
supports_rgba8 = true;
supports_rgb8 = true;
}
if (GLExtensions::support("GL_OES_depth24"))
supports_depth24 = true;
if (GLExtensions::support("GL_OES_depth32"))
supports_depth32 = true;
#elif USE_GL
supports_rgba8 = true;
supports_rgb8 = true;
supports_depth24 = true;
supports_depth32 = true;
#endif
if (vc.buffer == 32) {
if (supports_rgba8)
gl_color_format_ = GL_RGBA8;
else
gl_color_format_ = GL_RGBA4;
}
else if (vc.buffer == 24) {
if (supports_rgb8)
gl_color_format_ = GL_RGB8;
else
gl_color_format_ = GL_RGB565;
}
else if (vc.buffer == 16) {
if (vc.red == 4 && vc.green == 4 &&
vc.blue == 4 && vc.alpha == 4)
{
gl_color_format_ = GL_RGBA4;
}
else if (vc.red == 5 && vc.green == 5 &&
vc.blue == 5 && vc.alpha == 1)
{
gl_color_format_ = GL_RGB5_A1;
}
else if (vc.red == 5 && vc.green == 6 &&
vc.blue == 5 && vc.alpha == 0)
{
gl_color_format_ = GL_RGB565;
}
}
if (vc.depth == 32 && supports_depth32)
gl_depth_format_ = GL_DEPTH_COMPONENT32;
else if (vc.depth >= 24 && supports_depth24)
gl_depth_format_ = GL_DEPTH_COMPONENT24;
else if (vc.depth == 16)
gl_depth_format_ = GL_DEPTH_COMPONENT16;
Log::debug("Selected Renderbuffer ColorFormat: %s DepthFormat: %s\n",
get_gl_format_str(gl_color_format_),
get_gl_format_str(gl_depth_format_));
return (gl_color_format_ && gl_depth_format_);
}
bool
CanvasX11::ensure_fbo()
{
if (!fbo_) {
if (!ensure_gl_formats())
return false;
/* Create a texture for the color attachment */
glGenRenderbuffers(1, &color_renderbuffer_);
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer_);
glRenderbufferStorage(GL_RENDERBUFFER, gl_color_format_,
width_, height_);
/* Create a renderbuffer for the depth attachment */
glGenRenderbuffers(1, &depth_renderbuffer_);
glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer_);
glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format_,
width_, height_);
/* Create a FBO and set it up */
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, color_renderbuffer_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depth_renderbuffer_);
}
return true;
}
void
CanvasX11::release_fbo()
{
glDeleteFramebuffers(1, &fbo_);
glDeleteRenderbuffers(1, &color_renderbuffer_);
glDeleteRenderbuffers(1, &depth_renderbuffer_);
fbo_ = 0;
color_renderbuffer_ = 0;
depth_renderbuffer_ = 0;
gl_color_format_ = 0;
gl_depth_format_ = 0;
}
const char *
CanvasX11::get_gl_format_str(GLenum f)
{
const char *str;
switch(f) {
case GL_RGBA8: str = "GL_RGBA8"; break;
case GL_RGB8: str = "GL_RGB8"; break;
case GL_RGBA4: str = "GL_RGBA4"; break;
case GL_RGB5_A1: str = "GL_RGB5_A1"; break;
case GL_RGB565: str = "GL_RGB565"; break;
case GL_DEPTH_COMPONENT16: str = "GL_DEPTH_COMPONENT16"; break;
case GL_DEPTH_COMPONENT24: str = "GL_DEPTH_COMPONENT24"; break;
case GL_DEPTH_COMPONENT32: str = "GL_DEPTH_COMPONENT32"; break;
case GL_NONE: str = "GL_NONE"; break;
default: str = "Unknown"; break;
}
return str;
}