VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Video/mp/common/VBoxMPVidModes.cpp@ 37207

Last change on this file since 37207 was 37207, checked in by vboxsync, 14 years ago

wddm: more multimon & autoresize fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.7 KB
Line 
1/* $Id: VBoxMPVidModes.cpp 37207 2011-05-24 23:11:29Z vboxsync $ */
2
3/** @file
4 * VBox Miniport video modes related functions
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxMPCommon.h"
20
21#if _MSC_VER >= 1400 /* bird: MS fixed swprintf to be standard-conforming... */
22#define _INC_SWPRINTF_INL_
23extern "C" int __cdecl swprintf(wchar_t *, const wchar_t *, ...);
24#endif
25#include <wchar.h>
26
27/* Custom video modes which are being read from registry at driver startup. */
28static VIDEO_MODE_INFORMATION g_CustomVideoModes[64] = { 0 };
29
30/* Standart video modes list.
31 * Additional space is reserved for custom video modes for 64 guest monitors.
32 * The custom video mode index is alternating and 2 indexes are reserved for the last custom mode.
33 */
34static VIDEO_MODE_INFORMATION g_VideoModes[VBOXMP_MAX_VIDEO_MODES + 64 + 2] = { 0 };
35
36/* Number of available video modes, set by VBoxMPCmnBuildVideoModesTable. */
37static uint32_t g_NumVideoModes = 0;
38
39/* Fills given video mode BPP related fields */
40static void
41VBoxFillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
42 ULONG maskR, ULONG maskG, ULONG maskB)
43{
44 pMode->NumberRedBits = bitsR;
45 pMode->NumberGreenBits = bitsG;
46 pMode->NumberBlueBits = bitsB;
47 pMode->RedMask = maskR;
48 pMode->GreenMask = maskG;
49 pMode->BlueMask = maskB;
50}
51
52/* Fills given video mode structure */
53static void
54VBoxFillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset)
55{
56 LOGF(("%dx%d:%d (idx=%d, yoffset=%d)", xres, yres, bpp, index, yoffset));
57
58 memset(pMode, 0, sizeof(VIDEO_MODE_INFORMATION));
59
60 /*Common entries*/
61 pMode->Length = sizeof(VIDEO_MODE_INFORMATION);
62 pMode->ModeIndex = index;
63 pMode->VisScreenWidth = xres;
64 pMode->VisScreenHeight = yres - yoffset;
65 pMode->ScreenStride = xres * ((bpp + 7) / 8);
66 pMode->NumberOfPlanes = 1;
67 pMode->BitsPerPlane = bpp;
68 pMode->Frequency = 60;
69 pMode->XMillimeter = 320;
70 pMode->YMillimeter = 240;
71 pMode->VideoMemoryBitmapWidth = xres;
72 pMode->VideoMemoryBitmapHeight = yres - yoffset;
73 pMode->DriverSpecificAttributeFlags = 0;
74 pMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
75
76 /*BPP related entries*/
77 switch (bpp)
78 {
79#ifdef VBOX_WITH_8BPP_MODES
80 case 8:
81 VBoxFillVidModeBPP(pMode, 6, 6, 6, 0, 0, 0);
82
83 pMode->AttributeFlags |= VIDEO_MODE_PALETTE_DRIVEN | VIDEO_MODE_MANAGED_PALETTE;
84 break;
85#endif
86 case 16:
87 VBoxFillVidModeBPP(pMode, 5, 6, 5, 0xF800, 0x7E0, 0x1F);
88 break;
89 case 24:
90 case 32:
91 VBoxFillVidModeBPP(pMode, 8, 8, 8, 0xFF0000, 0xFF00, 0xFF);
92 break;
93 default:
94 Assert(0);
95 break;
96 }
97}
98
99void VBoxMPCmnInitCustomVideoModes(PVBOXMP_DEVEXT pExt)
100{
101 VBOXMPCMNREGISTRY Registry;
102 VP_STATUS rc;
103 int iMode;
104
105 LOGF_ENTER();
106
107 rc = VBoxMPCmnRegInit(pExt, &Registry);
108 VBOXMP_WARN_VPS(rc);
109
110 /* Initialize all custom modes to the 800x600x32 */
111 VBoxFillVidModeInfo(&g_CustomVideoModes[0], 800, 600, 32, 0, 0);
112 for (iMode=1; iMode<RT_ELEMENTS(g_CustomVideoModes); ++iMode)
113 {
114 g_CustomVideoModes[iMode] = g_CustomVideoModes[0];
115 }
116
117 /* Read stored custom resolution info from registry */
118 for (iMode=0; iMode<VBoxCommonFromDeviceExt(pExt)->cDisplays; ++iMode)
119 {
120 uint32_t CustomXRes = 0, CustomYRes = 0, CustomBPP = 0;
121
122 if (iMode==0)
123 {
124 /*First name without a suffix*/
125 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomXRes", &CustomXRes);
126 VBOXMP_WARN_VPS(rc);
127 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomYRes", &CustomYRes);
128 VBOXMP_WARN_VPS(rc);
129 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomBPP", &CustomBPP);
130 VBOXMP_WARN_VPS(rc);
131 }
132 else
133 {
134 wchar_t keyname[32];
135 swprintf(keyname, L"CustomXRes%d", iMode);
136 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomXRes);
137 VBOXMP_WARN_VPS(rc);
138 swprintf(keyname, L"CustomYRes%d", iMode);
139 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomYRes);
140 VBOXMP_WARN_VPS(rc);
141 swprintf(keyname, L"CustomBPP%d", iMode);
142 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomBPP);
143 VBOXMP_WARN_VPS(rc);
144 }
145
146 LOG(("got stored custom resolution[%d] %dx%dx%d", iMode, CustomXRes, CustomYRes, CustomBPP));
147
148 if (CustomXRes || CustomYRes || CustomBPP)
149 {
150 if (CustomXRes == 0)
151 {
152 CustomXRes = g_CustomVideoModes[iMode].VisScreenWidth;
153 }
154 if (CustomYRes == 0)
155 {
156 CustomYRes = g_CustomVideoModes[iMode].VisScreenHeight;
157 }
158 if (CustomBPP == 0)
159 {
160 CustomBPP = g_CustomVideoModes[iMode].BitsPerPlane;
161 }
162
163 VBoxFillVidModeInfo(&g_CustomVideoModes[iMode], CustomXRes, CustomYRes, CustomBPP, 0, 0);
164 }
165 }
166
167 rc = VBoxMPCmnRegFini(Registry);
168 VBOXMP_WARN_VPS(rc);
169 LOGF_LEAVE();
170}
171
172VIDEO_MODE_INFORMATION *VBoxMPCmnGetCustomVideoModeInfo(ULONG ulIndex)
173{
174 return (ulIndex<RT_ELEMENTS(g_CustomVideoModes)) ? &g_CustomVideoModes[ulIndex] : NULL;
175}
176
177VIDEO_MODE_INFORMATION* VBoxMPCmnGetVideoModeInfo(ULONG ulIndex)
178{
179 return (ulIndex<RT_ELEMENTS(g_VideoModes)) ? &g_VideoModes[ulIndex] : NULL;
180}
181
182static bool VBoxMPVideoModesMatch(const PVIDEO_MODE_INFORMATION pMode1, const PVIDEO_MODE_INFORMATION pMode2)
183{
184 return pMode1->VisScreenHeight == pMode2->VisScreenHeight
185 && pMode1->VisScreenWidth == pMode2->VisScreenWidth
186 && pMode1->BitsPerPlane == pMode2->BitsPerPlane;
187}
188
189static int
190VBoxMPFindVideoMode(const PVIDEO_MODE_INFORMATION pModesTable, int cModes, const PVIDEO_MODE_INFORMATION pMode)
191{
192 for (int i = 0; i < cModes; ++i)
193 {
194 if (VBoxMPVideoModesMatch(pMode, &pModesTable[i]))
195 {
196 return i;
197 }
198 }
199 return -1;
200}
201
202/* Helper function to dynamically build our table of standard video
203 * modes. We take the amount of VRAM and create modes with standard
204 * geometries until we've either reached the maximum number of modes
205 * or the available VRAM does not allow for additional modes.
206 * We also check registry for manually added video modes.
207 * Returns number of modes added to the table.
208 */
209static uint32_t
210VBoxMPFillModesTable(PVBOXMP_DEVEXT pExt, int iDisplay, PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize,
211 int32_t *pPrefModeIdx)
212{
213 /* the resolution matrix */
214 struct
215 {
216 uint16_t xRes;
217 uint16_t yRes;
218 } resolutionMatrix[] =
219 {
220 /* standard modes */
221 { 640, 480 },
222 { 800, 600 },
223 { 1024, 768 },
224 { 1152, 864 },
225 { 1280, 960 },
226 { 1280, 1024 },
227 { 1400, 1050 },
228 { 1600, 1200 },
229 { 1920, 1440 },
230#ifndef VBOX_WITH_WDDM
231 /* multi screen modes with 1280x1024 */
232 { 2560, 1024 },
233 { 3840, 1024 },
234 { 5120, 1024 },
235 /* multi screen modes with 1600x1200 */
236 { 3200, 1200 },
237 { 4800, 1200 },
238 { 6400, 1200 },
239#endif
240 };
241
242#ifdef VBOX_XPDM_MINIPORT
243 ULONG vramSize = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
244#else
245 ULONG vramSize = vboxWddmVramCpuVisibleSegmentSize(pExt);
246 /* at least two surfaces will be needed: primary & shadow */
247 vramSize /= 2 * pExt->u.primary.commonInfo.cDisplays;
248#endif
249
250 uint32_t iMode=0, iPrefIdx=0;
251 /* there are 4 color depths: 8, 16, 24 and 32bpp and we reserve 50% of the modes for other sources */
252 size_t maxModesPerColorDepth = VBOXMP_MAX_VIDEO_MODES / 2 / 4;
253
254 /* Always add 800x600 video modes. Windows XP+ needs at least 800x600 resolution
255 * and fallbacks to 800x600x4bpp VGA mode if the driver did not report suitable modes.
256 * This resolution could be rejected by a low resolution host (netbooks, etc).
257 */
258#ifdef VBOX_WITH_8BPP_MODES
259 int bytesPerPixel=1;
260#else
261 int bytesPerPixel=2;
262#endif
263 for (; bytesPerPixel<=4; bytesPerPixel++)
264 {
265 int bitsPerPixel = 8*bytesPerPixel;
266
267 if (800*600*bytesPerPixel > (LONG)vramSize)
268 {
269 /* we don't have enough VRAM for this mode */
270 continue;
271 }
272
273 VBoxFillVidModeInfo(&pModesTable[iMode], 800, 600, bitsPerPixel, iMode+1, 0);
274
275 if (32==bitsPerPixel)
276 {
277 iPrefIdx = iMode;
278 }
279 ++iMode;
280 }
281
282 /* Query yoffset from the host */
283 ULONG yOffset = VBoxGetHeightReduction();
284
285 /* Iterate through our static resolution table and add supported video modes for different bpp's */
286#ifdef VBOX_WITH_8BPP_MODES
287 bytesPerPixel=1;
288#else
289 bytesPerPixel=2;
290#endif
291 for (; bytesPerPixel<=4; bytesPerPixel++)
292 {
293 int bitsPerPixel = 8*bytesPerPixel;
294 size_t cAdded, resIndex;
295
296 for (cAdded=0, resIndex=0; resIndex<RT_ELEMENTS(resolutionMatrix) && cAdded<maxModesPerColorDepth; resIndex++)
297 {
298 if (resolutionMatrix[resIndex].xRes * resolutionMatrix[resIndex].yRes * bytesPerPixel > (LONG)vramSize)
299 {
300 /* we don't have enough VRAM for this mode */
301 continue;
302 }
303
304 if (yOffset == 0 && resolutionMatrix[resIndex].xRes == 800 && resolutionMatrix[resIndex].yRes == 600)
305 {
306 /* this mode was already added */
307 continue;
308 }
309
310 if (!VBoxLikesVideoMode(iDisplay, resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes - yOffset, bitsPerPixel))
311 {
312 /* host doesn't like this mode */
313 continue;
314 }
315
316 /* Sanity check, we shouldn't ever get here */
317 if (iMode >= tableSize)
318 {
319 WARN(("video modes table overflow!"));
320 break;
321 }
322
323 VBoxFillVidModeInfo(&pModesTable[iMode], resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes, bitsPerPixel, iMode+1, yOffset);
324 ++iMode;
325 ++cAdded;
326 }
327 }
328
329 /* Check registry for manually added modes, up to 128 entries is supported
330 * Give up on the first error encountered.
331 */
332 VBOXMPCMNREGISTRY Registry;
333 int fPrefSet=0;
334 VP_STATUS rc;
335
336 rc = VBoxMPCmnRegInit(pExt, &Registry);
337 VBOXMP_WARN_VPS(rc);
338
339 for (int curKey=0; curKey<128; curKey++)
340 {
341 if (iMode>=tableSize)
342 {
343 WARN(("ignoring possible custom mode(s), table is full!"));
344 break;
345 }
346
347 wchar_t keyname[24];
348 uint32_t xres, yres, bpp = 0;
349
350 swprintf(keyname, L"CustomMode%dWidth", curKey);
351 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &xres);
352 VBOXMP_CHECK_VPS_BREAK(rc);
353
354 swprintf(keyname, L"CustomMode%dHeight", curKey);
355 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &yres);
356 VBOXMP_CHECK_VPS_BREAK(rc);
357
358 swprintf(keyname, L"CustomMode%dBPP", curKey);
359 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &bpp);
360 VBOXMP_CHECK_VPS_BREAK(rc);
361
362 LOG(("got custom mode[%u]=%ux%u:%u", curKey, xres, yres, bpp));
363
364 /* round down width to be a multiple of 8 if necessary */
365 if (!pExt->fAnyX)
366 {
367 xres &= 0xFFF8;
368 }
369
370 if ( (xres > (1 << 16))
371 || (yres > (1 << 16))
372 || ( (bpp != 16)
373 && (bpp != 24)
374 && (bpp != 32)))
375 {
376 /* incorrect values */
377 break;
378 }
379
380 /* does it fit within our VRAM? */
381 if (xres * yres * (bpp / 8) > vramSize)
382 {
383 /* we don't have enough VRAM for this mode */
384 break;
385 }
386
387 if (!VBoxLikesVideoMode(iDisplay, xres, yres, bpp))
388 {
389 /* host doesn't like this mode */
390 break;
391 }
392
393 LOG(("adding video mode from registry."));
394
395 VBoxFillVidModeInfo(&pModesTable[iMode], xres, yres, bpp, iMode+1, yOffset);
396
397 if (!fPrefSet)
398 {
399 fPrefSet = 1;
400 iPrefIdx = iMode;
401 }
402#ifdef VBOX_WDDM_MINIPORT
403 /*check if the same mode has been added to the table already*/
404 int foundIdx = VBoxMPFindVideoMode(pModesTable, iMode, &pModesTable[iMode]);
405
406 if (foundIdx>=0)
407 {
408 if (iPrefIdx==iMode)
409 {
410 iPrefIdx=foundIdx;
411 }
412 }
413 else
414#endif
415 {
416 ++iMode;
417 }
418 }
419
420 rc = VBoxMPCmnRegFini(Registry);
421 VBOXMP_WARN_VPS(rc);
422
423 if (pPrefModeIdx)
424 {
425 *pPrefModeIdx = iPrefIdx;
426 }
427
428 return iMode;
429}
430
431/* Returns if we're in the first mode change, ie doesn't have valid video mode set yet */
432static BOOLEAN VBoxMPIsStartingUp(PVBOXMP_DEVEXT pExt, uint32_t iDisplay)
433{
434#ifdef VBOX_XPDM_MINIPORT
435 return (pExt->CurrentMode == 0);
436#else
437 return (!VBoxCommonFromDeviceExt(pExt)->cDisplays || !pExt->aSources[iDisplay].pPrimaryAllocation);
438#endif
439}
440
441/* Updates missing video mode params with current values,
442 * Checks if resulting mode is liked by the host and fits into VRAM.
443 * Returns TRUE if resulting mode could be used.
444 */
445static BOOLEAN
446VBoxMPValidateVideoModeParams(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, uint32_t &xres, uint32_t &yres, uint32_t &bpp)
447{
448 /* Make sure all important video mode values are set */
449 if (VBoxMPIsStartingUp(pExt, iDisplay))
450 {
451 /* Use stored custom values only if nothing was read from host. */
452 xres = xres ? xres:g_CustomVideoModes[iDisplay].VisScreenWidth;
453 yres = yres ? yres:g_CustomVideoModes[iDisplay].VisScreenHeight;
454 bpp = bpp ? bpp :g_CustomVideoModes[iDisplay].BitsPerPlane;
455 }
456 else
457 {
458 /* Use current values for field which weren't read from host. */
459#ifdef VBOX_XPDM_MINIPORT
460 xres = xres ? xres:pExt->CurrentModeWidth;
461 yres = yres ? yres:pExt->CurrentModeHeight;
462 bpp = bpp ? bpp :pExt->CurrentModeBPP;
463#else
464 xres = xres ? xres:pExt->aSources[iDisplay].pPrimaryAllocation->SurfDesc.width;
465 yres = yres ? yres:pExt->aSources[iDisplay].pPrimaryAllocation->SurfDesc.height;
466 bpp = bpp ? bpp :pExt->aSources[iDisplay].pPrimaryAllocation->SurfDesc.bpp;
467#endif
468 }
469
470 /* Round down width to be a multiple of 8 if necessary */
471 if (!pExt->fAnyX)
472 {
473 xres &= 0xFFF8;
474 }
475
476 /* We always need bpp to be set */
477 if (!bpp)
478 {
479 bpp=32;
480 }
481
482 /* Check if host likes this mode */
483 if (!VBoxLikesVideoMode(iDisplay, xres, yres, bpp))
484 {
485 WARN(("host does not like special mode %dx%d:%d for display %d", xres, yres, bpp, iDisplay));
486 return FALSE;
487 }
488
489#ifdef VBOX_XPDM_MINIPORT
490 ULONG vramSize = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
491#else
492 ULONG vramSize = vboxWddmVramCpuVisibleSegmentSize(pExt);
493 /* at least two surfaces will be needed: primary & shadow */
494 vramSize /= 2 * pExt->u.primary.commonInfo.cDisplays;
495#endif
496
497 /* Check that values are valid and mode fits into VRAM */
498 if (!xres || !yres
499 || !((bpp == 16)
500#ifdef VBOX_WITH_8BPP_MODES
501 || (bpp == 8)
502#endif
503 || (bpp == 24)
504 || (bpp == 32)))
505 {
506 LOG(("invalid params for special mode %dx%d:%d", xres, yres, bpp));
507 return FALSE;
508 }
509
510
511
512 if ((xres * yres * (bpp / 8) >= vramSize))
513 {
514 /* Store values of last reported release log message to avoid log flooding. */
515 static uint32_t s_xresNoVRAM=0, s_yresNoVRAM=0, s_bppNoVRAM=0;
516
517 LOG(("not enough VRAM for video mode %dx%dx%dbpp. Available: %d bytes. Required: more than %d bytes.",
518 xres, yres, bpp, vramSize, xres * yres * (bpp / 8)));
519
520 s_xresNoVRAM = xres;
521 s_yresNoVRAM = yres;
522 s_bppNoVRAM = bpp;
523
524 return FALSE;
525 }
526
527 return TRUE;
528}
529
530/* Checks if there's a pending video mode change hint,
531 * and fills pPendingMode with associated info.
532 * returns TRUE if there's a pending change. Otherwise returns FALSE.
533 */
534static BOOLEAN
535VBoxMPCheckPendingVideoMode(PVBOXMP_DEVEXT pExt, PVIDEO_MODE_INFORMATION pPendingMode)
536{
537 uint32_t xres=0, yres=0, bpp=0, display=0;
538
539 /* Check if there's a pending display change request for this display */
540 if (VBoxQueryDisplayRequest(&xres, &yres, &bpp, &display) && (xres || yres || bpp))
541 {
542 if (display>RT_ELEMENTS(g_CustomVideoModes))
543 {
544 /*display = RT_ELEMENTS(g_CustomVideoModes) - 1;*/
545 return FALSE;
546 }
547 }
548 else
549 {
550 return FALSE;
551 }
552
553 /* Correct video mode params and check if host likes it */
554 if (VBoxMPValidateVideoModeParams(pExt, display, xres, yres, bpp))
555 {
556 VBoxFillVidModeInfo(pPendingMode, xres, yres, bpp, display, 0);
557 return TRUE;
558 }
559
560 return FALSE;
561}
562
563/* Save custom mode info to registry */
564static void VBoxMPRegSaveModeInfo(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, PVIDEO_MODE_INFORMATION pMode)
565{
566 VBOXMPCMNREGISTRY Registry;
567 VP_STATUS rc;
568
569 rc = VBoxMPCmnRegInit(pExt, &Registry);
570 VBOXMP_WARN_VPS(rc);
571
572 if (iDisplay==0)
573 {
574 /*First name without a suffix*/
575 rc = VBoxMPCmnRegSetDword(Registry, L"CustomXRes", pMode->VisScreenWidth);
576 VBOXMP_WARN_VPS(rc);
577 rc = VBoxMPCmnRegSetDword(Registry, L"CustomYRes", pMode->VisScreenHeight);
578 VBOXMP_WARN_VPS(rc);
579 rc = VBoxMPCmnRegSetDword(Registry, L"CustomBPP", pMode->BitsPerPlane);
580 VBOXMP_WARN_VPS(rc);
581 }
582 else
583 {
584 wchar_t keyname[32];
585 swprintf(keyname, L"CustomXRes%d", iDisplay);
586 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->VisScreenWidth);
587 VBOXMP_WARN_VPS(rc);
588 swprintf(keyname, L"CustomYRes%d", iDisplay);
589 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->VisScreenHeight);
590 VBOXMP_WARN_VPS(rc);
591 swprintf(keyname, L"CustomBPP%d", iDisplay);
592 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->BitsPerPlane);
593 VBOXMP_WARN_VPS(rc);
594 }
595
596 rc = VBoxMPCmnRegFini(Registry);
597 VBOXMP_WARN_VPS(rc);
598}
599
600#ifdef VBOX_XPDM_MINIPORT
601VIDEO_MODE_INFORMATION* VBoxMPXpdmCurrentVideoMode(PVBOXMP_DEVEXT pExt)
602{
603 return VBoxMPCmnGetVideoModeInfo(pExt->CurrentMode - 1);
604}
605
606ULONG VBoxMPXpdmGetVideoModesCount()
607{
608 return g_NumVideoModes;
609}
610
611/* Makes a table of video modes consisting of:
612 * Default modes
613 * Custom modes manually added to registry
614 * Custom modes for all displays (either from a display change hint or stored in registry)
615 * 2 special modes, for a pending display change for this adapter. See comments below.
616 */
617void VBoxMPXpdmBuildVideoModesTable(PVBOXMP_DEVEXT pExt)
618{
619 uint32_t cStandartModes, cCustomModes;
620 BOOLEAN bPending, bHaveSpecial;
621 VIDEO_MODE_INFORMATION specialMode;
622
623 /* Fill table with standart modes and ones manually added to registry */
624 cStandartModes = VBoxMPFillModesTable(pExt, pExt->iDevice, g_VideoModes, RT_ELEMENTS(g_VideoModes), NULL);
625
626 /* Add custom modes for all displays to the table */
627 cCustomModes = VBoxCommonFromDeviceExt(pExt)->cDisplays;
628 for (uint32_t i=0; i<cCustomModes; i++)
629 {
630 memcpy(&g_VideoModes[cStandartModes+i], &g_CustomVideoModes[i], sizeof(VIDEO_MODE_INFORMATION));
631 g_VideoModes[cStandartModes+i].ModeIndex = cStandartModes+i+1;
632 }
633
634 /* Check if host wants us to switch video mode and it's for this adapter */
635 bPending = VBoxMPCheckPendingVideoMode(pExt, &specialMode);
636 bHaveSpecial = bPending && (pExt->iDevice == specialMode.ModeIndex);
637
638 /* Check the startup case */
639 if (!bHaveSpecial && VBoxMPIsStartingUp(pExt, pExt->iDevice))
640 {
641 uint32_t xres=0, yres=0, bpp=0;
642 /* Check if we could make valid mode from values stored to registry */
643 if (VBoxMPValidateVideoModeParams(pExt, pExt->iDevice, xres, yres, bpp))
644 {
645 VBoxFillVidModeInfo(&specialMode, xres, yres, bpp, 0, 0);
646 bHaveSpecial = TRUE;
647 }
648 }
649
650 /* Update number of modes */
651 g_NumVideoModes = cStandartModes + cCustomModes;
652
653 if (!bHaveSpecial)
654 {
655 /* Just add 2 dummy modes to maintain table size. */
656 memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[2], sizeof(VIDEO_MODE_INFORMATION));
657 g_VideoModes[g_NumVideoModes].ModeIndex = g_NumVideoModes+1;
658 g_NumVideoModes++;
659 memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[2], sizeof(VIDEO_MODE_INFORMATION));
660 g_VideoModes[g_NumVideoModes].ModeIndex = g_NumVideoModes+1;
661 g_NumVideoModes++;
662 }
663 else
664 {
665 /* We need to alternate mode index entry for a pending mode change,
666 * else windows will ignore actual mode change call.
667 * Only alternate index if one of mode parameters changed and
668 * regardless of conditions always add 2 entries to the table.
669 */
670 static int s_InvocationCounter=0;
671 BOOLEAN bAlternativeIndex = FALSE;
672
673 static uint32_t s_Prev_xres=0;
674 static uint32_t s_Prev_yres=0;
675 static uint32_t s_Prev_bpp=0;
676 BOOLEAN bChanged = (s_Prev_xres!=specialMode.VisScreenWidth
677 || s_Prev_yres!=specialMode.VisScreenHeight
678 || s_Prev_bpp!=specialMode.BitsPerPlane);
679 if (bChanged)
680 {
681 s_Prev_xres = specialMode.VisScreenWidth;
682 s_Prev_yres = specialMode.VisScreenHeight;
683 s_Prev_bpp = specialMode.BitsPerPlane;
684 }
685
686 /* Make sure there's no other mode in the table with same parameters,
687 * because we need windows to pick up a new video mode index otherwise
688 * actual mode change wouldn't happen.
689 */
690 int iFoundIdx;
691 uint32_t uiStart=0;
692
693 while (0 <= (iFoundIdx = VBoxMPFindVideoMode(&g_VideoModes[uiStart], g_NumVideoModes-uiStart, &specialMode)))
694 {
695 memcpy(&g_VideoModes[uiStart+iFoundIdx], &g_VideoModes[2], sizeof(VIDEO_MODE_INFORMATION));
696 g_VideoModes[uiStart+iFoundIdx].ModeIndex = uiStart+iFoundIdx+1;
697 uiStart += iFoundIdx+1;
698 }
699
700 /* Check if we need to alternate the index */
701 if (!VBoxMPIsStartingUp(pExt, pExt->iDevice))
702 {
703 if (bChanged)
704 {
705 s_InvocationCounter++;
706 }
707
708 if (s_InvocationCounter % 2)
709 {
710 bAlternativeIndex = TRUE;
711 memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[2], sizeof(VIDEO_MODE_INFORMATION));
712 g_VideoModes[g_NumVideoModes].ModeIndex = g_NumVideoModes+1;
713 ++g_NumVideoModes;
714 }
715 }
716
717 LOG(("add special mode[%d] %dx%d:%d for display %d (bChanged=%d, bAlretnativeIndex=%d)",
718 g_NumVideoModes, specialMode.VisScreenWidth, specialMode.VisScreenHeight, specialMode.BitsPerPlane,
719 pExt->iDevice, bChanged, bAlternativeIndex));
720
721 /* Add special mode to the table
722 * Note: Y offset isn't used for a host-supplied modes
723 */
724 specialMode.ModeIndex = g_NumVideoModes+1;
725 memcpy(&g_VideoModes[g_NumVideoModes], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
726 ++g_NumVideoModes;
727
728 /* Save special mode in the custom modes table */
729 memcpy(&g_CustomVideoModes[pExt->iDevice], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
730
731
732 /* Make sure we've added 2nd mode if necessary to maintain table size */
733 if (VBoxMPIsStartingUp(pExt, pExt->iDevice))
734 {
735 memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[g_NumVideoModes-1], sizeof(VIDEO_MODE_INFORMATION));
736 g_VideoModes[g_NumVideoModes].ModeIndex = g_NumVideoModes+1;
737 ++g_NumVideoModes;
738 }
739 else if (!bAlternativeIndex)
740 {
741 memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[2], sizeof(VIDEO_MODE_INFORMATION));
742 g_VideoModes[g_NumVideoModes].ModeIndex = g_NumVideoModes+1;
743 ++g_NumVideoModes;
744 }
745
746 /* Save special mode info to registry */
747 VBoxMPRegSaveModeInfo(pExt, pExt->iDevice, &specialMode);
748 }
749
750#if defined(LOG_ENABLED)
751 do
752 {
753 LOG(("Filled %d modes", g_NumVideoModes));
754
755 for (uint32_t i=0; i<g_NumVideoModes; ++i)
756 {
757 LOG(("Mode[%2d]: %4dx%4d:%2d (idx=%d)",
758 i, g_VideoModes[i].VisScreenWidth, g_VideoModes[i].VisScreenHeight,
759 g_VideoModes[i].BitsPerPlane, g_VideoModes[i].ModeIndex));
760 }
761 } while (0);
762#endif
763}
764#endif /*VBOX_XPDM_MINIPORT*/
765
766#ifdef VBOX_WDDM_MINIPORT
767static VBOXWDDM_VIDEOMODES_INFO g_aVBoxVideoModeInfos[VBOX_VIDEO_MAX_SCREENS] = {0};
768
769bool VBoxWddmFillMode(VIDEO_MODE_INFORMATION *pInfo, D3DDDIFORMAT enmFormat, ULONG w, ULONG h)
770{
771 switch (enmFormat)
772 {
773 case D3DDDIFMT_A8R8G8B8:
774 VBoxFillVidModeInfo(pInfo, w, h, 32, 0, 0);
775 return true;
776 case D3DDDIFMT_R8G8B8:
777 VBoxFillVidModeInfo(pInfo, w, h, 24, 0, 0);
778 return true;
779 case D3DDDIFMT_R5G6B5:
780 VBoxFillVidModeInfo(pInfo, w, h, 16, 0, 0);
781 return true;
782 case D3DDDIFMT_P8:
783 VBoxFillVidModeInfo(pInfo, w, h, 8, 0, 0);
784 return true;
785 default:
786 WARN(("unsupported enmFormat(%d)", enmFormat));
787 AssertBreakpoint();
788 break;
789 }
790
791 return false;
792}
793
794static void
795VBoxWddmBuildResolutionTable(PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize,
796 SIZE *pResolutions, uint32_t * pcResolutions)
797{
798 uint32_t cResolutionsArray = *pcResolutions;
799 uint32_t cResolutions = 0;
800
801 for (uint32_t i=0; i<tableSize; ++i)
802 {
803 PVIDEO_MODE_INFORMATION pMode = &pModesTable[i];
804 BOOLEAN bFound = FALSE;
805
806 for (uint32_t j=0; j<cResolutions; ++j)
807 {
808 if (pResolutions[j].cx == pMode->VisScreenWidth
809 && pResolutions[j].cy == pMode->VisScreenHeight)
810 {
811 bFound = TRUE;
812 break;
813 }
814 }
815
816 if (!bFound)
817 {
818 if (cResolutions == cResolutionsArray)
819 {
820 WARN(("table overflow!"));
821 break;
822 }
823
824 pResolutions[cResolutions].cx = pMode->VisScreenWidth;
825 pResolutions[cResolutions].cy = pMode->VisScreenHeight;
826 ++cResolutions;
827 }
828 }
829
830 *pcResolutions = cResolutions;
831}
832
833AssertCompile(sizeof (SIZE) == sizeof (D3DKMDT_2DREGION));
834AssertCompile(RT_OFFSETOF(SIZE, cx) == RT_OFFSETOF(D3DKMDT_2DREGION, cx));
835AssertCompile(RT_OFFSETOF(SIZE, cy) == RT_OFFSETOF(D3DKMDT_2DREGION, cy));
836static void
837VBoxWddmBuildVideoModesInfo(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId,
838 PVBOXWDDM_VIDEOMODES_INFO pModes, VIDEO_MODE_INFORMATION *paAddlModes,
839 UINT cAddlModes)
840{
841 pModes->cResolutions = RT_ELEMENTS(pModes->aResolutions);
842
843 /* Add default modes and ones read from registry. */
844 pModes->cModes = VBoxMPFillModesTable(pExt, VidPnTargetId, pModes->aModes, RT_ELEMENTS(pModes->aModes), &pModes->iPreferredMode);
845 Assert(pModes->cModes<=RT_ELEMENTS(pModes->aModes));
846
847 /* Check if there's a pending display change request for this adapter */
848 VIDEO_MODE_INFORMATION specialMode;
849 if (VBoxMPCheckPendingVideoMode(pExt, &specialMode) && (specialMode.ModeIndex==VidPnTargetId))
850 {
851 /*Minor hack, ModeIndex!=0 Means this mode has been validated already and not just read from registry */
852 specialMode.ModeIndex = 1;
853 memcpy(&g_CustomVideoModes[VidPnTargetId], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
854
855 /* Save mode to registry */
856 VBoxMPRegSaveModeInfo(pExt, VidPnTargetId, &specialMode);
857 }
858
859 /* Validate the mode which has been read from registry */
860 if (!g_CustomVideoModes[VidPnTargetId].ModeIndex)
861 {
862 uint32_t xres, yres, bpp;
863
864 xres = g_CustomVideoModes[VidPnTargetId].VisScreenWidth;
865 yres = g_CustomVideoModes[VidPnTargetId].VisScreenHeight;
866 bpp = g_CustomVideoModes[VidPnTargetId].BitsPerPlane;
867
868 if (VBoxMPValidateVideoModeParams(pExt, VidPnTargetId, xres, yres, bpp))
869 {
870 VBoxFillVidModeInfo(&g_CustomVideoModes[VidPnTargetId], xres, yres, bpp, 1/*index*/, 0);
871 Assert(g_CustomVideoModes[VidPnTargetId].ModeIndex == 1);
872 }
873 }
874
875 /* Add custom mode to the table */
876 if (g_CustomVideoModes[VidPnTargetId].ModeIndex)
877 {
878 if (RT_ELEMENTS(pModes->aModes) > pModes->cModes)
879 {
880 g_CustomVideoModes[VidPnTargetId].ModeIndex = pModes->cModes;
881 pModes->aModes[pModes->cModes] = g_CustomVideoModes[VidPnTargetId];
882
883 /* Check if we already have this mode in the table */
884 int foundIdx;
885 if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]))>=0)
886 {
887 pModes->iPreferredMode = foundIdx;
888 }
889 else
890 {
891 pModes->iPreferredMode = pModes->cModes;
892 ++pModes->cModes;
893 }
894
895 /* Add other bpp modes for this custom resolution */
896#ifdef VBOX_WITH_8BPP_MODES
897 UINT bpp=8;
898#else
899 UINT bpp=16;
900#endif
901 for (; bpp<=32; bpp+=8)
902 {
903 if (RT_ELEMENTS(pModes->aModes) == pModes->cModes)
904 {
905 WARN(("table full, can't add other bpp for specail mode!"));
906#ifdef DEBUG_misha
907 /* this is definitely something we do not expect */
908 AssertFailed();
909#endif
910 break;
911 }
912
913 AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
914
915 if (pModes->aModes[pModes->iPreferredMode].BitsPerPlane != bpp)
916 {
917 VBoxFillVidModeInfo(&pModes->aModes[pModes->cModes],
918 pModes->aModes[pModes->iPreferredMode].VisScreenWidth,
919 pModes->aModes[pModes->iPreferredMode].VisScreenHeight,
920 bpp, pModes->cModes, 0);
921 if (VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]) < 0)
922 {
923 ++pModes->cModes;
924 }
925 }
926 }
927 }
928 else
929 {
930 AssertRelease(RT_ELEMENTS(pModes->aModes) == pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
931 WARN(("table full, can't add video mode for a host request!"));
932#ifdef DEBUG_misha
933 /* this is definitely something we do not expect */
934 AssertFailed();
935#endif
936 }
937 }
938
939 /* Check and Add additional modes passed in paAddlModes */
940 for (UINT i=0; i<cAddlModes; ++i)
941 {
942 if (RT_ELEMENTS(pModes->aModes) == pModes->cModes)
943 {
944 WARN(("table full, can't add addl modes!"));
945#ifdef DEBUG_misha
946 /* this is definitely something we do not expect */
947 AssertFailed();
948#endif
949 break;
950 }
951
952 AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
953
954 if (!pExt->fAnyX)
955 {
956 paAddlModes[i].VisScreenWidth &= 0xFFF8;
957 }
958
959 if (VBoxLikesVideoMode(VidPnTargetId, paAddlModes[i].VisScreenWidth, paAddlModes[i].VisScreenHeight, paAddlModes[i].BitsPerPlane))
960 {
961 int foundIdx;
962 if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &paAddlModes[i]))>=0)
963 {
964 pModes->iPreferredMode = foundIdx;
965 }
966 else
967 {
968 memcpy(&pModes->aModes[pModes->cModes], &paAddlModes[i], sizeof(VIDEO_MODE_INFORMATION));
969 pModes->aModes[pModes->cModes].ModeIndex = pModes->cModes;
970 ++pModes->cModes;
971 }
972 }
973 }
974
975 pModes->cPrevModes = pModes->cModes;
976
977 /* Build resolution table */
978 VBoxWddmBuildResolutionTable(pModes->aModes, pModes->cModes, (SIZE*)((void*)pModes->aResolutions), &pModes->cResolutions);
979}
980
981void VBoxWddmInvalidateVideoModesInfo(PVBOXMP_DEVEXT pExt)
982{
983 for (UINT i = 0; i < RT_ELEMENTS(g_aVBoxVideoModeInfos); ++i)
984 {
985 g_aVBoxVideoModeInfos[i].cModes = 0;
986 }
987}
988
989PVBOXWDDM_VIDEOMODES_INFO VBoxWddmUpdateVideoModesInfo(PVBOXMP_DEVEXT pExt, PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo)
990{
991 VBoxWddmInvalidateVideoModesInfo(pExt);
992
993 if (pVidPnInfo)
994 {
995 for (UINT i = 0; i < pVidPnInfo->cScreenInfos; ++i)
996 {
997 PVBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO pScreenInfo = &pVidPnInfo->aScreenInfos[i];
998 Assert(pScreenInfo->Id < (DWORD)VBoxCommonFromDeviceExt(pExt)->cDisplays);
999 if (pScreenInfo->Id < (DWORD)VBoxCommonFromDeviceExt(pExt)->cDisplays)
1000 {
1001 PVBOXWDDM_VIDEOMODES_INFO pInfo = &g_aVBoxVideoModeInfos[pScreenInfo->Id];
1002 VIDEO_MODE_INFORMATION ModeInfo = {0};
1003 D3DDDIFORMAT enmFormat;
1004 switch (pScreenInfo->BitsPerPixel)
1005 {
1006 case 32:
1007 enmFormat = D3DDDIFMT_A8R8G8B8;
1008 break;
1009 case 24:
1010 enmFormat = D3DDDIFMT_R8G8B8;
1011 break;
1012 case 16:
1013 enmFormat = D3DDDIFMT_R5G6B5;
1014 break;
1015 case 8:
1016 enmFormat = D3DDDIFMT_P8;
1017 break;
1018 default:
1019 Assert(0);
1020 enmFormat = D3DDDIFMT_UNKNOWN;
1021 break;
1022 }
1023 if (enmFormat != D3DDDIFMT_UNKNOWN)
1024 {
1025 if (VBoxWddmFillMode(&ModeInfo, enmFormat, pScreenInfo->Width, pScreenInfo->Height))
1026 {
1027 VBoxWddmBuildVideoModesInfo(pExt, pScreenInfo->Id, pInfo, &ModeInfo, 1);
1028 }
1029 else
1030 {
1031 Assert(0);
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 /* ensure we have all the rest populated */
1039 VBoxWddmGetAllVideoModesInfos(pExt);
1040 return g_aVBoxVideoModeInfos;
1041}
1042
1043NTSTATUS VBoxWddmGetModesForResolution(VIDEO_MODE_INFORMATION *pAllModes, uint32_t cAllModes, int iSearchPreferredMode,
1044 const D3DKMDT_2DREGION *pResolution, VIDEO_MODE_INFORMATION * pModes, uint32_t cModes, uint32_t *pcModes, int32_t *piPreferrableMode)
1045{
1046 NTSTATUS Status = STATUS_SUCCESS;
1047 uint32_t cFound = 0;
1048 int iFoundPreferrableMode = -1;
1049 for (uint32_t i = 0; i < cAllModes; ++i)
1050 {
1051 VIDEO_MODE_INFORMATION *pCur = &pAllModes[i];
1052 if (pResolution->cx == pCur->VisScreenWidth
1053 && pResolution->cy == pCur->VisScreenHeight)
1054 {
1055 if (pModes && cModes > cFound)
1056 memcpy(&pModes[cFound], pCur, sizeof(VIDEO_MODE_INFORMATION));
1057 else
1058 Status = STATUS_BUFFER_TOO_SMALL;
1059
1060 if (i == iSearchPreferredMode)
1061 iFoundPreferrableMode = cFound;
1062
1063 ++cFound;
1064 }
1065 }
1066
1067 Assert(iFoundPreferrableMode < 0 || cFound > (uint32_t)iFoundPreferrableMode);
1068
1069 *pcModes = cFound;
1070 if (piPreferrableMode)
1071 *piPreferrableMode = iFoundPreferrableMode;
1072
1073 return Status;
1074}
1075
1076PVBOXWDDM_VIDEOMODES_INFO VBoxWddmGetAllVideoModesInfos(PVBOXMP_DEVEXT pExt)
1077{
1078 /* ensure all modes are initialized */
1079 for (int i = 0; i < VBoxCommonFromDeviceExt(pExt)->cDisplays; ++i)
1080 {
1081 VBoxWddmGetVideoModesInfo(pExt, (D3DDDI_VIDEO_PRESENT_TARGET_ID)i);
1082 }
1083
1084 return g_aVBoxVideoModeInfos;
1085}
1086
1087PVBOXWDDM_VIDEOMODES_INFO VBoxWddmGetVideoModesInfo(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId)
1088{
1089 Assert(VidPnTargetId < (D3DDDI_VIDEO_PRESENT_TARGET_ID)VBoxCommonFromDeviceExt(pExt)->cDisplays);
1090 if (VidPnTargetId >= (D3DDDI_VIDEO_PRESENT_TARGET_ID)VBoxCommonFromDeviceExt(pExt)->cDisplays)
1091 {
1092 return NULL;
1093 }
1094
1095 PVBOXWDDM_VIDEOMODES_INFO pInfo = &g_aVBoxVideoModeInfos[VidPnTargetId];
1096
1097 if (!pInfo->cModes)
1098 {
1099 VBoxWddmBuildVideoModesInfo(pExt, VidPnTargetId, pInfo, NULL, 0);
1100 Assert(pInfo->cModes);
1101 }
1102
1103 return pInfo;
1104}
1105
1106#endif /*VBOX_WDDM_MINIPORT*/
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette