如何在 dll 中使用 CUDA?

Posted

技术标签:

【中文标题】如何在 dll 中使用 CUDA?【英文标题】:How to use CUDA in a dll? 【发布时间】:2016-09-30 14:34:51 【问题描述】:

我的目的是通过一个存根库来使用 CUDA,它封装了 CUDA 特定的函数、方法和数据。 CUDA 内容将驻留在 dll 文件中,并将通过 LoadLibrary 和 GetProcAddress Windows API 函数动态使用。我正在使用 Visual Studio 2010 C++ 编译器创建 CUDA dll,但其余部分由另一个编译器完成。这意味着,我不能在 CUDA dll 中使用堆内存(即 malloc、new 或任何东西,它们位于堆栈内存之外。即使是全局变量也会导致内存损坏),因此它实际上是一个存根库。

但是,首先我想编写一个测试程序:CUDAhost.exe 和 CUDAdevice.dll,它们都是由 Visual Studio 2010 编译的,两个项目在一个解决方案中。这个程序在屏幕上绘制了一个 OpenGL 纹理,但首先图像数据由 CUDA 从 readGLTexture 复制到 viewGLTexture。请注意,为了避免使用堆内存,我使用了 void 指针引用 void* &cReadCudaResource 和 void* &cViewCudaResource。我的问题是我无法让程序运行,窗口是黑色的。我找不到错误。我不确定这是否可能,或者我应该选择完全不同的解决方案。我希望你能帮助我。任何建议表示赞赏。以下是源代码:

CUDAhost.cpp:

#include "stdafx.h"

const unsigned int window_width  = 512;
const unsigned int window_height = 512;

GLuint viewGLTexture;
GLuint readGLTexture;
void* cViewCudaResource;
void* cReadCudaResource;
HINSTANCE dll;
typedef void (*SETCUDA)(unsigned int& readGLTexture, void*   &cReadCudaResource, unsigned int& viewGLTexture, void* &cViewCudaResource);
SETCUDA setCuda;
typedef void (*DRAWPICTURE)(void* &cReadCudaResource, void* &cViewCudaResource);
DRAWPICTURE drawPicture;

bool loadTexture(const wchar_t* name, GLuint& number) 

  FILE* file;
  BITMAPFILEHEADER bitmapFileHeader;
  BITMAPINFOHEADER bitmapInfoHeader;
  unsigned char *bitmap;
  unsigned char temp;
  wchar_t path[45]=0;
  int width;
  int height;

//prepare file path
  wcsncat_s(path, L"Textures\\", 45);
  wcsncat_s(path, name, 45);
  wcsncat_s(path, L".bmp", 45);

//open BMP file
  file=_wfopen(path, L"rb");
  if (file==NULL) 
    return false;
  

//read bmp file header and sequre it is bmp file
  fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, file);
  if (bitmapFileHeader.bfType != 0x4D42) 
    fclose(file);
    return false;
  

//read bmp info header and move to the beginning of image data
  fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, file);
  fseek(file, bitmapFileHeader.bfOffBits, SEEK_SET);

//allocate memory space
  bitmap=(unsigned char*)malloc(bitmapInfoHeader.biSizeImage);
  if (!bitmap) 
free(bitmap);
bitmap=NULL;
    fclose(file);
    return false;
  

//read image
  fread(bitmap, 1, bitmapInfoHeader.biSizeImage, file);
  if (file==NULL) 
free(bitmap);
bitmap=NULL;
    fclose(file);
    return false;
  

//rearrange bgr to rgb
  for (int i=0; i<bitmapInfoHeader.biSizeImage; i+=3) 
    temp=bitmap[i];
    bitmap[i]=bitmap[i+2];
    bitmap[i+2]=temp;
  

//query image width and height
  width=bitmapInfoHeader.biWidth;
  height=abs(bitmapInfoHeader.biHeight);

//close bmp file
  fclose(file);
  glGetError();

//create OpenGL texture
  glGenTextures(1, &number);
  glBindTexture(GL_TEXTURE_2D, number);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

