FunkyFr3sh / cnc-ddraw

GDI, OpenGL and Direct3D 9 re-implementation of the DirectDraw API for classic 2D games for better compatibility with Windows 2000, XP, Vista, 7, 8, 10, 11, Wine (Linux/macOS/Android) and Virtual Machines
https://discord.gg/afWXJNDDF5
MIT License
2.12k stars 143 forks source link

Feature Request: Supersampling #284

Closed eierfrucht closed 4 months ago

eierfrucht commented 5 months ago

I apologise if this idea is dumb or mathematically implausible, but still... Could it be possible to achieve an even crispier, closer to pixel-perfect look by implementing the following algorithm?

  1. Get the screen resolution, e.g. 1920x1080
  2. Get the game's viewport resolution, e.g. 1280x720
  3. Integer-scale the viewport with Nearest Neighbor to the closest possible size that's larger than the screen resolution, e.g. 2560x1440
  4. Downscale the picture to the actual screen resolution with Catmull-Rom or Lanczos

Similarly for a 800x600 viewport on a 2560x1440 screen, the integer scaling factor would be 4 (i.e. 3200x2800 before it is scaled down)

So far, when I specify resolutions larger than that of my screen in cnc-ddraw.ini, it looks like ddraw.dll simply upscales the viewport to my screen resolution instead of going above and then downscaling.

FunkyFr3sh commented 5 months ago

The "sharp-bilinear.glsl" shader should be able to do that, you just have to open it in notepad and configure it the way you need

I think it works like this (2x scale)

define SHARP_BILINEAR_PRE_SCALE 2.0

I would probably rather use lanczos2-sharp.glsl instead though

Edit: make sure you check "shader-package.zip" in your shaders folder, that's where the additional shaders are

eierfrucht commented 5 months ago

sharp-bilinear already has these lines:

pragma parameter SHARP_BILINEAR_PRE_SCALE "Sharp Bilinear Prescale" 4.0 1.0 10.0 1.0

pragma parameter AUTO_PRESCALE "Automatic Prescale" 1.0 0.0 1.0 1.0

FunkyFr3sh commented 5 months ago

could be that you have to modify these, i'm not sure. You'll have to try it

Note: I'm not the creator of these shaders, they're made by the libretro community

eierfrucht commented 5 months ago

Yeah I googled libretro up, the initial "4.0" seems to be the prescale factor.

A quick test on a fan project already powered by a built-in cnc-ddraw: https://github.com/RedChimera/IWD2EE

Viewport is set to 1366x768 (original game assets are 768p), screen resolution is 1920x768.

No upscaling: https://i.postimg.cc/nc5w79bH/Original-1366x768.png

Photoshop 4X Nearest Neighbor Upscale + Bicubic Downscale: https://i.postimg.cc/QMqb6HPK/Photoshop-1920x1080.png

Bilinear-sharp at 4.0 pre-scale factor: https://i.postimg.cc/8cxBFWF9/Sharp-Bilinear-1920x1080.png

Lanczos-sharp: https://i.postimg.cc/wB95s9vq/Lanczos-Sharp-1920x1080.png

For some reason, what I did in Photoshop is both sharper and lacks the tiny distortions around the letters.

Lanczos-sharp is almost as good, however. Nearly identical.

Thank you for the insight.

FunkyFr3sh commented 5 months ago

It may look worse because of the bilinear downscale. But yeah, lanczos2-sharp is great. I think it looks even slightly better than your Photoshop version.

You can also tweak lanczos2, it does have a few variables (compare it to the jinc2 shaders, those are the same shader with different settings)

eierfrucht commented 5 months ago

My only issue with lanczos is that it tends to find and accentuate hard edges where the source material clearly calls for a smooth gradient. This mainly affects the anti-aliased fonts where the characters' smoothed edges aren't algorythmic in nature but rather are pre-rendered raster stuff made for the original low resolution like 600p or 768p. I remember playing those games on a CRT display: it looked decent but not stellar because naturally at resolutions like that, rasterized anti-aliased fonts can only get you this far. Lanczos has a hard time figuring out how to deal with smoothed edges put against a high-contrast background, which naturally applies to text but hardly to anything else. Honestly I expected Photoshop to perform much better until I tried it out.

Following your advice, I just tried https://forums.libretro.com/t/jinc-2-fixes/21146 an am very impressed with the output of the modified jinc2.glsl: https://i.postimg.cc/Zqvjyr6Z/jinc2-modified-1080p.jpg

It handles the fonts better than any other shader. It also does a fantastic job of blending the light dithering resulting from the 16bpp palette while preserving the hard dithering where it's more like part of the artistic method, and dithering-like effects that aren't actually dithering:

https://i.postimg.cc/q7ZRW3Ft/jinc2-modified-dithering.png

The part of the character obscured by the building is checkerboard-dithered in vanilla, jinc2-modified turns it into what essentially appears as true blended transparency. At the same time the straw roofs and the snow on them neither get blurred into a slush nor get absurd amounts of sharpness between the contrasting groups of pixels.

