Приведет ли какой-либо из следующих поисков текстуры к неопределенному поведению, неравномерному потоку или к тому и другому? - PullRequest
0 голосов
/ 12 декабря 2018

Я рисую спрайты на экране, используя вершинный шейдер, фрагментный шейдер и небольшое количество текстурного атласа (изображения, которые содержат десятки меньших спрайтов).Я стремлюсь использовать один вызов рисования для всей сцены, поэтому необходимо создать шейдер, который может динамически выбирать текстуру на основе атрибута.Каждый текстурный атлас связывается последовательно, и плоский атрибут textureid отправляется для определения используемой текстуры, а область этой текстуры отправляется uv.

В спецификации GLSL 3.30 говорится, что массивы сэмплеровнужно постоянное выражение в качестве индекса, но следующие компиляции и ссылки без ошибок (на последней версии драйвера Nvidia):

#version 330

uniform sampler2D sampler[4];
in vec2 uv;
flat in int textureid;
out vec4 endcolor;

void main() {
    endcolor = texture(sampler[textureid], uv);
}

Я не могу гарантировать, что это работает на всех аппаратных средствах, и я не совсем понимаю, почемусвязаны без каких-либо предупреждений.Затем я решил попробовать следующее:

void main() {
    if (textureid == 0) {
        endcolor = texture(sampler[0], uv);
    } else if (textureid == 1) {
        endcolor = texture(sampler[1], uv)
    } else if (textureid == 2) {
        endcolor = texture(sampler[2], uv);
    } else if (textureid == 3) {
        endcolor = texture(sampler[3], uv);
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

Я узнал, что это приведет к неопределенному поведению, так как оно зависит от неравномерного управления потоком.Текстуры, которые он выбирает, зависят от входного атрибута.Затем я обновил его до:

void main() {
    vec4 one = texture(sampler[0], uv);
    vec4 two = texture(sampler[1], uv);
    vec4 three = texture(sampler[2], uv);
    vec4 four = texture(sampler[3], uv);

    if (textureid == 0) {
        endcolor = one;
    } else if (textureid == 1) {
        endcolor = two;
    } else if (textureid == 2) {
        endcolor = three;
    } else if (textureid == 3) {
        endcolor = four;
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

Из этих трех методов:

  1. Какие из них вызывают неопределенное или ошибочное поведение, а какие нет и почему?
  2. Есть ли другие методы, которые были бы лучше (кроме sampler2DArray)?
  3. Почему первый метод продолжал компилировать, связывать и работать без ошибок?

Я понимаю, что могу использовать sampler2DArray, но мои изображения могут быть разных размеров.

1 Ответ

0 голосов
/ 12 декабря 2018
void main() {
  endcolor = texture(sampler[textureid], uv);
}

Это не работает в период GLSL 3.30, потому что 3.30 не позволяет индексировать массив непрозрачных типов с помощью неконстантных выражений.То, что компилятор NVIDIA допускает это на некоторых платформах, не имеет значения: спецификация говорит, что вы не можете этого сделать.


void main() {
    if (textureid == 0) {
        endcolor = texture(sampler[0], uv);
    } else if (textureid == 1) {
        endcolor = texture(sampler[1], uv)
    } else if (textureid == 2) {
        endcolor = texture(sampler[2], uv);
    } else if (textureid == 3) {
        endcolor = texture(sampler[3], uv);
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

Это тоже неправильно, но для (немного)разные причины.Вы обращаетесь к тем, кто находится в неравномерном потоке управления, что делает неявные производные неопределенными .Чтобы исправить это, нужно получить производные до доступа к текстуре, а затем использовать textureGrad для их передачи:

void main() {
    vec2 uvDx = dFdx(uv);
    vec2 uvDy = dFdy(uv);
    switch(textureid) {
    case 0:
        endcolor = textureGrad(sampler[0], uv, uvDx, uvDy);
        break;
    case 1:
        endcolor = textureGrad(sampler[1], uv, uvDx, uvDy);
        break;
    case 2:
        endcolor = textureGrad(sampler[2], uv, uvDx, uvDy);
        break;
    case 3:
        endcolor = textureGrad(sampler[3], uv, uvDx, uvDy);
        break;
    default:
       endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

Почему первый метод продолжает компилироваться, ссылкаи работать без ошибок?

Потому что NVIDIA собирается NVIDIA.На самом деле они не заботятся о том, чтобы убедиться, что вы случайно не используете функции, которые вам не нужны, или не следуете четкой формулировке спецификации.

...