Портативный способ создания массива данных разнородных вершин - PullRequest
4 голосов
/ 22 января 2020

В графическом программировании очень распространена работа с форматами вершин. Это описано, например, здесь .

Однако я ищу способ выполнить sh то, что не вызывает неопределенное поведение (я в основном ищу информацию C ++ , но C тоже подойдет).

Обычный способ сделать это так: сначала объявите формат вашей вершины как структуру.

struct Vertex {
    float x;
    float y;
    uint16_t someData;
    float etc;
};

Затем вы создайте их массив, заполните их и отправьте в свой графический API (например, OpenGL).

Vertex myVerts[100];
myVerts[0].x = 42.0;
// etc.

// when done, send the data along:
graphicsApi_CreateVertexBuffer(&myVerts[0], ...);

(В стороне: я пропустил ту часть, где вы сообщаете API, какой формат; мы просто предположим, что он знает).

Однако графический API не знает о вашей структуре. Ему просто нужна последовательность значений в памяти, что-то вроде:

|<---  first vertex   -->||<---  second vertex  -->| ...
[float][float][u16][float][float][float][u16][float] ...

И благодаря проблемам упаковки и выравнивания нет никакой гарантии, что myVerts будет таким образом размещен в памяти.

Конечно, тонны кода написаны таким образом, и это работает, несмотря на то, что не является переносимым .

Но есть ли любой переносимый способ сделать это не то, что

1. Inefficient
2. Awkward to write

?

Это в основном проблема сериализации. См. Также: Правильный, переносимый способ интерпретации буфера как структуры

Основной совместимый со стандартами способ, который я знаю, заключается в выделении вашей памяти как char[]. Затем вы просто заполняете все байты в точности так, как вы хотите, чтобы они были расположены.

Но для преобразования из представленного выше struct Vertex в это представление char[] потребуется дополнительная копия (и медленный один за другим). Так что это неэффективно.

В качестве альтернативы, вы можете записать данные непосредственно в представление char[], но это крайне неудобно. Гораздо приятнее сказать verts[5].x = 3.0f, чем обращаться к байтовому массиву, записывая число с плавающей точкой в ​​4 байта и т. Д. c.

Есть ли хороший, переносимый способ сделать это?

1 Ответ

1 голос
/ 22 января 2020

Однако графический API не знает о вашей структуре. Он просто хочет последовательность значений в памяти, что-то вроде:

|<---  first vertex   -->||<---  second vertex  -->| ...
[float][float][u16][float][float][float][u16][float] ...

Это не так. Графический API знает о вашей структуре, потому что вы рассказали ей о своей структуре. Вы даже знаете это уже:

(Кроме того: я пропустил часть, в которой вы сообщаете API, что это за формат; мы просто предположим, что он знает).

Когда вы сообщаете графическому API, где каждое поле находится в вашей структуре, вы должны использовать sizeof и offsetof вместо угадывания макета вашей структуры. Тогда ваш код будет работать, даже если компилятор вставит отступы. Например (в OpenGL):

struct Vertex {
    float position[2];
    uint16_t someData;
    float etc;
};

glVertexAttribPointer(position_index, 2, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*)offsetof(struct Vertex, position));
glVertexAttribIPointer(someData_index, 1, GL_SHORT, sizeof(struct Vertex), (void*)offsetof(struct Vertex, someData));
glVertexAttribPointer(etc_index, 1, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*)offsetof(struct Vertex, etc));

не

glVertexAttribPointer(position_index, 2, GL_FLOAT, GL_FALSE, 14, (void*)0);
glVertexAttribIPointer(someData_index, 1, GL_SHORT, 14, (void*)8);
glVertexAttribPointer(etc_index, 1, GL_FLOAT, GL_FALSE, 14, (void*)10);

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

...