//free temporary buffer
  free(bitmap);
  bitmap=NULL;

//if success, return true
  if (0==glGetError()) 
    return true;
   else 
    return false;
  


void initGLandCUDA(int argc, char* argv[]) 

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA);
  glutInitWindowSize(window_width, window_height);
  glutCreateWindow("CUDA GL Interop");

  glewInit();

  glEnable(GL_TEXTURE_2D);
  bool success=loadTexture(L"Tex", readGLTexture);

  glGenTextures(1, &viewGLTexture);
  glBindTexture(GL_TEXTURE_2D, viewGLTexture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glBindTexture(GL_TEXTURE_2D, 0);

  dll=LoadLibraryW(L"CUDAdevice.dll");
  if (dll) 
    setCuda=(SETCUDA)GetProcAddress(dll, "setCuda");
    setCuda(readGLTexture, cReadCudaResource, viewGLTexture, cViewCudaResource);
  
    

void renderFrame() 
  if (dll) 
    drawPicture=(DRAWPICTURE)GetProcAddress(dll, "drawPicture");
    drawPicture(cReadCudaResource, cViewCudaResource);
  
  glBindTexture(GL_TEXTURE_2D, viewGLTexture);
  
    glBegin(GL_QUADS);
    
      glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f);
      glTexCoord2f(1.0f, 0.0f); glVertex2f(+1.0f, -1.0f);
      glTexCoord2f(1.0f, 1.0f); glVertex2f(+1.0f, +1.0f);
      glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, +1.0f);
    
    glEnd();
  
  glBindTexture(GL_TEXTURE_2D, 0);
  glFinish();


int _tmain(int argc, _TCHAR* argv[])

  initGLandCUDA(argc, reinterpret_cast<char**>(argv));
  glutDisplayFunc(renderFrame);
  glutMainLoop();
  return 0;

dllmain.cpp:

BOOL APIENTRY DllMain( HMODULE hModule,
                   DWORD  ul_reason_for_call,
                   LPVOID lpReserved
                 )

  switch (ul_reason_for_call)
  
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    break;
  
  return TRUE;


