[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[E-devel] User-control of GL-context creation and draw/read buffers



Speaking of OpenGL, I'd like to repeat an earlier question which got lost
in the discussion (this mail here is mostly for the curious who wants a
different perspective on the usual discussion of opengl
support/interfacing in Evas):

If I'm not mistaken, the user is the one who creates an X drawable which
Evas uses, whereas the x11gl evas-engine will itself create the X context.
Isn't that odd? Should it not be so that the user also has control over
creating that X context (as well the buffers that x11gl renders to, e.g. a
PBuffer if desired).

My reason for asking is that I currently investigate a bit on the
possibility of using shared contexts and buffers. GLX offers the
possibility to have either Direct Rendering or Indirect Rendering, the
former being the one that everybody wants. However, there MAY be reasons
to wish for the latter.

In my case I consider whether I can have a "render-server" which can
deliver a signal to one of several "render-clients". I've made a simple
example of the two things. They use a shared PBuffer (I think all
processes can share PBuffers), a shared Context (if a Context uses Direct
Rendering, then it can't be shared at all, but if it uses Indirect
Rendering, then it can be shared with others using the import_context GLX
extension).

Compile like this:

gcc render_server.cpp -o render_server `ecore-config --libs --cflags`
`evas-config --libs --cflags` -lGL -lGLU -lstdc++
gcc render_client.cpp -o render_client `ecore-config --libs --cflags`
`evas-config --libs --cflags` -lGL -lGLU -lstdc++

Then run the render_server from one bash prompt and the render_client from
another.


What I'd like to point out with this is the need to control the ability to:

1) Choose to render to/from a PBuffer. Particularly I wouldn't mind a
speedy way of copying from a PBuffer to the screen (the functions
glxMakeContextCurrent takes both a Window/Pixmap/Pbuffer to use as draw
target and also a Window/Pixmap/Pbuffer to read from.

2) Choose whether or not I'd like to use Indirect Rendering

3) Choose whether or not I'd like to share display lists and textures with
other contexts (the 4th parameter to glXCreateNewContext).

Regards Rene Jensen



------ render_server.cpp -----------------------

#include <iostream>
#include <fstream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <GL/glx.h>
#include <GL/glxext.h>
#include "render_utils.h"

struct Vertex
{
    float tu, tv;
    float x, y, z;
};
Vertex g_cubeVertices[] =
{
    { 0.0f,0.0f, -1.0f,-1.0f, 1.0f },
    { 1.0f,0.0f,  1.0f,-1.0f, 1.0f },
    { 1.0f,1.0f,  1.0f, 1.0f, 1.0f },
    { 0.0f,1.0f, -1.0f, 1.0f, 1.0f },
    { 1.0f,0.0f, -1.0f,-1.0f,-1.0f },
    { 1.0f,1.0f, -1.0f, 1.0f,-1.0f },
    { 0.0f,1.0f,  1.0f, 1.0f,-1.0f },
    { 0.0f,0.0f,  1.0f,-1.0f,-1.0f },
    { 0.0f,1.0f, -1.0f, 1.0f,-1.0f },
    { 0.0f,0.0f, -1.0f, 1.0f, 1.0f },
    { 1.0f,0.0f,  1.0f, 1.0f, 1.0f },
    { 1.0f,1.0f,  1.0f, 1.0f,-1.0f },
    { 1.0f,1.0f, -1.0f,-1.0f,-1.0f },
    { 0.0f,1.0f,  1.0f,-1.0f,-1.0f },
    { 0.0f,0.0f,  1.0f,-1.0f, 1.0f },
    { 1.0f,0.0f, -1.0f,-1.0f, 1.0f },
    { 1.0f,0.0f,  1.0f,-1.0f,-1.0f },
    { 1.0f,1.0f,  1.0f, 1.0f,-1.0f },
    { 0.0f,1.0f,  1.0f, 1.0f, 1.0f },
    { 0.0f,0.0f,  1.0f,-1.0f, 1.0f },
    { 0.0f,0.0f, -1.0f,-1.0f,-1.0f },
    { 1.0f,0.0f, -1.0f,-1.0f, 1.0f },
    { 1.0f,1.0f, -1.0f, 1.0f, 1.0f },
    { 0.0f,1.0f, -1.0f, 1.0f,-1.0f }
};


class RenderServer
{
public:
    Display*            display;
    GLXPbuffer            pbuffer;
    GLXContext            pbufferContext;
    XID                    pbufferContextId;

