CUDA與OpenGL互操作之紋理映射

引言

在《CUDA與OpenGL互操作之頂點緩存映射》中講解了如何使用CUDA操作OpenGL中的頂點緩存數(shù)據(jù)扒寄,從而實現(xiàn)CUDA與OpenGL互操作烙常。有時候我們常常使用CUDA對圖像進行后處理国葬,比如降噪增強心包。然后把結(jié)果顯示在窗口上跪腹。這個時候使用VBO進行互操作顯然是不合適的畏梆,CUDA為我們提供了另外一種手段來實現(xiàn)該目的闸婴,即使用紋理映射方式坏挠。

相關(guān)接口介紹

操作紋理的方式和操作頂點緩沖的方式基本一致,先注冊資源邪乍,接著映射到CUDA流中降狠,獲取數(shù)組指針,將Cuda計算結(jié)果拷貝到紋理數(shù)組中庇楞。完成之后榜配,取消資源映射,最后取消注冊資源吕晌。

注冊顯卡資源

__host__ ?cudaError_t cudaGraphicsGLRegisterImage ( cudaGraphicsResource** resource, GLuint image, GLenum target, unsigned int  flags ) 

注冊一個OpenGL紋理對象蛋褥,參數(shù)說明:

  • resource - 指向返回的對象句柄
  • image - 被注冊的紋理名稱
  • flags - 注冊標(biāo)識
    • cudaGraphicsRegisterFlagsNone: 注冊的資源在CUDA程序可以進行讀寫,默認為該枚舉
    • cudaGraphicsRegisterFlagsReadOnly: 指定CUDA程序只能讀該資源
    • cudaGraphicsRegisterFlagsWriteDiscard: 指定CUDA程序不會讀該資源對象睛驳,每次寫資源時資源的內(nèi)容完全被覆蓋烙心,不會保留之前的任何數(shù)據(jù)
    • cudaGraphicsRegisterFlagsSurfaceLoadStore:指定CUDA將這個資源綁定到一個表面引用
    • cudaGraphicsRegisterFlagsTextureGather:指定CUDA將對該資源執(zhí)行紋理收集操作

取消注冊函數(shù)

__host__ ?cudaError_t cudaGraphicsUnregisterResource ( cudaGraphicsResource_t resource ) 

映射顯卡資源

__host__ ?cudaError_t cudaGraphicsMapResources ( int  count, cudaGraphicsResource_t* resources, cudaStream_t stream = 0 ) 

映射已經(jīng)注冊的資源到CUDA流中,參數(shù)說明:

  • count:映射的資源數(shù)
  • resources:映射的資源
  • stream:映射的CUDA同步流
    該函數(shù)具備默認同步動作

取消映射函數(shù)

cudaError_t cudaGraphicsUnmapResources(int count, cudaGraphicsResource_t *resources, cudaStream_t stream __dv(0));

獲取紋理數(shù)組

__host__ ?cudaError_t cudaGraphicsSubResourceGetMappedArray ( cudaArray_t* array, cudaGraphicsResource_t resource, unsigned int  arrayIndex, unsigned int  mipLevel )

獲取一個數(shù)組乏沸,通過該數(shù)組可以訪問已映射圖形資源的子資源淫茵,參數(shù)說明:

  • array:返回的數(shù)組,通過該數(shù)組可以訪問資源的子資源
  • resource:用于訪問的已映射資源
  • arrayIndex:數(shù)組索引蹬跃,數(shù)組紋理或cubemap面的索引匙瘪,通過cudaGraphicsCubeFace對cubemap紋理進行子資源訪問
  • mipLevel:用于子資源訪問的Mipmap級別

使用示例

參照CUDA simpleCUDA2GL示例,使用Qt進行改寫:

#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>

#include <cuda_runtime.h>
#include <cuda_gl_interop.h>

class Shader;
class QOpenGLTexture;
class CuTexImg : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    CuTexImg(QWidget *parent = nullptr);
    ~CuTexImg();

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();

