icculus / mojoshader

Use Direct3D shaders with other 3D rendering APIs.
https://icculus.org/mojoshader/
zlib License
122 stars 36 forks source link

[Feature Request] Support VPOS on the backbuffer #14

Closed flibitijibibo closed 4 years ago

flibitijibibo commented 4 years ago

A comment multiplatform graphics engineers love to see:

https://github.com/FNA-XNA/MojoShader/blob/fna/profiles/mojoshader_profile_glsl.c#L1041

Basically we have to add some data to the MOJOSHADER_glContext to do the correct flipping. For VPOS the proper line would look like this:

else if (mt == MISCTYPE_TYPE_POSITION)
{
    push_output(ctx, &ctx->globals);
    output_line(ctx, "uniform vec4 vposFlip;");
    output_line(ctx, "vec4 %s = floor(vec4(gl_FragCoord.x, (gl_FragCoord.y * vposFlip.x) + vposFlip.y, gl_FragCoord.z, gl_FragCoord.w));", var);
    pop_output(ctx);
    return;
} // else if

With vposFlip being

float vposFlip[4];
vposFlip[0] = renderTargetBound ? 1.0f : -1.0f;
vposFlip[1] = renderTargetBound ? 0.0f : (float) backbufferHeight;
vposFlip[2] = 0.0f; /* Unused */
vposFlip[3] = 0.0f; /* Unused */
glUniform4fv(vposFlipLoc, 1, vposFlip);

Naiively, we could simply add a MOJOSHADER_glViewportInfo(MOJOSHADER_glContext *ctx, float vpHeight, int renderTargetBound);, but if we're going to the effort of barfing up viewport/target information (as we actually already have in the past with glProgramViewportFlip), perhaps this would be an appropriate time to just make one big function that takes in all the viewport information at once. With this we get full support for VPOS, viewport flipping, and so on, and it also lets us start looking into optional support for half-pixel offset compensation as well, since MonoGame still uses a half-pixel hack and wine-mono would need something like this for D3D9 data as well.

So, it seems like the smartest thing to do would be to add something like this:

/* Call this where you would call MOJOSHADER_glProgramViewportFlip */
void MOJOSHADER_glViewportInfo(
    int viewportW, int viewportH,
    int backbufferW, int backbufferH,
    int renderTargetBound
) {
    float vposFlip[2];

    /* The uniform is only going to exist if VPOS is used! */
    if (ctx->bound_program->vpos_flip_loc != -1)
    {
        vposFlip[0] = renderTargetBound ? 1.0f : -1.0f;
        vposFlip[1] = renderTargetBound ? 0.0f : (float) backbufferH;
        if (ctx->bound_program->current_vpos_flip[0] != vposFlip[0] ||
            ctx->bound_program->current_vpos_flip[1] != vposFlip[1])
        {
            memcpy(
                ctx->bound_program->current_vpos_flip,
                vposFlip,
                sizeof(vposFlip)
            );
            glUniform4fv(
                ctx->bound_program->current_vpos_flip_loc,
                1,
                ctx->bound_program->current_vposFlip
            );
        } // if
    } // if

#ifdef MOJOSHADER_FLIP_RENDERTARGET
    assert(ctx->bound_program->vs_flip_loc != -1);

    /* Some compilers require that vpFlip be a float value, rather than int.
     * However, there's no real reason for it to be a float in the API, so we
     * do a cast in here. That's not so bad, right...?
     * -flibit
     */
    const int flip = renderTargetBound ? -1 : 1;
    if (flip != ctx->bound_program->current_flip)
    {
        ctx->glUniform1f(ctx->bound_program->vs_flip_loc, (float) flip);
        ctx->bound_program->current_flip = flip;
    } // if
#endif /* MOJOSHADER_FLIP_RENDERTARGET */

#ifdef MOJOSHADER_HALF_PIXEL_OFFSETS
    /* You get the idea... */
#endif /* MOJOSHADER_HALF_PIXEL_OFFSETS */
}

At the moment I'm mostly looking for feedback; MojoShader is pretty scary so I can be the one to ultimately write it (well, everything except for the half-pixel offset stuff maybe, blech). This will matter to @kg (uses VPOS), @TheSpydog (possibly fighting this in Metal?), and potentially the MG team if they want to update their MojoShader branch at all for the new GLSL emitter (the API call above wouldn't be useful since they do that in MG's GraphicsDevice instead).

kg commented 4 years ago

One other detail is that vpos in OpenGL is floating-point and often misaligned with the integer coordinates from D3D9, as a result I have to floor() it after flipping it vertically.

flibitijibibo commented 4 years ago

Totally forgot about that, will add it to the OP.

kg commented 4 years ago

Also as a follow-up here is my solution for both: https://github.com/sq/Fracture/blob/master/Squared/RenderLib/Shaders/TargetInfo.fxh

flibitijibibo commented 4 years ago

First draft has been written to a vpos branch:

https://github.com/FNA-XNA/MojoShader/compare/vpos

flibitijibibo commented 4 years ago

This is now in upstream.