1 /* 2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.scenario.effect.impl.prism.ps; 27 28 import java.io.InputStream; 29 import java.lang.reflect.Constructor; 30 import java.nio.FloatBuffer; 31 import java.util.Map; 32 import com.sun.glass.ui.Screen; 33 import com.sun.javafx.geom.Rectangle; 34 import com.sun.javafx.geom.transform.BaseTransform; 35 import com.sun.prism.Graphics; 36 import com.sun.prism.GraphicsPipeline; 37 import com.sun.prism.GraphicsPipeline.ShaderModel; 38 import com.sun.prism.GraphicsPipeline.ShaderType; 39 import com.sun.prism.Image; 40 import com.sun.prism.PixelFormat; 41 import com.sun.prism.RTTexture; 42 import com.sun.prism.ResourceFactory; 43 import com.sun.prism.ResourceFactoryListener; 44 import com.sun.prism.Texture; 45 import com.sun.prism.Texture.Usage; 46 import com.sun.prism.Texture.WrapMode; 47 import com.sun.prism.ps.Shader; 48 import com.sun.prism.ps.ShaderFactory; 49 import com.sun.scenario.effect.Effect.AccelType; 50 import com.sun.scenario.effect.FilterContext; 51 import com.sun.scenario.effect.Filterable; 52 import com.sun.scenario.effect.FloatMap; 53 import com.sun.scenario.effect.ImageData; 54 import com.sun.scenario.effect.LockableResource; 55 import com.sun.scenario.effect.impl.EffectPeer; 56 import com.sun.scenario.effect.impl.Renderer; 57 import com.sun.scenario.effect.impl.hw.ShaderSource; 58 import com.sun.scenario.effect.impl.prism.PrDrawable; 59 import com.sun.scenario.effect.impl.prism.PrImage; 60 import com.sun.scenario.effect.impl.prism.PrRenderer; 61 import com.sun.scenario.effect.impl.prism.PrTexture; 62 63 import static com.sun.scenario.effect.impl.Renderer.RendererState.*; 64 65 public class PPSRenderer extends PrRenderer { 66 67 private final Screen screen; 68 private final ShaderSource shaderSource; 69 private RendererState state; 70 private boolean needsSWDispMap; 71 72 private final ResourceFactoryListener listener = 73 new ResourceFactoryListener() 74 { 75 public void factoryReset() { 76 dispose(); 77 } 78 79 public void factoryReleased() { 80 dispose(); 81 } 82 }; 83 84 private PPSRenderer(Screen screen, ShaderSource shaderSource) { 85 this.screen = screen; 86 this.shaderSource = shaderSource; 87 synchronized (this) { 88 state = OK; 89 } 90 ResourceFactory rf = 91 GraphicsPipeline.getPipeline().getResourceFactory(screen); 92 rf.addFactoryListener(listener); 93 needsSWDispMap = !rf.isFormatSupported(PixelFormat.FLOAT_XYZW); 94 } 95 96 @Override 97 public PrDrawable createDrawable(RTTexture rtt) { 98 return PPSDrawable.create(rtt); 99 } 100 101 @Override 102 public AccelType getAccelType() { 103 return shaderSource.getAccelType(); 104 } 105 106 /** 107 * Warning: may be called on the rendering thread 108 */ 109 @Override 110 public synchronized RendererState getRendererState() { 111 return state; 112 } 113 114 @Override 115 protected Renderer getBackupRenderer() { 116 return this; 117 } 118 119 /** 120 * Disposes this renderer (flushes the associated images). 121 * 122 * Warning: must be called only on the rendering thread (for example in 123 * response to device reset event). 124 * 125 * May be called multiple times. 126 */ 127 protected void dispose() { 128 // even if new peers are added from another thread while we're executing 129 // this on the rendering thread, they won't have any native resources 130 // since we're on the rendering thread, so no need to synchronize 131 for (EffectPeer peer : getPeers()) { 132 peer.dispose(); 133 } 134 synchronized (this) { 135 state = DISPOSED; 136 } 137 GraphicsPipeline.getPipeline(). 138 getResourceFactory(screen).removeFactoryListener(listener); 139 } 140 141 /** 142 * Marks this renderer as lost. 143 * 144 * Warning: may be called on the rendering thread 145 */ 146 protected final synchronized void markLost() { 147 if (state == OK) { 148 state = LOST; 149 } 150 } 151 152 @Override 153 public int getCompatibleWidth(int w) { 154 return PPSDrawable.getCompatibleWidth(screen, w); 155 } 156 157 @Override 158 public int getCompatibleHeight(int h) { 159 return PPSDrawable.getCompatibleHeight(screen, h); 160 } 161 162 @Override 163 public final PPSDrawable createCompatibleImage(int w, int h) { 164 return PPSDrawable.create(screen, w, h); 165 } 166 167 @Override 168 public PPSDrawable getCompatibleImage(int w, int h) { 169 PPSDrawable im = (PPSDrawable)super.getCompatibleImage(w, h); 170 // either we ran out of vram or the device is lost 171 if (im == null) { 172 markLost(); 173 } 174 return im; 175 } 176 177 @Override 178 public LockableResource createFloatTexture(int w, int h) { 179 Texture prismTex = GraphicsPipeline.getPipeline(). 180 getResourceFactory(screen).createFloatTexture(w, h); 181 return new PrTexture(prismTex); 182 } 183 184 @Override 185 public void updateFloatTexture(LockableResource texture, FloatMap map) { 186 FloatBuffer buf = map.getBuffer(); 187 int w = map.getWidth(); 188 int h = map.getHeight(); 189 Image img = Image.fromFloatMapData(buf, w, h); 190 Texture prismTex = ((PrTexture)texture).getTextureObject(); 191 prismTex.update(img); 192 } 193 194 public Shader createShader(String name, 195 Map<String, Integer> samplers, 196 Map<String, Integer> params, 197 boolean isPixcoordUsed) 198 { 199 InputStream pscode = shaderSource.loadSource(name); 200 int maxTexCoordIndex = samplers.keySet().size()-1; 201 ShaderFactory factory = (ShaderFactory) 202 GraphicsPipeline.getPipeline().getResourceFactory(screen); 203 return factory.createShader(pscode, samplers, params, 204 maxTexCoordIndex, 205 isPixcoordUsed, false); 206 } 207 208 /** 209 * Creates a new {@code EffectPeer} instance that can be used by 210 * any of the Prism-based backend implementations. For example, 211 * we can implement the {@code Reflection} effect using only 212 * Prism operations, so we can share that implemenation across all 213 * of the Prism-based backends. 214 * 215 * @param fctx the filter context 216 * @param name the name of the effect peer 217 * @return a new {@code EffectPeer} instance 218 */ 219 private EffectPeer createIntrinsicPeer(FilterContext fctx, String name) { 220 Class klass = null; 221 EffectPeer peer; 222 try { 223 klass = Class.forName(rootPkg + ".impl.prism.Pr" + name + "Peer"); 224 Constructor ctor = klass.getConstructor(new Class[] 225 { FilterContext.class, Renderer.class, String.class }); 226 peer = (EffectPeer)ctor.newInstance(new Object[] 227 { fctx, this, name }); 228 } catch (Exception e) { 229 return null; 230 } 231 return peer; 232 } 233 234 /** 235 * Creates a new {@code EffectPeer} instance that is specific to 236 * the current shader-based backend. 237 * 238 * @param fctx the filter context 239 * @param name the name of the effect peer 240 * @param unrollCount the unroll count 241 * @return a new {@code EffectPeer} instance 242 */ 243 private EffectPeer createPlatformPeer(FilterContext fctx, String name, 244 int unrollCount) 245 { 246 EffectPeer peer; 247 248 String shaderName = name; 249 if (unrollCount > 0) { 250 shaderName += "_" + unrollCount; 251 } 252 try { 253 Class klass = Class.forName(rootPkg + ".impl.prism.ps.PPS" + name + "Peer"); 254 Constructor ctor = klass.getConstructor(new Class[] 255 { FilterContext.class, Renderer.class, String.class }); 256 peer = (EffectPeer)ctor.newInstance(new Object[] 257 { fctx, this, shaderName }); 258 } catch (Exception e) { 259 System.err.println("Error: Prism peer not found for: " + name + 260 " due to error: " + e.getMessage()); 261 return null; 262 } 263 return peer; 264 } 265 266 @Override 267 protected EffectPeer createPeer(FilterContext fctx, String name, 268 int unrollCount) 269 { 270 if (PrRenderer.isIntrinsicPeer(name)) { 271 // create an intrinsic peer (one that's handled by Prism) 272 return createIntrinsicPeer(fctx, name); 273 } else if (needsSWDispMap && name.equals("DisplacementMap")) { 274 return new PPStoPSWDisplacementMapPeer(fctx, this, name); 275 } else { 276 // try creating a platform-specific peer 277 return createPlatformPeer(fctx, name, unrollCount); 278 } 279 } 280 281 @Override 282 public boolean isImageDataCompatible(final ImageData id) { 283 if (getRendererState() == OK) { 284 Filterable f = id.getUntransformedImage(); 285 return (f instanceof PrDrawable && !f.isLost()); 286 } 287 return false; 288 } 289 290 @Override 291 public void clearImage(Filterable filterable) { 292 PPSDrawable img = (PPSDrawable)filterable; 293 img.clear(); 294 } 295 296 @Override 297 public ImageData createImageData(FilterContext fctx, Filterable src) { 298 if (!(src instanceof PrImage)) { 299 throw new IllegalArgumentException("Identity source must be PrImage"); 300 } 301 Image img = ((PrImage)src).getImage(); 302 int w = img.getWidth(); 303 int h = img.getHeight(); 304 PPSDrawable dst = createCompatibleImage(w, h); 305 if (dst == null) { 306 return null; 307 } 308 // RT-27561 309 // TODO: it is wasteful to create an RTT here; eventually it would 310 // be nice if we could use plain Textures as a source Filterable... 311 Graphics g = dst.createGraphics(); 312 ResourceFactory factory = g.getResourceFactory(); 313 Texture tex = factory.createTexture(img, Usage.DEFAULT, 314 WrapMode.CLAMP_TO_EDGE); 315 g.drawTexture(tex, 0, 0, w, h); 316 // NOTE: calling sync() should not be required; ideally calling 317 // Texture.dispose() would flush any pending operations that may 318 // depend on that texture... 319 g.sync(); 320 tex.dispose(); 321 BaseTransform tx; 322 float ps = img.getPixelScale(); 323 if (ps != 1.0f) { 324 ps = 1.0f / ps; 325 tx = BaseTransform.getScaleInstance(ps, ps); 326 } else { 327 tx = BaseTransform.IDENTITY_TRANSFORM; 328 } 329 ImageData id = new ImageData(fctx, dst, new Rectangle(w, h), tx); 330 return id; 331 } 332 333 @Override 334 public Filterable transform(FilterContext fctx, 335 Filterable original, 336 BaseTransform transform, 337 Rectangle origBounds, 338 Rectangle xformBounds) 339 { 340 PPSDrawable dst = (PPSDrawable) 341 getCompatibleImage(xformBounds.width, xformBounds.height); 342 if (dst != null) { 343 Graphics g = dst.createGraphics(); 344 g.translate(-xformBounds.x, -xformBounds.y); 345 g.transform(transform); 346 g.drawTexture(((PPSDrawable)original).getTextureObject(), 347 origBounds.x, origBounds.y, 348 origBounds.width, origBounds.height); 349 } 350 return dst; 351 } 352 353 @Override 354 public ImageData transform(FilterContext fctx, ImageData original, 355 BaseTransform transform, 356 Rectangle origBounds, 357 Rectangle xformBounds) 358 { 359 PPSDrawable dst = (PPSDrawable) 360 getCompatibleImage(xformBounds.width, xformBounds.height); 361 if (dst != null) { 362 PPSDrawable orig = (PPSDrawable)original.getUntransformedImage(); 363 Graphics g = dst.createGraphics(); 364 g.translate(-xformBounds.x, -xformBounds.y); 365 g.transform(transform); 366 g.drawTexture(orig.getTextureObject(), 367 origBounds.x, origBounds.y, 368 origBounds.width, origBounds.height); 369 } 370 original.unref(); 371 return new ImageData(fctx, dst, xformBounds); 372 } 373 374 private static ShaderSource createShaderSource(String name) { 375 Class klass = null; 376 try { 377 klass = Class.forName(name); 378 return (ShaderSource)klass.newInstance(); 379 } catch (ClassNotFoundException e) { 380 System.err.println(name + " class not found"); 381 return null; 382 } catch (Throwable t) { 383 //System.err.println("Error loading renderer:"); 384 //t.printStackTrace(); 385 return null; 386 } 387 } 388 389 public static Renderer createRenderer(FilterContext fctx) { 390 Object ref = fctx.getReferent(); 391 GraphicsPipeline pipe = GraphicsPipeline.getPipeline(); 392 if (pipe == null || !(ref instanceof Screen)) { 393 return null; 394 } 395 Screen screen = (Screen)ref; 396 ShaderSource shaderSource = null; 397 if (pipe.supportsShader(ShaderType.HLSL, ShaderModel.SM3)) { 398 shaderSource = createShaderSource(rootPkg + ".impl.hw.d3d.D3DShaderSource"); 399 } else if (pipe.supportsShader(ShaderType.GLSL, ShaderModel.SM3)) { 400 shaderSource = createShaderSource(rootPkg + ".impl.es2.ES2ShaderSource"); 401 } else { 402 throw new InternalError("Unknown GraphicsPipeline"); 403 } 404 if (shaderSource == null) { 405 return null; 406 } 407 return new PPSRenderer(screen, shaderSource); 408 } 409 }