5

如何通过OpenGL编程访问显卡的显存,具体是获取显存的所有内容?

我尝试了以下两种方法,但都失败了。附上它们,希望您能指出我的程序或我对显存结构的理解中的错误。提前致谢!

我的基本想法是继续在显存中分配一个未初始化的区域,直到探索完显存的所有内容。

(1) 在 main 函数中,我创建了一个带有双缓冲区的窗口(参见下面的代码)。

int main(int argc,char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); // double buffers
    glutInitWindowPosition(0,0);
    glutInitWindowSize(windowWidth, windowHeight);
    glutCreateWindow("Hope to be successful!");
    glutDisplayFunc(myDisplay); // call back function "myDisplay"
    glutKeyboardFunc(keyboard);
    glutMainLoop();

    free(PixelData);
    return 0;
}

然后,在“myDisplay”函数中,首先我调用 glReadPixels() 函数从后帧缓冲区中读出一个给定大小的像素矩形,将这些像素信息保存在一个数组“PixelData”中,最后将这些像素信息显示在通过调用 glDrawPixels() 函数来显示屏幕。执行此程序后,只出现一个黑色窗口。黑色是默认颜色。所以这意味着窗口不包含任何内容。然后我尝试从后面的帧缓冲区中读取其他区域,所有这些区域都没有包含任何内容。

从上面得出的结论是,后帧缓冲区中不包含任何内容。然而,这是不合理的,因为前缓冲区应该只包含当前屏幕内容/窗口,因此在屏幕底部打开和最小化的其他窗口应该在后帧缓冲区中有它们的内容,但后帧缓冲区是空的。那么这些窗口(由我在底部打开和最小化)存储在哪里?

我猜为我的程序分配的帧缓冲区(前后)只占整个视频内存的一小部分。因此,那些最小化的窗口内容应该存储在视频内存的其他位置。

过了一段时间,看了一篇OpenGL frameBuffer object (FBO)的介绍,里面有一句话:默认情况下,OpenGL使用framebuffer作为渲染目的地,完全由窗口系统创建和管理。此默认帧缓冲区称为窗口系统提供的帧缓冲区。这似乎验证了我的上述猜测。

(2) 然后我开始做FBO,希望FBO可以帮助我探索整个显存内容。

在下面的 main 函数中,我创建了一个 FBO 和 7 个渲染缓冲区对象(RBO),然后通过颜色附加点将这 7 个 RBO 附加到这个 FBO。

const int RBO_COUNT = 7; //how many renderBuffer objects are created to attach to the frameBuffer object

int main(int argc, char **argv)
{
    ....
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_ALPHA); // display mode
    ...  

    // create a framebuffer object
    glGenFramebuffers(1, &fboId);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);

    //Create several 7 render buffer objects and attach all of them to the FBO.
    glGenRenderbuffers(RBO_COUNT, rbo);
    for (int i = 0; i < RBO_COUNT; i++)
    {
        glBindRenderbuffer(GL_RENDERBUFFER, rbo[i]);
        glRenderbufferStorage(GL_RENDERBUFFER, PIXEL_FORMAT, SCREEN_WIDTH, SCREEN_HEIGHT);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, colorAttach[i], GL_RENDERBUFFER, rbo[i]);
    }

   // check FBO status
   bool status = checkFramebufferStatus();
   if(!status) fboUsed = false;

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    ...

    glutMainLoop(); /* Start GLUT event-processing loop */
    return 0;
}

下面是我的显示回调函数的主要部分。它的作用是使用空格键控制读取哪个renderBuffer对象,然后将其内容显示在屏幕上。

void displayCB()
{
glBindFramebuffer(GL_FRAMEBUFFER, fboId);

//read this FBO attached RBO, save its content to fboContent
//"count" is a global variable, and is incremented by 1 (mod )every time pressing the space key of the keyboard.
glReadBuffer(colorAttach[count]); 
glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, fboContent);

// back to normal window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind

// render to the default framebuffer //////////////////////////
glClear(GL_COLOR_BUFFER_BIT); // clear buffer
glClearColor(1, 0, 0, 1);
glDrawBuffer(GL_BACK);
glRasterPos2i(-1, -1);  
glDrawPixels(SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, fboContent);
glutSwapBuffers();
}

最后的结果令人失望。所有 renderBuffer 对象的内容都是空的。我不知道这是什么原因,因为我认为所有这些 RBO 都从未初始化过,应该包含一些剩余的内容。

4

3 回答 3

6

如何通过OpenGL编程访问显卡的显存,具体是获取显存的所有内容?

你不能这样做。无法通过 OpenGL 或 DirectX 访问原始视频内存。OpenGL只有各种“缓冲区”。唯一可以访问它的是视频驱动程序,它是windows平台专有的。空闲视频内存由视频驱动程序管理,视频内存中的某些对象可能只是暂时存在 - 驱动程序可以将副本存储在系统内存中,移动它们并在必要时将它们卸载到磁盘。所以你手动管理空闲内存的想法没有实际应用。

您应该能够直接在旧硬件上访问视频内存 - EGA/CGA/VGA,也可以在支持 VESA 的卡上,但是您将没有硬件加速,并且您需要运行类似于 MS-DOS 或具有内核/驱动程序访问权限。您可能能够在 Linux 上访问内存内容,但我认为这不会有帮助。

我建议忘记这个想法。

你的意思是OpenGL不能做这样的任务吗?实际上,我已经在这个问题上工作了将近两个月。

当你开始一个宏大的项目而没有先做足够的研究时,通常会发生类似的事情。我建议从这个经验中学习。在开始编码之前,请务必检查您的应用程序是否尚未编写,是否有任何实际应用程序,是否可以创建,是否需要该应用程序,以及您是否拥有足够的资源来创建它。

目前管理存储在显存中的对象是驱动程序的工作。

如果您想查看其他应用程序创建的对象,使用代理 dll、dll 注入或类似技术劫持 OpenGL api 并拦截相关调用可能会更容易。

于 2012-05-11T19:43:41.967 回答
2

如何通过OpenGL编程访问显卡的显存,具体是获取显存的所有内容?

与操作系统对话,而不是 OpenGL。

于 2012-05-11T17:32:47.090 回答
-1

目前尚不清楚您为什么要这样做,以及您的最终目的是什么,但在我看来,您正在尝试访问由 3rd 方应用程序生成的图形信息。

如果您尝试完成FRAPS 所做的事情,请记住,它使用的技术与您尝试做的完全不同(一种更简单的技术):它拦截对 OpenGL 和 DirectX 等图形库的调用。

于 2012-05-11T19:38:21.803 回答