VirtualBox

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

Last change on this file since 51943 was 51943, checked in by vboxsync, 10 years ago

wddm: fix display-only

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.0 KB
Line 
1/* $Id: VBoxMPVidModes.cpp 51943 2014-07-08 18:51:19Z vboxsync $ */
2
3/** @file
4 * VBox Miniport video modes related functions
5 */
6
7/*
8 * Copyright (C) 2011-2013 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#include <VBox/Hardware/VBoxVideoVBE.h>
27
28#ifdef VBOX_WITH_WDDM
29# define VBOX_WITHOUT_24BPP_MODES
30#endif
31
32/* Custom video modes which are being read from registry at driver startup. */
33static VIDEO_MODE_INFORMATION g_CustomVideoModes[VBOX_VIDEO_MAX_SCREENS] = { 0 };
34
35static BOOLEAN
36VBoxMPValidateVideoModeParamsGuest(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, uint32_t xres, uint32_t yres, uint32_t bpp)
37{
38 switch (bpp)
39 {
40 case 32:
41 break;
42 case 24:
43#ifdef VBOX_WITHOUT_24BPP_MODES
44 return FALSE;
45#else
46 break;
47#endif
48 case 16:
49 break;
50 case 8:
51#ifndef VBOX_WITH_8BPP_MODES
52 return FALSE;
53#else
54#ifdef VBOX_XPDM_MINIPORT
55 if (pExt->iDevice != 0) /* Secondary monitors do not support 8 bit */
56 return FALSE;
57#endif
58 break;
59#endif
60 default:
61 WARN(("Unexpected bpp (%d)", bpp));
62 return FALSE;
63 }
64 return TRUE;
65}
66
67/* Fills given video mode BPP related fields */
68static void
69VBoxFillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
70 ULONG maskR, ULONG maskG, ULONG maskB)
71{
72 pMode->NumberRedBits = bitsR;
73 pMode->NumberGreenBits = bitsG;
74 pMode->NumberBlueBits = bitsB;
75 pMode->RedMask = maskR;
76 pMode->GreenMask = maskG;
77 pMode->BlueMask = maskB;
78}
79
80/* Fills given video mode structure */
81static void
82VBoxFillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset)
83{
84 LOGF(("%dx%d:%d (idx=%d, yoffset=%d)", xres, yres, bpp, index, yoffset));
85
86 memset(pMode, 0, sizeof(VIDEO_MODE_INFORMATION));
87
88 /*Common entries*/
89 pMode->Length = sizeof(VIDEO_MODE_INFORMATION);
90 pMode->ModeIndex = index;
91 pMode->VisScreenWidth = xres;
92 pMode->VisScreenHeight = yres - yoffset;
93 pMode->ScreenStride = xres * ((bpp + 7) / 8);
94 pMode->NumberOfPlanes = 1;
95 pMode->BitsPerPlane = bpp;
96 pMode->Frequency = 60;
97 pMode->XMillimeter = 320;
98 pMode->YMillimeter = 240;
99 pMode->VideoMemoryBitmapWidth = xres;
100 pMode->VideoMemoryBitmapHeight = yres - yoffset;
101 pMode->DriverSpecificAttributeFlags = 0;
102 pMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
103
104 /*BPP related entries*/
105 switch (bpp)
106 {
107#ifdef VBOX_WITH_8BPP_MODES
108 case 8:
109 VBoxFillVidModeBPP(pMode, 6, 6, 6, 0, 0, 0);
110
111 pMode->AttributeFlags |= VIDEO_MODE_PALETTE_DRIVEN | VIDEO_MODE_MANAGED_PALETTE;
112 break;
113#endif
114 case 16:
115 VBoxFillVidModeBPP(pMode, 5, 6, 5, 0xF800, 0x7E0, 0x1F);
116 break;
117 case 24:
118 case 32:
119 VBoxFillVidModeBPP(pMode, 8, 8, 8, 0xFF0000, 0xFF00, 0xFF);
120 break;
121 default:
122 Assert(0);
123 break;
124 }
125}
126
127void VBoxMPCmnInitCustomVideoModes(PVBOXMP_DEVEXT pExt)
128{
129 VBOXMPCMNREGISTRY Registry;
130 VP_STATUS rc;
131 int iMode;
132
133 LOGF_ENTER();
134
135 rc = VBoxMPCmnRegInit(pExt, &Registry);
136 VBOXMP_WARN_VPS(rc);
137
138 /* Initialize all custom modes to the 800x600x32 */
139 VBoxFillVidModeInfo(&g_CustomVideoModes[0], 800, 600, 32, 0, 0);
140 for (iMode=1; iMode<RT_ELEMENTS(g_CustomVideoModes); ++iMode)
141 {
142 g_CustomVideoModes[iMode] = g_CustomVideoModes[0];
143 }
144
145 /* Read stored custom resolution info from registry */
146 for (iMode=0; iMode<VBoxCommonFromDeviceExt(pExt)->cDisplays; ++iMode)
147 {
148 uint32_t CustomXRes = 0, CustomYRes = 0, CustomBPP = 0;
149
150 if (iMode==0)
151 {
152 /*First name without a suffix*/
153 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomXRes", &CustomXRes);
154 VBOXMP_WARN_VPS_NOBP(rc);
155 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomYRes", &CustomYRes);
156 VBOXMP_WARN_VPS_NOBP(rc);
157 rc = VBoxMPCmnRegQueryDword(Registry, L"CustomBPP", &CustomBPP);
158 VBOXMP_WARN_VPS_NOBP(rc);
159 }
160 else
161 {
162 wchar_t keyname[32];
163 swprintf(keyname, L"CustomXRes%d", iMode);
164 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomXRes);
165 VBOXMP_WARN_VPS_NOBP(rc);
166 swprintf(keyname, L"CustomYRes%d", iMode);
167 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomYRes);
168 VBOXMP_WARN_VPS_NOBP(rc);
169 swprintf(keyname, L"CustomBPP%d", iMode);
170 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &CustomBPP);
171 VBOXMP_WARN_VPS_NOBP(rc);
172 }
173
174 LOG(("got stored custom resolution[%d] %dx%dx%d", iMode, CustomXRes, CustomYRes, CustomBPP));
175
176 if (CustomXRes || CustomYRes || CustomBPP)
177 {
178 if (CustomXRes == 0)
179 {
180 CustomXRes = g_CustomVideoModes[iMode].VisScreenWidth;
181 }
182 if (CustomYRes == 0)
183 {
184 CustomYRes = g_CustomVideoModes[iMode].VisScreenHeight;
185 }
186 if (CustomBPP == 0)
187 {
188 CustomBPP = g_CustomVideoModes[iMode].BitsPerPlane;
189 }
190
191 if (VBoxMPValidateVideoModeParamsGuest(pExt, iMode, CustomXRes, CustomYRes, CustomBPP))
192 {
193 VBoxFillVidModeInfo(&g_CustomVideoModes[iMode], CustomXRes, CustomYRes, CustomBPP, 0, 0);
194 }
195 }
196 }
197
198 rc = VBoxMPCmnRegFini(Registry);
199 VBOXMP_WARN_VPS(rc);
200 LOGF_LEAVE();
201}
202
203VIDEO_MODE_INFORMATION *VBoxMPCmnGetCustomVideoModeInfo(ULONG ulIndex)
204{
205 return (ulIndex<RT_ELEMENTS(g_CustomVideoModes)) ? &g_CustomVideoModes[ulIndex] : NULL;
206}
207
208#ifdef VBOX_XPDM_MINIPORT
209VIDEO_MODE_INFORMATION* VBoxMPCmnGetVideoModeInfo(PVBOXMP_DEVEXT pExt, ULONG ulIndex)
210{
211 return (ulIndex<RT_ELEMENTS(pExt->aVideoModes)) ? &pExt->aVideoModes[ulIndex] : NULL;
212}
213#endif
214
215static bool VBoxMPVideoModesMatch(const PVIDEO_MODE_INFORMATION pMode1, const PVIDEO_MODE_INFORMATION pMode2)
216{
217 return pMode1->VisScreenHeight == pMode2->VisScreenHeight
218 && pMode1->VisScreenWidth == pMode2->VisScreenWidth
219 && pMode1->BitsPerPlane == pMode2->BitsPerPlane;
220}
221
222static int
223VBoxMPFindVideoMode(const PVIDEO_MODE_INFORMATION pModesTable, int cModes, const PVIDEO_MODE_INFORMATION pMode)
224{
225 for (int i = 0; i < cModes; ++i)
226 {
227 if (VBoxMPVideoModesMatch(pMode, &pModesTable[i]))
228 {
229 return i;
230 }
231 }
232 return -1;
233}
234
235/* Helper function to dynamically build our table of standard video
236 * modes. We take the amount of VRAM and create modes with standard
237 * geometries until we've either reached the maximum number of modes
238 * or the available VRAM does not allow for additional modes.
239 * We also check registry for manually added video modes.
240 * Returns number of modes added to the table.
241 */
242static uint32_t
243VBoxMPFillModesTable(PVBOXMP_DEVEXT pExt, int iDisplay, PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize,
244 int32_t *pPrefModeIdx)
245{
246 /* the resolution matrix */
247 struct
248 {
249 uint16_t xRes;
250 uint16_t yRes;
251 } resolutionMatrix[] =
252 {
253 /* standard modes */
254 { 640, 480 },
255 { 800, 600 },
256 { 1024, 768 },
257 { 1152, 864 },
258 { 1280, 960 },
259 { 1280, 1024 },
260 { 1400, 1050 },
261 { 1600, 1200 },
262 { 1920, 1440 },
263#ifndef VBOX_WITH_WDDM
264 /* multi screen modes with 1280x1024 */
265 { 2560, 1024 },
266 { 3840, 1024 },
267 { 5120, 1024 },
268 /* multi screen modes with 1600x1200 */
269 { 3200, 1200 },
270 { 4800, 1200 },
271 { 6400, 1200 },
272#endif
273 };
274
275#ifdef VBOX_XPDM_MINIPORT
276 ULONG vramSize = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
277#else
278 ULONG vramSize = vboxWddmVramCpuVisibleSegmentSize(pExt);
279 vramSize /= pExt->u.primary.commonInfo.cDisplays;
280# ifdef VBOX_WDDM_WIN8
281 if (!g_VBoxDisplayOnly)
282# endif
283 {
284 /* at least two surfaces will be needed: primary & shadow */
285 vramSize /= 2;
286 }
287 vramSize &= ~PAGE_OFFSET_MASK;
288#endif
289
290 uint32_t iMode=0, iPrefIdx=0;
291 /* there are 4 color depths: 8, 16, 24 and 32bpp and we reserve 50% of the modes for other sources */
292 size_t maxModesPerColorDepth = VBOXMP_MAX_VIDEO_MODES / 2 / 4;
293
294 /* Always add 800x600 video modes. Windows XP+ needs at least 800x600 resolution
295 * and fallbacks to 800x600x4bpp VGA mode if the driver did not report suitable modes.
296 * This resolution could be rejected by a low resolution host (netbooks, etc).
297 */
298#ifdef VBOX_WITH_8BPP_MODES
299 int bytesPerPixel=1;
300#else
301 int bytesPerPixel=2;
302#endif
303 for (; bytesPerPixel<=4; bytesPerPixel++)
304 {
305 int bitsPerPixel = 8*bytesPerPixel;
306
307 if (800*600*bytesPerPixel > (LONG)vramSize)
308 {
309 /* we don't have enough VRAM for this mode */
310 continue;
311 }
312
313 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iMode, 800, 600, bitsPerPixel))
314 continue;
315
316 VBoxFillVidModeInfo(&pModesTable[iMode], 800, 600, bitsPerPixel, iMode+1, 0);
317
318 if (32==bitsPerPixel)
319 {
320 iPrefIdx = iMode;
321 }
322 ++iMode;
323 }
324
325 /* Query yoffset from the host */
326 ULONG yOffset = VBoxGetHeightReduction();
327
328 /* Iterate through our static resolution table and add supported video modes for different bpp's */
329#ifdef VBOX_WITH_8BPP_MODES
330 bytesPerPixel=1;
331#else
332 bytesPerPixel=2;
333#endif
334 for (; bytesPerPixel<=4; bytesPerPixel++)
335 {
336 int bitsPerPixel = 8*bytesPerPixel;
337 size_t cAdded, resIndex;
338
339 for (cAdded=0, resIndex=0; resIndex<RT_ELEMENTS(resolutionMatrix) && cAdded<maxModesPerColorDepth; resIndex++)
340 {
341 if (resolutionMatrix[resIndex].xRes * resolutionMatrix[resIndex].yRes * bytesPerPixel > (LONG)vramSize)
342 {
343 /* we don't have enough VRAM for this mode */
344 continue;
345 }
346
347 if (yOffset == 0 && resolutionMatrix[resIndex].xRes == 800 && resolutionMatrix[resIndex].yRes == 600)
348 {
349 /* this mode was already added */
350 continue;
351 }
352
353 if (
354#ifdef VBOX_WDDM_MINIPORT
355 /* 1024x768 resolution is a minimal resolutions for win8 to make most metro apps run.
356 * For small host display resolutions, host will dislike the mode 1024x768 and above
357 * if the framebuffer window requires scrolling to fit the guest resolution.
358 * So add 1024x768 resolution for win8 guest to allow user switch to it */
359 ( (VBoxQueryWinVersion() != WIN8 && VBoxQueryWinVersion() != WIN81)
360 || resolutionMatrix[resIndex].xRes != 1024
361 || resolutionMatrix[resIndex].yRes != 768)
362 &&
363#endif
364 !VBoxLikesVideoMode(iDisplay, resolutionMatrix[resIndex].xRes,
365 resolutionMatrix[resIndex].yRes - yOffset, bitsPerPixel))
366 {
367 /* host doesn't like this mode */
368 continue;
369 }
370
371 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes, bitsPerPixel))
372 {
373 /* guest does not like this mode */
374 continue;
375 }
376
377 /* Sanity check, we shouldn't ever get here */
378 if (iMode >= tableSize)
379 {
380 WARN(("video modes table overflow!"));
381 break;
382 }
383
384 VBoxFillVidModeInfo(&pModesTable[iMode], resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes, bitsPerPixel, iMode+1, yOffset);
385 ++iMode;
386 ++cAdded;
387 }
388 }
389
390 /* Check registry for manually added modes, up to 128 entries is supported
391 * Give up on the first error encountered.
392 */
393 VBOXMPCMNREGISTRY Registry;
394 int fPrefSet=0;
395 VP_STATUS rc;
396
397 rc = VBoxMPCmnRegInit(pExt, &Registry);
398 VBOXMP_WARN_VPS(rc);
399
400 for (int curKey=0; curKey<128; curKey++)
401 {
402 if (iMode>=tableSize)
403 {
404 WARN(("ignoring possible custom mode(s), table is full!"));
405 break;
406 }
407
408 wchar_t keyname[24];
409 uint32_t xres, yres, bpp = 0;
410
411 swprintf(keyname, L"CustomMode%dWidth", curKey);
412 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &xres);
413 VBOXMP_CHECK_VPS_BREAK(rc);
414
415 swprintf(keyname, L"CustomMode%dHeight", curKey);
416 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &yres);
417 VBOXMP_CHECK_VPS_BREAK(rc);
418
419 swprintf(keyname, L"CustomMode%dBPP", curKey);
420 rc = VBoxMPCmnRegQueryDword(Registry, keyname, &bpp);
421 VBOXMP_CHECK_VPS_BREAK(rc);
422
423 LOG(("got custom mode[%u]=%ux%u:%u", curKey, xres, yres, bpp));
424
425 /* round down width to be a multiple of 8 if necessary */
426 if (!VBoxCommonFromDeviceExt(pExt)->fAnyX)
427 {
428 xres &= 0xFFF8;
429 }
430
431 if ( (xres > (1 << 16))
432 || (yres > (1 << 16))
433 || ( (bpp != 16)
434 && (bpp != 24)
435 && (bpp != 32)))
436 {
437 /* incorrect values */
438 break;
439 }
440
441 /* does it fit within our VRAM? */
442 if (xres * yres * (bpp / 8) > vramSize)
443 {
444 /* we don't have enough VRAM for this mode */
445 break;
446 }
447
448 if (!VBoxLikesVideoMode(iDisplay, xres, yres, bpp))
449 {
450 /* host doesn't like this mode */
451 break;
452 }
453
454 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, xres, yres, bpp))
455 {
456 /* guest does not like this mode */
457 continue;
458 }
459
460 LOG(("adding video mode from registry."));
461
462 VBoxFillVidModeInfo(&pModesTable[iMode], xres, yres, bpp, iMode+1, yOffset);
463
464 if (!fPrefSet)
465 {
466 fPrefSet = 1;
467 iPrefIdx = iMode;
468 }
469#ifdef VBOX_WDDM_MINIPORT
470 /*check if the same mode has been added to the table already*/
471 int foundIdx = VBoxMPFindVideoMode(pModesTable, iMode, &pModesTable[iMode]);
472
473 if (foundIdx>=0)
474 {
475 if (iPrefIdx==iMode)
476 {
477 iPrefIdx=foundIdx;
478 }
479 }
480 else
481#endif
482 {
483 ++iMode;
484 }
485 }
486
487 rc = VBoxMPCmnRegFini(Registry);
488 VBOXMP_WARN_VPS(rc);
489
490 if (pPrefModeIdx)
491 {
492 *pPrefModeIdx = iPrefIdx;
493 }
494
495 return iMode;
496}
497
498/* Returns if we're in the first mode change, ie doesn't have valid video mode set yet */
499static BOOLEAN VBoxMPIsStartingUp(PVBOXMP_DEVEXT pExt, uint32_t iDisplay)
500{
501#ifdef VBOX_XPDM_MINIPORT
502 return (pExt->CurrentMode == 0);
503#else
504 VBOXWDDM_SOURCE *pSource = &pExt->aSources[iDisplay];
505 return !pSource->AllocData.SurfDesc.width || !pSource->AllocData.SurfDesc.height;
506#endif
507}
508
509#ifdef VBOX_WDDM_MINIPORT
510static const uint32_t g_aVBoxVidModesSupportedBpps[] = {
511 32
512#ifndef VBOX_WITHOUT_24BPP_MODES
513 , 24
514#endif
515 , 16
516#ifdef VBOX_WITH_8BPP_MODES
517 , 8
518#endif
519};
520DECLINLINE(BOOLEAN) VBoxMPIsSupportedBpp(uint32_t bpp)
521{
522 for (int i = 0; i < RT_ELEMENTS(g_aVBoxVidModesSupportedBpps); ++i)
523 {
524 if (bpp == g_aVBoxVidModesSupportedBpps[i])
525 return TRUE;
526 }
527 return FALSE;
528}
529
530DECLINLINE(uint32_t) VBoxMPAdjustBpp(uint32_t bpp)
531{
532 if (VBoxMPIsSupportedBpp(bpp))
533 return bpp;
534 Assert(g_aVBoxVidModesSupportedBpps[0] == 32);
535 return g_aVBoxVidModesSupportedBpps[0];
536}
537#endif
538/* Updates missing video mode params with current values,
539 * Checks if resulting mode is liked by the host and fits into VRAM.
540 * Returns TRUE if resulting mode could be used.
541 */
542static BOOLEAN
543VBoxMPValidateVideoModeParams(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, uint32_t &xres, uint32_t &yres, uint32_t &bpp)
544{
545 /* Make sure all important video mode values are set */
546 if (VBoxMPIsStartingUp(pExt, iDisplay))
547 {
548 /* Use stored custom values only if nothing was read from host. */
549 xres = xres ? xres:g_CustomVideoModes[iDisplay].VisScreenWidth;
550 yres = yres ? yres:g_CustomVideoModes[iDisplay].VisScreenHeight;
551 bpp = bpp ? bpp :g_CustomVideoModes[iDisplay].BitsPerPlane;
552 }
553 else
554 {
555 /* Use current values for field which weren't read from host. */
556#ifdef VBOX_XPDM_MINIPORT
557 xres = xres ? xres:pExt->CurrentModeWidth;
558 yres = yres ? yres:pExt->CurrentModeHeight;
559 bpp = bpp ? bpp :pExt->CurrentModeBPP;
560#else
561 PVBOXWDDM_ALLOC_DATA pAllocData = pExt->aSources[iDisplay].pPrimaryAllocation ?
562 &pExt->aSources[iDisplay].pPrimaryAllocation->AllocData
563 : &pExt->aSources[iDisplay].AllocData;
564 xres = xres ? xres:pAllocData->SurfDesc.width;
565 yres = yres ? yres:pAllocData->SurfDesc.height;
566 /* VBox WDDM driver does not allow 24 modes since OS could choose the 24bit mode as default in that case,
567 * the pExt->aSources[iDisplay].AllocData.SurfDesc.bpp could be initially 24 though,
568 * i.e. when driver occurs the current mode on driver load via DxgkCbAcquirePostDisplayOwnership
569 * and until driver reports the supported modes
570 * This is true for Win8 Display-Only driver currently since DxgkCbAcquirePostDisplayOwnership is only used by it
571 *
572 * This is why we need to adjust the current mode bpp to the value we actually report as supported */
573 bpp = bpp ? bpp : VBoxMPAdjustBpp(pAllocData->SurfDesc.bpp);
574#endif
575 }
576
577 /* Round down width to be a multiple of 8 if necessary */
578 if (!VBoxCommonFromDeviceExt(pExt)->fAnyX)
579 {
580 xres &= 0xFFF8;
581 }
582
583 /* We always need bpp to be set */
584 if (!bpp)
585 {
586 bpp=32;
587 }
588
589 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, xres, yres, bpp))
590 {
591 WARN(("GUEST does not like special mode %dx%d:%d for display %d", xres, yres, bpp, iDisplay));
592 return FALSE;
593 }
594
595 /* Check if host likes this mode */
596 if (!VBoxLikesVideoMode(iDisplay, xres, yres, bpp))
597 {
598 WARN_NOBP(("HOST does not like special mode %dx%d:%d for display %d", xres, yres, bpp, iDisplay));
599 return FALSE;
600 }
601
602#ifdef VBOX_XPDM_MINIPORT
603 ULONG vramSize = pExt->pPrimary->u.primary.ulMaxFrameBufferSize;
604#else
605 ULONG vramSize = vboxWddmVramCpuVisibleSegmentSize(pExt);
606 vramSize /= pExt->u.primary.commonInfo.cDisplays;
607# ifdef VBOX_WDDM_WIN8
608 if (!g_VBoxDisplayOnly)
609# endif
610 {
611 /* at least two surfaces will be needed: primary & shadow */
612 vramSize /= 2;
613 }
614 vramSize &= ~PAGE_OFFSET_MASK;
615#endif
616
617 /* Check that values are valid and mode fits into VRAM */
618 if (!xres || !yres
619 || !((bpp == 16)
620#ifdef VBOX_WITH_8BPP_MODES
621 || (bpp == 8)
622#endif
623 || (bpp == 24)
624 || (bpp == 32)))
625 {
626 LOG(("invalid params for special mode %dx%d:%d", xres, yres, bpp));
627 return FALSE;
628 }
629
630
631
632 if ((xres * yres * (bpp / 8) >= vramSize))
633 {
634 /* Store values of last reported release log message to avoid log flooding. */
635 static uint32_t s_xresNoVRAM=0, s_yresNoVRAM=0, s_bppNoVRAM=0;
636
637 LOG(("not enough VRAM for video mode %dx%dx%dbpp. Available: %d bytes. Required: more than %d bytes.",
638 xres, yres, bpp, vramSize, xres * yres * (bpp / 8)));
639
640 s_xresNoVRAM = xres;
641 s_yresNoVRAM = yres;
642 s_bppNoVRAM = bpp;
643
644 return FALSE;
645 }
646
647 return TRUE;
648}
649
650/* Checks if there's a pending video mode change hint,
651 * and fills pPendingMode with associated info.
652 * returns TRUE if there's a pending change. Otherwise returns FALSE.
653 */
654static BOOLEAN
655VBoxMPCheckPendingVideoMode(PVBOXMP_DEVEXT pExt, PVIDEO_MODE_INFORMATION pPendingMode)
656{
657 uint32_t xres=0, yres=0, bpp=0, display=0;
658
659 /* Check if there's a pending display change request for this display */
660 if (VBoxQueryDisplayRequest(&xres, &yres, &bpp, &display) && (xres || yres || bpp))
661 {
662 if (display>RT_ELEMENTS(g_CustomVideoModes))
663 {
664 /*display = RT_ELEMENTS(g_CustomVideoModes) - 1;*/
665 WARN(("VBoxQueryDisplayRequest returned invalid display number %d", display));
666 return FALSE;
667 }
668 }
669 else
670 {
671 LOG(("no pending request"));
672 return FALSE;
673 }
674
675 /* Correct video mode params and check if host likes it */
676 if (VBoxMPValidateVideoModeParams(pExt, display, xres, yres, bpp))
677 {
678 VBoxFillVidModeInfo(pPendingMode, xres, yres, bpp, display, 0);
679 return TRUE;
680 }
681
682 return FALSE;
683}
684
685/* Save custom mode info to registry */
686static void VBoxMPRegSaveModeInfo(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, PVIDEO_MODE_INFORMATION pMode)
687{
688 VBOXMPCMNREGISTRY Registry;
689 VP_STATUS rc;
690
691 rc = VBoxMPCmnRegInit(pExt, &Registry);
692 VBOXMP_WARN_VPS(rc);
693
694 if (iDisplay==0)
695 {
696 /*First name without a suffix*/
697 rc = VBoxMPCmnRegSetDword(Registry, L"CustomXRes", pMode->VisScreenWidth);
698 VBOXMP_WARN_VPS(rc);
699 rc = VBoxMPCmnRegSetDword(Registry, L"CustomYRes", pMode->VisScreenHeight);
700 VBOXMP_WARN_VPS(rc);
701 rc = VBoxMPCmnRegSetDword(Registry, L"CustomBPP", pMode->BitsPerPlane);
702 VBOXMP_WARN_VPS(rc);
703 }
704 else
705 {
706 wchar_t keyname[32];
707 swprintf(keyname, L"CustomXRes%d", iDisplay);
708 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->VisScreenWidth);
709 VBOXMP_WARN_VPS(rc);
710 swprintf(keyname, L"CustomYRes%d", iDisplay);
711 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->VisScreenHeight);
712 VBOXMP_WARN_VPS(rc);
713 swprintf(keyname, L"CustomBPP%d", iDisplay);
714 rc = VBoxMPCmnRegSetDword(Registry, keyname, pMode->BitsPerPlane);
715 VBOXMP_WARN_VPS(rc);
716 }
717
718 rc = VBoxMPCmnRegFini(Registry);
719 VBOXMP_WARN_VPS(rc);
720}
721
722#ifdef VBOX_XPDM_MINIPORT
723VIDEO_MODE_INFORMATION* VBoxMPXpdmCurrentVideoMode(PVBOXMP_DEVEXT pExt)
724{
725 return VBoxMPCmnGetVideoModeInfo(pExt, pExt->CurrentMode - 1);
726}
727
728ULONG VBoxMPXpdmGetVideoModesCount(PVBOXMP_DEVEXT pExt)
729{
730 return pExt->cVideoModes;
731}
732
733/* Makes a table of video modes consisting of:
734 * Default modes
735 * Custom modes manually added to registry
736 * Custom modes for all displays (either from a display change hint or stored in registry)
737 * 2 special modes, for a pending display change for this adapter. See comments below.
738 */
739void VBoxMPXpdmBuildVideoModesTable(PVBOXMP_DEVEXT pExt)
740{
741 uint32_t cStandartModes;
742 BOOLEAN bPending, bHaveSpecial;
743 VIDEO_MODE_INFORMATION specialMode;
744
745 /* Fill table with standart modes and ones manually added to registry.
746 * Up to VBOXMP_MAX_VIDEO_MODES elements can be used, the rest is reserved
747 * for custom mode alternating indexes.
748 */
749 cStandartModes = VBoxMPFillModesTable(pExt, pExt->iDevice, pExt->aVideoModes, VBOXMP_MAX_VIDEO_MODES, NULL);
750
751 /* Add custom mode for this display to the table */
752 /* Make 2 entries in the video mode table. */
753 uint32_t iModeBase = cStandartModes;
754
755 /* Take the alternating index into account. */
756 BOOLEAN bAlternativeIndex = (pExt->iInvocationCounter % 2)? TRUE: FALSE;
757
758 uint32_t iSpecialMode = iModeBase + (bAlternativeIndex? 1: 0);
759 uint32_t iStandardMode = iModeBase + (bAlternativeIndex? 0: 1);
760
761 /* Fill the special mode. */
762 memcpy(&pExt->aVideoModes[iSpecialMode], &g_CustomVideoModes[pExt->iDevice], sizeof(VIDEO_MODE_INFORMATION));
763 pExt->aVideoModes[iSpecialMode].ModeIndex = iSpecialMode + 1;
764
765 /* Wipe the other entry so it is not selected. */
766 memcpy(&pExt->aVideoModes[iStandardMode], &pExt->aVideoModes[3], sizeof(VIDEO_MODE_INFORMATION));
767 pExt->aVideoModes[iStandardMode].ModeIndex = iStandardMode + 1;
768
769 LOG(("added special mode[%d] %dx%d:%d for display %d\n",
770 iSpecialMode,
771 pExt->aVideoModes[iSpecialMode].VisScreenWidth,
772 pExt->aVideoModes[iSpecialMode].VisScreenHeight,
773 pExt->aVideoModes[iSpecialMode].BitsPerPlane,
774 pExt->iDevice));
775
776 /* Check if host wants us to switch video mode and it's for this adapter */
777 bPending = VBoxMPCheckPendingVideoMode(pExt, &specialMode);
778 bHaveSpecial = bPending && (pExt->iDevice == specialMode.ModeIndex);
779 LOG(("bPending %d, pExt->iDevice %d, specialMode.ModeIndex %d",
780 bPending, pExt->iDevice, specialMode.ModeIndex));
781
782 /* Check the startup case */
783 if (!bHaveSpecial && VBoxMPIsStartingUp(pExt, pExt->iDevice))
784 {
785 uint32_t xres=0, yres=0, bpp=0;
786 LOG(("Startup for screen %d", pExt->iDevice));
787 /* Check if we could make valid mode from values stored to registry */
788 if (VBoxMPValidateVideoModeParams(pExt, pExt->iDevice, xres, yres, bpp))
789 {
790 LOG(("Startup for screen %d validated %dx%d %d", pExt->iDevice, xres, yres, bpp));
791 VBoxFillVidModeInfo(&specialMode, xres, yres, bpp, 0, 0);
792 bHaveSpecial = TRUE;
793 }
794 }
795
796 /* Update number of modes. Each display has 2 entries for alternating custom mode index. */
797 pExt->cVideoModes = cStandartModes + 2;
798
799 if (bHaveSpecial)
800 {
801 /* We need to alternate mode index entry for a pending mode change,
802 * else windows will ignore actual mode change call.
803 * Only alternate index if one of mode parameters changed and
804 * regardless of conditions always add 2 entries to the table.
805 */
806 BOOLEAN bAlternativeIndex = FALSE;
807
808 BOOLEAN bChanged = (pExt->Prev_xres!=specialMode.VisScreenWidth
809 || pExt->Prev_yres!=specialMode.VisScreenHeight
810 || pExt->Prev_bpp!=specialMode.BitsPerPlane);
811
812 LOG(("prev %dx%dx%d, special %dx%dx%d",
813 pExt->Prev_xres, pExt->Prev_yres, pExt->Prev_bpp,
814 specialMode.VisScreenWidth, specialMode.VisScreenHeight, specialMode.BitsPerPlane));
815
816 if (bChanged)
817 {
818 pExt->Prev_xres = specialMode.VisScreenWidth;
819 pExt->Prev_yres = specialMode.VisScreenHeight;
820 pExt->Prev_bpp = specialMode.BitsPerPlane;
821 }
822
823 /* Check if we need to alternate the index */
824 if (!VBoxMPIsStartingUp(pExt, pExt->iDevice))
825 {
826 if (bChanged)
827 {
828 pExt->iInvocationCounter++;
829 }
830
831 if (pExt->iInvocationCounter % 2)
832 {
833 bAlternativeIndex = TRUE;
834 }
835 }
836
837 uint32_t iSpecialModeElement = cStandartModes + (bAlternativeIndex? 1: 0);
838 uint32_t iSpecialModeElementOld = cStandartModes + (bAlternativeIndex? 0: 1);
839
840 LOG(("add special mode[%d] %dx%d:%d for display %d (bChanged=%d, bAlternativeIndex=%d)",
841 iSpecialModeElement, specialMode.VisScreenWidth, specialMode.VisScreenHeight, specialMode.BitsPerPlane,
842 pExt->iDevice, bChanged, bAlternativeIndex));
843
844 /* Add special mode to the table
845 * Note: Y offset isn't used for a host-supplied modes
846 */
847 specialMode.ModeIndex = iSpecialModeElement + 1;
848 memcpy(&pExt->aVideoModes[iSpecialModeElement], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
849
850 /* Save special mode in the custom modes table */
851 memcpy(&g_CustomVideoModes[pExt->iDevice], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
852
853 /* Wipe the old entry so the special mode will be found in the new positions. */
854 memcpy(&pExt->aVideoModes[iSpecialModeElementOld], &pExt->aVideoModes[3], sizeof(VIDEO_MODE_INFORMATION));
855 pExt->aVideoModes[iSpecialModeElementOld].ModeIndex = iSpecialModeElementOld + 1;
856
857 /* Save special mode info to registry */
858 VBoxMPRegSaveModeInfo(pExt, pExt->iDevice, &specialMode);
859 }
860
861#if defined(LOG_ENABLED)
862 do
863 {
864 LOG(("Filled %d modes for display %d", pExt->cVideoModes, pExt->iDevice));
865
866 for (uint32_t i=0; i < pExt->cVideoModes; ++i)
867 {
868 LOG(("Mode[%2d]: %4dx%4d:%2d (idx=%d)",
869 i, pExt->aVideoModes[i].VisScreenWidth, pExt->aVideoModes[i].VisScreenHeight,
870 pExt->aVideoModes[i].BitsPerPlane, pExt->aVideoModes[i].ModeIndex));
871 }
872 } while (0);
873#endif
874}
875#endif /*VBOX_XPDM_MINIPORT*/
876
877#ifdef VBOX_WDDM_MINIPORT
878static VBOXWDDM_VIDEOMODES_INFO g_aVBoxVideoModeInfos[VBOX_VIDEO_MAX_SCREENS] = {0};
879static VBOXWDDM_VIDEOMODES_INFO g_VBoxVideoModeTmp;
880
881bool VBoxWddmFillMode(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, VIDEO_MODE_INFORMATION *pInfo, D3DDDIFORMAT enmFormat, ULONG w, ULONG h)
882{
883 switch (enmFormat)
884 {
885 case D3DDDIFMT_A8R8G8B8:
886 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, w, h, 32))
887 {
888 WARN(("unsupported mode info for format(%d)", enmFormat));
889 return false;
890 }
891 VBoxFillVidModeInfo(pInfo, w, h, 32, 0, 0);
892 return true;
893 case D3DDDIFMT_R8G8B8:
894 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, w, h, 24))
895 {
896 WARN(("unsupported mode info for format(%d)", enmFormat));
897 return false;
898 }
899 VBoxFillVidModeInfo(pInfo, w, h, 24, 0, 0);
900 return true;
901 case D3DDDIFMT_R5G6B5:
902 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, w, h, 16))
903 {
904 WARN(("unsupported mode info for format(%d)", enmFormat));
905 return false;
906 }
907 VBoxFillVidModeInfo(pInfo, w, h, 16, 0, 0);
908 return true;
909 case D3DDDIFMT_P8:
910 if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, w, h, 8))
911 {
912 WARN(("unsupported mode info for format(%d)", enmFormat));
913 return false;
914 }
915 VBoxFillVidModeInfo(pInfo, w, h, 8, 0, 0);
916 return true;
917 default:
918 WARN(("unsupported enmFormat(%d)", enmFormat));
919 AssertBreakpoint();
920 break;
921 }
922
923 return false;
924}
925
926static void
927VBoxWddmBuildResolutionTable(PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize, int iPreferredMode,
928 SIZE *pResolutions, uint32_t * pcResolutions, int *piPreferredResolution)
929{
930 uint32_t cResolutionsArray = *pcResolutions;
931 uint32_t cResolutions = 0;
932
933 *piPreferredResolution = -1;
934 *pcResolutions = 0;
935
936 for (uint32_t i=0; i<tableSize; ++i)
937 {
938 PVIDEO_MODE_INFORMATION pMode = &pModesTable[i];
939 int iResolution = -1;
940
941 for (uint32_t j=0; j<cResolutions; ++j)
942 {
943 if (pResolutions[j].cx == pMode->VisScreenWidth
944 && pResolutions[j].cy == pMode->VisScreenHeight)
945 {
946 iResolution = j;
947 break;
948 }
949 }
950
951 if (iResolution < 0)
952 {
953 if (cResolutions == cResolutionsArray)
954 {
955 WARN(("table overflow!"));
956 break;
957 }
958
959 iResolution = cResolutions;
960 pResolutions[cResolutions].cx = pMode->VisScreenWidth;
961 pResolutions[cResolutions].cy = pMode->VisScreenHeight;
962 ++cResolutions;
963 }
964
965 Assert(iResolution >= 0);
966 if (i == iPreferredMode)
967 {
968 Assert(*piPreferredResolution == -1);
969 *piPreferredResolution = iResolution;
970 }
971 }
972
973 *pcResolutions = cResolutions;
974 Assert(*piPreferredResolution >= 0);
975}
976
977static void VBoxWddmBuildResolutionTableForModes(PVBOXWDDM_VIDEOMODES_INFO pModes)
978{
979 pModes->cResolutions = RT_ELEMENTS(pModes->aResolutions);
980 VBoxWddmBuildResolutionTable(pModes->aModes, pModes->cModes, pModes->iPreferredMode,
981 (SIZE*)((void*)pModes->aResolutions), &pModes->cResolutions, &pModes->iPreferredResolution);
982 Assert(pModes->aResolutions[pModes->iPreferredResolution].cx == pModes->aModes[pModes->iPreferredMode].VisScreenWidth
983 && pModes->aResolutions[pModes->iPreferredResolution].cy == pModes->aModes[pModes->iPreferredMode].VisScreenHeight);
984 Assert(pModes->cModes >= pModes->cResolutions);
985}
986
987AssertCompile(sizeof (SIZE) == sizeof (D3DKMDT_2DREGION));
988AssertCompile(RT_OFFSETOF(SIZE, cx) == RT_OFFSETOF(D3DKMDT_2DREGION, cx));
989AssertCompile(RT_OFFSETOF(SIZE, cy) == RT_OFFSETOF(D3DKMDT_2DREGION, cy));
990static void
991VBoxWddmBuildVideoModesInfo(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId,
992 PVBOXWDDM_VIDEOMODES_INFO pModes, VIDEO_MODE_INFORMATION *paAddlModes,
993 UINT cAddlModes)
994{
995 pModes->cResolutions = RT_ELEMENTS(pModes->aResolutions);
996
997 /* Add default modes and ones read from registry. */
998 pModes->cModes = VBoxMPFillModesTable(pExt, VidPnTargetId, pModes->aModes, RT_ELEMENTS(pModes->aModes), &pModes->iPreferredMode);
999 Assert(pModes->cModes<=RT_ELEMENTS(pModes->aModes));
1000
1001 if (!VBoxMPIsStartingUp(pExt, VidPnTargetId))
1002 {
1003 /* make sure we keep the current mode to avoid mode flickering */
1004 PVBOXWDDM_ALLOC_DATA pAllocData = pExt->aSources[VidPnTargetId].pPrimaryAllocation ?
1005 &pExt->aSources[VidPnTargetId].pPrimaryAllocation->AllocData
1006 : &pExt->aSources[VidPnTargetId].AllocData;
1007 if (pModes->cModes < RT_ELEMENTS(pModes->aModes))
1008 {
1009 /* VBox WDDM driver does not allow 24 modes since OS could choose the 24bit mode as default in that case,
1010 * the pExt->aSources[iDisplay].AllocData.SurfDesc.bpp could be initially 24 though,
1011 * i.e. when driver occurs the current mode on driver load via DxgkCbAcquirePostDisplayOwnership
1012 * and until driver reports the supported modes
1013 * This is true for Win8 Display-Only driver currently since DxgkCbAcquirePostDisplayOwnership is only used by it
1014 *
1015 * This is why we check the bpp to be supported here and add the current mode to the list only in that case */
1016 if (VBoxMPIsSupportedBpp(pAllocData->SurfDesc.bpp))
1017 {
1018 int foundIdx;
1019 VBoxFillVidModeInfo(&pModes->aModes[pModes->cModes], pAllocData->SurfDesc.width, pAllocData->SurfDesc.height, pAllocData->SurfDesc.bpp, 1/*index*/, 0);
1020 if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]))>=0)
1021 {
1022 pModes->iPreferredMode = foundIdx;
1023 }
1024 else
1025 {
1026 pModes->iPreferredMode = pModes->cModes;
1027 ++pModes->cModes;
1028 }
1029
1030#ifdef VBOX_WITH_8BPP_MODES
1031 int bytesPerPixel=1;
1032#else
1033 int bytesPerPixel=2;
1034#endif
1035 for (; bytesPerPixel<=4; bytesPerPixel++)
1036 {
1037 int bpp = 8*bytesPerPixel;
1038
1039 if (bpp == pAllocData->SurfDesc.bpp)
1040 continue;
1041
1042 if (!VBoxMPValidateVideoModeParamsGuest(pExt, VidPnTargetId,
1043 pAllocData->SurfDesc.width, pAllocData->SurfDesc.height,
1044 bpp))
1045 continue;
1046
1047 if (pModes->cModes >= RT_ELEMENTS(pModes->aModes))
1048 {
1049 WARN(("ran out of video modes 2"));
1050 break;
1051 }
1052
1053 VBoxFillVidModeInfo(&pModes->aModes[pModes->cModes],
1054 pAllocData->SurfDesc.width, pAllocData->SurfDesc.height,
1055 bpp, pModes->cModes, 0);
1056 if (VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]) < 0)
1057 {
1058 ++pModes->cModes;
1059 }
1060 }
1061 }
1062 }
1063 else
1064 {
1065 WARN(("ran out of video modes 1"));
1066 }
1067 }
1068
1069 /* Check if there's a pending display change request for this adapter */
1070 VIDEO_MODE_INFORMATION specialMode;
1071 if (VBoxMPCheckPendingVideoMode(pExt, &specialMode) && (specialMode.ModeIndex==VidPnTargetId))
1072 {
1073 /*Minor hack, ModeIndex!=0 Means this mode has been validated already and not just read from registry */
1074 specialMode.ModeIndex = 1;
1075 memcpy(&g_CustomVideoModes[VidPnTargetId], &specialMode, sizeof(VIDEO_MODE_INFORMATION));
1076
1077 /* Save mode to registry */
1078 VBoxMPRegSaveModeInfo(pExt, VidPnTargetId, &specialMode);
1079 }
1080
1081 /* Validate the mode which has been read from registry */
1082 if (!g_CustomVideoModes[VidPnTargetId].ModeIndex)
1083 {
1084 uint32_t xres, yres, bpp;
1085
1086 xres = g_CustomVideoModes[VidPnTargetId].VisScreenWidth;
1087 yres = g_CustomVideoModes[VidPnTargetId].VisScreenHeight;
1088 bpp = g_CustomVideoModes[VidPnTargetId].BitsPerPlane;
1089
1090 if (VBoxMPValidateVideoModeParams(pExt, VidPnTargetId, xres, yres, bpp))
1091 {
1092 VBoxFillVidModeInfo(&g_CustomVideoModes[VidPnTargetId], xres, yres, bpp, 1/*index*/, 0);
1093 Assert(g_CustomVideoModes[VidPnTargetId].ModeIndex == 1);
1094 }
1095 }
1096
1097 /* Add custom mode to the table */
1098 if (g_CustomVideoModes[VidPnTargetId].ModeIndex)
1099 {
1100 if (RT_ELEMENTS(pModes->aModes) > pModes->cModes)
1101 {
1102 g_CustomVideoModes[VidPnTargetId].ModeIndex = pModes->cModes;
1103 pModes->aModes[pModes->cModes] = g_CustomVideoModes[VidPnTargetId];
1104
1105 /* Check if we already have this mode in the table */
1106 int foundIdx;
1107 if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]))>=0)
1108 {
1109 pModes->iPreferredMode = foundIdx;
1110 }
1111 else
1112 {
1113 pModes->iPreferredMode = pModes->cModes;
1114 ++pModes->cModes;
1115 }
1116
1117 /* Add other bpp modes for this custom resolution */
1118#ifdef VBOX_WITH_8BPP_MODES
1119 UINT bpp=8;
1120#else
1121 UINT bpp=16;
1122#endif
1123 for (; bpp<=32; bpp+=8)
1124 {
1125 if (RT_ELEMENTS(pModes->aModes) == pModes->cModes)
1126 {
1127 WARN(("table full, can't add other bpp for specail mode!"));
1128#ifdef DEBUG_misha
1129 /* this is definitely something we do not expect */
1130 AssertFailed();
1131#endif
1132 break;
1133 }
1134
1135 AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
1136
1137 if (pModes->aModes[pModes->iPreferredMode].BitsPerPlane == bpp)
1138 continue;
1139
1140 if (!VBoxMPValidateVideoModeParamsGuest(pExt, VidPnTargetId,
1141 pModes->aModes[pModes->iPreferredMode].VisScreenWidth,
1142 pModes->aModes[pModes->iPreferredMode].VisScreenHeight,
1143 bpp))
1144 continue;
1145
1146 VBoxFillVidModeInfo(&pModes->aModes[pModes->cModes],
1147 pModes->aModes[pModes->iPreferredMode].VisScreenWidth,
1148 pModes->aModes[pModes->iPreferredMode].VisScreenHeight,
1149 bpp, pModes->cModes, 0);
1150 if (VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]) < 0)
1151 {
1152 ++pModes->cModes;
1153 }
1154 }
1155 }
1156 else
1157 {
1158 AssertRelease(RT_ELEMENTS(pModes->aModes) == pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
1159 WARN(("table full, can't add video mode for a host request!"));
1160#ifdef DEBUG_misha
1161 /* this is definitely something we do not expect */
1162 AssertFailed();
1163#endif
1164 }
1165 }
1166
1167 /* Check and Add additional modes passed in paAddlModes */
1168 for (UINT i=0; i<cAddlModes; ++i)
1169 {
1170 if (RT_ELEMENTS(pModes->aModes) == pModes->cModes)
1171 {
1172 WARN(("table full, can't add addl modes!"));
1173#ifdef DEBUG_misha
1174 /* this is definitely something we do not expect */
1175 AssertFailed();
1176#endif
1177 break;
1178 }
1179
1180 AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
1181
1182 if (!VBoxCommonFromDeviceExt(pExt)->fAnyX)
1183 {
1184 paAddlModes[i].VisScreenWidth &= 0xFFF8;
1185 }
1186
1187 if (VBoxLikesVideoMode(VidPnTargetId, paAddlModes[i].VisScreenWidth, paAddlModes[i].VisScreenHeight, paAddlModes[i].BitsPerPlane))
1188 {
1189 int foundIdx;
1190 if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &paAddlModes[i]))>=0)
1191 {
1192 pModes->iPreferredMode = foundIdx;
1193 }
1194 else
1195 {
1196 memcpy(&pModes->aModes[pModes->cModes], &paAddlModes[i], sizeof(VIDEO_MODE_INFORMATION));
1197 pModes->aModes[pModes->cModes].ModeIndex = pModes->cModes;
1198 ++pModes->cModes;
1199 }
1200 }
1201 }
1202
1203 /* Build resolution table */
1204 VBoxWddmBuildResolutionTableForModes(pModes);
1205}
1206
1207static void VBoxWddmInitVideoMode(PVBOXMP_DEVEXT pExt, int i)
1208{
1209 VBoxWddmBuildVideoModesInfo(pExt, (D3DDDI_VIDEO_PRESENT_TARGET_ID)i, &g_aVBoxVideoModeInfos[i], NULL, 0);
1210}
1211
1212void VBoxWddmInitVideoModes(PVBOXMP_DEVEXT pExt)
1213{
1214 for (int i = 0; i < VBoxCommonFromDeviceExt(pExt)->cDisplays; ++i)
1215 {
1216 VBoxWddmInitVideoMode(pExt, i);
1217 }
1218}
1219
1220static NTSTATUS vboxWddmChildStatusReportPerform(PVBOXMP_DEVEXT pDevExt, PVBOXVDMA_CHILD_STATUS pChildStatus, D3DDDI_VIDEO_PRESENT_TARGET_ID iChild)
1221{
1222 DXGK_CHILD_STATUS DdiChildStatus;
1223
1224 Assert(iChild < UINT32_MAX/2);
1225 Assert(iChild < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
1226
1227 PVBOXWDDM_TARGET pTarget = &pDevExt->aTargets[iChild];
1228
1229 if ((pChildStatus->fFlags & VBOXVDMA_CHILD_STATUS_F_DISCONNECTED)
1230 && pTarget->fConnected)
1231 {
1232 /* report disconnected */
1233 memset(&DdiChildStatus, 0, sizeof (DdiChildStatus));
1234 DdiChildStatus.Type = StatusConnection;
1235 DdiChildStatus.ChildUid = iChild;
1236 DdiChildStatus.HotPlug.Connected = FALSE;
1237
1238 LOG(("Reporting DISCONNECT to child %d", DdiChildStatus.ChildUid));
1239
1240 NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbIndicateChildStatus(pDevExt->u.primary.DxgkInterface.DeviceHandle, &DdiChildStatus);
1241 if (!NT_SUCCESS(Status))
1242 {
1243 WARN(("DxgkCbIndicateChildStatus failed with Status (0x%x)", Status));
1244 return Status;
1245 }
1246 pTarget->fConnected = FALSE;
1247 }
1248
1249 if ((pChildStatus->fFlags & VBOXVDMA_CHILD_STATUS_F_CONNECTED)
1250 && !pTarget->fConnected)
1251 {
1252 /* report disconnected */
1253 memset(&DdiChildStatus, 0, sizeof (DdiChildStatus));
1254 DdiChildStatus.Type = StatusConnection;
1255 DdiChildStatus.ChildUid = iChild;
1256 DdiChildStatus.HotPlug.Connected = TRUE;
1257
1258 LOG(("Reporting CONNECT to child %d", DdiChildStatus.ChildUid));
1259
1260 NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbIndicateChildStatus(pDevExt->u.primary.DxgkInterface.DeviceHandle, &DdiChildStatus);
1261 if (!NT_SUCCESS(Status))
1262 {
1263 WARN(("DxgkCbIndicateChildStatus failed with Status (0x%x)", Status));
1264 return Status;
1265 }
1266 pTarget->fConnected = TRUE;
1267 }
1268
1269 if (pChildStatus->fFlags & VBOXVDMA_CHILD_STATUS_F_ROTATED)
1270 {
1271 /* report disconnected */
1272 memset(&DdiChildStatus, 0, sizeof (DdiChildStatus));
1273 DdiChildStatus.Type = StatusRotation;
1274 DdiChildStatus.ChildUid = iChild;
1275 DdiChildStatus.Rotation.Angle = pChildStatus->u8RotationAngle;
1276
1277 LOG(("Reporting ROTATED to child %d", DdiChildStatus.ChildUid));
1278
1279 NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbIndicateChildStatus(pDevExt->u.primary.DxgkInterface.DeviceHandle, &DdiChildStatus);
1280 if (!NT_SUCCESS(Status))
1281 {
1282 WARN(("DxgkCbIndicateChildStatus failed with Status (0x%x)", Status));
1283 return Status;
1284 }
1285 }
1286
1287 return STATUS_SUCCESS;
1288}
1289
1290static NTSTATUS vboxWddmChildStatusHandleRequest(PVBOXMP_DEVEXT pDevExt, VBOXVDMACMD_CHILD_STATUS_IRQ *pBody)
1291{
1292 NTSTATUS Status = STATUS_SUCCESS;
1293
1294 for (UINT i = 0; i < pBody->cInfos; ++i)
1295 {
1296 PVBOXVDMA_CHILD_STATUS pInfo = &pBody->aInfos[i];
1297 if (pBody->fFlags & VBOXVDMACMD_CHILD_STATUS_IRQ_F_APPLY_TO_ALL)
1298 {
1299 for (D3DDDI_VIDEO_PRESENT_TARGET_ID iChild = 0; iChild < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++iChild)
1300 {
1301 Status = vboxWddmChildStatusReportPerform(pDevExt, pInfo, iChild);
1302 if (!NT_SUCCESS(Status))
1303 {
1304 WARN(("vboxWddmChildStatusReportPerform failed with Status (0x%x)", Status));
1305 break;
1306 }
1307 }
1308 }
1309 else
1310 {
1311 Status = vboxWddmChildStatusReportPerform(pDevExt, pInfo, pInfo->iChild);
1312 if (!NT_SUCCESS(Status))
1313 {
1314 WARN(("vboxWddmChildStatusReportPerform failed with Status (0x%x)", Status));
1315 break;
1316 }
1317 }
1318 }
1319
1320 return Status;
1321}
1322
1323#ifdef VBOX_WDDM_MONITOR_REPLUG_IRQ
1324typedef struct VBOXWDDMCHILDSTATUSCB
1325{
1326 PVBOXVDMACBUF_DR pDr;
1327 PKEVENT pEvent;
1328} VBOXWDDMCHILDSTATUSCB, *PVBOXWDDMCHILDSTATUSCB;
1329
1330static DECLCALLBACK(VOID) vboxWddmChildStatusReportCompletion(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, PVOID pvContext)
1331{
1332 /* we should be called from our DPC routine */
1333 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1334
1335 PVBOXWDDMCHILDSTATUSCB pCtx = (PVBOXWDDMCHILDSTATUSCB)pvContext;
1336 PVBOXVDMACBUF_DR pDr = pCtx->pDr;
1337 PVBOXVDMACMD pHdr = VBOXVDMACBUF_DR_TAIL(pDr, VBOXVDMACMD);
1338 VBOXVDMACMD_CHILD_STATUS_IRQ *pBody = VBOXVDMACMD_BODY(pHdr, VBOXVDMACMD_CHILD_STATUS_IRQ);
1339
1340 vboxWddmChildStatusHandleRequest(pDevExt, pBody);
1341
1342 vboxVdmaCBufDrFree(&pDevExt->u.primary.Vdma, pDr);
1343
1344 if (pCtx->pEvent)
1345 {
1346 KeSetEvent(pCtx->pEvent, 0, FALSE);
1347 }
1348}
1349#endif
1350
1351static NTSTATUS vboxWddmChildStatusReportReconnected(PVBOXMP_DEVEXT pDevExt, D3DDDI_VIDEO_PRESENT_TARGET_ID idTarget)
1352{
1353#ifdef VBOX_WDDM_MONITOR_REPLUG_IRQ
1354 NTSTATUS Status = STATUS_UNSUCCESSFUL;
1355 UINT cbCmd = VBOXVDMACMD_SIZE_FROMBODYSIZE(sizeof (VBOXVDMACMD_CHILD_STATUS_IRQ));
1356
1357 PVBOXVDMACBUF_DR pDr = vboxVdmaCBufDrCreate(&pDevExt->u.primary.Vdma, cbCmd);
1358 if (pDr)
1359 {
1360 // vboxVdmaCBufDrCreate zero initializes the pDr
1361 /* the command data follows the descriptor */
1362 pDr->fFlags = VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR;
1363 pDr->cbBuf = cbCmd;
1364 pDr->rc = VERR_NOT_IMPLEMENTED;
1365
1366 PVBOXVDMACMD pHdr = VBOXVDMACBUF_DR_TAIL(pDr, VBOXVDMACMD);
1367 pHdr->enmType = VBOXVDMACMD_TYPE_CHILD_STATUS_IRQ;
1368 pHdr->u32CmdSpecific = 0;
1369 PVBOXVDMACMD_CHILD_STATUS_IRQ pBody = VBOXVDMACMD_BODY(pHdr, VBOXVDMACMD_CHILD_STATUS_IRQ);
1370 pBody->cInfos = 1;
1371 if (idTarget == D3DDDI_ID_ALL)
1372 {
1373 pBody->fFlags |= VBOXVDMACMD_CHILD_STATUS_IRQ_F_APPLY_TO_ALL;
1374 }
1375 pBody->aInfos[0].iChild = idTarget;
1376 pBody->aInfos[0].fFlags = VBOXVDMA_CHILD_STATUS_F_DISCONNECTED | VBOXVDMA_CHILD_STATUS_F_CONNECTED;
1377 /* we're going to KeWaitForSingleObject */
1378 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
1379
1380 PVBOXVDMADDI_CMD pDdiCmd = VBOXVDMADDI_CMD_FROM_BUF_DR(pDr);
1381 VBOXWDDMCHILDSTATUSCB Ctx;
1382 KEVENT Event;
1383 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1384 Ctx.pDr = pDr;
1385 Ctx.pEvent = &Event;
1386 vboxVdmaDdiCmdInit(pDdiCmd, 0, 0, vboxWddmChildStatusReportCompletion, &Ctx);
1387 /* mark command as submitted & invisible for the dx runtime since dx did not originate it */
1388 vboxVdmaDdiCmdSubmittedNotDx(pDdiCmd);
1389 int rc = vboxVdmaCBufDrSubmit(pDevExt, &pDevExt->u.primary.Vdma, pDr);
1390 Assert(rc == VINF_SUCCESS);
1391 if (RT_SUCCESS(rc))
1392 {
1393 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1394 Assert(Status == STATUS_SUCCESS);
1395 return STATUS_SUCCESS;
1396 }
1397
1398 Status = STATUS_UNSUCCESSFUL;
1399
1400 vboxVdmaCBufDrFree(&pDevExt->u.primary.Vdma, pDr);
1401 }
1402 else
1403 {
1404 /* @todo: try flushing.. */
1405 WARN(("vboxVdmaCBufDrCreate returned NULL"));
1406 Status = STATUS_INSUFFICIENT_RESOURCES;
1407 }
1408
1409 return Status;
1410#else
1411 VBOXVDMACMD_CHILD_STATUS_IRQ Body = {0};
1412 Body.cInfos = 1;
1413 if (idTarget == D3DDDI_ID_ALL)
1414 {
1415 Body.fFlags |= VBOXVDMACMD_CHILD_STATUS_IRQ_F_APPLY_TO_ALL;
1416 }
1417 Body.aInfos[0].iChild = idTarget;
1418 Body.aInfos[0].fFlags = VBOXVDMA_CHILD_STATUS_F_DISCONNECTED | VBOXVDMA_CHILD_STATUS_F_CONNECTED;
1419 Assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
1420 return vboxWddmChildStatusHandleRequest(pDevExt, &Body);
1421#endif
1422}
1423
1424NTSTATUS vboxWddmChildStatusConnect(PVBOXMP_DEVEXT pDevExt, uint32_t iChild, BOOLEAN fConnect)
1425{
1426#ifdef VBOX_WDDM_MONITOR_REPLUG_IRQ
1427# error "port me!"
1428#else
1429 Assert(iChild < (uint32_t)VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
1430 NTSTATUS Status = STATUS_SUCCESS;
1431 VBOXVDMACMD_CHILD_STATUS_IRQ Body = {0};
1432 Body.cInfos = 1;
1433 Body.aInfos[0].iChild = iChild;
1434 Body.aInfos[0].fFlags = fConnect ? VBOXVDMA_CHILD_STATUS_F_CONNECTED : VBOXVDMA_CHILD_STATUS_F_DISCONNECTED;
1435 Assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
1436 Status = vboxWddmChildStatusHandleRequest(pDevExt, &Body);
1437 if (!NT_SUCCESS(Status))
1438 WARN(("vboxWddmChildStatusHandleRequest failed Status 0x%x", Status));
1439
1440 return Status;
1441#endif
1442}
1443
1444void VBoxWddmUpdateVideoMode(PVBOXMP_DEVEXT pExt, int i)
1445{
1446 g_VBoxVideoModeTmp = g_aVBoxVideoModeInfos[i];
1447
1448 Assert(g_aVBoxVideoModeInfos[i].cModes);
1449
1450 VBoxWddmInitVideoMode(pExt, i);
1451
1452 if (!memcmp(&g_VBoxVideoModeTmp, &g_aVBoxVideoModeInfos[i], sizeof (g_VBoxVideoModeTmp)))
1453 return;
1454
1455#ifdef DEBUG_misha
1456 LOGREL(("modes changed for target %d", i));
1457#else
1458 LOG(("modes changed for target %d", i));
1459#endif
1460 vboxWddmChildStatusReportReconnected(pExt, (D3DDDI_VIDEO_PRESENT_TARGET_ID)i);
1461}
1462
1463PVBOXWDDM_VIDEOMODES_INFO VBoxWddmUpdateVideoModesInfoByMask(PVBOXMP_DEVEXT pExt, uint8_t *pScreenIdMask)
1464{
1465 for (int i = 0; i < VBoxCommonFromDeviceExt(pExt)->cDisplays; ++i)
1466 {
1467 if (!pScreenIdMask || ASMBitTest(pScreenIdMask, i))
1468 {
1469 VBoxWddmUpdateVideoMode(pExt, i);
1470 }
1471 }
1472
1473 return g_aVBoxVideoModeInfos;
1474}
1475
1476void VBoxWddmAdjustMode(PVBOXMP_DEVEXT pExt, PVBOXWDDM_ADJUSTVIDEOMODE pMode)
1477{
1478 pMode->fFlags = 0;
1479
1480 if (pMode->Mode.Id >= (UINT)VBoxCommonFromDeviceExt(pExt)->cDisplays)
1481 {
1482 WARN(("invalid screen id (%d)", pMode->Mode.Id));
1483 pMode->fFlags = VBOXWDDM_ADJUSTVIDEOMODE_F_INVALISCREENID;
1484 return;
1485 }
1486
1487 /* @todo: this info should go from the target actually */
1488 PVBOXWDDM_SOURCE pSource = &pExt->aSources[pMode->Mode.Id];
1489
1490 UINT newWidth = pMode->Mode.Width;
1491 UINT newHeight = pMode->Mode.Height;
1492 UINT newBpp = pMode->Mode.BitsPerPixel;
1493
1494 if (!VBoxMPValidateVideoModeParams(pExt, pMode->Mode.Id, newWidth, newHeight, newBpp))
1495 {
1496 PVBOXWDDM_SOURCE pSource = &pExt->aSources[pMode->Mode.Id];
1497 pMode->fFlags |= VBOXWDDM_ADJUSTVIDEOMODE_F_UNSUPPORTED;
1498 }
1499
1500 if (pMode->Mode.Width != newWidth
1501 || pMode->Mode.Height != newHeight
1502 || pMode->Mode.BitsPerPixel != newBpp)
1503 {
1504 pMode->fFlags |= VBOXWDDM_ADJUSTVIDEOMODE_F_ADJUSTED;
1505 pMode->Mode.Width = newWidth;
1506 pMode->Mode.Height = newHeight;
1507 pMode->Mode.BitsPerPixel = newBpp;
1508 }
1509
1510 if (pSource->cTargets /* <- active */
1511 && pSource->AllocData.SurfDesc.width == pMode->Mode.Width
1512 && pSource->AllocData.SurfDesc.height == pMode->Mode.Height
1513 && pSource->AllocData.SurfDesc.bpp == pMode->Mode.BitsPerPixel)
1514 {
1515 pMode->fFlags |= VBOXWDDM_ADJUSTVIDEOMODE_F_CURRENT;
1516 if (pMode->fFlags & VBOXWDDM_ADJUSTVIDEOMODE_F_UNSUPPORTED)
1517 {
1518 WARN(("current mode is reported as unsupported"));
1519 }
1520 }
1521}
1522
1523void VBoxWddmAdjustModes(PVBOXMP_DEVEXT pExt, uint32_t cModes, PVBOXWDDM_ADJUSTVIDEOMODE aModes)
1524{
1525 for (UINT i = 0; i < cModes; ++i)
1526 {
1527 PVBOXWDDM_ADJUSTVIDEOMODE pMode = &aModes[i];
1528 VBoxWddmAdjustMode(pExt, pMode);
1529 }
1530}
1531
1532NTSTATUS VBoxWddmGetModesForResolution(VIDEO_MODE_INFORMATION *pAllModes, uint32_t cAllModes, int iSearchPreferredMode,
1533 const D3DKMDT_2DREGION *pResolution, VIDEO_MODE_INFORMATION * pModes, uint32_t cModes, uint32_t *pcModes, int32_t *piPreferrableMode)
1534{
1535 NTSTATUS Status = STATUS_SUCCESS;
1536 uint32_t cFound = 0;
1537 int iFoundPreferrableMode = -1;
1538 for (uint32_t i = 0; i < cAllModes; ++i)
1539 {
1540 VIDEO_MODE_INFORMATION *pCur = &pAllModes[i];
1541 if (pResolution->cx == pCur->VisScreenWidth
1542 && pResolution->cy == pCur->VisScreenHeight)
1543 {
1544 if (pModes && cModes > cFound)
1545 memcpy(&pModes[cFound], pCur, sizeof(VIDEO_MODE_INFORMATION));
1546 else
1547 Status = STATUS_BUFFER_TOO_SMALL;
1548
1549 if (i == iSearchPreferredMode)
1550 iFoundPreferrableMode = cFound;
1551
1552 ++cFound;
1553 }
1554 }
1555
1556 Assert(iFoundPreferrableMode < 0 || cFound > (uint32_t)iFoundPreferrableMode);
1557
1558 *pcModes = cFound;
1559 if (piPreferrableMode)
1560 *piPreferrableMode = iFoundPreferrableMode;
1561
1562 return Status;
1563}
1564
1565PVBOXWDDM_VIDEOMODES_INFO VBoxWddmGetAllVideoModesInfos(PVBOXMP_DEVEXT pExt)
1566{
1567 return g_aVBoxVideoModeInfos;
1568}
1569
1570PVBOXWDDM_VIDEOMODES_INFO VBoxWddmGetVideoModesInfo(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId)
1571{
1572 return &VBoxWddmGetAllVideoModesInfos(pExt)[VidPnTargetId];
1573}
1574
1575#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