Initial import
[glmemperf] / cpuinterleavingtest.cpp
diff --git a/cpuinterleavingtest.cpp b/cpuinterleavingtest.cpp
new file mode 100644 (file)
index 0000000..16c64d3
--- /dev/null
@@ -0,0 +1,314 @@
+/**
+ * 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);
+}