2016-03-29 5 views
1

Ma question est de savoir comment accélérer le dessin sur OpenGL sous Windows.Comment accélérer le dessin avec Cairo sur les fenêtres Opengl?

Le code de test est ci-dessous. Je l'ai copié à partir d'un exemple de cairo sur le web.

les fps tombent à 30 à 40 par seconde, encore plus lentement qu'un navigateur Web. Il suffit de dessiner la ligne chaque image, j'ai essayé d'écrire javascript sur html5. La même fonction dessine juste une ligne, et elle court beaucoup plus vite.

Pourquoi le caire dessine-t-il une ligne si ouverte? Est-ce que j'ai fait quelque chose de mal? et comment puis-je l'accélérer?

Je pense que C++ devrait être beaucoup plus rapide que le javascript

#include <stdlib.h> 
#include <stdio.h> 
#include <malloc.h> 

#define _USE_MATH_DEFINES 
#include <math.h> 

#include <iostream> 
#include <chrono> 
#include <random> 

#include <gl/glut.h> 
#include <gl/glext.h> 

#include <cairo.h> 

using namespace std; 

double win_width = 800; 
double win_height = 600; 
double hw = win_width/2; 
double hh = win_height/2; 
double line_width = 1; 
//double line_width = 1/win_width; 

cairo_surface_t * surf = NULL; 
cairo_t   * cr = NULL; 
unsigned char * surf_data = NULL; 

GLuint texture_id; 

// Interface // 

void opengl_init(void) 
{ 
    printf("OpenGL version: %s\n", glGetString(GL_VERSION)); 
    printf("OpenGL vendor: %s\n", glGetString(GL_VENDOR)); 
    printf("OpenGL renderer: %s\n", glGetString(GL_RENDERER)); 

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 
    glDisable(GL_DEPTH_TEST); 
    glEnable(GL_BLEND); 
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
    glEnable(GL_TEXTURE_RECTANGLE_ARB); 
} 

void opengl_cleanup(void) 
{ 
    glDeleteTextures(1, &texture_id); 
} 

void opengl_draw(int width, int height, unsigned char * surf_data) 
{ 
    if (!surf_data) 
    { 
     printf("draw_func() - No valid pointer to surface-data passed\n"); 
     return; 
    } 

    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glClear(GL_COLOR_BUFFER_BIT); 

    glPushMatrix(); 

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id); 
    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 
     0, 
     GL_RGBA, 
     width, 
     height, 
     0, 
     GL_BGRA, 
     GL_UNSIGNED_BYTE, 
     surf_data); 

    glColor3f(0.25f, 0.5f, 1.0f); 
    glBegin(GL_QUADS); 
    glTexCoord2f(0.0f, 0.0f); 
    glVertex2f(0.0f, 0.0f); 
    glTexCoord2f((GLfloat)width, 0.0f); 
    glVertex2f(1.0f, 0.0f); 
    glTexCoord2f((GLfloat)width, (GLfloat)height); 
    glVertex2f(1.0f, 1.0f); 
    glTexCoord2f(0.0f, (GLfloat)height); 
    glVertex2f(0.0f, 1.0f); 
    glEnd(); 

    glPopMatrix(); 
} 

void opengl_resize(int width, int height) 
{ 
    glViewport(0, 0, width, height); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); 

    glClear(GL_COLOR_BUFFER_BIT); 

    glDeleteTextures(1, &texture_id); 
    glGenTextures(1, &texture_id); 
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id); 
    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 
     0, 
     GL_RGBA, 
     width, 
     height, 
     0, 
     GL_BGRA, 
     GL_UNSIGNED_BYTE, 
     NULL); 
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); 
} 


void drawShape() 
{ 
    //save current brush 
    cairo_save(cr); 

    // clear background 
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER); 
    //cairo_scale(cr, (double)win_height/1.0f, (double)win_height/1.0f); 
    cairo_set_source_rgba(cr, 1, 1, 1, 1); 
    cairo_paint(cr); 

    //set line color and style 
    cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); 
    cairo_set_line_width(cr, line_width); 



    static double angle = 0; 
    angle += 0.01f; 

    //draw rect 
    cairo_set_source_rgba(cr, 1, 0, 0, 1); 
    //cairo_rectangle(cr, 0.5f + sinf(angle) * 0.1f, 0.5f, 0.1f, 0.1f); 
    cairo_rectangle(cr, hw + sin(angle) * 100, hh, 100, 100); 
    cairo_fill(cr); 
    cairo_stroke(cr); 

    //draw circle 
    cairo_set_source_rgba(cr, 0, 0, 1, 1); 
    cairo_arc(cr, 300, hh, 100, 0, 2 * M_PI); 
    //cairo_fill(cr); 
    cairo_stroke(cr); 

    //draw line 
    static double r = 100; 
    static double posx = 500; 
    static double posy = 500; 
    static double x = 0; 
    static double y = 0; 

    x = r * cosf(angle); 
    y = r * sinf(angle); 

    cairo_set_source_rgba(cr, 0, 1, 0, 1); 
    cairo_move_to(cr, x + posx, y + posy); 
    cairo_line_to(cr, -x + posx, -y + posy); 
    cairo_stroke(cr); 


    int minx = 5; 
    int maxx = win_width - 5; 
    int miny = 5; 
    int maxy = win_height - 5; 
    int n = 50 * 2; 

    std::default_random_engine randomEngine; 
    randomEngine.seed(std::chrono::steady_clock::now().time_since_epoch().count()); 


    std::uniform_real_distribution<float> rangeX(minx, maxx); 
    std::uniform_real_distribution<float> rangeY(miny, maxy); 

    cairo_set_source_rgba(cr, 0, 0, 0, 1); 
    for (int i = 0; i < n * 2; i += 4) 
    { 
     float x1 = rangeX(randomEngine); 
     float y1 = rangeY(randomEngine); 

     float x2 = rangeX(randomEngine); 
     float y2 = rangeY(randomEngine); 

     cairo_move_to(cr, x1, y1); 
     cairo_line_to(cr, x2, y2); 

    } 
    cairo_stroke(cr); 


    //restore previous brush 
    cairo_restore(cr); 
} 


