2016-08-30 1 views
5

Я только что узнал от Bug in VC++ 14.0 (2015) compiler?, что не следует делать предположения о том, как макет структуры окажется в памяти. Однако я не понимаю, как это распространено в большом количестве кода, который я видел. Например, Vulkan графики API выполняет следующие действия:Как нельзя делать предположения о структурах структуры C++?

Определяет STRUCT

struct { 
    glm::mat4 projection; 
    glm::mat4 model; 
    glm::vec4 lightPos; 
} uboVS; 

Затем пополняет свои поля:

uboVS.model = ... 
    uboVS.... 

Тогда просто копирует в структуры (в памяти хоста), к память устройства через memcpy:

uint8_t *pData; 
    vkMapMemory(device, memory, 0, sizeof(uboVS), 0, (void **)&pData); 
    memcpy(pData, &uboVS, sizeof(uboVS)); 
    vkUnmapMemory(device, memory); 

Затем, на GPU, он определяет UBO, чтобы соответствовать этой структуре ct:

layout (binding = 0) uniform UBO 
{ 
    mat4 projection; 
    mat4 model; 
    vec4 lightPos; 
} ubo; 

Затем, на стороне графического процессора, ubo всегда будет соответствовать uboVS.

Является ли это тем же неопределенным поведением? Разве этот код не полагается на структуру uboVS, которая должна быть выстроена точно так же, как определено, или для обеих сторон (скомпилированный код C++ и скомпилированный шейдер SPIR-V), чтобы в основном генерировать одинаковый разный макет структуры? (аналогично первому примеру в https://www.securecoding.cert.org/confluence/display/c/EXP11-C.+Do+not+make+assumptions+regarding+the+layout+of+structures+with+bit-fields)

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

Благодаря

+2

Нельзя использовать структуру для компилируемых доменов. Иногда это может работать некоторое время, но, делая привычку, вы, возможно, в конечном итоге должны много выполнять этот код, когда другие решения могут быть написаны один раз и не требуют регулярного обслуживания. если ваш план должен сделать это для обеспечения безопасности работы, вы можете попробовать это и посмотреть, насколько хорошо он работает для вас. –

+1

Вы можете посмотреть [этот вопрос и ответ.] (Http://stackoverflow.com/questions/38428666/why-can-glbufferdata-buffer-structs-for-ubo-and-ssbo-when-c-does -not-define-s/38429253 # 38429253) –

+0

Чтобы избежать предположений о макетах структуры, не используйте 'memcpy' в структурах (между прочим) –

ответ

7

Важно понимать разницу между тем, что вы делали в вопросе, который вы цитируете, и тем, что вы здесь делаете.

Что вы сделали в вопросе, который вы показали, нарушает правила C++. Он вызывает неопределенное поведение. Вы пытались притвориться, что объект, содержащий 16 поплавков, - это то же самое, что и массив с 16 поплавками. C++ не позволяет этому быть четко определенным поведением, а компиляторам разрешено предположить, что вы его не попробуете.

В отличие от этого преобразование структуры в массив байтов и копирование этого массива где-то еще на самом деле не нарушает правила объектной модели C++. Он имеет очень специфическое положение, разрешающее такие вещи для соответствующих типов.

Разница в том, что это не компилятор C++, который заботится о макете объекта; это графический процессор. Пока макет данных, которые вы предоставляете, соответствует тому, что сказал ваш шейдер, все в порядке. Вы не бросаете поплавки в массивы или пытаетесь получить доступ к одному объекту с помощью указателя на другой или что-то вроде этого. Вы просто копируете байты.

В какой момент остается только вопрос о том, соответствует ли представление байта этой структуры байтовому представлению ожидаемого определения структуры данных SPIR-V. И да, this is something you can rely upon for most systems that Vulkan can run on.

5

Это правда, что, грубо говоря, стандарт C++ не предписывает какой-либо конкретной внутренней компоновки членов класса.

Однако специальные библиотеки, такие как графические библиотеки для конкретной операционной системы, будут нацелены на конкретный компилятор операционной системы. Они знают, как этот конкретный компилятор упорядочивает компоновку элементов класса и структуры C/C++, а библиотека будет предоставлять подходящие определения, соответствующие фактическому оборудованию.

Операционные системы, которые имеют более одного компилятора, часто имеют официальную спецификацию для бинарного ABI этой операционной системы, а компиляторы будут следовать этому ABI, а специальная библиотека предоставит определения классов и структур, которые будут синхронизированы с этим.

Итак, в вашем конкретном случае вы можете «предположить, и когда это нормально, просто использовать структуру как кусок памяти» после того, как вы обратитесь к документации вашего компилятора, определите, как ваш компилятор излагает члены структур или классов , а затем придумать компоновку структуры соответственно.

0

Spir-V (язык затенения, который вы передаете вулкану) требует, чтобы вы добавляли макеты к элементам структуры, используемым для переменных UniformConstant, Uniform и PushConstant. Вы можете использовать это, чтобы смещения члена spir-V соответствовали смещениям членов в структуре C++.

На самом деле это сложно, так как требует проверки кода спира-V и установки смещений по мере необходимости.

Смежные вопросы