001package polaris.sdk.ui.render;
002import java.awt.Canvas;
003import java.io.*;
004import java.nio.*;
005import java.nio.charset.StandardCharsets;
006/** Desktop OpenGL 3.3 backend. One live native backend / GL context per process in this release. */
007public final class NativeBackend implements AutoCloseable {
008 private final Thread owner=Thread.currentThread();private boolean closed;private final boolean window;private static boolean live;
009 private NativeBackend(Canvas canvas)throws IOException{
010  synchronized(NativeBackend.class){if(live)throw new IllegalStateException("Only one NativeBackend may be alive per process");live=true;}
011  window=canvas!=null;
012  try{String jawt=NativeLoader.load();if(NativeGpu.protocolVersion()!=24164)throw new IllegalStateException("Native backend/Java version mismatch: use a complete JAR matching native protocol 24164");String[] shaders=shaders();if(canvas!=null)NativeGpu.open(canvas,jawt,shaders);else NativeGpu.attach(shaders);checkError();}
013  catch(Throwable e){try{NativeGpu.close();}catch(Throwable ignored){}synchronized(NativeBackend.class){live=false;}if(e instanceof IOException)throw (IOException)e;if(e instanceof RuntimeException)throw (RuntimeException)e;throw new IOException(e);}
014 }
015 public static NativeBackend open(Canvas canvas)throws IOException{if(canvas==null)throw new NullPointerException("canvas");return new NativeBackend(canvas);}
016 /** The caller already owns and has made current a desktop GL 3.3+ context. No window is created. */
017 public static NativeBackend attachCurrentContext()throws IOException{return new NativeBackend(null);}
018 private static String resource(String name)throws IOException{try(InputStream in=NativeBackend.class.getResourceAsStream("/shaders/"+name)){if(in==null)throw new IOException("Missing shader "+name);ByteArrayOutputStream out=new ByteArrayOutputStream();byte[] b=new byte[4096];int n;while((n=in.read(b))!=-1)out.write(b,0,n);return new String(out.toByteArray(),StandardCharsets.UTF_8);}}
019 private static String[] shaders()throws IOException{String[] names={"fullscreen.vert","background.frag","blur.frag","quad.vert","glass.frag","shape.frag","text.frag"};String common=resource("geometry.glsl");String[] result=new String[names.length];for(int i=0;i<names.length;i++)result[i]=resource(names[i]).replace("#include \"geometry.glsl\"",common);return result;}
020 public void check(){if(closed)throw new IllegalStateException("Backend closed");if(Thread.currentThread()!=owner)throw new IllegalStateException("Use backend on the context-owning thread");}
021 public String renderer(){check();return NativeGpu.rendererString();}public String version(){check();return NativeGpu.contextInfo();}
022 public long clientSize(){check();if(!window)throw new IllegalStateException("Host supplies framebuffer dimensions");return NativeGpu.clientSize();}
023 public void background(int w,int h,float time,boolean grid,boolean light){background(w,h,time,grid,light?polaris.sdk.ui.Theme.light():polaris.sdk.ui.Theme.dark());}
024 public void background(int w,int h,float time,boolean grid,polaris.sdk.ui.Theme theme){check();if(!window)throw new IllegalStateException("background() is a standalone-demo helper");NativeGpu.background(w,h,time,grid,!theme.dark,theme.background,theme.backgroundAccent);}
025 void submit(FloatBuffer commands,int count,int fbo,int buffer,int pw,int ph,float w,float h,float sigma,float mx,float my){check();if(!Float.isFinite(sigma)||sigma<0||sigma>32)throw new IllegalArgumentException("sigma must be 0..32");NativeGpu.submit(commands,count,fbo,buffer,pw,ph,w,h,sigma,mx,my);}
026 int texture(int w,int h){check();return NativeGpu.createTexture(w,h);}void upload(int t,int x,int y,int w,int h,ByteBuffer data){check();NativeGpu.upload(t,x,y,w,h,data);}void deleteTexture(int t){check();NativeGpu.deleteTexture(t);}
027 public ByteBuffer screenshot(int w,int h){check();if(!window)throw new IllegalStateException("Screenshot helper only reads the standalone window");ByteBuffer buffer=ByteBuffer.allocateDirect(Math.multiplyExact(Math.multiplyExact(w,h),4)).order(ByteOrder.nativeOrder());NativeGpu.readPixels(w,h,buffer);return buffer;}
028 public void checkError(){check();int e=NativeGpu.glError();if(e!=0)throw new IllegalStateException("OpenGL error 0x"+Integer.toHexString(e));}
029 public void swap(){check();if(!window)throw new IllegalStateException("Host owns swap");NativeGpu.swap();}
030 @Override public void close(){if(closed)return;check();NativeGpu.close();closed=true;synchronized(NativeBackend.class){live=false;}}
031}