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}