美文网首页
CUDA与OpenGL互操作之纹理映射

CUDA与OpenGL互操作之纹理映射

作者: 爬上墙头deng红杏 | 来源:发表于2020-06-13 08:33 被阅读0次

引言

在《CUDA与OpenGL互操作之顶点缓存映射》中讲解了如何使用CUDA操作OpenGL中的顶点缓存数据,从而实现CUDA与OpenGL互操作。有时候我们常常使用CUDA对图像进行后处理,比如降噪增强。然后把结果显示在窗口上。这个时候使用VBO进行互操作显然是不合适的,CUDA为我们提供了另外一种手段来实现该目的,即使用纹理映射方式。

相关接口介绍

操作纹理的方式和操作顶点缓冲的方式基本一致,先注册资源,接着映射到CUDA流中,获取数组指针,将Cuda计算结果拷贝到纹理数组中。完成之后,取消资源映射,最后取消注册资源。

注册显卡资源

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

注册一个OpenGL纹理对象,参数说明:

  • resource - 指向返回的对象句柄
  • image - 被注册的纹理名称
  • flags - 注册标识
    • cudaGraphicsRegisterFlagsNone: 注册的资源在CUDA程序可以进行读写,默认为该枚举
    • cudaGraphicsRegisterFlagsReadOnly: 指定CUDA程序只能读该资源
    • cudaGraphicsRegisterFlagsWriteDiscard: 指定CUDA程序不会读该资源对象,每次写资源时资源的内容完全被覆盖,不会保留之前的任何数据
    • cudaGraphicsRegisterFlagsSurfaceLoadStore:指定CUDA将这个资源绑定到一个表面引用
    • cudaGraphicsRegisterFlagsTextureGather:指定CUDA将对该资源执行纹理收集操作

取消注册函数

__host__ ​cudaError_t cudaGraphicsUnregisterResource ( cudaGraphicsResource_t resource ) 

映射显卡资源

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

映射已经注册的资源到CUDA流中,参数说明:

  • count:映射的资源数
  • resources:映射的资源
  • stream:映射的CUDA同步流
    该函数具备默认同步动作

取消映射函数

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

获取纹理数组

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

获取一个数组,通过该数组可以访问已映射图形资源的子资源,参数说明:

  • array:返回的数组,通过该数组可以访问资源的子资源
  • resource:用于访问的已映射资源
  • arrayIndex:数组索引,数组纹理或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);       // 格式!!!m_cuPtr的数据类型必须与该格式匹配,采样器的类型也必须一致
    m_texture->setSize(DIM, DIM);                       // m_cuPtr的内存尺寸必须与纹理尺寸一致
    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核函数基本与示例代码保持一致

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

运行结果:


Cuda&GL.jpg

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

相关文章

网友评论

      本文标题:CUDA与OpenGL互操作之纹理映射

      本文链接:https://www.haomeiwen.com/subject/zjxitktx.html