2011-06-25 26 views
17

He estado leyendo la documentación de CUDA y me parece que cada búfer que necesita para interactuar con OpenGL necesita ser creado en el glBuffer.Cuda y OpenGL Interop

De acuerdo con la guía de programación de Nvidia, esto tiene que ser hecho de esta manera:

GLuint positionsVBO; 
struct cudaGraphicsResource* positionsVBO_CUDA; 

int main() { 

    // Explicitly set device 
    cudaGLSetGLDevice(0); 
    // Initialize OpenGL and GLUT 
    ... 
    glutDisplayFunc(display); 
    // Create buffer object and register it with CUDA 
    glGenBuffers(1, positionsVBO); 
    glBindBuffer(GL_ARRAY_BUFFER, &vbo); 
    unsigned int size = width * height * 4 * sizeof(float); 
    glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 
    cudaGraphicsGLRegisterBuffer(&positionsVBO_CUDA, positionsVBO, cudaGraphicsMapFlagsWriteDiscard); 

    // Launch rendering loop 
    glutMainLoop(); 
} 
void display() { 
    // Map buffer object for writing from CUDA 
    float4* positions; 
    cudaGraphicsMapResources(1, &positionsVBO_CUDA, 0); 
    size_t num_bytes; 
    cudaGraphicsResourceGetMappedPointer((void**)&positions, &num_bytes, positionsVBO_CUDA)); 
    // Execute kernel 
    dim3 dimBlock(16, 16, 1); 
    dim3 dimGrid(width/dimBlock.x, height/dimBlock.y, 1); 
    createVertices<<<dimGrid, dimBlock>>>(positions, time, width, height); 
    // Unmap buffer object 
    cudaGraphicsUnmapResources(1, &positionsVBO_CUDA, 0); 
    // Render from buffer object 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    glBindBuffer(GL_ARRAY_BUFFER, positionsVBO); 
    glVertexPointer(4, GL_FLOAT, 0, 0); 
    glEnableClientState(GL_VERTEX_ARRAY); 
    glDrawArrays(GL_POINTS, 0, width * height); 
    glDisableClientState(GL_VERTEX_ARRAY); 
    // Swap buffers 
    glutSwapBuffers(); 
    glutPostRedisplay(); 
} 
void deleteVBO() { 
    cudaGraphicsUnregisterResource(positionsVBO_CUDA); 
    glDeleteBuffers(1, &positionsVBO); 
} 

__global__ void createVertices(float4* positions, float time, unsigned int width, unsigned int height) { 
    // [....] 
} 

¿Hay una manera de dar al cudaMalloc creado un espacio de memoria directamente a OpenGL? Ya tengo el código de trabajo escrito en cuda y quiero poner mi matriz float4 directamente en OpenGL.

Di if've ya tiene un código como:

float4 *cd = (float4*) cudaMalloc(elements*sizeof(float4)). 
do_something<<<16,1>>>(cd); 

y quería mostrar la salida de hacer_algo través de OpenGL.

Nota al margen: ¿por qué la función cudaGraphicsResourceGetMappedPointer se ejecuta en cada paso de tiempo?

Respuesta

11

A partir de CUDA 4.0, la interoperabilidad OpenGL es unidireccional. Eso significa hacer lo que quiera (ejecutar un kernel CUDA que escribe datos en un búfer GL o una imagen de textura), tiene que asignar el búfer a un puntero de dispositivo, y pasar ese puntero a su kernel, como se muestra en el ejemplo.

En cuanto a su nota lateral: se llama a cudaGraphicsResourceGetMappedPointer cada vez que se llama a display() porque cudaGraphicsMapResource se llama cada cuadro. Cada vez que vuelva a mapear un recurso, debe volver a obtener el puntero mapeado, porque puede haber cambiado. ¿Por qué volver a mapear cada cuadro? Bueno, OpenGL a veces mueve objetos de memoria intermedia en la memoria, por razones de rendimiento (especialmente en aplicaciones GL con memoria intensiva). Si deja el recurso mapeado todo el tiempo, no puede hacer esto, y el rendimiento puede sufrir. Creo que la capacidad y necesidad de GL para virtualizar objetos de memoria también es una de las razones por las que la API actual de interoperabilidad GL es unidireccional (el GL no puede mover asignaciones CUDA, y por lo tanto no puede mapear un puntero del dispositivo asignado por CUDA en un objeto buffer de GL).

+0

así que supongo que no hay forma de evitar esto en este momento. Esperemos una mejor interoperabilidad en el futuro. – Pascal

+0

No veo lo que está mal con eso. Sí, sería más conveniente también poder pasar un puntero de dispositivo a OpenGL para usarlo como textura o VBO, pero no es tan fácil, y no veo por qué el otro camino no funciona solo también. ¿Tiene un caso de uso donde no puede hacer que su kernel escriba directamente en un puntero mapeado? – harrism

+0

Es principalmente un problema de velocidad. Digamos que quiero hacer algo de hpc, entonces quiero estar seguro de que no hay ninguna sobrecarga adicional mientras se usa el búfer de OpenGL. Y sería más fácil programarlo: solo dale a OpenGL la dirección en la tarjeta gráfica y cuéntale cuán grande es. – Pascal