2017-07-26 2 views
1

J'essaie d'exécuter des parties du code this sur le GPU en utilisant OpenCL. J'essaie maintenant d'exécuter la fonction qui traite de YCbCr à la conversion RVB.Le noyau OpenCL provoque l'exécution indéfinie de l'application et ne s'arrête que lorsque j'ai arrêté mon IDE.

Veuillez noter qu'à partir de maintenant je n'essaie pas d'optimiser le code GPU. Je veux simplement une sortie identique à celle du CPU.

La fonction est à l'origine écrit comme ceci:

void YCbCr_to_ARGB(uint8_t *YCbCr_MCU[3], uint32_t *RGB_MCU, uint32_t nb_MCU_H, uint32_t nb_MCU_V) 
{ 

    uint8_t *MCU_Y, *MCU_Cb, *MCU_Cr; 
    int R, G, B; 
    uint32_t ARGB; 
    uint8_t index, i, j; 

    MCU_Y = YCbCr_MCU[0]; 
    MCU_Cb = YCbCr_MCU[1]; 
    MCU_Cr = YCbCr_MCU[2]; 
    for (i = 0; i < 8 * nb_MCU_V; i++) { 
     for (j = 0; j < 8 * nb_MCU_H; j++) { 
      index = i * (8 * nb_MCU_H) + j; 
      R = (MCU_Cr[index] - 128) * 1.402f + MCU_Y[index]; 
      B = (MCU_Cb[index] - 128) * 1.7772f + MCU_Y[index]; 
      G = MCU_Y[index] - (MCU_Cb[index] - 128) * 0.34414f - 
       (MCU_Cr[index] - 128) * 0.71414f; 
      /* Saturate */ 
      if (R > 255) 
       R = 255; 
      if (R < 0) 
       R = 0; 
      if (G > 255) 
       G = 255; 
      if (G < 0) 
       G = 0; 
      if (B > 255) 
       B = 255; 
      if (B < 0) 
       B = 0; 
      ARGB = ((R & 0xFF) << 16) | ((G & 0xFF) << 8) | (B & 0xFF); 
      // ARGB = 0xFF << 8; 
      RGB_MCU[(i * (8 * nb_MCU_H) + j)] = ARGB; 
     } 
    } 
} 

Les variables de cette fonction sont déclarées dans main.c de la manière suivante:

cl_uchar* YCbCr_MCU[3] = { NULL, NULL, NULL}; 
cl_uint* RGB_MCU = NULL; 

mémoire pour ces variables est attribué dans ce chemin:

if (screen_init_needed == 1) 
{ 
        screen_init_needed = 0; 

..... 
..... 
//Some code 

for (index = 0 ; index < SOF_section.n ; index++) { 
YCbCr_MCU[index] = malloc(MCU_sx * MCU_sy * max_ss_h * max_ss_v); 

YCbCr_MCU_ds[index] = malloc(MCU_sx * MCU_sy * max_ss_h * max_ss_v); 

} 

RGB_MCU = malloc (MCU_sx * MCU_sy * max_ss_h * max_ss_v * sizeof(cl_int)); 
} 

break; 
} 

Je l'ai copié et collé directement dans mon fichier .cl et apporté quelques modifications mineures pour le rendre conforme aux normes OpenCL. Mon code OpenCL modifié ressemblait à ceci:

__kernel void YCbCr_to_ARGB(__global uchar* YCbCr_MCU[3], __global uint* RGB_MCU, uint nb_MCU_H, uint nb_MCU_V) 
    {   
     __global uchar *MCU_Y, *MCU_Cb, *MCU_Cr; 
     int R, G, B; 
     uint ARGB; 
     uchar index, i, j; 

     MCU_Y = YCbCr_MCU[0]; 
     MCU_Cb = YCbCr_MCU[1]; 
     MCU_Cr = YCbCr_MCU[2]; 

//Same code as the first code snippet 
     ...... 
     ...... 
     ...... 

    } 

