• entries
    743
  • comments
    1924
  • views
    578690

Hmm

Sign in to follow this  
Aardvajk

59 views

Thought I'd make my copy constructor and assignment operator private for my texture class last night. Thought that was quite clever.

Wrong!

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.

[LATER]

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 Texture
{
private:
class Imp; Imp *I;

bool GetOwnCopy();

public:
Texture();

// etc
};


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 because it references D3DCOLOR types. So if I can be bothered to work around that, I would not need to include anywhere in the project except in the cpp for the graphics unit, which appeals.

I ran some tests for my copy-on-write, a bit like:


Texture T;
Graphics.LoadSingleSprite(T,"test.spr",0);

while(Loop)
{
std::vector V;

for(int I=0;I<100;++I) V.push_back(T);

Graphics.LoadSingleSprite(V[4],"test.spr",2);

V[8].Release();
V[9].Release();

V[2].Fill(0,0,10,10,D3DCOLOR_XRGB(100,0,0));

for(int I=0;I<100;++I) Graphics.Render(I*10,10,V);
}

T.Release();
Graphics.Release();


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[4] has its sprite loaded, and another when V[2] 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[8] and V[9] 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:


std::vector V;
V.resize(1000);


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:


QuadBuffer Qu(Tex);

Qu.Add(10,20); // x,y
Qu.Add(10,20,32,32,0,0); // x,y,width,height,source_x,source_y

Graphics.Draw(Qu);


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.

[LATER]

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. By pimpl-ing it, that means I have been able to move the Vertex class out of the header so it is now private to the graphics unit .cpp.

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.
Sign in to follow this  


2 Comments


Recommended Comments

I did it like this: I have another class ImageHandler or something that stores all the images I load to the application and puts an index on them. Then when I load a image it returns the index, so if I have already loaded a image and tries to load it again I get the same index. Then i store a int in like sprite or whatever is using the image, int image_index. Then Blit(pImageHandler->getImage(image_index));
is all I have to do, and smoothly only allows one image to be loaded and used everywhere.
Don't know if this makes any sense, writing on my lunch which is soon to be over, and still no internet access at home :(

Share this comment


Link to comment
Certainly does make perfect sense.

I have considered using like a handle system to textures stored internally by the graphics library. This would also mean I could automatically release textures when the device gets released.

I guess if I used reference counting in this internal list it would work with copy-on-write, rather than storing a pointer-to-implementation in the Texture class.

Cheers. You've given me an interesting idea.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now