How to get ActionScript shaders compilation logs

Introduction

ActionScript shaders are an incredible feature: they make it possible to program the graphics hardware using nothing more than pure plain ActionScript code. The ActionScript code written in those shaders is then executed on the GPU, providing an easy and powerful way to create hardware accelerated rendering effects.

In this tutorial we will briefly describe how ActionScript shaders are compiled at runtime by Minko's JIT shader compiler, what debug data is made available and how to trace it for educational purposes.

ActionScript shaders compilation process

The code written in ActionScript shaders is not actually directly compiled to shader code. Instead, it seamlessly builds a data structure called an Abstract Shader Graph (ASG). Each ASG represents the operations performed by one shader program. This ASG is then compiled to AGAL bytecode by Minko's JIT compiler. That AGAL bytecode will then be uploaded to the graphics hardware to be executed whenever rendering occurs.

The compilation process is done in 3 steps:



  • The ASG is optimized using mutliple optimization algorithms. One of the most important optimizations is "memoization": operations are eventually deleted and reordered to make sure than the same computation is never done twice in the program.
  • The compiler lists all the input textures, constants and vertex attributes used in the shader and performs memory allocations. The memory allocation process generates allocation tables that will be used to setup the shader and its required vertex buffers, constants and texture in order to prepare the graphics hardware for rendering.
  • AGAL bytecode is then generated using the optimized ASG.

Available traces

The output produced by each of the 3 compilation steps can be logged for educational purposes. This is done using Minko's debug flags that can be set using the Minko.debugLevel static property. All the available values regarding shaders compilation are available in the DebugLevel static enumeration class:

  • SHADER_ANTECOMPILE_DOTTY: Trace the ASG.
  • SHADER_POSTCOMPILE_DOTTY: Trace the ASG after optimization.
  • SHADER_ATTR_ALLOC: Trace the vertex attributes allocation table.
  • SHADER_CONST_ALLOC: Trace the constants allocation table.
  • SHADER_AGAL: Trace the AGAL assembly code corresponding to the generated AGAL bytecode.

The ASG is outputed using the DOT graph description language. You can read the DOT language article on Wikipedia to learn more about DOT and how to display DOT files.

Those flags can be combined to log all the desired traces. The following code can be used to trace the vertex attributes allocation table, the constants allocation table and the AGAL assembly code corresponding to the compiled bytecode:

Minko.debugLevel = DebugLevel.SHADER_ATTR_ALLOC | DebugLevel.SHADER_CONST_ALLOC | DebugLevel.SHADER_AGAL;

Only the shaders compiled after the Minko.debugLevel was properly set will log traces. A safe way to enforce this is to place it in the constructor of your main class.

Conclusion

We can adapt the code of the previous tutorial to log the shader compilation traces:

public class Main extends Sprite { private var _viewport : Viewport = new Viewport(); private var _camera : ArcBallCamera = new ArcBallCamera(); private var _scene : Group = new Group( _camera, CubeMesh.cubeMesh ); public function Main() { Minko.debugLevel = DebugLevel.SHADER_ATTR_ALLOC | DebugLevel.SHADER_CONST_ALLOC | DebugLevel.SHADER_AGAL; _camera.distance = 3.; _viewport.defaultEffect = new SinglePassRenderingEffect(new RedShader()); stage.addCild(_viewport); stage.addEventListener(Event.ENTER_FRAME); } private function enterFrameHandler(event : Event) : void { _viewport.render(_scene); } } Running this example will output the following trace in the console:
va0             x, y, z

vc[0-3]         TransformParameter[key='localToScreen']

fc0.x           Constant[value='1']
fc0.y           Constant[value='0']
fc0.z           Constant[value='0']
fc0.w           Constant[value='1']

m44 vt0, va0, vc0
mov op, vt0

mov oc, fc0