| /* |
| * Copyright (c) 2007, 2015, 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 jdk.internal.perf.PerfCounter; |
| import sun.awt.AWTAccessor; |
| import sun.awt.AWTAccessor.ComponentAccessor; |
| 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; |
| PerfCounter.getD3DAvailable().set(1); |
| } else { |
| 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 |
| protected void enterFullScreenExclusive(final int screen, WindowPeer wp) |
| { |
| final WWindowPeer wpeer = AWTAccessor.getComponentAccessor() |
| .getPeer(realFSWindow); |
| 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 |
| 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). |
| final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); |
| if (!(w instanceof Frame) && !(w instanceof Dialog) && |
| (realFSWindow = getToplevelOwner(w)) != null) |
| { |
| ownerOrigBounds = realFSWindow.getBounds(); |
| WWindowPeer fp = acc.getPeer(realFSWindow); |
| 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) acc.getPeer(realFSWindow)).setAlwaysOnTop(true); |
| |
| fsWindowListener = new D3DFSWindowAdapter(); |
| realFSWindow.addWindowListener(fsWindowListener); |
| } |
| |
| @Override |
| 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). |
| */ |
| final WWindowPeer wpeer = AWTAccessor.getComponentAccessor() |
| .getPeer(realFSWindow); |
| 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 |
| 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 = AWTAccessor.getComponentAccessor() |
| .getPeer(realFSWindow); |
| |
| // 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<DisplayMode> modes); |
| @Override |
| protected void enumDisplayModes(final int screen, final ArrayList<DisplayMode> 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); |
| } |
| } |