OpenGL Perfomance zu niedrig



  • Hallo Leute,

    ich habe eine kleine OGL Anwendung geschrieben, welche Messdaten grafisch darstellt. Dabei gibt es für jeden Punkt(X,Y) einen Messwert, welcher die Tiefe(Z) darstellt. Es ensteht somit also ein großes 3D Objekt, welches mit einer 1D Textur überzogen wird. Das ganze funktioniert auch einwandfrei, allerdings ist die Perfomance absolut bescheiden (ca. 5 fps ), sobald ich das Objekt drehe oder bewege.
    Das Objekt selbst besteht aus ca. 350.000 Dreiecken.

    Der Code dazu findet sich hier:

    void TestGUI::InitializeGL()
    {
        PIXELFORMATDESCRIPTOR pfd;
        int format;
    
        // get the device context (DC)
        HDC hDC = GetDC( m_hWindow );
    
        // set the pixel format for the DC
        ZeroMemory( &pfd, sizeof( pfd ) );
        pfd.nSize = sizeof( pfd );
        pfd.nVersion = 1;
        pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
        pfd.iPixelType = PFD_TYPE_RGBA;
        pfd.cColorBits = 24;
        pfd.cDepthBits = 16;
        pfd.iLayerType = PFD_MAIN_PLANE;
        format = ChoosePixelFormat( hDC, &pfd );
        SetPixelFormat( hDC, format, &pfd );
    
        // create and enable the render context (RC)
        m_hglrc = wglCreateContext( hDC );
        wglMakeCurrent( hDC, m_hglrc );
    
        //=========================================
        glEnable (GL_POINT_SMOOTH);    // Antialiasing für Punkte einschalten
        glEnable (GL_LINE_SMOOTH);    // Antialiasing für Linien einschalten
        glClearColor (0.0, 0.0, 0.0, 0.0);
    
        glEnable( GL_TEXTURE_1D );
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
    
        glGenTextures( 1, &m_paletteTexture );
    
        glBindTexture( GL_TEXTURE_1D, m_paletteTexture );
    
        glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT );
        glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER,
                        GL_NEAREST );
        glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER,
                        GL_NEAREST );
    
        glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, m_colorPalette.size()/4, 0,
                      GL_RGBA, GL_UNSIGNED_BYTE, &m_colorPalette[0] );
    
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
    
        glEnable( GL_TEXTURE_1D );
    
        CRect rcClientRect;
        GetClientRect( m_hWindow, &rcClientRect );
    
        glViewport(0, 0, (GLsizei) rcClientRect.Width(), (GLsizei) rcClientRect.Height() );
    
      if ( rcClientRect.Width() <=  rcClientRect.Height() )
          glOrtho (-2.0, 2.0, -2.0*(GLfloat)rcClientRect.Height() / (GLfloat)rcClientRect.Width(),
                  2.0*(GLfloat)rcClientRect.Height() / (GLfloat)rcClientRect.Width(), -2.0, 2.0 );
      else
          glOrtho (-2.0*(GLfloat)rcClientRect.Width() / (GLfloat)rcClientRect.Height(),
                  2.0*(GLfloat)rcClientRect.Width() / (GLfloat)rcClientRect.Height(), -2.0, 2.0, -2.0, 2.0);
    
        glNewList( m_3DCScan, GL_COMPILE  );
            make3DCScan();
        glEndList();
    
    }
    
    void TestGUI::make3DCScan()
    {
        if ( m_layer.Valid() )
        {
            //glClear(GL_COLOR_BUFFER_BIT );
    
            ULONG dimX  = 0;
            ULONG dimY  = 0;
            POINT point = { 0, 0 };
    
            LONG value0 = 0;
            LONG value1 = 0;
    
            float xPt = 0;
            float yPt = 0;
            float zPt = 0;
    
            if ( m_layer->GetCScanDimX( &dimX ) == S_OK && m_layer->GetCScanDimY( &dimY ) == S_OK )
            {
                for( ULONG y = 0; y < dimY; ++y )
                {
                    glBegin( GL_TRIANGLE_STRIP );
                    for( ULONG x = 0; x < dimX; ++x )
                    {
                        if ( m_layer->GetCScanDataY( x,  y,  &value0 ) == S_OK && value0 != 0x7070 &&
                            m_layer->GetCScanDataY( x,  y+1, &value1 ) == S_OK && value1 != 0x7070 )
                        {
                            // vertex 0
                            xPt = x /100.0;
                            yPt = y /100.0;
    
                            glTexCoord1f( value0 / 32768.0 / 2 + 0.5 );
                            glVertex3f( xPt, yPt, value0 / 32768.0  );
    
                            // vertex 1
                            xPt = x    /100.0;
                            yPt = (y+1) /100.0;
    
                            glTexCoord1f( value1 / 32768.0 / 2 + 0.5 );
                            glVertex3f( xPt, yPt, value1 / 32768.0  );
                        }
                    }
                    glEnd();
                }
            }
        }
    }
    
    void TestGUI::Paint( HDC hDC )
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        switch( m_rotateCamera )
        {
            case rotateXp:
                glRotatef( 10, 1.0, 0.0, 0.0 );
                m_rotateCamera = none;
                break;
            case rotateXn:
                glRotatef( -10, 1.0, 0.0, 0.0 );
                m_rotateCamera = none;
                break;
            case rotateYp:
                glRotatef( 10, 0.0, 1.0, 0.0 );
                m_rotateCamera = none;
                break;
            case rotateYn:
                glRotatef( -10, 0.0, 1.0, 0.0 );
                m_rotateCamera = none;
                break;
            case rotateZp:
                glRotatef( 10, 0.0, 0.0, 1.0 );
                m_rotateCamera = none;
                break;
            case rotateZn:
                glRotatef( -10, 0.0, 0.0, 1.0 );
                m_rotateCamera = none;
                break;
        }
    
        glCallList( m_3DCScan );
    
        glFlush();
        SwapBuffers( hDC );
    
    }
    LRESULT TestGUI::DefWndProc( HWND hwindow, UINT msg, WPARAM wparam, LPARAM lparam )
        {
    
            if ( msg == WM_KEYDOWN  )
            {
                switch( wparam )
                {
                case 'W':
                    m_rotateCamera = rotateXp;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                case 'S':
                    m_rotateCamera = rotateXn;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                case 'A':
                    m_rotateCamera = rotateYn;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                case 'D':
                    m_rotateCamera = rotateYp;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                case 'Q':
                    m_rotateCamera = rotateZn;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                case 'E':
                    m_rotateCamera = rotateZp;
                    RedrawWindow( m_hWindow, NULL, NULL, RDW_INVALIDATE );
                    break;
                }
            }
            return TWindow::DefWndProc( hwindow, msg, wparam, lparam);
        }
    

    Wie kann ich das ganze nun performanter machen?


  • Mod

    vermutlich indem du erstmal misst wo das problem genau liegt.

    ist es die anzahl der dreiecke? die groesse der dreiecke? etc.

    und dann koennen wir dir vorschlagen wie du es loest, falls z.b. du vertex limitiert sein solltest: "glDrawArray koennte dann eine moegliche loesung sein".



  • 2.) Vertex buffer objects verwenden

    glEnable (GL_POINT_SMOOTH);
    glEnable (GL_LINE_SMOOTH);
    

    2.) Abschalten ...



  • ja ich denke es ist die Anzahl der Dreiecke. Ich hab das ganze jetz einmal dahingehend geändert, dass ich erstmal alle Vertexte in einen Vector schreibe:

    void TestGUI::make3DCScanVector()
    {
        if ( m_layer.Valid() )
        {
    
            ULONG dimX  = 0;
            ULONG dimY  = 0;
            POINT point = { 0, 0 };
    
            LONG value0 = 0;
            LONG value1 = 0;
    
            GLfloat xPt = 0;
            GLfloat yPt = 0;
            GLfloat zPt = 0;
    
            if ( m_layer->GetCScanDimX( &dimX ) == S_OK && m_layer->GetCScanDimY( &dimY ) == S_OK )
            {
                for( ULONG y = 0; y < dimY; ++y )
                {
    
                    for( ULONG x = 0; x < dimX; ++x )
                    {
                        if ( m_layer->GetCScanDataY( x,   y,   &value0 ) == S_OK && value0 != 0x7070 &&
                             m_layer->GetCScanDataY( x,   y+1, &value1 ) == S_OK && value1 != 0x7070 )
                        {
                            // vertex 0 
                            xPt = (GLfloat) x /100.0;
                            yPt = (GLfloat) y /100.0;
    
                            m_triangleVec.push_back( xPt );
                            m_triangleVec.push_back( xPt );
                            m_triangleVec.push_back(  (GLfloat) value0 / 32768.0 );
                            // vertex 1
                            yPt = (GLfloat) (y+1) /100.0;
                            m_triangleVec.push_back( xPt );
                            m_triangleVec.push_back( xPt );
                            m_triangleVec.push_back(  (GLfloat) value1 / 32768.0 );
                        }
                    }
                }
            }
        }
    
    }
    

    und diese dann folgendermaße ausgebe:

    void TestGUI::Paint( HDC hDC )
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glEnableClientState( GL_VERTEX_ARRAY );
        glVertexPointer( 3 ,GL_FLOAT, 0, &m_triangleVec[0] );
        glDrawArrays( GL_TRIANGLE_STRIP, 0, m_triangleVec.size()/3 );
        glDisableClientState( GL_VERTEX_ARRAY );
    
        glFlush();
        SwapBuffers( hDC );
    
    }
    

    das ganze ist jedoch leider genauso langsam.



  • Tja, wo befinden sich denn die Daten fuer die Geometrie. 350.000 jedesmal aus dem Hauptspeicher ueber den Prozessor zur Grafikkarte zu transportieren dauert halt. Vertex buffer objects erlaubt deine Daten auf der GraKa abzulegen. Zugegeben, 350.000 Dreiecke sind viel, aber ...

    Keine Ahnung, welche Karte du hast, aber nach den Leistungsdaten sollte sie eigentlich ausreichen:
    http://www.nvidia.com/page/geforce4mx.html
    http://www.nvidia.com/page/geforce_6600.html

    Auch wenn die Leistung nur ansatzweise ausgeschoepft wird, sollte es eigentlich fuer 350.000 Dreiecke reichen. Auch weiss ich nicht, was so zwischen deinen Draw calls so passiert, vielleicht ist ja gar nicht das Rendering der Flaschenhals. Auch solltest du VSync mal deaktivieren.

    Um besser vergleichen zu koennen, kannst du ja auch mal die TechDemos deines Herstellers laufen lassen und dir dort Vertex pro Sekunde ausgeben lassen.


  • Mod

    Fahrrad schrieb:

    ja ich denke es ist die Anzahl der Dreiecke. Ich hab das ganze jetz einmal dahingehend geändert, dass ich erstmal alle Vertexte in einen Vector schreibe:
    ...
    das ganze ist jedoch leider genauso langsam.

    was ja schonmal beweist, dass deine annahme wohl nicht richtig war. deswegen solltest du das mal benchmarken. erstmal z.b. mit AMD's codecolaborator



  • Problem gelöst.

    Nach ewigem hin und her habe ich festgestellt, dass es am Texturvektor lag. Der hatte eine Größe von 864. Geändert auf 1024 und schwups es läuft sauschnell.
    Graka benutze ich eine Ati Radeon x1300Pro. Vorher wurde als alles per Software gerendert, da die Vektorgröße keine Potenz von 2 war.

    Vielen Dank für eure Hilfen.


  • Mod

    ich glaube deine graphikkarte sollte eigentlich auch mit nicht power of two texturen arbeiten koennen. aber falls es ein problem gab, haette der in glLastError reportet werden muessen.


Anmelden zum Antworten