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 }