Я пытался выяснить, как получить доступ к отображенному буферу из C++ 17, не вызывая неопределенное поведение. В этом примере я буду использовать буфер, возвращаемый Vulkan vkMapMemory
.
Итак, согласно N4659 (окончательный рабочий проект C++ 17), раздел [intro.object] (выделение добавлено):
Конструкции в программе C++ создают, уничтожают, обращаются к объектам, обращаются к ним и управляют ими. Объект создается по определению (6.1), новым выражением (8.3.4), при неявном изменении активного члена объединения (12.3) или при создании временного объекта (7.4, 15.2).
Это, по-видимому, единственные допустимые способы создания объекта C++. Итак, допустим, мы получаем указатель void*
на сопоставленную область видимой хостом (и когерентной) памяти устройства (при условии, конечно, что все необходимые аргументы имеют допустимые значения, и вызов завершается успешно, а возвращаемый блок памяти имеет достаточный размер и правильно выровненный):
void* ptr{};
vkMapMemory(device, memory, offset, size, flags, &ptr);
assert(ptr != nullptr);
Теперь я хочу получить доступ к этой памяти как массив с float
. Очевидная вещь, которую нужно сделать, - это static_cast
указатель и продолжить мой веселый путь следующим образом:
volatile float* float_array = static_cast<volatile float*>(ptr);
(volatile
включена, поскольку она отображается как когерентная память и, таким образом, может быть записана графическим процессором в любой момент). Тем не менее, массив с float
технически не существует в этой области памяти, по крайней мере, не в смысле цитируемой выдержки, и, таким образом, доступ к памяти через такой указатель был бы неопределенным поведением. Поэтому, насколько я понимаю, у меня есть два варианта:
1. memcpy
данные
Всегда должна быть возможность использовать локальный буфер, привести его к std::byte*
и memcpy
представить в отображенную область. Графический процессор будет интерпретировать его, как указано в шейдерах (в данном случае, как массив 32-разрядных float
), и, таким образом, проблема решена. Однако это требует дополнительной памяти и дополнительных копий, поэтому я бы предпочел этого избежать.
2. placement- new
массив
Похоже, что раздел [new.delete.placement] не накладывает никаких ограничений на способ получения адреса размещения (он не должен быть указателем, полученным безопасно, независимо от безопасности указателя реализации). Следовательно, должно быть возможно создать действительный массив с плавающей точкой через placement- new
следующим образом:
volatile float* float_array = new (ptr) volatile float[sizeInFloats];
Указатель float_array
теперь должен быть безопасным для доступа (в пределах массива или в прошлом).
Итак, мои вопросы следующие:
- Является ли простой
static_cast
действительно неопределенным поведением? - Это placement-
new
использование хорошо определено? - Применима ли эта методика к подобным ситуациям, таким как доступ к оборудованию с отображенной памятью?
Как примечание, у меня никогда не было проблемы, просто приводя возвращенный указатель, я просто пытаюсь выяснить, каким будет правильный способ сделать это, согласно букве стандарта.