Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Discussion about displays and related hardware including MiSTer filters and video settings.
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

There's been a fair bit of talk lately about using custom aspect ratios with vscale_mode 1 to get integer scaling both horizontally and vertically. This works, but it's a bit of a nuisance. I've had a fiddle with the SMS core and implemented automatic horizontal integer scaling as an OSD option which can be set to round the scaling multiple to the nearest integer either above or below what the selected aspect ratio would produce with non-integer scaling. I'm not a programmer and what little I know of verilog, system verilog, and vhdl I learned trying to figure this out (and my previous modification of the SMS core), so it probably could have been done more cleanly, but my implementation seems to work, at least in my limited tests (via HDMI in video modes 0 and 8).

I changed the menu a little in SMS.sv, and the rest of the changes all took place in sys_top.v and ascal.vhd. Both of those files are in the sys section of the code, so changing them is maybe a no-no and this won't get accepted into the official core, but it also means that if my changes (maybe cleaned up a bit) are accepted into the framework then adding this feature to other cores should just require adding an item to the OSD menu.

I've made a branch to my fork of the SMS core here with this change implemented. The .rbf build is under releases as SMSHoriMenu.rbf. Anyone interested, please download it, try it out, see if I've broken anything, and let me know. Thanks.

I'll explain what I did and how. The target for this explanation is me a week ago, i.e. someone with very little idea how this stuff works. People who already know can probably skip over it. The short version is that I added code in the scaler which divides the horizontal output resolution by the horizontal input resolution. The result of that is rounded down to an integer, which I then multiply the input resolution by to get the new, integer scaled, output resolution.

In more detail, here's my code, starting with changes to SMS.sv:

Code: Select all

45      output [1:0] HORIZ_INT,	// horizontal integer scaling setting
 
149     assign HORIZ_INT = status [31:30];

183     "P1OUV,Intgr Hori Scale,Off,Narrow,Wide;",
This is the menu option and its transmission to sys_top.v. This seriously confused me for a while, but I'll talk about the mistakes I made trying to get this to work after I finish talking about how it works. Anyway, line 183 here adds an option to the Audio/Video menu of the SMS core for "Intgr Hori Scale" with options off, narrow, and wide. Changing this option changes status bits 30 and 31. Line 149 assigns the values of those bits to HORIZ_INT, and line 45 passes that information to the module (in sys_top.v) which calls this module.

In sys_top.v:

Code: Select all

320     wire[1:0]  horiz_int;

694     .hint	  (horiz_int),

1471    .HORIZ_INT(horiz_int),
Line 320 here declares a two bit wire named horiz_int. Line 1471 is in the part of the file that calls the module (emu) I changed in SMS.sv, and takes the HORIZ_INT value from there and sets horiz_int to it. Line 320 is in the part of the file that calls the scaler (ascal.vhd), and passes that value through to "hint" in there. Maybe I should have been more consistent with my names, dunno. Or at least gone with h_int rather than hint.

In ascal.vhd:

Code: Select all

214    hint    : IN std_logic_vector(1 DOWNTO 0);

1716      o_hdisp  <=hdisp; -- <ASYNC> ?
1717      IF hint(1 DOWNTO 0)="00" THEN            -- Horizontal integer scaling off
1718           o_hmin   <=hmin; -- <ASYNC> ?
1719           o_hmax   <=hmax; -- <ASYNC> ?
1720           o_hsize  <=o_hmax - o_hmin + 1;
1721      ELSE                                     --Horizontal integer scaling on
1722           IF hint(1 DOWNTO 0)="01" THEN       --Narrow (round upscaling multiple down)
1723               o_hsize <= ((hmax-hmin + 1)/(i_hmax + 1))*(i_hmax + 1);
1724           ELSE                                --Wide (round upscaling multiple up)
1725               IF ((hmax-hmin + 1)/(i_hmax + 1) +1)*(i_hmax + 1) < o_hdisp THEN
1726                    o_hsize <= ((hmax-hmin + 1)/(i_hmax + 1) +1)*(i_hmax + 1);
1727               ELSE                            --switch to narrow if image wider than total display
1728                    o_hsize <= ((hmax-hmin + 1)/(i_hmax + 1))*(i_hmax + 1);
1729               END IF;
1730           END IF;
1731           IF o_hsize = 0 THEN                 -- No integer scaling if output smaller than input
1732                o_hsize <= hmax - hmin + 1;
1733           END IF; 
1734           o_hmin <= (o_hdisp - o_hsize) / 2;
1735           o_hmax <= o_hmin + o_hsize -1;
1736      END IF;
Alright, this is the meat of it. Line 214 accepts the info from the OSD menu - does the user want horizontal integer scaling? Then 1716 through 1736 do the actual work. There are a whole lot of variables here, some of which are set by the inputs to the core, and some of which are derived from the inputs. Anything starting with o_h relates to output, and anything starting with i_h relates to input. The h is for horizontal, there's more code I didn't change that relates to vertical scaling. When sys_top.v calls the scaler module it requests an output resolution, but doesn't specify the input resolution. The scaler figures out the input resolution from the video source. hdisp, hmax and hmin are some of the output parameters requested by sys_top. hdisp is the total displayed horizontal resolution, including black areas at the edge of the screen. hmin is where the picture starts within that, and hmax is where the picture ends.

