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

Add overscan (border) support #1792

Open
johnnovak opened this issue Jul 14, 2022 · 35 comments
Open

Add overscan (border) support #1792

johnnovak opened this issue Jul 14, 2022 · 35 comments
Labels
enhancement New feature or enhancement of existing features video Graphics and video related issues
Projects

Comments

@johnnovak
Copy link
Member

johnnovak commented Jul 14, 2022

Overview

On real hardware, the actual drawing area (e.g. 320x200, 640x480, etc.) doesn't fill the whole screen but there's always a border around it (usually black). It is possible to set the colour of this "overscan area" to some other colour than black, and some games and applications use the border colour to convey information to the user. But even when the coloured border doesn't serve any "useful" purpose, it enhances the "retro feel" for people who still remember how real CGA/EGA/VGA output on a CRT looked like.

CGA-like systems

On CGA and CGA-like systems (200-line EGA, Tandy, and PCjr), the overscan colour fills the whole screen, as you can see on the second and third image. There is no way to change the extents of the overscan border in software; the size is hardcoded.

image

image

image

VGA

On VGA, the overscan area is much smaller, usually just a few extra pixels, and most of the black border still remains. The actual size of the border can be controlled via software (there is a dedicated VGA register for it).

In the below video, note that every time something is selected in the menu, the screen goes full red, and then fades to black. When this happens, you can see a small stripe of red outside the visible menu area (the 320x200 actual content area); that's your border background colour on VGA.

https://youtu.be/LM1HYM_qSsw?t=84

Examples

A non-exhaustive list of games that use the overscan area:

  • Alley Cat
  • Castle of Kroz
  • Catacombs
  • Caverns of Kroz 1, 2
  • Commander Keen 1, 2, 3, 4
  • Crystal Caves
  • Dungeons of Kroz 1, 2
  • Doom
  • Duke Nukem 3D
  • Final Crusade of Kroz
  • Flashback
  • Impossible Mission II (Tandy)
  • Kingdom of Kroz 1, 2
  • Lost Adventures of Kroz
  • Return to Kroz
  • Secret Agent
  • Temple of Kroz
  • Wolfenstein 3D

Applications that use the overscan area:

  • FastTracker II

Detailed examples

Crystal Caves

In the original DOS version, the game's border will change color from red to green when all crystals have been collected and the level can be exited. This feature is not emulated in the DOSBox emulator (which the official releases for Windows, Mac and Linux use), thus the player has to rely on only a sound cue to mark this.
(source)

FastTracker II

When you toggle Edit Mode by pressing Space, the border changes colour, and there's no other way to tell whether you are in Edit Mode or not.

DOOM, Wolfenstein 3D

When you get hit, not only the screen but also the border flashes red.

Implementation

Currently, DOSBox Staging doesn't support overscan at all. DOSBox-X has limited support, and there was an old experimental proof-of-concept hack that added basic support, but apparently it only ever worked in windowed mode.

Other emulators, such as FUSE, VICE and WinUAE/P-UAE, have been emulating overscan borders right from the start (but to be fair, overscan borders play a much bigger role on the ZX Spectrum, Amiga and C64 than on the PC).

There's some lengthy discussions on the Vogons forum about the exact extents of the overscan area on different graphics adapters and monitors, and ideas about possible implementations (see the references below). I don't think we need to be so super-detailed about it, and I don't think that 100% faithfulness to the original hardware matters either.

The user should simply be able to set the horizontal and vertical overscan borders in pixels or percentages relative to the actual drawing area, and that's it. The borders would always be drawn symmetrically around the actual output, and it would always be "additive", meaning that in windowed mode the window size would be increased by the overscan dimensions, and in fullscreen mode parts of the border would simply be clipped if they wouldn't fit into the screen. If the user wanted to see the full border in fullscreen mode, they would need to restrict the viewport size with viewport_resolution accordingly.

References

https://www.vogons.org/viewtopic.php?p=328413#p328413
https://www.vogons.org/viewtopic.php?p=421895#p421895
https://www.vogons.org/viewtopic.php?f=32&t=38643&p=348920#p348920
https://www.vogons.org/viewtopic.php?f=41&t=49160&start=60

@johnnovak johnnovak added the enhancement New feature or enhancement of existing features label Jul 14, 2022
@johnnovak johnnovak changed the title Add overscan support Add overscan (border) support Nov 12, 2022
@sulix
Copy link
Contributor