private:
    void makeObject();
    void runCuda();
    void cleanup();

private:
    Shader* m_shader;
    QOpenGLTexture* m_texture;
    QOpenGLBuffer m_vbo, m_ibo;
    QOpenGLVertexArrayObject m_vao;

    uchar4* m_cuPtr;
    cudaGraphicsResource_t m_resource;
};
#include "CuTexImg.h"
#include "Shader.h"
#include <QOpenGLTexture>
#include "helper_cuda.h"

static const int DIM = 512;

extern "C" void
launch_cudaProcess(dim3 grid, dim3 block, uchar4 *g_odata, int imgw);

CuTexImg::CuTexImg(QWidget *parent) : QOpenGLWidget(parent), m_vbo(QOpenGLBuffer::VertexBuffer), m_ibo(QOpenGLBuffer::IndexBuffer)
{
    resize(DIM, DIM);
}

CuTexImg::~CuTexImg() 
{

}

void CuTexImg::initializeGL() 
{
    initializeOpenGLFunctions();

    makeObject();

    checkCudaErrors(cudaGraphicsGLRegisterImage(&m_resource, m_texture->textureId(), GL_TEXTURE_2D, cudaGraphicsMapFlagsWriteDiscard));
    checkCudaErrors(cudaMalloc((void**)&m_cuPtr, DIM*DIM * sizeof(uchar4)));
    runCuda();

    connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &CuTexImg::cleanup);
}

void CuTexImg::resizeGL(int w, int h) 
{
    glViewport(0, 0, w, h);
}

void CuTexImg::paintGL() 
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    m_shader->bind();
    m_texture->bind();
    m_vao.bind();
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);    
}

void CuTexImg::makeObject()
{
    m_shader = new Shader("shaders/cuTexImg.vert", "shaders/cuTexImg.frag", this);

    float vertices[] = {
        // positions        // texture coords
         1.0f,  1.0f, 0.0f, 1.0f, 1.0f, // top right
         1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
        -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
        -1.0f,  1.0f, 0.0f, 0.0f, 1.0f  // top left 
    };
    unsigned int indices[] = {  // note that we start from 0!
        0, 1, 3,  // first Triangle
        1, 2, 3   // second Triangle
    };

    m_vao.create();
    m_vao.bind();

    m_vbo.create();
    m_vbo.bind();
    m_vbo.allocate(vertices, sizeof(vertices));

    m_ibo.create();
    m_ibo.bind();
    m_ibo.allocate(indices, sizeof(indices));

    m_shader->setAttributeBuffer("aPos", GL_FLOAT, 0, 3, 5 * sizeof(float));
    m_shader->enableAttributeArray("aPos");
    m_shader->setAttributeBuffer("aTexCoord", GL_FLOAT, 3 * sizeof(float), 2, 5 * sizeof(float));
    m_shader->enableAttributeArray("aTexCoord");
    
    m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
    m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
    m_texture->setMinificationFilter(QOpenGLTexture::Nearest);
    m_texture->setWrapMode(QOpenGLTexture::Repeat);
    m_texture->setFormat(QOpenGLTexture::RGBA8U);       // 格式5骸5び鳌!m_cuPtr的數(shù)據(jù)類型必須與該格式匹配,采樣器的類型也必須一致
    m_texture->setSize(DIM, DIM);                       // m_cuPtr的內(nèi)存尺寸必須與紋理尺寸一致
    m_texture->allocateStorage();
}

void CuTexImg::runCuda()
{
    cudaArray_t glArray;
    checkCudaErrors(cudaGraphicsMapResources(1, &m_resource, NULL));
    checkCudaErrors(cudaGraphicsSubResourceGetMappedArray(&glArray, m_resource, 0, 0));
    
    dim3 blocks(16, 16);
    dim3 grids((DIM - 1) / 16 + 1, (DIM - 1) / 16 + 1);
    launch_cudaProcess(grids, blocks, m_cuPtr, DIM);

    checkCudaErrors(cudaMemcpyToArray(glArray, 0, 0, m_cuPtr, DIM * DIM * sizeof(uchar4), cudaMemcpyDeviceToDevice));
    
    checkCudaErrors(cudaGraphicsUnmapResources(1, &m_resource, nullptr));
}