Line 1717 checks if hint has the value 00, which would indicate horizontal integer scaling is set to off. If so, it uses the normal output values as if I hadn't changed anything.

1722 checks if hint is 01, which would reflect the menu being set to Narrow integer scaling. 1723 sets the output horizontal size to integer scaling:

Code: Select all

o_hsize <= ((hmax-hmin + 1)/(i_hmax + 1))*(i_hmax + 1);
hmax-hmin + 1 is the output size for the specified aspect ratio with whatever vertical scaling is going on. These values come from sys_top, where they are derived from the aspect ratio setting. I didn't change any of that. +1 is because the numbers start from zero. For example, if I had full screen scaling on in video_mode 8, hmin would be 0, and hmax would be 1919, and the width 1920.

i_hmax + 1 is the size of the horizontal input. i_hmin will always be 0 (I think), so there's no need to subtract it to get the width, but again I need to add one.

Dividing the horizontal output by the horizontal input gives me the horizontal multiplier. Because these are integers, the result ignores the remainder, so 6.782 becomes 6, 4.2 becomes 4, and so on. That result is then multiplied by the input horizontal resolution to get the new horizontal output size. Simple!

Line 1724's ELSE covers the third menu option. If horizontal integer scaling is on and not set to narrow, the only other option is wide. The formula here is the same as for the narrow scaling, but I add one to the initial division result so that 6.782 becomes 7 and 4.2 becomes 5 and so on. This is slightly complicated by the possibility that the result will be wider than the screen, which I think resulted in no picture in one of my test builds, so I've put in a check so that if the result is too wide it'll revert to narrow scaling, which should never exceed the screen width (I think). It probably would have been more efficient to declare a variable, set the result of the wide calculation to it, and then check that variable against the display size and use it to set the output size rather than do the same calculation twice on subsequent lines, but as I mentioned I'm not a programmer.

Lines 1731 through 1733 are intended to protect against an output resolution which is narrower than the input screwing up the results. Dividing a smaller output by a larger input would give a result below 1 which would be rounded down to zero, resulting in an o_hsize of 0. I'm not sure if this part of the code is actually working, though. The output horizontal resolution seems to bottom out at the width of the OSD. Further evaluation required.

Lines 1734 and 1735 set where the image starts and stops on the screen. To get it centred, you want to halve the difference between the size of the image and the total display size, and start drawing there. Then add the total width to that value to get the stopping point. I had to subtract one from that value because the width is a number that starts from one, but the position starts from zero. I think that's why. In reality, I did it because turning integer scaling on without subtracting one here added an extra column of pixels to the right side of the image that shouldn't have been there.

And that's it! It's pretty simple, and probably could have been done more cleanly, but it's working. Aside from maybe the fringe case with an output narrower than the input. I don't think you can do an integer downscale, so I want it to revert to normal scaling in that situation, but I'm not sure my IF statement is working. Maybe I need to put the zero in quotes or something? I'll take another look later, but if anyone who actually knows what they're doing could tell me that'd be appreciated.

Some mistakes I made along the way:

Lots of leaving out semicolons in the code. Good thing the compiler picks up on this.

I screwed up the OSD menu option initially. The menu would freeze when I selected the A/V tab, but the game would continue running in the background. This really had me stumped for a while - I thought at first maybe I was setting bits of the status string that were already claimed by other options, so I changed to some different ones. Eventually I figured out that I'd used too many characters. Initially it was "Horizontal Integer Scaling,Off,Narrow,Wide". The menu is I think 27 characters wide, one of which is a colon to separate the title and the options. My title was using all of that by itself. Once I abbreviated it a bit the freezing stopped. And it turned out that the bits of the status string I'd changed to were assigned already so I had to change them back to where I'd had them originally.