Quand je courais et construit ma demande avec le code du noyau ci-dessus dans mon dossier .cl, je suis arrivé erreurs. L'une des erreurs indique qu'OpenCL n'autorise pas le pointage vers les arguments pointeurs.

Pour contourner ces erreurs, je modifié mon code à nouveau pour ressembler à ceci:

__kernel void YCbCr_to_ARGB(__global uchar YCbCr_MCU[3], __global uint* RGB_MCU, uint nb_MCU_H, uint nb_MCU_V) 
{   
      __global uchar *MCU_Y, *MCU_Cb, *MCU_Cr; 
      int R, G, B; 
      uint ARGB; 
      uchar index, i, j; 

      MCU_Y = &YCbCr_MCU[0]; 
      MCU_Cb = &YCbCr_MCU[1]; 
      MCU_Cr = &YCbCr_MCU[2]; 

    //Same code as the first code snippet 
      ...... 
      ...... 
      ...... 
} 

Quand je courais et construit à nouveau l'application, je n'ai pas eu d'erreurs. Cela m'a incité à écrire le code hôte pour ce noyau.

Il ressemble à ceci:

color_kernel= clCreateKernel(program, "YCbCr_to_ARGB", &ret); 

//YCbCr_MCU for YCbCrtoARGB 
cl_mem colorMCU_GPU= clCreateBuffer(context, CL_MEM_READ_WRITE, 3 * sizeof(cl_uchar), NULL, &ret); 


//rgb_MCU for YCbCrtoARGB 
cl_mem RGB_GPU= clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(cl_uint), NULL, &ret); 

J'ai appelé les arguments du noyau exactement où la fonction d'origine a été appelée main.c. Je fis les étapes restantes pour ce noyau de la manière suivante:

