2013-02-27 261 views
7

मैं टाइल-आधारित ओपनजीएल, सी ++ एप्लिकेशन पर काम कर रहा हूं। मैं आवेदन से नमूना स्क्रीन जोड़ रहा है, ताकि इसे और अधिक स्पष्ट हो जाएगा:प्राइमेटिव ड्राइंग के अप्रचलित विधि से धीमी गति से वीबीओ - क्यों?

मैं Tile वर्ग जो Object रों की एक सरणी शामिल है। प्रत्येक टाइल 15 ऑब्जेक्ट्स तक स्टोर कर सकती है - इसका उदाहरण Tile पर हरा और पीला वर्ग (दो ऑब्जेक्ट्स) है, इसलिए यह 10x10x15 = 1500 Object एस ड्रॉ करने के लिए है (सबसे खराब मामले में, क्योंकि मैं खाली नहीं कर रहा हूं 'खाली है 'वाले)। आम तौर पर यह कम है, मेरे परीक्षण में मैं उनमें से 600 का उपयोग करता हूं। Object में इसका अपना ग्राफिक है, जिसे खींचा जा सकता है। प्रत्येक Object एक समय में एक Tile से संबंधित है, लेकिन इसे स्थानांतरित किया जा सकता है (उदाहरण के लिए चित्र में लाल वर्ग)।

Object की पृष्ठभूमि में सीमा होगी और उन्हें अच्छी तरह से स्केलेबल होने की आवश्यकता है, इसलिए मैं उन्हें आकर्षित करने के लिए 9-पैच पैटर्न का उपयोग कर रहा हूं (वे 9 क्वाड से बने हैं)।

ड्राइंग Tile एस (उनके Object एस सटीक होने के बिना), मेरे आवेदन के पास लगभग 600 fps है।

सबसे पहले, मैं अप्रचलित विधि का उपयोग किया गया है उन Tile रों आकर्षित करने के लिए - का उपयोग कर glBegin(GL_QUADS)/glEnd() और glDisplayList रों। उस ड्राइंग के कारण प्रदर्शन की एक बड़ी बूंद थी - 600 से 320 fps तक। इस प्रकार मैं उन्हें चित्रित कर रहा हूं:

bool Background::draw(const TPoint& pos, int width, int height) 
{ 
    if(width <= 0 || height <= 0) 
     return false; 
    //glFrontFace(GL_CW); 
    glPushMatrix(); 
    glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f);  // Move background to right direction 
    if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list, 
     // then recalculate everything and save in display list 
    { 
     // That size will be now saved in display list 
     m_savedWidth = width; 
     m_savedHeight = height; 

     // If this background doesn't have unique display list id specified yet, 
     // then let OpenGL generate one 
     if(m_displayListId == NO_DISPLAY_LIST_ID) 
     { 
      GLuint displayList; 
      displayList = glGenLists(1); 
      m_displayListId = displayList; 
     } 

     glNewList(m_displayListId, GL_COMPILE); 

     GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width); 

     // Instead of coordinates range 0..1 we need to specify new ones 
     GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0; 
     GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0; 

     GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width; 
     GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height; 

     /* 9-cell-pattern 

     ------------------- 
     | 1 | 2 | 3 | 
     ------------------- 
     | |   | | 
     | 4 | 9 | 5 | 
     | |   | | 
     ------------------- 
     | 6 | 7 | 8 | 
     ------------------- 

     */ 

     glBindTexture(GL_TEXTURE_2D, m_texture);    // Select Our Texture 

     // Top left quad [1] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0, maxTexCoordBorderY); 
      glVertex2i(0, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f(0.0, 0.0); 
      glVertex2i(0, 0); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, 0.0); 
      glVertex2i(0 + m_borderWidth, 0); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 
     glEnd(); 

     // Top middle quad [2] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 0.0); 
      glVertex2i(0 + m_borderWidth, 0); 

      // Top right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 0.0); 
      glVertex2i(0 + width - m_borderWidth, 0); 

      // Bottom right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 
     glEnd(); 

     // Top right quad [3] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 0.0); 
      glVertex2i(0 + width - m_borderWidth, 0); 

      // Top right 
      glTexCoord2f(1.0, 0.0); 
      glVertex2i(0 + width, 0); 

      // Bottom right 
      glTexCoord2f(1.0, maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + m_borderWidth); 
     glEnd(); 

     // Middle left quad [4] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f(0.0, maxTexCoordBorderY); 
      glVertex2i(0, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 
     glEnd(); 

     // Middle right quad [5] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(1.0, maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + height - m_borderWidth); 
     glEnd(); 

     // Bottom left quad [6] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0f, 1.0); 
      glVertex2i(0, 0 + height); 

      // Top left 
      glTexCoord2f(0.0f, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, 1.0); 
      glVertex2i(0 + m_borderWidth, 0 + height); 
     glEnd(); 

     // Bottom middle quad [7] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 1.0); 
      glVertex2i(0 + m_borderWidth, 0 + height); 

      // Top left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 1.0); 
      glVertex2i(0 + width - m_borderWidth, 0 + height); 
     glEnd(); 

     // Bottom right quad [8] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 1.0); 
      glVertex2i(0 + width - m_borderWidth, 0 + height); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f(1.0, 1.0); 
      glVertex2i(0 + width, 0 + height); 
     glEnd(); 

     GLfloat xTexOffset; 
     GLfloat yTexOffset; 

     if(m_borderWidth > 0) 
     { 
      glBindTexture(GL_TEXTURE_2D, m_centerTexture);  // If there's a border, we have to use 
      // second texture now for middle quad 
      xTexOffset = 0.0;         // We are using another texture, so middle middle quad 
      yTexOffset = 0.0;         // has to be texture with a whole texture 
     } 
     else 
     { 
      // Don't bind any texture here - we're still using the same one 

      xTexOffset = maxTexCoordBorderX;     // But it implies using offset which equals 
      yTexOffset = maxTexCoordBorderY;     // maximum texture coordinates 
     } 

     // Middle middle quad [9] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(xTexOffset, maxTexCoordHeight - yTexOffset); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f(xTexOffset, yTexOffset); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordWidth - xTexOffset, yTexOffset); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordWidth - xTexOffset, maxTexCoordHeight - yTexOffset); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 
     glEnd(); 

     glEndList(); 
    } 

    glCallList(m_displayListId); // Now we can call earlier or now created display list 

    glPopMatrix(); 

    return true; 
} 