At first nothing was happening when I changed the menu option. Initially I had my check for the output being narrower than the input in line 1717, so it was IF horizontal scaling is off OR the output is narrower than the input THEN don't do integer scaling. I forget what exactly I was using to determine if the output was narrower than the input but whatever it was was always returning a positive value so integer scaling was never used.

Line 1725, which checks if the output is going to be wider than the screen, wasn't initially there. Instead, if wide scaling was selected I would calculate o_hsize and then compare that to the display size and recalculate using narrow scaling if it was too wide. I don't really get why (maybe a delay in the value of o_hsize being set so the comparison would be to the wrong value), but this was producing a really strange picture - part of the right side of the image would be cut off and there would be what looked like vertical scanlines, and sometimes the whole image would shimmer. Adding the calculation to the IF statement before setting the value seems to have fixed that.

My first test build didn't include menu options, it just set the core to always do the narrow integer scaling. I had the non-integer scaling commented out. When I first added the menu option, I forgot to uncomment it. The result was a blank screen (but I could hear the game's title screen music). This was before I figured out that my too-long menu option was crashing the menu, so I'd get a blank screen and then bring up the OSD to try to change the settings and the OSD would crash. Very dispiriting.


Anyways, I hope there's some interest in this. The MiSTer template says "Basically it's prohibited to change any files in this folder (sys)", so I'm not sure if I've broken the rules here. I think a lot of people are interested in having horizontal integer scaling, and I think this could be added to other cores without too much difficulty once the kinks are ironed out of my code. If you're interested, please download my build and see how it works for you. Let me know what's wrong with it.
User avatar
morf77
Posts: 63
Joined: Fri Aug 28, 2020 1:42 pm
Has thanked: 12 times
Been thanked: 24 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by morf77 »

Really cool stuff! Haven't tested it out yet. Concerning the changes to sys, as I understand it you will lose those changes with any update to the framework. In your experimental core a minor code change to the framework would be implied so it's not like you're breaking the rules when doing a proof of concept core.
User avatar
LamerDeluxe
Top Contributor
Posts: 1239
Joined: Sun May 24, 2020 10:25 pm
Has thanked: 887 times
Been thanked: 284 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by LamerDeluxe »

Awesome job! Wow, that was a lot of complicated work. Would this solution also allow for integer horizontal scaling with aspect-correct vertical scaling (which usually will result in a border)? I.E. the opposite of the vertical integer scaling option in mister.ini.

I was trying to achieve that today with the vscale_border option, but then I realized I don't know what the correct aspect ratios of the arcade cores are in the first place :D
User avatar
meauxdal
Posts: 152
Joined: Mon Nov 23, 2020 3:28 am
Location: atlanta
Has thanked: 39 times
Been thanked: 126 times
Contact:

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by meauxdal »

Just did some testing with this. It's fantastic! It seems to work well in 1920x1440 without breaking due to width, and even when used in conjunction with custom aspect ratios, still seems to produce a fully integer scaled image. In other words, even if you alter aspect ratio in conjunction with toggling this on, the output remains integer scaled. I did note that I couldn't get square pixels when playing Game Gear games using the Intgr Hori Scale at 1440p (Off/Narrow/Wide are all identical with Aspect Ratio: Original), but it did seem to otherwise work as intended.
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Yim »

LamerDeluxe wrote: Wed Feb 10, 2021 9:42 pmWould this solution also allow for integer horizontal scaling with aspect-correct vertical scaling (which usually will result in a border)?
This doesn’t do anything to the vertical scaling, so it won’t make that easier but it won’t stop it either. I think you’d have to turn vertical integer scaling off and use the vertical border adjust to get the vertical resolution you wanted, then set an aspect ratio that gave the integer multiple you wanted for the horizontal scaling.
meauxdal wrote: Wed Feb 10, 2021 10:51 pmI did note that I couldn't get square pixels when playing Game Gear games using the Intgr Hori Scale at 1440p (Off/Narrow/Wide are all identical with Aspect Ratio: Original), but it did seem to otherwise work as intended.
I haven’t looked closely at game gear mode, but I think the original aspect ratio there is 4:3 with non-square pixels. At your resolution I’d expect with vertical integer scaling the default upscale would be a horizontal integer already, meaning there’d be no difference between having the hori scaling setting off or on narrow. Setting it to wide would result in a picture wider than the screen, so it reverts to narrow to make it fit. So that’s working as intended. If you want square pixels you’d need a custom aspect ratio of 160x144 (10:9).

