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