2010年8月22日 星期日

善用 DRI 加速貼圖

Standard
近日以來,筆者都在著手處理 Linux 下圖形運算的議題,不久前也曾發表一篇『善用 XShm 加速貼圖』以探討 X 架構之下的貼圖機制,舊文最後也提出範例,使用共享記憶體(Shared Memory)的方式,達到效能的改善。不過,透過減少資料複製,雖然能有效提升貼圖的效率,但都還只是停留在彌補 X 架構先天上的缺陷,令人不禁去想,是否有更直接的解決方案?尋思,現今硬體能力不可同日而語,幾乎每台電腦都具備著基本能力的影像晶片,若運用 DRI 直接對硬體貼圖應該是種可行的手段。

欲在 X 上透過 DRI 繪圖,最簡單的方法是使用 GLX Extension,藉由建立 OpenGL Window 開啟直接和硬體溝通的管道。我們可以修改與舊文相同的程式碼,並改用 OpenGL 來達成相同的效果:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>

int main(void)
{
    Window win;
    Display display = XOpenDisplay(getenv("DISPLAY"));
    unsigned char *buffer;
    GLint glxattr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 32, GLX_DOUBLEBUFFER, None };

    /* Initialize GLX */
    xvinfo = glXChooseVisual(display, 0, glxattr);
    if (xvinfo == NULL)
        exit(1);

    /* Initial Window */
    win = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)),
                                                        0, 0, 100, 100, 0,
                                                        BlackPixel(display, DefaultScreen(display)),
                                                        BlackPixel(display, DefaultScreen(display)));

    XMapWindow(display, win);

    /* Create context */
    glcontext = glXCreateContext(display, vinfo, NULL, GL_TRUE);
    if (glcontext == NULL)
        exit(0);

    glXMakeCurrent(display, win, glcontext);
    glEnable(GL_DEPTH_TEST);

    XSync(display, False);

    /* Allocate image memory for 100x100x32bits */
    buffer = (unsigned char *)malloc(sizeof(char) * 100 * 100 * 4);

    /* Create texture from framebuffer */
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &texture_id);
    glBindTexture(GL_TEXTURE_2D, texture_id);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 100, 100, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)buffer);

    /* Loop */
    while(1) {
        glViewport(0, 0, 100, 100);
        glClearColor(0., 0., 0., 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-1., 1., -1., 1., -1., 1.);

        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_QUADS);
            glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0, 0.0);
            glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0, 0.0);
            glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -1.0, 0.0);
            glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
        glEnd(); 

        glXSwapBuffers(display, win);
        usleep(1000);
    };

    /* Release */
    glXMakeCurrent(display, None, NULL);
    glXDestroyContext(display, glcontext);
    XCloseDisplay(display);
}

編譯 GLX 程式:
gcc -o glximage glximage.c -lX11 -lGL -lGLU