GLSL-주요 함수 범위를 벗어난 전역 변수 선언


12

GLSL에서 주 함수 범위 밖에서 변수를 선언하는 데 도움이됩니까? 이러한 변수는 실제로 재사용되며 더 효율적입니까?

문제의 코드는 다음과 같습니다.

varying vec2 vposition;
uniform float seed;
uniform float top;
uniform float bottom;
uniform float phi;
uniform float theta;
uniform float scaledPI;
uniform float yn;
uniform float ym;
uniform float rx;
uniform float ry;
uniform float radius;

const float PI = 3.141592653589793238462643383;

float left;
float right;
float mscaled;
float xn;
float xm;
void main() {
    float t = vposition.y * yn + ym;

    if(t <= 0.0 || t >= PI){
        left = phi - PI;
        right = phi + PI;
    }else{
        mscaled = scaledPI / (1 - abs(Math.cos(theta)));
        mscaled = mscaled < PI ? mscaled : PI;
        left = phi - mscaled;
        right = phi + mscaled;
    }
    xn = (left - right) / ((-rx / 2.0) - (rx / 2.0));
    xm = left - ((-rx/2.0) * xn);
    float p = vposition.x * xn + xm;

    vec3 coords = vec3( sin(p) * sin(t), cos(t), cos(p) * sin(t) );
    float nv = surface( vec4( coords, seed ) );
    gl_FragColor = vec4( vec3( nv, nv, nv ), 1.0 );
}

3
이 질문은 혼란 스럽다. GLSL에는 메인 루프가 없습니다. main()기능 을 의미 합니까? 값이 실제로 전역 변수 (GLSL 용어의 균일 또는 속성) 또는 상수 값입니까?
Sean Middleditch

당신이 말하는 것에 대한 예제로 코드를 게시 할 수 있습니까?
Nathan Reed

코드로 질문을 업데이트했습니다.
RodgerDodger 4

@ user1286792 : GLSL 에 메인 루프가 없다는 사실 은 바뀌지 않습니다 . 무슨 말을하는지 분명하지 않습니다. 이렇게하면 정확히 무엇을 구할 수 있을까요?
Nicol Bolas

@NicolBolas 더 명확하게 질문을 업데이트했습니다. 잘만되면 그것은 미래에 누군가에게 지금 유용합니다.
RodgerDodger

답변:


33

나는 당신이 요구하는 것을 얻는다고 생각합니다. 나는 당신의 주요 관심사가 외부에 정의 된 비 균일 변수라고 가정합니다 main().

float left;
float right;
float mscaled;
float xn;
float xm;

GPU와 GLSL의 작동 방식을 살펴 보겠습니다. GPU에는 스택 또는 통화 활성화 레코드가 없습니다. C 컴파일러가 대부분의 CPU에서 할 수있는 것처럼 GLSL에서 범위 또는 로컬 변수를 시뮬레이트하는 방법은 없습니다. 존재하는 모든 레지스터는 균일 한 레지스터, 쉐이더 스테이지 입력, 출력 및 해당 쉐이더 호출에 고유 한 로컬 레지스터 파일입니다.

다시 말해, 함수 나 스택 또는 힙과 같은 것이 없기 때문에 어디에나 선언 된 모든 변수는 레지스터에 존재합니다. GLSL의 일부 범위에 로컬인지 전체 파일에 대해 전역인지 여부는 차이가 없습니다. 그들은 단지 등록입니다.