void display(void) 
{ 
    static int fps = 0; 
    static int frame = 0; 
    static long long startTime = chrono::system_clock::now().time_since_epoch().count(); 
    static long long lastTime = 2; 
    long long now = chrono::system_clock::now().time_since_epoch().count(); 

    ++frame; 

    //update per second 
    if (now - lastTime > 10000000) 
    { 
     lastTime = now; 
     fps = frame; 
     frame = 0; 
     cout << fps << endl; 
    } 

    drawShape(); 

    opengl_draw(win_width, win_height, surf_data); 

    glutSwapBuffers(); 
} 

cairo_t* 
create_cairo_context(int    width, 
    int    height, 
    int    channels, 
    cairo_surface_t** surf, 
    unsigned char** buffer) 
{ 
    cairo_t* cr; 

    // create cairo-surface/context to act as OpenGL-texture source 
    *buffer = (unsigned char*)calloc(channels * width * height, sizeof(unsigned char)); 
    if (!*buffer) 
    { 
     printf("create_cairo_context() - Couldn't allocate buffer\n"); 
     return NULL; 
    } 

    *surf = cairo_image_surface_create_for_data(*buffer, 
     CAIRO_FORMAT_ARGB32, 
     width, 
     height, 
     channels * width); 
    if (cairo_surface_status(*surf) != CAIRO_STATUS_SUCCESS) 
    { 
     free(*buffer); 
     printf("create_cairo_context() - Couldn't create surface\n"); 
     return NULL; 
    } 

    cr = cairo_create(*surf); 
    if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) 
    { 
     free(*buffer); 
     printf("create_cairo_context() - Couldn't create context\n"); 
     return NULL; 
    } 

    return cr; 
} 

void cleanup(void) 
{ 
    opengl_cleanup(); 
    free(surf_data); 
    cairo_destroy(cr); 
    exit(0); 
} 

void keyboard(unsigned char key, int x, int y) 
{ 
    switch (key) 
    { 
    //27 is ESC key 
    case 27: 
    case 'q': 
     cleanup(); 
     break; 

    case 'd': 
     cairo_surface_write_to_png(surf, "frame.png"); 
     break; 

    case '+': 
     if (line_width < 10) 
      line_width += 1; 
     break; 

    case '-': 
     if (line_width > 1) 
      line_width -= 1; 
     break; 

    } 
} 

void idle(void) 
{ 
    glutPostRedisplay(); 
} 

int main(int argc, char ** argv) 
{ 
    glutInit(&argc, argv); 
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); 
    glutInitWindowSize(win_width, win_height); 

    if (glutCreateWindow("Opengl Test") == 0) 
     exit(-2); 

    // create cairo-surface/context to act as OpenGL-texture source 
    cr = create_cairo_context(win_width, win_height, 4, &surf, &surf_data); 

    // setup "GL-context" 
    opengl_init(); 

    glutDisplayFunc(display); 
    glutKeyboardFunc(keyboard); 
    glutIdleFunc(idle); 
    opengl_resize(win_width, win_height); 

    glutMainLoop(); 

    return 0; 
} 

et voici le code html et js i utilisent

index.html

<!DOCTYPE html> 
<html> 
<head> 
    <title></title> 
    <script type="text/javascript" src="main.js"></script> 
    <style type="text/css"> 
    html, body { 
     margin: 0px; 
    } 

    canvas { 
     display: block; 
    } 

    </style> 
</head> 
<body> 
<canvas id="canvas"></canvas> 
</body> 
</html> 

main.js

window.onload = function() { 
    var canvas = document.getElementById("canvas"), 
     context = canvas.getContext("2d"), 
     width = canvas.width = window.innerWidth, 
     height = canvas.height = window.innerHeight; 

    render(); 

    function render() { 
     context.clearRect(0, 0, width, height); 
     for(var i = 0; i < 100; i += 1){ 
      context.beginPath(); 
      context.moveTo(Math.random() * width, Math.random() * height); 
      context.lineTo(Math.random() * width, Math.random() * height); 
      context.stroke(); 
     } 
     requestAnimationFrame(render); 
    } 
}; 

