joncampbell123 / dosbox-x

DOSBox-X fork of the DOSBox project
GNU General Public License v2.0
2.78k stars 381 forks source link

Option to maintain aspect ratio when stretching the screen (Square pixel stretching) #2437

Closed henk717 closed 2 years ago

henk717 commented 3 years ago

As someone who has a 21:9 ultrawide monitor it seems i currently do not have a way to conveniently and reliably get content displayed on the screen in the aspect ratio's i would expect. The fit to aspect ratio option will automatically correct the scaling when the application goes fullscreen, but this also forces a 4:3 aspect ratio that would not happen on these monitors if i use my real retro hardware. In the older DOSBox-Daum fork i can use the fullscreen size set to original which combined with the DirectX mode would then change the monitors resolution to the correct aspect ratio. Daum is quite old, and especially on Windows 98 emulation where i am constantly switching between different aspect ratio's this is very apparent in Dosbox-X.

86Box has a different solution to this problem i think would be appropriate for this fork, they have different full screen scaling modes selectable so you can pick if you want a full stretch like DOSBox-X does. The 4:3 aspect ratio stretch that we get with the aspect=true option, but also a square pixel scaling option that produces the result i am looking for since it accurately mimics the natural behavior of the monitors i own.

So in order to get the desired aspect ratio on my system i'd either need a return of the option to change the native monitor's resolution, or the option to stretch maintaining square pixels so we automatically use the native aspect ratio's of the content emulated. Because of the cross platform nature of the fork i assume the last one is the most suitable.

I hope you will consider adding this option to the fork, since in every other aspect Dosbox-X has surpassed DOSBox-Daum but for fullscreen gaming i keep having to keep going back to the much older fork or 86Box which does not offer the same performance.

Wengier commented 3 years ago

Thanks for your feedback. Have you tried DOSBox-X's pixel-perfect output option (i.e. output=openglpp) so that pixels will be perfect squares? With this output it tries to fit the image into 4:3 with aspect=true and keeps square pixels with aspect=false. DOSBox Daum has never supported such output of course. @ant-222 also recommends to set doublescan=false when using this output option in DOSBox-X. Moreover, you can switch the current output dynamically from the menu ("Video" => "Output"; select “OpenGL perfect” for the openglpp output).

henk717 commented 3 years ago

