Hi!

Published December 08, 2005
Advertisement
GTL - Game Texture Loader
In case you didn't know, this is Phantom's little project right now. It's an interesting idea, so I thought that I would look at it and give some input.

Arg!
The first thing I noticed was that this wasn't going to be particularly friendly to port to a DLL. The Image structure has a lot of public members, one of which happens to be a template class. There was also the fact that the members all ended in underscores. We also have a series of functions which are to be used to extract information from the structure, such as getWidth/getHeight. These functions do a bit more than just return the members of the structure, so their usage should be preferred.

The LoadTexture function overloads present the most immediate problem for portage to a DLL. DLL exports, unless you want to be locked into a single compiler, require a very simple name mangling scheme, the C method for most user DLLs and Pascal for windows. With the C name mangling, the names of your functions just get prefixed with an underscore. Since parameter information is not preserved, there is no way to export overloaded functions. There is also the fact that they return straight Image structure instances, which restricts our ability to change the underlying code without recompiling everything that uses it.

Finally, there is the inability to add new filters without rebuilding the GTL. Ideally you would be able to add new filters at runtime to handle custom image formats not supported natively by the GTL. Exposing this functionality to the client would be a great addition.

A managed wrapper
Building a managed framework to wrap the existing implementation isn't nice, but isn't to bad either. The main thing you end up having to deal with is the lack of pointers being returned from the LoadTexture functions. Because of this you have to allocate your own pointers and manage them in the managed class. Mixed types are not allowed, yet. The rest is pretty straight forward, just some properties that expose the get* functions, a destructor and a finalizer to ensure that the pointers get cleaned up when the managed Image is destroyed.

Suggested changes
After looking it over, and playing with it a bit (along with writing a managed wrapper around the current beta), I made a few recommendations. The first was to make the Image structure a base class with pure virtual methods that expose the get* functions. This would eliminate the need for those particular functions. It also means that the data members of the structure would no longer be visible to the public. This makes it friendlier for DLL export, as that nasty template member is no longer present. The LoadTexture functions would be changed to return pointers, and for those of you who don't like managing pointers, LoadTextureSafe versions that returned boost::shared_ptr's would be added that were inline in the header (thus not exported by the DLL, but part of the client directly). This would also make it more managed wrapper friendly, as you don't have to jump through hoops in order to get the managed type to be able to host an Image type. Finally I recommended that some form of an ability to register filters be added, enabling them to be changed at runtime. While this functionality is almost present now, it's not exposed to the client.
//hoop jumping:img->image = new GameTextureLoader::Image(    GameTextureLoader::LoadTexture(std::string(fileNamePtr),     safe_cast(type)));


It's been a while...
So...yeah, kind of stopped updating again, didn't I? I'll try and update more frequently, as time permits. I still need to finish up my XML stuff and give you guys an XSLT journal. Dunno what I'll discuss after that, it's a bit blank [grin].

However, before I close this journal entry, I thought I would give you all a little gift...

Dynamic DLL Loading
So you've got a C# wrapper around a managed set of functions. It loads up your DLL, executes beautifully, but now you're wondering "how on earth am I going to make this code be able to accept a DLL name at runtime?" Well, here's a solution for you...

It all started with Seriema asking about importing his ThumbView into C#. He did write a sample, however it was statically linked to an implementation DLL. Being the entrepreneur that I am, I decided to rewrite it in C#, and at the same time enable the client to choose the DLL to use at runtime. Unfortunately, there is no clean way to do this...

In order to link to a library dynamically at runtime, I had to have a method to load up the DLL at runtime. So, using win32 interop, I conjured up the following class, which you are free to use as long as the copyright is kept intact:
/*	Copyright (c) 2005 by Sean "Washu" Kent (ryoohki@gmail.com). All rights reserved.	Permission to use, copy, modify and distribute this software for any	purpose is hereby granted without fee, provided this copyright and	permissions notice appear in all copies and derivatives.*/using System;using System.Runtime.InteropServices;namespace Kent.Library {	public sealed class DynamicLibrary : IDisposable {		public DynamicLibrary(string libraryFileName) {			this.libraryFileName = libraryFileName;			if (libraryFileName == null)				throw new ArgumentNullException("Library name must not be null.");			libraryHandle = LoadLibrary(libraryFileName);			if (libraryHandle == IntPtr.Zero) {				int lastError = Marshal.GetLastWin32Error();				throw new InvalidOperationException("LoadLibrary failed with error code: " + lastError);			}		}		public void GetDelegateByName(string functionName, out T del) {			if (disposed)				throw new InvalidOperationException("Object has already been disposed.");			IntPtr functionPointer = GetProcAddress(libraryHandle, functionName);			object delegateObject = Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(T));			del = (T)delegateObject;		}		public void Dispose() {			if (!disposed) {				FreeLibrary(libraryHandle);				disposed = true;			}		}		~DynamicLibrary() {			Dispose();		}		public string LibraryFileName { get { return libraryFileName; } }		[DllImport("Kernel32", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError=true)]		private static extern IntPtr LoadLibrary(string libraryFileName);		[DllImport("Kernel32", CallingConvention = CallingConvention.StdCall, SetLastError = true)]		private static extern void FreeLibrary(IntPtr libraryHandle);		[DllImport("Kernel32", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]		private static extern IntPtr GetProcAddress(IntPtr handle, string libraryFileName);		private IntPtr libraryHandle;		private string libraryFileName;		private bool disposed = false;	}}


The usage is fairly simple, you just need to construct it with the library you want to load, and then call GetDelegateByName to obtain delegates to the unmanaged functions, as demonstrated below:
internal delegate void PrintStringDelegate(string stringToPrint);public class Sample {    public Sample(string sampleDllName) {        library = new Kent.Library.DynamicLibrary(sampleDllName);        library.GetDelegateByName("PrintString", out printString);    }    public void PrintString(string stringToPrint) {        printString(stringToPrint);    }    private PrintStringDelegate printString;    private Kent.Library.DynamicLibrary library;}


Pretty cool eh?
Previous Entry Even better
Next Entry Compiler Intrinsics
0 likes 2 comments

Comments

jollyjeffers
Too tired to read this thing fully right now, but seem like you forgot a closing </tt> tag [smile]

Will check it out tomorrow

Jack

[edit]
Washu: Me forget a closing tt tag? never!
[/edit]
December 08, 2005 06:37 PM
Seriema
omg I got mentioned in Washus journal! *proud*

And thanks again for that handy piece of code! =D
December 18, 2005 10:21 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement