LearnedShaderSmoke.kt
src/jvmMain/kotlin/afield/graphics/shader/learning/LearnedShaderSmoke.kt · 66 lines
package afield.graphics.shader.learning
import afield.document.graph.Box
import afield.graphics.vulkan.Renderer
import afield.media.reconstruction.ReconstructionCapture
import afield.graphics.vulkan.util.Options
import afield.graphics.vulkan.util.Window
import org.json.JSONObject
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.*
import kotlin.system.exitProcess
object LearnedShaderSmoke
@JvmStatic fun main(args: Array<String>)
body — 50 lines, from line 16
@JvmStatic fun main(args: Array<String>) {
Options.enableVR=false;Options.enableHDR=false;Options.enableRayTracing=false;Options.doVulkanSDK()
val dir=File("build/neural-tools/learned").also { it.mkdirs() }
val window=Window("Field learned shader");val renderer=Renderer(window);val box=Box("learned-check")
box.setPropertyRaw("renderer",renderer,doResync=false)
val bytes=ByteBuffer.wrap(File("public/docs/assets/learned_shader/weights.f16").readBytes()).order(ByteOrder.LITTLE_ENDIAN)
val weights=FloatArray(bytes.capacity()/2){java.lang.Float.float16ToFloat(bytes.getShort(it*2))}
fun reference(x: Float,y: Float,t: Float): FloatArray {
fun s(x: Float)=sin(x*PI).toFloat()
fun c(x: Float)=cos(x*PI).toFloat()
var v=floatArrayOf(x,y,t,1f,s(x),c(x),s(y),c(y),s(t),c(t),s(x+y),c(x-y),s(2*x),s(2*y),c(2*x),c(2*y))
for((layer,shape) in listOf(16 to 32,32 to 32,32 to 3).withIndex()) {
val w=listOf(0,1088,3200)[layer]/2;val b=listOf(1024,3136,3392)[layer]/2
val old=v;v=FloatArray(shape.second){j ->var sum=weights[b+j];for(i in 0 until shape.first)sum+=old[i]*weights[w+j*shape.first+i];if(layer<2)max(0f,sum) else sum}
};return v
}
try {Box.withBoxContextNow(box){
fun pump(n: Int=1){val end=renderer.frameCounter+n;while(renderer.frameCounter<end){window.pollEvents();renderer.render()}}
val report=JSONObject().put("enabledExtensions",renderer.device.enabledExtensions.toList())
.put("cooperativeVectorInferenceEnabled",renderer.device.supportsCooperativeVector)
.put("cooperativeVectorTrainingEnabled",false).put("opticalFlowEnabled",renderer.device.supportsOpticalFlow)
for(cooperative in listOf(false,true)) {
LearnedShader(renderer,cooperative=cooperative).use { demo ->
demo.time=.25f;demo.view=1;pump(12)
val pixels=ReconstructionCapture.read(renderer,demo.output);check(pixels.all { it.isFinite() })
var error=0f
repeat(64){i->val x=(i*317+21)%demo.width;val y=(i*193+37)%demo.height
val expected=reference((x+.5f)/demo.width*2-1,(y+.5f)/demo.height*2-1,demo.time)
for(c in 0..2)error=max(error,abs(expected[c]-pixels[(y*demo.width+x)*4+c]))}
check(error<if(cooperative).02f else .0001f) { "CPU/GPU network disagreement: $error" }
val result=JSONObject().put("cpuMaxError",error).put("allocatedBytes",demo.allocatedBytes)
for(view in listOf(0,1)) {
demo.view=view;pump(32);val start=demo.gpuMilliseconds.size;pump(100)
val times=demo.gpuMilliseconds.drop(start).sorted();check(times.size>=95)
result.put(if(view==0)"originalGpuMs" else "learnedGpuMs",JSONObject().put("count",times.size).put("p50",times[times.size/2]).put("p95",times[(times.size*.95).toInt()]))
if(view==0)result.put("stripe",ShaderLearningChecks.stripeMetrics(
ReconstructionCapture.read(renderer,demo.output),pixels,demo.width,demo.height))
}
demo.view=2;pump(2)
ReconstructionCapture.save(File(dir,if(cooperative)"cooperative.png" else "arithmetic.png"),ReconstructionCapture.read(renderer,demo.output),demo.width,demo.height)
if(cooperative && "--clip" in args)repeat(120){i->demo.time=sin(i/120f*2*PI).toFloat();pump()
ReconstructionCapture.save(File(dir,"clip/%04d.png".format(i)),ReconstructionCapture.read(renderer,demo.output),demo.width,demo.height)}
report.put(if(cooperative)"cooperative" else "arithmetic",result)
};pump(32)
}
File(dir,"measurements.json").writeText(report.toString(2))
}} finally {renderer.cleanup();window.cleanup()}
println("LEARNED SHADER CHECK DONE");exitProcess(0)
}
}