शायद वहां बहुत अधिक कोड है, लेकिन मैं सब कुछ दिखाना चाहता था। इस संस्करण के बारे में मुख्य बात डिस्प्ले सूचियों और glVertex2i का उपयोग है जो बहिष्कृत हैं।

मैंने सोचा कि इस तरह की धीमी गति की समस्या इस अप्रचलित विधि का उपयोग था जो मैंने पढ़ा है, काफी धीमी है, इसलिए मैंने VBO के लिए जाने का फैसला किया। मैं का उपयोग किया है this tutorial और यह के अनुसार मैं इस तरह मेरी पद्धति को बदल:

bool Background::draw(const TPoint& pos, int width, int height) 
{ 
    if(width <= 0 || height <= 0) 
     return false; 

    glPushMatrix(); 
    glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f);    // Move background to right direction 
    if((width != m_savedWidth) || (height != m_savedHeight))  // If size to draw is different than the one saved in display list, 
                    // then recalculate everything and save in display list 
    { 
     // That size will be now saved in display list 
     m_savedWidth = width; 
     m_savedHeight = height; 

     GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width); 

     // Instead of coordinates range 0..1 we need to specify new ones 
     GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0; 
     GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0; 

     GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width; 
     GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height; 

     /* 9-cell-pattern, each number represents one quad 

     ------------------- 
     | 1 | 2 | 3 | 
     ------------------- 
     | |   | | 
     | 4 | 9 | 5 | 
     | |   | | 
     ------------------- 
     | 6 | 7 | 8 | 
     ------------------- 

     */ 

     /* How vertices are distributed on one quad made of two triangles 

     v1 ------ v0 
     |  /| 
     | / | 
     |/  | 
     v2 ------ v3 

     */ 

     GLfloat vertices[] = { 
           // Top left quad [1] 
           m_borderWidth, 0, 0,       // v0 
           0, 0, 0,          // v1  
           0, m_borderWidth, 0,       // v2    

           0, m_borderWidth, 0,       // v2 
           m_borderWidth, m_borderWidth, 0,    // v3 
           m_borderWidth, 0, 0,       // v0 

           // Top middle quad [2] 
           width-m_borderWidth, 0, 0,      // v0 
           m_borderWidth, 0, 0,       // v1 
           m_borderWidth, m_borderWidth, 0,    // v2 

           m_borderWidth, m_borderWidth, 0,    // v2 
           width-m_borderWidth, m_borderWidth, 0,   // v3 
           width-m_borderWidth, 0, 0,      // v0 

           // Top right quad [3] 
           width, 0, 0,         // v0 
           width-m_borderWidth, 0, 0,      // v1 
           width-m_borderWidth, m_borderWidth, 0,   // v2 

           width-m_borderWidth, m_borderWidth, 0,   // v2 
           width, m_borderWidth, 0,      // v3 
           width, 0, 0,         // v0 

           // Middle left quad [4] 
           m_borderWidth, m_borderWidth, 0,    // v0 
           0, m_borderWidth, 0,       // v1 
           0, height-m_borderWidth, 0,      // v2 

           0, height-m_borderWidth, 0,      // v2 
           m_borderWidth, height-m_borderWidth, 0,   // v3 
           m_borderWidth, m_borderWidth, 0,    // v0 

           // Middle right quad [5] 
           width, m_borderWidth, 0,      // v0 
           width-m_borderWidth, m_borderWidth, 0,   // v1 
           width-m_borderWidth, height-m_borderWidth, 0, // v2 

           width-m_borderWidth, height-m_borderWidth, 0, // v2 
           width, height-m_borderWidth, 0,     // v3 
           width, m_borderWidth, 0,      // v0 

           // Bottom left quad [6] 
           m_borderWidth, height-m_borderWidth, 0,   // v0 
           0, height-m_borderWidth, 0,      // v1 
           0, height, 0,         // v2 

           0, height, 0,         // v2 
           m_borderWidth, height, 0,      // v3 
           m_borderWidth, height-m_borderWidth, 0,   // v0 

           // Bottom middle quad [7] 
           width-m_borderWidth, height-m_borderWidth, 0, // v0 
           m_borderWidth, height-m_borderWidth, 0,   // v1 
           m_borderWidth, height, 0,      // v2 

           m_borderWidth, height, 0,      // v2 
           width-m_borderWidth, height, 0,     // v3 
           width-m_borderWidth, height-m_borderWidth, 0, // v0 

           // Bottom right quad [8] 
           width, height-m_borderWidth, 0,     // v0 
           width-m_borderWidth, height-m_borderWidth, 0, // v1 
           width-m_borderWidth, height, 0,     // v2 

           width-m_borderWidth, height, 0,     // v2 
           width, height, 0,        // v3 
           width, height-m_borderWidth, 0,     // v0 

           // Middle middle quad [9] 
           width-m_borderWidth, m_borderWidth, 0,   // v0 
           m_borderWidth, m_borderWidth, 0,    // v1 
           m_borderWidth, height-m_borderWidth, 0,   // v2 

           m_borderWidth, height-m_borderWidth, 0,   // v2 
           width-m_borderWidth, height-m_borderWidth, 0, // v3 
           width-m_borderWidth, m_borderWidth, 0   // v0 
          }; 

     copy(vertices, vertices + 162, m_vCoords);    // 162, because we have 162 coordinates 


     int dataSize = 162 * sizeof(GLfloat); 
     m_vboId = createVBO(m_vCoords, dataSize); 

    } 

    // bind VBOs for vertex array 
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vboId);   // for vertex coordinates 

    glEnableClientState(GL_VERTEX_ARRAY);     // activate vertex coords array 
     glVertexPointer(3, GL_FLOAT, 0, 0);      
     glDrawArrays(GL_TRIANGLES, 0, 162); 
    glDisableClientState(GL_VERTEX_ARRAY);     // deactivate vertex array 

    // bind with 0, so, switch back to normal pointer operation 
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, NO_VBO_ID); 

    glPopMatrix(); 

    return true; 
} 

