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