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 }