This scaling setting prioritises integer multiplying over aspect ratio, but it still considers the aspect ratio setting. If the aspect ratio setting is already integer scaling, then narrow makes no difference but wide will go wider if the result will fit. If the aspect ratio results in non-integer scaling, then narrow rounds the scaling down to the nearest integer multiple and wide rounds it up.
User avatar
bootsector
Posts: 170
Joined: Sun May 24, 2020 6:58 pm
Has thanked: 4 times
Been thanked: 30 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by bootsector »

This will come in handy for cores that have at least a couple of horizontal resolutions on games that change them a lot! Thanks for bringing this up here!
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Yim »

I've made a mega drive build of this - I just copied the changes from sys_top.v and ascal.vhd into the genesis core sys directory and added a menu option for it in genesis.sv, I didn't spend a lot of time thinking about whether it would work. Fortunately it does seem to work, at least at first glance.

It's here.

I haven't had much time for testing, but I loaded up Alisia Dragoon and watched the scrolling intro that comes on when you boot it. With integer scaling off, there seemed to be a lot of shimmer. With it turned on I couldn't spot any.

I tried to test it out with going from 320 to 256 horizontal modes, but I haven't had a lot of time and I'm not really sure which games have easily accessible transitions between modes. Something with gameplay in 256 and a pause menu in 320 would be perfect. Anyways, give it a download and test it out for me if you feel so inclined.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Zorlac »

I’m still a little confused by this...

Are you trying to achieve perfect horizontal AND vertical integer scaling at the same time?

If so, then would this achieve perfect aspect ratios assuming square pixels?
I was me, but now he's gone!
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Yim »

Yes to the first, not necessarily to the second. This doesn’t do anything to the vertical scaling, but since you can already set the MiSTer for integer vertical scaling this does allow both.

The aspect ratio of the whole image will not necessarily be “perfect” because integer scaling limits your choice of aspect ratios since you upscale in large steps (between 1x, 2x, 3x and so on). The goal is to have “perfect” pixels, by which I mean that every pixel of the image is made of the same number of pixels on the screen. They don’t have to be square. If the vertical upscale is 4x and the horizontal is 5x then you’ll have perfect pixels with a pixel aspect ratio of 5:4. What the image aspect ratio would be depends on the native resolution of the core.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Zorlac »

Nice! So there would be no need for aspect ratio settings if one wanted perfect pixel mapping?

Also, this will theoretically work for games that change resolutions during game play? I think I remember Vay for Mega-CD changes resolution for battles.
I was me, but now he's gone!
FoxbatStargazer
Top Contributor
Posts: 1018
Joined: Thu Dec 10, 2020 5:44 pm
Has thanked: 315 times
Been thanked: 238 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by FoxbatStargazer »

MegaCD games seem far more interested in resolution swapping. You can let the Sonic CD attract cycle through title screen, main gameplay, special stages and movie and it keeps changing.

And yeah this would be kind of a big deal for all cores.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Zorlac »

Damn dude...this sounds really cool! I hope the other devs are open to a 1:1 pixel mapping mode (or whatever it will be called).

I need to try and learn this stuff so I can fix Mega Drive / Mega-CD borders too. Just need more time!! 😂
I was me, but now he's gone!
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Yim »

So there would be no need for aspect ratio settings if one wanted perfect pixel mapping?

Also, this will theoretically work for games that change resolutions during game play? I think I remember Vay for Mega-CD changes resolution for battles.
Depends what you mean by perfect pixel mapping. If you mean 1:1 pixel aspect ratio, you can do that already by matching the aspect ratio to the core resolution. My implementation here still uses the aspect ratio setting because that tells it what to aim for when deciding which integer to upscale by.

It should work fairly well for games that change resolution, but it is possible the output size and aspect ratio will change slightly with the core resolution. I’m not sure about Vay, but say you’re running a 256x240/320x240 mega drive game at 720p with 4:3 aspect ratio. The vertical upscale will be x3 to 720, and the horizontal upscale will be aiming for 960 to give a 4:3 aspect ratio. In 320 mode that’s easy - a 3x integer upscale. In 256 mode the upscale is 3.75x. In my build, with the narrow setting on, you’ll get an upscale of 3x to 768 for an image aspect ratio of 16:15. Set to wide, it’ll upscale 4x to 1024 and image aspect ratio 64:45. At 1080p the same game set to 4:3 with narrow integer scaling would have a consistent 4:3 aspect ratio because the required 1280 horizontal resolution is an integer multiple of both 256 and 320. Wide would give horizontal resolutions of 1536 and 1600 for the two modes, so there’d be some aspect ratio change when switching modes.