Répondre

3

Votre goulot d'étranglement n'est en fait pas OpenGL mais Cairo. Vous utilisez Cairo avec son backend de logiciel standard de rasterizer; donc le CPU fait tout le travail lourd et OpenGL est juste utilisé comme un blitter de surface glorifié. Il est vrai que la méthode de chargement de l'image finie dans OpenGL n'est pas optimale (glTexSubImage2D devrait être utilisé à la place de glTexImage2D), mais ce n'est pas là votre goulot d'étranglement.

Alors, que devez-vous faire: Idéalement, vous devriez utiliser un backend OpenGL accéléré pour le Caire comme décrit dans http://cairographics.org/OpenGL/

Une autre option est le creusement de fossés du Caire et utiliser un vecteur bibliothèque de rendu ciblée directement à OpenGL; Je pense à NanoVG ici (je n'ai aucune affiliation à ce projet). Le principal avantage de NanoVG est que toute son architecture interne a été conçue avec OpenGL en tête.

Si vous souhaitez profiler l'influence sur la méthode mal choisie pour le téléchargement de la texture est ici une variante fixe du code (supprimer opengl_cleanup, il ne fait rien bon pour cet exemple très et aussi se débarrasser de opengl_resize il est très mauvaise pratique de le faire configuration de la projection dans le gestionnaire de redimensionnement).

void opengl_draw(int width, int height, void const * surf_data) 
{ 
    static GLuint texture_id = 0; 
    static int tex_width = 0; 
    static int tex_height = 0; 

    if (!surf_data) 
    { 
     printf("draw_func() - No valid pointer to surface-data passed\n"); 
     return; 
    } 

    if(!texture_id) { 
     glGenTextures(1, &texture_id); 
    } 
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id); 
    if(width != tex_width || height != tex_height) { 
     glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 
      0, 
      GL_RGBA, 
      tex_width = width, 
      tex_height = height, 
      0, 
      GL_BGRA, 
      GL_UNSIGNED_BYTE, 
      surf_data); 
    } else { 
     glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 
      0, 0, 0, 
      tex_width, tex_height, 
      GL_BGRA, 
      GL_UNSIGNED_BYTE, 
      surf_data); 
    } 

    glViewport(0, 0, width, height); 

    glMatrixMode(GL_PROJECTION); 
    glPushMatrix(); 
    glLoadIdentity(); 
    glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); 

    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glPushMatrix(); 

    glColor3f(0.25f, 0.5f, 1.0f); 
    glBegin(GL_QUADS); 
    glTexCoord2f(0.0f, 0.0f); 
    glVertex2f(0.0f, 0.0f); 
    glTexCoord2f((GLfloat)width, 0.0f); 
    glVertex2f(1.0f, 0.0f); 
    glTexCoord2f((GLfloat)width, (GLfloat)height); 
    glVertex2f(1.0f, 1.0f); 
    glTexCoord2f(0.0f, (GLfloat)height); 
    glVertex2f(0.0f, 1.0f); 
    glEnd(); 

    glMatrixMode(GL_PROJECTION); 
    glPopMatrix(); 
    glMatrixMode(GL_MODELVIEW); 
    glPopMatrix(); 
} 

juste ligne dessiner chaque image, j'ai essayé écrire javascript sur html5. La même fonction dessine juste une ligne, et elle court beaucoup plus vite.

Et que pensez-vous que cela vous dit? HTML Canvas peut être implémenté d'une manière qui satisfait à la spécification. Le navigateur peut utiliser Cairo, son propre moteur de rendu, peut ou non utiliser le GPU. Ce n'est pas une comparaison utile, parce que vous ne savez pas ce que vous comparez réellement là.

+0

Merci, datenwolf.Je ne suis pas un anglophone natif, que voulez-vous dire - Idéalement, vous utiliseriez un backend accéléré OpenGL pour Cairo? Je n'ai trouvé aucune description ou lien de cette page opengl backend accéléré dois-je utiliser exactement? – kileyi

+0

Je veux juste dessiner environ 512 - 1024 lignes chaque image sur Opengl, mieux vaut dessiner vectoriel, donc j'ai essayé Cairo, j'ai entendu cairo dessiner sur beaucoup de contexte, donc je l'essaie juste. N'a pas réalisé CPU ou GPU les usages.Mais je ne suis pas limité à Cario seulement, je vais essayer NanoVG maintenant, voir s'il peut dessiner rapidement 1024 lignes par image – kileyi

+0

OK, essayé NanoVG avec 3000 lignes par seconde sur l'exemple gl3, 100 + fps pour mon ordinateur, Je suis plutôt content du résultat, merci encore, je pense que je vais utiliser NanoVG à la place :) – kileyi