Did some further testing, the reason it initially did not appear to work is because the pixelperfect scaling only does pixel perfect integer scaling. So on a smaller screen like the laptop screen i originally tried this with it has no impact and the view will be centered, on larger monitors it will be a noticeable difference but still not fully utilizing the screen as a native resolution change would (This is the same as 86Box's integer scaling option).

The square pixel stretching is different (Definitely give it a try in the 86Box emulator if you want to get a feel for it) this will produce the same blurry result that monitor scaling will produce. So stretching the image out like the original OpenGL renderer will do in a way closer to the Fit to aspect ratio option. But instead of forcing 4:3 you'd force the pixels to remain square and gradually change in size so that it automatically creates borders around incorrect aspect ratio's so the end result matches with the way the content is trying to display (Which can also mean a 16:10 game on a 16:9 resolution leaving small borders). This could be a seperate button in the menu, or you could change the Fit to aspect ratio to a menu which would then have a 4:3 or a original aspect ratio option to nicely implement this.

So to me these are two different use cases, Pixel Perfect integer scaling for people who desire having the game look correct at the largest resolution possible without the resolutions in between and are then guaranteed the largest correct sharp image. And the maintain aspect ratio for those who want it to look like a monitor stretching it out while maintaining the aspect ratio as my retro PC will do for its DVI signal and my monitor can do for the VGA signal to some degree. This would also work in windowed mode which means a streamer could set the intended stream resolution in the window and turn off the automatic resizing, after which all content is displayed in the correct aspect ratio even if he is doing something like a dos marathon stream where he will rapidly change between games, with or without dos based menu's (For example a shareware disc).

Because it would in theory function similar to the already existing Fit to aspect ratio option i expect this would then also work in the original OpenGL and DirectX modes.

ant-222 commented 3 years ago

First of all, I do not think it is a good idea to treat fullscreen and windowed modes differently, unless one has a CRT display. If you have an LCD display, the best results are always achieved at its native resultion or a factor thereof, regardless of the specific rendering and scaling algorithm. And you are of course correct that a cross-platform solution operating at the display's native resution and stretching the image to the desired proportions is desirable.

As I understand, you propose that a new scaling mode be introduced that will stretch the emulated display interpolatively assuming square pixels. In the current version of DOSBox-X only the pixel-perfect mode is aware of square pixels, whereas the other modes behave as in the official version, forcing 4:3 with asepct=true and stretching to screen with aspect=false. I think we need a third value, such as aspect=squarepixel to do what you want.

It would be a simple modification if the code were not so clumsy as it seems to me, yet I can give it a try. Let me know what you think.

henk717 commented 3 years ago

I am indeed not advocating to treat fullscreen and window modes differently, but the issue i am describing is less of an issue in the Windowed mode since the application can just resize to match. aspect=square(pixel) would indeed be what i am proposing given this gives the desired fullscreen result and in windowed mode this would allow people to set the dosbox window to a desired size while maintaining the aspect ratio's they would expect (4:3, 16:10, 16:9, etc) completely dynamically.

What you are describing is indeed exactly what i am proposing with this feature request and how i'd imagine it being implemented, in addition to adding this to the menu by for example changing the force aspect ratio to be a menu with the several modes being presented there. And it would be different from the existing pixel perfect mode since it would allow a blurry end result if this better matches the window or fullscreen size, where the pixel perfect mode tries to match the largest sharp resolution it can.

ant-222 commented 3 years ago

@henk717:

but the issue i am describing is less of an issue in the Windowed mode since the application can just resize to match.

I think that square pixels should be handled both in fullscreen and windowed modes.

what i am proposing given this gives the desired fullscreen result and in windowed mode this would allow people to set the dosbox window to a desired size while maintaining the aspect ratio's they would expect (4:3, 16:10, 16:9, etc) completely dynamically.

Do you mean the original aspect ratios of the emulated graphical modes in terms of square pixels? If so, how can 16:9 be possible?

What you are describing is indeed exactly what i am proposing with this feature request and how i'd imagine it being implemented

I have tried:

output = opengl
aspect = false

and it already assumes square pixels. Now I am a bit confused, as I cannot reproduce the undesirable behavior that you want to change. Can you please describe in more detail how to reproduce it?—

As someone who has a 21:9 ultrawide monitor it seems i currently do not have a way to conveniently and reliably get content displayed on the screen in the aspect ratio's i would expect.

Can you post the actual results vs. what you would expect, in terms of:

  1. the emulated graphical mode,
  2. the actual dimensions of the scaled image in DOSBox,
  3. the expected dimensions of the scaled image in DOSBox.
ant-222 commented 3 years ago

Here is a screenshot of a DOSBox window running Prince of Perdia on my 1600x1200 monitor with the following settings:

windowresolution    = 1120x1000
output = opengl
aspect = false

As you can see, the emulated display size is 1120x700, which corresponds to square pixels at a scale of 3.5. And here is what it looks like in fullscreen mode. Now the emulated display is 1600x1000, that is square pixels magnified by a factor of 5.0. Why is not what you ask for, @henk717?

ant-222 commented 3 years ago

I just noticed that I performed the tests above with another version of DOSBox, not with DOSBox-X. As you can see, it interprets aspect = false as a command to assume square pixels, which is exactly what @henk717 needs. With DOXBox-X, however, the same test stretches the emulated display to the whole window or to the entire monitor. What about reverting the breaking change and restoring the logical behavior of the original DOSBox:

aspect=true  => enforce 4:3
aspect=false => assume square pixels 

instead of implementing a third aspect setting, for I think this is a case where the official DOSBox works better than DOSBox-X.

Edit:
@henk717, can you please try the original DOSBox with

output = opengl
aspect = false

to see if it does what you want (square pixels)?

henk717 commented 3 years ago

With the original dosbox (Or the daum fork i use) opengl with fullscreensize=desktop will produce the desirable result, but Direct3D for example will not. Its very specific to the OpenGL implementation and only that renderer. The reason i am suggesting to do this with an aspect scaler instead of reverting OpenGL is that i assume this would be a better fit for this fork, because here we are dealing with multiple different platforms (SDL1, SDL2, native UI, rendering a toolbar, multiple operating systems, etc) and the scaler implementation would produce a more universal fit.

The reason i expect we lost this behavior on the OpenGL renderer was because of that modernization this fork has gone under. So in my eyes aspect=square makes more sense (could even be the default) than patching only one display mode to have this behavior again since you can then profit from this on all the other display modes as well including on platforms like MS-DOS that may not support the OpenGL rendering mode.

ant-222 commented 3 years ago

I agree with you, @henk717: the basic scaling modes should be universal and independent on specific renderers. I will soon try to implement it with some of the renderers that work on my Windows XP.

ant-222 commented 3 years ago

Adding aspect=squarepix is harder than I thought. I tried to restore the square-pixel mode by re-declaring Render_t.aspect as a enum with three values:

typedef enum {ASP_SQUARE, ASP_CORRECT, ASP_STRETCH} aspect_t;

but it didn't work because the current aspect setting in DOSBox-X is broken. Instead of two clearly understandable values that it takes in the official DOSBox, DOSBox-X allows the follwing values:

false,   true,
0,       1,
yes,     no,
nearest, bilinear

which means that it mixes three (!) ways of specifying a boolean value with two interpolation methods: nearest-neighbor and bilinear. This is clearly too much for a single setting: alternative methods to specify a boolean create confusion, whereas interpolation methods add a second dimention, violating the principle of single responsibility.

As I understand, the interpolation methods are related to something called postrenderer aspect, but it is not mentioned on the WIKI. This postrenderer aspect seems related to the xBRZ scaler, but in output_surface.cpp it is used once in the invocation of xBRZ_PostScale() outside the actual xBRZ handler. Does anyone know why?

Although the possible aspect values are defined in ASPECT_MODES enumeration in render.h, the actual value, Render_t.aspect, is an int, which is treated as a boolan elsewhere, e.g. in function AspectRatio_mapper_shortcut() from sdlmain.cpp, which inverts this flat. How is it supposed to work—will it not disregard all the values except zero and unity?

That said, which is the better way of adding the square-pixel mode:

  1. by adding a value to the existing enumeration ASPECT_MODES,
  2. by extracting interpolation methods from ASPECT_MODES into a separate enum and configuration setting, to avoid confustion with the actual aspect ratio?
Wengier commented 3 years ago

I think the "aspect" config option can be extended to make clear distinction between stretch and square pixel options, as well as a fixed ratio option. The fixed ratio option will be default to 4:3, but may be changed with another config option e.g. aspect_ratio. If possible, you can certainly try add a value to the existing enumeration ASPECT_MODES, and the function for inverting aspect can be changed to something like (Render_t.aspect+1)%3. 0 means aspect=false (which perhaps refers to square pixel mode for compatibility with other DOSBox versions), whereas 1 and 2 (within the enumeration) mean fixed-ratio mode (aspect=true) and stretch mode (or perhaps square pixel mode if aspect=false refers to stretch mode).

henk717 commented 2 years ago

Tried the current build and the desired option is implemented. I am able to force the aspect ratio while also being able to select the original aspect ratio resulting in the desired behavior.

I will close this since this issue was likely missed when it got succesfully implemented.