    void init ()
    {
        // Open the X display
        display = XOpenDisplay(0);
        if (! display)
            throw "Can't open X display";

        // Print FBConfig attributes
        glx::FBConfig* myConfig = new glx::FBConfig(display);
        myConfig->get_attributes();
        std::cout << "Server FBConfig XID:   "
                  << myConfig->fbconfig_id << std::endl;

        int pbufAttrib[] =
        {
            GLX_PBUFFER_WIDTH, PBUFFER_WIDTH,
            GLX_PBUFFER_HEIGHT, PBUFFER_HEIGHT,
            GLX_LARGEST_PBUFFER, False,
            None
        };
        pbuffer = glXCreatePbuffer (
            display, myConfig->getFBConfig(0), pbufAttrib);
        XVisualInfo* visinfo = glXGetVisualFromFBConfig(
            display, myConfig->getFBConfig(0) );
        if (! visinfo)
            throw "Error: init - Couldn't get visual";
        std::cout << "Server PBuffer XID:   " << pbuffer << std::endl;

        pbufferContext = glXCreateNewContext(
            display, myConfig->getFBConfig(0),GLX_RGBA_TYPE,NULL, GL_FALSE);
        if (! pbufferContext)
            throw "Can't make pbuffer context";

        pbufferContextId = glXGetContextIDEXT (pbufferContext);
        std::cout << "Server Context XID:   "<<pbufferContextId<<std::endl;

        XFree( visinfo );
    }

    void render ()
    {
        if (! glXMakeContextCurrent(display,pbuffer,pbuffer,pbufferContext))
            throw "Can't make context current";
        static float time = 20.0;
        time += 0.1;

        glClearColor( 0,0,0, 1.0f );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 45.0f,PBUFFER_WIDTH / PBUFFER_HEIGHT, 0.1f, 10.0f );

        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
        glTranslatef( 0.0f, 0.0f, -5.0f );
        glRotatef( time, 1.0f, 0.0f, 0.0f );
        glRotatef( time, 0.0f, 1.0f, 0.0f );

        glInterleavedArrays( GL_T2F_V3F, 0, g_cubeVertices );
        glDrawArrays( GL_QUADS, 0, 24 );

        glFlush();
    }
};



RenderServer* renderServer;



int main(int argc, const char** argv)
{
    glx::init_extensions();
    renderServer = new RenderServer();
    renderServer->init();

    while (true)
    {
        renderServer->render();
        usleep (30 * 1000);
        // std::cout << "Server: Making frame" << std::endl;
    }


    return 0;
}



------ render_client.cpp -----------------------

#include <iostream>
#include <fstream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>

#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Evas.h>

#include "render_utils.h"



class RenderClient
{
public:
    unsigned long*        data;
    Display*            display;
    XID                    serverConfig;
    XID                    serverBuffer;
    XID                    serverContextXid;
    GLXContext            serverContext;
    GLXContext            clientContext;

    void init()
    {
        std::cout << "Enter XID of the server's FBConfig: ";
        std::cin >> serverConfig;
        std::cout << "Enter XID of the server's Pbuffer: ";
        std::cin >> serverBuffer;
        std::cout << "Enter XID of the server's Context: ";
        std::cin >> serverContextXid;

        // Open the X display
        display = XOpenDisplay(0);
        if (! display)
            throw "Can't open X display";

        glx::FBConfig* myConfig = new glx::FBConfig(display, serverConfig);


        if (true)
        {
            serverContext = glXImportContextEXT (display, serverContextXid);

            // Create context, sharing the buffer with the server
            clientContext = glXCreateNewContext(display,
                myConfig->getFBConfig(0), GLX_RGBA_TYPE,
                serverContext, GL_FALSE);
        }
        else
        {
            // Create context by making a shared one
            // using the import_context extension
        }

        if (! clientContext)
            throw "Client can't make pbuffer context";
        std::cout << "Client Context XID:   " << clientContext << std::endl;

        data = new unsigned long[PBUFFER_WIDTH*PBUFFER_HEIGHT];
    }

    void render ()
    {
        if (! glXMakeContextCurrent(
            display, serverBuffer, serverBuffer, clientContext))
            throw "Client can't make context current";
        glReadPixels (0,0, PBUFFER_WIDTH, PBUFFER_HEIGHT,
            GL_BGRA, GL_UNSIGNED_BYTE, data);
    }
};




