1 /* 2 * Copyright (c) 2009, 2014, 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 }