--- /dev/null
+/**
+ * OpenGL ES 2.0 memory performance estimator
+ * Copyright (C) 2009 Nokia
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * \author Sami Kyöstilä <sami.kyostila@nokia.com>
+ *
+ * CPU texture streaming test
+ */
+#include "cpuinterleavingtest.h"
+#include "util.h"
+#include "native.h"
+#include <sstream>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+template <typename TYPE>
+void fillTexture(TYPE* pixels, int width, int height, int stride, int frame)
+{
+ TYPE color = (TYPE)0xffffffffu;
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ if ((x + y + frame) & 0x10)
+ {
+ pixels[x] = color;
+ }
+ else
+ {
+ pixels[x] = 0;
+ }
+ }
+ pixels += stride / sizeof(TYPE);
+ }
+}
+
+CPUInterleavingTest::CPUInterleavingTest(CPUInterleavingMethod method,
+ int buffers, int bitsPerPixel,
+ int width, int height,
+ float texW, float texH):
+ BlitTest(width, height, false, texW, texH),
+ m_method(method),
+ m_buffers(buffers),
+ m_dataBitsPerPixel(bitsPerPixel),
+ m_readBuffer(buffers - 1),
+ m_writeBuffer(0)
+{
+}
+
+
+void CPUInterleavingTest::prepare()
+{
+ int i;
+ bool success;
+
+ BlitTest::prepare();
+
+ glGenTextures(m_buffers, m_textures);
+ for (int i = 0; i < m_buffers; i++)
+ {
+ glBindTexture(GL_TEXTURE_2D, m_textures[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+
+ ASSERT_GL();
+
+ switch (m_method)
+ {
+ case CPUI_TEXTURE_UPLOAD:
+ {
+ m_dataStride = m_width * m_dataBitsPerPixel / 8;
+ for (i = 0; i < m_buffers; i++)
+ {
+ m_textureData[i] = new char[m_height * m_dataStride];
+ }
+ }
+ break;
+ case CPUI_XSHM_IMAGE:
+ {
+ Status shmSupported = XShmQueryExtension(ctx.nativeDisplay);
+ if (!shmSupported)
+ {
+ fail("X11 shared memory extension not supported");
+ }
+
+ m_completionEvent = XShmGetEventBase(ctx.nativeDisplay) + ShmCompletion;
+
+ const EGLint pixmapConfigAttrs[] =
+ {
+ EGL_BUFFER_SIZE, m_dataBitsPerPixel,
+ EGL_NONE
+ };
+ EGLint configCount = 0;
+
+ eglChooseConfig(ctx.dpy, pixmapConfigAttrs, &m_config, 1, &configCount);
+ assert(configCount);
+
+ for (i = 0; i < m_buffers; i++)
+ {
+ success = nativeCreatePixmap(ctx.nativeDisplay, ctx.dpy,
+ m_config, m_width, m_height, &m_pixmaps[i]);
+ assert(success);
+
+ XGCValues gcValues;
+ m_gc[i] = XCreateGC(ctx.nativeDisplay, m_pixmaps[i], 0, &gcValues);
+
+ const EGLint surfAttrs[] =
+ {
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_MIPMAP_TEXTURE, EGL_FALSE,
+ EGL_NONE
+ };
+
+ m_surfaces[i] = eglCreatePixmapSurface(ctx.dpy, m_config, m_pixmaps[i], surfAttrs);
+ assert(m_surfaces[i] != EGL_NO_SURFACE);
+
+ glBindTexture(GL_TEXTURE_2D, m_textures[i]);
+ success = eglBindTexImage(ctx.dpy, m_surfaces[i], EGL_BACK_BUFFER);
+ assert(success);
+
+ XVisualInfo visualInfo;
+ XVisualInfo* visual;
+ int visualCount = 0;
+ visualInfo.depth = m_dataBitsPerPixel;
+ visualInfo.screen = DefaultScreen(ctx.nativeDisplay);
+ visual = XGetVisualInfo(ctx.nativeDisplay, VisualDepthMask | VisualScreenMask,
+ &visualInfo, &visualCount);
+
+ assert(visualCount > 0);
+
+ m_ximage[i] = XShmCreateImage(ctx.nativeDisplay, visual->visual, m_dataBitsPerPixel,
+ ZPixmap, NULL,
+ &m_shminfo[i], m_width, m_height);
+ m_shminfo[i].shmid = shmget(IPC_PRIVATE,
+ m_ximage[i]->bytes_per_line *
+ m_ximage[i]->height, IPC_CREAT | 0777);
+ m_shminfo[i].shmaddr = m_ximage[i]->data = (char*)shmat(m_shminfo[i].shmid, 0, 0);
+ assert(m_shminfo[i].shmaddr);
+ m_shminfo[i].readOnly = False;
+ Status status = XShmAttach(ctx.nativeDisplay, &m_shminfo[i]);
+ assert(status);
+
+ m_textureData[i] = m_ximage[i]->data;
+ m_dataStride = m_ximage[i]->bytes_per_line;
+ m_writeCompleted[i] = true;
+ m_drawableIndex[m_pixmaps[i]] = i;
+ }
+ }
+ break;
+ default:
+ assert(0);
+ return;
+ }
+}
+
+void CPUInterleavingTest::teardown()
+{
+ int i;
+ glDeleteTextures(m_buffers, m_textures);
+
+ switch (m_method)
+ {
+ case CPUI_TEXTURE_UPLOAD:
+ {
+ for (i = 0; i < m_buffers; i++)
+ {
+ delete[] m_textureData[i];
+ }
+ }
+ break;
+ case CPUI_XSHM_IMAGE:
+ {
+ for (i = 0; i < m_buffers; i++)
+ {
+ XShmDetach(ctx.nativeDisplay, &m_shminfo[i]);
+ XDestroyImage(m_ximage[i]);
+ shmdt(m_shminfo[i].shmaddr);
+ shmctl(m_shminfo[i].shmid, IPC_RMID, 0);
+
+ eglReleaseTexImage(ctx.dpy, m_surfaces[i], EGL_BACK_BUFFER);
+ eglDestroySurface(ctx.dpy, m_surfaces[i]);
+ nativeDestroyPixmap(ctx.nativeDisplay, m_pixmaps[i]);
+ XFreeGC(ctx.nativeDisplay, m_gc[i]);
+ }
+ }
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ BlitTest::teardown();
+}
+
+std::string CPUInterleavingTest::name() const
+{
+ std::stringstream s;
+
+ s << "blit_cpu_";
+
+ switch (m_method)
+ {
+ case CPUI_TEXTURE_UPLOAD:
+ s << "texupload";
+ break;
+ case CPUI_XSHM_IMAGE:
+ s << "shmimage";
+ break;
+ case CPUI_IMG_TEXTURE_STREAMING:
+ s << "texstream";
+ break;
+ case CPUI_PIXEL_BUFFER_OBJECT:
+ s << "pbo";
+ break;
+ case CPUI_EGL_LOCK_SURFACE:
+ s << "locksurf";
+ break;
+ }
+
+ switch (m_dataBitsPerPixel)
+ {
+ case 16:
+ s << "_16bpp";
+ break;
+ case 32:
+ s << "_32bpp";
+ break;
+ }
+
+ s << "_" << m_buffers << "x" << m_width << "x" << m_height;
+
+ return s.str();
+}
+
+void CPUInterleavingTest::operator()(int frame)
+{
+ switch (m_dataBitsPerPixel)
+ {
+ case 16:
+ fillTexture(reinterpret_cast<uint16_t*>(m_textureData[m_writeBuffer]),
+ m_width, m_height, m_dataStride, frame);
+ break;
+ case 32:
+ fillTexture(reinterpret_cast<uint32_t*>(m_textureData[m_writeBuffer]),
+ m_width, m_height, m_dataStride, frame);
+ break;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, m_textures[m_writeBuffer]);
+
+ switch (m_method)
+ {
+ case CPUI_TEXTURE_UPLOAD:
+ if (m_dataBitsPerPixel == 32)
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, m_textureData[m_writeBuffer]);
+ }
+ else
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_textureData[m_writeBuffer]);
+ }
+ break;
+ case CPUI_XSHM_IMAGE:
+ {
+ // Wait for the completion event for this buffer
+ while (XEventsQueued(ctx.nativeDisplay, QueuedAfterReading) > 0 ||
+ !m_writeCompleted[m_writeBuffer])
+ {
+ XEvent event;
+ XNextEvent(ctx.nativeDisplay, &event);
+ if (event.type == m_completionEvent)
+ {
+ XShmCompletionEvent* e = reinterpret_cast<XShmCompletionEvent*>(&event);
+ int i = m_drawableIndex[e->drawable];
+ m_writeCompleted[i] = true;
+ }
+ }
+ XShmPutImage(ctx.nativeDisplay, m_pixmaps[m_writeBuffer], m_gc[m_writeBuffer],
+ m_ximage[m_writeBuffer], 0, 0, 0, 0, m_width, m_height, True);
+ m_writeCompleted[m_writeBuffer] = false;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, m_textures[m_readBuffer]);
+ m_writeBuffer = (m_writeBuffer + 1) % m_buffers;
+ m_readBuffer = (m_readBuffer + 1) % m_buffers;
+
+ BlitTest::operator()(frame);
+}