0

I'm using Qt5.1 compiled for Visual Studio 2008.

I'm trying to render objects with multiple textures. In my original code, I use a mesh class to manage loading 3D objects, which is based on this tutorial:

http://ogldev.atspace.co.uk/www/tutorial22/tutorial22.html

The mesh class contains a vector that holds textures. Textures load image data from files and keep track of all of the OpenGL state housekeeping necessary to render themselves.

I get a "Debug assertion failed: _BLOCK_TYPE_US_VALID(pHead->nBlockUse)" error when trying to use Qt to add more than one texture to my mesh class vector container. This error occurs when the destructor is called when adding textures to the vector, similar to what is described in this thread:

Destructor called on object when adding it to std::list

I am new to Qt and am wondering what the best way to handle textures is in this case. The tutorial code uses GLEW rather than Qt and I had to inherit from the QOpenGLFunctions to manage the OpenGL state for rendering the textures (my mesh class also inherits from QOpenGLFunctions). It appears that the default copy constructor is doing a shallow copy of the texture object, which deletes memory that is managed by Qt. However, when I switch to using a list container for textures, I don't get the assertion error.

Here is a simplification of my code that illustrates my problem:

#include <QOpenGLFunctions_3_3_Core>
#include <list>
#include <vector>

class Texture : protected QOpenGLFunctions_3_3_Core
{

public:
    Texture() 
    { 
    }

    ~Texture()
    {
        std::cout << "destructor" << std::endl;
    }
};

std::vector<Texture> textureList; //this causes the assertion failure
//std::list<Texture> textureList; //this works
void tester()
{

    for (int i = 0; i < 3; i++)
    {
        Texture Texture;
        textureList.push_back(Texture);
    }
}

int main()
{
    tester();

    //Qt application code
}
Community
  • 1
  • 1
maogenc
  • 203
  • 7
  • 13
  • Does the non-simplified version of `Texture` have any dynamic members (i.e. things you're `new`-ing and `delete`-ing) maintained within? And I apologize in advance for not knowing qt well, but does `QOpenGLFunctions_3_3_Core` have built-in [Rule of Three](http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)) support (and likewise your `Texture` derivative if the answer to the aforementioned question about dynamic member management is "yes") ? – WhozCraig Jan 23 '14 at 06:38
  • I don't do any dynamic memory management in my texture class. I load the image data, send it to an OpenGL target, and bind the texture when I render. In this example, if the `Texture` class doesn't inherit from `QOpenGLFunctions_3_3_Core` then I don't get the assertion error. It doesn't look like QOpenGLFunctions supports Rule of Three. Since QOpenGLFuntions is a complicated object that keeps track of the OpenGL context, maybe I need to pass in a pointer to a single instance of QOpenGLFunctions rather than trying to inherit from it in `Texture` and in my `Mesh` class (not shown here)? – maogenc Jan 23 '14 at 17:29
  • A couple of examples I've seen have a class that inherits from QOpenGLFunctions, but I'm not sure what the correct usage is for Qt when things get more complicated. – maogenc Jan 23 '14 at 17:31

1 Answers1

0

This problem comes from difference of internal implementation between vector and list.

First of all, list is doubly linked list, so list can grow without copies for original elements. But vector is dynamically allocated array. If capacity of vector is full, vector allocate new array with greater size (Array doubling), and copy elements from old array to new array. Below snippet shows why assertion occurs. (Maybe)

#include <iostream>
#include <list>
#include <vector>

class Texture
{

public:
  Texture() 
  {   
    std::cout << "constructor" << std::endl;
  }   
  Texture(const Texture &t) 
  {   
    std::cout << "copy constructor" << std::endl;
  }   

  ~Texture()
  {   
    std::cout << "destructor" << std::endl;
  }   
};

std::vector<Texture> textureList;
//std::list<Texture> textureList;
void tester()
{
  for (int i = 0; i < 3; i++)
  {   
    //textureList.reserve(4);
    std::cout << "Loop\n";
    Texture texture;
    textureList.push_back(texture);
    std::cout << "Capacity: " << textureList.capacity() << "\n";
  }   
  std::cout << "End\n";
}

int main()
{
  std::cout << "main\n";
  tester();
  std::cout << "main end\n";
}

Result

main
Loop
constructor
copy constructor
Capacity: 1
destructor
Loop
constructor
copy constructor
copy constructor
destructor
Capacity: 2
destructor
Loop
constructor
copy constructor
copy constructor
copy constructor
destructor
destructor
Capacity: 4
destructor
End
main end
destructor
destructor
destructor

Texture instance is destroyed whenever vector grows up.

If you prevent from this problem, use vector::reserve (check the above code: textureList.reserve(4)). But I think that another problems will be occurred with this code, so I recommend to use pointer of texture instead of instance of texture to deal with.

angdev
  • 108
  • 8
  • `QOpenGLFunctions_3_3_Core` doesn't seem to support the Rule of Three. I passed a pointer to `QOpenGLFunctions_3_3_Core` into the `Texture` class rather than inheriting from it in order to set the OpenGL state. It no longer fails the assertion even when using vectors of `Texture`s. The additional details on the differences between vector and list behavior were helpful. – maogenc Jan 23 '14 at 23:46