crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come)
-
@riverstorm said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
Ok, I think I am square now! ;)
Can you explain one last thing. I don't quite understand the comment below. If it was a real 4:3 monitor wouldn't the dimensions be 224 x 384 scaled with whole numbers because you're actually running a real 4:3 monitor? Why the change from 384 to 298?
This is the pixel height if we had a real 4:3 ratio. Why 298?
It's because the pixels in the original game would not have been square. But on modern displays, the pixels are square. So if we don't account for the non-squareness of the pixels, we'll end up with a game that's stretched.
To test this, you can do the following;
Edit your 1941.cfg file and put in the following values;
custom_viewport_width = "448" custom_viewport_height = "768" custom_viewport_x = "0" custom_viewport_y = "0"
Never mind that it's small and off-centre, this is just for testing purposes. You should see that the game appears vertically stretched when we do a simple 2x scaling on both axes. If instead you take the height as 298 rather than 384 and do the same 2x scaling, you'll see that it's correctly proportioned;
custom_viewport_width = "448" custom_viewport_height = "596" custom_viewport_x = "0" custom_viewport_y = "0"
So, we're not really scaling the game vertically by 2x - we're scaling it vertically by 1.55 (384 x 1.55 = 596), and that corrects for the difference in 'squareness' of the pixels between the game and the modern screen.
-
@andrewh said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
So, we're not really scaling the game vertically by 2x - we're scaling it vertically by 1.55 (384 x 1.55 = 596), and that corrects for the difference in 'squareness' of the pixels between the game and the modern screen.
Ah, nice ok, so the calculations are compensating for odd PARs. I didn't fully understand it but I think I'm on the train now. I appreciate you taking the time to explain that with the examples helped quite a lot. Kudo's to Dank for building the script. There's a bit of complexity there figuring all that out.
Not using the configs with the CRT-PI shader and you can get rainbow lines because it is not integer scaling in the dimension it needs to in order to make the shader look nicer.
So it basically it fills the screen disregarding any type of integer scaling which would makes sense for MAME. Being one of the most extensive and longest running projects on so many display types (traditional and modern) to not make assumptions on if to scale output and to what resolution.
There's a small difference. My script will always keep the viewport within the bounds of the display, whereas Dank's may in some circumstances allow it to overspill a little in order to use more screenspace.
Are you saying some games will be clipped (off the edge of the screen) vertically or horizontally?
-
my script will never overspill the edge of the screen. i thought about letting that happen to a certain tolerance but arcade games tend to have important stuff right to the edge of the image, since the cabinet CRT would be calibrated to each game, compared to console games which had to deal with varying consumer tv overscan capabilities.
-
@AndrewH There's a small difference. My script will always keep the viewport within the bounds of the display, whereas Dank's may in some circumstances allow it to overspill a little in order to use more screenspace.
Are you saying some games will be clipped (off the edge of the screen) vertically or horizontally?
This confused me a bit too. I didn't think the original script from Dankcushions would actually crop the video, but that it still uses the display max to "squeeze" the non-integer dimension it back into view. This is no worse than being under by some pixels and stretching to the display limit, it is just the opposite distortion. The key is finding which one looks better: over/compressed or under/stretched.
It looked to me that one example could allow over and compressed because it fell within the tolerance, when an under/stretch was distorted less.
-
Seems like the best script would be one that measures the distortion with each integer scale and selects the one that distorts the least. No threshold necessary.
-
maybe it overspills the x axis with 4:3 displays since i never tested for that ;) with the widescreen hdtv i made it for it has plenty of room on the X axis!
-
@caver01 said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
Seems like the best script would be one that measures the distortion with each integer scale and selects the one that distorts the least. No threshold necessary.
i don’t agree. that’s how mine used to work but i changed it to pick the wider one (within a certain low tolerance) as it looks better to stretch than to squeeze on a widescreen display (up to a point, hence the tolerance variable). if you set tolerance to 0 then it won’t do that.
-
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
i don’t agree. that’s how mine used to work but i changed it to pick the wider one (within a certain low tolerance) as it looks better to stretch than to squeeze on a widescreen display (up to a point, hence the tolerance variable). if you set tolerance to 0 then it won’t do that.
That does sound close to perfect then, as it takes into account some subjectivity while my idea (your original idea) is very rigid. However, the example above where the lower version is literally just 4 pixels smaller than perfect--it seems like it should have picked the lower, as it would be almost visually perfect, yet according to @AndrewH it picked the upper. Is the tolerance compared against the lower or the upper?
-
@caver01 said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
i don’t agree. that’s how mine used to work but i changed it to pick the wider one (within a certain low tolerance) as it looks better to stretch than to squeeze on a widescreen display (up to a point, hence the tolerance variable). if you set tolerance to 0 then it won’t do that.
That does sound close to perfect then, as it takes into account some subjectivity while my idea (your original idea) is very rigid. However, the example above where the lower version is literally just 4 pixels smaller than perfect--it seems like it should have picked the lower, as it would be almost visually perfect, yet according to @AndrewH it picked the upper. Is the tolerance compared against the lower or the upper?
you'll have to show me the/an example in my own generated .cfgs from the original post. some of the examples above were generated via an older version of python, and/or a lower resolution 4:3 screen which my script needs a lowered tolerance value to work with. IMO you shouldn't bother with scanline shaders on sub-HD screens anyway!
some of the examples were generated with an edited version of the script!!
i have now definitely spent more time explaining this script than writing it ;)
-
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
i thought about letting that happen to a certain tolerance but arcade games tend to have important stuff right to the edge of the image, since the cabinet CRT would be calibrated to each game
That was my thought. There's nothing worse than playing a game and the top half of the score is "clipped".
i have now definitely spent more time explaining this script than writing it ;)
I have to say I've learned quite a bit from the whole discussion but continuing to beat on 1941. This statement below would actually be false and not true.
There's then a check to see whether the new scale is within the tolerance
>if ((widerAspect - aspectRatio)/aspectRatio * 100) <= tolerance: viewportWidth = int(gameWidth * (scaleX + 1))
Using the figures above :
(0.9333-0.7466)/0.7466 * 100 = 25
So, the horizontal scale value of 5 gives a result that's bang on the tolerance, and therefore used, even though this gives an aspect ratio that's much further from the original.The actual value will be 25.006 (continuing on). That value would not be <= due to the fractional part so 896 should still be used?
Working with whole numbers 896 is exactly 25% less then 1120 which is exactly 224 pixels which is the original width of the game? Whether that's a coincidence or an actual percentage for tolerance I don't know.
-
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
my script will never overspill the edge of the screen. i thought about letting that happen to a certain tolerance but arcade games tend to have important stuff right to the edge of the image, since the cabinet CRT would be calibrated to each game, compared to console games which had to deal with varying consumer tv overscan capabilities.
It does, I'm afraid - for non-widescreen aspect ratios (i.e. 4:3 and 5:4).
Below is a slightly modified version of your script - the only change is that it also creates a .csv file in the directory it is run in, which has the following columns;
Name,X,Y,Orientation,Aspect1,Aspect2,ViewportWidth,ViewportHeight,HorizontalOffset,VerticalOffset
All of the games, with their original and scaled sizes will be listed.If it's run with the following command line, 361 of the 4646 games have a negative vertical offset - meaning they exceed the screen area.
python3 crt_pi_configs.py mame2003 1600 1200Running for 1280 x 1024 gives 104 games with negative vertical offset.
Setting the tolerance to 0 doesn't reduce the number of games with negative offsets.
But running it for 1920 x 1080, 1280 x 720, or 1366 x 768 gives no games with negative vertical offset.
# creates cfg files for crt-pi # params are: # * core (eg mame2003 or fbalpha) # * screen width (eg 1920) OR curvature # * screen height (eg 1080) # example usage: # python crt_pi_configs.py mame2003 1920 1080 # python crt_pi_configs.py fbalpha 1920 1080 # python crt_pi_configs.py consoles 1920 1080 # python -c "import crt_pi_configs; crt_pi_configs.createZip(False,1920,1080)" import sys import os import shutil def generateConfigs(arg1, arg2, arg3): console = False if "mame2003" in arg1: fileName = "resolution_db/mame2003.txt" coreName = "MAME 2003" elif "fbalpha" in arg1: fileName = "resolution_db/fbalpha.txt" coreName = "FB Alpha" elif "consoles" in arg1: fileName = "resolution_db/consoles.txt" console = True if "curvature" in arg2: curvature = True else: curvature = False screenWidth = int(arg2) screenHeight = int(arg3) screenAspectRatio = screenWidth / screenHeight tolerance = 25 resolution = str(screenWidth) + "x" + str(screenHeight) resolutionDbFile = open(fileName, "r" ) print("opened file {}".format(fileName)) outputFile = open(resolution + ".csv", "w") outputFile.write("Name,X,Y,Orientation,Aspect1,Aspect2,ViewportWidth,ViewportHeight,HorizontalOffset,VerticalOffset\n") for gameInfo in resolutionDbFile: # strip line breaks gameInfo = gameInfo.rstrip() # parse info gameInfo = gameInfo.split(",") gameName = gameInfo[0] gameOrientation = gameInfo[3] gameWidth = int(gameInfo[1]) gameHeight = int(gameInfo[2]) aspectRatio = int(gameInfo[9]) / int(gameInfo[10]) gameType = gameInfo[4] #integerWidth = int(gameInfo[7]) #integerHeight = int(gameInfo[8]) if console: coreName = gameName cfgFileName = gameName + ".cfg" # Create directory for cfgs, if it doesn"t already exist if curvature: path = "curvature" + "/" + coreName else: path = resolution + "/" + coreName if not os.path.isdir(path): os.makedirs (path) # create cfg file print("creating {}/{}".format(path,cfgFileName)) newCfgFile = open(path + "/" + cfgFileName, "w") if "V" in gameType: # Vector games shouldn"t use shaders, so clear it out newCfgFile.write("# Auto-generated vector .cfg\n") newCfgFile.write("# Place in /opt/retropie/configs/all/retroarch/config/{}/\n".format(coreName)) newCfgFile.write("video_shader_enable = \"false\"\n") else: if "V" in gameOrientation: if curvature: shader = "crt-pi-curvature-vertical.glslp" else: shader = "crt-pi-vertical.glslp" # flip vertical games gameWidth = int(gameInfo[2]) gameHeight = int(gameInfo[1]) elif "H" in gameOrientation: if curvature: shader = "crt-pi-curvature.glslp" else: shader = "crt-pi.glslp" newCfgFile.write("# Auto-generated {} .cfg\n".format(shader)) newCfgFile.write("# Place in /opt/retropie/configs/all/retroarch/config/{}/\n".format(coreName)) newCfgFile.write("video_shader_enable = \"true\"\n") newCfgFile.write("video_shader = \"/opt/retropie/configs/all/retroarch/shaders/{}\"\n".format(shader)) if not curvature: # if not perfectly integer scaled, we will get scaling artefacts, so let"s fix that if screenAspectRatio > aspectRatio: # games with an aspect ratio smaller than your screen should be scaled to fit vertically newCfgFile.write("# To avoid horizontal rainbow artefacts, use integer scaling for the width\n") # build list of potential aspect ratios with different integer scales aspectRatios = [] for scaleX in range(1, 99): aspectRatios.append((scaleX * gameWidth) / screenHeight) # find closest integer scale to desired ratio aspectRatios.reverse() scaleX = 98-aspectRatios.index(min(aspectRatios, key=lambda x:abs(x-aspectRatio))) viewportWidth = int(gameWidth * scaleX) if console: # consoles have overscan, so adjust viewportHeight to "Title Safe Area" if "Nestopia" in coreName: overscanV = 8 else: overscanV = 0 # build list of potential aspect ratios with different integer scales aspectRatios = [] for scaleY in range(1, 99): aspectRatios.append(viewportWidth / (scaleY * gameHeight)) # find closest integer scale to desired ratio aspectRatios.reverse() scaleY = 98-aspectRatios.index(min(aspectRatios, key=lambda x:abs(x-aspectRatio))) viewportHeight = screenHeight + (overscanV * scaleY) else: viewportHeight = screenHeight # we prefer it to be wider than narrower, so do that, according to tolerance newAspect = viewportWidth / viewportHeight if newAspect < aspectRatio: widerAspect = (gameWidth * (scaleX + 1)) / screenHeight if ((widerAspect - aspectRatio)/aspectRatio * 100) <= tolerance: viewportWidth = int(gameWidth * (scaleX + 1)) # centralise the image viewportX = int((screenWidth - viewportWidth) / 2) viewportY = int((screenHeight - viewportHeight) / 2) else: # games with an aspect ratio larger than your screen should be scaled to fit horizontally newCfgFile.write("# To avoid horizontal rainbow artefacts, use integer scaling for the height\n") # build list of potential aspect ratios with different integer scales aspectRatios = [] for scaleX in range(1, 99): aspectRatios.append(screenWidth / (scaleX * gameHeight)) # find closest integer scale to desired ratio aspectRatios.reverse() scaleY = 98-aspectRatios.index(min(aspectRatios, key=lambda x:abs(x-aspectRatio))) viewportWidth = screenWidth viewportHeight = int(gameHeight * scaleY) # centralise the image viewportX = 0 viewportY = int((screenHeight - viewportHeight) / 2) newCfgFile.write("aspect_ratio_index = \"22\"\n") newCfgFile.write("custom_viewport_width = \"{}\"\n".format(viewportWidth)) newCfgFile.write("custom_viewport_height = \"{}\"\n".format(viewportHeight)) newCfgFile.write("custom_viewport_x = \"{}\"\n".format(viewportX)) newCfgFile.write("custom_viewport_y = \"{}\"\n".format(viewportY)) outputFile.write("{},{},{},{},{},{},{},{},{},{}\n".format(gameInfo[0],gameInfo[1],gameInfo[2],gameInfo[3],gameInfo[9],gameInfo[10],viewportWidth,viewportHeight,viewportX,viewportY)) newCfgFile.close() resolutionDbFile.close() outputFile.close() def createZip(curvature=False, screenWidth=0, screenHeight=0): if curvature: outputFileName = "crt-pi-curvature_configs" path = "curvature" else: resolution = str(screenWidth) + "x" + str(screenHeight) outputFileName = "crt-pi_configs_" + resolution path = resolution outputFileName = outputFileName.replace(" ", "") outputFileName = outputFileName.lower() print("Creating zipfile {}".format(outputFileName)) shutil.make_archive(outputFileName, "zip", path) # now delete config dirs print("Deleting temp directory: {}".format(path)) shutil.rmtree(path) if __name__ == "__main__": generateConfigs(sys.argv[1], sys.argv[2], sys.argv[3])
-
Using the figures above :
(0.9333-0.7466)/0.7466 * 100 = 25
So, the horizontal scale value of 5 gives a result that's bang on the tolerance, and therefore used, even though this gives an aspect ratio that's much further from the original.The actual value will be 25.006 (continuing on). That value would not be <= due to the fractional part so 896 should still be used?
There's some rounding going on with those figures - they're there for display purposes only :-)
Working with whole numbers 896 is exactly 25% less then 1120 which is exactly 224 pixels which is the original width of the game? Whether that's a coincidence or an actual percentage for tolerance I don't know.
That's the crux of the problem - the aspect ratio using a width of 896 is slightly smaller than the original, and by coincidence the next value up is 25% bigger, which is bang on the tolerance that's set in the script by default, so it gets used even though it's quite far from the original aspect ratio.
-
@andrewh said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
There's some rounding going on with those figures - they're there for display purposes only :-)
That was kind of the point. By rounding it changed the statement from false to true? 25.006 is not <= 25 so the check is actually false and it would use the proper value of 896 which disregards the point for that example.
-
But running it for 1920 x 1080, 1280 x 720, or 1366 x 768 gives no games with negative vertical offset.
right - i didn’t test or design it for non-widescreen displays. if anyone wants to fix that please send a PR on git. i had a limited goal for the script and i reached that, but if anyone wants to add anything to it, that’s cool!
i don’t even use shaders anymore!
-
@andrewh said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
That's the crux of the problem - the aspect ratio using a width of 896 is slightly smaller than the original, and by coincidence the next value up is 25% bigger, which is bang on the tolerance that's set in the script by default, so it gets used even though it's quite far from the original aspect ratio.
Setting the tolerance to 0 doesn't reduce the number of games with negative offsets.
Can you explain the issue a little more? If tolerance of 25 or 0 yields the same results. It doesn't sound like the tolerance check is what's failing?
-
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
i had a limited goal for the script and i reached that
That will teach them for using old school monitors and true CRTs for retro gaming! Traditional vs contemporary! :)
It's about 13% of the games that will be off, hopefully none of the major ones. I imagine Andrew has the issue so well defined he can, well has, already hacked the script and fixed the issue.
-
@riverstorm said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
i had a limited goal for the script and i reached that
That will teach them for using old school monitors and true CRTs for retro gaming! Traditional vs contemporary! :)
It's about 13% of the games that will be off, hopefully none of the major ones. I imagine Andrew has the issue so well defined he can, well has, already hacked the script and fixed the issue.
it's not CRTs - if you're using a CRT you should be using native 240p or whatever so you get natural scanlines. you don't need a shader!
~4:3 monitors, sure. there appears to be an issue there. PRs welcome!
-
@dankcushions said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
~4:3 monitors, sure. there appears to be an issue there.
4:3 and 4:5 monitors so far.
PRs welcome!
Just doing a quick look through the script. That looks like a full blown programming language. You wrote that whole thing yourself? I'm impressed. I think that it would take me several weeks looking up & fighting with syntax of commands let alone concepts of the script. Writing & knowing the code intimately I take it you figure the issue is harder to solve then a quick 1 or 2 line tweak.
There's a lot of programming concepts that most (non-programmers) wouldn't understand or even make sense that require some study to understand. Maybe a quick reference guide for commands & syntax might help or I guess nowadays Google handy on the left. It's slow going when first learning a language but having any programming experience sure expedites the process as the fundamentals are the same it's just knowing the syntax.
It doesn't affect me at all so I am happy how it is too! It sounds like Caver don't use the script either so it depends if Andrew feels like forking and fixing! ;)
Actually I think Andrew would get to the heart of the issue quicker if it's worth fixing and is a hacking maniac but if he needs help with anything I will do what I can. It's been a while since I did any true programming like COBOL, RPG, Fortran, BASIC and some C when I was with the public school systems & then UPS like 20 odd years ago (mostly stuff no-one uses anymore) but the script doesn't look to bad.
-
@riverstorm I looked at the script too and I think I see where the potential issue is. There is a conditional—the place where it says that we like to expand wider more than we like to be narrower. It is here that it does the tolerance comparison that @AndrewH cited above. The first problem is that it needs an additional condition—one that checks to see if the recalculated viewport width (gameWidth * (scaleX +1)) would exceed screenWidth before proceeding. That would solve the viewport cropping.
The second issue is that it simply favors any version of the AR that is wider (within tolerance, of course), even though the narrower factor might come within a few pixels of a perfect aspect ratio. It seems like the tolerance should be compared against the narrow version, not the wide one. And yet, I still think there is an element of subjectivity here. I might even be willing to add some black bars to the border (again, within a certain tolerance) if it let me avoid heavy distortion.
I think the real benefit here was that @dankcushions realized that you can eliminate shader artifacts by forcing integer scaling in one dimension only. We always knew that full integer scaling would solve both AR and shader issues, but being able to live with some distorition in one dimension to take advantage of more screen area was a fantastic insight. But as always with our build projects, one size rarely fits all.
-
@caver01 said in crt-pi shader users - reduce scaling artifacts with these configs in lr-mame2003, lr-fbalpha, lr-nestopia (and more to come):
@riverstorm I looked at the script too and I think I see where the potential issue is. There is a conditional—the place where it says that we like to expand wider more than we like to be narrower. It is here that it does the tolerance comparison that @AndrewH cited above. The first problem is that it needs an additional condition—one that checks to see if the recalculated viewport width (gameWidth * (scaleX +1)) would exceed screenWidth before proceeding. That would solve the viewport cropping.
The second issue is that it simply favors any version of the AR that is wider (within tolerance, of course), even though the narrower factor might come within a few pixels of a perfect aspect ratio. It seems like the tolerance should be compared against the narrow version, not the wide one. And yet, I still think there is an element of subjectivity here. I might even be willing to add some black bars to the border (again, within a certain tolerance) if it let me avoid heavy distortion.
I think the real benefit here was that @dankcushions realized that you can eliminate shader artifacts by forcing integer scaling in one dimension only. We always knew that full integer scaling would solve both AR and shader issues, but being able to live with some distorition in one dimension to take advantage of more screen area was a fantastic insight. But as always with our build projects, one size rarely fits all.
I agree this is an incredible insight doing single axis scaling to correct the rainbow effects. I don't mean to be to open or blunt as I tend to be.
They way I understand it after this discussion is there's a few pieces of key information. The original DAR (resolution), the aspect ratio, orientation & target DAR (resolution).
It seems the PAR can be calculated from the resolution and aspect ratio. Similar to a TV. The PAR is used to calculate a "modified" height/width that is used to fit the target DAR. Without taking the original PAR into account some games (that aren't 1:1) are going to be off visually on a modern display with square pixels.
A quick look through the script it doesn't look like the PAR isn't taken into account at all? It's calculating scaling from 1 to 99 in both dimensions and then goes through comparing all of them looking for the best aspect ratio within the tolerance. It's very well written.
The use of the key function (lambda), data structures ( aspectRatios = []), reverse method (aspectRatios.reverse()), absolute function, etc. just seems like this is an incredibly efficient and well written script I was just wondering if it was a collaboration with someone that knows a bit of programming or just natural talent.
If @AndrewH maybe shares an example or two of negative offsets I think that will help.
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.