gfx: the chosen resolution reaches every head, and the greeting stops racing
Two things.
**The F8 resolution now applies to the other displays as well.** It already
reached them once vgpud started -- that was the last commit but one -- but not
before: choosing 1024x768 changed the screen it was chosen on and left the
other at 1280x800, so the menu and then the splash appeared on it as a small
rectangle in the middle of a larger black one. Which is a fair reading of "the
resolution change is not changing resolution".
The resolution offered in that menu is a statement about the machine's display,
not about one adapter of it, so both stages before vgpud now follow it:
the loader mirrors_match() puts every mirror GOP into the primary's mode,
called from mirrors_show() so it cannot be forgotten at a new
call site. Two integer comparisons in the common case.
the kernel setup_scanout() takes gfx's dimensions over GET_DISPLAY_INFO's.
Same argument as vgpud's bring_up(): the host window's size is
a fact about QEMU, and the console's mode is the one somebody
chose.
**And the greeting is no longer a race.** "Welcome to SchOS!" was printed after
every job had been started, including the shell on the console -- so which of
the two reached the screen first was up to the scheduler, and it went both ways
about evenly. A boot that sometimes greets you above the prompt and sometimes
below it does not read as two orderings of one boot; it reads as a line printed
by mistake.
schinitd now holds back the job that owns the console -- `own_tty`, which is
exactly the job whose output lands there -- until after the greeting is
written. That is enough to settle it: this process writes the line, and only
then asks PID 1 to spawn the shell, so the fork has not happened yet. Nothing
waits on it that was not already waiting.
Verified: F8 -> Resolution -> 1024x768 -> boot, with the virtio head
screendumped over QMP at three points during startup and once at the prompt --
1024x768 throughout, and byte-identical to the stdvga head. Three consecutive
boots put the greeting at the same offset, ahead of the prompt every time.
Both supported configurations: one boot each, no traps, no panics, socktest and
memfdtest green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
182cc76588
commit
9ab8a7a426
4 changed files with 95 additions and 1 deletions
|
|
@ -1045,6 +1045,45 @@ mirrors_find(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
|
|||
}
|
||||
}
|
||||
|
||||
/* Put every mirror into the primary's mode, if it has one.
|
||||
*
|
||||
* Without this, choosing 1024x768 at the F8 menu changed the screen it was
|
||||
* chosen on and left the other one at whatever the firmware had picked -- so
|
||||
* the menu and then the splash appeared on it as a small rectangle in the
|
||||
* middle of a larger black one. The resolution in that menu is a statement
|
||||
* about the machine's display, not about one adapter of it.
|
||||
*
|
||||
* Cheap to call on every frame: the common case is two integer comparisons and
|
||||
* no mode set at all. A mirror with no matching mode keeps the one it has and
|
||||
* gets the centred-and-cropped treatment below, which is still better than
|
||||
* nothing.
|
||||
*/
|
||||
static void
|
||||
mirrors_match(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
|
||||
{
|
||||
UINT32 w = primary->Mode->Info->HorizontalResolution;
|
||||
UINT32 h = primary->Mode->Info->VerticalResolution;
|
||||
|
||||
for (UINTN i = 0; i < mirror_n; i++) {
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL *g = mirror_gop[i];
|
||||
|
||||
if (g->Mode->Info->HorizontalResolution == w &&
|
||||
g->Mode->Info->VerticalResolution == h)
|
||||
continue;
|
||||
|
||||
for (UINT32 m = 0; m < g->Mode->MaxMode; m++) {
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mi = 0;
|
||||
UINTN sz = 0;
|
||||
if (EFI_ERROR(g->QueryMode(g, m, &sz, &mi)) || !mi)
|
||||
continue;
|
||||
if (mi->HorizontalResolution == w && mi->VerticalResolution == h) {
|
||||
g->SetMode(g, m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Put what is on the primary onto all of them. Centred when the sizes differ,
|
||||
* cropped when the other display is smaller -- the same rules the kernel's
|
||||
* console mirror and vgpud's both use, because it is the same picture. */
|
||||
|
|
@ -1058,6 +1097,8 @@ mirrors_show(void)
|
|||
if (!primary->Mode->FrameBufferBase)
|
||||
return; /* nothing to copy *from* */
|
||||
|
||||
mirrors_match(primary);
|
||||
|
||||
UINTN sw = primary->Mode->Info->HorizontalResolution;
|
||||
UINTN sh = primary->Mode->Info->VerticalResolution;
|
||||
UINTN delta = (UINTN)primary->Mode->Info->PixelsPerScanLine * 4;
|
||||
|
|
|
|||
|
|
@ -631,9 +631,11 @@ main(void)
|
|||
unsigned int highest = adopt();
|
||||
next_cookie = highest + 1;
|
||||
|
||||
// Everything except whatever owns the console. That one waits for the
|
||||
// greeting below -- see the comment on it.
|
||||
for (int i = 0; i < njobs; i++) {
|
||||
struct job *j = &jobs[i];
|
||||
if (j->disabled || !j->run_at_load)
|
||||
if (j->disabled || !j->run_at_load || j->own_tty)
|
||||
continue;
|
||||
if (j->pid)
|
||||
continue; // adopted; it never stopped running
|
||||
|
|
@ -658,6 +660,35 @@ main(void)
|
|||
out("\nWelcome to SchOS!"
|
||||
" (F8 at boot for the full log, or dmesg once you are in)\n\n");
|
||||
|
||||
// *Now* the shell on the console.
|
||||
//
|
||||
// It used to be started with everything else, and then this greeting was
|
||||
// printed -- which made the two a race that the shell won about half the
|
||||
// time. A boot that sometimes reads
|
||||
//
|
||||
// Welcome to SchOS! (F8 at boot for the full log, ...)
|
||||
// root@schos / »
|
||||
//
|
||||
// and sometimes reads the same two lines the other way round is not two
|
||||
// orderings of one boot, it is a greeting that looks like it was printed
|
||||
// by mistake.
|
||||
//
|
||||
// Holding it back until after the write is enough to settle it: this
|
||||
// process writes the line, and only then asks PID 1 to spawn the shell, so
|
||||
// the fork cannot even have happened yet. Nothing else waits on it -- the
|
||||
// console shell is the last job to start either way, and starting it a
|
||||
// few microseconds later costs nothing a person can see.
|
||||
//
|
||||
// `own_tty` rather than a name: the job that claims the console as its
|
||||
// controlling terminal is exactly the job whose output lands here.
|
||||
for (int i = 0; i < njobs; i++) {
|
||||
struct job *j = &jobs[i];
|
||||
if (j->disabled || !j->run_at_load || !j->own_tty || j->pid)
|
||||
continue;
|
||||
vout("schinitd: starting "); vout(j->label); vout("\n");
|
||||
ask_spawn(j);
|
||||
}
|
||||
|
||||
// From here on this program does two things: answer PID 1's heartbeat, and
|
||||
// act on the exits it reports. There is no wait() in this program and
|
||||
// there cannot be -- the children are not ours.
|
||||
|
|
|
|||
|
|
@ -138,6 +138,15 @@ system information, the splash, each segment of the kernel load. Blt rather
|
|||
than a second framebuffer to draw into, because a virtio GOP does not have one
|
||||
— the same fact that made a virtio-only machine reboot-loop.
|
||||
|
||||
Each stage puts the other displays into the mode the F8 menu chose, rather than
|
||||
leaving them in whatever the firmware picked. Without that, choosing 1024×768
|
||||
changed the screen it was chosen on and left the other one at 1280×800, showing
|
||||
the menu and then the splash as a small rectangle in the middle of a larger
|
||||
black one. The loader sets the mode on each mirror GOP; the kernel's virtio
|
||||
driver takes `gfx`'s dimensions over `GET_DISPLAY_INFO`'s, for the same reason
|
||||
`vgpud` does — the host window's size is a fact about QEMU, not about the
|
||||
machine.
|
||||
|
||||
**The kernel.** `display_mirror_console()` copies the console's newly-drawn
|
||||
rows to the display class whenever that is a *different screen*. Cheap because
|
||||
`gfx` remembers which rows those were: a line of boot text is one cell-row
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "display.h"
|
||||
#include "dma.h"
|
||||
#include "virtio.h"
|
||||
#include "gfx.h"
|
||||
|
||||
// One control queue is enough for 2D: there is also a cursor queue, which is
|
||||
// only worth having once there is a hardware cursor to move.
|
||||
|
|
@ -280,6 +281,18 @@ setup_scanout(struct vgpu *g)
|
|||
g->width = (int)info->pmodes[0].r.width;
|
||||
g->height = (int)info->pmodes[0].r.height;
|
||||
|
||||
// But the console's mode wins, when there is a console.
|
||||
//
|
||||
// GET_DISPLAY_INFO answers with the size of the *host's window*, which says
|
||||
// nothing about the machine -- somebody dragged a corner. The resolution
|
||||
// chosen at the F8 menu is the console's, and a second head that shows the
|
||||
// console in a different mode shows it letterboxed for no reason. Same
|
||||
// argument, and the same fix, as in cmd/vgpud/main.c's bring_up().
|
||||
if (gfx_ready() && gfx_width() && gfx_height()) {
|
||||
g->width = (int)gfx_width();
|
||||
g->height = (int)gfx_height();
|
||||
}
|
||||
|
||||
// A host resource to scan out from.
|
||||
{
|
||||
struct virtio_gpu_resource_create_2d *c = g->cmd;
|
||||
|
|
|
|||
Loading…
Reference in a new issue