It’s not always possible to maintain the same aspect ratio with integer scaling. The default behaviour of the mister is to change the scaling to match the aspect ratio. This version prioritises clean scaling but still tries to get as close to the chosen aspect ratio as possible.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Zorlac »

That actually sounds like the best of both worlds!!! Nice job!! Hope to see this become the new standard!
I was me, but now he's gone!
flynnsbit
Top Contributor
Posts: 552
Joined: Sun May 24, 2020 8:07 pm
Has thanked: 185 times
Been thanked: 310 times
Contact:

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by flynnsbit »

Yim wrote: Fri Feb 12, 2021 1:27 am
So there would be no need for aspect ratio settings if one wanted perfect pixel mapping?

Also, this will theoretically work for games that change resolutions during game play? I think I remember Vay for Mega-CD changes resolution for battles.
Depends what you mean by perfect pixel mapping. If you mean 1:1 pixel aspect ratio, you can do that already by matching the aspect ratio to the core resolution. My implementation here still uses the aspect ratio setting because that tells it what to aim for when deciding which integer to upscale by.

It should work fairly well for games that change resolution, but it is possible the output size and aspect ratio will change slightly with the core resolution. I’m not sure about Vay, but say you’re running a 256x240/320x240 mega drive game at 720p with 4:3 aspect ratio. The vertical upscale will be x3 to 720, and the horizontal upscale will be aiming for 960 to give a 4:3 aspect ratio. In 320 mode that’s easy - a 3x integer upscale. In 256 mode the upscale is 3.75x. In my build, with the narrow setting on, you’ll get an upscale of 3x to 768 for an image aspect ratio of 16:15. Set to wide, it’ll upscale 4x to 1024 and image aspect ratio 64:45. At 1080p the same game set to 4:3 with narrow integer scaling would have a consistent 4:3 aspect ratio because the required 1280 horizontal resolution is an integer multiple of both 256 and 320. Wide would give horizontal resolutions of 1536 and 1600 for the two modes, so there’d be some aspect ratio change when switching modes.

It’s not always possible to maintain the same aspect ratio with integer scaling. The default behaviour of the mister is to change the scaling to match the aspect ratio. This version prioritises clean scaling but still tries to get as close to the chosen aspect ratio as possible.
Interesting. Do you think you could do the same with the ao486 core? Resolution switching while adhering to Resolution and AR would be cool.
User avatar
dfilskov
Posts: 34
Joined: Sun May 24, 2020 9:35 pm
Has thanked: 26 times
Been thanked: 9 times
Contact:

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by dfilskov »

Yim wrote: Wed Feb 10, 2021 3:43 pm There's been a fair bit of talk lately about using custom aspect ratios with vscale_mode 1 to get integer scaling both horizontally and vertically. This works, but it's a bit of a nuisance. I've had a fiddle with the SMS core and implemented automatic horizontal integer scaling as an OSD option
This is GREAT work! - thank you so much! - Please consider suggesting it as an update of the main MiSTer system. Perhaps simply explain and paste the code in an issue here:

https://github.com/MiSTer-devel/Main_MiSTer/issues

Otherwise I guess we'd need to suggest it for each and every core - or have forked parallel releases of "all" cores and catch up when there're changes to the official cores. I hope Sorgelig will consider it. Perhaps he has a different way that he prefers to do it - but as long as it doesn't break any other features and it doesn't take too much effort I think he'd be open to the suggestion to make it implement system-wide.

Meanwhile I'm working with a friend on a spreadsheet that can calculate the ratio / border settings for any given core reso and display reso. It seems quite hard though so we may end up simply releasing lists of settings for each standard resolution and the 10 most popular cores or so.
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Yim »

flynnsbit wrote: Fri Feb 12, 2021 9:40 pmInteresting. Do you think you could do the same with the ao486 core? Resolution switching while adhering to Resolution and AR would be cool.
I'm not really familiar with the ao486 core, not having used it before today, but I downloaded a freedos vhd and did a build of the ao486 core with the changes in it. Unfortunately it doesn't seem to do anything. It runs just fine, and the menu option is there, but nothing changes when I go between options. I suspect either the core is scaling internally and so the output is already a full screen image (including the borders) or I've just not run anything with a small enough resolution for it to be possible to change the scaling while remaining integer. I dunno. As I say, I'm not familiar with the ao486 to begin with.

Here's the ao486 core. Maybe someone more familiar with the core can make the horizontal integer scaling option change something.

