簡體   English   中英

C#渲染OpenCL生成的圖像

[英]C# Rendering OpenCL-generated image

問題:我試圖實時渲染動態的Julia分形。 由於分形不斷變化,因此我需要能夠每秒至少渲染20幀,最好是每秒渲染更多。 關於Julia分形,您需要了解的是每個像素都可以獨立計算,因此任務很容易並行化。

第一種方法:因為我已經習慣了C#的Monogame,所以我嘗試用HLSL編寫可以完成這項工作的着色器,但是編譯器一直在抱怨,因為我用了超過允許的64個算術槽(我至少需要一千個) )。

第二種方法:使用CPU,產生一幀需要花費大約兩分鍾的時間。

第三種方法:我開始使用稱為Cloo的包裝器學習OpenCL的基礎知識。 通過使用OpenCL計算圖像數據,然后從GPU中獲取數據,將數據存儲在Texture2D中並將紋理繪制到屏幕上,我實際上獲得了快速,不錯的結果。 對於1000x1000的圖像,我每秒獲得約13幀。 這仍然不是我所希望的,因為圖像應為1920x1080填滿我的屏幕,並且幀頻非常明顯。 我意識到我實際上是在GPU上生成圖像,然后將數據發送到CPU,然后再將其發送回GPU,因此這似乎是一個不必要的步驟,如果可以刪除它,可能會解決我的問題。 我在某個論壇上讀到OpenGL可以做到這一點,但是我還沒有找到具體的信息。

問題:首先,是否有一種簡單的方法可以直接繪制由OpenCL生成的數據而不涉及CPU(最好與Monogame兼容)? 如果不是這種情況,是否可以使用OpenGL來實現它,然后再將它與Monogame結合起來? 其次,為什么用簡單的HLSL着色器無法做到這一點? 由於HLSL和OpenCL都使用GPU,為什么在進行許多算術運算時HLSL如此有限?

編輯

我發現站點可以滿足我的要求,但是使用的是GLSL着色器。 這再次質疑了我在HLSL中的習慣。 不幸的是,由於monogame不支持GLSL(至今),我的問題仍未得到解答。

抱歉,我不使用OpenCL也不使用C#,但是您可以使用GLSL在着色器中完全執行此操作(但是對於Julia而言,您可能會遇到精度問題,例如分形有時甚至是64位double還不夠)。 無論如何,這里是我幾年前做過的曼德布羅集的簡單例子。