void CuTexImg::cleanup()
{
    makeCurrent();

    // 釋放顯存
    checkCudaErrors(cudaFree(m_cuPtr));
    // 取消資源注冊   
    checkCudaErrors(cudaGraphicsUnregisterResource(m_resource));

    // 銷毀VAO/VBO
    m_vao.destroy();
    m_vbo.destroy();

    // 釋放紋理
    delete m_texture;

    doneCurrent();
}

接下來是cuda代碼扼劈,cuda核函數(shù)基本與示例代碼保持一致

#include "helper_cuda.h"

// clamp x to range [a, b]
__device__ float clamp(float x, float a, float b)
{
    return max(a, min(b, x));
}

// convert floating point rgb color to 8-bit integer
__device__ uchar4 rgbToInt(float r, float g, float b)
{
    r = clamp(r, 0.0f, 255.0f);
    g = clamp(g, 0.0f, 255.0f);
    b = clamp(b, 0.0f, 255.0f);
    return make_uchar4(r, g, b, 0);
}

static __global__ void cudaProcess(uchar4* g_odata, int imgw)
{
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    int bw = blockDim.x;
    int bh = blockDim.y;
    int x = blockIdx.x*bw + tx;
    int y = blockIdx.y*bh + ty;

    uchar4 c4 = make_uchar4((x & 0x20) ? 100 : 0, 0, (y & 0x20) ? 100 : 0, 0);
    g_odata[y*imgw + x] = rgbToInt(c4.z, c4.y, c4.x);
}

extern "C" void
launch_cudaProcess(dim3 grid, dim3 block, uchar4 *g_odata, int imgw)
{
    cudaProcess << < grid, block >> > (g_odata, imgw);
}

最后是頂點著色器代碼和片段著色器代碼

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}
#version 330

in vec2 TexCoord;
uniform usampler2D texImage;

void main()
{
    vec4 c = texture(texImage, TexCoord);
    gl_FragColor = c / 255.0;
}

運行結(jié)果:


Cuda&GL.jpg

參考文章:
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驻啤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荐吵,更是在濱河造成了極大的恐慌骑冗,老刑警劉巖赊瞬,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贼涩,居然都是意外死亡巧涧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門遥倦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谤绳,“玉大人,你說我怎么就攤上這事袒哥∷跎福” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵堡称,是天一觀的道長瞎抛。 經(jīng)常有香客問我,道長却紧,這世上最難降的妖魔是什么桐臊? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮晓殊,結(jié)果婚禮上断凶,老公的妹妹穿的比我還像新娘。我一直安慰自己巫俺,他們只是感情好认烁,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著识藤,像睡著了一般砚著。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痴昧,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天稽穆,我揣著相機與錄音,去河邊找鬼赶撰。 笑死舌镶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的豪娜。 我是一名探鬼主播餐胀,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘤载!你這毒婦竟也來了否灾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤鸣奔,失蹤者是張志新(化名)和其女友劉穎墨技,沒想到半個月后惩阶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡扣汪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年断楷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崭别。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡冬筒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茅主,到底是詐尸還是另有隱情舞痰,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布暗膜,位于F島的核電站匀奏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏学搜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一论衍、第九天 我趴在偏房一處隱蔽的房頂上張望瑞佩。 院中可真熱鬧,春花似錦坯台、人聲如沸炬丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稠炬。三九已至,卻和暖如春咪啡,著一層夾襖步出監(jiān)牢的瞬間首启,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工撤摸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毅桃,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓准夷,卻偏偏與公主長得像钥飞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衫嵌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354