sulix commented Jan 26, 2023

FYI: My Commander Keen reimplementation project supports this, albeit emulating only the VGA variant.

The border size we use for VGA 320×200 is:

  • Left and right borders are 8px wide each.
  • Top and bottom borders are 7 scanlines each, which, due to 320×200 being a line doubled mode, is 3½ px high each.

The latter can cause a few problems at 1× scale, but that can be worked around with some care.

@Jkapp76
Copy link
Contributor

Jkapp76 commented Jan 27, 2023

If it's a vote, I vote YES for this one! I really dig this old stuff. I like to keep the border smaller though... like with Commodore 64 Emulators too.

And, If you play Alley Cat, use the booter version or you won't get the awesome PCJr music.

I think Archon too had a color-changing border.

@johnnovak
Copy link
Member Author

johnnovak commented Jan 27, 2023

Yeah I'll add this at some point, but it's relatively low on my priority list. Ideally, I'd like to do it after @GranMinigun's pipeline refactoring stuff; easier than fighting the current mess.

I like to keep the border smaller though... like with Commodore 64 Emulators too.

With my proposed solution you could set the exact desired border size — even just a few pixels if that's what you want. I see very little point in mimicking the monitor hardware exactly for this feature like one of the guys on Vogons tried in his super complex patch I linked above.

@johnnovak
Copy link
Member Author

And, If you play Alley Cat, use the booter version or you won't get the awesome PCJr music.

You seem to be a bit of an expert in all things PCjr, perhaps you'd like to help with this Wiki page about games that have the best video and/or sound on the PCjr or the Tandy:

https://github.com/dosbox-staging/dosbox-staging/wiki/Games-with-Tandy-&-PCjr-exclusive-graphics-and-sound

It's work in progress, so don't go crazy yet, but I'll ping you when I think it's ready. I'm gonna include a lot of other games from another source and reformat the whole thing.

@GranMinigun
Copy link
Contributor

The easiest way to show overscan colour would be to set clear colour for both texture and GL outputs. Of course, that will paint the whole area around actual image, but I guess that's still better than nothing? Couple with limited viewport size to also get top and bottom borders. Specific border size is a bit more involved, but should be trivial enough still (SDL_FillRect(), and some fiddling with the core shaders for OpenGL, respectively).

Does DOSBox currently expose any overscan controls? That is, is it possible to get the overscan colour and know when it was set?

@GranMinigun GranMinigun added this to Suggested new features in Backlog via automation Jan 27, 2023
@johnnovak
Copy link
Member Author

johnnovak commented Jan 28, 2023

The easiest way to show overscan colour would be to set clear colour for both texture and GL outputs. Of course, that will paint the whole area around actual image, but I guess that's still better than nothing? Couple with limited viewport size to also get top and bottom borders. Specific border size is a bit more involved, but should be trivial enough still (SDL_FillRect(), and some fiddling with the core shaders for OpenGL, respectively).