CPU端應用程式C ++ / OpenGL / GLSL / VCL程式碼::

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h" // VCL window header
#include "gl\\OpenGL3D_double.cpp" // my GL engine
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
OpenGLscreen scr;
GLSLprogram shd;
float mx=0.0,my=0.0,mx0=0.0,my0=0.0,mx1=0.0,my1=0.0;
TShiftState sh0,sh1;
int xs=1,ys=1;
int txrmap=-1;
float zoom=1.000;
unsigned int queryID[2];
//---------------------------------------------------------------------------
void gl_draw()
    {
    float x,y,dx,dy;
    scr.cls();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // matrix for old GL rendering
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();


    // GLSL uniforms
    shd.bind();
    shd.set1i("txrmap",0);      // texture unit
    shd.set2f("p0",mx,my);      // pan position
    shd.set1f("zoom",zoom);     // zoom

    // issue the first query
    // Records the time only after all previous
    // commands have been completed
    glQueryCounter(queryID[0], GL_TIMESTAMP);

    // QUAD covering screen
    scr.txrs.bind(txrmap);
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,+1.0);
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,-1.0);
    glTexCoord2f(1.0,1.0); glVertex2f(+1.0,-1.0);
    glTexCoord2f(1.0,0.0); glVertex2f(+1.0,+1.0);
    glEnd();
    shd.unbind();
    scr.txrs.unbind();

    // issue the second query
    // records the time when the sequence of OpenGL
    // commands has been fully executed
    glQueryCounter(queryID[1], GL_TIMESTAMP);


    // GL driver info and GLSL log
    scr.text_init_pix(1.0);
    glColor4f(0.0,0.2,1.0,0.8);
    scr.text(glGetAnsiString(GL_VENDOR));
    scr.text(glGetAnsiString(GL_RENDERER));
    scr.text("OpenGL ver: "+glGetAnsiString(GL_VERSION));
    glColor4f(0.4,0.7,0.8,0.8);
    for (int i=1;i<=shd.log.Length();) scr.text(str_load_lin(shd.log,i,true));
    scr.text_exit();

    scr.exe();
    scr.rfs();

    // wait until the results are available
    int e;
    unsigned __int64 t0,t1;
    for (e=0;!e;) glGetQueryObjectiv(queryID[0],GL_QUERY_RESULT_AVAILABLE,&e);
    for (e=0;!e;) glGetQueryObjectiv(queryID[1],GL_QUERY_RESULT_AVAILABLE,&e);
    glGetQueryObjectui64v(queryID[0], GL_QUERY_RESULT, &t0);
    glGetQueryObjectui64v(queryID[1], GL_QUERY_RESULT, &t1);
    Form1->Caption=AnsiString().sprintf("Time spent on the GPU: %f ms\n", (t1-t0)/1000000.0);
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    scr.init(this);

    OpenGLtexture txr;
    txr.load      ("gradient.jpg");
    txrmap=scr.txrs.add(txr);

    shd.set_source_file("","","","Mandelbrot_set.glsl_vert","Mandelbrot_set.glsl_frag");

    glGenQueries(2, queryID);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    scr.exit();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    scr.resize();
    xs=ClientWidth;
    ys=ClientHeight;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X,int Y)
    {
    bool q0,q1;
    mx1=1.0-divide(X+X,xs-1);
    my1=divide(Y+Y,ys-1)-1.0;
    sh1=Shift;
    q0=sh0.Contains(ssLeft);
    q1=sh1.Contains(ssLeft);
    if (q1)
        {
        mx-=(mx1-mx0)*zoom;
        my-=(my1-my0)*zoom;
        }
    mx0=mx1; my0=my1; sh0=sh1;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y)
    {
    FormMouseMove(Sender,Shift,X,Y);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y)
    {
    FormMouseMove(Sender,Shift,X,Y);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled)
    {
    zoom*=1.2;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelUp(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled)
    {
    zoom/=1.2;
    gl_draw();
    }
//---------------------------------------------------------------------------

您可以忽略大多數代碼,重要的是gl_draw()渲染單個QUAD覆蓋整個屏幕並傳遞zoompan位置。 此代碼使用舊風格的glBegin/glEnd和默認的nVidia位置,因此可能無法在其他供應商的gfx驅動程序上使用。 網格應位於VAO / VBO中,以便布局位置匹配以查看操作方法,或者查看答案末尾的鏈接或將着色器移植到兼容性配置文件。

頂點:

// Vertex
#version 420 core
layout(location=0) in vec2 pos;     // glVertex2f <-1,+1>
out smooth vec2 p;                  // texture end point <0,1>
void main()
    {
    p=pos;
    gl_Position=vec4(pos,0.0,1.0);
    }

分段:

// Fragment
#version 420 core
uniform sampler2D txrmap;           // texture unit for light map
uniform vec2 p0=vec2(0.0,0.0);      // mouse position <-1,+1>
uniform float zoom=1.000;           // zoom [-]
in smooth vec2 p;
out vec4 col;
void main()
    {
    int i,n;
    vec2 pp;
    float x,y,q,xx,yy;
    pp=(p*zoom)-p0;         // y (-1, 1)
    pp.x=(1.75*pp.x)-0.75;  // x (-2.5, 1)
    for (x=0.0,y=0.0,xx=0.0,yy=0.0,i=0,n=200;(i<n)&&(xx+yy<4.0);i++)
        {
        q=xx-yy+pp.x;
        y=(2.0*x*y)+pp.y;
        x=q;
        xx=x*x;
        yy=y*y;
        }
    q=float(i)/float(n);
    col=texture2D(txrmap,vec2(q,0.5));
//  col=vec4(q,q,q,1.0);
    }

使用此紋理作為漸變:

梯度

結果截圖如下:

屏幕截圖

如果您需要開始使用GLSL (以替換我的gl引擎內容),請參見:

但我敢肯定,在C#中一定會有大量的教程,所以google

如果您對色彩增強感興趣,請參閱:

回答問題:是的,OpenCL可以繪制,但是Monogame顯然沒有封裝在CL的頂部,因此對問題1否。問題2是正確的問題:也許,請參閱下面的建議。 問題3:HLSL本質上是PS 1.1,所以“為什么不可能”是因為PS演化到2.x以便通過更廣泛的數據管道來管理並行化...所以您想要Dx12支持或GLSL / OpenGL。

由於您使用CLoo接近了性能預期,為什么不嘗試使用OpenCL.Net和/或OpenTK將Julia的計算更緊密地綁定到Monogame API? -如果您必須使用GPU-CPU-GPU,則至少要使該管道盡可能寬。

或者,對於您的並行化和幀速率問題,稍作橫向解決方案可能是將諸如Quanta Alea之類的GP-GPU包裝器與您的Monogame解決方案集成。 我建議您看一下Cudafy,但Alea更強大且支持跨供應商GPU。

構建過程將決定Julia代碼的哪一部分將通過Alea在GPU上進行計算,而Monogame部分將接收用於渲染的像素字段。 症結在於庫的“游戲性”兼容性,以及最終的幀速率(如果您可以使之工作)。

最重要的是:您有選擇地陷入HLSL(閱讀:Microsoft Dx9)中,而Monogame不支持GLSL / Dx12...。因此,您將不得不創造性地進行操作以免陷入困境。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM