Есть ли способ использовать объединение в GLSL?

#glsl #shader #union

#glsl #шейдер #объединение

Вопрос:

Мне было интересно, есть ли способ использовать объединение в GLSL. Я не видел никакой документации по этому поводу. Если нет, есть ли чистый обходной путь для использования объединения.

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

Спасибо!

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

Редактировать:

Пример кода (не будет компилироваться, поскольку объединение недоступно):

 struct A{
    bool isA;
    float value;
};
struct B{
    bool isA;
    int value;
};

/* union */
union AandB {
    A a;
    B b;
}

void main()
{
    AandB foo;
    if(foo.a.isA)
        /* process on A */

    else
        /* process on B */
}
  

Кроме того, представьте, что A и B огромны, вы не хотели бы их дублировать.

Редактировать 2:

Вот еще немного информации, надеюсь, это поможет:

Эти данные будут поступать «извне» и будут зависеть от вершины. Его нужно было бы обрабатывать по-разному в зависимости от типа (A или B). К ним, вероятно, можно было бы получить доступ из VBO.

Комментарии:

1. Можете ли вы привести пример или какой-нибудь код того, что вы пытаетесь сделать?

2. Да, это недопустимый GLSL, но это то, что я хочу сделать.

3. И откуда именно берется эта информация?

4. Я составил этот пример, часть объединения точно не будет компилироваться, но идея того, что я хотел бы сделать, есть.

5. @user2888798: Вы не совсем понимаете мой вопрос. Откуда берутся данные, имеет значение с точки зрения того, как вы можете на самом деле делать то, о чем говорите. Являются ли данные в объекте буфера доступными через UBO? Это в SSBO? Возможно, текстура? Атрибут вершины? У каждого из них есть свои потенциальные решения, специфичные для этих источников данных.

Ответ №1:

Поскольку мы говорим об атрибутах вершин, вы можете сделать это:

 layout(location = 0) vec4 floatVal;
layout(location = 0) ivec4 intVal;
  

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

 //Don't use either variable yet

if(isFloat)
{
  //Do something with `floatVal`
}
else
{
  //Do something with `intVal`
}

//Don't use either variable again.
  

Ну, теоретически все будет в порядке. Сглаживание атрибутов — очень странный угловой случай, и маловероятно, что какие-либо основные программы, использующие OpenGL, действительно делают это. Это означает, что поддержка реализации, скорее всего, будет ошибочной (неиспользуемый код — это код, который не был протестирован). Поэтому я бы посоветовал вам переключать программы, если вы хотите убедиться, что ваш код действительно работает на разных платформах.

Комментарии:

1. Черт возьми, это такой хак, но он подходит для того, чего я хочу. Большое спасибо, извините, если я не был ясен в своем вопросе.

Ответ №2:

Согласно спецификации GLSL, ключевое union слово зарезервировано для будущего использования и действительно приведет к ошибке компиляции.

Одним из способов обойти это было бы поддержано GLSL, поскольку они поощряют вас использовать простые структуры данных (без злого выделения указателей или странных трюков с приведением), было бы использование массива struct с флагом.

 struct AandB {
   A a;
   B b;
   bool isA;
};
  

Затем вы можете использовать его по назначению с чем-то вроде этого:

 AandB foo;

if (a.isA) {
   /* a process */
}
else {
   /* b process */
}
  

Однако, если элемент может содержать ни один из типов, вы должны быть осторожны с тем фактом, что флаг изначально будет false и примет тип B. В этом случае обходным путем было бы использовать два флага и установить правильный.

Обновить:

Если использование памяти является проблемой, все еще есть обходной путь, но для этого нужна более забавная структура данных. Один из способов, который приходит на ум, — это наличие массива определений элементов, которые будут содержать индекс и флаг. Давайте представим эту структуру данных:

 struct Item {
   bool isA;
   int index;
};

Item items[5];
A a[5];
B b[5];

Item firstAItem;
firstItem.isA = true;
firstItem.index = 0;
items[0] = firstAItem;

Item firstBItem;
firstBItem.isA = false;
firstBItem.index = 0;
items[1] = firstBItem;
  

При повторении вы можете проверить флаг и перейти к связанному индексу, например, так, предполагая, что N_items это количество элементов, которые у вас есть:

 for (int i=0; i<N_items; i  ) {
   Item currentItem = items[i];
   if (currentItem.isA) {
      A foo = a[currentItem.index];
      /* do some stuff with foo */
   } else {
      B bar = b[currentItem.index];
      /* do some stuff with bar */
   }
}
  

Комментарии:

1. Представьте, что A и B огромны, я бы хотел, чтобы они разделяли одно и то же пространство памяти. Есть ли другой чистый способ сделать это?

2. Смотрите мой обновленный ответ. Это все еще возможно сделать без интенсивного использования памяти. Однако за все приходится платить сложностью.

3. Это интересное решение, вы используете первый массив (элементы) в качестве «указателя» на ваш 2-й или 3-й массив. Это, вероятно, сработало бы, если бы количество элементов каждого типа было известно. (т.е.. У меня было бы 9 элементов A и 1 из B, мне, вероятно, все равно пришлось бы объявить A a[10] и B b[10] на всякий случай). Это заняло бы вдвое больше памяти. Возможно, я мог бы заставить это работать, но я действительно ищу способ использовать одно и то же пространство памяти, если это возможно…

4. Вы все равно можете вести подсчет количества элементов каждого типа, которые вы вставляете. Было бы проще использовать одно и то же пространство памяти, но я не думаю, что это возможно без union указателей or, которые не поддерживаются.

5. Спасибо, я отмечу ваш ответ как лучший, если у кого-нибудь нет лучшего решения