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;
}