프래그먼트 셰이더에서이 조건이 왜 이렇게 느립니까?


19

WebGL에서 FPS 측정 코드를 설정하고 ( 이 SO answer 기반 ) 프래그먼트 셰이더의 성능과 약간의 차이점을 발견했습니다. 이 코드는 1024x1024 캔버스에 단일 쿼드 (또는 오히려 두 개의 삼각형)를 렌더링하므로 조각 매직에서 모든 마법이 발생합니다.

이 간단한 셰이더 (GLSL; 정점 셰이더는 단지 통과)를 고려하십시오.

// some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}

따라서 이것은 흰색 캔버스를 렌더링합니다. 내 컴퓨터의 평균 약 30fps입니다.

이제 숫자 크 런칭을 늘리고 몇 옥타브의 위치 종속 노이즈를 기반으로 각 조각을 계산해 보겠습니다.

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

      float noise;
      for ( int j=0; j<10; ++j)
      {
        noise = 0.0;
        for ( int i=4; i>0; i-- )
        {
            float oct = pow(2.0,float(i));
            noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
        }
      }

      value = noise/2.0+0.5;

    gl_FragColor = vec4(value, value, value, 1.0);
}

위의 코드를 실행하려면 이 구현을snoise 사용 하고 있습니다.

이것은 fps를 7과 같은 것으로 만듭니다.

이제 이상한 부분은 다음과 같은 조건에서 노이즈 계산을 래핑하여 16 개 조각 중 하나만 노이즈로 계산하고 나머지는 흰색으로 남겨 둡니다.

if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}

이 속도가 훨씬 빠를 것으로 예상되지만 여전히 7fps입니다.

한 번 더 테스트하려면 다음 조건부로 픽셀을 필터링하십시오.

if (x > 0.5 && y > 0.5) {
    // same noise computation
}

이것은 이전과 정확히 같은 수의 노이즈 픽셀을 제공하지만 이제는 거의 30fps까지 백업됩니다.

무슨 일이야? 16 분의 1의 픽셀을 걸러내는 두 가지 방법이 정확히 같은 횟수의 사이클을 제공해서는 안됩니까? 그리고 왜 모든 픽셀을 노이즈 처럼 렌더링하는 것보다 느린 픽셀이 느린 가요?

보너스 질문 : 이것에 대해 어떻게해야합니까? 실제로 경우 끔찍한 성능 해결하려면 어떤 방법이 있나요 않는 몇 비싼 조각 내 캔버스 반점 싶어은?

(확실히 말해서, 실제 모듈로 계산은 흰색 대신 16 번째 픽셀을 검은 색으로 렌더링하여 프레임 속도에 전혀 영향을 미치지 않음을 확인했습니다.)

답변:


22

픽셀은 작은 사각형으로 그룹화되고 (하드웨어에 따라 얼마나 큰가) 단일 SIMD 파이프 라인 에서 함께 계산됩니다 . (SIMD의 배열 유형의 구조)

이 파이프 라인 (공급 업체에 따라 여러 가지 이름이 있음 : 날실, 파면)은 잠금 단계에서 각 픽셀 / 조각에 대한 작업을 실행합니다. 즉, 1 픽셀에 계산이 필요한 경우 모든 픽셀이 계산하고 결과가 필요하지 않은 픽셀은 버립니다.

모든 프래그먼트가 셰이더를 통해 동일한 경로를 따르는 경우 다른 브랜치가 실행되지 않습니다.

즉, 16 번째 픽셀마다 계산하는 첫 번째 방법은 최악의 분기입니다.

여전히 이미지 크기를 줄이려면 더 작은 텍스처로 렌더링 한 다음 이미지를 업 스케일하십시오.


5
더 작은 텍스처와 업 샘플링으로 렌더링하는 것이 좋습니다. 그러나 어떤 이유로 든 큰 텍스처의 16 번째 픽셀마다 실제로 쓰기가 필요한 경우 16 번째 픽셀마다 한 번의 호출로 계산 쉐이더를 사용하고 렌더링을 렌더링 대상으로 쓰기 위해 이미지로드 / 저장을 사용하는 것이 좋습니다.
Nathan Reed
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.