这确实是一个硬件问题,实际上...
不,这不对。你马上就会明白为什么。
我来到了关于 OpenGL 缓冲区的部分,据我所知,它们是分配在显卡内存中的内存空间,这是正确的吗?
不完全的。您必须明白,虽然 OpenGL 让您真正接近实际硬件,但您离直接接触它还很远。glMapBuffer 所做的是,它设置了一个虚拟地址范围映射。在现代计算机系统上,软件不在物理地址上运行。而是使用虚拟地址空间(一定大小)。这个虚拟地址空间对软件来说看起来像是一个大的连续内存块,而实际上它是由物理页面拼凑而成的。这些页面无论如何都可以实现,它们可以是实际的物理内存,可以是 I/O 内存,甚至可以由另一个程序就地创建。该机制由 CPU 的内存管理单元与操作系统协作提供。
因此,对于每个进程,操作系统管理一个表,其中进程虚拟地址空间的哪一部分映射到哪个页面处理程序。如果您正在运行 Linux,请查看/proc/$PID/maps
. 如果您有一个使用glMapBuffer/proc/self/maps
的程序在地图缓冲区之前和之后读取(使用您的程序,不要调用系统)并查找差异。
据我所知,所有可能的内存地址(例如,在 64 位系统上有 uint64_t num = 0x0; num = ~num; 可能的地址)都用于系统内存,如 RAM / CPU 端内存。
什么让你有那个想法?谁告诉你(如果有人告诉你)应该被扇耳光……狠狠地打。
你所拥有的是一个虚拟地址空间。而且这个地址空间与硬件端的物理地址空间完全不同。事实上,虚拟地址空间的大小和物理地址空间的大小可以有很大的不同。例如,很长一段时间里都有 32 位 CPU 和 32 位操作系统。但那时已经希望拥有超过 4 GiB 的系统内存。因此,虽然 CPU 仅支持进程的 32 位地址空间(指针的最大大小),但它可能已向内存提供 36 位物理地址线,以支持大约 64 GiB 的系统 RAM;然后,操作系统的工作就是手动切换这些额外的位,这样虽然每个进程只能看到大约 3 GiB 的系统 RAM(最大),但进程可以传播。
此外,并非进程中的所有地址空间都由 RAM 支持。就像我已经解释过的,地址空间映射可以由任何东西支持。通常内存页面错误处理程序也会实现交换,即如果周围没有足够的空闲 RAM,它将使用 HDD 存储(实际上在 Linux 上,所有用户空间对内存的请求都由磁盘 I/O 缓存支持处理程序)。此外,由于地址空间映射是每个进程的,因此地址空间的某些部分是映射的内核内存,这对于所有进程(物理上)相同,并且也位于所有进程中的相同位置。从用户空间这个地址空间映射是不可访问的,但是一旦系统调用转换到内核空间,它就可以访问;是的,操作系统内核也在内部使用虚拟内存。它只是不能从可用的支持中进行广泛的选择(例如,如果网络驱动程序的内存由网络本身支持,那么它很难操作)。
无论如何:在现代 64 位系统上,指针大小为 64 位,而当前硬件有 48 位物理 RAM 地址线。这为周围没有 RAM 的虚拟映射留下了足够的空间,即 16 × 48 位(EDIT表示 2^16 - 48 位地址空间的 1 倍)。而且因为有很多事情要做,每张 PCI 卡都有自己的地址空间,它的行为有点像 CPU 的 RAM(记住我之前提到的那些 PAE,好吧,在旧的 32 位时代类似的东西必须已经与扩展卡通话)。
现在来了 OpenGL 驱动程序。它只是提供了一个新的地址映射处理程序,通常只是构建在 PCI 地址空间处理程序之上,它将映射进程的一部分虚拟地址空间。并且该地址空间中发生的任何事情都将由该映射处理程序反映到最终由 GPU 访问的缓冲区中。然而,GPU 本身可能会直接访问 CPU 内存。AMD 的计划是,GPU 和 CPU 将存在于同一个 Die 上并访问相同的内存,因此那里不再有物理区别。