Почему буфер glBufferData имеет структуру для UBO и SSBO, когда C++ не определяет структуру структуры
Я просматривал эту страницу о том, как использовать объекты Uniform Buffer в openGL, и увидел следующую структуру:
struct shader_data_t
{
float camera_position[4];
float light_position[4];
float light_diffuse[4];
} shader_data;
буферизуется в объект OpenGL Uniform Buffer с помощью
GLuint ubo = 0;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(shader_data), &shader_data, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
и используется в шейдере как
...
layout (std140) uniform shader_data
{
vec4 camera_position;
vec4 light_position;
vec4 light_diffuse;
};
...
Тем не менее, я не понимаю, как openGl знает, как сопоставить данные в структуре с униформой, когда glBufferData преобразует указатель структуры и указатель void, что должно сделать openGL не осведомленным о расположении памяти в структуре. Структура структуры C++ определяется реализацией, и хотя автор и другие пользователи UBO вручную дополняют свои структуры C++ фиктивными переменными в соответствии со стандартизованным макетом std140 в шейдере, что мешает компилятору C++ добавить дополнительные отступы и нарушить макет? есть ли в стандарте C++ сильная гарантия того, что дополнительные отступы не будут вставлены или это "практически переносимая" сделка?
1 ответ
OpenGL очень четко определяет, что такое структура байтов std140
интерфейсный блок есть. Все, что вам нужно сделать на стороне C++, это предоставить данные в соответствии с этим макетом. Если вы можете определить структуру, с которой ваш компилятор будет соответствовать std140
тогда ты в порядке. Как ты это делаешь?
Вы должны знать правила, которые ваш компилятор использует для разметки типов.
C++ 11 определяет концепцию, называемую " стандартные типы макетов". Если вы следуете определенным правилам, ваши типы будут стандартными. Теперь, это не очень много значит для того, чтобы точно знать, как они расположены в памяти. Единственное, что C++ говорит вам о стандартных типах компоновки в отношении компоновки, это то, что пустые базовые классы игнорируются (при условии, что они остаются стандартным макетом) и что первый NSDM будет в самом начале класса. То есть, никогда не будет отступов спереди.
Еще одна вещь, о которой говорится в стандарте, заключается в том, что NSDM одного и того же класса доступа будут распределяться по порядку, причем более поздние из них будут иметь большее смещение, чем более ранние. А так как вам не разрешено иметь разные NSDM разных классов доступа в стандартных типах макетов, вы можете положиться на их расположение в указанном порядке.
Но это все, что касается стандарта C++. [class.mem]/13 утверждает, что реализации могут добавлять отступы между членами по разным причинам.
Неофициально, однако, правила "стандартных типов макетов" дают вам хорошее руководство, чтобы знать, когда такие отступы не будут добавлены. Следуйте правилам стандартного макета, и вы можете предположить, что для большинства систем макет вашего класса будет настолько плотно упакован, насколько позволяют размеры и выравнивание используемых типов.
Должны ли реализации соответствовать друг другу? Нет. Но на самом деле нет оснований предполагать, что они не будут. И худшее становится худшим, вы всегда можете проверить, как реализации выкладывают типы.