यह काफी पिछले संस्करण में समान है, लेकिन glDisplayList और glVertex2i() मैं VBO इस्तेमाल किया जो एक सरणी में संग्रहीत डेटा से बनाया जा रहा है के बजाय ।

लेकिन परिणाम, मुझे निराश है क्योंकि मैं बढ़ावा के बजाय प्रदर्शन ड्रॉप मिल गया, मैं मुश्किल से ~260 fps मिला है और मैं ध्यान दें चाहिए कि इस विधि संस्करण मैं अभी तक बनावट के उपयोग को लागू नहीं किया है, तो वहाँ अब बिना के लिए केवल quads हैं किसी भी बनावट से बंधे हैं।

मैंने कुछ लेख पढ़े हैं ताकि यह पता चल सके कि इतनी धीमी गति का कारण क्या हो सकता है और पता चला है कि शायद यह VBO एस की बड़ी मात्रा के कारण है और मेरे पास शायद VBO होना चाहिए जिसमें सभी पृष्ठभूमि डेटा हैं प्रत्येक पृष्ठभूमि के लिए VBO अलग करें। लेकिन समस्या यह है कि Object एस चारों ओर स्थानांतरित हो सकते हैं और उनके पास अलग-अलग बनावट हैं (और बनावट एटलस मेरे लिए एक अच्छा समाधान नहीं है), इसलिए उन लोगों के लिए उन परिवर्तनों को अपडेट करना मुश्किल होगा जो Object एस ने अपना राज्य बदल दिया। अभी के लिए, जब Object एस बदल रहा है, तो मैं इसे VBO फिर से बना देता हूं और शेष VBO एस बिना छूटे रहते हैं।

