0502 discard 쓰지 않기

260502

discard를 쓰면

early depth test가 꺼지기 때문에

결국 오래걸리는 작업은 다 한 뒤에야

그 픽셀을 폐기한다.


특히 내가 생각했던

dynamic scissor의 경우

내가 봐도(...) 비효율적으로 생겼다.

이를 좀 정상화시키는 게

float inside =
    step(WorldClipRect.x, WorldFragPos.x) *
    step(WorldClipRect.y, WorldFragPos.y) *
    step(WorldFragPos.x, WorldClipRect.x + WorldClipRect.z) *
    step(WorldFragPos.y, WorldClipRect.y + WorldClipRect.w);

FragColor *= inside;

이런 식으로 discard를 쓰지 않고

alpha = 0으로 두면

조기 최적화가 깨지지도 않고,

더 낫다.

step(a, b) -> (a <= b ? 1 : 0) 으로 구현된다.

이걸 Branchless 라고도 부르는 듯 하다.

셰이더에는 분기를 쓰지 않는게 좋다.

이런식으로 써봤다.

#if 0
    if (uClippingEnabled == 1 && 
          (FragPos.x < uClippingRect.x
        || FragPos.x > uClippingRect.x + uClippingRect.z
        || FragPos.y > uClippingRect.y
        || FragPos.y < uClippingRect.y + uClippingRect.w)
    ) {
        discard;
    }
#else

    float FINAL_FACTOR = 1.0f;
    if (uClippingEnabled == 1) {
        // Y축은 인자 둘다 -붙여주기.
        FINAL_FACTOR = 
            step(uClippingRect.x, FragPos.x) *
            step(-uClippingRect.y, -FragPos.y) *
            step(FragPos.x, uClippingRect.x + uClippingRect.z) *
            step(-FragPos.y, -(uClippingRect.y + uClippingRect.w));
    }

#endif

step이 비교하는 거다 보니까

음수 영역에서는 결과가 반대로 나와서

두 인자에 부호를 붙여준다.


중요한건

discard 이후에?

계산이 얼마나 무거운지이다.

만약 discard 이후에

무겁고 오래걸리는 작업이 있다면,

alpha = 0 을 쓰면

그 작업을 결국 하기 때문에,

어차피 안 보일, 무거운 픽셀이면

discard 해버리는 게 훨씬 낫다.

반대로

discard 이후의 작업이 가볍다면

그냥 위에처럼 하는게 낫다.


여담이지만

저 dynamic scissor는

다음과 같이 glsl의 내부 최적화용 함수들로

줄일 수 있다.

    vec2 minBound = uClippingRect.xy;
    vec2 maxBound = uClippingRect.xy + uClippingRect.zw;

    if (any(lessThan(FragPos.xy, minBound)) ||
        any(greaterThan(FragPos.xy, maxBound))) {
        discard;
    }