-
-
Notifications
You must be signed in to change notification settings - Fork 172
Double-scanning support for VESA modes, accurate mode change logging, vga_draw.cpp
rework & many other improvements
#2654
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
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
be79227
to
6899da8
Compare
56c3c61
to
252b529
Compare
This is a big rework that has been extensively tested on (almost) all video modes supported by DOSBox (some are really hard to trigger because they apparently are virtually not used by any program), and with a large number of games and demoscene productions that use custom tweaked resolutions. Specifically: - Untangle the current quite coupled way of performing these calculations and make calculations for the different modes largely independent of each other. - Only group related modes where this does not hinder maintainabilitiy and ease of understanding. Previously, fixing something for one specific mode could easily break other modes -- that was an absolute nightmare to maintain. - Remove the majority of heuristics-based and special-cased double-scanning decisions based on image dimensions and prefer to use bit flags in VGA registers that actually control the behaviour of VGA hardware. - Same deal for pixel-doubling in 320/640 and 360/720 pixel wide modes and their tweaked variants. - Same deal for aspect ratio calculations; all hardcoded values and most special-casing naturally fell away as the result of deriving the ratios either from display timings or the calculated image dimensions. - Large number double-scanning and aspect ratio related fixes (e.g. CGA/Tandy/PCjr text modes were double-scanned which was wrong). - Introduce a clear separation of the dimensions and pixel aspect ratio of the *video mode* vs the *rendered image*. Downstream processing often needs information about both (e.g. image capturing, logging details about the current video mode, viewport scaling, etc.) Previously, only the parameters of the rendered image were propagated downstream, and deriving the video mode's parameters from those is a bad and error-prone way of doing things at the very least, and impossible in some other cases (e.g. "baked in" double-scanning for VGA modes). Trying to patch the deficiencies up resulted in some hacks that were not even working very well. - Add proper double-scanning and pixel-doubling support to all VESA modes, according to the INT 10h mode tables. - Proper mode changes are now performed when toggling composite modes.
In addition to the currently stored rendered image dimensions and pixel aspect ratio (PAR), also store the dimensions & PAR of the video mode. This is necessary for logging the correct "nominal" pixel dimensions and PAR of a given mode and storing them as metadata in the captured images. This might also be handy later for certain OSD features that display information about the current video mode.
- Improve VGA_GetCurrentMode() so the results reflect actual reality better. - Always log the video standard the mode first appeared in (e.g. CGA modes should be always CGA, regardless of the display adapter), but don't use the name "MCGA" -- we don't even emulate the MCGA adapter, so this is confusing. - Log the dimensions and pixel aspect ratio of the *video mode*, not the rendered image (this was super confusing when for example the 320x200 256-colour 13h VGA mode got logged as 640x400) - Don't log mode number when the actual dimensions don't equals the standard dimensions of the screen mode (this usually indicates a custom tweaked mode).
Previously, this used the rendered image's properties which resulted in incorrect image dimensions being written into the metadata (e.g. 640x400 for mode 13h).
The image capturer now always uses the dimensions of the *video mode* as the basis of performing the upscaling (as opposed to the dimensions of the *rendered image*, as done previously). In some cases (when dealing with "baked in" double-scanning) this can result in hitting the target ~1200px of upscaled vertical resolution better (because we can effectively use 2x finer vertical integer scaling steps for "baked-in" double-scanning). An additional constraint has been introduced so we always use a 2:2 upscale factor at minimum, except for the 1600x1200 mode where we don't upscale. Also, the 'raw' image capture mode now always writes the unaltered contents of the VGA framebuffer without any doublings applied (e.g. 640x200 now gets written as a raster of 640x200 pixels; previously, the height was doubled to 400 pixels). This breaks compatibility with the legacy DOSBox image capture behaviour *on purpose* -- the new default is the aspect-ratio preserving 'upscaled' mode; 'raw' mode is supposed to be only used in special circumstances when one wants to dump the raw, unaltered pixel-data.
- Move 'force_vga_single_scan' setting to the [render] section - 'force_vga_single_scan' can now be toggled at runtime - Decouple the "double-scanning enabled" draw-state from forcing single-scanning
Sub-640 pixel wide video modes are always pixel-doubled now by default. This is needed to achieve more accurate CRT emulation with current CRT shaders. For example, on a real monitor there should be zero difference between a 320x200 image displayed in a 320x200 video mode, or the same image width-doubled and displayed in a 640x200 mode. Without pixel-doubling, we would have a very noticeable difference with current CRT shaders that don't take such differences into account (basically, none of them do), resulting in horizontally blurrier image for the 320x200 video mode case. A new 'force_no_pixel_doubling' render setting is also available to disable this behaviour. This might be useful for optimising performance on low-powered devices, or to emulate a really blurry consumer CRT monitor or TV set.
Also instead of letting downstream processing override the PAR when forcing square pixels (questionable), do that decision in vga_draw.cpp and pass the appropriate PARs further down.
…ragmas Shaders can now request single-scanning and no pixel-doubling with these pragmas. The motivation for this can be to increase performance where such doublings would make no visual difference (e.g. for our default 'interpolation/sharp' shader), or to achieve a certain look (e.g. to emulate a consumer TV or an arcade or home computer monitor).
…ader Double-scanning and pixel-doubling makes no visual difference to the 'sharp' shader, but results in 4x more pixels being processed. Additionally, this allows for finer scaling steps when 'integer_scaling' is enabled.
Previously, these settings were set on every video mode change which is unnecessary. The way of setting these flags is also less convoluted now (it's 100% driven by the renderer now).
3209cb4
to
7af1948
Compare
|
4 tasks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Introduction
If it wasn't absolutely clear to everyone here that I'm a man who does not believe in half-measures, this PR should really drive that home! 😎 🚬
This started as something quite innocent: I only wanted to expand upon kcgen's excellent
vgaonly
work to add proper double-scanning to all VESA modes (see this ticket #2604) because it was annoying me that late DOS era demos that use high-colour modes appeared single-scanned... like an EGA monitor 😬 Plus I wanted to fix the logging on video mode changes so it logs the dimensions of the actual video mode, not the render buffer.Sounds easy right? It turns out it was a case of having to take 10 steps back and take a quite different route just to be able to take one step forward. I discussed this with kcgen, his work was mostly based on heuristics and patching things up here and there, but that approach wasn't really maintainable and scalable; continuing down that track would've resulted in a huge unmaintainable mess. Still, without him bravely forging the path ahead, I would never have dared to touch the scary beast that is
vga_draw.cpp
!So one thing lead to another, and suddenly I've sunk easily 150+ hours into this over the last month or so... Running literally thousands of test cases (see test cases below), fun stuff like that 😎 I'm still in recovery mode, refactoring this was exhausting...
To make sense half of all this, you need to be quite familiar with CGA/EGA/VGA... especially with VGA, the custom "Mode X" tweak mode stuff, and how double-scanning of VGA adapters affect image quality on real CRT monitors. Otherwise, you'll just need to trust me 😎 🤘🏻
I've tested things quite extensively over and over again, so I'm quite confident in these changes, but of course it's not impossible that some edge cases are broken. However, with the new, better structure in place, it will be much easier to fix problems with one specific video mode on one adapter without breaking random stuff on other adapters. That's not the case in current main...
Overview
Ok, so main changes in a nutshell (well, it's a rather large nut... 😅 🥜 )
REVIEW BY COMMIT IF YOU VALUE YOUR SANITY!!! I've put considerable effort in coming up with a relatively logical and clean history (I must have spent several full days just reorganising stuff with rebase...)
Double-scanning
The
force_vga_single_scan
setting has been moved to the[render]
`section.force_vga_single_scan
can now be toggled at runtime and it gets activated on the next video mode change (which I find pretty cool).All VESA modes supported by DOSBox are now double-scanned according to the mode table in
int10_modes.cpp
(tested withvesa_modes = all
andvmemsize = 8
). Naturally, the same goes for all non-VESA SVGA modes too on all supported SVGA adapters.CGA, PCjr & Tandy text modes are now properly single-scanned.
The determination of whether a given VGA mode should be double-scanned or not should be almost perfectly correct now (I just don't dare tempt the devil by saying it's 100% 😈 🔥 ). This is because double-scanning is now controlled by the state of VGA registers for the most part instead of various image dimension and BIOS mode based heuristics. This has been tested with demos and various emulators (RealSpec, CCS64) that set up wacky custom modes to for example achieve a single-scanned look with around 300-lines of resolution at 85Hz+ refresh rates... Yep, all that works, in addition to all the games and demoscene productions that use strange custom modes on VGA!
Pixel-doubling
Sub-640 pixel wide video modes are always pixel-doubled now by default. This is needed to achieve more accurate CRT emulation with virtually all current CRT shaders in existence. For example, on a real monitor there should be zero difference between a 320x200 image displayed in a 320x200 video mode, or the same image width-doubled and displayed in a 640x200 mode. Without pixel-doubling, we would have a very noticeable difference with current CRT shaders that don't take such differences into account, resulting in horizontally blurrier image for the 320x200 video mode case (there are some image comparisons on this below).
A new
force_no_pixel_doubling
render setting is also available to disable this behaviour. This might be useful for optimising performance on low-powered devices, or to emulate a really blurry consumer CRT monitor or TV set.See these threads for my discussions with prominent shader authors / people
with an intense interest in accurate CRT emulation on the matter:
https://www.vogons.org/viewtopic.php?f=63&t=94909
https://forums.libretro.com/t/new-crt-shader-from-guest-crt-guest-advanced-updates/25444/3591?u=rincewind
https://forums.libretro.com/t/new-crt-shader-from-guest-crt-guest-advanced-updates/25444/3592?u=rincewind
VileR was kind enough to confirm the above by taking some actual photos of his CGA monitor in 320x200 and 640x200 modes: https://www.vogons.org/viewtopic.php?p=1174367#p1174367
The TL;DR is that pixel-doubling of roughly sub-640 pixel wide content is necessary to achieve an authentic CRT look with currently available CRT shader technology, without having to maintain a set of different shaders and switch between them manually depending on whether you're playing a game that uses ~320 or ~640 pixel wide modes. Plus I find the multi-shader approach a non-solution when using DOSBox as a computer emulator because a shader optimised for ~320-pixel-wide content would look off in ~640-pixel-wide modes, and vice versa.
Shaders
Introduced two new shader pragmas,
force_single_scan
andforce_no_pixel_doubling
, so shaders can explicitly request single-scanning and no pixel-doubling if this would make no visual difference (we cannot determine this upstream, only the shader author can make this decision). The default "sharp" shader is such an example; this allows for finer scaling granularity with integer scaling enabled, and there are 4x less pixels to process by the shader which can make a big difference on low-powered devices.The default
interpolation/sharp
shader has been updated to force single-scanning and no pixel-doubling. This ensures there's zero negative performance impact on people who are content with just using the defaults.These pragmas also for cool possibilities, e.g. to make a "Commodore monitor" shader that forces single-scanning and no pixel-doubling.
Logging of current video mode
The removal of
vgaonly
broke the logging of the video mode dimensions because only the rendered image dimensions were available downstream, so we got things likeMCGA 640x400 (mode 13h)
in the logs which is super-confusing and incorrect. That is now fixed; e.g., for mode 13h we getVGA 320x200 (mode 13h)
as we should.The logging of video modes is now a lot more accurate in general; now we're consistently logging the video standard the mode first appeared in (e.g., CGA modes should be always CGA, regardless of the emulated display adapter in use).
If there's ambiguity regarding the video mode (e.g., the actual source image dimensions don't match the nominal dimensions of the BIOS video mode), we omit the
(mode XYh)
part from the logs to avoid confusing the user. The most noticeable side effect of this is that we no longer log weird stuff likeMCGA 360x240 (mode 13h)
which is clearly not mode 13h but a tweaked VGA mode---it's just mode 13h was used as a starting point to tweak the mode further. Stuff like that gets simply logged asVGA 360x240
now.Another subtle but important detail: now the pixel aspect ratio of the video mode gets logged, not whatever pixel aspect ratio we derive from the rendered image's dimensions for rendering purposes.
Composite CGA/Tandy/PCjr modes in particular are being logged more accurately, and we get video mode change log message when toggling composite modes.
Image capture
If the below points don't make much sense, it's time to refresh your memory about the new enhanced & revamped image capture feature: #2503
Because now the dimensions and PAR of the video mode get propagated downstream, the image capturer can write the correct metadata tags into the images (this was just as incorrect as the logging of the current video mode's dimensions).
The
raw
image capture mode now really means raw; e.g., a 640x200 image isn't height-doubled like before but simply written as a 640x200 pixel-raster, end of story. This is the logical conclusion of the new revamped image capture functionality; theupscaled
aspect-ratio preserving, "sharp" upscaled output is the default, this is what most people should use. Theraw
mode that corresponded to the "legacy" DOSBox screenshot capture functionality break compatibility with it on purse; this mode is really just there for special needs when you must capture the original, unaltered pixel-raster data.The image capturer now always uses the dimensions of the video mode as the basis of performing the upscaling (as opposed to the dimensions of the rendered image, as done previously). In some cases (when dealing with "baked in" double-scanning) this can result in hitting the target ~1200px of upscaled vertical resolution better (because we can effectively use 2x finer vertical integer scaling steps for "baked-in" double-scanning).
Naturally, and following from the previous point, all combinations of pixel-doubling, "fake" double-scanning, real "baked-in" double-scanning are handled 100% correctly as you would expect during image capturing. Basically, the results for the same video mode should always be completely identical, regardless of these rendering side variations.