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 }