Yeah there are some solutions that seem easy but have some drawbacks. E.g. when the viewport resolution is set to 960x720 (that's typically what I use on my 1920x1080 screen), I really don't want the whole black area in fullscreen to change colour.

For example, when I use FastTracker II, in edit mode the border becomes red. You're frequently in edit mode, like 50% of the time when writing music. Having to stare at a 50% red screen is a no-go (FT II uses 640x480, so there would be lots of blank space in fullscreen with the typical 2x scaling).

Another consideration, I definitely want to border to be processed by the shaders as well (probably that will happen automatically even with setting the clear colour). There's two reasons for this:

  • The obvious one, the border is part of the emulated screen, so for example if the shader implements screen geometry emulation, the border needs to be a part of that, naturally.
  • For CRT shaders that emulate bloom and glow (so all good ones), the glow effects are currently cut off at the edges of the emulated screen. So ideally we'd have a say 10px padding around the image, e.g. for 320x200 we'd be rendering into a 340x240 texture, with the image centered to the texture. This will become an even bigger problem once we have proper multi-pass shaders, as the best RetroArch CRT shaders have much better blow, gloom and halation emulation that extend much further around the pixels (these are impossible to do without multipass, at least not efficiently; the performance costs to do this in a single-pass are astronomical).
  • Following from the above two, it's more efficient to just run a say 960x720 + padding for the border texture through the shader than the whole screen (but as I explained, setting the whole background around the viewport is not a good solution anyway).

If I were to implement this now, I'd do it like this probably:

  • border_size – Width of the border expressed as percentage of the width (!) of the DOS framebuffer. Default would be 2.5%, which gives you 8px border around a 320x200 screen, 16px around a 640x480 screen, etc. (You need percentages/proportions instead of absolute pixels so it works consistently across all display modes.)

  • border_mode — two options:

    • inner — The border is included in the viewport size, making the drawable image area smaller. For example, for a 320x200 VGA game and the above default border setting, the actual drawable area would become 304x184, and the border would always be fully visible inside the viewport since it's included in it.

    • outer — This is the more interesting setting and what we should default to. The border is applied on top of the specified viewport resolution, with the extra twist that the border is "allowed to be clipped" if it doesn't fit within the extents of the window or screen. The VICE C64 emulator implements this as one of its border mode options. In principle, we render into a texture "expanded" by the border dimensions (so for 320x200 we render into 336x216, with the border colour set, and the actual drawable image centered), but then we center the image as if were just 320x200, letting the border to be clipped!

The inner behaviour is useful if someone always wants to play in fullscreen with the image occupying as much of the screen as possible, but also have the full border visible around the drawable area at all times. So it's kind of a special case, and in general I think outer should be preferred.

It's easier to illustrate the outer behaviour via a few images. viewport_resolution was set to 640x480 for these example image. Note the extents of the drawable area is unchanged in all example, it's always 640x480, and the border is always applied around it as extra padding, and clipped if it doesn't fit into the window.

windowresolution == viewport_resolution — border fully clipped

image

windowresolution > viewport_resolution — border not clipped

Same scenario in fullscreen mode.

border1

windowresolution > viewport_resolution (vertically only) — border is not clipped vertically, but is clipped horizontally

border2

variation of the above — the border is not clipped horizontally, but is clipped a little bit vertically because it doesn't fit into the window

border3

Anyway, I think the images and my above description should make it clear how I envisage this to work. In the end, I think it will be easiest if I implement it 😄 I'm quite convinced this is the best way to implement border support 😄

Does DOSBox currently expose any overscan controls? That is, is it possible to get the overscan colour and know when it was set?

You can read it from certain CGA/EGA/VGA registers — it is easy to get that info.

image

@GranMinigun
Copy link
Contributor

probably that will happen automatically even with setting the clear colour

Not the case, because clearing backbuffer doesn't equal drawing to it with a shader.

You can read it from certain CGA/EGA/VGA registers — it is easy to get that info.

Great. Then it's only a matter of improving rendering pipeline.

@johnnovak
Copy link
Member Author

Not the case, because clearing backbuffer doesn't equal drawing to it with a shader.

Yeah, so everything what I wrote in my long post above stands then.

@sulix
Copy link
Contributor

sulix commented Jan 28, 2023

Personally, I suspect the most accurate way to handle this is to have the VGA code just generate a larger image with the border included. The exact size of the border could easily depend on which video card is being emulated. (Though, as mentioned, you'd need to have the VGA code do line-doubling for some modes, which could cause problems for some setups. You could just disable line doubling and round the border size if scaling is disabled / the window is too small, though.)

The way I handle it in my Commander Keen project is by using the clear colour, but rendering to a texture (which then undergoes the final scaling). But I was rendering-to-texture as part of my two-stage scaling process anyway, so it has no extra cost: I suspect that DOSBox doesn't, so this might be slower.

Regardless, it's probably worth looking at @NY00123's old patches on Vogons. As mentioned, I think having the overscan border be a part of the VGA code is probably the way to go about it, so I'd probably start by looking here.

Of course, that wouldn't get you the border clipping you describe without some significant extra work…

@johnnovak
Copy link
Member Author

johnnovak commented Jan 28, 2023

Personally, I suspect the most accurate way to handle this is to have the VGA code just generate a larger image with the border included. The exact size of the border could easily depend on which video card is being emulated. (Though, as mentioned, you'd need to have the VGA code do line-doubling for some modes, which could cause problems for some setups. You could just disable line doubling and round the border size if scaling is disabled / the window is too small, though.)

The way I handle it in my Commander Keen project is by using the clear colour, but rendering to a texture (which then undergoes the final scaling). But I was rendering-to-texture as part of my two-stage scaling process anyway, so it has no extra cost: I suspect that DOSBox doesn't, so this might be slower.

Regardless, it's probably worth looking at @NY00123's old patches on Vogons. As mentioned, I think having the overscan border be a part of the VGA code is probably the way to go about it, so I'd probably start by looking here.

Of course, that wouldn't get you the border clipping you describe without some significant extra work…

Yeah so that is what I proposed, to render the border to texture, minus using the "exact" approach from that old patch. That unnecessarily complicates things for no good reason.

I was thinking of the same approach as yours: oversize the texture (or not in the inner border mode), clear with the border colour, then render the actual content that's smaller with offsets so it's centered.

The clipping thing isn't difficult; it's more difficult to explain it than to implement. Just a few extra lines of code to the logic that places the texture within the viewport, plus all the windowing logic should use the borderless dimensions. Easy to do in practice if you understand it well what you want to achieve.

There is a single thing that only the much more complicated approach can emulate: some games simulated screen shaking by screwing around with the registers that control the border. Frankly, I don't care about this and don't want to complicate the whole thing just to simulate those effects worth a few seconds in total in the 0.0001% of games...

@Jkapp76
Copy link
Contributor

Jkapp76 commented Jan 28, 2023

I use the Hoxs Commodore 64 emulator and it allows you to set the border to just the right and left sides. This effectively makes your widescreen monitor the correct oldschool dimensions and gives you the colored border at the left and right sides.
This is my favorite way to keep it for C64 games.

@johnnovak
Copy link
Member Author

I use the Hoxs Commodore 64 emulator and it allows you to set the border to just the right and left sides. This effectively makes your widescreen monitor the correct oldschool dimensions and gives you the colored border at the left and right sides. This is my favorite way to keep it for C64 games.

With the outside border option you'd get exactly that in fullscreen.

@johnnovak johnnovak added the video Graphics and video related issues label Mar 24, 2023
@anikom15
Copy link

anikom15 commented May 8, 2023

Please see here for timing of the VGA card: https://courses.cs.washington.edu/courses/cse467/03wi/lab/VGA_timing_information.pdf

You can also consult the IBM technical references but I can't share those as they are still under copyright.

86Box implements overscan for all the IBM standards so they may be a good resource for understanding of where they derive their overscan amounts.

I recommend ripping the band-aid off and doing this the right way. This could mean compatibility issues with 320x200 mode due to line-doubling. The borders are added after the line doubling, which means you will need to process horizontal and vertical borders separately. The horizontal borders should be applied first, with 8 on each side for 640 width modes and 16 on each side for 320 width modes. Note that my understang is that the VGA line doubler doesn't double horizontally (86Box/86Box#3323). The line doubler should then scale the signal vertically, and then the vertical part of the border would then be added. All of this should be considered part of the emulation layer and immutable.

A presentation layer will then decide whether to crop or present the borders based on the user's preference. Ideally, this would only be done with shaders, but pratically it's acceptable to have this done in software as a config option.

Please note that you will notice the borders change the storage aspect ratio (SAR) of the signal. I'm not exactly sure how to handle this, but it's most likely that given the behavior of CRTs of the time, the 4:3 display aspect ratio (DAR) would apply to the entire visible signal, including the borders. This would mean that in 640x480 mode, the 'square' pixels are actually just a wee bit wide. Overall, it shouldn't affect much, but I think it would be a mistake to try to correct for this by making just the non-overscan area 4:3. Instead, present the entire visible image as 4:3. When cropping is applied, you will get a image that has a DAR of ~1.34.

@johnnovak
Copy link
Member Author

johnnovak commented May 9, 2023

I recommend ripping the band-aid off and doing this the right way.

Well, I don't agree with any of that. What practical benefits would this extremely hardware-accurate approach bring? Some games/apps use the border to convey information, that's all. It's nice to be able to see that, because we currently don't have that visual feedback. We don't want to make this more complex than that—not even DOSBox-X does that...

Implementing a super-accurate-down-to-the-cycle level VGA emulation is not the goal of DOSBox Staging by far. This would add a lot of unnecessary complexity for the code for no good reason, and it would be not in line with our goals. Please see our goals here: https://dosbox-staging.github.io/about/

Screwing around with the ideal 4:3 aspect ratio because of the width of the border... Man, no one cares for that stuff. While technically the border size might affect the actual image size/aspect ratio a little bit, it's a moot point. Tell me one example where the app/game uses some extreme border settings which would substantially alter the perceived aspect ratio, and that was actually the intent of the developer. I would bet good money on it that the number of such programs is exactly zero!

To be clear, we are uninterested in adding 3x as much code to the graphics card emulation just that stuff like Area 5150 and similar demos can run (and note these usually require some very specific hardware and refuse to run on anything else because of all the low-level, bang-the-hardware-directly hackery... but they're cool, no doubt... if you can make them run at all 😎). The main goal of the project is to be able to run all DOS era games and a good chunk of application software, that's about it.

@kcgen
Copy link
Member

kcgen commented May 9, 2023

@anikom15,
I recommend ripping the band-aid off and doing this the right way.

The DOSBox's Team's VGA draw code is compatible with over 10,000 DOS games spanning 25+ years of hardware evolution - more so than any piece of original PC hardware in existence (https://gona.mactar.hu/DOS_TESTS/); so it's pretty hard for me to sit back in an armchair and claim that "DOSBox is doing it wrong".

With SDL now providing a hardware-accelerated "flexible" canvas, there is no doubt logic and software-scaling methods that can be eliminated from vga_draw and render.cpp, and might ease the addition of the border ovescan colour.

Is that something you're interested in digging into? Touching this code while maintaining performance.. without regressions.. without (further) complexifying the code.. running countless regression tests, is a tall order!

My own effort in combining vgadraw with svga_* is itself a partial failure.. I've introduced regressions and the code isn't any better off! (and I'm feeling like Brundle Fly after climbing out from this experience).

There's a lot of room for improvement, but there's a huge amount of risk for failure and regression. I appreciate the confidence, and yes - like @johnnovak said, if you can refactor it and make it all run: then my hats off to you! Join the effort and dig in 🍝

@johnnovak
Copy link
Member Author

There's a lot of room for improvement, but there's a huge amount of risk for failure and regression. I appreciate the confidence, and yes - like @johnnovak said, if you can refactor it and make it all run: then my hats off to you! Join the effort and dig in 🍝

Yeah, please clean up and simplify the graphics card emulation code @anikom15, plus add the border stuff in a clean way, while not breaking any of the 10k+ supported games. If you can do it, we'll merge it in, of course!

But screwing around with the 1:1 PAR in modes that are supposed be 1:1 PAR is a bit crazy... I personally won't have any of that.

@anikom15
Copy link

anikom15 commented May 9, 2023

Yes, I'll look into it. The trouble is the 320x200 modes, simply because the border in those modes uses effective half-pixels. If that wasn't the case, the borders could be added in one go.

Thankfully DOSBox already seems to handle the 320x200 modes in a flexible way.

Also, don't rely on 86Box. The border sizes they use aren't always correct. I'd appreciate if anyone could share documentation on the signal timings of the various standards. I have CGA and VGA. I need EGA. I'm sure PCjr and Tandy had their own borders. MDA and Hercules probably have an invisible border as well but that's not as important.

Also, I apologize for my tone. I did not mean to suggest a hardware-accurate method (whatever that actually means) was required. I was just trying to offer a way to avoid the potential stumbling block of having to deal with the half-pixel borders in 320x200 mode.

@johnnovak
Copy link
Member Author

johnnovak commented May 11, 2023

I'm honestly curious (no irony) where would the "correct" border implementation have any tangible benefits over the proposed simplified approach where you just pick any border width you like, basically? Can you list those programs? In my experience, very few programs use the border colour to convey some information, and for those that do, it does not matter one bit whether the border is 5px, 10px, 20px, or whatever else. Overall, probably 1% of all programs use the border in any meaningful way, making this a rather niche and quite low priority feature anyway.

@anikom15
Copy link

Correctness is a tangible benefit to me in itself, personally.

But also it's a way to document history. Unfortunately, I have seen many resources, once easily found across the internet, disappear quietly during the past several years. You've probably seen this video of a guy building a video 'card' on a breadboard: https://www.youtube.com/watch?v=l7rce6IQDWs . He wouldn't have been able to build that without a reference on the timing, or at least it would have been a lot more difficult for him to figure it out.

@johnnovak
Copy link
Member Author

johnnovak commented May 11, 2023

Note hardware accuracy and "emulation as documenting hardware accurately" is not among our goals, by far. It was never among the goals of the original DOSBox either. Have you ever seen the VGA code? 😎

He wouldn't have been able to build that without a reference on the timing, or at least it would have been a lot more difficult for him to figure it out.

I appreciate that, and I want that information to be preserved, but this is not the project for that. Some people are writing cycle accurate PC emulators out there, forgot the name. Know what? It can barely emulate anything faster than a 5150 on current hardware with full cycle-accuracy... It's just not practical.

I'm all about preservation too, but I think you're barking up the wrong tree here. In DOSBox Staging it's always a balance between accuracy and tangible benefits to the end user, which is 99% of the time people playing old DOS games. Not developing new software, not trying to make the hardware do never-before-seen-things (that would require an 1:1 FPGA re-creation of the hardware anyway), not even running demos that do low-level trickery correctly. Main focus is playing games, that's about it. Getting most apps to work correctly is a bonus we get "for free", kinda.

Accurate sound emulation that affects all games and audio being a very important, crucial part of the gaming experience—yes, by all means! Accurate border emulation where the "accurate" border is not an essential part of the experience, and less than 1% of games use the border meaningfully for anything, while complicating the already complicated-beyond-imagination graphics card code (note it's not "accurate" emulation of CGA/EGA/VGA/etc.—it's a mismash of all things that does all gfx card emulation in "one spot"... very messy, and by no means an "accurate representation of hardware". It's "accurate" enough to run 99% of all games correctly, and that's it, it was never any different in DOSBox land).

I'm asking again, do you have a list of games where the accurate border emulation makes a big difference for the average user who's not super into accurate border? 😄 Please share the list of games and appications. I've listed some apps at the top where the border conveys info—the "simplified" border emulation I proposed would take care of those. As far as I see it, that approach is sufficient and hits the right balance between complexity, effort, and benefit.

Specifically, from our about page, please familiarise yourself with the goals of the project before raising similar tickets and comments (and read the full thing; the non-goals section refers to the "above goals" in the "goals" section): https://dosbox-staging.github.io/about/

image

@johnnovak
Copy link
Member Author

johnnovak commented May 11, 2023

Having said all that, by all means dig in, and show us how it's done in an easy, accurate, and maintainable way. It has to work correctly in windowed and fullscreen, with shaders, in the texture output mode, handling borders in the screenshots need to be worked out, etc. Looking forward to your design and PR, and if it's great, we'll sure as hell merge it!

Otherwise, I'll do as planned.

@anikom15
Copy link

Thanks for the feedback. I never suggested a 'cycle-accurate' approach though (cycle accuracy is in itself a myth, but I digress).

@johnnovak
Copy link
Member Author

cycle accuracy is in itself a myth, but I digress

Yes and no. It makes a lot of sense and it's practically mandatory to accurately emulate the timings of the various custom chips in many more advanced home computers, e.g. the Commodore Amiga and the Commodore 64. Without that most games and demos would simply not run. Same for cycle-accurate, flux level floppy emulation; it's mandatory for emulating many copy protection mechanisms.

It's more like a sliding scale, and the PC architecture is a lot less advanced and much more decoupled, making a much looser and less accurate approach feasible. But you're right, I always smirk when people say "cycle-accurate" in conjunction with PCem or 86box. They're far from it in all aspects of the emulation, but of course they emulate a whole PC more accurately than the really loose and abstract DOSBox approach with "adaptive, selective" accuracy of specific areas (OPL -- cycle accurate; MT-32 emu -- 95%+ as hardware; VGA -- quite loose but most stuff works; CPU -- emulating a very abstract generic x86 CPU with a very simple MIPS approximation, etc.)

Then there are the Macintosh emulators which are really simple as there was barely any hardware in those early machines :trollface:

@johnnovak
Copy link
Member Author

Unfortunately, I have seen many resources, once easily found across the internet, disappear quietly during the past several years.

By the way @anikom15 if you care about preserving such info, register an archive.org account and start uploading mirrors/dumps of existing sites that contain useful info about retro computing. I'm doing the same, just I'm limited by available time...

@dbalsom
Copy link

dbalsom commented Dec 28, 2023

I would suggest not thinking about the border as something separate - rather think of a display field in totality, and the portion we display being some "aperture" into that field. So for CGA, for example, the entire display field is 912x262 (* ~60Hz = ~14Mhz)

We can see as much or as little border as we please by defining an aperture into this field, in this case, I chose a value of 704x224 as the "accurate" setting, just based on aesthetics and a few photographs of scenes on IBM 5153 monitors. It need not be perfect, every monitor was adjusted differently and the picture was not always centered well.

cga_aperture

The essential bit is not trying to choose a resolution from CRTC registers. The resolution is derived from the dot clock, which is fixed on MDA/CGA, one of two values on EGA/VGA, potentially more on SuperVGA, etc. But the basic method is adaptable to any raster device, although at times your "screen-accurate" aperture may be larger than the field in which case you would pad with the overscan color or black.

@FeralChild64
Copy link
Collaborator

It will have to be considered as something at least partially separate, or else the seamless mouse integration won’t work properly - there will be a strange effect when moving mouse in/out of the screen drawing area.

@johnnovak
Copy link
Member Author

johnnovak commented Dec 28, 2023

I would suggest not thinking about the border as something separate - rather think of a display field in totality, and the portion we display being some "aperture" into that field. So for CGA, for example, the entire display field is 912x262 (* ~60Hz = ~14Mhz)

Thanks for stopping by @dbalsom. I'm aware of your interests in accurate emulation, but I need to consider practicality here and how this would work together (not against) our existing features. Which includes:

  • integer scaling
  • custom viewport resolutions
  • seamless mouse support
  • screenshots
  • video capturing
  • etc.

At the end of the day, sure we could implement anything, accurate borders to the max etc... but to what end? Some programs convey important info in the border, and that's it. I'm not interested in doing this 100% accurately, just doing the bare minimum that conveys this info in good old DOSBox tradition 😎 Not interested in those 5 games either that use border tricks to shake the screen, etc. People can live without that, and the code will be much simpler 😄

I definitely do want to implement this as something "tacked-on" at the end of the frame render time. Literally like this in pseudo-code:

  1. Render frame normally
  2. Check current border colour
  3. Extend frame by configured border amount, fill it with colour.
  4. Present extended frame. In some border modes, the border can be partially or fully outside of the screen.

We most definitely won't suddenly treat say 640x480 as 645x485 (just making things up and assuming a 5px border). Like I said, I don't want to complicate the code and punish everybody with complexity and weird display resolutions just because 10-20 games use the border for something interesting... It's gonna be literally "tacked-on" at the end of render time, so the mouse code won't be affected either @FeralChild64 then way I'm gonna do this.

I just wanna hit the sweet spot here between complexity and usefulness. 😄 We could and probably should do somewhat "accureate" border sizes, so larger for CGA and paper-thing for VGA, etc. Those would be hardcoded and auto-chosen based on the current video mode & graphics standard with border = auto or something, just like the auto CRT shaders. Then fussy people could override it per-game if they want.

EDIT: Read your post again and maybe I misunderstood it and you're saying to "seed" this padding from the dot clock values? I guess that's an option, although as those are fixed per graphics standard anyway, we can just use table of percentage/pixel values anyway, I guess.

@kcgen
Copy link
Member

kcgen commented Dec 29, 2023

Thanks for the very understandable explanation, @dbalsom; and we couldn't ask for more battle-hardened insights!

@johnnovak, my guess is that treating the display as a 2D field that's scanned over at the given MHz rate would mean that line timings would become a lot more accurate, and perhaps some simplicity would fall out of this approach, too.

No doubt this change would ripple outward (having to handle or expect a large outbound frame at the rendering, shader, mouse-edges, etc), but my guess this would have to be parameterized (perhaps more so than it currently is).

Just thinking about possibilities.

@johnnovak
Copy link
Member Author

@johnnovak, my guess is that treating the display as a 2D field that's scanned over at the given MHz rate would mean that line timings would become a lot more accurate, and perhaps some simplicity would fall out of this approach, too.

The current code must do that, otherwise thing would misbehave, I'm pretty sure. It's just only the visible part is rendered.

@dbalsom
Copy link

dbalsom commented Dec 29, 2023

@johnnovak, my guess is that treating the display as a 2D field that's scanned over at the given MHz rate would mean that line timings would become a lot more accurate, and perhaps some simplicity would fall out of this approach, too.

There is a surprising elegance to it. Whether you render as an indexed intermediate buffer or directly as RGBA, when you tick a character clock you are incrementing your buffer pointer by as many bytes as the character width (8 for CGA/EGA, 9 for MDA/VGA) or 4x for RGBA. When you reach hsync you reset your pointer position to the next scanline by the field stride, and vsync is just resetting your pointer the start of the buffer again.

Nobody needs to be punished by obscure video sizes unless they desire to see them; the 'cropped' aperture values provide the traditional, regular resolutions we are all familiar with, 640x200, 320x200, 640x480, and so on, and if nothing else, they would be safe fallbacks for features that require them. But I am not attempting to tell you how you should design things or what does or does not meet your goals - just suggesting an approach that worked for me and I think has some positive benefits besides emulation of esoterica.

@johnnovak
Copy link
Member Author

johnnovak commented Dec 29, 2023

@johnnovak, my guess is that treating the display as a 2D field that's scanned over at the given MHz rate would mean that line timings would become a lot more accurate, and perhaps some simplicity would fall out of this approach, too.

There is a surprising elegance to it. Whether you render as an indexed intermediate buffer or directly as RGBA, when you tick a character clock you are incrementing your buffer pointer by as many bytes as the character width (8 for CGA/EGA, 9 for MDA/VGA) or 4x for RGBA. When you reach hsync you reset your pointer position to the next scanline by the field stride, and vsync is just resetting your pointer the start of the buffer again.

Nobody needs to be punished by obscure video sizes unless they desire to see them; the 'cropped' aperture values provide the traditional, regular resolutions we are all familiar with, 640x200, 320x200, 640x480, and so on, and if nothing else, they would be safe fallbacks for features that require them. But I am not attempting to tell you how you should design things or what does or does not meet your goals - just suggesting an approach that worked for me and I think has some positive benefits besides emulation of esoterica.

Thanks for the clarification @dbalsom, I think I slightly misunderstood your previous post.

Yes, I think this is probably the best approach from the rendering point of view. The rest of the concerns I listed are unrelated to the pure rendering path, so my bad conflating two related but actually quite separate things 😅 I just have this "big picture" thinking by default: how does X benefit users, how does X fit into our existing set of features, how does X simplify or complicate using the software, etc... Coding comes after that. Probably the result of doing this for 20 years professionally for a living 😆

I original idea (crappy idea) was to copy the rendered image into a larger image to add the buffer. The next idea was to "oversize" the render buffer by the configured border amount, then either pre-fill that with the border colour, or insert some special logic to fill in the border colour at the start and end of lines. Well, basically what you're suggesting is a variation of that approach, so yeah, we're in agreement.

@dbalsom
Copy link

dbalsom commented Dec 29, 2023

It will have to be considered as something at least partially separate, or else the seamless mouse integration won’t work properly - there will be a strange effect when moving mouse in/out of the screen drawing area.

I'm not entirely familiar with this feature (I'm really curious how you sync up mouse deltas between guest and host...) but seems like maintaining a transformation matrix or such would not be infeasible - you could have a "display aperture" and an "input aperture" and a simple function to convert "display aperture" to "input aperture" coordinates.

@johnnovak
Copy link
Member Author

johnnovak commented Dec 29, 2023

I'm really curious how you sync up mouse deltas between guest and host...)

Well, we can't, in the general sense. Games/programs are free to do whatever, so most of the time we have no idea where the mouse pointer is. The seamless mode really only works reliably with the VMware protocol in Windows 3.x that we support as the driver can report absolute positions in that specific protocol. But you need 3rd VMware Win 3.x mouse drivers for that.

@dbalsom
Copy link

dbalsom commented Dec 29, 2023

image

here's a cleaned-up diagram I made to help illustrate the 'field buffer'. This is a standard NTSC diagram to scale, just doubled in height so it doesn't look squished. The idea is that we reset the pointer to the next scanline at the end of the blue bar, and we reset the pointer to the start of the buffer at the end of the purple one, and so on.

This moves the left overscan to the left side of the buffer, and the top overscan to the top, respectively. Then we just choose our aperture (sub-rectangle) into the field. These apertures are fixed - as the few games that actually bother to do so adjust the CRTC registers, stuff will move around in the field, but our aperture remains fixed, and the adjustments and screen-shake effects "just work". It's sort of a basic monitor emulation.

EDIT: I would be remiss not to mention that I didn't invent this technique, it is pretty standard for Commodore and Amstrad CPC emulation, I just brought it over to PC.

@Torinde

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or enhancement of existing features video Graphics and video related issues
Projects
Backlog
Suggested new features
Development

No branches or pull requests

9 participants