//this function is used to setup CUDA
void setCuda(unsigned int& readGLTexture, void* &cReadCudaResource, unsigned int& viewGLTexture, void* &cViewCudaResource) 

  struct cudaGraphicsResource* viewCudaResource;
  struct cudaGraphicsResource* readCudaResource;
  cudaError cError;

  cudaGLSetGLDevice(0);
  cError=cudaGraphicsGLRegisterImage(&viewCudaResource, viewGLTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
  cError=cudaGraphicsGLRegisterImage(&readCudaResource, readGLTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsSurfaceLoadStore);
  cReadCudaResource=reinterpret_cast<void*>(readCudaResource);
  cViewCudaResource=reinterpret_cast<void*>(viewCudaResource);
    

//this function is used to draw texture image via CUDA
void drawPicture(void* &cReadCudaResource, void* &cViewCudaResource) 

  cudaError cError;

  struct cudaGraphicsResource* viewCudaResource=reinterpret_cast<cudaGraphicsResource*>(cReadCudaResource);
  struct cudaGraphicsResource* readCudaResource=reinterpret_cast<cudaGraphicsResource*>(cViewCudaResource);
  cudaArray *readCudaArray;
  cudaArray *viewCudaArray;

  cError=cudaGraphicsMapResources(1, &readCudaResource);
  cError=cudaGraphicsMapResources(1, &viewCudaResource);
  cError=cudaGraphicsSubResourceGetMappedArray(&readCudaArray, readCudaResource, 0, 0);
  cError=cudaGraphicsSubResourceGetMappedArray(&viewCudaArray, viewCudaResource, 0, 0);
  callCUDAKernel(readCudaArray, viewCudaArray);
  cudaGraphicsUnmapResources(1, &viewCudaResource);
  cudaStreamSynchronize(0);

kernels.cu:

    #include "stdafx.h"

texture<uchar4, cudaTextureType2D, cudaReadModeElementType> readCudaTextureObject;
surface<void, cudaSurfaceType2D> viewCudaSurfaceObject;


__global__ void renderingKernel() 

  unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
  unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;

  uchar4 dd=tex2D(readCudaTextureObject, x, y);

  surf2Dwrite(dd, viewCudaSurfaceObject, x*sizeof(dd), y, cudaBoundaryModeZero);



void callCUDAKernel(cudaArray *readCudaArray, cudaArray *viewCudaArray) 

  cudaError cError;

  cError=cudaBindTextureToArray(readCudaTextureObject, readCudaArray);
  cError=cudaBindSurfaceToArray(viewCudaSurfaceObject, viewCudaArray);
  dim3 block(256, 1, 1);
  dim3 grid(2, 512, 1);
  renderingKernel<<<grid, block>>>();
  cudaPeekAtLastError();
  cudaDeviceSynchronize();

CUDAdevice 的 stdafx.h:

    #pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>



// TODO: reference additional headers your program requires here
#include <cuda_runtime_api.h>
#include <cuda_gl_interop.h>

#include "kernels.h"

#if defined (__cplusplus)
extern "C"

#endif

__declspec(dllexport) void setCuda(unsigned int& readGLTexture, void* &cReadCudaResource, unsigned int& viewGLTexture, void* &cViewCudaResource);
__declspec(dllexport) void drawPicture(void* &cReadCudaResource, void* &cViewCudaResource);

#if defined (__cplusplus)

#endif

kernels.h:

    #ifndef __kernels_H
#define __kernels_H

void callCUDAKernel(cudaArray *readCudaArray, cudaArray *viewCudaArray);

#endif

PS。我还设置了 CUDA 库、头文件、源代码和二进制文件的路径,将 cudart.lib 添加到其他依赖项,设置 CUDA 4.2 目标和 compute_20、sm_21。测试程序使用 GLEW 和 GLUT 库。

【问题讨论】:

没有 DLL 也能工作吗?你使用调试器吗?错误检查在哪里? 这个测试程序有效 你好Drop,这个测试程序可以在没有DLL的情况下工作,但真正的可能不会,因为大多数代码不会被VS编译。是的,我使用 Visual Studio 调试器,它可以同时运行 EXE 和 DLL。除了 cudaError cError 之外没有错误检查以保持代码简单:我可以使用 VS 调试器查看不存在错误。但是,屏幕仍然是黑色的,所以那里出了点问题。我想了解我的 cudaGraphicsResource* 指针使用情况。 我写了一个与上面类似的测试程序,但它只包含一个EXE文件。是的,它将纹理绘制到屏幕上,不再是黑屏!但是,问题是,我需要将 CUDA 的东西隔离到一个单独的 DLL 文件中,所以我希望有人可以帮助我让上面的代码工作:) 【参考方案1】:

如果有人读到这里更好的更新解决方案...

上面的代码不能工作。整个想法是避免在 DLL 模块中使用堆内存。但是,CUDA 需要纹理和表面是全局的:

texture<uchar4, cudaTextureType2D, cudaReadModeElementType>readCudaTextureObject;
surface<void, cudaSurfaceType2D> viewCudaSurfaceObject;

但是,为了避免使用堆内存,要求这些是本地的,所以这是不可能的。

此外,根据我的理解,在 CUDA DLL 模块中使用堆内存应该没有任何问题。我已经制作了可以工作的 EXE/DLL 测试程序,它们在 DLL 模块中使用堆内存。在我之前的尝试中我没有发现这个错误,但这可能是由于 GLEW 库。我没有在我的工作程序中使用 GLEW。

【讨论】:

以上是关于如何在 dll 中使用 CUDA?的主要内容,如果未能解决你的问题,请参考以下文章

在导出DLL的非启动项目中使用Nsight调试CUDA代码

CUDA 链接错误(Lib 到 Dll)

如何在 Windows 10 中安装 Tensorrt

在 VS2010 (CUDA) 中导出 DLL 的问题

无法使用 Cuda 引用编译 dll

如何通过C#调用OpenCV函数(自制OpenCV的c++ dll文件)