一,什么是克隆?
克隆是指通过共享缓冲区来复制内容(例如,两个窗口共享相同的内容)。
克隆可用于提高性能:
- 可以减少所需的更新次数。
你可以在多个显示器上显示内容,但只需要更新一个缓冲区。视频播放就是一个很好的例子。你可以解码一次,但在多个显示器上播放。
- 可以减少显示器的混合量。
例如,假设有一个完全透明的窗口,除了需要有一个在一角进行混合的水印。如果去混合整个窗口,那么成本会很高,可以通过将此窗口的透明度设置为SCREEN_TRANSPARENCY_DISCARD来完全避免。然而,为了保持水印可见,我们可以创建另一个窗口,与透明窗口共享其缓冲区。这样,透明窗口大部分被丢弃,只有一小块区域,即只有水印的 那部分大小才会被混合。
二,窗口的缓冲区共享
当你希望一个窗口显示与另一个窗口相同的内容时,可以使用缓冲区共享。
当窗口共享缓冲区时,只有一组缓冲区。也就是说一个窗口必须是缓冲区的所有者,另一个窗口只是访问这些缓冲区。这些缓冲区必须使用 screen_create_window_buffers() 创建或与 screen_attach_window_buffer() 相关联。共享缓冲区的窗口本身不一定需要任何缓冲区,因为它依赖于使用其他窗口拥有的缓冲区。自然地,不拥有缓冲区的窗口不能设置这些缓冲区的任何属性;共享缓冲区的属性由拥有它们的窗口控制。
共享缓冲区的更新只能由拥有缓冲区的窗口发布(即,其句柄被标识为 screen_share_window_buffers() 的第二个参数(share)的窗口)。只有 screen_share_window_buffers() 被调用时存在的缓冲区才是共享的。这意味着,如果拥有共享缓冲区的窗口在 screen_share_window_buffers() 被调用后创建了新的缓冲区,则共享窗口无法访问新创建的缓冲区。旧缓冲区仍然存在,因为有一个与缓冲区相关的窗口,但它们不会被更新。你可以再次调用 screen_share_window_buffers() 用任何新的或更新的缓冲区更新共享窗口。
在显示这些窗口的内容时,Screen使用一组缓冲区来更新所有窗口。
如下图,为我们展示了窗口之间的缓冲区共享:
2.1 嵌套窗口下的缓冲区共享
调用 screen_share_window_buffers() 时,子窗口的缓冲区不包含在共享缓冲区中。只有在API函数中指定的窗口的缓冲区(而不是其子窗口)可以访问以共享。例如,假设有一个窗口,该窗口有一个子窗口。父窗口只是一个黄色背景,子窗口有一个缓冲区,其中包含一个简单的沙漏形状的图像。如果另一个窗口与父窗口共享缓冲区,则该窗口将无法访问子窗口的沙漏形状。
下图是一个嵌套窗口下的缓冲区共享示例:
正如你所看到的,与父窗口(Window_A)共享缓冲区的窗口(Window_B)不包括沙漏,因为沙漏图像存储在子窗口(Window_C)的缓冲区中,因此不包括在Window_B和Window_A之间的共享缓冲区中。
如果你需要的是在另一个窗口中看到一个窗口的可见内容,而不用担心原始窗口的层次结构,可以使用函数 screen_read_window()。该函数会截取一个窗口的屏幕截图,并将结果存储到你提供的缓冲区中。它可以是像素图或窗口缓冲区。这样,你就可以访问另一个窗口的可见内容,而无需共享窗口及其子窗口的缓冲区。
下图是一个窗口正在截取另一个窗口的屏幕截图示例:
三,流的缓冲区共享
当需要窗口显示与流相同的内容时,同样使用缓冲区共享。
当一个流与一个窗口共享其缓冲区时,只有一组缓冲区。流是缓冲区的所有者,窗口只是访问这些缓冲区。这些缓冲区必须使用 screen_create_stream_buffers() 创建或与 screen_attach_stream_buffer() 相关联。窗口本身不需要任何缓冲区,因为它依赖于使用流所拥有的缓冲区。自然地,窗口不能设置缓冲区的任何属性;共享缓冲区的属性由拥有它们的流控制。
共享缓冲区的更新只能由拥有缓冲区的流发布。只有调用 screen_share_stream_buffers() 时存在的缓冲区才会共享。这意味着,如果拥有共享缓冲区的流在调用 screen_share_stream_buffers() 后创建了新缓冲区,则窗口无法访问新创建的缓冲区。旧缓冲区仍然存在,因为窗口与缓冲区相关联,但它们不会被更新。你可以再次调用 screen_share_stream_buffers(),用任何新的或更新的缓冲区更新窗口。
下图是流的缓冲区共享示例:
Screen不允许共享已消耗的缓冲区。如下图,不允许共享已消费的缓冲区。
四,显示设备的缓冲区共享
当需要窗口显示与显示器相同的内容时,可以使用缓冲区共享。
当调用 screen_share_display_buffers() 时,帧缓冲区(framebuffer)是与窗口共享的。在显示设备支持回写的情况下,如果需要,以考虑大小、像素格式或组织内容,函数 screen_share_display_buffers() 会创建新的缓冲区。支持回写的显示设备会触发新缓冲区的创建,并将帧缓冲区的内容复制到这些新缓冲区。这种行为与 screen_share_window_buffers() 不同,因为当窗口共享缓冲区时,缓冲区只是共享,而不会创建新的缓冲区。
下图是显示器之间的缓冲区共享,如显示设备1共享缓冲区给到显示设备2.
通常对于显示器,帧缓冲区是共享的,因为显示器本身并不与任何实际的缓冲区相关联。当没有现成的帧缓冲区,就需要创建一个,此时则根据启动Screen时在graphics.conf中指定的配置来创建帧缓冲区。如果没有在graphics.conf中配置帧缓冲区种类,那么当我们尝试共享显示缓冲区时,将返回错误,errno为ENOTSUP。
当窗口共享缓冲区时,只会应用拥有缓冲区的窗口的属性。但是,当窗口与显示器共享缓冲区时,会考虑窗口的属性,如SCREEN_PROPERTY_FORMAT和SCREEN_PROPERTY_SWAP_INTERVAL,因为显示器没有自己的缓冲区。因此,窗口更新的速率可能与原始显示器的刷新速率不同。例如,如果将SCREEN_PROPERTY_SWAP_INTERVAL属性设置为0,则表示窗口缓冲区将在显示器更新时进行更新。如果将SCREEN_PROPERTY_SWAP_INTERVAL属性设置为1,则窗口缓冲区将以显示器的刷新速率进行更新,即使显示器上没有发生任何变化。设置适当的交换间隔非常重要,因为我们不想在绝对必要时更新窗口缓冲区。
共享显示缓冲区的另一种方法是使用函数 screen_read_display()。通常,如果只需要显示的一次性截图,则可以使用此函数。如果调用 screen_read_display() 后显示发生更改,则缓冲区不会更新。