GameTaskQueueManager Aspect

hey there. Since I think that enqueuing Callables into the GameTaskQueueManager all over the code when you use StandardGame, I implemented a little Aspect in AspectJ.



Note that you have to set the name of the OpenGL Thread name to "GL" or you can change the static string in the aspect.



The Pointcuts are the calls from the list of calls that have to be done inside the OpenGL thread, as listed in the wiki plus a few I added.



I have not tested the performance loss thoroughly, but it is unnoticable afaik because the code is weaved at load time i guess.



EDIT: changed the code on 12/10/2008




import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import com.jme.util.GameTaskQueueManager;

/**
 * @author Andreas Grabner
 *
 */
public aspect GTQMAspect {

   private static final String glName = "GL";


   public pointcut openGlMethodExecution():
     within(/* yourgamepackage */..*) &&
     !within(GTQMAspect) &&
           (call(com.jme.renderer.Renderer.Renderer.new(..)) ||
           call(* com.jme.renderer.Renderer.clearZBuffer(..)) ||
           call(* com.jme.renderer.Renderer.clearColorBuffer(..)) ||
           call(* com.jme.renderer.Renderer.clearStencilBuffer(..)) ||
           call(* com.jme.renderer.Renderer.clearBuffers(..)) ||
           call(* com.jme.renderer.Renderer.clearStrictBuffers(..)) ||
           call(* com.jme.renderer.Renderer.displayBackBuffer(..)) ||
           call(* com.jme.renderer.Renderer.setOrtho(..)) ||
           call(* com.jme.renderer.Renderer.setOrthoCenter(..)) ||
           call(* com.jme.renderer.Renderer.unsetOrtho(..)) ||
           call(* com.jme.renderer.Renderer.takeScreenshot(..)) ||
           call(* com.jme.renderer.Renderer.grabScreenContents(..)) ||
           call(* com.jme.renderer.Renderer.draw(..)) ||
           call(* com.jme.renderer.Renderer.flush(..)) ||
           call(* com.jme.renderer.Renderer.finish(..)) ||
           call(* com.jme.renderer.Renderer.createCamera(..)) ||
           call(* com.jme.renderer.Renderer.setBackgroundColor(..)) ||
           call(* com.jme.renderer.Renderer.createFogState(..)) ||
           call(* com.jme.renderer.Camera.setFrustumPerspective(..)) ||
           call(* com.jme.input.MouseInput.setCursorVisible(..)) ||
           call(* com.jme.scene..lock*(..)) ||
           call(* com.jme.scene..lock(..)) ||
           call(* com.jme.scene..removeFromParent(..)) ||
           call(* com.jme.scene.Skybox.preloadTextures(..)) ||
           call(* com.jme.util.TextureManager.loadTexture(..)) ||
           call(* com.jme.util.TextureManager.doTextureCleanup(..)) ||
           call(* com.jme.util.TextureManager.preloadCache(..)) ||
           call(* com.jmex.audio.MusicTrackQueue.play(..)) ||
           call(* com.jmex.audio.MusicTrackQueue.setCurrentTrack(..)) ||
           call(* com.jmex.audio.MusicTrackQueue.stop(..)) ||
           target(com.jmex.effects.water.WaterRenderPass) ||
           call(* java.awt.Component.setBackground(..)) ||
           call(* java.awt.Component.setSize(..)) ||
           call(com.jmex.font2d.Font2D.new(..)) ||
           call(com.jmex.font3d.Font3D.new(..)));


   Object around(): openGlMethodExecution() {
      if (Thread.currentThread().getName().equals(glName)) {
         return proceed();
      } else {
         try {
            Signature sig = thisJoinPoint.getSignature();
            if (sig instanceof MethodSignature) {
               final MethodSignature ms = (MethodSignature) sig;
               if (ms.getReturnType().equals(void.class)) {
                  GameTaskQueueManager.getManager().update(new Callable<Object>() {

                     public Object call() throws Exception {
                        proceed();
                        System.err.println("wrapped with no return value " + ms.toLongString());
                        return null;
                     }
                  });
                  return null;
               } // if
            } // if
            Future<Object> future = GameTaskQueueManager.getManager().update(new Callable<Object>() {

               public Object call() throws Exception {

                  System.err.println("wrapped with return value " + thisJoinPoint.getSignature().toLongString());
                  return proceed();
               }
            });
            return future.get();
         } catch (InterruptedException ex) {

         } catch (ExecutionException ex) {

         } // catch
         return null; // should not happen
      } // else
   } // around

} // GTQMAspec



so long,
Andy

true, the pointcut definition is a bit of a mess (too large). but why would it be cleaner to call the GTQM yourself? this is one file in your project and you dont have to worry about calling things outside the the OpenGl thread anymore.  :expressionless:

I'm a big fan of AspectJ, but this seems a bit ugly to me.  :-o



Seems a lot cleaner to be calling GameTaskQueueManager directly.

Yeah, that is a nice advantage, but when you start doing stuff like this is makes it a nightmare to debug when there is a problem…that's one of the unfortunate results of using AspectJ.

darkfrog said:

Yeah, that is a nice advantage, but when you start doing stuff like this is makes it a nightmare to debug when there is a problem...that's one of the unfortunate results of using AspectJ.


well, i have to disagree, since using eclipse with the AJDT (AspectJ Development Tools) lets you debug everything normally, even the advices and much more. plus: the AJDT marks all statements in your application when and which pointcut applies. so no debug problems there.  ;) (unless you are not using static pointcut definitions)

Even within Eclipse with AJDT there are bugs when debugging.  I've done quite a lot with it and many times Eclipse can get lost or not be able to find the aspect or something else strange.  I guess I'm just saying it still has a ways to go before I really am happy with it.  However, that's not to say it's not very useful now, I just wish that Java or Scala had AOP support built-in and didn't require the custom compiler.

darkfrog said:

I just wish that Java or Scala had AOP support built-in and didn't require the custom compiler.


thats the microsoft way of doing things  ;) extend the language to implement new features (LINQ, ...) and thats just not pretty  ;)

For most things I agree, but AOP is just not something that can be done well without language-level support (case and point the need for a custom compiler).



If they would add native support to get notifications on method invocation and field changes then it could be added by an API, but I doubt that is likely to come…well, ever. :o