xvfb: Add multiple CRTC support

Multiple CRTCs can be added on a per-screen basis with the new -crtcs
option. Each CRTC has one associated output. Outputs beyond the first
are disabled by default and can be enabled by setting a mode. Outputs
can be disabled again by setting the associated CRTC's mode and output
to None.

Signed-off-by: Andy Myers <andy.myers@zetier.com>
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1749>
This commit is contained in:
Andy Myers 2024-12-06 15:16:50 -05:00 committed by Marge Bot
parent b61647f3a1
commit 7933cc24d0

View File

@ -81,8 +81,17 @@ from The Open Group.
#define VFB_DEFAULT_WHITEPIXEL 1 #define VFB_DEFAULT_WHITEPIXEL 1
#define VFB_DEFAULT_BLACKPIXEL 0 #define VFB_DEFAULT_BLACKPIXEL 0
#define VFB_DEFAULT_LINEBIAS 0 #define VFB_DEFAULT_LINEBIAS 0
#define VFB_DEFAULT_NUM_CRTCS 1
#define XWD_WINDOW_NAME_LEN 60 #define XWD_WINDOW_NAME_LEN 60
typedef struct {
int width;
int height;
int x;
int y;
int numOutputs;
} vfbCrtcInfo, *vfbCrtcInfoPtr;
typedef struct { typedef struct {
int width; int width;
int paddedBytesWidth; int paddedBytesWidth;
@ -92,6 +101,8 @@ typedef struct {
int bitsPerPixel; int bitsPerPixel;
int sizeInBytes; int sizeInBytes;
int ncolors; int ncolors;
int numCrtcs;
vfbCrtcInfoPtr crtcs;
char *pfbMemory; char *pfbMemory;
XWDColor *pXWDCmap; XWDColor *pXWDCmap;
XWDFileHeader *pXWDHeader; XWDFileHeader *pXWDHeader;
@ -140,6 +151,43 @@ static Bool Render = TRUE;
if (needswap) { CARD32 _s = _src; cpswapl(_s, _dst); } \ if (needswap) { CARD32 _s = _src; cpswapl(_s, _dst); } \
else _dst = _src; else _dst = _src;
static void
vfbAddCrtcInfo(vfbScreenInfoPtr screen, int numCrtcs)
{
int i;
int count = numCrtcs - screen->numCrtcs;
if (count > 0) {
vfbCrtcInfoPtr crtcs =
reallocarray(screen->crtcs, numCrtcs, sizeof(*crtcs));
if (!crtcs)
FatalError("Not enough memory for %d CRTCs", numCrtcs);
memset(crtcs + screen->numCrtcs, 0, count * sizeof(*crtcs));
for (i = screen->numCrtcs; i < numCrtcs; ++i) {
crtcs[i].width = screen->width;
crtcs[i].height = screen->height;
}
screen->crtcs = crtcs;
screen->numCrtcs = numCrtcs;
}
}
static vfbScreenInfoPtr
vfbInitializeScreenInfo(vfbScreenInfoPtr screen)
{
*screen = defaultScreenInfo;
vfbAddCrtcInfo(screen, VFB_DEFAULT_NUM_CRTCS);
/* First CRTC initializes with one output */
if (screen->numCrtcs > 0)
screen->crtcs[0].numOutputs = 1;
return screen;
}
static void static void
vfbInitializePixmapDepths(void) vfbInitializePixmapDepths(void)
{ {
@ -196,6 +244,8 @@ freeScreenInfo(vfbScreenInfoPtr pvfb)
free(pvfb->pXWDHeader); free(pvfb->pXWDHeader);
break; break;
} }
free(pvfb->crtcs);
} }
void void
@ -262,6 +312,9 @@ ddxUseMsg(void)
#ifdef MITSHM #ifdef MITSHM
ErrorF("-shmem put framebuffers in shared memory\n"); ErrorF("-shmem put framebuffers in shared memory\n");
#endif #endif
ErrorF("-crtcs n number of CRTCs per screen (default: %d)\n",
VFB_DEFAULT_NUM_CRTCS);
} }
int int
@ -277,7 +330,7 @@ ddxProcessArgument(int argc, char *argv[], int i)
} }
if (lastScreen == -1) if (lastScreen == -1)
currentScreen = &defaultScreenInfo; currentScreen = vfbInitializeScreenInfo(&defaultScreenInfo);
else else
currentScreen = &vfbScreens[lastScreen]; currentScreen = &vfbScreens[lastScreen];
@ -301,7 +354,7 @@ ddxProcessArgument(int argc, char *argv[], int i)
if (!vfbScreens) if (!vfbScreens)
FatalError("Not enough memory for screen %d\n", screenNum); FatalError("Not enough memory for screen %d\n", screenNum);
for (; vfbNumScreens <= screenNum; ++vfbNumScreens) for (; vfbNumScreens <= screenNum; ++vfbNumScreens)
vfbScreens[vfbNumScreens] = defaultScreenInfo; vfbInitializeScreenInfo(&vfbScreens[vfbNumScreens]);
} }
if (3 != sscanf(argv[i + 2], "%dx%dx%d", if (3 != sscanf(argv[i + 2], "%dx%dx%d",
@ -382,6 +435,24 @@ ddxProcessArgument(int argc, char *argv[], int i)
} }
#endif #endif
if (strcmp(argv[i], "-crtcs") == 0) { /* -crtcs n */
int numCrtcs;
CHECK_FOR_REQUIRED_ARGUMENTS(1);
numCrtcs = atoi(argv[i + 1]);
if (numCrtcs < 1) {
ErrorF("Invalid number of CRTCs %d\n", numCrtcs);
UseMsg();
FatalError("Invalid number of CRTCs (%d) passed to -crtcs\n",
numCrtcs);
}
vfbAddCrtcInfo(currentScreen, numCrtcs);
return 2;
}
return 0; return 0;
} }
@ -785,10 +856,22 @@ vfbRRCrtcSet(ScreenPtr pScreen,
int x, int x,
int y, int y,
Rotation rotation, Rotation rotation,
int numOutput, int numOutputs,
RROutputPtr *outputs) RROutputPtr *outputs)
{ {
return RRCrtcNotify(crtc, mode, x, y, rotation, NULL, numOutput, outputs); vfbCrtcInfoPtr pvci = crtc->devPrivate;
if (pvci) {
if (mode) {
pvci->width = mode->mode.width;
pvci->height = mode->mode.height;
}
pvci->x = x;
pvci->y = y;
pvci->numOutputs = numOutputs;
}
return RRCrtcNotify(crtc, mode, x, y, rotation, NULL, numOutputs, outputs);
} }
static Bool static Bool
@ -804,17 +887,20 @@ static Bool
vfbRandRInit(ScreenPtr pScreen) vfbRandRInit(ScreenPtr pScreen)
{ {
rrScrPrivPtr pScrPriv; rrScrPrivPtr pScrPriv;
#if RANDR_12_INTERFACE #if RANDR_12_INTERFACE
RRModePtr mode; RRModePtr mode;
RRCrtcPtr crtc; RRCrtcPtr crtc;
RROutputPtr output; RROutputPtr output;
xRRModeInfo modeInfo; xRRModeInfo modeInfo;
char name[64]; char name[64];
int i;
vfbScreenInfoPtr pvfb = &vfbScreens[pScreen->myNum];
#endif #endif
int mmWidth, mmHeight; int mmWidth, mmHeight;
if (!RRScreenInit (pScreen)) if (!RRScreenInit(pScreen))
return FALSE; return FALSE;
pScrPriv = rrGetScrPriv(pScreen); pScrPriv = rrGetScrPriv(pScreen);
pScrPriv->rrGetInfo = vfbRRGetInfo; pScrPriv->rrGetInfo = vfbRRGetInfo;
#if RANDR_12_INTERFACE #if RANDR_12_INTERFACE
@ -827,44 +913,53 @@ vfbRandRInit(ScreenPtr pScreen)
pScrPriv->rrOutputValidateMode = vfbRROutputValidateMode; pScrPriv->rrOutputValidateMode = vfbRROutputValidateMode;
pScrPriv->rrModeDestroy = NULL; pScrPriv->rrModeDestroy = NULL;
mmWidth = pScreen->width * 25.4 / monitorResolution; RRScreenSetSizeRange(pScreen, 1, 1, pScreen->width, pScreen->height);
mmHeight = pScreen->height * 25.4 / monitorResolution;
RRScreenSetSizeRange (pScreen, for (i = 0; i < pvfb->numCrtcs; i++) {
1, 1, vfbCrtcInfoPtr pvci = &pvfb->crtcs[i];
pScreen->width, pScreen->height);
sprintf (name, "%dx%d", pScreen->width, pScreen->height); mmWidth = pvci->width * 25.4 / monitorResolution;
memset (&modeInfo, '\0', sizeof (modeInfo)); mmHeight = pvci->height * 25.4 / monitorResolution;
modeInfo.width = pScreen->width;
modeInfo.height = pScreen->height;
modeInfo.nameLength = strlen (name);
mode = RRModeGet (&modeInfo, name); crtc = RRCrtcCreate(pScreen, pvci);
if (!mode) if (!crtc)
return FALSE; return FALSE;
crtc = RRCrtcCreate (pScreen, NULL); /* Set gamma to avoid xrandr complaints */
if (!crtc) RRCrtcGammaSetSize(crtc, 256);
return FALSE;
/* This is to avoid xrandr to complain about the gamma missing */ /* Setup an Output for each CRTC: 'screen' for the first, then 'screen_N' */
RRCrtcGammaSetSize (crtc, 256); snprintf(name, sizeof(name), i == 0 ? "screen" : "screen_%d", i);
output = RROutputCreate(pScreen, name, strlen(name), NULL);
if (!output)
return FALSE;
if (!RROutputSetClones(output, NULL, 0))
return FALSE;
if (!RROutputSetCrtcs(output, &crtc, 1))
return FALSE;
if (!RROutputSetConnection(output, RR_Connected))
return FALSE;
if (!RROutputSetPhysicalSize(output, mmWidth, mmHeight))
return FALSE;
output = RROutputCreate (pScreen, "screen", 6, NULL); /* Setup a Mode and notify only for CRTCs with Outputs */
if (!output) if (pvci->numOutputs > 0) {
return FALSE; snprintf(name, sizeof(name), "%dx%d", pvci->width, pvci->height);
if (!RROutputSetClones (output, NULL, 0)) memset(&modeInfo, '\0', sizeof(modeInfo));
return FALSE; modeInfo.width = pvci->width;
if (!RROutputSetModes (output, &mode, 1, 0)) modeInfo.height = pvci->height;
return FALSE; modeInfo.nameLength = strlen(name);
if (!RROutputSetCrtcs (output, &crtc, 1))
return FALSE; mode = RRModeGet(&modeInfo, name);
if (!RROutputSetConnection (output, RR_Connected)) if (!mode)
return FALSE; return FALSE;
if (!RROutputSetPhysicalSize (output, mmWidth, mmHeight)) if (!RROutputSetModes(output, &mode, 1, 0))
return FALSE; return FALSE;
RRCrtcNotify (crtc, mode, 0, 0, RR_Rotate_0, NULL, 1, &output); if (!RRCrtcNotify(crtc, mode, pvci->x, pvci->y, RR_Rotate_0, NULL,
1, &output))
return FALSE;
}
}
#endif #endif
return TRUE; return TRUE;
} }