OpenGL与CUDA的显存数据共享

所需头文件
#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;
}
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值