com.sun.prism.d3d.D3DResourceFactory Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.prism.d3d;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import com.sun.glass.ui.Screen;
import com.sun.prism.Image;
import com.sun.prism.MediaFrame;
import com.sun.prism.Mesh;
import com.sun.prism.MeshView;
import com.sun.prism.MultiTexture;
import com.sun.prism.PhongMaterial;
import com.sun.prism.PixelFormat;
import com.sun.prism.Presentable;
import com.sun.prism.PresentableState;
import com.sun.prism.Texture;
import com.sun.prism.Texture.Usage;
import com.sun.prism.Texture.WrapMode;
import com.sun.prism.d3d.D3DResource.D3DRecord;
import com.sun.prism.impl.PrismSettings;
import com.sun.prism.impl.ps.BaseShaderFactory;
import com.sun.prism.impl.TextureResourcePool;
import com.sun.prism.ps.Shader;
import com.sun.prism.ps.ShaderFactory;
import java.util.WeakHashMap;
class D3DResourceFactory extends BaseShaderFactory {
private static final Map clampTexCache = new WeakHashMap<>();
private static final Map repeatTexCache = new WeakHashMap<>();
private static final Map mipmapTexCache = new WeakHashMap<>();
private final D3DContext context;
private final int maxTextureSize;
/**
* List of disposer records for d3d resources created by the pipeline.
* @see D3DResource
*/
private final LinkedList records =
new LinkedList<>();
D3DResourceFactory(long pContext, Screen screen) {
super(clampTexCache, repeatTexCache, mipmapTexCache);
context = new D3DContext(pContext, screen, this);
context.initState();
maxTextureSize = computeMaxTextureSize();
if (PrismSettings.noClampToZero && PrismSettings.verbose) {
System.out.println("prism.noclamptozero not supported by D3D");
}
}
D3DContext getContext() {
return context;
}
@Override
public TextureResourcePool getTextureResourcePool() {
return D3DVramPool.instance;
}
static final int STATS_FREQUENCY = PrismSettings.prismStatFrequency;
private int nFrame = -1;
private D3DFrameStats frameStats;
private void displayPrismStatistics() {
if (STATS_FREQUENCY > 0) {
if (++nFrame == STATS_FREQUENCY) {
nFrame = 0;
frameStats = context.getFrameStats(true, frameStats);
if (frameStats != null) {
System.err.println(frameStats.toDebugString(STATS_FREQUENCY));
}
}
}
}
@Override
public boolean isDeviceReady() {
if (isDisposed()) {
return false;
}
displayPrismStatistics();
return context.testLostStateAndReset();
}
static int nextPowerOfTwo(int val, int max) {
if (val > max) {
return 0;
}
int i = 1;
while (i < val) {
i *= 2;
}
return i;
}
@Override
public boolean isCompatibleTexture(Texture tex) {
return tex instanceof D3DTexture;
}
@Override
public D3DTexture createTexture(PixelFormat format, Usage usagehint,
WrapMode wrapMode, int w, int h) {
return createTexture(format, usagehint, wrapMode, w, h, false);
}
@Override
public D3DTexture createTexture(PixelFormat format, Usage usagehint,
WrapMode wrapMode, int w, int h, boolean useMipmap) {
if (checkDisposed()) return null;
if (!isFormatSupported(format)) {
throw new UnsupportedOperationException(
"Pixel format " + format +
" not supported on this device");
}
if (format == PixelFormat.MULTI_YCbCr_420) {
throw new UnsupportedOperationException("MULTI_YCbCr_420 textures require a MediaFrame");
}
int allocw, alloch;
if (PrismSettings.forcePow2) {
allocw = nextPowerOfTwo(w, Integer.MAX_VALUE);
alloch = nextPowerOfTwo(h, Integer.MAX_VALUE);
} else {
allocw = w;
alloch = h;
}
if (allocw <= 0 || alloch <= 0) {
throw new RuntimeException("Illegal texture dimensions (" + allocw + "x" + alloch + ")");
}
int bpp = format.getBytesPerPixelUnit();
if (allocw >= (Integer.MAX_VALUE / alloch / bpp)) {
throw new RuntimeException("Illegal texture dimensions (" + allocw + "x" + alloch + ")");
}
D3DVramPool pool = D3DVramPool.instance;
long size = pool.estimateTextureSize(allocw, alloch, format);
if (!pool.prepareForAllocation(size)) {
return null;
}
long pResource = nCreateTexture(context.getContextHandle(),
format.ordinal(), usagehint.ordinal(),
false /*isRTT*/, allocw, alloch, 0, useMipmap);
if (pResource == 0L) {
return null;
}
int texw = nGetTextureWidth(pResource);
int texh = nGetTextureHeight(pResource);
if (wrapMode != WrapMode.CLAMP_NOT_NEEDED && (w < texw || h < texh)) {
wrapMode = wrapMode.simulatedVersion();
}
return new D3DTexture(context, format, wrapMode, pResource, texw, texh, w, h, useMipmap);
}
@Override
public Texture createTexture(MediaFrame frame) {
if (checkDisposed()) return null;
frame.holdFrame();
int width = frame.getWidth();
int height = frame.getHeight();
int texWidth = frame.getEncodedWidth();
int texHeight = frame.getEncodedHeight();
PixelFormat texFormat = frame.getPixelFormat();
if (texFormat == PixelFormat.MULTI_YCbCr_420) {
// Create a MultiTexture instead
MultiTexture tex = new MultiTexture(texFormat, WrapMode.CLAMP_TO_EDGE, width, height);
// create/add the subtextures
// plane indices: 0 = luma, 1 = Cb, 2 = Cr, 3 (optional) = alpha
for (int index = 0; index < frame.planeCount(); index++) {
int subWidth = texWidth;
int subHeight = texHeight; // might not match height if height is odd
if (index == PixelFormat.YCBCR_PLANE_CHROMABLUE
|| index == PixelFormat.YCBCR_PLANE_CHROMARED)
{
subWidth /= 2;
subHeight /= 2;
}
D3DTexture subTex = createTexture(PixelFormat.BYTE_ALPHA, Usage.DYNAMIC, WrapMode.CLAMP_TO_EDGE,
subWidth, subHeight);
if (subTex == null) {
tex.dispose();
return null;
}
tex.setTexture(subTex, index);
}
frame.releaseFrame();
return tex;
} else {
if (texWidth <= 0 || texHeight <= 0) {
frame.releaseFrame();
throw new RuntimeException("Illegal texture dimensions (" + texWidth + "x" + texHeight + ")");
}
int bpp = texFormat.getBytesPerPixelUnit();
if (texWidth >= (Integer.MAX_VALUE / texHeight / bpp)) {
frame.releaseFrame();
throw new RuntimeException("Illegal texture dimensions (" + texWidth + "x" + texHeight + ")");
}
D3DVramPool pool = D3DVramPool.instance;
long size = pool.estimateTextureSize(texWidth, texHeight, texFormat);
if (!pool.prepareForAllocation(size)) {
return null;
}
long pResource = nCreateTexture(context.getContextHandle(),
texFormat.ordinal(), Usage.DYNAMIC.ordinal(),
false, texWidth, texHeight, 0, false);
if (0 == pResource) {
return null;
}
int physWidth = nGetTextureWidth(pResource);
int physHeight = nGetTextureHeight(pResource);
WrapMode wrapMode = (texWidth < physWidth || texHeight < physHeight)
? WrapMode.CLAMP_TO_EDGE_SIMULATED : WrapMode.CLAMP_TO_EDGE;
D3DTexture tex = new D3DTexture(context, texFormat, wrapMode, pResource,
physWidth, physHeight, width, height, false);
frame.releaseFrame();
return tex;
}
}
@Override
public int getRTTWidth(int w, WrapMode wrapMode) {
// D3DRTTexture returns the requested dimension as the content dimension
// so the answer here is just "w" despite the fact that a pow2 adjustment
// is made for the actual allocation. Typically, D3D supports non-pow2
// textures on every implementation so the pow2 code below is not really
// encountered in practice anyway (it's only supported for "debugging").
// if (PrismSettings.forcePow2) {
// w = nextPowerOfTwo(w, Integer.MAX_VALUE);
// }
return w;
}
@Override
public int getRTTHeight(int h, WrapMode wrapMode) {
// D3DRTTexture returns the requested dimension as the content dimension
// so the answer here is just "h" despite the fact that a pow2 adjustment
// is made for the actual allocation. Typically, D3D supports non-pow2
// textures on every implementation so the pow2 code below is not really
// encountered in practice anyway (it's only supported for "debugging").
// if (PrismSettings.forcePow2) {
// h = nextPowerOfTwo(h, Integer.MAX_VALUE);
// }
return h;
}
@Override
public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode) {
return createRTTexture(width, height, wrapMode, false);
}
@Override
public D3DRTTexture createRTTexture(int width, int height, WrapMode wrapMode, boolean msaa) {
if (checkDisposed()) return null;
if (PrismSettings.verbose && context.isLost()) {
System.err.println("RT Texture allocation while the device is lost");
}
int createw = width;
int createh = height;
int cx = 0;
int cy = 0;
if (PrismSettings.forcePow2) {
createw = nextPowerOfTwo(createw, Integer.MAX_VALUE);
createh = nextPowerOfTwo(createh, Integer.MAX_VALUE);
}
if (createw <= 0 || createh <= 0) {
throw new RuntimeException("Illegal texture dimensions (" + createw + "x" + createh + ")");
}
PixelFormat format = PixelFormat.INT_ARGB_PRE;
int bpp = format.getBytesPerPixelUnit();
if (createw >= (Integer.MAX_VALUE / createh / bpp)) {
throw new RuntimeException("Illegal texture dimensions (" + createw + "x" + createh + ")");
}
D3DVramPool pool = D3DVramPool.instance;
int aaSamples;
if (msaa) {
int maxSamples = D3DPipeline.getInstance().getMaxSamples();
aaSamples = maxSamples < 2 ? 0 : (maxSamples < 4 ? 2 : 4);
} else {
aaSamples = 0;
}
// TODO: 3D - Improve estimate to include if multisample rtt
long size = pool.estimateRTTextureSize(width, height, false);
if (!pool.prepareForAllocation(size)) {
return null;
}
long pResource = nCreateTexture(context.getContextHandle(),
format.ordinal(),
Usage.DEFAULT.ordinal(),
true /*isRTT*/, createw, createh, aaSamples, false);
if (pResource == 0L) {
return null;
}
int texw = nGetTextureWidth(pResource);
int texh = nGetTextureHeight(pResource);
D3DRTTexture rtt = new D3DRTTexture(context, wrapMode, pResource, texw, texh,
cx, cy, width, height, aaSamples);
// ensure the RTTexture is cleared to all zeros before returning
// (Decora relies on the Java2D behavior, where an image is expected
// to be fully transparent after initialization)
rtt.createGraphics().clear();
return rtt;
}
@Override
public Presentable createPresentable(PresentableState pState) {
if (checkDisposed()) return null;
if (PrismSettings.verbose && context.isLost()) {
System.err.println("SwapChain allocation while the device is lost");
}
long pResource = nCreateSwapChain(context.getContextHandle(),
pState.getNativeView(),
PrismSettings.isVsyncEnabled);
if (pResource != 0L) {
int width = pState.getRenderWidth();
int height = pState.getRenderHeight();
D3DRTTexture rtt = createRTTexture(width, height, WrapMode.CLAMP_NOT_NEEDED, pState.isMSAA());
if (PrismSettings.dirtyOptsEnabled) {
rtt.contentsUseful();
}
if (rtt != null) {
return new D3DSwapChain(context, pResource, rtt, pState.getRenderScaleX(), pState.getRenderScaleY());
}
D3DResourceFactory.nReleaseResource(context.getContextHandle(), pResource);
}
return null;
}
private static ByteBuffer getBuffer(InputStream is) {
if (is == null) {
throw new RuntimeException("InputStream must be non-null");
}
try {
int len = 4096;
byte[] data = new byte[len];
BufferedInputStream bis = new BufferedInputStream(is, len);
int offset = 0;
int readBytes = -1;
while ((readBytes = bis.read(data, offset, len - offset)) != -1) {
offset += readBytes;
if (len - offset == 0) {
// grow the array
len *= 2;
// was
// data = Arrays.copyOf(data, len);
//
byte[] newdata = new byte[len];
System.arraycopy(data, 0, newdata, 0, data.length);
data = newdata;
}
}
bis.close();
// NOTE: for now the D3DShader native code only knows how to
// deal with direct ByteBuffers, so we have to dump the byte[]
// into a newly allocated direct buffer...
ByteBuffer buf = ByteBuffer.allocateDirect(offset);
buf.put(data, 0, offset);
return buf;
} catch (IOException e) {
throw new RuntimeException("Error loading D3D shader object", e);
}
}
@Override
public Shader createShader(String pixelShaderName,
InputStream pixelShaderCode,
Map samplers,
Map params,
int maxTexCoordIndex,
boolean isPixcoordUsed,
boolean isPerVertexColorUsed)
{
if (checkDisposed()) return null;
long shaderHandle = D3DShader.init(
context.getContextHandle(), getBuffer(pixelShaderCode),
maxTexCoordIndex, isPixcoordUsed, isPerVertexColorUsed);
return new D3DShader(context, shaderHandle, params);
}
@Override
public Shader createStockShader(final String name) {
if (name == null) {
throw new IllegalArgumentException("Shader name must be non-null");
}
try {
@SuppressWarnings("removal")
InputStream stream = AccessController.doPrivileged(
(PrivilegedAction) () -> D3DResourceFactory.class.
getResourceAsStream("hlsl/" + name + ".obj")
);
Class klass = Class.forName("com.sun.prism.shader." + name + "_Loader");
Method m = klass.getMethod("loadShader",
new Class[] { ShaderFactory.class, String.class, InputStream.class });
return (Shader)m.invoke(null, new Object[] { this, name, stream });
} catch (Throwable e) {
e.printStackTrace();
throw new InternalError("Error loading stock shader " + name);
}
}
@Override
public boolean isFormatSupported(PixelFormat format) {
return true;
}
private int computeMaxTextureSize() {
int size = nGetMaximumTextureSize(context.getContextHandle());
if (PrismSettings.verbose) {
System.err.println("Maximum supported texture size: " + size);
}
if (size > PrismSettings.maxTextureSize) {
size = PrismSettings.maxTextureSize;
if (PrismSettings.verbose) {
System.err.println("Maximum texture size clamped to " + size);
}
}
return size;
}
@Override
public int getMaximumTextureSize() {
return maxTextureSize;
}
@Override
protected void notifyReset() {
for (ListIterator it = records.listIterator(); it.hasNext();) {
D3DRecord r = it.next();
if (r.isDefaultPool()) {
r.markDisposed();
it.remove();
}
}
super.notifyReset();
}
@Override
public void dispose() {
context.dispose();
for (ListIterator it = records.listIterator(); it.hasNext();) {
D3DRecord r = it.next();
r.markDisposed();
}
records.clear();
super.dispose();
}
void addRecord(D3DRecord record) {
records.add(record);
}
void removeRecord(D3DRecord record) {
records.remove(record);
}
@Override
public PhongMaterial createPhongMaterial() {
if (checkDisposed()) return null;
return D3DPhongMaterial.create(context);
}
@Override
public MeshView createMeshView(Mesh mesh) {
if (checkDisposed()) return null;
return D3DMeshView.create(context, (D3DMesh) mesh);
}
@Override
public Mesh createMesh() {
if (checkDisposed()) return null;
return D3DMesh.create(context);
}
static native long nGetContext(int adapterOrdinal);
static native boolean nIsDefaultPool(long pResource);
static native int nTestCooperativeLevel(long pContext);
static native int nResetDevice(long pContext);
static native long nCreateTexture(long pContext,
int format, int hint,
boolean isRTT,
int width, int height, int samples,
boolean useMipmap);
static native long nCreateSwapChain(long pContext, long hwnd,
boolean isVsyncEnabled);
static native int nReleaseResource(long pContext, long resource);
static native int nGetMaximumTextureSize(long pContext);
static native int nGetTextureWidth(long pResource);
static native int nGetTextureHeight(long pResource);
static native int nReadPixelsI(long pContext, long pResource,
long length,
Buffer pixels, int[] arr,
int contentWidth, int contentHeight);
static native int nReadPixelsB(long pContext, long pResource,
long length,
Buffer pixels, byte[] arr,
int contentWidth, int contentHeight);
static native int nUpdateTextureI(long contextHandle, long pResource,
IntBuffer buf, int[] pixels,
int dstx, int dsty,
int srcx, int srcy,
int srcw, int srch, int srcscan);
static native int nUpdateTextureF(long contextHandle, long pResource,
FloatBuffer buf, float[] pixels,
int dstx, int dsty,
int srcx, int srcy,
int srcw, int srch, int srcscan);
static native int nUpdateTextureB(long contextHandle, long pResource,
ByteBuffer buf, byte[] pixels,
int formatHint,
int dstx, int dsty,
int srcx, int srcy,
int srcw, int srch, int srcscan);
static native long nGetDevice(long pContext);
static native long nGetNativeTextureObject(long pResource);
}