Of course, it broke all the code that stores Textures in std::vectors. Doh!
Got me thinking a bit. I can't actually have the copy constructor do an actual copy of the texture, else every time I push_back a Texture onto a vector, we could end up needlessly copying every other texture already in the vector.
So I reckon I need to implement reference-counting copy-on-write.
Seems like the best solution to me. I can then use Textures in std::vectors, I can pass them to functions by value and since I'm not allowing access to the underlying IDirect3DTexture9* through the Texture interface, it should be okay.
Just rambling again really since I'm still in this temp job and have nothing to do.
Got copy-on-write working for the Texture class now. It's quite nice, actually, because now the header of the Texture class looks a bit more like:
class Imp; Imp *I;
and doesn't refer to a D3D type in the header at all.
In fact, the whole of my graphics unit header file now only needs
I ran some tests for my copy-on-write, a bit like:
for(int I=0;I<100;++I) V.push_back(T);
for(int I=0;I<100;++I) Graphics.Render(I*10,10,V);
That ends up creating a maximum of three actual Direct3D textures under the hood, one when T has its sprite loaded, another at the point that V has its sprite loaded, and another when V has a square filled with a colour.
Commenting out either of these "write" operations stops a new texture from being created.
And since everything is reference counted, releasing V and V has no effect on the rest of the textures (Render just returns without trying to draw them).
My Texture class is does unfortunately have a NULL state, whereby the internal representation's IDirect3DTexture9 pointer is NULL, so everything that operates on it has to test for this case.
Seems unavoidable though, since if I do:
then I hardly want the 1000 Textures in the vector to be allocating IDirect3DTextures before anything is allocated to them.
Bit of a bugger internally, but it is transparent to the user - a NULL Texture can be passed to all the same methods or have all its methods safely called. Doing so just does nothing. And the Height() and Width() methods return 0.
Next thing I want to do is modify the QuadBuffer class a little bit.
At the moment, you have to pass a const reference to a Texture in its constructor, the idea being that you use it like:
Qu.Add(10,20); // x,y
Qu.Add(10,20,32,32,0,0); // x,y,width,height,source_x,source_y
which is fine, and can support atlasing as well as very simple drawing of the whole texture.
BUT - if I wanted to fill a quad buffer with untextured quads (for example to create large black backgrounds for windows or whatever), I'm stuffed.
Unfortunately, much of the internals of the QuadBuffer rely upon a valid texture being owned by the buffer. For example, the second Add() method above makes sense without a texture, but the first Add() call, Add(x,y), is meaningless without a valid texture since there is nowhere to imply a width and height from.
Guess I didn't think that one through properly. A small voice in my head is suggesting I have a QuadBufferBase and derive a QuadBuffer and TexturedQuadBuffer from it, but I'm not too sure whether to listen to it or not.
Seems okay. I have a QuadBuffer base, with protected constructors and so on so you can't instatiate it, then BlankQuadBuffer and TexturedQuadBuffer derived from it, each with their different Add() methods.
I decided as well to apply pimpl to the QuadBuffer, since it previously contained a std::vector
I like that since this is strictly a sprite interface to Direct3D and the Vertex class exists purely as an implementation detail.
Obviously now that QuadBuffer contains a pointer, I've had to implement copy constructor and assignment operator, but they are pretty trivial.