Same goes to the snowy cliffs: https://i.postimg.cc/wB9L9LF0/jinc2-snowy-rock-1080p.png

Vanilla lanczos and lanczos-sharper do a much poorer job handling the cracks, tiny deep shadows and the snow, they look tampered unlike with jinc2-modified.

Jinc2-sharp-modified and its even "sharper" versions give the picture an annoying "deep fried" quality: https://i.postimg.cc/MKbr5bcY/jinc2-sharper.png

This looks pretty much like what was annoying me about vanilla lanczos... must be some isolated parameter dealing specifically with sharpening.

Heroes of Might and Magic III is another game that has very difficult-to-process source material: its different assets were created in a number of contrasting styles. Some sprites look like Doom 64 era play-doh action figures, others have intentional dithering baked into them, yet others are 3D renders manually post-processed into outright pixel art: https://mightandmagic.fandom.com/wiki/List_of_adventure_map_structures_in_Heroes_III This is further complicated in fan-made expansions like Horn of the Abyss which with its extra objects introduces a very consistent graphics style that essentially is an elaborate middle ground between the plastic looks and hard-dithered, hand-enhanced quasi pixel art stuff: a yet another layer in a lot of visually clashing stuff.

The universally accepted HD resolution mod offers a number of stretch filters and even scaling algorithms like Super2XSai that you can freely play with and combine. Luckily it also allows to hand over the entire scaling routine to a third party ddraw.dll, limiting itself just to adapting the viewport for widescreen resolutions. I'm amazed how well jinc2-modified handles the hodge-podge graphics of H3:

https://i.postimg.cc/9M1Fhqz5/Homm3.jpg

The tiny creature sprites are by design oversmoothed and have never looked great, but with jinc2-modified they appear even better than in vanilla on CRT. At the same time, the trees and rocks don't get oversharpened. Even the 3D windmill with heaps of hand edits, one of the weirdest mixed-style assets in the game, stands unbutchered. This looks truly fantastic. The dithering on the sulfur piles is at the same time carefully preserved: it still looks like powder, not goo nor a bunch of super sharp contrasting pixels caught in a gangbang.

There are at least four different graphics styles in this combat screen (background, statics, creatures and UI are all done differently) but all look great: https://i.postimg.cc/cCHDDPVy/combat-screen.png

For comparison, this is lanczos + super2xsai provided by the optional built-in OpenGL wrapper by Verok:

https://i.postimg.cc/PxL8CbcH/lanczos-super2x-sai.png https://i.postimg.cc/50v2csTX/lanczos-super2x-sai-combat.png

Super2xSai before lanczos is indeed helpful (contrary to what one might expect because smart scaling filters tend to be finicky and too sensitive not just to the source graphics but also to how the viewport resolution relates to the screen resolution, and these two inevitably differ between the various games and monitors) and does make the picture look less screwed but... there's still a lot of stuff that looks off: the tiny trees and rocks on the adventure map as well as the lava running down the far-away volcanoes in the combat screen. None of that with jinc2-modified -- with zero need for a first-pass smart scaling filter.

I honestly think jinc2-modified (the base, not sharp or sharper versions) should be made one of the default shaders shipped with cnc-ddraw. I'm awed how smartly it tells between the dithering that's meant to stay and the dithering that's better off blended, and the high-contrast pixel-art style areas that shouldn't have the contrast boosted any further beyond vanilla. This has huge potential with titles like Arcanum that use different rendering styles for different groups of assets, especially the UI and its oftentimes fancy fonts. This clashing of art styles is a common thing in old 2D games, but you barely notice it... until it gets all run over and back again by a not-so-smart stretch filter.

FunkyFr3sh commented 4 months ago

Just been playing around with the jinc2-modified you found and it's really awesome, does look nice with warcraft 2 too (especially the upscaled checkerboard FOW)

Catmull rom 832x480 upscaled to 1280x720 catmull-upscale-832

jinc2-modified 832x480 upscaled to 1280x720 jinc-upscale-832

