VBoxMPVidModes.cpp revision 340ee06f35257fee1bd68223ab3504cf2b1d0c3e
/* $Id$ */
/** @file
* VBox Miniport video modes related functions
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "VBoxMPCommon.h"
#define _INC_SWPRINTF_INL_
#endif
#include <wchar.h>
#ifdef VBOX_WITH_WDDM
# define VBOX_WITHOUT_24BPP_MODES
#endif
/* Custom video modes which are being read from registry at driver startup. */
/* Standart video modes list.
* Additional space is reserved for custom video modes for 64 guest monitors.
* The custom video mode index is alternating and 2 indexes are reserved for the last custom mode.
*/
/* Number of available video modes, set by VBoxMPCmnBuildVideoModesTable. */
static uint32_t g_NumVideoModes = 0;
static BOOLEAN
VBoxMPValidateVideoModeParamsGuest(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, uint32_t xres, uint32_t yres, uint32_t bpp)
{
switch (bpp)
{
case 32:
break;
case 24:
#ifdef VBOX_WITHOUT_24BPP_MODES
return FALSE;
#else
break;
#endif
case 16:
break;
case 8:
#ifndef VBOX_WITH_8BPP_MODES
return FALSE;
#else
break;
#endif
default:
return FALSE;
}
return TRUE;
}
/* Fills given video mode BPP related fields */
static void
{
}
/* Fills given video mode structure */
static void
VBoxFillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset)
{
/*Common entries*/
/*BPP related entries*/
switch (bpp)
{
#ifdef VBOX_WITH_8BPP_MODES
case 8:
break;
#endif
case 16:
break;
case 24:
case 32:
break;
default:
Assert(0);
break;
}
}
{
int iMode;
LOGF_ENTER();
/* Initialize all custom modes to the 800x600x32 */
{
}
/* Read stored custom resolution info from registry */
{
if (iMode==0)
{
/*First name without a suffix*/
}
else
{
wchar_t keyname[32];
}
{
if (CustomXRes == 0)
{
}
if (CustomYRes == 0)
{
}
if (CustomBPP == 0)
{
}
{
}
}
}
LOGF_LEAVE();
}
{
}
{
}
static bool VBoxMPVideoModesMatch(const PVIDEO_MODE_INFORMATION pMode1, const PVIDEO_MODE_INFORMATION pMode2)
{
}
static int
VBoxMPFindVideoMode(const PVIDEO_MODE_INFORMATION pModesTable, int cModes, const PVIDEO_MODE_INFORMATION pMode)
{
for (int i = 0; i < cModes; ++i)
{
{
return i;
}
}
return -1;
}
/* Helper function to dynamically build our table of standard video
* modes. We take the amount of VRAM and create modes with standard
* geometries until we've either reached the maximum number of modes
* or the available VRAM does not allow for additional modes.
* We also check registry for manually added video modes.
* Returns number of modes added to the table.
*/
static uint32_t
VBoxMPFillModesTable(PVBOXMP_DEVEXT pExt, int iDisplay, PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize,
{
/* the resolution matrix */
struct
{
} resolutionMatrix[] =
{
/* standard modes */
{ 640, 480 },
{ 800, 600 },
{ 1024, 768 },
{ 1152, 864 },
{ 1280, 960 },
{ 1280, 1024 },
{ 1400, 1050 },
{ 1600, 1200 },
{ 1920, 1440 },
#ifndef VBOX_WITH_WDDM
/* multi screen modes with 1280x1024 */
{ 2560, 1024 },
{ 3840, 1024 },
{ 5120, 1024 },
/* multi screen modes with 1600x1200 */
{ 3200, 1200 },
{ 4800, 1200 },
{ 6400, 1200 },
#endif
};
#ifdef VBOX_XPDM_MINIPORT
#else
/* at least two surfaces will be needed: primary & shadow */
#endif
/* there are 4 color depths: 8, 16, 24 and 32bpp and we reserve 50% of the modes for other sources */
/* Always add 800x600 video modes. Windows XP+ needs at least 800x600 resolution
* and fallbacks to 800x600x4bpp VGA mode if the driver did not report suitable modes.
* This resolution could be rejected by a low resolution host (netbooks, etc).
*/
#ifdef VBOX_WITH_8BPP_MODES
int bytesPerPixel=1;
#else
int bytesPerPixel=2;
#endif
{
{
/* we don't have enough VRAM for this mode */
continue;
}
continue;
if (32==bitsPerPixel)
{
}
++iMode;
}
/* Query yoffset from the host */
/* Iterate through our static resolution table and add supported video modes for different bpp's */
#ifdef VBOX_WITH_8BPP_MODES
#else
#endif
{
for (cAdded=0, resIndex=0; resIndex<RT_ELEMENTS(resolutionMatrix) && cAdded<maxModesPerColorDepth; resIndex++)
{
if (resolutionMatrix[resIndex].xRes * resolutionMatrix[resIndex].yRes * bytesPerPixel > (LONG)vramSize)
{
/* we don't have enough VRAM for this mode */
continue;
}
if (yOffset == 0 && resolutionMatrix[resIndex].xRes == 800 && resolutionMatrix[resIndex].yRes == 600)
{
/* this mode was already added */
continue;
}
if (!VBoxLikesVideoMode(iDisplay, resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes - yOffset, bitsPerPixel))
{
/* host doesn't like this mode */
continue;
}
if (!VBoxMPValidateVideoModeParamsGuest(pExt, iDisplay, resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes, bitsPerPixel))
{
/* guest does not like this mode */
continue;
}
/* Sanity check, we shouldn't ever get here */
{
WARN(("video modes table overflow!"));
break;
}
VBoxFillVidModeInfo(&pModesTable[iMode], resolutionMatrix[resIndex].xRes, resolutionMatrix[resIndex].yRes, bitsPerPixel, iMode+1, yOffset);
++iMode;
++cAdded;
}
}
/* Check registry for manually added modes, up to 128 entries is supported
* Give up on the first error encountered.
*/
int fPrefSet=0;
{
{
WARN(("ignoring possible custom mode(s), table is full!"));
break;
}
wchar_t keyname[24];
/* round down width to be a multiple of 8 if necessary */
{
xres &= 0xFFF8;
}
|| ( (bpp != 16)
&& (bpp != 24)
&& (bpp != 32)))
{
/* incorrect values */
break;
}
/* does it fit within our VRAM? */
{
/* we don't have enough VRAM for this mode */
break;
}
{
/* host doesn't like this mode */
break;
}
{
/* guest does not like this mode */
continue;
}
LOG(("adding video mode from registry."));
if (!fPrefSet)
{
fPrefSet = 1;
}
#ifdef VBOX_WDDM_MINIPORT
/*check if the same mode has been added to the table already*/
if (foundIdx>=0)
{
{
}
}
else
#endif
{
++iMode;
}
}
if (pPrefModeIdx)
{
*pPrefModeIdx = iPrefIdx;
}
return iMode;
}
/* Returns if we're in the first mode change, ie doesn't have valid video mode set yet */
{
#ifdef VBOX_XPDM_MINIPORT
return (pExt->CurrentMode == 0);
#else
#endif
}
/* Updates missing video mode params with current values,
* Checks if resulting mode is liked by the host and fits into VRAM.
* Returns TRUE if resulting mode could be used.
*/
static BOOLEAN
VBoxMPValidateVideoModeParams(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, uint32_t &xres, uint32_t &yres, uint32_t &bpp)
{
/* Make sure all important video mode values are set */
{
/* Use stored custom values only if nothing was read from host. */
}
else
{
/* Use current values for field which weren't read from host. */
#ifdef VBOX_XPDM_MINIPORT
#else
#endif
}
/* Round down width to be a multiple of 8 if necessary */
{
xres &= 0xFFF8;
}
/* We always need bpp to be set */
if (!bpp)
{
bpp=32;
}
return FALSE;
/* Check if host likes this mode */
{
return FALSE;
}
#ifdef VBOX_XPDM_MINIPORT
#else
/* at least two surfaces will be needed: primary & shadow */
#endif
/* Check that values are valid and mode fits into VRAM */
|| !((bpp == 16)
#ifdef VBOX_WITH_8BPP_MODES
|| (bpp == 8)
#endif
|| (bpp == 24)
|| (bpp == 32)))
{
return FALSE;
}
{
/* Store values of last reported release log message to avoid log flooding. */
LOG(("not enough VRAM for video mode %dx%dx%dbpp. Available: %d bytes. Required: more than %d bytes.",
s_xresNoVRAM = xres;
s_yresNoVRAM = yres;
s_bppNoVRAM = bpp;
return FALSE;
}
return TRUE;
}
/* Checks if there's a pending video mode change hint,
* and fills pPendingMode with associated info.
* returns TRUE if there's a pending change. Otherwise returns FALSE.
*/
static BOOLEAN
{
/* Check if there's a pending display change request for this display */
{
{
/*display = RT_ELEMENTS(g_CustomVideoModes) - 1;*/
return FALSE;
}
}
else
{
return FALSE;
}
/* Correct video mode params and check if host likes it */
{
return TRUE;
}
return FALSE;
}
/* Save custom mode info to registry */
static void VBoxMPRegSaveModeInfo(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, PVIDEO_MODE_INFORMATION pMode)
{
if (iDisplay==0)
{
/*First name without a suffix*/
}
else
{
wchar_t keyname[32];
}
}
#ifdef VBOX_XPDM_MINIPORT
{
}
{
return g_NumVideoModes;
}
/* Makes a table of video modes consisting of:
* Default modes
* Custom modes manually added to registry
* Custom modes for all displays (either from a display change hint or stored in registry)
* 2 special modes, for a pending display change for this adapter. See comments below.
*/
{
/* Fill table with standart modes and ones manually added to registry */
cStandartModes = VBoxMPFillModesTable(pExt, pExt->iDevice, g_VideoModes, RT_ELEMENTS(g_VideoModes), NULL);
/* Add custom modes for all displays to the table */
for (uint32_t i=0; i<cCustomModes; i++)
{
}
/* Check if host wants us to switch video mode and it's for this adapter */
/* Check the startup case */
{
/* Check if we could make valid mode from values stored to registry */
{
bHaveSpecial = TRUE;
}
}
/* Update number of modes */
if (!bHaveSpecial)
{
/* Just add 2 dummy modes to maintain table size. */
}
else
{
/* We need to alternate mode index entry for a pending mode change,
* else windows will ignore actual mode change call.
* Only alternate index if one of mode parameters changed and
* regardless of conditions always add 2 entries to the table.
*/
static int s_InvocationCounter=0;
static uint32_t s_Prev_xres=0;
static uint32_t s_Prev_yres=0;
static uint32_t s_Prev_bpp=0;
if (bChanged)
{
}
/* Make sure there's no other mode in the table with same parameters,
* because we need windows to pick up a new video mode index otherwise
* actual mode change wouldn't happen.
*/
int iFoundIdx;
while (0 <= (iFoundIdx = VBoxMPFindVideoMode(&g_VideoModes[uiStart], g_NumVideoModes-uiStart, &specialMode)))
{
}
/* Check if we need to alternate the index */
{
if (bChanged)
{
}
if (s_InvocationCounter % 2)
{
}
}
LOG(("add special mode[%d] %dx%d:%d for display %d (bChanged=%d, bAlretnativeIndex=%d)",
/* Add special mode to the table
* Note: Y offset isn't used for a host-supplied modes
*/
/* Save special mode in the custom modes table */
/* Make sure we've added 2nd mode if necessary to maintain table size */
{
memcpy(&g_VideoModes[g_NumVideoModes], &g_VideoModes[g_NumVideoModes-1], sizeof(VIDEO_MODE_INFORMATION));
}
else if (!bAlternativeIndex)
{
}
/* Save special mode info to registry */
}
#if defined(LOG_ENABLED)
do
{
for (uint32_t i=0; i<g_NumVideoModes; ++i)
{
LOG(("Mode[%2d]: %4dx%4d:%2d (idx=%d)",
}
} while (0);
#endif
}
#endif /*VBOX_XPDM_MINIPORT*/
#ifdef VBOX_WDDM_MINIPORT
bool VBoxWddmFillMode(PVBOXMP_DEVEXT pExt, uint32_t iDisplay, VIDEO_MODE_INFORMATION *pInfo, D3DDDIFORMAT enmFormat, ULONG w, ULONG h)
{
switch (enmFormat)
{
case D3DDDIFMT_A8R8G8B8:
{
return false;
}
return true;
case D3DDDIFMT_R8G8B8:
{
return false;
}
return true;
case D3DDDIFMT_R5G6B5:
{
return false;
}
return true;
case D3DDDIFMT_P8:
{
return false;
}
return true;
default:
break;
}
return false;
}
static void
VBoxWddmBuildResolutionTable(PVIDEO_MODE_INFORMATION pModesTable, size_t tableSize, int iPreferredMode,
{
uint32_t cResolutions = 0;
*piPreferredResolution = -1;
{
int iResolution = -1;
for (uint32_t j=0; j<cResolutions; ++j)
{
{
iResolution = j;
break;
}
}
if (iResolution < 0)
{
if (cResolutions == cResolutionsArray)
{
WARN(("table overflow!"));
break;
}
++cResolutions;
}
Assert(iResolution >= 0);
if (i == iPreferredMode)
{
}
}
Assert(*piPreferredResolution >= 0);
}
{
Assert(pModes->aResolutions[pModes->iPreferredResolution].cx == pModes->aModes[pModes->iPreferredMode].VisScreenWidth
&& pModes->aResolutions[pModes->iPreferredResolution].cy == pModes->aModes[pModes->iPreferredMode].VisScreenHeight);
}
static void
{
/* Add default modes and ones read from registry. */
pModes->cModes = VBoxMPFillModesTable(pExt, VidPnTargetId, pModes->aModes, RT_ELEMENTS(pModes->aModes), &pModes->iPreferredMode);
/* Check if there's a pending display change request for this adapter */
{
/*Minor hack, ModeIndex!=0 Means this mode has been validated already and not just read from registry */
/* Save mode to registry */
}
/* Validate the mode which has been read from registry */
{
{
}
}
/* Add custom mode to the table */
{
{
/* Check if we already have this mode in the table */
int foundIdx;
if ((foundIdx=VBoxMPFindVideoMode(pModes->aModes, pModes->cModes, &pModes->aModes[pModes->cModes]))>=0)
{
}
else
{
}
/* Add other bpp modes for this custom resolution */
#ifdef VBOX_WITH_8BPP_MODES
#else
#endif
{
{
WARN(("table full, can't add other bpp for specail mode!"));
#ifdef DEBUG_misha
/* this is definitely something we do not expect */
AssertFailed();
#endif
break;
}
AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
continue;
bpp))
continue;
{
}
}
}
else
{
AssertRelease(RT_ELEMENTS(pModes->aModes) == pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
WARN(("table full, can't add video mode for a host request!"));
#ifdef DEBUG_misha
/* this is definitely something we do not expect */
AssertFailed();
#endif
}
}
/* Check and Add additional modes passed in paAddlModes */
for (UINT i=0; i<cAddlModes; ++i)
{
{
WARN(("table full, can't add addl modes!"));
#ifdef DEBUG_misha
/* this is definitely something we do not expect */
AssertFailed();
#endif
break;
}
AssertRelease(RT_ELEMENTS(pModes->aModes) > pModes->cModes); /* if not - the driver state is screwed up, @todo: better do KeBugCheckEx here */
{
}
if (VBoxLikesVideoMode(VidPnTargetId, paAddlModes[i].VisScreenWidth, paAddlModes[i].VisScreenHeight, paAddlModes[i].BitsPerPlane))
{
int foundIdx;
{
}
else
{
}
}
}
/* Build resolution table */
}
{
{
g_aVBoxVideoModeInfos[i].cModes = 0;
}
}
PVBOXWDDM_VIDEOMODES_INFO VBoxWddmUpdateVideoModesInfo(PVBOXMP_DEVEXT pExt, PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo)
{
if (pVidPnInfo)
{
{
{
VIDEO_MODE_INFORMATION ModeInfo = {0};
switch (pScreenInfo->BitsPerPixel)
{
case 32:
break;
case 24:
break;
case 16:
break;
case 8:
break;
default:
Assert(0);
break;
}
if (enmFormat != D3DDDIFMT_UNKNOWN)
{
if (VBoxWddmFillMode(pExt, pScreenInfo->Id, &ModeInfo, enmFormat, pScreenInfo->Width, pScreenInfo->Height))
{
}
else
{
Assert(0);
}
}
}
}
}
/* ensure we have all the rest populated */
return g_aVBoxVideoModeInfos;
}
NTSTATUS VBoxWddmGetModesForResolution(VIDEO_MODE_INFORMATION *pAllModes, uint32_t cAllModes, int iSearchPreferredMode,
const D3DKMDT_2DREGION *pResolution, VIDEO_MODE_INFORMATION * pModes, uint32_t cModes, uint32_t *pcModes, int32_t *piPreferrableMode)
{
int iFoundPreferrableMode = -1;
{
{
else
if (i == iSearchPreferredMode)
++cFound;
}
}
if (piPreferrableMode)
return Status;
}
static PVBOXWDDM_VIDEOMODES_INFO vboxWddmGetVideoModesInfoInternal(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId)
{
{
return NULL;
}
{
}
return pInfo;
}
static VOID vboxWddmAddVideoModes(PVBOXMP_DEVEXT pExt, PVBOXWDDM_VIDEOMODES_INFO pDstInfo, PVBOXWDDM_VIDEOMODES_INFO pSrcInfo)
{
{
if (foundIdx >= 0)
continue;
Assert(0);
}
}
{
/* ensure all modes are initialized */
{
}
return g_aVBoxVideoModeInfos;
}
PVBOXWDDM_VIDEOMODES_INFO VBoxWddmGetVideoModesInfo(PVBOXMP_DEVEXT pExt, D3DDDI_VIDEO_PRESENT_TARGET_ID VidPnTargetId)
{
}
#endif /*VBOX_WDDM_MINIPORT*/