Я пытался выяснить, как получить доступ к отображенному буферу из C ++ 17, не вызывая неопределенное поведение. В этом примере я буду использовать буфер, возвращаемый Vulkan vkMapMemory
.
Итак, согласно N4659 (окончательный рабочий проект C ++ 17), раздел [intro.object] (выделение добавлено):
Конструкции в C ++
программа создает, уничтожает, обращается, получает доступ и манипулирует объектами.
объект
является
созданный определением (6.1),
новое выражение
(8.3.4), когда неявно изменяется активный член
union (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. размещение - new
массив
Похоже, что раздел [new.delete.placement] не накладывает каких-либо ограничений на способ получения адреса места размещения (это не обязательно должен быть безопасный вывод указателя независимо от безопасности указателя реализации). Следовательно, должна быть возможность создать действительный массив с плавающей точкой посредством размещения- new
следующим образом:
volatile float* float_array = new (ptr) volatile float[sizeInFloats];
Указатель float_array
теперь должен быть безопасным для доступа (в пределах массива или в прошлом).
Итак, мои вопросы следующие:
- Является ли простое
static_cast
действительно неопределенным поведением?
- Является ли это размещение -
new
использование четко определено?
- Применима ли эта методика к подобным ситуациям, таким как доступ к оборудованию с отображенной памятью ?
В качестве примечания: у меня никогда не возникало проблем, просто приводя возвращенный указатель, я просто пытаюсь выяснить, каким будет правильный способ сделать это, согласно письму. стандарта.