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.MediaFrame; 44 import com.sun.prism.Mesh; 45 import com.sun.prism.MeshView; 46 import com.sun.prism.MultiTexture; 47 import com.sun.prism.PhongMaterial; 48 import com.sun.prism.PixelFormat; 49 import com.sun.prism.Presentable; 50 import com.sun.prism.PresentableState; 51 import com.sun.prism.Texture; 52 import com.sun.prism.Texture.Usage; 53 import com.sun.prism.Texture.WrapMode; 54 import com.sun.prism.d3d.D3DResource.D3DRecord; 55 import com.sun.prism.impl.PrismSettings; 56 import com.sun.prism.impl.VertexBuffer; 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 62 class D3DResourceFactory extends BaseShaderFactory { 63 64 private final D3DContext context; 65 private final int maxTextureSize; 66 67 /** 68 * List of disposer records for d3d resources created by the pipeline. 69 * @see D3DResource 70 */ 71 private final LinkedList<D3DResource.D3DRecord> records = 72 new LinkedList<D3DResource.D3DRecord>(); 73 74 D3DResourceFactory(long pContext, Screen screen) { 75 context = new D3DContext(pContext, screen, this); 76 context.initState(); 77 maxTextureSize = computeMaxTextureSize(); 78 79 if (PrismSettings.noClampToZero && PrismSettings.verbose) { 80 System.out.println("prism.noclamptozero not supported by D3D"); 81 } 82 } 83 84 D3DContext getContext() { 85 return context; 86 } 87 88 public TextureResourcePool getTextureResourcePool() { 89 return D3DVramPool.instance; 90 } 91 92 static final int STATS_FREQUENCY = PrismSettings.prismStatFrequency; 93 private int nFrame = -1; 94 private D3DFrameStats frameStats; 95 96 private void displayPrismStatistics() { 97 if (STATS_FREQUENCY > 0) { 98 if (++nFrame == STATS_FREQUENCY) { 99 nFrame = 0; 100 frameStats = context.getFrameStats(true, frameStats); 101 if (frameStats != null) { 102 System.err.println(frameStats.toDebugString(STATS_FREQUENCY)); 103 } 104 } 105 } 106 } 107 108 @Override 109 public boolean isDeviceReady() { 110 displayPrismStatistics(); 111 return context.testLostStateAndReset(); 112 } 113 114 static int nextPowerOfTwo(int val, int max) { 115 if (val > max) { 116 return 0; 117 } 118 int i = 1; 119 while (i < val) { 120 i *= 2; 121 } 122 return i; 123 } 124 125 @Override 126 public boolean isCompatibleTexture(Texture tex) { 127 return tex instanceof D3DTexture; 128 } 129 130 @Override 131 public D3DTexture createTexture(PixelFormat format, Usage usagehint, 132 WrapMode wrapMode, int w, int h) { 133 return createTexture(format, usagehint, wrapMode, w, h, false); 134 } 135 136 @Override 137 public D3DTexture createTexture(PixelFormat format, Usage usagehint, 138 WrapMode wrapMode, int w, int h, boolean useMipmap) { 139 if (!isFormatSupported(format)) { 140 throw new UnsupportedOperationException( 141 "Pixel format " + format + 142 " not supported on this device"); 143 } 144 145 if (format == PixelFormat.MULTI_YCbCr_420) { 146 throw new UnsupportedOperationException("MULTI_YCbCr_420 textures require a MediaFrame"); 147 } 148 149 int allocw, alloch; 150 if (PrismSettings.forcePow2) { 151 allocw = nextPowerOfTwo(w, Integer.MAX_VALUE); 152 alloch = nextPowerOfTwo(h, Integer.MAX_VALUE); 153 } else { 154 allocw = w; 155 alloch = h; 156 } 157 D3DVramPool pool = D3DVramPool.instance; 158 long size = pool.estimateTextureSize(allocw, alloch, format); 159 if (!pool.prepareForAllocation(size)) { 160 return null; 161 } 162 long pResource = nCreateTexture(context.getContextHandle(), 163 format.ordinal(), usagehint.ordinal(), 164 false /*isRTT*/, allocw, alloch, 0, useMipmap); 165 if (pResource == 0L) { 166 return null; 167 } 168 169 int texw = nGetTextureWidth(pResource); 170 int texh = nGetTextureHeight(pResource); 171 if (wrapMode != WrapMode.CLAMP_NOT_NEEDED && (w < texw || h < texh)) { 172 wrapMode = wrapMode.simulatedVersion(); 173 } 174 return new D3DTexture(context, format, wrapMode, pResource, texw, texh, w, h, useMipmap); 175 } 176 177 @Override 178 public Texture createTexture(MediaFrame frame) { 179 frame.holdFrame(); 180 181 int width = frame.getWidth(); 182 int height = frame.getHeight(); 183 int texWidth = frame.getEncodedWidth(); 184 int texHeight = frame.getEncodedHeight(); 185 PixelFormat texFormat = frame.getPixelFormat(); 186 187 if (texFormat == PixelFormat.MULTI_YCbCr_420) { 188 // Create a MultiTexture instead 189 MultiTexture tex = new MultiTexture(texFormat, WrapMode.CLAMP_TO_EDGE, width, height); 190 191 // create/add the subtextures 192 // plane indices: 0 = luma, 1 = Cb, 2 = Cr, 3 (optional) = alpha 193 for (int index = 0; index < frame.planeCount(); index++) { 194 int subWidth = texWidth; 195 int subHeight = texHeight; // might not match height if height is odd 196 197 if (index == PixelFormat.YCBCR_PLANE_CHROMABLUE 198 || index == PixelFormat.YCBCR_PLANE_CHROMARED) 199 { 200 subWidth /= 2; 201 subHeight /= 2; 202 } 203 204 D3DTexture subTex = createTexture(PixelFormat.BYTE_ALPHA, Usage.DYNAMIC, WrapMode.CLAMP_TO_EDGE, 205 subWidth, subHeight); 206 if (subTex == null) { 207 tex.dispose(); 208 return null; 209 } 210 tex.setTexture(subTex, index); 211 } 212 213 frame.releaseFrame(); 214 return tex; 215 } else { 216 D3DVramPool pool = D3DVramPool.instance; 217 long size = pool.estimateTextureSize(texWidth, texHeight, texFormat); 218 if (!pool.prepareForAllocation(size)) { 219 return null; 220 } 221 long pResource = nCreateTexture(context.getContextHandle(), 222 texFormat.ordinal(), Usage.DYNAMIC.ordinal(), 223 false, texWidth, texHeight, 0, false); 224 if (0 == pResource) { 225 return null; 226 } 227 228 int physWidth = nGetTextureWidth(pResource); 229 int physHeight = nGetTextureHeight(pResource); 230 WrapMode wrapMode = (texWidth < physWidth || texHeight < physHeight) 231 ? WrapMode.CLAMP_TO_EDGE_SIMULATED : WrapMode.CLAMP_TO_EDGE; 232 D3DTexture tex = new D3DTexture(context, texFormat, wrapMode, pResource, 233 physWidth, physHeight, width, height, false); 234 frame.releaseFrame(); 235 return tex; 236 } 237 } 238 239 public int getRTTWidth(int w, WrapMode wrapMode) { 240 // D3DRTTexture returns the requested dimension as the content dimension 241 // so the answer here is just "w" despite the fact that a pow2 adjustment 242 // is made for the actual allocation. Typically, D3D supports non-pow2 243 // textures on every implementation so the pow2 code below is not really 244 // encountered in practice anyway (it's only supported for "debugging"). 245 // if (PrismSettings.forcePow2) { 246 // w = nextPowerOfTwo(w, Integer.MAX_VALUE); 247 // } 248 return w; 249 } 250 251 public int getRTTHeight(int h, WrapMode wrapMode) { 252 // D3DRTTexture returns the requested dimension as the content dimension 253 // so the answer here is just "h" despite the fact that a pow2 adjustment 254 // is made for the actual allocation. Typically, D3D supports non-pow2 255 // textures on every implementation so the pow2 code below is not really 256 // encountered in practice anyway (it's only supported for "debugging"). 257 // if (PrismSettings.forcePow2) { 258 // h = nextPowerOfTwo(h, Integer.MAX_VALUE); 259 // } 260 return h; 261 } 262 263 @Override 264 public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode) { 265 return createRTTexture(width, height, wrapMode, false); 266 } 267 268 @Override 269 public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean msaa) { 270 if (PrismSettings.verbose && context.isLost()) { 271 System.err.println("RT Texture allocation while the device is lost"); 272 } 273 274 int createw = width; 275 int createh = height; 276 int cx = 0; 277 int cy = 0; 278 if (PrismSettings.forcePow2) { 279 createw = nextPowerOfTwo(createw, Integer.MAX_VALUE); 280 createh = nextPowerOfTwo(createh, Integer.MAX_VALUE); 281 } 282 D3DVramPool pool = D3DVramPool.instance; 283 int aaSamples; 284 if (msaa) { 285 int maxSamples = D3DPipeline.getInstance().getMaxSamples(); 286 aaSamples = maxSamples < 2 ? 0 : (maxSamples < 4 ? 2 : 4); 287 } else { 288 aaSamples = 0; 289 } 290 // TODO: 3D - Improve estimate to include if multisample rtt 291 long size = pool.estimateRTTextureSize(width, height, false); 292 if (!pool.prepareForAllocation(size)) { 293 return null; 294 } 295 296 long pResource = nCreateTexture(context.getContextHandle(), 297 PixelFormat.INT_ARGB_PRE.ordinal(), 298 Usage.DEFAULT.ordinal(), 299 true /*isRTT*/, createw, createh, aaSamples, false); 300 if (pResource == 0L) { 301 return null; 302 } 303 304 int texw = nGetTextureWidth(pResource); 305 int texh = nGetTextureHeight(pResource); 306 D3DRTTexture rtt = new D3DRTTexture(context, wrapMode, pResource, texw, texh, 307 cx, cy, width, height, aaSamples); 308 // ensure the RTTexture is cleared to all zeros before returning 309 // (Decora relies on the Java2D behavior, where an image is expected 310 // to be fully transparent after initialization) 311 rtt.createGraphics().clear(); 312 return rtt; 313 } 314 315 public Presentable createPresentable(PresentableState pState) { 316 if (PrismSettings.verbose && context.isLost()) { 317 System.err.println("SwapChain allocation while the device is lost"); 318 } 319 320 long pResource = nCreateSwapChain(context.getContextHandle(), 321 pState.getNativeView(), 322 PrismSettings.isVsyncEnabled); 323 324 if (pResource != 0L) { 325 int width = D3DResourceFactory.nGetTextureWidth(pResource); 326 int height = D3DResourceFactory.nGetTextureHeight(pResource); 327 D3DRTTexture rtt = createRTTexture(width, height, WrapMode.CLAMP_NOT_NEEDED, pState.isMSAA()); 328 if (PrismSettings.dirtyOptsEnabled) { 329 rtt.contentsUseful(); 330 } 331 332 if (rtt != null) { 333 return new D3DSwapChain(context, pResource, rtt); 334 } 335 336 D3DResourceFactory.nReleaseResource(context.getContextHandle(), pResource); 337 } 338 return null; 339 340 } 341 342 343 public VertexBuffer createVertexBuffer(int maxQuads) { 344 return new VertexBuffer(maxQuads); 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 }