=== modified file 'doc/glmark2.1.in'
@@ -21,15 +21,17 @@
Run a quick output validation test instead of
running the benchmarks
.TP
-\fB\-\-no\-swap\-buffers\fR
-Don't update the screen by swapping the front and
-back buffer, use glFinish() instead
+\fB\-\-frame-end\fR METHOD
+How to end a frame [default,none,swap,finish,readpixels]
+.TP
+\fB\-\-off-screen\fR
+Render to an off-screen surface
.TP
\fB\-\-reuse\-context\fR
Use a single context for all scenes
(by default, each scene gets its own context)
.TP
-\fB\-s\fR, \fB\-\-size WxH\fR
+\fB\-s\fR, \fB\-\-size\fR WxH
Size of the output window (default: 800x600)
.TP
\fB\-l\fR, \fB\-\-list\-scenes\fR
=== modified file 'src/canvas-x11-egl.cpp'
@@ -86,6 +86,20 @@
return true;
}
+void
+CanvasX11EGL::get_glvisualinfo(GLVisualInfo &gl_visinfo)
+{
+ if (!ensure_egl_config())
+ return;
+
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_BUFFER_SIZE, &gl_visinfo.buffer_size);
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_RED_SIZE, &gl_visinfo.red_size);
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_GREEN_SIZE, &gl_visinfo.green_size);
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_BLUE_SIZE, &gl_visinfo.blue_size);
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_ALPHA_SIZE, &gl_visinfo.alpha_size);
+ eglGetConfigAttrib(egl_display_, egl_config_, EGL_DEPTH_SIZE, &gl_visinfo.depth_size);
+}
+
/*******************
* Private methods *
*******************/
@@ -256,18 +270,7 @@
CanvasX11EGL::init_gl_extensions()
{
#if USE_GLESv2
- /*
- * Parse the extensions we care about from the extension string.
- * Don't even bother to get function pointers until we know the
- * extension is present.
- */
- std::string extString;
- const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
- if (exts) {
- extString = exts;
- }
-
- if (extString.find("GL_OES_mapbuffer") != std::string::npos) {
+ if (GLExtensions::support("GL_OES_mapbuffer")) {
GLExtensions::MapBuffer =
reinterpret_cast<PFNGLMAPBUFFEROESPROC>(eglGetProcAddress("glMapBufferOES"));
GLExtensions::UnmapBuffer =
=== modified file 'src/canvas-x11-egl.h'
@@ -43,6 +43,7 @@
bool make_current();
bool reset_context();
void swap_buffers() { eglSwapBuffers(egl_display_, egl_surface_); }
+ void get_glvisualinfo(GLVisualInfo &gl_visinfo);
private:
bool ensure_egl_display();
=== modified file 'src/canvas-x11-glx.cpp'
@@ -68,6 +68,19 @@
return true;
}
+void
+CanvasX11GLX::get_glvisualinfo(GLVisualInfo &gl_visinfo)
+{
+ if (!ensure_glx_fbconfig())
+ return;
+
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_BUFFER_SIZE, &gl_visinfo.buffer_size);
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_RED_SIZE, &gl_visinfo.red_size);
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_GREEN_SIZE, &gl_visinfo.green_size);
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_BLUE_SIZE, &gl_visinfo.blue_size);
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_ALPHA_SIZE, &gl_visinfo.alpha_size);
+ glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_DEPTH_SIZE, &gl_visinfo.depth_size);
+}
/*******************
* Private methods *
=== modified file 'src/canvas-x11-glx.h'
@@ -43,6 +43,7 @@
bool make_current();
bool reset_context();
void swap_buffers() { glXSwapBuffers(xdpy_, xwin_); }
+ void get_glvisualinfo(GLVisualInfo &gl_visinfo);
private:
bool check_glx_version();
=== modified file 'src/canvas-x11.cpp'
@@ -35,10 +35,12 @@
bool
CanvasX11::reset()
{
+ release_fbo();
+
if (!reset_context())
return false;
- if (!make_current())
+ if (!do_make_current())
return false;
if (!supports_gl2()) {
@@ -78,7 +80,7 @@
void
CanvasX11::visible(bool visible)
{
- if (visible)
+ if (visible && !offscreen_)
XMapWindow(xdpy_, xwin_);
}
@@ -97,16 +99,35 @@
void
CanvasX11::update()
{
- if (Options::swap_buffers)
- swap_buffers();
- else
- glFinish();
+ 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()
{
- make_current();
+ do_make_current();
std::stringstream ss;
@@ -173,6 +194,12 @@
glViewport(0, 0, width_, height_);
}
+unsigned int
+CanvasX11::fbo()
+{
+ return fbo_;
+}
+
bool
CanvasX11::supports_gl2()
{
@@ -285,7 +312,183 @@
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;
+
+ GLVisualInfo gl_visinfo;
+ get_glvisualinfo(gl_visinfo);
+
+ 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 (gl_visinfo.buffer_size == 32) {
+ if (supports_rgba8)
+ gl_color_format_ = GL_RGBA8;
+ else
+ gl_color_format_ = GL_RGBA4;
+ }
+ else if (gl_visinfo.buffer_size == 24) {
+ if (supports_rgb8)
+ gl_color_format_ = GL_RGB8;
+ else
+ gl_color_format_ = GL_RGB565;
+ }
+ else if (gl_visinfo.buffer_size == 16) {
+ if (gl_visinfo.red_size == 4 && gl_visinfo.green_size == 4 &&
+ gl_visinfo.blue_size == 4 && gl_visinfo.alpha_size == 4)
+ {
+ gl_color_format_ = GL_RGBA4;
+ }
+ else if (gl_visinfo.red_size == 5 && gl_visinfo.green_size == 5 &&
+ gl_visinfo.blue_size == 5 && gl_visinfo.alpha_size == 1)
+ {
+ gl_color_format_ = GL_RGB5_A1;
+ }
+ else if (gl_visinfo.red_size == 5 && gl_visinfo.green_size == 6 &&
+ gl_visinfo.blue_size == 5 && gl_visinfo.alpha_size == 0)
+ {
+ gl_color_format_ = GL_RGB565;
+ }
+ }
+
+ if (gl_visinfo.depth_size == 32 && supports_depth32)
+ gl_depth_format_ = GL_DEPTH_COMPONENT32;
+ else if (gl_visinfo.depth_size >= 24 && supports_depth24)
+ gl_depth_format_ = GL_DEPTH_COMPONENT24;
+ else if (gl_visinfo.depth_size == 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;
+}
+
=== modified file 'src/canvas-x11.h'
@@ -44,10 +44,25 @@
virtual void write_to_file(std::string &filename);
virtual bool should_quit();
virtual void resize(int width, int height);
+ virtual unsigned int fbo();
protected:
CanvasX11(int width, int height) :
- Canvas(width, height), xwin_(0), xdpy_(0) {}
+ Canvas(width, height), xwin_(0), xdpy_(0),
+ gl_color_format_(0), gl_depth_format_(0),
+ color_renderbuffer_(0), depth_renderbuffer_(0), fbo_(0) {}
+
+ /**
+ * Information about a GL visual.
+ */
+ struct GLVisualInfo {
+ int buffer_size;
+ int red_size;
+ int green_size;
+ int blue_size;
+ int alpha_size;
+ int depth_size;
+ };
/**
* Gets the XVisualInfo to use for creating the X window with.
@@ -88,6 +103,13 @@
virtual void swap_buffers() = 0;
/**
+ * Gets information about the GL visual used for this canvas.
+ *
+ * This method should be implemented in derived classes.
+ */
+ virtual void get_glvisualinfo(GLVisualInfo &gl_visinfo) = 0;
+
+ /**
* Whether the current implementation supports GL(ES) 2.0.
*
* @return true if it supports GL(ES) 2.0, false otherwise
@@ -103,6 +125,18 @@
private:
void resize_no_viewport(int width, int height);
bool ensure_x_window();
+ bool do_make_current();
+ bool ensure_gl_formats();
+ bool ensure_fbo();
+ void release_fbo();
+
+ const char *get_gl_format_str(GLenum f);
+
+ GLenum gl_color_format_;
+ GLenum gl_depth_format_;
+ GLuint color_renderbuffer_;
+ GLuint depth_renderbuffer_;
+ GLuint fbo_;
};
#endif
=== modified file 'src/canvas.h'
@@ -191,6 +191,13 @@
virtual void resize(int width, int height) { static_cast<void>(width); static_cast<void>(height); }
/**
+ * Gets the FBO associated with the canvas.
+ *
+ * @return the FBO
+ */
+ virtual unsigned int fbo() { return 0; }
+
+ /**
* Gets a dummy canvas object.
*
* @return the dummy canvas
@@ -224,12 +231,21 @@
*/
const LibMatrix::mat4 &projection() { return projection_; }
+ /**
+ * Sets whether the canvas should be backed by an off-screen surface.
+ *
+ * This takes effect after the next init()/reset().
+ */
+ void offscreen(bool offscreen) { offscreen_ = offscreen; }
+
protected:
- Canvas(int width, int height) : width_(width), height_(height) {}
+ Canvas(int width, int height) :
+ width_(width), height_(height), offscreen_(false) {}
int width_;
int height_;
LibMatrix::mat4 projection_;
+ bool offscreen_;
};
#endif
=== modified file 'src/gl-headers.cpp'
@@ -24,3 +24,23 @@
void* (*GLExtensions::MapBuffer) (GLenum target, GLenum access) = 0;
GLboolean (*GLExtensions::UnmapBuffer) (GLenum target) = 0;
+bool
+GLExtensions::support(const std::string &ext)
+{
+ std::string ext_string;
+ const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+ if (exts) {
+ ext_string = exts;
+ }
+
+ const size_t ext_size = ext.size();
+ size_t pos = 0;
+
+ while ((pos = ext_string.find(ext, pos)) != std::string::npos) {
+ char c = ext_string[pos + ext_size];
+ if (c == ' ' || c == '\0')
+ break;
+ }
+
+ return pos != std::string::npos;
+}
=== modified file 'src/gl-headers.h'
@@ -27,19 +27,43 @@
#if USE_GL
#include <GL/gl.h>
#include <GL/glext.h>
+#ifndef GL_RGB565
+#define GL_RGB565 0x8D62
+#endif
#elif USE_GLESv2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#ifndef GL_WRITE_ONLY
#define GL_WRITE_ONLY GL_WRITE_ONLY_OES
#endif
-#endif
+#ifndef GL_DEPTH_COMPONENT24
+#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
+#endif
+#ifndef GL_DEPTH_COMPONENT32
+#define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES
+#endif
+#ifndef GL_RGBA8
+#define GL_RGBA8 GL_RGBA8_OES
+#endif
+#ifndef GL_RGB8
+#define GL_RGB8 GL_RGB8_OES
+#endif
+#endif
+
+#include <string>
/**
* Struct that holds pointers to functions that belong to extensions
* in either GL2.0 or GLES2.0.
*/
struct GLExtensions {
+ /**
+ * Whether the current context has support for a GL extension.
+ *
+ * @return true if the extension is supported
+ */
+ static bool support(const std::string &ext);
+
static void* (*MapBuffer) (GLenum target, GLenum access);
static GLboolean (*UnmapBuffer) (GLenum target);
};
=== modified file 'src/main.cpp'
@@ -165,6 +165,8 @@
CanvasX11EGL canvas(Options::size.first, Options::size.second);
#endif
+ canvas.offscreen(Options::offscreen);
+
vector<Scene*> scenes;
// Register the scenes, so they can be looked up by name
=== modified file 'src/options.cpp'
@@ -33,7 +33,7 @@
std::vector<std::string> Options::benchmarks;
std::vector<std::string> Options::benchmark_files;
bool Options::validate = false;
-bool Options::swap_buffers = true;
+Options::FrameEnd Options::frame_end = Options::FrameEndDefault;
std::pair<int,int> Options::size(800, 600);
bool Options::list_scenes = false;
bool Options::show_all_options = false;
@@ -42,13 +42,15 @@
bool Options::reuse_context = false;
bool Options::run_forever = false;
bool Options::annotate = false;
+bool Options::offscreen = false;
static struct option long_options[] = {
{"annotate", 0, 0, 0},
{"benchmark", 1, 0, 0},
{"benchmark-file", 1, 0, 0},
{"validate", 0, 0, 0},
- {"no-swap-buffers", 0, 0, 0},
+ {"frame-end", 1, 0, 0},
+ {"off-screen", 0, 0, 0},
{"reuse-context", 0, 0, 0},
{"run-forever", 0, 0, 0},
{"size", 1, 0, 0},
@@ -83,6 +85,31 @@
size.second = size.first;
}
+/**
+ * Parses a frame-end method string
+ *
+ * @param str the string to parse
+ *
+ * @return the parsed frame end method
+ */
+static Options::FrameEnd
+frame_end_from_str(const std::string &str)
+{
+ Options::FrameEnd m = Options::FrameEndDefault;
+
+ if (str == "swap")
+ m = Options::FrameEndSwap;
+ else if (str == "finish")
+ m = Options::FrameEndFinish;
+ else if (str == "readpixels")
+ m = Options::FrameEndReadPixels;
+ else if (str == "none")
+ m = Options::FrameEndNone;
+
+ return m;
+}
+
+
void
Options::print_help()
{
@@ -96,8 +123,8 @@
" (the option can be used multiple times)\n"
" --validate Run a quick output validation test instead of \n"
" running the benchmarks\n"
- " --no-swap-buffers Don't update the canvas by swapping the front and\n"
- " back buffer, use glFinish() instead\n"
+ " --frame-end METHOD How to end a frame [default,none,swap,finish,readpixels]\n"
+ " --off-screen Render to an off-screen surface\n"
" --reuse-context Use a single context for all scenes\n"
" (by default, each scene gets its own context)\n"
" -s, --size WxH Size of the output window (default: 800x600)\n"
@@ -139,8 +166,10 @@
Options::benchmark_files.push_back(optarg);
else if (!strcmp(optname, "validate"))
Options::validate = true;
- else if (!strcmp(optname, "no-swap-buffers"))
- Options::swap_buffers = false;
+ else if (!strcmp(optname, "frame-end"))
+ Options::frame_end = frame_end_from_str(optarg);
+ else if (!strcmp(optname, "off-screen"))
+ Options::offscreen = true;
else if (!strcmp(optname, "reuse-context"))
Options::reuse_context = true;
else if (c == 's' || !strcmp(optname, "size"))
=== modified file 'src/options.h'
@@ -28,13 +28,21 @@
#include <vector>
struct Options {
+ enum FrameEnd {
+ FrameEndDefault,
+ FrameEndNone,
+ FrameEndSwap,
+ FrameEndFinish,
+ FrameEndReadPixels
+ };
+
static bool parse_args(int argc, char **argv);
static void print_help();
static std::vector<std::string> benchmarks;
static std::vector<std::string> benchmark_files;
static bool validate;
- static bool swap_buffers;
+ static FrameEnd frame_end;
static std::pair<int,int> size;
static bool list_scenes;
static bool show_all_options;
@@ -43,6 +51,7 @@
static bool reuse_context;
static bool run_forever;
static bool annotate;
+ static bool offscreen;
};
#endif /* OPTIONS_H_ */
=== modified file 'src/scene-desktop.cpp'
@@ -356,6 +356,7 @@
class RenderScreen : public RenderObject
{
public:
+ RenderScreen(Canvas &canvas) { fbo_ = canvas.fbo(); }
virtual void init() {}
virtual void release() {}
};
@@ -736,8 +737,8 @@
RenderClearImage desktop;
std::vector<RenderObject *> windows;
- SceneDesktopPrivate() :
- desktop(GLMARK_DATA_PATH"/textures/effect-2d.png") {}
+ SceneDesktopPrivate(Canvas &canvas) :
+ screen(canvas), desktop(GLMARK_DATA_PATH"/textures/effect-2d.png") {}
~SceneDesktopPrivate() { Util::dispose_pointer_vector(windows); }
@@ -747,7 +748,7 @@
SceneDesktop::SceneDesktop(Canvas &canvas) :
Scene(canvas, "desktop")
{
- priv_ = new SceneDesktopPrivate();
+ priv_ = new SceneDesktopPrivate(canvas);
options_["effect"] = Scene::Option("effect", "blur",
"the effect to use [blur]");
options_["windows"] = Scene::Option("windows", "4",