phetsims / alpenglow

Experimental rasterization engine, by PhET Interactive Simulations
MIT License
10 stars 0 forks source link

Code Review and Unit Tests for RenderPrograms #2

Open marlitas opened 1 year ago

marlitas commented 1 year ago

This issue will be used to track review questions/comments as well as unit test commits for RenderPrograms

jonathanolson commented 1 year ago

If it's helpful, I added some quick non-unit testing code in the alpenglow playground under executeTest():

const program = new phet.alpenglow.RenderLinearBlend(
  phet.dot.v2( 1, 0 ),
  0.25,
  phet.alpenglow.RenderLinearBlendAccuracy.Accurate,
  new phet.alpenglow.RenderColor( phet.dot.v4( 1, 0, 0, 1 ) ),
  new phet.alpenglow.RenderColor( phet.dot.v4( 0, 1, 0, 1 ) )
);

// unit square
const context = new phet.alpenglow.RenderEvaluationContext().set(
  null, 1, phet.dot.v2( 0.5, 0.5 ), 0, 0, 1, 1
);

console.log( 'RenderProgram' );
console.log( program.toRecursiveString() );

const instructions = [];
program.writeInstructions( instructions );
console.log( '(object) instructions' );
console.log( instructions.map( instruction => instruction.toString() ).join( '\n' ) );

const encoder = new phet.alpenglow.ByteEncoder();
phet.alpenglow.RenderInstruction.instructionsToBinary( encoder, instructions );

console.log( '(binary) instructions' );
console.log( encoder.getDebug32String() );

const fromBinaryInstructions = phet.alpenglow.RenderInstruction.binaryToInstructions( encoder, 0 );
console.log( '(object) instructions (from binary)' );
console.log( fromBinaryInstructions.map( instruction => instruction.toString() ).join( '\n' ) );

const instructionsEqual = phet.alpenglow.RenderInstruction.instructionsEquals( instructions, fromBinaryInstructions );
console.log( 'instructions before/after binary conversion equal?', instructionsEqual );

const executor = new phet.alpenglow.RenderExecutor();
executor.loadInstructions( instructions );

const result = phet.dot.v4( 0, 0, 0, 0 );
executor.execute( context, result );
console.log( 'evaluation (instructions)', result );

const directResult = program.evaluate( context );
console.log( 'evaluation (RenderProgram)', directResult );

const simplifiedProgram = program.simplified();

const simplifiedResult = simplifiedProgram.evaluate( context );
console.log( 'evaluation (RenderProgram simplified)', simplifiedResult );

console.log( 'evaluations equal', result.equalsEpsilon( directResult, 1e-6 ) && result.equalsEpsilon( simplifiedResult, 1e-6 ) );

When run:

image

It takes a RenderProgram and essentially tests two things:

  1. Converts it to instruction form (RenderInstruction), converts THAT to binary instruction form (prints it out), converts THAT back into instruction form (RenderInstruction), and compares to make sure the two arrays of RenderInstructions are equivalent.
  2. Evaluates the original RenderProgram, evaluates the instruction form (RenderInstruction), and evaluates the simplified RenderProgram, and compares to see if it gets the same results.

This should work for almost all RenderPrograms (RenderImage not yet in binary form, since I'll need to figure out what info is needed, and RenderPathBoolean/RenderDepthSort should always be split/replaced away and won't need a binary form).

If this were executed by a range of valid RenderPrograms in unit tests (and ideally fuzzed with randomly-generated RenderPrograms), that would be amazing!