1 /*
   2  * Copyright (c) 2009, 2015, 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.prism.d3d;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.lang.reflect.Method;
  32 import java.nio.Buffer;
  33 import java.nio.ByteBuffer;
  34 import java.nio.FloatBuffer;
  35 import java.nio.IntBuffer;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.LinkedList;
  39 import java.util.ListIterator;
  40 import java.util.Map;
  41 
  42 import com.sun.glass.ui.Screen;
  43 import com.sun.prism.Image;
  44 import com.sun.prism.MediaFrame;
  45 import com.sun.prism.Mesh;
  46 import com.sun.prism.MeshView;
  47 import com.sun.prism.MultiTexture;
  48 import com.sun.prism.PhongMaterial;
  49 import com.sun.prism.PixelFormat;
  50 import com.sun.prism.Presentable;
  51 import com.sun.prism.PresentableState;
  52 import com.sun.prism.Texture;
  53 import com.sun.prism.Texture.Usage;
  54 import com.sun.prism.Texture.WrapMode;
  55 import com.sun.prism.d3d.D3DResource.D3DRecord;
  56 import com.sun.prism.impl.PrismSettings;
  57 import com.sun.prism.impl.VertexBuffer;
  58 import com.sun.prism.impl.ps.BaseShaderFactory;
  59 import com.sun.prism.impl.TextureResourcePool;
  60 import com.sun.prism.ps.Shader;
  61 import com.sun.prism.ps.ShaderFactory;
  62 import java.util.WeakHashMap;
  63 
  64 class D3DResourceFactory extends BaseShaderFactory {
  65     private static final Map<Image,Texture> clampTexCache = new WeakHashMap<>();
  66     private static final Map<Image,Texture> repeatTexCache = new WeakHashMap<>();
  67     private static final Map<Image,Texture> mipmapTexCache = new WeakHashMap<>();
  68 
  69     private final D3DContext context;
  70     private final int maxTextureSize;
  71 
  72     /**
  73      * List of disposer records for d3d resources created by the pipeline.
  74      * @see D3DResource
  75      */
  76     private final LinkedList<D3DResource.D3DRecord> records =
  77         new LinkedList<D3DResource.D3DRecord>();
  78 
  79     D3DResourceFactory(long pContext, Screen screen) {
  80         super(clampTexCache, repeatTexCache, mipmapTexCache);
  81         context = new D3DContext(pContext, screen, this);
  82         context.initState();
  83         maxTextureSize = computeMaxTextureSize();
  84 
  85         if (PrismSettings.noClampToZero && PrismSettings.verbose) {
  86             System.out.println("prism.noclamptozero not supported by D3D");
  87         }
  88     }
  89 
  90     D3DContext getContext() {
  91         return context;
  92     }
  93 
  94     public TextureResourcePool getTextureResourcePool() {
  95         return D3DVramPool.instance;
  96     }
  97 
  98     static final int STATS_FREQUENCY = PrismSettings.prismStatFrequency;
  99     private int nFrame = -1;
 100     private D3DFrameStats frameStats;
 101 
 102     private void displayPrismStatistics() {
 103         if (STATS_FREQUENCY > 0) {
 104             if (++nFrame == STATS_FREQUENCY) {
 105                 nFrame = 0;
 106                 frameStats = context.getFrameStats(true, frameStats);
 107                 if (frameStats != null) {
 108                     System.err.println(frameStats.toDebugString(STATS_FREQUENCY));
 109                 }
 110             }
 111         }
 112     }
 113 
 114     @Override
 115     public boolean isDeviceReady() {
 116         displayPrismStatistics();
 117         return context.testLostStateAndReset();
 118     }
 119 
 120     static int nextPowerOfTwo(int val, int max) {
 121         if (val > max) {
 122             return 0;
 123         }
 124         int i = 1;
 125         while (i < val) {
 126             i *= 2;
 127         }
 128         return i;
 129     }
 130 
 131     @Override
 132     public boolean isCompatibleTexture(Texture tex) {
 133         return tex instanceof D3DTexture;
 134     }
 135   
 136     @Override
 137     public D3DTexture createTexture(PixelFormat format, Usage usagehint,
 138             WrapMode wrapMode, int w, int h) {
 139         return createTexture(format, usagehint, wrapMode, w, h, false);
 140     }
 141 
 142     @Override
 143     public D3DTexture createTexture(PixelFormat format, Usage usagehint,
 144             WrapMode wrapMode, int w, int h, boolean useMipmap) {
 145         if (!isFormatSupported(format)) {
 146             throw new UnsupportedOperationException(
 147                 "Pixel format " + format +
 148                 " not supported on this device");
 149         }
 150 
 151         if (format == PixelFormat.MULTI_YCbCr_420) {
 152             throw new UnsupportedOperationException("MULTI_YCbCr_420 textures require a MediaFrame");
 153         }
 154 
 155         int allocw, alloch;
 156         if (PrismSettings.forcePow2) {
 157             allocw = nextPowerOfTwo(w, Integer.MAX_VALUE);
 158             alloch = nextPowerOfTwo(h, Integer.MAX_VALUE);
 159         } else {
 160             allocw = w;
 161             alloch = h;
 162         }
 163         D3DVramPool pool = D3DVramPool.instance;
 164         long size = pool.estimateTextureSize(allocw, alloch, format);
 165         if (!pool.prepareForAllocation(size)) {
 166             return null;
 167         }
 168         long pResource = nCreateTexture(context.getContextHandle(),
 169                                         format.ordinal(), usagehint.ordinal(),
 170                                         false /*isRTT*/, allocw, alloch, 0, useMipmap);
 171         if (pResource == 0L) {
 172             return null;
 173         }
 174 
 175         int texw = nGetTextureWidth(pResource);
 176         int texh = nGetTextureHeight(pResource);
 177         if (wrapMode != WrapMode.CLAMP_NOT_NEEDED && (w < texw || h < texh)) {
 178             wrapMode = wrapMode.simulatedVersion();
 179         }
 180         return new D3DTexture(context, format, wrapMode, pResource, texw, texh, w, h, useMipmap);
 181     }
 182 
 183     @Override
 184     public Texture createTexture(MediaFrame frame) {
 185         frame.holdFrame();
 186 
 187         int width = frame.getWidth();
 188         int height = frame.getHeight();
 189         int texWidth = frame.getEncodedWidth();
 190         int texHeight = frame.getEncodedHeight();
 191         PixelFormat texFormat = frame.getPixelFormat();
 192 
 193         if (texFormat == PixelFormat.MULTI_YCbCr_420) {
 194             // Create a MultiTexture instead
 195             MultiTexture tex = new MultiTexture(texFormat, WrapMode.CLAMP_TO_EDGE, width, height);
 196 
 197             // create/add the subtextures
 198             // plane indices: 0 = luma, 1 = Cb, 2 = Cr, 3 (optional) = alpha
 199             for (int index = 0; index < frame.planeCount(); index++) {
 200                 int subWidth = texWidth;
 201                 int subHeight =  texHeight; // might not match height if height is odd
 202 
 203                 if (index == PixelFormat.YCBCR_PLANE_CHROMABLUE
 204                         || index == PixelFormat.YCBCR_PLANE_CHROMARED)
 205                 {
 206                     subWidth /= 2;
 207                     subHeight /= 2;
 208                 }
 209 
 210                 D3DTexture subTex = createTexture(PixelFormat.BYTE_ALPHA, Usage.DYNAMIC, WrapMode.CLAMP_TO_EDGE,
 211                                                   subWidth, subHeight);
 212                 if (subTex == null) {
 213                     tex.dispose();
 214                     return null;
 215                 }
 216                 tex.setTexture(subTex, index);
 217             }
 218 
 219             frame.releaseFrame();
 220             return tex;
 221         } else {
 222             D3DVramPool pool = D3DVramPool.instance;
 223             long size = pool.estimateTextureSize(texWidth, texHeight, texFormat);
 224             if (!pool.prepareForAllocation(size)) {
 225                 return null;
 226             }
 227             long pResource = nCreateTexture(context.getContextHandle(),
 228                     texFormat.ordinal(), Usage.DYNAMIC.ordinal(),
 229                     false, texWidth, texHeight, 0, false);
 230             if (0 == pResource) {
 231                 return null;
 232             }
 233 
 234             int physWidth = nGetTextureWidth(pResource);
 235             int physHeight = nGetTextureHeight(pResource);
 236             WrapMode wrapMode = (texWidth < physWidth || texHeight < physHeight)
 237                     ? WrapMode.CLAMP_TO_EDGE_SIMULATED : WrapMode.CLAMP_TO_EDGE;
 238             D3DTexture tex = new D3DTexture(context, texFormat, wrapMode, pResource,
 239                                             physWidth, physHeight, width, height, false);
 240             frame.releaseFrame();
 241             return tex;
 242         }
 243     }
 244 
 245     public int getRTTWidth(int w, WrapMode wrapMode) {
 246         // D3DRTTexture returns the requested dimension as the content dimension
 247         // so the answer here is just "w" despite the fact that a pow2 adjustment
 248         // is made for the actual allocation.  Typically, D3D supports non-pow2
 249         // textures on every implementation so the pow2 code below is not really
 250         // encountered in practice anyway (it's only supported for "debugging").
 251 //        if (PrismSettings.forcePow2) {
 252 //            w = nextPowerOfTwo(w, Integer.MAX_VALUE);
 253 //        }
 254         return w;
 255     }
 256 
 257     public int getRTTHeight(int h, WrapMode wrapMode) {
 258         // D3DRTTexture returns the requested dimension as the content dimension
 259         // so the answer here is just "h" despite the fact that a pow2 adjustment
 260         // is made for the actual allocation.  Typically, D3D supports non-pow2
 261         // textures on every implementation so the pow2 code below is not really
 262         // encountered in practice anyway (it's only supported for "debugging").
 263 //        if (PrismSettings.forcePow2) {
 264 //            h = nextPowerOfTwo(h, Integer.MAX_VALUE);
 265 //        }
 266         return h;
 267     }
 268 
 269     @Override
 270     public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
 271         return createRTTexture(width, height, wrapMode, false);
 272     }
 273 
 274     @Override
 275     public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean msaa) {
 276         if (PrismSettings.verbose && context.isLost()) {
 277             System.err.println("RT Texture allocation while the device is lost");
 278         }
 279 
 280         int createw = width;
 281         int createh = height;
 282         int cx = 0;
 283         int cy = 0;
 284         if (PrismSettings.forcePow2) {
 285             createw = nextPowerOfTwo(createw, Integer.MAX_VALUE);
 286             createh = nextPowerOfTwo(createh, Integer.MAX_VALUE);
 287         }
 288         D3DVramPool pool = D3DVramPool.instance;
 289         int aaSamples;
 290         if (msaa) {
 291             int maxSamples = D3DPipeline.getInstance().getMaxSamples();
 292             aaSamples =  maxSamples < 2 ? 0 : (maxSamples < 4 ? 2 : 4);
 293         } else {
 294             aaSamples = 0;
 295         }
 296         // TODO: 3D - Improve estimate to include if multisample rtt
 297         long size = pool.estimateRTTextureSize(width, height, false);
 298         if (!pool.prepareForAllocation(size)) {
 299             return null;
 300         }
 301 
 302         long pResource = nCreateTexture(context.getContextHandle(),
 303                                         PixelFormat.INT_ARGB_PRE.ordinal(),
 304                                         Usage.DEFAULT.ordinal(),
 305                                         true /*isRTT*/, createw, createh, aaSamples, false);
 306         if (pResource == 0L) {
 307             return null;
 308         }
 309 
 310         int texw = nGetTextureWidth(pResource);
 311         int texh = nGetTextureHeight(pResource);
 312         D3DRTTexture rtt = new D3DRTTexture(context, wrapMode, pResource, texw, texh,
 313                                             cx, cy, width, height, aaSamples);
 314         // ensure the RTTexture is cleared to all zeros before returning
 315         // (Decora relies on the Java2D behavior, where an image is expected
 316         // to be fully transparent after initialization)
 317         rtt.createGraphics().clear();
 318         return rtt;
 319     }
 320 
 321     public Presentable createPresentable(PresentableState pState) {
 322         if (PrismSettings.verbose && context.isLost()) {
 323             System.err.println("SwapChain allocation while the device is lost");
 324         }
 325 
 326         long pResource = nCreateSwapChain(context.getContextHandle(),
 327                                           pState.getNativeView(),
 328                                           PrismSettings.isVsyncEnabled);
 329 
 330         if (pResource != 0L) {
 331             int width = D3DResourceFactory.nGetTextureWidth(pResource);
 332             int height = D3DResourceFactory.nGetTextureHeight(pResource);
 333             D3DRTTexture rtt = createRTTexture(width, height, WrapMode.CLAMP_NOT_NEEDED, pState.isMSAA());
 334             if (PrismSettings.dirtyOptsEnabled) {
 335                 rtt.contentsUseful();
 336             }
 337 
 338             if (rtt != null) {
 339                 return new D3DSwapChain(context, pResource, rtt);
 340             }
 341 
 342             D3DResourceFactory.nReleaseResource(context.getContextHandle(), pResource);
 343         }
 344         return null;
 345 
 346     }
 347 
 348 
 349     public VertexBuffer createVertexBuffer(int maxQuads) {
 350         return new VertexBuffer(maxQuads);
 351     }
 352 
 353     private static ByteBuffer getBuffer(InputStream is) {
 354         if (is == null) {
 355            throw new RuntimeException("InputStream must be non-null");
 356         }
 357         try {
 358             int len = 4096;
 359             byte[] data = new byte[len];
 360             BufferedInputStream bis = new BufferedInputStream(is, len);
 361             int offset = 0;
 362             int readBytes = -1;
 363             while ((readBytes = bis.read(data, offset, len - offset)) != -1) {
 364                 offset += readBytes;
 365                 if (len - offset == 0) {
 366                     // grow the array
 367                     len *= 2;
 368                     // was
 369                     // data = Arrays.copyOf(data, len);
 370                     //
 371                     byte[] newdata = new byte[len];
 372                     System.arraycopy(data, 0, newdata, 0, data.length);
 373                     data = newdata;
 374                 }
 375             }
 376             bis.close();
 377             // NOTE: for now the D3DShader native code only knows how to
 378             // deal with direct ByteBuffers, so we have to dump the byte[]
 379             // into a newly allocated direct buffer...
 380             ByteBuffer buf = ByteBuffer.allocateDirect(offset);
 381             buf.put(data, 0, offset);
 382             return buf;
 383         } catch (IOException e) {
 384             throw new RuntimeException("Error loading D3D shader object", e);
 385         }
 386     }
 387 
 388     public Shader createShader(InputStream pixelShaderCode,
 389                                Map<String, Integer> samplers,
 390                                Map<String, Integer> params,
 391                                int maxTexCoordIndex,
 392                                boolean isPixcoordUsed,
 393                                boolean isPerVertexColorUsed)
 394     {
 395         long shaderHandle = D3DShader.init(
 396                 context.getContextHandle(), getBuffer(pixelShaderCode),
 397                 maxTexCoordIndex, isPixcoordUsed, isPerVertexColorUsed);
 398 
 399         return new D3DShader(context, shaderHandle, params);
 400     }
 401 
 402     public Shader createStockShader(final String name) {
 403         if (name == null) {
 404             throw new IllegalArgumentException("Shader name must be non-null");
 405         }
 406         try {
 407             InputStream stream = AccessController.doPrivileged(
 408                     (PrivilegedAction<InputStream>) () -> D3DResourceFactory.class.
 409                            getResourceAsStream("hlsl/" + name + ".obj")
 410             );
 411             Class klass = Class.forName("com.sun.prism.shader." + name + "_Loader");
 412             Method m = klass.getMethod("loadShader",
 413                 new Class[] { ShaderFactory.class, InputStream.class });
 414             return (Shader)m.invoke(null, new Object[] { this, stream });
 415         } catch (Throwable e) {
 416             e.printStackTrace();
 417             throw new InternalError("Error loading stock shader " + name);
 418         }
 419     }
 420 
 421     @Override
 422     public void dispose() {
 423         throw new UnsupportedOperationException("Not supported yet.");
 424     }
 425 
 426     @Override
 427     public boolean isFormatSupported(PixelFormat format) {
 428         return true;
 429     }
 430 
 431     private int computeMaxTextureSize() {
 432         int size = nGetMaximumTextureSize(context.getContextHandle());
 433         if (PrismSettings.verbose) {
 434             System.err.println("Maximum supported texture size: " + size);
 435         }
 436         if (size > PrismSettings.maxTextureSize) {
 437             size = PrismSettings.maxTextureSize;
 438             if (PrismSettings.verbose) {
 439                 System.err.println("Maximum texture size clamped to " + size);
 440             }
 441         }
 442         return size;
 443     }
 444 
 445     public int getMaximumTextureSize() {
 446         return maxTextureSize;
 447     }
 448 
 449     @Override
 450     protected void notifyReset() {
 451         for (ListIterator<D3DRecord> it = records.listIterator(); it.hasNext();) {
 452             D3DRecord r = it.next();
 453             if (r.isDefaultPool()) {
 454                 r.markDisposed();
 455                 it.remove();
 456             }
 457         }
 458         super.notifyReset();
 459     }
 460 
 461     @Override
 462     protected void notifyReleased() {
 463         for (ListIterator<D3DRecord> it = records.listIterator(); it.hasNext();) {
 464             D3DRecord r = it.next();
 465             r.markDisposed();
 466         }
 467         records.clear();
 468         super.notifyReleased();
 469     }
 470 
 471     void addRecord(D3DRecord record) {
 472         records.add(record);
 473     }
 474 
 475     void removeRecord(D3DRecord record) {
 476         records.remove(record);
 477     }
 478 
 479     public PhongMaterial createPhongMaterial() {
 480         return D3DPhongMaterial.create(context);
 481     }
 482 
 483     public MeshView createMeshView(Mesh mesh) {
 484         return D3DMeshView.create(context, (D3DMesh) mesh);
 485 
 486     }
 487 
 488     public Mesh createMesh() {
 489         return D3DMesh.create(context);
 490     }
 491 
 492     static native long nGetContext(int adapterOrdinal);
 493     static native boolean nIsDefaultPool(long pResource);
 494     static native int nTestCooperativeLevel(long pContext);
 495     static native int nResetDevice(long pContext);
 496     static native long nCreateTexture(long pContext,
 497                                       int format, int hint,
 498                                       boolean isRTT,
 499                                       int width, int height, int samples,
 500                                       boolean useMipmap);
 501     static native long nCreateSwapChain(long pContext, long hwnd,
 502                                         boolean isVsyncEnabled);
 503     static native int nReleaseResource(long pContext, long resource);
 504     static native int nGetMaximumTextureSize(long pContext);
 505     static native int nGetTextureWidth(long pResource);
 506     static native int nGetTextureHeight(long pResource);
 507     static native int nReadPixelsI(long pContext, long pResource,
 508                                     long length,
 509                                     Buffer pixels, int[] arr,
 510                                     int contentWidth, int contentHeight);
 511     static native int nReadPixelsB(long pContext, long pResource,
 512                                     long length,
 513                                     Buffer pixels, byte[] arr,
 514                                     int contentWidth, int contentHeight);
 515     static native int nUpdateTextureI(long contextHandle, long pResource,
 516                                       IntBuffer buf, int[] pixels,
 517                                       int dstx, int dsty,
 518                                       int srcx, int srcy,
 519                                       int srcw, int srch, int srcscan);
 520     static native int nUpdateTextureF(long contextHandle, long pResource,
 521                                       FloatBuffer buf, float[] pixels,
 522                                       int dstx, int dsty,
 523                                       int srcx, int srcy,
 524                                       int srcw, int srch, int srcscan);
 525     static native int nUpdateTextureB(long contextHandle, long pResource,
 526                                       ByteBuffer buf, byte[] pixels,
 527                                       int formatHint,
 528                                       int dstx, int dsty,
 529                                       int srcx, int srcy,
 530                                       int srcw, int srch, int srcscan);
 531 
 532     static native long nGetDevice(long pContext);
 533     static native long nGetNativeTextureObject(long pResource);
 534 }