欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL与CUDA的显存数据共享

程序员文章站 2024-02-09 18:02:04
...

所需头文件

#include<gl/glut.h>
#include "cuda_gl_interop.h"

涉及变量

cudaGraphicsResource_t cudaResource[1];//CUDA图像资源对象,用以联系OpenGL与CUDA
GLuint textureID[1];//OpenGL纹理上下文
cudaArray* devArray;//cuda共享数据区指针

操作流程

glEnable(GL_TEXTURE_2D);//开启2d上下文,告诉OpenGL要绘制的是2d的纹理
glGenTextures(1, textureID);//生成1个纹理上下文对象
glBindTexture(GL_TEXTURE_2D, textureID[0]);//绑定2d纹理上下文
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//设置2d纹理的插值方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);//生成2d纹理,对应当前的2d纹理上下文
//在CUDA中注册这2d纹理上下文,这样CUDA就知道有这么个玩意存在
cudaError_t err = cudaGraphicsGLRegisterImage(&cudaResource[0], textureID[0], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
if (err != cudaSuccess)
{
	std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
	return -1;
}
//告诉CUDA运行时映射这个共享资源
cudaGraphicsMapResources(1, cudaResource, 0);
//获得共享资源的指针,指针类型为cudaArray
cudaGraphicsSubResourceGetMappedArray(&devArray, cudaResource[0], 0, 0);
//拷贝图像数据到纹理中去
cudaMemcpyToArray(devArray, 0, 0, (void*)dpFrame, dec->GetWidth()*dec->GetHeight() * sizeof(uchar4), cudaMemcpyDeviceToDevice);
//解除锁定,让OpenGL操作
cudaGraphicsUnmapResources(1, &cudaResource[0], 0);
//可以用OpenGL在这里绘图了!
myDisplay();

示例程序

以之前这篇文章为例,可以改为OpenGL进行显示,代码如下:

#include <cuda.h>
#include <iostream>
#include "NvDecoder/NvDecoder.h"
#include "../Utils/NvCodecUtils.h"
#include "../Utils/FFmpegDemuxer.h"
#include "../Common/AppDecUtils.h"
#include "../Utils/ColorSpace.h"

#include<gl/glut.h>
#include "cuda_gl_interop.h"

// 初始化两个Texture并绑定
cudaGraphicsResource_t cudaResource[1];
GLuint textureID[1];
cudaArray* devArray;

simplelogger::Logger* logger = simplelogger::LoggerFactory::CreateConsoleLogger();

void myDisplay(void)
{
	//将纹理映射到四边形上
	glBegin(GL_QUADS);
	//纹理的坐标和四边形顶点的对应,可以通过设置四边形的位置调整图像在窗体的位置
	glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 0.0);
	glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
	glTexCoord2f(1.0, 1.0); glVertex3f(1.0, -1.0, 0.0);
	glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 0.0);
	glEnd();
	glFlush();
}