그러나 레지스터 할당자는 GLSL 표준의 일부가 아닙니다. 높은 수준의 GLSL 코드를 GPU가 이해하는 낮은 수준의 기계 코드로 변환 할 때 다른 OpenGL 구현은 다양한 수준의 품질을 가질 수 있습니다. 컴파일러의 복잡한 부분 중 하나 (GLSL 또는 기타)는 레지스터 할당 입니다. 이것은 주어진 변수가 차지하는 레지스터를 결정하는 컴파일러의 일부입니다. C는 일반적으로 매우 작은 레지스터 파일 (특히 x86)을 처리해야하기 때문에 조금 더 어려우며 레지스터 스필 (변수를 스택으로 이동) 및 앨리어싱 (변수를 호출하기 전에 변수를 RAM에 다시 저장)을 처리해야합니다. 출력이 특정 레지스터에 있어야하는 홀수 명령어 (x86 's)idiv예를 들어). GPU에는 스택이나 힙이 없기 때문에 레지스터 파일이 더 커서 할당자가 더 간단해질 수 있습니다.

그러나 레지스터 파일은 무한하지 않습니다. 하드웨어가 지원하는 레지스터보다 많은 변수가있는 경우 컴파일러는 모든 변수를 레지스터에 맞춰야합니다. 이것은 일반적으로 어떤 형태의 라이브 니스 범위 점검을 요구합니다 . 즉, xn하나의 계산에 변수 를 사용하고 다시는 사용하지 않으면 컴파일러가이를 결정한 다음 xn나중에 다른 변수가 차지하는 레지스터를 사용할 수 있으므로 레지스터보다 더 많은 변수를 허용 할 수 있습니다. 한 번에 너무 많은 라이브 변수가 없기 때문에).

그러나 컴파일러는이를 수행하지 않을 수 있습니다. 없습니다. 또는 경우에 따라서 만 할 수도 있습니다. 더 간단한 컴파일러가 제공하는 범위는 해결하기가 훨씬 쉬운 문제입니다. 로컬 함수 변수에 할당 된 모든 레지스터는 변수가 죽은 것으로 알고 있으므로 함수가 종료 된 후에 재사용 할 수 있습니다. 전역 변수는 그렇게 쉬운 보장이 없습니다. 따라서 성능이 떨어지는 일부 컴파일러는 수명을 최적화하지 못할 수 있으며 전역 변수는 항상 레지스터를 사용합니다. 이것은 느리게 만들지는 않지만 일부 드라이버에서는 작성할 수있는 셰이더의 크기를 제한 할 수 있습니다.

일반적으로 모든 변수를 현지화하는 것이 좋습니다. 이해하기 쉬운 변수 사용에 가깝게 정의를 유지하십시오. 이것은 GLSL뿐만 아니라 모든 프로그래밍 언어에 적용됩니다. 또한 가능한 모든 경우에 모든 "가변"구성 요소를 작성하는 것이 좋습니다. 다시 말해, 최적화가 불가능한 특정 컴파일러에 대한 힌트가 될 수 있으며, 더 중요한 것은 코드를보다 자체 문서화하고 유지 관리하기 쉽게 만드는 것입니다.

그리고 물론, 여기에 당신의 의무적 인 "테스트하기위한 프로파일"이 있습니다. 글로벌 유무에 관계없이 셰이더를 작성하고 프로파일 링하십시오. 온라인상의 모든 성능 조언은 불신해야하며 가정 상 또는 구식으로 가파른 것으로 가정합니다.


그것이 바로 내가 의미 한 바입니다. 당신은 내 질문에 완벽하게 대답하고 내가 무엇을 요구했는지 더 잘 설명했습니다.
RodgerDodger

1
실제로 때때로 const가 아닌 const 대신 배열을 선언하면 전체 셰이더가 느려집니다 (A LOT 느림). GTX 460에서 그 문제를 발견했습니다.
Tara

방금 이상한 오류를 없애고 변수가 main 외부에서 선언 되었기 때문에 Adreno GPU (OpenGL ES 3.1)가 셰이더를 컴파일하지 못하는 것으로 의심됩니다.
comodoro

내가 본 것 중 가장 철저한 답변 중 하나입니다.
duhaime

오늘 나는 새로운 것을 배웁니다. 오늘, 나는 glsl을 정말로 이해하거나 이해하지 못한다는 것을 배웁니다. cyllindrical space transformed geometry gif를 만들기 위해 사용할 수 있다고해서 그것이 어떻게 작동하는지 이해하지는 않습니다.
cmarangu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.