Sony Megatron Colour Video Monitor

Hi @MajorPainTheCactus

I would like to extend some of my testing with the shader, and noticed that on my 1440p monitor shader output white is different from my 1080p monitor (both are native close to D65). Looking up close I can see on the 1080p monitor the shader output is doing “RBG” subpixels cleanly. While on the 1440p monitor the shader output is not outputting subpixels cleanly, the best I can get (changing 1080p|4K|8K parameter and 300|600|800|1000TVL parameter) is something like RBRG.

Is that due to needing explicit 1440p support added to the shader, or something else?

FYI both monitors have a normal native RGB subpixel layout. So my second question is whether it’s normal for the 1080p monitor to have the order changed by the shader to RBG? (note changing RGB|BGR does not change this).

2 Likes

Hi rafan so the resolution is only there really to choose the appropriate mask for the desired target TVL. You can use what ever resolution you like it’s just going to choose a different mask based on that.

As for RBG thing that is odd what are you choosing for TVL, resolution and CRT type? There maybe a bug in the mask code for that particular mask.

2 Likes

I’m choosing 4K | APERTURE GRILL | 1000TVL.

Could you let me know what your preferred version is to test with? Is it the latest commit and then HDR mode or SDR? I will then try to make a photo of the results.

Edit: and what preset should I start with? crt-sony-megatron-sony-pvm-2730-sdr.slangp or crt-sony-megatron-sony-pvm-2730-hdr.slangp or another one?

2 Likes

Hi rafan sorry for the delayed response. The preset you should start with is definitely 2730 SDR or HDR. Possibly SDR is less ‘dangerous’ as in less things that can go wrong but that’s probably subjective. Always latest commit should be good (should be :rofl:).

I’ll take a look at that 4K mask and see if there is a bug with it.

2 Likes

Hi, thanks for answering. I’ve been doing some more testing with latest release in SDR mode and I’ve found something that I’m sure will help you tackle the issue.

The issue with the 4K mask that I mentioned is being caused by the colour grading that is present in your shader.

I’ve used the 2730 sdr preset as a base and changed a few settings, mostly making things neutral and adjusting gamma as we discussed. See my preset quoted at the end of this post.

Currently this is tested with the shader colour space set to dci-p3 output, since it is what my monitor sdr output comes closest to and it also having the best gray ramp

With this preset the 4K|1000TVL mask output looks like this, which is obviously not how we want it:

Output2

Now, if I make the shader skip the colour grading / colour space conversion in gamma_correct.h like below (removing the color space conversion * k709_to_XYZ) * kXYZ_to_DCIP3):

