1 /*
2 * Copyright (c) 2001, 2013, 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
27 /**
28 * This class encapsulates the array of Win32GraphicsDevices,
29 * allowing it to be accessed and recreated from multiple
30 * threads in a thread-safe manner.
31 *
32 * The MT-safeness of the array is assured in the following ways:
33 * - hide the actual array being used so that access to
34 * it can only be made from this class
35 * - Do not delete the array until all references to the
36 * array have released it. That way, anyone that happens
37 * to have a pointer to an element of the array can still
38 * safely refer to that item, even if the situation has
39 * changed and the array is out of date.
40 * - ensure that the user of the array always gets a non-disposed
41 * instance (before the user is handed over a reference to the
42 * instance, a ref counter of the instance is increased atomically)
43 * - The act of replacing an old encapsulated array
44 * of devices with the new one is protected via common lock
45 *
46 * Expected usage patterns:
47 * 1. The array element will not be used outside of this code block.
48 * {
49 * // first, get the reference to the Devices instance through InstanceAccess
50 * // subclass (this automatically increases ref count of this instance)
51 * Devices::InstanceAccess devices; // increases the ref count of current instance
52 * // Then the object can be used, for example, to retrieve the awt device.
53 * // (note: ref count is not increased with GetDevice())
54 * AwtWin32GraphicsDevice *dev = devices->GetDevice(idx);
55 * dev->DoStuff();
56 * Data data = dev->GetData();
57 * return data;
58 * // don't need to release the reference, it's done automatically in
59 * // InstanceAccess destructor
60 * }
61 *
62 * 2. The array element will be used outside of this code block (i.e.
63 * saved for later use).
64 * {
65 * Devices::InstanceAccess devices; // increases the ref count
66 * // next call increases the ref count of the instance again
67 * AwtWin32GraphicsDevice *dev = devices->GetDeviceReference(idx);
68 * wsdo->device = dev;
69 * // we saved the ref to the device element, the first reference
70 * // will be released automatically in the InstanceAccess destructor
71 * }
72 *
73 * {
74 * wsdo->device->DoStuff(); // safe because we hold a reference
75 * // then, sometime later (different thread, method, whatever)
76 * // release the reference to the array element, which in
77 * // turn will decrease the ref count of the instance of Devices class
78 * // this element belongs to
79 * wsdo->device->Release();
80 * wsdo->device = NULL; // this reference can no longer be used
81 * }
82 */
83
84 #include "Devices.h"
85 #include "Trace.h"
86 #include "D3DPipelineManager.h"
87
88
89 /* Some helper functions (from awt_MMStub.h/cpp) */
90
91 int g_nMonitorCounter;
92 int g_nMonitorLimit;
93 HMONITOR* g_hmpMonitors;
94
95 // Callback for CountMonitors below
clb_fCountMonitors(HMONITOR hMon,HDC hDC,LPRECT rRect,LPARAM lP)96 BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
97 {
98 g_nMonitorCounter ++;
99 return TRUE;
100 }
101
CountMonitors(void)102 int WINAPI CountMonitors(void)
103 {
104 g_nMonitorCounter = 0;
105 ::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L);
106 return g_nMonitorCounter;
107
108 }
109
110 // Callback for CollectMonitors below
clb_fCollectMonitors(HMONITOR hMon,HDC hDC,LPRECT rRect,LPARAM lP)111 BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
112 {
113
114 if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) {
115 g_hmpMonitors[g_nMonitorCounter] = hMon;
116 g_nMonitorCounter ++;
117 }
118
119 return TRUE;
120 }
121
CollectMonitors(HMONITOR * hmpMonitors,int nNum)122 int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
123 {
124 int retCode = 0;
125
126 if (NULL != hmpMonitors) {
127
128 g_nMonitorCounter = 0;
129 g_nMonitorLimit = nNum;
130 g_hmpMonitors = hmpMonitors;
131
132 ::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L);
133
134 retCode = g_nMonitorCounter;
135
136 g_nMonitorCounter = 0;
137 g_nMonitorLimit = 0;
138 g_hmpMonitors = NULL;
139
140 }
141 return retCode;
142 }
143
MonitorBounds(HMONITOR hmMonitor,RECT * rpBounds)144 BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds)
145 {
146 BOOL retCode = FALSE;
147
148 if ((NULL != hmMonitor) && (NULL != rpBounds)) {
149 MONITORINFOEX miInfo;
150
151 memset((void*)(&miInfo), 0, sizeof(MONITORINFOEX));
152 miInfo.cbSize = sizeof(MONITORINFOEX);
153
154 if (TRUE == (retCode = ::GetMonitorInfo(hmMonitor, &miInfo))) {
155 (*rpBounds) = miInfo.rcMonitor;
156 }
157 }
158 return retCode;
159 }
160
161 /* End of helper functions */
162
163 Devices* Devices::theInstance = NULL;
164 CriticalSection Devices::arrayLock;
165
166 /**
167 * Create a new Devices object with numDevices elements.
168 */
Devices(int numDevices)169 Devices::Devices(int numDevices)
170 {
171 J2dTraceLn1(J2D_TRACE_INFO, "Devices::Devices numDevices=%d", numDevices);
172 this->numDevices = numDevices;
173 this->refCount = 0;
174 devices = (AwtWin32GraphicsDevice**)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc,
175 numDevices, sizeof(AwtWin32GraphicsDevice *));
176 }
177
178 /**
179 * Static method which updates the array of the devices
180 * while holding global lock.
181 *
182 * If the update was successful, method returns TRUE,
183 * otherwise it returns FALSE.
184 */
185 // static
UpdateInstance(JNIEnv * env)186 BOOL Devices::UpdateInstance(JNIEnv *env)
187 {
188 J2dTraceLn(J2D_TRACE_INFO, "Devices::UpdateInstance");
189
190 int numScreens = CountMonitors();
191 HMONITOR *monHds = (HMONITOR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc,
192 numScreens, sizeof(HMONITOR));
193 if (numScreens != CollectMonitors(monHds, numScreens)) {
194 J2dRlsTraceLn(J2D_TRACE_ERROR,
195 "Devices::UpdateInstance: Failed to get all "\
196 "monitor handles.");
197 free(monHds);
198 return FALSE;
199 }
200
201 Devices *newDevices = new Devices(numScreens);
202 // This way we know that the array will not be disposed of
203 // at least until we replaced it with a new one.
204 newDevices->AddReference();
205
206 // Create all devices first, then initialize them. This allows
207 // correct configuration of devices after contruction of the
208 // primary device (which may not be device 0).
209 AwtWin32GraphicsDevice** rawDevices = newDevices->GetRawArray();
210 int i;
211 for (i = 0; i < numScreens; ++i) {
212 J2dTraceLn2(J2D_TRACE_VERBOSE, " hmon[%d]=0x%x", i, monHds[i]);
213 rawDevices[i] = new AwtWin32GraphicsDevice(i, monHds[i], newDevices);
214 }
215 for (i = 0; i < numScreens; ++i) {
216 rawDevices[i]->Initialize();
217 }
218 {
219 CriticalSection::Lock l(arrayLock);
220
221 // install the new devices array
222 Devices *oldDevices = theInstance;
223 theInstance = newDevices;
224
225 if (oldDevices) {
226 // Invalidate the devices with indexes out of the new set of
227 // devices. This doesn't cover all cases when the device
228 // might should be invalidated (like if it's not the last device
229 // that was removed), but it will have to do for now.
230 int oldNumScreens = oldDevices->GetNumDevices();
231 int newNumScreens = theInstance->GetNumDevices();
232 J2dTraceLn(J2D_TRACE_VERBOSE, " Invalidating removed devices");
233 for (int i = newNumScreens; i < oldNumScreens; i++) {
234 // removed device, needs to be invalidated
235 J2dTraceLn1(J2D_TRACE_WARNING,
236 "Devices::UpdateInstance: device removed: %d", i);
237 oldDevices->GetDevice(i)->Invalidate(env);
238 }
239 // Now that we have a new array in place, remove this (possibly the
240 // last) reference to the old instance.
241 oldDevices->Release();
242 }
243 D3DPipelineManager::HandleAdaptersChange((HMONITOR*)monHds,
244 theInstance->GetNumDevices());
245 }
246 free(monHds);
247
248 return TRUE;
249 }
250
251 /**
252 * Add a reference to the array. This could be someone that wants
253 * to register interest in the array, versus someone that actually
254 * holds a reference to an array item (in which case they would
255 * call GetDeviceReference() instead). This mechanism can keep
256 * the array from being deleted when it has no elements being
257 * referenced but is still a valid array to use for new elements
258 * or references.
259 */
AddReference()260 void Devices::AddReference()
261 {
262 J2dTraceLn(J2D_TRACE_INFO, "Devices::AddReference");
263 CriticalSection::Lock l(arrayLock);
264 refCount++;
265 J2dTraceLn1(J2D_TRACE_VERBOSE, " refCount=%d", refCount);
266 }
267
268 /**
269 * Static method for getting a reference
270 * to the instance of the current devices array.
271 * The instance will automatically have reference count increased.
272 *
273 * The caller thus must call Release() when done dealing with
274 * the array.
275 */
276 // static
GetInstance()277 Devices* Devices::GetInstance()
278 {
279 J2dTraceLn(J2D_TRACE_INFO, "Devices::GetInstance");
280 CriticalSection::Lock l(arrayLock);
281 if (theInstance != NULL) {
282 theInstance->AddReference();
283 } else {
284 J2dTraceLn(J2D_TRACE_ERROR,
285 "Devices::GetInstance NULL instance");
286 }
287 return theInstance;
288 }
289
290 /**
291 * Retrieve a pointer to an item in the array and register a
292 * reference to the array. This increases the refCount of the
293 * instance, used to track when the array can be deleted.
294 *
295 * This method must be called while holding a reference to the instance.
296 *
297 * If adjust parameter is true (default), adjust the index into the
298 * devices array so that it falls within the current devices array.
299 * This is needed because the devices array can be changed at any
300 * time, and the index may be from the old array. But in some
301 * cases we prefer to know that the index is incorrect.
302 *
303 */
GetDeviceReference(int index,BOOL adjust)304 AwtWin32GraphicsDevice *Devices::GetDeviceReference(int index,
305 BOOL adjust)
306 {
307 J2dTraceLn2(J2D_TRACE_INFO,
308 "Devices::GetDeviceReference index=%d adjust?=%d",
309 index, adjust);
310
311 AwtWin32GraphicsDevice * ret = GetDevice(index, adjust);
312 if (ret != NULL) {
313 AddReference();
314 }
315 return ret;
316 }
317
318 /**
319 * Returns a reference to a device with the passed index.
320 *
321 * This method does not increase the ref count of the Devices instance.
322 *
323 * This method must be called while holding a reference to the instance.
324 */
GetDevice(int index,BOOL adjust)325 AwtWin32GraphicsDevice *Devices::GetDevice(int index, BOOL adjust)
326 {
327 J2dTraceLn2(J2D_TRACE_INFO,
328 "Devices::GetDevice index=%d adjust?=%d",
329 index, adjust);
330 if (index < 0 || index >= numDevices) {
331 if (!adjust) {
332 J2dTraceLn1(J2D_TRACE_WARNING,
333 "Devices::GetDevice: "\
334 "incorrect index %d, returning NULL.", index);
335 return NULL;
336 }
337 J2dTraceLn1(J2D_TRACE_WARNING,
338 "Devices::GetDevice: "\
339 "adjusted index %d to 0.", index);
340 index = 0;
341 }
342 return devices[index];
343 }
344
345 /**
346 * Returns a raw reference to the incapsulated array.
347 *
348 * This method does not increase the ref count of the Devices instance.
349 *
350 * This method must be called while holding a reference to the instance.
351 */
GetRawArray()352 AwtWin32GraphicsDevice **Devices::GetRawArray()
353 {
354 J2dTraceLn(J2D_TRACE_INFO, "Devices::GetRawArray");
355 return devices;
356 }
357
358
359 /**
360 * Decreases the reference count of the array. If the refCount goes to 0,
361 * then there are no more references to the array and all of the
362 * array elements, the array itself, and this object can be destroyed.
363 *
364 * Returns the number of references left after it was decremented.
365 */
Release()366 int Devices::Release()
367 {
368 J2dTraceLn(J2D_TRACE_INFO, "Devices::Release");
369 CriticalSection::Lock l(arrayLock);
370
371 int refs = --refCount;
372
373 J2dTraceLn1(J2D_TRACE_VERBOSE, " refCount=%d", refs);
374
375 if (refs == 0) {
376 J2dTraceLn(J2D_TRACE_VERBOSE, " disposing the array");
377 if (devices != NULL) {
378 for (int i = 0; i < numDevices; ++i) {
379 if (devices[i] != NULL) {
380 delete devices[i];
381 devices[i] = NULL;
382 }
383 }
384 free(devices);
385 // null out data, can help with debugging
386 devices = NULL;
387 }
388 // it's safe to delete the instance and only
389 // then release the static lock
390 delete this;
391 // for safety return immediately after committing suicide
392 // (note: can not reference refCount here!)
393 return refs;
394 } else if (refs < 0) {
395 J2dTraceLn1(J2D_TRACE_ERROR,
396 "Devices::Release: Negative ref count! refCount=%d",
397 refs);
398 }
399
400 return refs;
401 }
402