class HWdecode
{
public:
	//参数对象
	FFmpegDemuxer *demuxer = NULL;//FFMPEG对象
	CUcontext cuContext;//Cuda设备
	NvDecoder *dec = NULL;//Cuda解码对象
	CUdeviceptr dpFrame = 0;//数据存储对象初始化
	int hwinit()//显卡设备初始化
	{
		int iGpu = 0;//选择解码播放的GPU,只有一个的话直接设置为0就行
		//检测硬件GPU设备
		ck(cuInit(0));
		int nGpu = 0;
		ck(cuDeviceGetCount(&nGpu));
		if (iGpu < 0 || iGpu >= nGpu)
		{
			std::cout << "没有相应的GPU" << std::endl;
			return 0;
		}
		//获得CUDA对象
		CUdevice cuDevice = 0;
		ck(cuDeviceGet(&cuDevice, iGpu));
		char szDeviceName[80];
		ck(cuDeviceGetName(szDeviceName, sizeof(szDeviceName), cuDevice));
		std::cout << "GPU in use: " << szDeviceName << std::endl;
		ck(cuCtxCreate(&cuContext, CU_CTX_SCHED_BLOCKING_SYNC, cuDevice));
		return 1;
	}
	void ffmpeginit(char* url)//ffmpeg对象初始化
	{
		demuxer = new FFmpegDemuxer(url);
	}
	void cudadecoderinit()//cuda解码对象初始化
	{
		//RGBA帧存储显存初始化
		ck(cuMemAlloc(&dpFrame, demuxer->GetWidth() * demuxer->GetHeight() * 4));
		//解码器初始化
		dec = new NvDecoder(cuContext, (*demuxer).GetWidth(), (*demuxer).GetHeight(), true, FFmpeg2NvCodecId((*demuxer).GetVideoCodec()));
	}
	void decAndshow()//解码和播放
	{
		int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0;
		uint8_t* pVideo = NULL, ** ppFrame;
		long bt = clock();
		do
		{
			demuxer->Demux(&pVideo, &nVideoBytes);//获取一帧视频数据 
			dec->Decode(pVideo, nVideoBytes, &ppFrame, &nFrameReturned);//解码这一帧数据
			//if (!nFrame && nFrameReturned)
			//	LOG(INFO) << HWD.dec->GetVideoInfo();
			for (int i = 0; i < nFrameReturned; i++)
			{
				//对解码出来的图像数据类型做转换,转到BGRA
				if (dec->GetOutputFormat() == cudaVideoSurfaceFormat_YUV444)
					YUV444ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), (uint8_t*)dpFrame, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
				else    // default assumed as NV12
					Nv12ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), (uint8_t*)dpFrame, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
				//获得共享资源的指针,指针类型为cudaArray
				cudaGraphicsSubResourceGetMappedArray(&devArray, cudaResource[0], 0, 0);
				//拷贝图像数据到纹理中去
				cudaMemcpyToArray(devArray, 0, 0, (void*)dpFrame, dec->GetWidth()*dec->GetHeight() * sizeof(uchar4), cudaMemcpyDeviceToDevice);
				//解除锁定,让OpenGL操作
				cudaGraphicsUnmapResources(1, &cudaResource[0], 0);
				myDisplay();
			}
			nFrame += nFrameReturned;
		} while (nVideoBytes);

		ck(cuMemFree(dpFrame));
		std::cout << "Total frame decoded: " << nFrame << std::endl;
		std::cout << "花费时间:" << clock() - bt << "ms" << std::endl;
		std::cout << "FPS:" << nFrame / ((clock() - bt) / 1000.0) << std::endl;
		return;
	}
};

int main(int argc, char *argv[])
{
	//初始化OpenGL
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(720, 720);
	glutInitWindowPosition(200, 200);
	//创建窗口
	glutCreateWindow("imgshow");
	
	glEnable(GL_TEXTURE_2D);//开启2d上下文,告诉OpenGL要绘制的是2d的纹理
	glGenTextures(1, textureID);//生成1个2d上下文对象
	glBindTexture(GL_TEXTURE_2D, textureID[0]);//绑定2d上下文
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//设置2d上下文对象的插值方式
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);//生成2d纹理,对应当前的2d上下文
	//在CUDA中注册这2d上下文,这样CUDA就知道有这么个玩意存在
	cudaError_t err = cudaGraphicsGLRegisterImage(&cudaResource[0], textureID[0], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
	if (err != cudaSuccess)
	{
		std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
		return -1;
	}
	//告诉CUDA运行时映射这个共享资源
	cudaGraphicsMapResources(1, cudaResource, 0);
	HWdecode mhwdecoder;
	if (!mhwdecoder.hwinit())
	{
		std::cout << "硬件初始化失败!" << std::endl;
		return 0;
	}
	mhwdecoder.ffmpeginit("G:\\硬解码\\3.mp4");
	mhwdecoder.cudadecoderinit();
	mhwdecoder.decAndshow();
	return 1;
}