include/gamma_correct.h

          else
      {
         //const vec3 dcip3_colour = (scanline_colour * k709_to_XYZ) * kXYZ_to_DCIP3;
         const vec3 dcip3_colour = scanline_colour;  
         gamma_out = LinearToDCIP3(dcip3_colour);

Then the exact same preset gives the following output, which is what we want obviously:

Output1

It would be nice to know whether or not the issue is related to other parts of your code. I noticed you have been incorporating the colour grading stuff from Dogway’s grade.slang, but there’s a big caveat with some of that colourspace code (I’m talking solely about the colour gamut / colour space conversions, I have no opinion on the other stuff). From what I’ve tested with grade.slang the colour space conversions to wide gamut are disfunctional. When I just use dogway’s grade.slang as a standalone it gives me very washed out colors. Much more than what it should be when putting sRGB/709 color space into wide gamut on a wide gamut monitor.

To me it seems the wide gamut colour space stuff in grade.slang was never really tested because dogway didn’t have a wide gamut monitor himself, so no proper way to compare whether 709 to DCI-P3 etc was giving comparable output as just being in native sRGB mode etc.

So again to not give the impression I’m dissing grade.slang, it’s only the colour space stuff in grade that I think should not be used.

My suggestion for you to try and get to the bottom of this would be to remove all colour gamut conversion stuff that you’ve integrated from grade.slang, and instead use the colour space conversion code from guest.r advanced. Which according to my testing seems to work OK. It may save you a lot of headache further along the road.

The guest.r code I’m referring to is in the pre-shaders-afterglow.slang pass, see here: https://github.com/libretro/slang-shaders/blob/master/crt/shaders/guest/advanced/pre-shaders-afterglow.slang

With regards to sony megatron, personally I would be for having all the colour grading and gamma correct stuff in a single pass and not spread out among different passes and parts of the code. Maybe that is in the realm of possibility for the sony megatron?

Preset used with testing: Note that I’ve been testing with a core that has double horizontal pixels, so you may want to adjust the sharpness when used with other cores :slight_smile:

hcrt_hdr = "0.000000"
hcrt_colour_space = "2.000000"
hcrt_crt_resolution = "3.000000"
hcrt_colour_system = "0.000000"
hcrt_gamma_in = "1.760000"
hcrt_gamma_out = "1.759991"
hcrt_red_scanline_min = "0.800001"
hcrt_red_scanline_max = "0.900001"
hcrt_red_scanline_attack = "0.450000"
hcrt_green_scanline_min = "0.800001"
hcrt_green_scanline_max = "0.900001"
hcrt_green_scanline_attack = "0.450000"
hcrt_blue_scanline_min = "0.800001"
hcrt_blue_scanline_max = "0.900001"
hcrt_blue_scanline_attack = "0.450000"
hcrt_red_beam_sharpness = "0.500000"
hcrt_green_beam_sharpness = "0.500000"
hcrt_blue_beam_sharpness = "0.500000"
4 Likes

Here’s an iPhone photo of the new OLED mask using a third party app without AI processing. I think it looks quite good. This is on an LG OLED42C2

5 Likes

Great work @rafan thanks for this investigation, this is really fantastic stuff! I did have a feeling its was to do with this bit of the code, as in the colour space mapping and looking at your snippet of code I think I can see the bug already:

const vec3 dcip3_colour = (scanline_colour * k709_to_XYZ) * kXYZ_to_DCIP3;
         gamma_out = LinearToDCIP3(dcip3_colour);

As in this kXYZ_to_DCIP3 transform is doing a LinearToDCIP3 conversion so we’re effectively doing it twice it looks to me. I’ll look at the code and confirm though. However this doesn’t explain the issue I was seeing as I wasn’t using DCIP3 (I think! hmm). I will look at this as soon as I can I’m just in the middle of a large software project.

As for Dogway and Guest code I certainly based my first attempt on Dogways (stipping out all the stuff I didn’t want/need) but I kind of ditched most of it when I came across the Kronos Groups transforms and various issues I had related to what you’ve said and other things. It may structurally bear some resemblance but the important stuff is all based on Kronos Group examples. I did look at Guest as well and have taken some ideas where they made sense along with other shaders Ive looked at. We all stand on the shoulders of giants at the end of the day and add our own twist.

As for grouping stuff in the same pass this is all down to performance - the first passes are at 240p (fast), the main pass at 4K (slow) and you just have to do things in certain orders rather than all up front. If you have any specific sugeestions I can certainly take a look at them though.

Thanks again for this and hopefully I’ll get some time to fix these issues in the next week or so.

3 Likes

Amazing photo and now I think I can see where those incorrect sub pixels are coming from - they just look like the white sub pixels are being triggered in the whites of eyes so maybe there’s not an issue here! Woo hoo! Does it look alright?

1 Like

Hi @MajorPainTheCactus,

Thanks again for investigating and being around here to answer questions. Now that I’ve been using your megatron a bit more and getting used to it, I’m really liking it. It’s a quite nifty approach to simulating our beloved CRTs on modern display devices. Your work on this is really appreciated.

With regards to the colour gamut stuff, the mapping from one color space into another is done via the CIE 1931 XYZ color space. I don’t see a gamma correction being done in kXYZ_to_DCIP3, while LinearToDCIP3 does it explictly, so I’m not sure something is done twice.

I found the following video a nice visualisation of how the colour gamuts are projected within the XYZ space as it also visualizes the spectral locus. Well worth a watch:

https://www.youtube.com/watch?v=x0-qoXOCOow

Since this colour mapping seems problematic in the shader it may have to do with the way gamma correction is integrated in the gamut mapping? From what I understand the matrix 709(RGB)toXYZ * XYZtoDCI(RGB) maps the 709 “cube” into the DCI-P3 “cube”, such that when you display your content on a DCI-P3 monitor your content is shown as rec709 saturated content.

But how is gamma correctly integrated into mapping one colour gamut into another one? Does the Kronos Group examples spell that out by any chance? There’s both a mismatch in gamma functions and values. I can imagine when using a 709 gamma value of 2.2 and a DCI-P3 gamma of 2.6 (as per spec), is there an implicit gamma correction of 0.4 in this colour gamut mapping or how does that work?

Edit: to complicate stuff my monitor is a wide gamut monitor, targeting DCI-P3, but its default “average” gamma is 2.35.

2 Likes

Thanks for the video I’ll take a watch as for your questions I’d have to get my head back into the shader and those matrices and transfer functions again. In the mean time here’s the Kronos Groups low down on them chapter 13 is all the transfer functions and 14 are all the colour primaries conversions:

https://www.google.com/url?q=https://registry.khronos.org/DataFormat/specs/1.2/dataformat.1.2.pdf&sa=U&ved=2ahUKEwilsMeM6Jf6AhVtQEEAHZ2tCBsQFnoECAYQAg&usg=AOvVaw2B53QFDWWO_j2LvPFej4Nv

I vaguely remember the transfer functions working on the ‘intermediate’ XYZ colour space although I could be completely wrong and they work on the input/output RGB spaces - it’s definitely one of the two!

3 Likes

Thanks for the link to the Khronos document.

So the transfer functions work on the input and output RGB spaces.

As far as I can see it’s not very explicit about what gamma is applied on the input RGB (to make it the required linear input) and what gamma is applied on the output. On the Bruce Lindbloom site it mentions in both the RGB to XYZ section and XYZ to RGB section that “the operation depends on the companding function associated with the RGB color system.”

Taken literally, in the case of converting the 709 space/primaries to DCI-P3 space/primaries one would implicitly create a gamma correction, as input linearization is done with rec.709 gamma ~2.2, but output is done with DCI-P3 gamma of 2.6.

That seems a bit strange… If I look at guest.r method, he only uses the output space gamma value for both input and output (so 2.6 in case of DCI). Seems to make more sense, since you’re not implictly doing gamma correction, while remaining the transformation of the color primaries.

Also what I’m reading in the Bruce Lindbloom site is that the input and output space need to have the same whitepoint defined or otherwise the difference needs to be accounted for via chromatic adaptation (read: complicating things further?). I’m not sure how your whitepoint adaption in your shader ties in with the color space transform, but possibly it’s something to keep in mind?

Unfortunately most of the above doesn’t seem to help much with the shader issues we discussed, I think…

3 Likes

So you need to convert into linear space to convert your colour primaries and then back out into the accompanying gamma space.

I might be able to look at this tomorrow and just see if I can spot on anything. Hopefully shouldn’t be too hard to figure out what’s wrong here.

3 Likes

@rafan so found the issue! My gut instinct was right - it was a precision issue and a complete school boy error by having my SDR linear space buffer in low precision. Upped it to float 16 and bang fixed the weird issues.

So one thing you’ll notice now is that those bars in SDR are still very dark however they aren’t clipped now, they just contain very dark values. If you remove the sRGB gamma clip which is part of the sRGB standard and just replace it with a direct pow(X, 2.4) they pop back up but I’m keeping with the standard. I can elaborate on this if you’d like to try yourself.

HDR is unaffected by this as it uses the PQ transfer function. Although it would have been affected by the precision bug.

All in all this fix should be a large quality improvement so thanks rafan!

The pull request is Sony Megatron 3.5.

LCD Photos: OnePlus 8 Pro Camera: Pro Mode, ISO 400, WB 6500K, Aperture Speed 1/60, Auto Focus, 48MPixel JPEG.

5 Likes

Quality improvements welcome! Thanks @rafan & @MajorPainTheCactus! Gorgeous screenshot by the way!

1 Like

@MajorPainTheCactus Just thought I’d let you know it appears the BGR version of the 8K RYCBX mask just repeats the RGB version. It ought to output BCYRX, but ends up outputting RYCBX anyway. Fixing it is as simple as editing line 378 from

kRYCBX, kRMCGX, kRYCBX

into

kRYCBX, kRMCGX, kBCYRX

2 Likes

Good catch! Thanks - I’m sure there are more of these lurking around what with all the tables!

1 Like

That’s awesome @MajorPainTheCactus, gamma issues are fixed now! Thanks! :heart_eyes:

One issue remaining is the subpixel output when setting colour space to DCI-P3

"SDR: Display's Colour Space: r709 | sRGB | DCI-P3" to DCI-P3.

Then I’m getting a messed up subpixel output like below. It’s most visible when setting TVL to 1000.

whereas with Color Space set to 709 or sRGB I do get a clean subpixel output:

Could you test this by loading “crt-sony-megatron-sony-pvm-2730-sdr.slangp” preset, then -only- change “SDR: Display’s Colour Space” to DCI-P3 (setting 2.00), TVL to 1000 and check the subpixel output on a white screen?

Edit: I mentioned before that I’m testing this on 1440p monitor with native RGB subpixels.

Edit 2: a picture of the native monitor RGB subpixel layout

Output3_native_no_shader

2 Likes

Some additional test results to previous post, but now for HDR mode.

When using RA in HDR mode and loading 2730-HDR preset, whatever setting I change, the mask output is incorrect, similar to SDR mode in DCI-P3, e.g. for Aperture Grille| 4K | 1000TVL:

Since HDR mode is ALWAYS doing the color gamut transformations (either 709 to 2020 space or “extended/vivid” to 2020 space) it seems that this colour gamut mapping is a source of messing up the mask output.

I think replacing the color transform matrix in the HDR shader with a unity matrix (i.e. it would pass native colors without doing any transform) could be good test case, to see whether then the mask output returns correct. That would confirm the transform between different gamuts is causing an issue.

Edit: So I’ve just tried it, replacing the transform matrix for 709 and expanded709 in include/hdr10.h

Note the matrices seem to already be the product of RGBtoXYZ and back out XYZtoRGB, i.e. 709(RGB)toXYZ * XYZto2020(RGB), both for 709_to_2020 and also this seems the case for the Expanded709_to_2020 matrix

/*
const mat3 k709_to_2020 = mat3 (
   0.6274040f, 0.3292820f, 0.0433136f,
   0.0690970f, 0.9195400f, 0.0113612f,
   0.0163916f, 0.0880132f, 0.8955950f);

/* START Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.)  https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
/*
const mat3 kExpanded709_to_2020 = mat3 (
    0.6274040f,  0.3292820f, 0.0433136f,
    0.0457456f,  0.941777f,  0.0124772f,
   -0.00121055f, 0.0176041f, 0.983607f);
*/

Replace by a “unity” matrix, such that there’s no transform and native colors get passed on:

const mat3 k709_to_2020 = mat3 (
 1.0f, 0.0f, 0.0f,
 0.0f, 1.0f, 0.0f,
 0.0f, 0.0f, 1.0f);
 
const mat3 kExpanded709_to_2020 = mat3 (
 1.0f, 0.0f, 0.0f,
 0.0f, 1.0f, 0.0f,
 0.0f, 0.0f, 1.0f);

And indeed the mask issue is solved, the shader subpixel output is clean RGB (RBG):

Now the million dollar question, can you think of any reason why color gamut mapping (e.g. 709 to DCI-P3 (in SDR mode) or 709 to 2020 (in HDR mode) in the shader is causing the mask output to get “messed up”?

It would be really nice if this could be resolved or at least understood why it is causing this result.

2 Likes

Hi rafan great stuff so we’re making progress. So aperture grille 4K 1000TVL should be selecting a Magenta Green mask but what you’re seeing is yellow and cyan in there too? This is odd. Maybe I haven’t taken into account the relative recent conversion into XYZ but then I’d expect that to be influencing every mask. I’ll take a look my end.

1 Like

Hi @rafan, so although my camera isn’t good enough to capture the sub pixels of the high density 1000TVL mask on my 27" 4K monitor I am able to take a screen grab of the shaders output and it looks like this:

which looks correct as in its a pure magenta green mask which is what the 4K 1000TVL options would select as a mask.

Also the maths all works out I think. The main shader (last pass) deals with rec. 709 colour primaries (see line 158 in colour_grade.h) not XYZ colour space and so all of the last colour gamut transforms should be from rec. 709 space (sRGB uses the same colour primaries as rec.709 it just uses a different gamma curve).

If you put those matrices to identity then you’ll still be in rec .709 space and so (I’d guess) your monitor is in that space and not DCI-P3 or rec.2020 and hence why it looks correct using identity.

So I say all that with the massive caveat that I’ve understood you correctly - I can misread things.

2 Likes