if(color&&(SOF_section.n>1) 
{ 
ret = clEnqueueWriteBuffer(command_queue, colorMCU_GPU, CL_TRUE, 0, 3 * sizeof(cl_uchar), YCbCr_MCU, 0, NULL, NULL); 

ret = clEnqueueWriteBuffer(command_queue, RGB_GPU, CL_TRUE, 0, sizeof(cl_uint), RGB_MCU, 0, NULL, NULL); 

ret = clSetKernelArg(color_kernel, 0, sizeof(cl_mem), (void *)&colorMCU_GPU); 
ret |= clSetKernelArg(color_kernel, 1, sizeof(cl_mem), (void *)&RGB_GPU); 
ret = clSetKernelArg(color_kernel, 2, sizeof(cl_uint), (void *)&max_ss_h); 
ret |= clSetKernelArg(color_kernel, 3, sizeof(cl_uint), (void *)&max_ss_v); 

ret = clEnqueueTask(command_queue, color_kernel, 0, NULL, NULL); 

ret = clEnqueueReadBuffer(command_queue, RGB_GPU, CL_TRUE, 0, sizeof(cl_uint), RGB_MCU, 0, NULL, NULL); 

//YCbCr_to_ARGB(YCbCr_MCU, RGB_MCU, max_ss_h, max_ss_v); 

Après exécuter et compiler le code avec ces arguments, le code continue à fonctionner indéfiniment (La sortie de ce qui est censé être un clip vidéo en cours d'exécution sur un écran avec ce code, je n'ai qu'un écran noir). Je dois fermer Eclipse et le rouvrir pour apporter des modifications supplémentaires au code après cela.

Qu'est-ce qui provoque le comportement du programme comme ceci? Est-il possible d'exécuter cette fonction en toute sécurité sur le GPU?

Mise à jour:

J'ai suivi les conseils de Anders Cedronius et changé mon code du noyau de la manière suivante:

__kernel void YCbCr_to_ARGB(__global uchar YCbCr_MCU[3], __global uint* RGB_MCU, uint nb_MCU_H, uint nb_MCU_V) 
{ 
    printf("Doing color conversion\n"); 

    __global uchar *MCU_Y, *MCU_Cb, *MCU_Cr; 
    int R, G, B; 
    uint ARGB; 
    uchar index, i, j; 

    i= get_global_id(0); 
    j= get_global_id(1); 

    MCU_Y = &YCbCr_MCU[0]; 
    MCU_Cb = &YCbCr_MCU[1]; 
    MCU_Cr = &YCbCr_MCU[2]; 

    if (i < 8 * nb_MCU_V && j < 8 * nb_MCU_H) 
    { 
      index = i * (8 * nb_MCU_H) + j; 
      R = (MCU_Cr[index] - 128) * 1.402f + MCU_Y[index]; 
      B = (MCU_Cb[index] - 128) * 1.7772f + MCU_Y[index]; 
      G = MCU_Y[index] - (MCU_Cb[index] - 128) * 0.34414f - 
       (MCU_Cr[index] - 128) * 0.71414f; 


      /* Saturate */ 
      if (R > 255) 
       R = 255; 
      if (R < 0) 
       R = 0; 
      if (G > 255) 
       G = 255; 
      if (G < 0) 
       G = 0; 
      if (B > 255) 
       B = 255; 
      if (B < 0) 
       B = 0; 
      ARGB = ((R & 0xFF) << 16) | ((G & 0xFF) << 8) | (B & 0xFF); 
      // ARGB = 0xFF << 8; 
     RGB_MCU[(i * (8 * nb_MCU_H) + j)] = ARGB; 


} 

printf("Finished color conversion\n"); 
} 

Mon code hôte pour appeler le noyau ressemble maintenant à ceci:

color_kernel= clCreateKernel(program, "YCbCr_to_ARGB", &ret); 

Je définis la taille de travail et les arguments du noyau de la façon suivante:

ret = clEnqueueWriteBuffer(command_queue, colorMCU_GPU, CL_TRUE, 0, 3*sizeof(cl_uchar), YCbCr_MCU, 0, NULL, NULL); 
chk(ret, "clEnqueueWriteBuffer"); 

ret = clEnqueueWriteBuffer(command_queue, RGB_GPU, CL_TRUE, 0, sizeof(cl_uint), RGB_MCU, 0, NULL, NULL); 
chk(ret, "clEnqueueWriteBuffer"); 


ret = clSetKernelArg(color_kernel, 0, sizeof(cl_mem), (void *)&colorMCU_GPU); 
ret |= clSetKernelArg(color_kernel, 1, sizeof(cl_mem), (void *)&RGB_GPU); 
ret = clSetKernelArg(color_kernel, 2, sizeof(cl_uint), (void *)&max_ss_h); 
ret |= clSetKernelArg(color_kernel, 3, sizeof(cl_uint), (void *)&max_ss_v); 


size_t itemColor[2] = {1, 1}; 

ret = clEnqueueNDRangeKernel(command_queue, kernel, 2, NULL, itemColor, NULL, 0, NULL, NULL); 
chk(ret, "clEnqueueNDRange"); 

ret = clEnqueueReadBuffer(command_queue, RGB_GPU, CL_TRUE, 0, sizeof(cl_uint), RGB_MCU, 0, NULL, NULL); 


clFinish(command_queue); 

J'ai couru ce code et je n'ai plus un écran noir. Cependant, le noyau pour "YCbCr à RGB" n'est pas reconnu maintenant. Même mes commentaires printf ne sont pas affichés sur la console de sortie. C'est comme si mon code n'avait pas la fonction de conversion de couleur.

Mise à jour:

Je ne l'avais pas changé le nom de mon noyau dans la commande EnqueueNDRangeKernel. J'ai changé le nom et maintenant les instructions printf apparaissent sur la console. Cependant, je ne reçois toujours pas la bonne sortie.

size_t itemColor[2] = {1, 1}; 

ret = clEnqueueNDRangeKernel(command_queue, color_kernel, 2, NULL, itemColor, NULL, 0, NULL, NULL); 

chk(ret, "clEnqueueNDRange"); 

clFinish(command_queue); 

Mise à jour:

J'ai suivi les conseils de pmdj et apporté des modifications à mon code du noyau. Il ressemble maintenant à ceci:

__kernel void YCbCr_to_ARGB(__global uchar* Y_GPU, __global uchar* Cb_GPU, __global uchar* Cr_GPU, __global uint* RGB_MCU, uint nb_MCU_H, uint nb_MCU_V) 
{ 
    __global uchar *MCU_Y, *MCU_Cb, *MCU_Cr; 
    int R, G, B; 
    uint ARGB; 
    uchar index, i, j; 

unsigned char iid= get_global_id(0); 
unsigned char jid= get_global_id(1); 

    // MCU_Y = &YCbCr_MCU[0]; 
     // MCU_Cb = &YCbCr_MCU[1]; 
     // MCU_Cr = &YCbCr_MCU[2]; 

     MCU_Y= Y_GPU; 
     MCU_Cb= Cb_GPU; 
     MCU_Cr= Cr_GPU; 

    if (iid <= (8 * nb_MCU_V) && jid <= (8 * nb_MCU_H)) 
    { 

      index = iid * (8 * nb_MCU_H) + jid; 
      R = (MCU_Cr[index] - 128) * 1.402f + MCU_Y[index]; 
      B = (MCU_Cb[index] - 128) * 1.7772f + MCU_Y[index]; 
      G = MCU_Y[index] - (MCU_Cb[index] - 128) * 0.34414f - 
       (MCU_Cr[index] - 128) * 0.71414f; 


      /* Saturate */ 
      if (R > 255) 
       R = 255; 
      if (R < 0) 
       R = 0; 
      if (G > 255) 
       G = 255; 
      if (G < 0) 
       G = 0; 
      if (B > 255) 
       B = 255; 
      if (B < 0) 
       B = 0; 
      ARGB = ((R & 0xFF) << 16) | ((G & 0xFF) << 8) | (B & 0xFF); 

     RGB_MCU[(iid * (8 * nb_MCU_H) + jid)] = ARGB; 

     } 
} 

Dans le code hôte, j'ai créé et la mémoire allouée pour 4 nouvelles variables:

Y_ForGPU= (cl_uchar *)malloc(MCU_sx * MCU_sy * max_ss_h * max_ss_v); 

Cb_ForGPU= (cl_uchar *)malloc(MCU_sx * MCU_sy * max_ss_h * max_ss_v); 

Cr_ForGPU= (cl_uchar *)malloc(MCU_sx * MCU_sy * max_ss_h * max_ss_v); 

//Now will do it for RGB 
RGB_testing= (cl_uint *)malloc (MCU_sx * MCU_sy * max_ss_h * max_ss_v * sizeof(cl_int)); 

j'ai créé des tampons de la manière suivante:

cl_mem for_Y= clCreateBuffer(context, CL_MEM_READ_WRITE| CL_MEM_COPY_HOST_PTR, (MCU_sx * MCU_sy * max_ss_h * max_ss_v), Y_ForGPU, &ret); 


cl_mem for_Cb= clCreateBuffer(context, CL_MEM_READ_WRITE| CL_MEM_COPY_HOST_PTR, (MCU_sx * MCU_sy * max_ss_h * max_ss_v), Cb_ForGPU , &ret); 

cl_mem for_Cr= clCreateBuffer(context, CL_MEM_READ_WRITE| CL_MEM_COPY_HOST_PTR, (MCU_sx * MCU_sy * max_ss_h * max_ss_v), Cr_ForGPU, &ret); 

//rgb_MCU for YCbCrtoARGB 
cl_mem RGB_GPU= clCreateBuffer(context, CL_MEM_READ_WRITE, (MCU_sx * MCU_sy * max_ss_h * max_ss_v * sizeof(cl_int)), NULL, &ret); 

J'ai ensuite défini les arguments du noyau, exécuté le noyau et renvoyé les données calculées sur l'hôte:

ret = clSetKernelArg(color_kernel, 0, sizeof(cl_mem), &for_Y); 
ret |= clSetKernelArg(color_kernel, 1, sizeof(cl_mem), &for_Cb); 
ret |= clSetKernelArg(color_kernel, 2, sizeof(cl_mem), &for_Cr); 
ret |= clSetKernelArg(color_kernel, 3, sizeof(cl_mem), &RGB_GPU); 
ret |= clSetKernelArg(color_kernel, 4, sizeof(cl_uint), &max_ss_h); 
ret |= clSetKernelArg(color_kernel, 5, sizeof(cl_uint), &max_ss_v); 


const size_t itemColor[2] = {100, 100}; 

ret = clEnqueueNDRangeKernel(command_queue, color_kernel, 2, NULL, itemColor, NULL, 0, NULL, NULL); 
clFinish(command_queue); 

//Copy result to the host 
ret = clEnqueueReadBuffer(command_queue, RGB_GPU, CL_TRUE, 0, (MCU_sx * MCU_sy * max_ss_h * max_ss_v * sizeof(cl_int)), RGB_testing, 0, NULL, NULL); 

Cependant, maintenant mon code se termine simplement brusquement. Pourquoi cela pourrait-il se produire?

Mise à jour:

Mon code fonctionne maintenant. Les problèmes étaient probablement dus à des différences dans les pointeurs. Je définis les variables Y, Cb, Cr et RGB (que j'ai créées) égales aux variables d'origine dans le code hôte.

//---Setting color variables equal to array elements----// 

Y_ForGPU= YCbCr_MCU[0]; 
Cb_ForGPU= YCbCr_MCU[1]; 
Cr_ForGPU= YCbCr_MCU[2]; 

//----RGB is being assigned value-----// 

RGB_testing= RGB_MCU; 
+3

Vous ne devriez pas avoir la boucle interne dans le programme du noyau. Le programme du noyau doit simplement faire le calcul YUV-> RGB pour un seul 'élément', puis vous utilisez clEnqueueNDRangeKernel pour segmenter les travailleurs vers les GPU. –

+0

@AndersCedronius J'ai mis à jour mon code pour inclure votre suggestion. Cependant, mon noyau n'est pas reconnu maintenant. J'ai inclus les détails dans ma question ci-dessus. –

+0

Ne va pas juste deviner des choses. Si vous voulez faire quelque chose de significatif, prenez un livre et commencez à lire, vous n'irez nulle part ailleurs. – Jovasa

Répondre

1

Je ne sais pas si cela est la seule cause de vos problèmes (il peut y avoir plus je ne l'ai pas encore repéré), mais vous avez une incompatibilité de type dans votre argument noyau YCbCr_MCU. Vous ne pouvez pas avoir d'arguments pointeur vers pointeur, c'est vrai. Le simple fait de supprimer le * ne le résoudra pas.

En particulier, la ligne

MCU_Cb = &YCbCr_MCU[1]; 

dans le noyau obtient 1 octet après le début de ce que les points de YCbCr_MCU à qui, regardant le code hôte, est en fait le début du tableau de pointeurs, non le tableau de pixels.

ret = clSetKernelArg(color_kernel, 0, sizeof(cl_mem), (void *)&colorMCU_GPU); 

On dirait que YCbCr_MCU est censé être un tableau de 3 pointeurs vers les Y, Cb, Cr contenant vos avions pixels source. Vous devrez les passer à votre noyau comme 3 pointeurs directs vers les 3 tableaux au lieu d'un pointeur vers les 3 pointeurs. En d'autres termes, transformez-le en arguments Y, Cb et Cr, et définissez-les sur colorMCU_GPU[0] à colorMCU_GPU[2] sur l'hôte.

+0

J'ai fait des changements dans mon code en fonction des conseils que vous m'avez donnés. Mon programme se termine brusquement quand je l'exécute. Pourquoi cela pourrait-il se produire? Cela pourrait-il être le résultat d'une mauvaise allocation de mémoire? –