CUDA approfondi


précédentsommairesuivant

XV. Interopérabilité avec DirectX

Des ressources Direct3D peuvent être utilisées avec CUDA, pour que CUDA puisse lire ces données pour les traiter, ou bien pour que CUDA puisse écrire des données qui seront utilisées par Direct3D.

CUDA peut fonctionner avec les versions 9.0 et 10.0 de Direct3D. Pour la première, les fonctions sont préfixées par cudaD3D9. Pour la seconde, par cudaD3D10. C'est le seul élément qui change entre les deux fonctions (à part le fait qu'elles ne sont pas utilisées avec les mêmes versions de D3D). C'est pourquoi je ne préciserai que les fonctions pour D3D9.

Cependant, tout ne peut pas fonctionner comme vous le désirez, mais ces restrictions restent très logiques. Un contexte CUDA ne peut communiquer qu'avec un seul périphérique D3D à la fois et ils doivent correspondre au même GPU. De plus, le périphérique D3D doit être créé avec le drapeau D3DCREATE_HARDWARE_VERTEXPROCESSING.

 
Sélectionnez
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
    return E_FAIL;

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( & d3dpp, sizeof(d3dpp) );
d3dpp.Windowed              = TRUE;
d3dpp.BackBufferCount       = 1;
d3dpp.SwapEffect            = D3DSWAPEFFECT_COPY;
d3dpp.hDeviceWindow         = g_handles.hwndText;
d3dpp.BackBufferWidth       = IMAGE_SIZE_X;
d3dpp.BackBufferHeight      = IMAGE_SIZE_Y;
d3dpp.BackBufferFormat      = D3DFMT_UNKNOWN;
d3dpp.PresentationInterval  = D3DPRESENT_INTERVAL_IMMEDIATE;

hr = g_pD3D->CreateDevice (adapter, D3DDEVTYPE_HAL, g_handles.hwndCGH,
                            D3DCREATE_HARDWARE_VERTEXPROCESSING, 
                            &d3dpp, &g_pD3DDevice);
if( FAILED( hr ) )
    return hr;

cudaD3D9SetDirect3DDevice(g_pD3DDevice);

La fonction cudaD3D9SetDirect3DDevice() doit être appelée avant toute autre fonction de CUDA !

Pour associer une ressource DirectX à CUDA, cudaD3D9RegisterResource() est utilisé.

 
Sélectionnez
IDirect3DVertexBuffer9 * vbuffer;
cudaD3D9RegisterResource(vbuffer, cudaD3D9RegisterFlagsNone);

IDirect3DSurface9 * surface;
cudaD3D9RegisterResource(surface, cudaD3D9RegisterFlagsNone);

ID3D9Buffer * buffer;
cudaD3D9RegisterResource(buffer, cudaD3D9RegisterFlagsNone);

ID3D9Texture2D* tex2D;
cudaD3D9RegisterResource(tex2D, cudaD3D9RegisterFlagsNone);

Vous pouvez désenregistrer les ressources grâce à cudaD3D9UnregisterVertexBuffer()

 
Sélectionnez
cudaD3D9UnregisterVertexBuffer(vbuffer);
cudaD3D9UnregisterVertexBuffer(surface);
cudaD3D9UnregisterVertexBuffer(buffer);
cudaD3D9UnregisterVertexBuffer(tex2D);

Dès qu'une ressource est enregistrée, vous pouvez la lier avec cudaD3D9MapResources() et la délier avec cudaD3D9UnmapResources(). Dès qu'elle est liée, vous pouvez l'utiliser dans un kernel. Pour ce faire, vous aurez besoin de son adresse, retournée par cudaD3D9ResourceGetMappedPointer(). Si vous accédez à une ressource liée par D3D, les résultats sont indéfinis.

 
Sélectionnez
ID3D9Buffer * buffer;
cudaD3D9RegisterResource(buffer, cudaD3D9RegisterFlagsNone);

void * devPtr;
cudaD3D9ResourceGetMappedPointer(& devPtr, buffer, 0, 0);

size_t size;
cudaD3D9ResourceGetMappedSize(& size, buffer, 0, 0);

cudaMemset(devPtr, 0, size);

Dans ce dernier exemple, chaque thread accède à un pixel de la surface 2D.

 
Sélectionnez
void* devPtr;
cudaD3D9ResourceGetMappedPointer(& devPtr, surface, 0, 0);
size_t pitch;
cudaD3D9ResourceGetMappedPitch(& pitch, 0, surface, 0, 0);
dim3 Db = dim3(16, 16);
dim3 Dg = dim3( (width+Db.x-1) / Db.x, (height+Db.y-1) / Db.y);
mykernel <<< Dg, Db >>> ( (unsigned char *) devPtr, width, height, pitch);
 
Sélectionnez
__global__ void mykernel(unsigned char * surface,
                         int width, int height, size_t pitch)
{
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    if (x >= width || y >= height) return;
    float * pixel = (float *) (surface + y * pitch) + 4 * x;
}

XVI. Interopérabilité avec OpenGL

Des ressources OpenGL peuvent être utilisées avec CUDA, pour que CUDA puisse lire ces données pour les traiter, ou bien pour que CUDA puisse écrire des données qui seront utilisées par OpenGL.

Tout comme Direct3D, la fonction cudaGLSetGLDevice() doit être appelée avant toutes les autres fonctions CUDA.

Un buffer peut être enregistré avec la fonction cudaGLRegisterBufferObject() et lié avec cudaGLMapBufferObject().

Vous pouvez délier avec cudaGLUnmapBufferObject() et désenregistrer avec cudaGLUnregisterBufferObject()

 
Sélectionnez
GLuint bufferObj;

cudaGLRegisterBufferObject(bufferObj);

float * devPtr;

cudaGLMapBufferObject( (void **) & devPtr, bufferObj);

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

CUDA et le GPGPU
Introduction à CUDA
CUDA approfondi
La FAQ GPGPU & CUDA
  

Copyright © 2009 Thibaut Cuvelier. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.