All modern laptop CPUs have an embedded graphics chip. While not as powerful as a discrete GPU, they are far less power hungry.

To help Windows users choose between their discrete GPU and the integrated chip NIVDIA, has introduced Optimus. When the user starts an application, Optimus will determine if the integrated GPU will be enough, or if all power is needed. This is a great idea, but sadly Optimus doesn’t recognize Caromble! as an application that would benefit from a bit more firepower. And Caromble! does love the firepower.

This weekend I finally managed to get Optimus to start Caromble! on the high-performance GPU, and since it took me a while, I thought it might be nice to share how I did it. All of it is in this pdf, but applying it to Java requires a bit of tinkering around.

Telling Optimus to choose the high-performance GPU is very simple. You just set the global variable “NvOptimusEnablement ” to 1. Which is easy enough, apart from that you can’t do so from Java directly. You have to do it from C or C++, you have to do it before an OpenGL context is created, and it has to be on the same process as your java game.

So that gives you two options. Using the Java Native Interface (JNI) you can either call C++ from Java, or you can start Java from within C++. I chose the latter, since it seemed simple, and would give us a proper Windows .exe. This is nice, because we can pick our custom icon, and Caromble! will show up as Caromble!.exe in the taskmanager, and not as java.exe. And, to a geek like me, that is a bit important too.

Enough talking. The basics of starting the Java Virtual Machine from C++ are explained in the official Java documentation. Beware, it’s a tough read.

Below is my interpretation. I have omitted error checking / handling at some places, all includes, and some other stuff that should be easy to figure out. Visual Studio Express is a good choice for writing your .exe.  This post at java-gaming.org helped me quite a lot.

I hope it helps!

//Please oh mighty Optimus, give us the High Performance GPU by default
extern "C" { 
 _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}

typedef jint (APIENTRY * CreateJavaVMPROC) (JavaVM **pvm, void **penv, void *args);

int _tmain(int argc, _TCHAR* argv[])
{
	//Load the java dll. Alternative is to start the jvm directly, 
        //using the system JRE
	HMODULE jvmdll = LoadLibrary(TEXT("java\\bin\\server\\jvm.dll"));

        const int numOptions = 4;
	JavaVMOption* options = new JavaVMOption[numOptions];

	options[0].optionString = "-Djava.class.path=Caromble-GAME.jar";
	options[1].optionString = "-Djava.library.path=lib";
	options[2].optionString = "-Xms600m";
	options[3].optionString = "-XX:CompileThreshold=2000";

	JavaVMInitArgs vm_args; 
	vm_args.version = JNI_VERSION_1_2;
        vm_args.nOptions = numOptions;
	vm_args.options = options;
        vm_args.ignoreUnrecognized = false;

	JavaVM *jvm;      
        JNIEnv *env;    

	CreateJavaVMPROC CreateJavaVM = 
              (CreateJavaVMPROC) GetProcAddress(jvmdll, "JNI_CreateJavaVM");
        int res = CreateJavaVM(&jvm, (void**)&env, &vm_args);
	delete options;
	if( res != 0)
	{
		std::cout << "Error creating JVM" << std::endl; 		
                return 1;
 	} 	

        //Find our main class          
        jclass cls = env->FindClass("main/Caromble");
	if( env->ExceptionOccurred())
	{
		/* Mostly this means that you have configured you 
                   Visual Studio Project paths incorrectly */
		std::cout << "Could not find main class because: " << std::endl; 	 	        
                env->ExceptionDescribe();
	}

	//Find our main method
        jmethodID mid = 
                  env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");

	/* Prepare the arguments for the main function, 
           here we use it to bypass the menu and start a level directly */
	jobjectArray args = 
                env->NewObjectArray(2, env->FindClass("java/lang/String"), NULL);
	env->SetObjectArrayElement(args, 0, 
                env->NewStringUTF("-a"));
	env->SetObjectArrayElement(args, 1, 
                env->NewStringUTF("levels\\story\\C3L2.xml"));

        env->CallStaticVoidMethod(cls, mid, args);
	if( env->ExceptionOccurred())
	{
		std::cout << "Could not run main method because: " << std::endl; 		        
                env->ExceptionDescribe();
	}

	//Destroy the JVM, this function will now return
        jvm->DestroyJavaVM();

	return 0;
}

« »