我一直试图弄清楚如何从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);
Run Code Online (Sandbox Code Playgroud)
现在,我希望将此内存作为float数组访问.显而易见的事情static_cast是指针并按照我的快乐方式继续如下:
volatile float* float_array = static_cast<volatile float*>(ptr);
Run Code Online (Sandbox Code Playgroud)
(volatile包含它因为它被映射为相干存储器,因此可以在任何时候由GPU写入).然而,在该存储器位置中技术上float不存在阵列,至少不是在引用的摘录的意义上,因此通过这样的指针访问存储器将是未定义的行为.因此,根据我的理解,我有两种选择:
memcpy数据它应该总是能够使用本地缓存,将它转换为std::byte*和memcpy的表示到映射区域.GPU将按照着色器中的指示解释它(在这种情况下,作为32位数组float),从而解决了问题.但是,这需要额外的内存和额外的副本,所以我宁愿避免这种情况.
new阵列看来,[new.delete.placement]部分没有对如何获得放置地址施加任何限制(无论实现的指针安全性如何,它都不必是安全派生的指针).因此,可以通过放置创建有效的浮点数组new,如下所示:
volatile …Run Code Online (Sandbox Code Playgroud)