PDA

View Full Version : OpenGL Texture Manager



Brainer
18-11-2007, 06:32 AM
System: Windows XP SP2, Pentium D 805 2,66 GHz, GeForce 7600 GS, 2 GB RAM
Compiler/IDE: Delphi 2007
API: JEDI-SDL, OpenGL
---
Hi everyone! :)

I want to make a texture manager for my first 3D engine. I heard there are different ways of doing this. Well, I previously did a texture manager, but it was crappy and I want something optimized and well-working for this project. Can you suggest me how I should build the manager? :wink:

vgo
18-11-2007, 09:17 AM
Here's how I did mine. I have a global gTextureManager class which returns handles to textures, you get the handle by the filename and when you don't need the texture anymore you tell that to the manager and it decreases the usage counter for that texture.

When the texture's usage counter is 0 it means that this texture can be replaced by another. Textures used for the same purpose usually are the same size and glTexSubImage is used to replace the data in the graphics card's memory.

Another way to do it would be to use massive texture sizes, ie. allocate as big texture as possible and then start fitting the actual textures in that, a sort of texture atlas. This would reduce the number of state changes when binding textures. But you'd probably need several textures and handling all the reallocation can be tricky and the texture classes should handle all bindings themselves, no more calling glBindTexture directly with a handle and setting the texture coordinates would be tricky too (would have to alter the texture matrix). I might try something like this next.

Brainer
18-11-2007, 09:36 AM
Hmm, pretty interesting approach. I guess a singleton-based texture manager would be a good choice, too. The manager I have in mind loads textures from a file/stream, uses TThreadList to store the texture object. The texture object is just a simple class (something like that):


type
TTexture = class(TObject)
private
{ Private declarations }
FName: String;
FID: Cardinal;
public
{ Public declarations }
constructor Create(const ATexName: String; const ATexId: Cardinal);

property TexName: String read FName;
property ID: Cardinal read FID;
end;


What do you think of this idea?

User137
18-11-2007, 10:03 AM
This is how it's defined on my latest engine:


TTexture = record
name: string[12];
sizeX, sizeY, values: word; // values 3 or 4
index,format: cardinal; // GL_RGB or GL_RGBA
Data: PByteArray; // size = sizeX*sizeY*values
end;
PTexture = ^TTexture;

TTextureLoadOptions = set of (toMipMap, toAlphaColor,
toKeepData, toAbsoluteSize, toColorKey);

TTextureSet = class
texture: array of TTexture;
count, TextureQuality: integer;
Options: TTextureLoadOptions;
TransparentColor: TRGBTriple;
private
count2: integer;
function Pow2Limit(var w,h: word; var sx,sy: single): boolean;
procedure SetCount(n: integer);
public
constructor Create;
destructor Destroy; override;
// AddTexture creates new item, makes call to LoadTextureData which makes extension check and loads any format that is identified
function AddTexture(name,filename: string; transparency: boolean = false): integer;
procedure Clear;
procedure Disable;
procedure Enable;
function IndexOf(name: string): integer;
function LoadBMPDataFile(tex: PTexture; filename: string): boolean;
function LoadBMPData(tex: PTexture; bmp: TBitmap): boolean;
function LoadPNGData(tex: PTexture; filename: string): boolean;
function LoadJPGData(tex: PTexture; filename: string): boolean;
function LoadTextureData(tex: PTexture; filename: string): boolean;
procedure RemoveTexture(n: integer);
procedure ReloadTexture(n: integer; filename: string; transparency: boolean);
procedure Restore(tex: PTexture);
procedure SetTex(n: integer);
end;

Also because glBindTexture calls should be minimized as much as possible, this one is used every time (except forced when making displaylists):

implementation

var lastTex: cardinal;

procedure nxSetTex(t: cardinal; force: boolean);
begin
if (t<>lastTex) or force then begin
glBindTexture(GL_TEXTURE_2D,t);
lastTex:=t;
end;
end;

Brainer
18-11-2007, 10:11 AM
That's almost the same I' was thinking about. :D I don't understand the last snippet, though. Can you explain it further to me, please?

User137
18-11-2007, 10:21 AM
It simply skips glBindTexture call if it is already bound, therefore making program run much faster if same ID was been tried to set many times in a row. Only way to change binding is another glBindTexture call, no other method can unbind it.

Brainer
18-11-2007, 10:28 AM
I suppose this method is not a member of any class and is called within SetTex method. Am I right? ;)

User137
18-11-2007, 10:34 AM
I suppose this method is not a member of any class and is called within SetTex method. Am I right? ;)
Right :)

TTextureSet.SetTex makes call to nxSetTex

Also, why there is private SetCount procedure and Count2 variable, is because i made setlength calls to power of 2 only. This reduce amount of times array is resized and likely makes program faster.

Brainer
18-11-2007, 10:39 AM
Pretty clever idea! :) Still, I heard that using a singleton is the best option. What do you think?

User137
18-11-2007, 11:15 AM
If you mean, each texture should be object... it's matter of taste maybe. I like mix of object oriented and procedural :) Every bit of procedural gives a little more efficiency to program but little more headache to programmer if done too much.

Brainer
18-11-2007, 11:29 AM
That's right. :) I prefer to use classes and records; I don't like procedural programming. In my humble opinion, using OOP the code is more readable and easier to understand. I don't know much about the performance.

By the way, I read a little about singletons and found this:


&#123; .&#58; TSingleton &#58;. &#125;
TSingleton = class&#40;TObject&#41;
public
&#123; Public declarations &#125;
class function NewInstance&#40;&#41;&#58; TObject; override;
procedure FreeInstance&#40;&#41;; override;
class function RefCount&#40;&#41;&#58; Integer;
end;

&#40;...&#41;
var
Instance&#58; TSingleton = nil;
Ref_Count&#58; Integer = 0;

&#123; TSingleton &#125;

procedure TSingleton.FreeInstance;
begin
Dec&#40;Ref_Count&#41;;
if &#40;Ref_Count = 0&#41; then
begin
Instance &#58;= nil;
// Destroy private variables here
inherited FreeInstance&#40;&#41;;
end;
end;

class function TSingleton.NewInstance&#58; TObject;
begin
if &#40;not Assigned&#40;Instance&#41;&#41; then
begin
Instance &#58;= inherited NewInstance&#40;&#41;;
// Initialize private variables here, like this&#58;
// TSingleton&#40;Result&#41;.Variable &#58;= Value;
end;
Result &#58;= Instance;
Inc&#40;Ref_Count&#41;;
end;

class function TSingleton.RefCount&#58; Integer;
begin
Result &#58;= Ref_Count;
end;


How do I use this for a texture manager? :?