Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NTOS:INBV] Implement rotation bar for boot screen #194

Merged
merged 2 commits into from Jan 3, 2018

Conversation

binarymaster
Copy link
Member

@binarymaster binarymaster commented Dec 14, 2017

Purpose

Implement a rotation bar for bootup screen! 😃

Inspired by JIRA issue: CORE-14101

Fixes issue: CORE-10327

Proposed changes

It adds implementation of bootup rotation bar, like in Windows XP.

Also implemented a rotation line animation which would be showed along with classic progress bar.

TODO

Any help is appreciated.

  • Figure out reason of BSOD 0x0000007E and fix it
  • Create system thread for rotation drawing code
  • Figure out reason of another BSOD 0x0000007E and fix it
  • Animation starts with a small delay, improve it
  • Implement a new animation to save original progress bar
  • Reduce patch diff for better compatibility

@sanchaez sanchaez added help wanted Request for help. WIP labels Dec 14, 2017
@HBelusca
Copy link
Contributor

A fourth TODO would be to have this appearance set via a registry key or any other sort of setting :)

@@ -768,6 +775,7 @@ INIT_FUNCTION
DisplayBootBitmap(IN BOOLEAN TextMode)
{
PVOID Header = NULL, Footer = NULL, Screen = NULL;
UCHAR Buffer[128];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this 128-byte value comes from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since bar bitmap is 22x9 and 4bpp (hardcoded, yes), minimum value would be 22 * 9 * (4 bpp / 8 bits) = 99, but I made it a bit larger to reserve.

Also I don't know whether InbvScreenToBufferBlt uses some headers for the data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It calls VidScreenToBufferBlt (in bootvid.dll).
By looking at the code, this function seems to use Delta * Height bytes in the buffer (delta is the last parameter of the function). I don't personally completely understand yet what this Delta is for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scan lines in a bitmap get aligned (to a 4-byte boundary IIRC), so the scanline width can be different from the image width. Delta is likely the scanline width.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yagoulas commented in IRC:

it looks like delta is the width of the source bitmap
and I gues this would be used when you want to paint only a part of the marqee when it has reached the right part of the scrollbar and starts disappearing
this would be a case when width<Delta

Copy link
Contributor

@HBelusca HBelusca Dec 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hypothesis of the scanline width makes sense, and this would explain the value of "4" (or "8", don't remember) seen in other places where this function is called, and where the used Delta is completely different than the specified Width.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so 24 * 9 * (4/8) = 108, still should be ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, UCHAR Buffer[24 * 9 * 4 / 8];? ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and it used in three places... 😆

{
ULONG X, Y, Iteration, Index, Total = 18;
UCHAR Buffer[128];
#ifndef INBV_ROTBAR_IMPLEMENTED
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this you could do:

#ifdef INBV_ROTBAR_IMPLEMENTED
VOID NTAPI ... InbvRotBarInit(VOID)
{
    // The implementation
    return;
}
#else
VOID NTAPI ... InbvRotBarInit(VOID)
{
    return;
}
#endif

or even, just have the function defined in that case and calling in the main code only between #ifdef INBV_ROTBAR_IMPLEMENTED guards ?

if (RotBarSelection != RB_SQUARE_CELLS)
return;

/* This function needs to be in system thread, but it's not yet */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean "needs to be called by system thread" ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of code should be in while () loop instead of for, and run in context of a separate thread.

For now it's executed sequentially, so the loop is limited to 6 rotations (for demonstration).

@@ -920,8 +930,14 @@ DisplayBootBitmap(IN BOOLEAN TextMode)
#endif

#ifdef INBV_ROTBAR_IMPLEMENTED
/* Save previous screen pixels to buffer */
InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably can move this whole block inside the if (Bar) since saving restoring is unnecessary otherwise?

@@ -932,18 +948,67 @@ DisplayBootBitmap(IN BOOLEAN TextMode)
}

#ifdef INBV_ROTBAR_IMPLEMENTED
/* Hide simple progress bar if not used */
if (RotBarSelection != RB_PROGRESS_BAR)
ShowProgressBar = FALSE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces around all blocks please.

@ThFabba
Copy link
Member

ThFabba commented Dec 14, 2017

You probably have neither file system nor registry available during this part of boot, so making it user-configurable is not exactly trivial.

@@ -768,6 +775,7 @@ INIT_FUNCTION
DisplayBootBitmap(IN BOOLEAN TextMode)
{
PVOID Header = NULL, Footer = NULL, Screen = NULL;
UCHAR Buffer[128];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be placed inside a #ifdef INBV_ROTBAR_IMPLEMENTED.

@binarymaster binarymaster force-pushed the bootloader-bar branch 2 times, most recently from 7d7ce5f to be981d8 Compare December 14, 2017 16:52
@HBelusca
Copy link
Contributor

You're right Thomas; actually it seems the best way would be:

  • either via a parameter given to the kernel command-line (and specified via freeldr.ini entries, similar to the NOLOGO or NOGUIBOOT parameter).
  • patch ntoskrnl or something.

@ThFabba
Copy link
Member

ThFabba commented Dec 14, 2017

Yes, and even the parameter would likely not work, since, as I said, no filesystem access! You might need to do something like compile the logo as a resource into a sys file, have freeldr load that as a boot driver and then pass a kernel command line argument to specify that driver's name. In which case you might as well have recompiled the kernel in the first place... Like I said, nontrivial :)

@ColinFinck
Copy link
Member

I remember that we had the rotation bar early in 0.3.1 times (just when the current bootvid was checked in). It was a very buggy implementation, so someone shortly disabled it. Later, the current progress bar was added.

Given that we have a progress bar that shows boot progress from 0 to 100%, I wonder what's the value of a less informative rotation bar? Just because XP does it? Well, Win2k had a progress bar too, and I preferred that :)

I'm all for making our boot animation more exciting. However, that should be more than just replacing the progress bar.

@gigaherz
Copy link
Member

I think it's good to have some animation, because it shows that the system is not frozen. But it doesn't have to be XP's "marquee" bar. We could easily go the macOS way and show an actual spinner/throbber animation, if showing a full blown boot animation (like vista+ do) would be too much without boot-time graphics support. A small throbber animation should be fairly easy to produce and implement, and not slower than the not-really-progress bar?

@gigaherz
Copy link
Member

Because I wasn't clear: I do agree with Colin that the existing progressbar should stay, I'm talking about an added thing to show that it's not frozen.

@learn-more
Copy link
Member

Maybe some animantion that uses the progress bar like in Win7's progress bar?

https://social.msdn.microsoft.com/Forums/getfile/390525

@gigaherz
Copy link
Member

gigaherz commented Dec 15, 2017

That may be too slow depending on how large the progressbar gets. The idea of the small rotating image or a spinner, is that the number of pixels redrawn each frame is relatively small (if the drawing code only writes the needed pixels), which is important due to having to poke the video memory directly since we don't have boot-time video support like NT6 does.

EDIT: On second thought, if the XP-style animation was actually the highlight, contained in the "completed" area of a progressbar, that would work!

@binarymaster
Copy link
Member Author

Please notice this PR is not targeting to replace current progress bar (I like it too) but to bring up an ability to change its behaviour at compile time (or maybe at boot time later).

When it will be finished, it would be simpler to create some other animations by reusing the code.

@binarymaster
Copy link
Member Author

Rewrote part of drawing code, so now it properly renders appearing part from the left. 😉

Demonstration: https://webmshare.com/1WZNg

@binarymaster
Copy link
Member Author

By looking at the code, this function seems to use Delta * Height bytes in the buffer

This was correct, the pixels are stored in bytes inside 8-bit buffer, so the size should be 24 * 9 and not 24 * 9 * 4 / 8, and this fixed the BSOD!

Also I noticed that InbvSolidColorFill uses InbvAcquireLock(); and InbvReleaseLock(); but the lock is already acquired before InbvRotBarInit, so I replaced it with VidSolidColorFill, that fixed the hang after animation!

So there is only one step left - to make it run separately (in thread) along with boot loading process. @ThFabba do you know how to do that?

@HBelusca
Copy link
Contributor

HBelusca commented Dec 16, 2017

About the locking thing, maybe there is a proper way to reorganize that (in your helper function) without the need to directly call the Vid* functions.
Otherwise the demo looks nice!
Any idea why it ends with a BSOD though?

@ThFabba
Copy link
Member

ThFabba commented Dec 16, 2017

I don't know if that's available at this point in the boot process, but normally to create a thread you would use PsCreateSystemThread, like here:
https://github.com/reactos/reactos/blob/master/modules/rostests/kmtests/include/kmt_test_kernel.h#L136-L187

@binarymaster
Copy link
Member Author

Any idea why it ends with a BSOD though?

I already fixed it (commented above), this video demo is a bit outdated already :)

@ThFabba
Copy link
Member

ThFabba commented Dec 16, 2017

Directly calling the Vid* functions from inside Inbv seems correct -- in fact I'd say you probably should do it for VidBufferToScreenBlt as well.

@HBelusca
Copy link
Contributor

HBelusca commented Dec 27, 2017

Just for reference purposes, here is our (very) old animation procedure we used to have in bootvid: BootVidAnimationThread , before this module got rewritten.

@HBelusca
Copy link
Contributor

HBelusca commented Dec 27, 2017

Code updated with @ThFabba's suggestions.

Status = PsCreateSystemThread(&ThreadHandle,
SYNCHRONIZE,
0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The '0' works OK.

RB_ANIMATE,
RB_STOP_ANIMATE,
RB_STATUSMAX
} ROT_BAR_STATUS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have better names for all these, just let us know!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe RBS_* ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good :)

{
/* Acquire the lock */
InbvAcquireLock();
LARGE_INTEGER Delay; // Specified in nanoseconds
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not nanoseconds

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh indeed, it's in hundreds of nanoseconds :) I'll fix the comments; the calculations/values used for Delay are however OK.

LARGE_INTEGER Delay = {{0}};

// FIXME: This was added to suppress thread hanging
KeSetPriorityThread(KeGetCurrentThread(), HIGH_PRIORITY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still like to get rid of this before merging

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@binarymaster, could you please check whether things work without the priority setting? I think they should.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I retested without this change, and found no difference. So this can be removed.

@binarymaster
Copy link
Member Author

Just for reference purposes, here is our (very) old animation procedure we used to have in bootvid: BootVidAnimationThread , before this module got rewritten.

Interesting... rotation bar animation was implemented with 3 buffers for each square cell :)

@@ -161,7 +161,7 @@ BootLogoFadeIn(VOID)
LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
ULONG Iteration, Index, ClrUsed;

LARGE_INTEGER Delay; // Specified in nanoseconds
LARGE_INTEGER Delay = {{0}}; // Specified in hundreds of nanoseconds
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this needs to be initialized in all places, or it have only one field (QuadPart)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure it zeroes out the Delay because when the while() loop is run for the first time, KeDelayExecutionThread is called with a valid delay.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, specifying an initializer for any member of a struct, union, or array means the whole object will be initialized.
On an unrelated note, this comment seems completely unnecessary. The unit is obvious to anyone familiar with kernel programming, and anyone else needs to read the documentation for KeDelayExecutionThread anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, removed.

break;
}

if (PltRotBarStatus >= RBS_STATUS_MAX)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way this can happen, right? So an ASSERT should be enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ASSERT.

*/
typedef enum _ROT_BAR_STATUS
{
RBS_FADEIN = 1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to make this distinct from 0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really, why isn't this just a boolean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally (and as the original code -- before @binarymaster's patch -- also suggested via the value of "PltRotBarStatus"), this looked like it was designed as a "command" variable for the animation thread to know what to do "next", like, first, do the palette fading-in, then the rotation, then stop (in the usual order, but you can also choose to blank the screen again, then redo a fading in etc...).
In the current code the fading-in is done before starting the animation thread; I was wondering whether it could be nice instead to move it in the animation thread where that PltRotBarStatus variable would then make better sense (I didn't do the move as you can see).
But perhaps we can also do the way of the old bootvid code (see my link above), where the animation thread always do "fading in" first, then bar rotation, and thus the PltRotBarStatus would just become a Boolean "start / stop".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer leave this variable as is...

@ColinFinck
Copy link
Member

Judging from your latest demonstration video, you are using the rotation bar bitmap from Windows Server 2003. This must be changed to something custom should this PR be merged.
I remember that the original bootvid rotation bar code by Filip Navara used a custom bitmap that nicely integrated into the rest of the ReactOS boot screen. Maybe you can use that.

Nevertheless, I still prefer a boot screen with a progress bar that shows actual progress over a rotating one.
I'm not opposed to improving our boot animation and its code in general though.

@binarymaster
Copy link
Member Author

binarymaster commented Dec 30, 2017

@ColinFinck

Judging from your latest demonstration video, you are using the rotation bar bitmap from Windows Server 2003.

No, I haven't used it. I even didn't modify it (see diff), this bitmap is slightly different and currently in master branch.

Nevertheless, I still prefer a boot screen with a progress bar that shows actual progress over a rotating one.

Have you tried building my PR branch? 😈 There's already a different animation, which also stated in TODO list. 😉

@binarymaster
Copy link
Member Author

https://gfycat.com/EvergreenBrilliantCoelacanth

New GIF demo ;)

@HBelusca
Copy link
Contributor

@ThFabba and @ColinFinck , is everything OK so that we can proceed to a squash-and-merge?

binarymaster and others added 2 commits January 2, 2018 15:55
- Use KeDelayExecutionThread() instead of KeStallExecutionProcessor().
- Fix magic values and add comments.
- Fix structure name.
@binarymaster
Copy link
Member Author

Rearranged commits for rebase and merge (two commits without squash), I think it would be better.

@HBelusca HBelusca removed the help wanted Request for help. label Jan 3, 2018
@HBelusca HBelusca merged commit b2be558 into reactos:master Jan 3, 2018
@binarymaster binarymaster deleted the bootloader-bar branch January 3, 2018 07:05
@HBelusca HBelusca added enhancement For PRs with an enhancement/new feature. and removed WIP labels Aug 1, 2019
julenuri pushed a commit to julenuri/reactos that referenced this pull request Dec 9, 2023
* Update openoffice.txt

* Update SHA1 hash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement For PRs with an enhancement/new feature.
Projects
None yet
7 participants