我如何直接访问图形卡的输出? [英] How can I access a graphics card's output directly?

查看:117
本文介绍了我如何直接访问图形卡的输出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

图形卡通常会将输出写到我可以访问的内存中的某个地方吗?我必须使用驱动程序吗?如果是这样,我可以使用OpenGL吗?



我想知道是否有可能捕获可直接访问GPU的Linux上的VM的输出,以及正在运行Windows。理想情况下,我可以直接访问内存中的输出,而不用接触GPU,因为这些代码可以在Linux主机上运行。



另一种选择是编写一个Windows驱动程序,读取GPU的输出并将其写入内存中的某个位置。然后,在Linux端,程序可以读取这个内存。这似乎有点不可能,因为我不确定如何让主机上的进程与guest虚拟机上的进程共享内存。



是否可以执行选项1,并简单地读取内存中的输出? 我不在 Linux 下编码,但在 Windows (无论如何都是在模拟器中运行它),您可以使用 WinAPI 直接从第3方应用程序访问任何窗口甚至桌面的画布。一些 GPU 覆盖图可能会遇到问题(特别是 DirectX ),但我现在对我的 GL / GLSL 没有任何问题。

>

如果您有权访问App源,您可以使用 glReadPixels 直接从 GL 但仅适用于当前基于 GL 的渲染)。


  1. 使用 glReadPixels



    如前所述,这必须直接在目标应用程序中实现,因此您需要具有源代码或在正确的地点/时间注入您的代码。我用这个代码截图:
    $ b $ pre $ void OpenGLscreen :: screenshot(Graphics :: TBitmap * bmp)
    {
    if(bmp == NULL)return;
    int * dat = new int [xs * ys],x,y,a,* p;
    if(dat == NULL)return;
    bmp-> HandleType = bmDIB;
    bmp-> PixelFormat = pf32bit; (bmp->宽度!= xs)||(bmp->高度!= ys))bmp-> SetSize(xs,ys); (bmp-> Width == xs)&&(bmp-> Height == ys))
    {
    glReadPixels(0,0,xs,ys, GL_BGRA,GL_UNSIGNED_BYTE,DAT);
    glFinish(); (p =(int *)bmp-> ScanLine [y],x = 0;(b = 0,y = ys-1; y> = 0; y-)
    。 x< xs; x ++,a ++)
    p [x] = dat [a];
    }
    delete [] dat;

    其中 xs,ys 是OpenGL窗口分辨率,你可以忽略整个 bmp 东西(这是我用来存储屏幕截图的VCL位图),也可以忽略 / code>它只是将图像从缓冲区复制到位图。所以重要的东西就是这样:

      int * dat = new int [xs * ys]; //每个像素是32位int 
    glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat);
    glFinish();

    您需要在渲染完成后执行此代码,否则您将获得未完成或空的缓冲区。我在重绘/重绘事件后使用它。如前所述,这将只获得GL呈现的东西,所以如果你的应用程序结合了GDI + OpenGL,最好使用下一种方法。

  2. WinAPI方法



    要获取任何窗口的Canvas图像,我写了这个类:

      // ------------------------------------------ --------------------------------- 
    // ---屏幕截图ver:1.00 --- -------------------------------------------
    // - -------------------------------------------------- ------------------------
    class scrcap
    {
    public:
    HWND hnd,hnda;
    TCanvas * scr;
    Graphics :: TBitmap * bmp;
    int x0,y0,xs,ys;
    scrcap()
    {
    hnd = NULL;
    hnda = NULL;
    scr = new TCanvas();
    bmp = new Graphics :: TBitmap;
    #ifdef _mmap_h
    mmap_new('scrc',scr,sizeof(TCanvas()));
    mmap_new('scrc',bmp,sizeof(Graphics :: TBitmap));
    #endif
    if(bmp)
    {
    bmp-> HandleType = bmDIB;
    bmp-> PixelFormat = pf32bit;
    }
    x0 = 0; Y0 = 0; XS = 1; YS = 1;
    hnd = GetDesktopWindow();

    scrcap()
    {
    #ifdef _mmap_h
    mmap_del('scrc',scr);
    mmap_del('scrc',bmp);
    #endif
    if(scr)delete scr; SCR = NULL;
    if(bmp)delete bmp; BMP = NULL;
    }
    void init(HWND _hnd = NULL)
    {
    RECT r;
    if(scr == NULL)return;
    if(bmp == NULL)return;
    bmp-> SetSize(1,1);
    if(!IsWindow(_hnd))_hnd = hnd;
    scr-> Handle = GetDC(_hnd);
    hnda = _hnd;
    resize();
    }
    void resize()
    {
    if(!IsWindow(hnda))return;
    RECT r;
    // GetWindowRect(hnda,& r);
    GetClientRect(hnda,& r);
    x0 = r.left; XS = r.right-X0;
    y0 = r.top; YS = r.bottom-Y0;
    bmp-> SetSize(xs,ys);
    xs = bmp->宽度;
    ys = bmp->高度;
    }
    void capture()
    {
    if(scr == NULL)return;
    if(bmp == NULL)return; (Rect(0,0,xs,ys),scr,TRect(x0,y0,x0 + xs,y0 + ys));
    bmp-> Canvas-> CopyRect
    }
    };
    // -------------------------------------------- -------------------------------
    // ------------- -------------------------------------------------- ------------
    // -------------------------------- -------------------------------------------

    再次使用 VCL ,重写位图 bmp 和画布 scr 添加到您的编程环境样式中。还有一些代码块,它们只是用来调试/跟踪内存指针,这些指针与我当时正在面对的一些令人讨厌的编译器错误有关。



    用法很简单:

      // globals 
    scrcap cap;
    // init
    cap.init();

    //截图
    cap.capture();
    //这里使用cap.bmp

    如果您调用 cap .init()它会锁定整个Windows桌面。如果您调用 cap.init(window_handle),它将锁定在特定的可视窗口/组件上。要从第三方应用程序端获取窗口句柄,请参阅:


    • 是否可以在不使用messagebox API的情况下显示消息?
    • 是SE / RE而不是SE / SO这里,但我在这里覆盖这个主题的答案已被删除。我用这个视频捕获...我的答案中的所有动画GIF由此代码创建。另一个例子可以在这个答案的底部看到:



      正如你所看到的,它也适用于媒体上的DirectX覆盖播放器的经典(甚至Windows PrintScreen功能无法做到这一点)。正如我写的,我没有遇到任何问题。

      MAIN THREAD(WNDPROC)否则可能会导致严重问题,导致应用程序中随机无关的WinAPI调用异常。


Do graphics cards typically write their output to some place in memory which I can access? Do I have to use the driver? If so, can I use OpenGL?

I want to know if it's possible to "capture" the output of a VM on Linux which has direct access to a GPU, and is running Windows. Ideally, I could access the output from memory directly, without touching the GPU, since this code would be able to run on the Linux host.

The other option is to maybe write a Windows driver which reads the output of the GPU and writes it to some place in memory. Then, on the Linux side, a program can read this memory. This seems somewhat impossible, since I'm not really sure how to get a process on the host to share memory with a process on the guest.

Is it possible to do option 1 and simply read the output from memory?

解决方案

I do not code under Linux but in Windows (you are running it in emulator anyway) you can use WinAPI to directly access canvas of any window or even desktop from 3th party App. Some GPU overlays could be problematic to catch (especially DirectX based) but I had no problems with mine GL/GLSL for now.

If you got access to App source you can use glReadPixels for image extraction from GL directly (but that works only for current GL based rendering).

  1. Using glReadPixels

    As mentioned this must be implemented directly in the targeted app so you need to have it source code or inject your code in the right place/time. I use for screenshoting this code:

    void OpenGLscreen::screenshot(Graphics::TBitmap *bmp)
        {
        if (bmp==NULL) return;
        int *dat=new int[xs*ys],x,y,a,*p;
        if (dat==NULL) return;
        bmp->HandleType=bmDIB;
        bmp->PixelFormat=pf32bit;
        if ((bmp->Width!=xs)||(bmp->Height!=ys)) bmp->SetSize(xs,ys);
        if ((bmp->Width==xs)&&(bmp->Height==ys))
            {
            glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat);
            glFinish();
            for (a=0,y=ys-1;y>=0;y--)
             for (p=(int*)bmp->ScanLine[y],x=0;x<xs;x++,a++)
              p[x]=dat[a];
            }
        delete[] dat;
        }
    

    where xs,ys is OpenGL window resolution, you can ignore the whole bmp stuff (it is VCL bitmap I use to store screenshot) and also can ignore the for it just copy the image from buffer to bitmap. So the important stuff is just this:

    int *dat=new int[xs*ys]; // each pixel is 32bit int
    glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat);
    glFinish();
    

    You need to execute this code after the rendering is done otherwise you will obtain unfinished or empty buffer. I use it after redraw/repaint events. As mentioned before this will obtain only the GL rendered stuff so if your App combines GDI+OpenGL it is better to use the next approach.

  2. WinAPI approach

    To obtain Canvas image of any window I wrote this class:

    //---------------------------------------------------------------------------
    //--- screen capture ver: 1.00 ----------------------------------------------
    //---------------------------------------------------------------------------
    class scrcap
        {
    public:
        HWND hnd,hnda;
        TCanvas *scr;
        Graphics::TBitmap *bmp;
        int x0,y0,xs,ys;
        scrcap()
            {
            hnd=NULL;
            hnda=NULL;
            scr=new TCanvas();
            bmp=new Graphics::TBitmap;
            #ifdef _mmap_h
            mmap_new('scrc',scr,sizeof(TCanvas()        ));
            mmap_new('scrc',bmp,sizeof(Graphics::TBitmap));
            #endif
            if (bmp)
                {
                bmp->HandleType=bmDIB;
                bmp->PixelFormat=pf32bit;
                }
            x0=0; y0=0; xs=1; ys=1;
            hnd=GetDesktopWindow();
            }
        ~scrcap()
            {
            #ifdef _mmap_h
            mmap_del('scrc',scr);
            mmap_del('scrc',bmp);
            #endif
            if (scr) delete scr; scr=NULL;
            if (bmp) delete bmp; bmp=NULL;
            }
        void init(HWND _hnd=NULL)
            {
            RECT r;
            if (scr==NULL) return;
            if (bmp==NULL) return;
            bmp->SetSize(1,1);
            if (!IsWindow(_hnd)) _hnd=hnd;
            scr->Handle=GetDC(_hnd);
            hnda=_hnd;
            resize();
            }
        void resize()
            {
            if (!IsWindow(hnda)) return;
            RECT r;
    //      GetWindowRect(hnda,&r);
            GetClientRect(hnda,&r);
            x0=r.left; xs=r.right-x0;
            y0=r.top; ys=r.bottom-y0;
            bmp->SetSize(xs,ys);
            xs=bmp->Width;
            ys=bmp->Height;
            }
        void capture()
            {
            if (scr==NULL) return;
            if (bmp==NULL) return;
            bmp->Canvas->CopyRect(Rect(0,0,xs,ys),scr,TRect(x0,y0,x0+xs,y0+ys));
            }
        };
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    

    Again it uses VCL so rewrite bitmap bmp and canvas scr to your programing environment style. Also igneore the _mmap_h chunks of code they are just for debugging/tracing of memory pointers related to some nasty compiler bug I was facing at that time I wrote this.

    The usage is simple:

    // globals
    scrcap cap;
    // init
    cap.init();
    
    // on screenshot
    cap.capture();
    // here use cap.bmp
    

    If you call cap.init() it will lock on the whole windows desktop. If you call cap.init(window_handle) it will lock on specific visual window/component instead. To obtain window handle from 3th app side see:

    Sorry it is on SE/RE instead of here on SE/SO but My answer here covering this topic was deleted. I use this for video capture ... all of the animated GIFs in my answers where created by this code. Another example could be seen on the bottom of this answer of mine:

    As you can see it works also for DirectX overlay with media player classic (even Windows PrintScreen function cant do it right). As I wrote I got no problems with this yet.

    Beware visual stuff WinAPI calls MUST BE CALLED FROM APP MAIN THREAD (WNDPROC) otherwise serious problems could occur leading to random unrelated WinAPI calls exceptions anywhere in the App.

这篇关于我如何直接访问图形卡的输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