Will include it on the next update (not going to make it the default though, it's a lot slower than catmull-rom and will not work on 10+ year old hardware)

eierfrucht commented 4 months ago

Hmmm I have a machine from circa 2008 that still can handle this... What's the exact roadblock?

Why bother about performance if the GLSL code is run (mostly) on the GPU that otherwise sits unused in these old games?

FunkyFr3sh commented 4 months ago

Hmmm I have a machine from circa 2008 that still can handle this... What's the exact roadblock?

Why bother about performance if the GLSL code is run (mostly) on the GPU that otherwise sits unused in these old games?

I tried these jinc2/lanczos shaders some time ago on two low end setups and they didn't manage to reach 60fps (not even at 1366x768). It were old intel IGPU's (10+ year old desktop and 8+ year old laptop).

For now I prefer to have the best compatibility with the default settings. I may change my opinion about it one day though (I would like to have borderless enabled by default too, but it doesn't work with all games either)

eierfrucht commented 4 months ago

Sounds weird, 10 year old Intel GPUs should be handling this code quite well... unless they are mistakenly assuming the lowest possible power state in between the batches of calls? Could be a driver configuration issue, could be a rogue BIOS setting. My 2012 mobo has an EFI setting to enable/disable iGPU power saving (Intel HD2000).

BTW infinity engine games (as well as a lot of other old games) are locked at 30 fps, others are meant to be run at around 30 and cause CPU / GPU overload if left uncapped (HoMM1, HoMM2, HoMM4, partially true for HoMM3 also -- all of these have a 'cold CPU' fix in their respective modernized wrappers).

FunkyFr3sh commented 4 months ago

You could try the debug build (it does have a FPS overlay) with your HD2000, but you won't get 60FPS with it at 1366x768.

There are a lot games that run at 30 fps and lower, but there are also games that run at 60 FPS (e.g. all the old C&C games)

eierfrucht commented 4 months ago

Just tried this: https://gist.github.com/agyild/82219c545228d70c5604f865ce0b0ce5

Results:

https://i.postimg.cc/jsZq0sMY/FSR-1.jpg https://i.postimg.cc/0khsPrmF/FSR-2.jpg https://i.postimg.cc/R9nmGQxB/FSR-3.jpg https://i.postimg.cc/p2y4rR2H/FSR-4.jpg

Looks pretty sick, eh?

FunkyFr3sh commented 4 months ago

Just tried this: https://gist.github.com/agyild/82219c545228d70c5604f865ce0b0ce5

Results:

https://i.postimg.cc/jsZq0sMY/FSR-1.jpg https://i.postimg.cc/0khsPrmF/FSR-2.jpg https://i.postimg.cc/R9nmGQxB/FSR-3.jpg https://i.postimg.cc/p2y4rR2H/FSR-4.jpg

Looks pretty sick, eh?

Hm, not sure.. would need some comparisons screenshots from all the different jinc2 versions. The fog does look really bad in these though, so much I can tell already

eierfrucht commented 4 months ago

I'm ok with the checkered fog...

By the way:

// User variables - EASU

define FSR_PQ 0 // Whether the source content has PQ gamma or not. Needs to be set to the same value for both passes. 0 or 1.

define FSR_EASU_DERING 1 // If set to 0, disables deringing for a small increase in performance. 0 or 1.

define FSR_EASU_SIMPLE_ANALYSIS 0 // If set to 1, uses a simpler single-pass direction and length analysis for an increase in performance. 0 or 1.

define FSR_EASU_QUIT_EARLY 0 // If set to 1, uses bilinear filtering for non-edge pixels and skips EASU on those regions for an increase in performance. 0 or 1.

// User variables - RCAS

define SHARPNESS 0.2 // Controls the amount of sharpening. The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. 0.0 to 2.0.

define FSR_RCAS_DENOISE 1 // If set to 1, lessens the sharpening on noisy areas. Can be disabled for better performance. 0 or 1.

define FSR_PQ 0 // Whether the source content has PQ gamma or not. Needs to be set to the same value for both passes. 0 or 1.

FunkyFr3sh commented 4 months ago

I'm ok with the checkered fog...

By the way:

// User variables - EASU #define FSR_PQ 0 // Whether the source content has PQ gamma or not. Needs to be set to the same value for both passes. 0 or 1. #define FSR_EASU_DERING 1 // If set to 0, disables deringing for a small increase in performance. 0 or 1. #define FSR_EASU_SIMPLE_ANALYSIS 0 // If set to 1, uses a simpler single-pass direction and length analysis for an increase in performance. 0 or 1. #define FSR_EASU_QUIT_EARLY 0 // If set to 1, uses bilinear filtering for non-edge pixels and skips EASU on those regions for an increase in performance. 0 or 1.

// User variables - RCAS #define SHARPNESS 0.2 // Controls the amount of sharpening. The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. 0.0 to 2.0. #define FSR_RCAS_DENOISE 1 // If set to 1, lessens the sharpening on noisy areas. Can be disabled for better performance. 0 or 1. #define FSR_PQ 0 // Whether the source content has PQ gamma or not. Needs to be set to the same value for both passes. 0 or 1.

Just tested and that shader isn't working, it's not a valid format. When you use a shader with a bad format cnc-ddraw will just use the default shader (catmull-rom-bilinear)

eierfrucht commented 4 months ago

This seems to be true (screenshots turn out pixel-perfect the same) but... at the time of taking those I didn't even have catmull-rom-bilinear.glsl in my Shaders directory!

FunkyFr3sh commented 4 months ago

This seems to be true (screenshots turn out pixel-perfect the same) but... at the time of taking those I didn't even have catmull-rom-bilinear.glsl in my Shaders directory!

Some shaders are built-in, if you delete the shaders folder and start the config tool you'll see which shaders are inlcuded inside of ddraw.dll

eierfrucht commented 4 months ago

Oh! Makes sense. Thanks for pointing out.