I've also made some changes to the logic in the scaler. Here's my new version of the code:

Code: Select all

      VARIABLE h_int_multiple : natural RANGE 0 TO 255;
First, I've added a variable to the Scalaire process in ascal.vhd. This holds the multiplier for integer scaling. I wasn't sure what the smallest output resolution of a mister core was, or the largest resolution of a mister display, so I made the maximum multiplier way higher than I think it could ever need to be. If you're running a core with less than ten pixel horizontal resolution then this might not work. Otherwise it should be ok.

Code: Select all

      h_int_multiple := (hmax-hmin+1)/(i_hmax+1);

      IF h_int(1 DOWNTO 0)="00" OR (h_int_multiple=0 AND h_int(1 DOWNTO 0) = "01") THEN            -- Horizontal integer scaling off
           o_hmin   <=hmin; -- <ASYNC> ?
           o_hmax   <=hmax; -- <ASYNC> ?
           o_hsize  <=o_hmax - o_hmin + 1;
      ELSE                                                          -- Horizontal integer scaling on
           IF h_int(1 DOWNTO 0)="01" OR (h_int_multiple+1)*(i_hmax+1) > o_hdisp THEN    -- Narrow (round upscaling multiple down)
               o_hsize <= h_int_multiple*(i_hmax + 1);
           ELSE                                                     -- Wide (round upscaling multiple up)
               o_hsize <= (h_int_multiple +1)*(i_hmax + 1);
           END IF;
           o_hmin <= (o_hdisp - o_hsize) / 2;
           o_hmax <= o_hmin + o_hsize -1;
      END IF;
And here's the meat of it again. It's a bit simpler, I think. I start by setting the horizontal multiplier by dividing the planned output resolution by the input resolution. This way I can just plug h_int_multiple in where it's needed rather than redoing the calculation like in my first attempt. I'll step through the rest of it:

Code: Select all

IF h_int(1 DOWNTO 0)="00" OR (h_int_multiple=0 AND h_int(1 DOWNTO 0) = "01") THEN
    o_hmin   <=hmin; -- <ASYNC> ?
    o_hmax   <=hmax; -- <ASYNC> ?
    o_hsize  <=o_hmax - o_hmin + 1;
I check if integer scaling is turned off. If not, then the output is set to what was requested. I also do a check that we're not asking for an integer downscale (which I'm pretty sure is impossible). If the narrow setting is on and the multiplier is zero (indicating a downscale), then it acts as if horizontal integer scaling is turned off.

Code: Select all

IF h_int(1 DOWNTO 0)="01" OR (h_int_multiple+1)*(i_hmax+1) > o_hdisp THEN
    o_hsize <= h_int_multiple*(i_hmax + 1);
Then I check if we're set to narrow (h_int=01), or if wide upscaling will exceed the display width. In either case, I want to do narrow upscaling (i.e., multiplier rounded down).

Code: Select all

ELSE                                                     -- Wide (round upscaling multiple up)
     o_hsize <= (h_int_multiple +1)*(i_hmax + 1);
END IF;
Otherwise, we do wide upscaling - multiplier rounded up. This is the same as the narrow scaling, but I add one to the multiplier before applying it.

Code: Select all

o_hmin <= (o_hdisp - o_hsize) / 2;
o_hmax <= o_hmin + o_hsize -1;
Then we set the image position. I figure out how much blank space there is (total display side - scaled image side), and divide it by two. By using that figure as the starting point of the image, half of the blank space winds up on the left and half on the right. Then add the image size to that to get where the image finishes. I have to subtract one because o_hmax's lowest value is zero, not one, so the one hundredth pixel from the left, for example, would be described as 99 rather than 100.

So far as I can tell this is working perfectly in the genesis core, which is where I did the new version (I used it for 486 as well, except when I built that there wasn't the AND h_int(1 DOWNTO 0) = "01" in the first IF statement, meaning it doesn't do wide scaling if the aspect ratio requests a horizontal resolution below the input resolution - which is a pity because I could use that to check if it's doing anything at all in that core if it were present. But it takes an hour for my computer to build ao486 so I won't be doing it again right now). I'm out of time for this stuff at present, but I might make a fork of the genny core and do a pull request, see if I can get this feature into the framework from there.
User avatar
dfilskov
Posts: 34
Joined: Sun May 24, 2020 9:35 pm
Has thanked: 26 times
Been thanked: 9 times
Contact:

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by dfilskov »

