2016-07-22 3 views
2

Как правило, графические карты записывают свой вывод в какое-то место в памяти, к которому я могу получить доступ? Должен ли я использовать драйвер? Если да, могу ли я использовать OpenGL?Как я могу напрямую получить доступ к выводам видеокарты?

Я хочу знать, можно ли «захватить» выход VM на Linux, который имеет прямой доступ к графическому процессору и работает под управлением Windows. В идеале я мог напрямую обращаться к выводам из памяти, не касаясь GPU, так как этот код мог бы работать на хосте Linux.

Другой вариант - это, возможно, написать драйвер Windows, который считывает выходные данные графического процессора и записывает его в какое-то место в памяти. Затем, на стороне Linux, программа может читать эту память. Это кажется несколько невозможным, так как я не совсем уверен, как заставить процесс на хосте делиться памятью с процессом на гостевом компьютере.

Можно ли сделать вариант 1 и просто прочитать вывод из памяти?

+0

Как вы считаете, «выход» видеокарты? Вероятно, вы ошибаетесь. – stark

+0

Рамка. Букет пикселей. В основном я хочу, чтобы Windows и его драйверы обрабатывали весь рендеринг, и я хочу получить доступ к выходу. – Hassan

+0

Выход идет по кабелю к монитору. Кто говорит, что он существует где угодно в памяти? – stark

ответ

4

Я не код под Linux но в Windows, (вы работаете его в эмуляторе все равно), вы можете использовать WinAPI для прямого доступа холста любого окна или даже рабочего стола из 3th партии App. Некоторые Графики могут быть проблематичными для улавливания (особенно DirectX), но у меня не было проблем с моим GL/GLSL пока.

Если вы получили доступ к источнику App можно использовать glReadPixels для извлечения изображений из GL напрямую (но это работает только для текущего GL рендеринга на основе).

  1. Использование glReadPixels

    Как уже упоминалось это должно быть реализовано непосредственно в целевом приложении, чтобы вы должны иметь его исходный код или вводить код в нужное место/время. Я использую для снятия изображения этого кода:

    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; 
        } 
    

    где xs,ys является OpenGL разрешения окна, вы можете игнорировать весь bmp материал (это VCL растровый я использую скриншот магазина), а также могу игнорировать for это просто скопировать изображение от буфера до растрового изображения. Таким образом, важный материал просто так:

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

    Вы должны выполнить этот код после того, как визуализация производится в противном случае вы получите незавершенные или пустой буфер. Я использую его после перерисовывания/перерисовки событий. Как уже упоминалось ранее, будет получен только материал, обработанный GL, поэтому, если ваше приложение объединяет GDI + OpenGL, лучше использовать следующий подход.

  2. WinAPI подход

    Для получения холста изображения любого окна я написал этот класс:

    //--------------------------------------------------------------------------- 
    //--- 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)); 
         } 
        }; 
    //--------------------------------------------------------------------------- 
    //--------------------------------------------------------------------------- 
    //--------------------------------------------------------------------------- 
    

    Опять он использует VCL так переписать растровый bmp и холст scr к вашему стилю окружающего ПРОГРАММИРОВАНИЯ , Также проигнорируйте фрагменты кода _mmap_h, они предназначены только для отладки/трассировки указателей на память, связанных с некоторой неприятной ошибкой компилятора, с которой я столкнулся в то время, когда я это написал.

    Использование прост:

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

    Если вы звоните cap.init() он зафиксирует на всем рабочем столе окна. Если вы вызываете cap.init(window_handle), он будет блокировать определенное визуальное окно/компонент. Чтобы получить дескриптор окна с 3-го на стороне приложения смотрите:

    К сожалению это на SE/RE вместо здесь на SE/SO, но мой ответ здесь охватывающий эту тему был удален. Я использую это для захвата видео ... все анимированные GIF-файлы в моих ответах, когда они созданы этим кодом. Другой пример можно увидеть на день этого ответа шахты:

    Как вы можете видеть, что это работает и для DirectX накладки с Media Player Classic (даже функцией Windows, PrintScreen не может сделать это правильно). Как я уже писал, у меня пока нет проблем с этим.

    Осторожно визуального материала WinAPI вызовов должны вызываться из приложения основного потока (WndProc) в противном случае серьезные проблемы могут возникнуть приводят к случайной несвязанной WinAPI вызывает исключения в любом месте в App.

+0

Спасибо!Я, вероятно, буду использовать WinAPI. Я вижу, что с этим все наладится, мне нужно выяснить, как получить доступ к этому со стороны Linux. Однако это другая тема. Я ценю вашу помощь. – Hassan

Смежные вопросы