简而言之,我的问题是,当我尝试根据一组脏矩形更新图像时,偏移/尺寸不匹配。
这来自Chromium Embedded Framework,可以通过更新整个图像来正确呈现 - 这通常是不必要的,CEF会为您提供一个更改并需要更新的矩形列表。
完整的副本通过以下方式成功实现:
def copyBuffertoImageRegion(self, topleft, size, fullsize):
print(topleft, size, fullsize)
with CmdBuffer(self.interface, True) as cmdbuffers:
region = VkBufferImageCopy(
bufferOffset=0,
bufferRowLength=fullsize[0],
bufferImageHeight=fullsize[1],
imageSubresource=self.subresource,
imageExtent=size,
imageOffset=topleft
)
vkCmdCopyBufferToImage(
cmdbuffers[0],
self.staging.buffer,
self.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
region
)
本例中的打印给出(0,0,0)(1920,1080,1)(1920,1080) 如需完整副本,则可以使用。
然而,一旦我尝试使用脏的rects,我打印例如这些值:
(88, 88, 0) (120, 120, 1) (1920, 1080)
(88, 88, 0) (120, 120, 1) (1920, 1080)
(88, 96, 0) (120, 120, 1) (1920, 1080)
(72, 80, 0) (152, 136, 1) (1920, 1080)
(72, 80, 0) (152, 136, 1) (1920, 1080)
(80, 80, 0) (144, 136, 1) (1920, 1080)
(80, 80, 0) (136, 136, 1) (1920, 1080)
(80, 88, 0) (152, 144, 1) (1920, 1080)
(88, 88, 0) (152, 144, 1) (1920, 1080)
如果我理解VkBufferImageCopy命令,那么应该是否正确?
脏兮兮的开始的第一个元组; topleft点。第二个元组是rect的宽度,高度和深度,最后一个元组是图像和缓冲区的完整大小。
但是,它看起来像这样: https://imgur.com/qoggLz0
偏移是"错误" - 图形的起源跳了起来,我不确定程度。
非常感谢任何帮助。
编辑:有关可能进一步优化的更多信息:
传入数据的处理方式如下:
def fill(self, pointer, rects):
rect = self.combine_rects(rects)
ffi.memmove(self.mappedhostmemory, pointer, self.buffer.size)
with self.interface.main_lock:
self.image.transitionImageLayout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
self.image.copyBuffertoImageRegion(*rect,self.interface.resolution)
self.image.transitionImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
指针是这个指针: https://github.com/cztomczak/cefpython/blob/master/api/PaintBuffer.md#getintpointer
和mappedhostmemory是我一直映射的临时缓冲区的映射内存。
所以,如果我可以限制CEF指针,那就更好了 - > vulkan上传甚至更多,甚至没有抓住整个纹理并把它放入缓冲区,但实际上只抓住脏的部分。
是否重要; combine_rects获取脏的列表,并在保留队列系列的同时制作一个大的rects。考虑到粒度。
编辑2: 感谢到目前为止的答案,越来越近了(谢谢!),但还没到那里: https://imgur.com/a/Q7tAR
它仍然会跳转,但至少它不再是数据沙拉。 这是通过设置
实现的bufferOffset = (topleft[0]*fullsize[0]+topleft[1])*4
在这种情况下,我不需要确保它是4的倍数,因为它(至少在我的计算机上,稍后将被修复) - 的图像粒度为(8,8) ,8)。
def combine_rects(self, rects):
#start rect is *resolution, 0, 0
left, up, width, height = self.start_rect
for rect in rects:
rleft, rup, rwidth, rheight = rect
left = min(left, rleft)
up = min(up, rup)
width = max(width, rwidth)
height = max(height, rheight)
if width == self.interface.resolution[0] or height == self.interface.resolution[1]:
#issue full copy
return (0, 0, 0), (*self.interface.resolution, 1)
if self.granularity_important:#granularity != (1,1,1)
left = (left // self.granularity_x) * self.granularity_x
up = (up // self.granularity_y) * self.granularity_y
#safety buffer, as we may remove up to granularity-1 texels
width += self.granularity_x
height += self.granularity_y
width = width if width % self.granularity_x == 0 else width + self.granularity_x - width % self.granularity_x
height = height if height % self.granularity_y == 0 else height + self.granularity_y - height % self.granularity_y
return (left, up, 0), (width, height, 1)
如果此功能出现错误,请将其放在此处。
答案 0 :(得分:3)
在缓冲区和图像之间进行复制时,您有两组参数。一个描述了图像中感兴趣的位置;这些是由VkBufferImageCopy::image*
参数定义的。另一个描述了缓冲区内的感兴趣位置;这些是由VkBufferImageCopy::buffer*
参数定义的。
imageExtent
对两者都很重要,因为它描述了将传输多少数据。它在图像的空间中这样做,但它也会影响缓冲区内的感兴趣区域。
缓冲区当然不包含图像;它们包含任意数据。因此,您在缓冲区中描述数据的方式与使用图像的方式不同。
在缓冲区中,图像数据紧凑;每个像素与下一个像素直接相邻。并且每个像素元素按其格式定义存储。复制区域的缓冲区部分由3个参数定义。
bufferRowLength
是从一行到下一行的像素数。 bufferImageHeight
是从一个纹理图层到下一个纹理图层的行数。
这些参数允许您从缓冲区中进行子选择。例如,如果缓冲区逻辑上存储的图像为100x100,而您只想复制前50x50像素,则仍然会提供100x100的bufferRowLength/bufferImageHeight
值。
imageExtent
会阻止它复制到每个维度的第50个像素。 imageExtent
确定从VkImage
传输到缓冲区/从缓冲区传输的数据量。因此,如果您将其设置为50x50,那么您将获得所需的一切。
请注意,当我说"首先50x50"像素,我的意思是左上角50x50。如果您想从顶部右侧 50x50进行复制,那就更具挑战性了。
bufferOffset
允许您指定图像数据开头的字节偏移量。并且因为您可以单独指定行长度与图像的范围,您可以通过提供50 *元素大小的bufferOffset
来实现从右上角50x50的转移。缓冲行长度/高度将与之前相同。
从图像坐标到缓冲区字节地址映射的等式如下:
address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * elementSize;
因此,如果要从缓冲区的左下角50x50进行传输,可以执行此操作。将bufferOffset
设置为:
elementSize * (50 * rowLength)
对于右下角50x50,您可以将bufferOffset
设置为:
elementSize * ((50 * rowLength) + 50)
但请注意,bufferOffset
必须是4的倍数(如果格式不是深度/模板,则为元素大小的倍数)。因此,如果这是一个R8格式,这将不起作用,因为50不是4的倍数。
这也适用于3D图层副本。 bufferImageHeight
指定要跳到下一层的行数。
所以,为了做你感兴趣的事情,你需要以下内容(注意:我不懂Python,所以我只是猜测语法):
bufferOffset = VkDeviceSize(((fullsize[0] * topleft.y) + topleft.x) * elementSize)
region = VkBufferImageCopy(
bufferOffset=bufferOffset ,
bufferRowLength=fullsize[0],
bufferImageHeight=fullsize[1],
imageSubresource=self.subresource,
imageExtent=size,
imageOffset=topleft
)
您需要根据相关格式计算elementSize
。此外,上述代码仅适用于2D副本;对于3D副本,您还需要考虑topleft.y
。
答案 1 :(得分:1)
你所在的地区对我来说有些不一致。 缓冲区的几何结构是什么?
如果它总是全尺寸而您只复制子矩形,那么bufferOffset
不应该是0
,而是根据topleft
设置(并向下舍入以满足其他限制)
这可能是一个常见错误,因此Vulkan规范说:
请注意,
imageOffset
不会影响地址计算 缓冲存储器。相反,bufferOffset
可以用于选择 缓冲存储器中的起始地址。
或者,如果缓冲区实际上只是矩形,则bufferRowLength
和bufferImageHeight
不应该是fullsize
。