通用帧缓冲区的当前方法?

Posted

技术标签:

【中文标题】通用帧缓冲区的当前方法?【英文标题】:Current methods for generic frame buffer? 【发布时间】:2017-01-19 12:18:41 【问题描述】:

我一直致力于将通用帧缓冲区实现作为操作系统的一部分。明确地说,我是说没有可用的 Linux 内核或 Windows 内核或设备驱动程序。

我的帧缓冲区运行良好(更新下面的代码),但我希望改进它,而不是为每个制造商创建单独的驱动程序。

TL;DR

我目前正在使用 SSE2 和 XMM 寄存器来更新视频内存。我开始冒险走 DMA 道路,但后来意识到我是基于 30 年前所知道的。 DMA 今天感觉用错了;下一步如何更好地优化此更新过程?

    const uint32_t h_res = gop->Mode->Info->HorizontalResolution;
    const uint32_t v_res = gop->Mode->Info->VerticalResolution;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *framebuffer = (void *)(gop->Mode->FrameBufferBase);

    volatile unsigned int ops = (h_res * v_res)/16;

  __asm__ __volatile__(
        "1:"
        "PREFETCHNTA 128(%%rax);"
        "MOVDQA (%%rax), %%xmm0;"
        "MOVDQA 16(%%rax), %%xmm1;"
        "MOVDQA 32(%%rax), %%xmm2;"
        "MOVDQA 48(%%rax), %%xmm3;"
        "MOVDQA 64(%%rax), %%xmm4;"
        "MOVDQA 80(%%rax), %%xmm5;"
        "MOVDQA 96(%%rax), %%xmm6;"
        "MOVDQA 112(%%rax), %%xmm7;"
        "MOVNTDQ %%xmm0, (%%rbx);"
        "MOVNTDQ %%xmm1, 16(%%rbx);"
        "MOVNTDQ %%xmm2, 32(%%rbx);"
        "MOVNTDQ %%xmm3, 48(%%rbx);"
        "MOVNTDQ %%xmm4, 64(%%rbx);"
        "MOVNTDQ %%xmm5, 80(%%rbx);"
        "MOVNTDQ %%xmm6, 96(%%rbx);"
        "MOVNTDQ %%xmm7, 112(%%rbx);"
        "ADDQ $128, %%rax;"
        "ADDQ $128, %%rbx;"
        "DEC %%rcx;"
        "JNZ 1b;"
        : "+a"(canvas), "+b"(framebuffer)
        : "c"(ops)
        : "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "memory"
    );

【问题讨论】:

我不认为有什么巨大的性能方面,当然不更新。仍然(也有过时的知识)一些吹毛求疵:为什么h_res?让缓冲区总是“hres”像素宽在硬件中并不常见,所以我想知道您是否可能不会遇到行的偏移量与hrs * bpp不同的模式?如果不是,为什么?我会担心你和硬件之间还有另一层,隐藏了这一点。另外我可能睡着了,但为什么/16128/16 = 8,那么你每个像素有 8 个字节,还是我太困惑了? ops 可以在每次模式更改时计算一次,并保存在某个地方。 关于“不更新”不确定您对 gfx API 的控制程度,但如果您有特别大的分辨率,并且您可以完全控制 gfx 更新 + 只有屏幕的某些区域会发生变化,那么一些dirty-rect优化可能会为您节省大量数据传输。如果这是通用的“每帧显示全帧”,那么这可能就已经很好了,而无需使用一些硬件特定的方式。 DMA 听起来不错,但它甚至是现代 x86 的一个选项吗?此外,在 4 核以上的机器上为此专用一个内核可能会解决任何 DMA 梦想,这会以任何方式限制总线吞吐量吗? @Ped7g 好吧,我正在处理 EFI 提供的 LFB 以及位深度。 EFI 引导服务正在填充实际水平宽度。 @Ped7g 我没有每个像素 8 个字节...我正在通过 8 个 64 位 XMM 寄存器移动长度为 128 个字节的块...;) @Ped7g 确实,我需要检查我的数学... ;) 简单地这样做已经使事情加速了好几倍。 ;) 谢谢 【参考方案1】:

关于复制的错误字节数(来自 cmets)。

我建议以与块大小无关的方式编写代码:

const uint32_t h_res = gop->Mode->Info->HorizontalResolution;
const uint32_t v_res = gop->Mode->Info->VerticalResolution;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *framebuffer = (void *)(gop->Mode->FrameBufferBase);
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *end_of_frame = framebuffer + (h_res * v_res);
// "ops" removed

或者如果你不想自己计算,这里也提供了FrameBufferSize

UINT8* end_of_frame = ((UINT8*)framebuffer) + gop->Mode->FrameBufferSize;

...在 asm 中,用end_of_frame 加载rcx 和...

    ...
    "ADDQ $128, %%rbx;"
    "ADDQ $128, %%rax;"
    "CMP %%rcx, %%rbx;"
    "JB 1b;"
    ...

(我不习惯做内联和气体语法,所以在使用前请仔细检查)

有了这个,您以后可以按您希望的方式更改块代码(如果您将进一步试验),终止比较不再依赖于块大小。即使总帧长度不能被块大小整除,它实际上也会继续存在,因此它会在最后一个块被写入后终止(超过帧的边界)。

【讨论】:

那是一种出乎意料的廉价收益...... :) 我什至没有想到我会提供如此巨大的帮助(按性能百分比),而实际上并没有提供您正在寻找的任何信息. ;) 嗯,这就是您在凌晨 3 点编写代码并在脑海中完成所有数学运算时发生的情况。 :) 在第一个示例中,您没有正确计算帧缓冲区的大小。我实际上是VerticalResolution * PixelsPerScanLine * PixelElementSize,其中PixelElementSize 的计算如示例代码所示,该示例代码显示了UEFI 规范中EFI_GRAPHICS_OUTPUT_MODE_INFORMATION 的预期字段用法。不能保证这个数字是 128 的偶数倍,@DavidHoelzer 的源缓冲区需要使用与 LFB 相同的音高。 感谢@RossRidge 当指出另一个数学错误时,我实际上发现并纠正了它。 @RossRidge:在我的第一条评论中,我也对h_res 的使用表示怀疑。我只是没有自己检查规格。此外,我在第一个变体中的示例仅适用于 32bpp 模式。 FrameBufferSize 的第二个变体应该可以工作。如果间距相当大,他可能会通过跳过线的屏幕外部分来节省一些时间(通常进行间距是为了固定线路对齐,所以下一行开始是双赢的)。跨度>

以上是关于通用帧缓冲区的当前方法?的主要内容,如果未能解决你的问题,请参考以下文章

Android OpenGLES3绘图 - 帧缓冲

Opengl 和 Webgl:从附加到当前帧缓冲区的纹理中采样

如何计算帧缓冲区间距?

缩放 OpenGL ES 帧缓冲区的内容

如何保存帧缓冲区然后将其取回

Android opengl 1.1 Render to Texture 1286错误(无效的帧缓冲操作)