Amazing how thorough you are with this! :) - Thanks a lot! - I reeeeally hope Sorgelig / Alexey pays attention and is considering accepting this into the official builds! - Thanks so much for your effort!
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS

Unread post by Zorlac »

Yes! Pretty sure this would also fix border mode aspect ratio for the Mega Drive and Mega-CD cores that is currently wrong.
I was me, but now he's gone!
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

I've made a fork for the template and pull requested it. I've also forked the Genesis core, including an rbf of my latest version of that. Haven't put a pull request there, not sure if I should.

Anyways, I've done what I can. Hopefully it gets approved.
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

I’ve closed the pull request for now because I’ve thought of another change for it: I’ll make the options Off, Narrow, Wide, and Auto. Auto will choose between narrow and wide on the basis of which would be closer to the selected aspect ratio. I’ve got the code planned out, once I get the time I’ll make a build, and once that’s working I’ll redo the pull request.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Zorlac »

Funny...I was actually thinking of asking about “auto”. :)
I was me, but now he's gone!
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

I've added auto to the menu, and here's the new version of the scaler code:

Code: Select all

      h_int_multiple := (hmax-hmin+1)/(i_hmax+1);
      h_int_mod := (hmax - hmin +1) MOD (i_hmax + 1);
      h_int_up := h_int(1 DOWNTO 0)="10" OR (h_int(1 DOWNTO 0)="11" AND (h_int_mod * 2) > (i_hmax + 1));
I've now got three variables. The multiple is the integer result (rounded down) of dividing the requested output resolution (as determined by the horizontal resolution and the aspect ratio) by the input resolution. h_int_mod is the modulus of the same division - the remainder. h_int_up is whether or not the multiple will be increased by one. It's a boolean which is positive if the menu option is set to Wide, or if it's set to Auto and h_int_mod is more than half of the input resolution.

A lot of websites say that the MOD operator I used here is only synthesizable if the divisor is a power of 2. That doesn't seem to be the case, because this compiled just fine and ran successfully with divisors of 256 (which is a power of 2) and 320 (which isn't). Or maybe I don't understand what synthesizable means. If necessary it could be replaced by multiplying h_int_multiple by the input resolution and subtracting that figure from the output resolution.

Code: Select all

      IF h_int(1 DOWNTO 0)="00" OR (h_int_multiple=0 AND NOT h_int_up) THEN   -- Horizontal integer scaling off
           o_hmin   <=hmin; -- <ASYNC> ?
           o_hmax   <=hmax; -- <ASYNC> ?
           o_hsize  <=o_hmax - o_hmin + 1;
This is the default behaviour, used when integer scaling is turned off or the requested output resolution would be a downscale of the input.

Code: Select all

      ELSE                                                          -- Horizontal integer scaling on
           IF NOT h_int_up OR (h_int_multiple+1)*(i_hmax+1) > o_hdisp THEN    -- Narrow (round upscaling multiple down)
               o_hsize <= h_int_multiple*(i_hmax + 1);
If we're not using the default, then we're using integer scaling, but do we go wide, or narrow? I've already set h_int_up to answer that question. If h_int_up is false, or if wide scaling would exceed the display resolution, then we scale narrow.

Code: Select all

           ELSE                                                     -- Wide (round upscaling multiple up)
               o_hsize <= (h_int_multiple +1)*(i_hmax + 1);
           END IF;
           o_hmin <= (o_hdisp - o_hsize) / 2;
           o_hmax <= o_hmin + o_hsize -1;
      END IF;
Otherwise, scale wide by adding one to the multiple before applying it to the input resolution. Then we set the horizontal position as in my earlier versions, and done. I'll resubmit my pull request now, because I think I actually am done this time.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Zorlac »

So in theory, we just set AR to 4:3 when horizontal and vertical are both running in integer mode now? :)
I was me, but now he's gone!
User avatar
LamerDeluxe
Top Contributor
Posts: 1239
Joined: Sun May 24, 2020 10:25 pm
Has thanked: 887 times
Been thanked: 284 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by LamerDeluxe »

Yim wrote: Mon Feb 15, 2021 3:13 pm A lot of websites say that the MOD operator I used here is only synthesizable if the divisor is a power of 2. That doesn't seem to be the case, because this compiled just fine and ran successfully with divisors of 256 (which is a power of 2) and 320 (which isn't). Or maybe I don't understand what synthesizable means. If necessary it could be replaced by multiplying h_int_multiple by the input resolution and subtracting that figure from the output resolution.
That probably means they are using bitwise AND instead of using the remainder of a divide.
User avatar
bootsector
Posts: 170
Joined: Sun May 24, 2020 6:58 pm
Has thanked: 4 times
Been thanked: 30 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by bootsector »