तो मेरा सवाल है - मैं क्या गलत कर रहा हूं?क्या VBO एस की बड़ी (~ 600) संख्या का उपयोग glVertex2i के साथ ड्राइंग की अप्रचलित विधि से वास्तव में धीमा है? और क्या हो सकता है - शायद मेरे मामले में सबसे अच्छा, लेकिन बेहतर नहीं - समाधान?

+1

फ्रेम्स-प्रति-सेकंड एक [भयानक तुलनात्मक मीट्रिक] है (http://www.mvps.org/directx/articles/fps_versus_frame_time.htm)। प्रति फ्रेम (मिली-सेकंड सेकंड) पर स्विच करें। – genpfault

+0

@genpfault धन्यवाद, यह जानना निश्चित रूप से अच्छा है। तथ्य यह है कि उस मामले में वीबीओ धीमा है, हालांकि। –

+1

कुछ ड्राइवर वास्तव में प्रदर्शन सूचियों को संकलित करने में बहुत अच्छे हैं। – JasonD

उत्तर

3

सिर्फ इसलिए कि फिक्स्ड-फ़ंक्शन सामग्री पुरानी है, बहिष्कृत है, और आम तौर पर अनुशंसित नहीं है, इसका मतलब यह नहीं है कि यह हमेशा धीमा रहता है।

न ही फैंसी 'नया' (यह थोड़ी देर के आसपास रहा है) बफर और शेडर्स के साथ कार्यक्षमता और इस तरह की जरूरी अर्थ यह है कि सब कुछ बिजली तेज होगा।

जब आप अपनी ड्राइंग को डिस्प्ले सूची में लपेटते हैं, तो आप मूल रूप से ड्राइवर को ऑपरेशन का एक गुच्छा गुजर रहे हैं। यह वास्तव में ड्राइवर के लिए क्या हो रहा है अनुकूलित करने के लिए एक उचित गुंजाइश देता है। जीपीयू संचालन के एक सुंदर कुशल प्री-पैक किए गए गांठ में आप जो कुछ भी कर रहे हैं, वह बहुत अधिक पैकेज कर सकता है। यह पर आपके डेटा को बफर में पैकेज करने और उन्हें भेजने के लिए क्या होता है उससे थोड़ा अधिक कुशल हो सकता है।

यह कहना नहीं है कि मैं पुराने शैली के इंटरफेस के साथ चिपकने की सिफारिश करता हूं, लेकिन निश्चित रूप से मुझे आश्चर्य नहीं है कि ऐसे मामले हैं जहां यह अच्छी नौकरी करता है।

7

इसकी प्रकृति से, आप प्रत्येक फ्रेम के साथ वीबीओ को दोबारा बना रहे हैं। यदि आप डेटा का उपयोग glBufferSubData को बदलना चाहते हैं, glBufferData पूरे, लंबे VBO प्रारंभिक के माध्यम से जाता है।

यदि डेटा स्थिर है, तो केवल एक बार VBO बनाएं, फिर इसका पुन: उपयोग करें।

+3

ध्यान दें कि यदि आप डेटा के साथ एक वीबीओ चाहते हैं जो बहुत बदलता है, तो आपको VL के लिए डेटा प्रकार के रूप में GL_DYNAMIC_DRAW का उपयोग करना चाहिए। – Ethereal

+1

यह ठीक तरह से मुद्दा लगता है। अपने वीबीओ सृजन कोड को एक अलग विधि (जैसे 'createMesh') में ले जाएं और केवल एक नई ऑब्जेक्ट की आवश्यकता होने पर इसे कॉल करें (जैसे प्रारंभिकरण)। वीबीओ को दोबारा बनाने से प्रत्येक फ्रेम इसे नियोजित करने से किसी भी लाभ को कम करता है। – ssell

+1

वीबीओ प्रत्येक फ्रेम को फिर से नहीं बनाया जा रहा है - केवल तभी जब ड्राइंग ऑब्जेक्ट का आकार बदला जा रहा है, जो बहुत ही कम होता है। –