Ecore_Evas *ee;
Evas *evas;
Evas_Object *bg;
RenderClient* renderClient;


int timer_cb (void *data)
{
    renderClient->render();
    evas_object_image_data_set ((Evas_Object*)bg, renderClient->data);
    evas_object_image_data_update_add (bg,0,0,PBUFFER_WIDTH,PBUFFER_HEIGHT);

    return 1;
}

int main(int argc, const char** argv)
{
    glx::init_extensions();

    renderClient = new RenderClient();
    renderClient->init();

    ecore_init();
    ecore_app_args_set(argc, argv);
    if (!ecore_evas_init()) return -1;
    ee = ecore_evas_software_x11_new(
        NULL,0,0,0,PBUFFER_WIDTH,PBUFFER_HEIGHT);
    if (!ee) return -1;

    ecore_evas_title_set(ee, "EE Test");
    ecore_evas_show(ee);
    evas = ecore_evas_get(ee);

    bg = evas_object_image_add(evas);
    evas_object_image_size_set (bg, PBUFFER_WIDTH, PBUFFER_HEIGHT);
    evas_object_move(bg, 0, 0);
    evas_object_resize(bg, PBUFFER_WIDTH, PBUFFER_HEIGHT);
    evas_object_layer_set(bg, 0);
    evas_object_color_set(bg, 255, 255, 220, 255);
    evas_object_show(bg);
    evas_object_image_data_set (bg, renderClient->data);
    evas_object_image_size_set (bg, PBUFFER_WIDTH, PBUFFER_HEIGHT);
    evas_object_image_fill_set (bg, 0,0, PBUFFER_WIDTH, PBUFFER_HEIGHT);
    ecore_timer_add(0.01, timer_cb, bg);

    ecore_main_loop_begin();

    return 0;
}

------ render_utils.cpp -----------------------

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>

static const int    PBUFFER_WIDTH = 512;
static const int    PBUFFER_HEIGHT = 512;

PFNGLXGETCONTEXTIDEXTPROC glXGetContextIDEXT;
PFNGLXIMPORTCONTEXTEXTPROC glXImportContextEXT;

namespace glx
{
    void init_extensions()
    {
        glXGetContextIDEXT  = (PFNGLXGETCONTEXTIDEXTPROC)
            glXGetProcAddress ((const GLubyte*) "glXGetContextIDEXT");
        glXImportContextEXT = (PFNGLXIMPORTCONTEXTEXTPROC)
            glXGetProcAddress ((const GLubyte*) "glXImportContextEXT");
        std::cout << "func1: " << (void*) glXGetContextIDEXT << std::endl;
        std::cout << "func2: " << (void*) glXImportContextEXT << std::endl;
    }

    class FBConfig
    {
        Display*        display;
        GLXFBConfig *    fbconfig;
        int                selectedFBConfig;

    public:
        int                fbconfig_id;

        FBConfig (Display* _display, XID configXid)
        {
            display = _display;

            selectedFBConfig = 0;

            int nItems;
            int attrib[] =
            {
                GLX_FBCONFIG_ID,  configXid,
                None
            };

            fbconfig = glXChooseFBConfig (display, DefaultScreen(display),
                attrib, &nItems);

            if (fbconfig == 0)
                throw "Can't choose fbconfig using given XID";
        }

        FBConfig (Display* _display)
        {
            display = _display;

            selectedFBConfig = 0;

            int nItems;
            int attrib[] =
            {
                GLX_DOUBLEBUFFER,  False,
                GLX_RED_SIZE,      1,
                GLX_GREEN_SIZE,    1,
                GLX_BLUE_SIZE,     1,
                GLX_DEPTH_SIZE,    1,
                GLX_RENDER_TYPE,   GLX_RGBA_BIT,
                GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT,
                None
            };

            fbconfig = glXChooseFBConfig (display, DefaultScreen(display),
                attrib, &nItems);

            if (fbconfig == 0)
                throw "Can't choose fbconfig";
        }

        ~FBConfig()
        {
            XFree( fbconfig );
        }

        GLXFBConfig getFBConfig(int n)
        {
            return fbconfig[n];
        }

        void get_attributes ()
        {
            glXGetFBConfigAttrib (display, fbconfig[selectedFBConfig],
                GLX_FBCONFIG_ID, &fbconfig_id);
        }
    };
}