PR is merged! :shock:

Can't wait for new core releases based on this! Thank you, Yim!!!!

Edit: I looked at the wrong repo. It wasn't merged yet. Hope the concerns sorg raised can be fixed! :?
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

Yeah, the comment from sorgelig is that division and multiplication are costly in terms of logic and reduce the fmax (the maximum clock frequency), so this is not a good addition for the framework. If it works ok in some cores then it could be added to them individually, though I’d have to figure out how. I think I wouldn’t be able to do it in the sys files as I do presently because it would get wiped whenever the framework updates. I could maintain a series of parallel updates by reinserting it after each update and doing unofficial releases, but that sounds like a lot of work.

I have got ideas about alternative framework implementations that don’t use multiplication or division, but I’m not sure how much logic they would consume or what the effect would be on fmax. I might give it a go and see what sorgelig says, but otherwise I’ll have to look at doing it on a core by core basis (or admit defeat).

What I’m thinking is either a for loop that would check the integer output size against the non-integer output size and increment it by the input size until it exceeded it (or a set number of repetitions had passed), or a series of ifs that did the same thing. Wouldn’t be elegant and I’m not sure what it would do to fmax, but should be fairly easily done without any multiplication or division.
User avatar
dfilskov
Posts: 34
Joined: Sun May 24, 2020 9:35 pm
Has thanked: 26 times
Been thanked: 9 times
Contact:

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by dfilskov »

Yim wrote: Mon Feb 15, 2021 3:13 pm I've added auto to the menu, and here's the new version of the scaler code:
GREAT work!! :) - much appreciated! - fingers crossed for a fix and the pull request to be accepted! :)

Which repository is it? - this one? - https://github.com/MiSTer-devel/Main_MiSTer/pulls ?
User avatar
morf77
Posts: 63
Joined: Fri Aug 28, 2020 1:42 pm
Has thanked: 12 times
Been thanked: 24 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by morf77 »

Yim wrote: Tue Feb 16, 2021 4:03 am I have got ideas about alternative framework implementations that don’t use multiplication or division, but I’m not sure how much logic they would consume or what the effect would be on fmax. I might give it a go and see what sorgelig says, but otherwise I’ll have to look at doing it on a core by core basis (or admit defeat).

What I’m thinking is either a for loop that would check the integer output size against the non-integer output size and increment it by the input size until it exceeded it (or a set number of repetitions had passed), or a series of ifs that did the same thing. Wouldn’t be elegant and I’m not sure what it would do to fmax, but should be fairly easily done without any multiplication or division.
What about RPC-FPGA? This could offload the division and multiplication to the cpu.

https://ieeexplore.ieee.org/document/8280158
https://core.ac.uk/download/pdf/153399229.pdf

Possibly there are downsides to this as well I'm not aware of. Just brainstorming.
Yim
Posts: 126
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: Automatic horizontal integer scaling implementation on SMS/Genesis/potentially others

Unread post by Yim »

dfilskov wrote: Tue Feb 16, 2021 2:23 pmGREAT work!! :) - much appreciated! - fingers crossed for a fix and the pull request to be accepted! :)

Which repository is it? - this one? - https://github.com/MiSTer-devel/Main_MiSTer/pulls ?
Thanks! My request was on the template, with the goal of getting it into the framework. It looks like that isn't going to happen unless I can figure out a much more optimised way to do it. I did kind of think that this change was too easy to not have been done already - looks like it's harder than I thought.

Here's the build of the genesis core I did last night with the auto option added. It seems to work ok there.

I've taken a shot at a different method of working out the integer scale size, but it seems to be making things worse. I'm starting to think I should leave this stuff to people who know what they're doing instead of fumbling around like I've been doing. If I can't get it to work in a well-optimised way in the framework, then I'll take a look at implementing it in individual cores, but I dunno if I'll be able to pull anything off there.
morf77 wrote: Tue Feb 16, 2021 3:13 pmWhat about RPC-FPGA? This could offload the division and multiplication to the cpu.

https://ieeexplore.ieee.org/document/8280158
https://core.ac.uk/download/pdf/153399229.pdf

Possibly there are downsides to this as well I'm not aware of. Just brainstorming.
Uhh... maybe? Offloading that stuff makes sense, but I have no idea how to do it or if it would work and figuring it out looks complicated. I have no prior experience with this stuff, only what I've figured out as I go working on this.
Post Reply