/* * Copyright (c) 2007, 2014, 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 sun.java2d.d3d; import java.awt.Dialog; import java.awt.DisplayMode; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.peer.WindowPeer; import java.util.ArrayList; import sun.awt.Win32GraphicsDevice; import sun.awt.windows.WWindowPeer; import sun.java2d.pipe.hw.ContextCapabilities; import sun.java2d.windows.WindowsFlags; import static sun.java2d.d3d.D3DContext.D3DContextCaps.*; import sun.java2d.d3d.D3DContext.D3DContextCaps; /** * This class implements D3D-specific functionality, such as fullscreen * exclusive mode and display changes. It is kept separate from * Win32GraphicsDevice to help avoid overburdening the parent class. */ public class D3DGraphicsDevice extends Win32GraphicsDevice { private D3DContext context; private static boolean d3dAvailable; private ContextCapabilities d3dCaps; private static native boolean initD3D(); static { // loading the library doesn't help because we need the // toolkit thread running, so we have to call getDefaultToolkit() Toolkit.getDefaultToolkit(); d3dAvailable = initD3D(); if (d3dAvailable) { // we don't use pixel formats for the d3d pipeline pfDisabled = true; sun.misc.PerfCounter.getD3DAvailable().set(1); } else { sun.misc.PerfCounter.getD3DAvailable().set(0); } } /** * Used to construct a Direct3D-enabled GraphicsDevice. * * @return a D3DGraphicsDevice if it could be created * successfully, null otherwise. */ public static D3DGraphicsDevice createDevice(int screen) { if (!d3dAvailable) { return null; } ContextCapabilities d3dCaps = getDeviceCaps(screen); // could not initialize the device successfully if ((d3dCaps.getCaps() & CAPS_DEVICE_OK) == 0) { if (WindowsFlags.isD3DVerbose()) { System.out.println("Could not enable Direct3D pipeline on " + "screen " + screen); } return null; } if (WindowsFlags.isD3DVerbose()) { System.out.println("Direct3D pipeline enabled on screen " + screen); } D3DGraphicsDevice gd = new D3DGraphicsDevice(screen, d3dCaps); return gd; } private static native int getDeviceCapsNative(int screen); private static native String getDeviceIdNative(int screen); private static ContextCapabilities getDeviceCaps(final int screen) { ContextCapabilities d3dCaps = null; D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { class Result { int caps; String id; }; final Result res = new Result(); rq.flushAndInvokeNow(new Runnable() { public void run() { res.caps = getDeviceCapsNative(screen); res.id = getDeviceIdNative(screen); } }); d3dCaps = new D3DContextCaps(res.caps, res.id); } finally { rq.unlock(); } return d3dCaps != null ? d3dCaps : new D3DContextCaps(CAPS_EMPTY, null); } public final boolean isCapPresent(int cap) { return ((d3dCaps.getCaps() & cap) != 0); } private D3DGraphicsDevice(int screennum, ContextCapabilities d3dCaps) { super(screennum); descString = "D3DGraphicsDevice[screen="+screennum; this.d3dCaps = d3dCaps; context = new D3DContext(D3DRenderQueue.getInstance(), this); } public boolean isD3DEnabledOnDevice() { return isValid() && isCapPresent(CAPS_DEVICE_OK); } /** * Returns true if d3d pipeline has been successfully initialized. * @return true if d3d pipeline is initialized, false otherwise */ public static boolean isD3DAvailable() { return d3dAvailable; } /** * Return the owning Frame for a given Window. Used in setFSWindow below * to set the properties of the owning Frame when a Window goes * into fullscreen mode. */ private Frame getToplevelOwner(Window w) { Window owner = w; while (owner != null) { owner = owner.getOwner(); if (owner instanceof Frame) { return (Frame) owner; } } // could get here if passed Window is an owner-less Dialog return null; } private boolean fsStatus; private Rectangle ownerOrigBounds = null; private boolean ownerWasVisible; private Window realFSWindow; private WindowListener fsWindowListener; private boolean fsWindowWasAlwaysOnTop; private static native boolean enterFullScreenExclusiveNative(int screen, long hwnd); @Override @SuppressWarnings("deprecation") protected void enterFullScreenExclusive(final int screen, WindowPeer wp) { final WWindowPeer wpeer = (WWindowPeer)realFSWindow.getPeer(); D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { rq.flushAndInvokeNow(new Runnable() { public void run() { long hwnd = wpeer.getHWnd(); if (hwnd == 0l) { // window is disposed fsStatus = false; return; } fsStatus = enterFullScreenExclusiveNative(screen, hwnd); } }); } finally { rq.unlock(); } if (!fsStatus) { super.enterFullScreenExclusive(screen, wp); } } private static native boolean exitFullScreenExclusiveNative(int screen); @Override protected void exitFullScreenExclusive(final int screen, WindowPeer w) { if (fsStatus) { D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { rq.flushAndInvokeNow(new Runnable() { public void run() { exitFullScreenExclusiveNative(screen); } }); } finally { rq.unlock(); } } else { super.exitFullScreenExclusive(screen, w); } } /** * WindowAdapter class for the full-screen frame, responsible for * restoring the devices. This is important to do because unless the device * is restored it will not go back into the FS mode once alt+tabbed out. * This is a problem for windows for which we do not do any d3d-related * operations (like when we disabled on-screen rendering). * * REMIND: we create an instance per each full-screen device while a single * instance would suffice (but requires more management). */ private static class D3DFSWindowAdapter extends WindowAdapter { @Override @SuppressWarnings("static") public void windowDeactivated(WindowEvent e) { D3DRenderQueue.getInstance().restoreDevices(); } @Override @SuppressWarnings("static") public void windowActivated(WindowEvent e) { D3DRenderQueue.getInstance().restoreDevices(); } } @Override @SuppressWarnings("deprecation") protected void addFSWindowListener(Window w) { // if the window is not a toplevel (has an owner) we have to use the // real toplevel to enter the full-screen mode with (4933099). if (!(w instanceof Frame) && !(w instanceof Dialog) && (realFSWindow = getToplevelOwner(w)) != null) { ownerOrigBounds = realFSWindow.getBounds(); WWindowPeer fp = (WWindowPeer)realFSWindow.getPeer(); ownerWasVisible = realFSWindow.isVisible(); Rectangle r = w.getBounds(); // we use operations on peer instead of component because calling // them on component will take the tree lock fp.reshape(r.x, r.y, r.width, r.height); fp.setVisible(true); } else { realFSWindow = w; } fsWindowWasAlwaysOnTop = realFSWindow.isAlwaysOnTop(); ((WWindowPeer)realFSWindow.getPeer()).setAlwaysOnTop(true); fsWindowListener = new D3DFSWindowAdapter(); realFSWindow.addWindowListener(fsWindowListener); } @Override @SuppressWarnings("deprecation") protected void removeFSWindowListener(Window w) { realFSWindow.removeWindowListener(fsWindowListener); fsWindowListener = null; /** * Bug 4933099: There is some funny-business to deal with when this * method is called with a Window instead of a Frame. See 4836744 * for more information on this. One side-effect of our workaround * for the problem is that the owning Frame of a Window may end * up getting resized during the fullscreen process. When we * return from fullscreen mode, we should resize the Frame to * its original size (just like the Window is being resized * to its original size in GraphicsDevice). */ WWindowPeer wpeer = (WWindowPeer)realFSWindow.getPeer(); if (wpeer != null) { if (ownerOrigBounds != null) { // if the window went into fs mode before it was realized it // could have (0,0) dimensions if (ownerOrigBounds.width == 0) ownerOrigBounds.width = 1; if (ownerOrigBounds.height == 0) ownerOrigBounds.height = 1; wpeer.reshape(ownerOrigBounds.x, ownerOrigBounds.y, ownerOrigBounds.width, ownerOrigBounds.height); if (!ownerWasVisible) { wpeer.setVisible(false); } ownerOrigBounds = null; } if (!fsWindowWasAlwaysOnTop) { wpeer.setAlwaysOnTop(false); } } realFSWindow = null; } private static native DisplayMode getCurrentDisplayModeNative(int screen); @Override protected DisplayMode getCurrentDisplayMode(final int screen) { D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { class Result { DisplayMode dm = null; }; final Result res = new Result(); rq.flushAndInvokeNow(new Runnable() { public void run() { res.dm = getCurrentDisplayModeNative(screen); } }); if (res.dm == null) { return super.getCurrentDisplayMode(screen); } return res.dm; } finally { rq.unlock(); } } private static native void configDisplayModeNative(int screen, long hwnd, int width, int height, int bitDepth, int refreshRate); @Override @SuppressWarnings("deprecation") protected void configDisplayMode(final int screen, final WindowPeer w, final int width, final int height, final int bitDepth, final int refreshRate) { // we entered fs mode via gdi if (!fsStatus) { super.configDisplayMode(screen, w, width, height, bitDepth, refreshRate); return; } final WWindowPeer wpeer = (WWindowPeer)realFSWindow.getPeer(); // REMIND: we do this before we switch the display mode, so // the dimensions may be exceeding the dimensions of the screen, // is this a problem? // update the bounds of the owner frame if (getFullScreenWindow() != realFSWindow) { Rectangle screenBounds = getDefaultConfiguration().getBounds(); wpeer.reshape(screenBounds.x, screenBounds.y, width, height); } D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { rq.flushAndInvokeNow(new Runnable() { public void run() { long hwnd = wpeer.getHWnd(); if (hwnd == 0l) { // window is disposed return; } // REMIND: do we really need a window here? // we should probably just use the current one configDisplayModeNative(screen, hwnd, width, height, bitDepth, refreshRate); } }); } finally { rq.unlock(); } } private static native void enumDisplayModesNative(int screen, ArrayList modes); @Override protected void enumDisplayModes(final int screen, final ArrayList modes) { D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { rq.flushAndInvokeNow(new Runnable() { public void run() { enumDisplayModesNative(screen, modes); } }); if (modes.size() == 0) { modes.add(getCurrentDisplayModeNative(screen)); } } finally { rq.unlock(); } } private static native long getAvailableAcceleratedMemoryNative(int screen); @Override public int getAvailableAcceleratedMemory() { D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { class Result { long mem = 0L; }; final Result res = new Result(); rq.flushAndInvokeNow(new Runnable() { public void run() { res.mem = getAvailableAcceleratedMemoryNative(getScreen()); } }); return (int)res.mem; } finally { rq.unlock(); } } @Override public GraphicsConfiguration[] getConfigurations() { if (configs == null) { if (isD3DEnabledOnDevice()) { defaultConfig = getDefaultConfiguration(); if (defaultConfig != null) { configs = new GraphicsConfiguration[1]; configs[0] = defaultConfig; return configs.clone(); } } } return super.getConfigurations(); } @Override public GraphicsConfiguration getDefaultConfiguration() { if (defaultConfig == null) { if (isD3DEnabledOnDevice()) { defaultConfig = new D3DGraphicsConfig(this); } else { defaultConfig = super.getDefaultConfiguration(); } } return defaultConfig; } private static native boolean isD3DAvailableOnDeviceNative(int screen); // REMIND: this method is not used now, we use caps instead public static boolean isD3DAvailableOnDevice(final int screen) { if (!d3dAvailable) { return false; } // REMIND: should we cache the result per device somehow, // and then reset and retry it on display change? D3DRenderQueue rq = D3DRenderQueue.getInstance(); rq.lock(); try { class Result { boolean avail = false; }; final Result res = new Result(); rq.flushAndInvokeNow(new Runnable() { public void run() { res.avail = isD3DAvailableOnDeviceNative(screen); } }); return res.avail; } finally { rq.unlock(); } } D3DContext getContext() { return context; } ContextCapabilities getContextCapabilities() { return d3dCaps; } @Override public void displayChanged() { super.displayChanged(); // REMIND: make sure this works when the device is lost and we don't // disable d3d too eagerly if (d3dAvailable) { d3dCaps = getDeviceCaps(getScreen()); } } @Override protected void invalidate(int defaultScreen) { super.invalidate(defaultScreen); // REMIND: this is a bit excessive, isD3DEnabledOnDevice will return // false anyway because the device is invalid d3dCaps = new D3DContextCaps(CAPS_EMPTY, null); } }