Is ZFast SLANG version missing mask?

Hi there,

I have used ZFast, GLSL version, for years now. However, the SLANG version that I am trying to use along with Vulkan, seems to be missing the mask effect: it only shows scanlines, and disabling the scanlines and ramping up the mask to the maximum all it does is darken the screen.

So, am I right on that the SLANG version on this fantastic shader is missing mask?


Both look identical to me. However, the shader is by default being compiled with a fine-grained mask that is made for low resolution screens. To get the mask that is intended for normal screens, you need to edit the file:


And edit this line:

#define FINEMASK

Put // in front of it:

//#define FINEMASK

Then, you need to fix a code mistake. Find this line:

float whichmask = fract(floor(vTexCoord.x*params.OutputSize.x)*-0.3333));

And remove the last parenthesis so it looks like this:

float whichmask = fract(floor(vTexCoord.x*params.OutputSize.x)*-0.3333);

Now you should get a proper CRT mask.

Unfortunately you’ll need to do this again if you update the shaders in the online updater, unless you copy zfast-crt.slangp and shaders/zfast-crt.slang to somewhere else and use the copies.

Perhaps it’s a good idea to add a new preset to the official shader package that uses the coarse mask so that people won’t have to edit the file.

1 Like

You are not wrong, mask is broken on GLSL too.


Ah! Thanks for the correction! I hadn’t seen the missing parenthesis…

Since you did a PR to fix the code error, do you think it would be a good idea to simply comment out the FINEMASK define by default? That way the shader’s mask would work for most people out of the box…

Nope, because an important use case of this shader is to run it on devices with very low res screens that need the fine mask, or that have bad graphics chips that don’t support the coarse mask. What I’ll do instead is do a PR to add a new preset called zfast_crt_coarse (or maybe zfast_crt_hdmask to make it more apparent what it is) that uses the coarse mask instead.

Note that there are a few other CRT shaders that are almost as fast:


Some of these also support curvature so you can fit curved overlays.

The new preset is now available. Update the slang shaders in the online updater and you’ll find it in crt/zfast-crt-hdmask.slangp.

1 Like

Thanks! Tested and working very well

I noticed that mask is fixed in GLSL when change line

COMPAT_PRECISION float whichmask = fract(floor(vTexCoord.x*OutputSize.x*-0.4999));

which contains a typo too, with

COMPAT_PRECISION float whichmask = fract(floor(vTexCoord.x*OutputSize.x*4.0)*-0.4999);

Then i was curious and set up a simple shader that i wrote in the end

FragColor = vec4(vTexCoord,0.0,0.0);

and i got this (scale not matter, same even if 4x)

but if i write

FragColor = vec4(vTexCoord*4.0,0.0,0.0);

Shouldn’t that happen without the need to multiply by 4.0?

1 Like

With GLSL and RA the buffer is sized as a power of 2.0, which usually means that the viewing part of the image is smaller as the buffer itself.

Coordinate range is still from (0.0, 0.0) to (1.0, 1.0) for the whole buffer, but if the ‘image’ is smaller maximum displayed color components won’t reach 1.0, unlike with SLANG shaders.


So I think it shouldn’t be used to calculate exact positions like in a mask in glsl (?)

1 Like

There is a method, but gl_FragCoord can be used instead.

An example form glsl crt-hyllian:

vec2 mask_coords = gl_FragCoord.xy;
//vec2 mask_coords = (texCoord.xy * OutputSize.xy) * TextureSize.xy / InputSize.xy;
1 Like

Is there any gain on doing that? Because instead of making all those calculations (6 times multiply/divide if I count correctly) better use gl_FragCoord.

1 Like

One of the major reasons i can think of is real-time translation of glsl shaders to ‘hlsl’ environment, which doesn’t have a FragCoord eqivalent. With Libretro (glsl) shaders it’s now more or less a Cg legacy, and some ported shaders still have or had this implementation.

1 Like

GLSL original code

COMPAT_PRECISION float whichmask = fract(floor(vTexCoord.x*OutputSize.x*-0.4999));

No mask at all, only darkens the image,

if changed to

COMPAT_PRECISION float whichmask = fract(floor(vTexCoord.x*OutputSize.x)*-0.4999);

This is what we get

Probably this code is meant for SLANG.