Skip navigation

Első példa

Bevezetés

Ebben a példában egy olyan OpenCL programot mutatunk be, amely egy 2D-s képet állít elő, amelyet a szálak két dimenziós indexelésével érünk el. Pontosabban, egy olyan képet, amelyben a körgyűrűket egy fügvény segítségével állítjuk elő.

A program az OpenCL kernellel előállított mátrixot egy png formátumú képként menti el, amely így néz ki.

Output kép

Gazdagép kód

Először tekintsük át a szükséges változtatásokat az előző OpenCL programokhoz képest.

Az első dolog, amire oda kell figyelnünk a megfelelő méretű memória objektum létrehozása. Mivel RGB komponensű képet állítunk elő, ezért ennek megfelelően kell lefoglalnunk a memóriát.

bool CreateMemObjects(cl_context context, cl_mem memObjects[1])
{
  memObjects[0] = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(unsigned char) * N * N * 3, NULL, NULL);

  if (memObjects[0] == NULL )
  {
    std::cerr << "Error creating memory objects." << std::endl;
    return false;
  }

  return true;
}

A következő változás az előzőekhez képest, hogy a lokális és globális mukacsoportokat két dimenziós indextartományon kell majd kezelni. Ehhez szükség van egy kerekítésre is, ami a lokális méretnek megfelelően alakítja a szükséges globális index tartományt.

size_t RoundUp(int groupSize, int globalSize)
{
  int r = globalSize % groupSize;
  if (r == 0)
  {
    return globalSize;
  }
  else
  {
    return globalSize + groupSize - r;
  }
}

A main függvényben a munkacsoport méretét a következőképpen kell beállítani

size_t localWorkSize[2] = { 16, 16 };
size_t globalWorkSize[2] = { RoundUp(localWorkSize[0], N), RoundUp(localWorkSize[1], N) };

A kernelt is a két dimenzios tartományoknak megfelelően kell elhelyezni a parancssorban a manin függvényben.

errNum = clEnqueueNDRangeKernel(commandQueue, kernel, 2, NULL, globalWorkSize, localWorkSize, 0, NULL, NULL);

A kernel végrehajtása után az eredményt be kell tölteni a gazdagép memóriájába. Ehhez először le kell foglalni a megfelelő méretű memória területet, majd pedig az adott memória objektum tartalmát be kell tölteni a gazdagép memóriáába.

unsigned char* result = new unsigned char[N*N * 3];
errNum = clEnqueueReadBuffer(commandQueue, memObjects[0], CL_TRUE, 0, 3 * N * N * sizeof(char), result, 0, NULL, NULL);

A sikeres memória másolás után az eredményt kiírjuk egy png állományba. Az eredmény kiírására a következő szekcióban mutatunk két lehetséges megoldást a FreeImage függvénykönyvtár használatával.

	
writeRGBImageToFile("image.png", result, N, N);

A teljes forráskód letöltése itt.

Kép mentése

 void writeRGBImageToFile(char* fileName, unsigned char* bytes, int w, int h)
{
    FreeImage_Initialise();
    FIBITMAP* image = FreeImage_Allocate(w, h, 24);
    unsigned int lineWidth = FreeImage_GetPitch(image);

    for (int i = 0; i < h; i++)
    {
        unsigned char* src = bytes + i * lineWidth;
        unsigned char* dst = FreeImage_GetScanLine(image, i);
        memcpy(dst, src, lineWidth);
    }

    FreeImage_Save(FIF_PNG, image, fileName);
    FreeImage_Unload(image);
    FreeImage_DeInitialise();
}
bool SaveImage(char *fileName, unsigned char *buffer, int width, int height)
{
    FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(fileName);
    FIBITMAP *image = FreeImage_ConvertFromRawBits((BYTE*)buffer, width, height, width * 4, 32, 0xFF000000, 0x00FF0000, 0x0000FF00);
    return (FreeImage_Save(format, image, fileName) == TRUE) ? true : false;
}

Eszköz kód

// Add you device OpenCL code
__kernel void helloWave(__global char* output, int width)
{
    const int x = get_global_id(0);
    const int y = get_global_id(1);

    float halfWidth = width/2.0f;
// Egy dimenziós tömb indexelése RGB komponenseket figyelembe véve
    int pos = 3 * (y * get_global_size(0) + x);
// Középponttól vett távolság kiszámítása
    __private float dist = sqrt((float)((x-halfWidth)*(x-halfWidth)+(y-halfWidth)*(y-halfWidth)));
// 0-255 közötti intenzitás érték előállítása a középponttól vett távolságot figyelembe véve
    __private float value = (127/sqrt(2.0f)) *( cos(dist) + sin(dist) + sqrt(2.0f));

// RGB komponensek feltöltése, ha az adott pont a kép méretenén belül van
    if (x < width && y < width)
    { 
	output[pos] = value;
	output[pos + 1] = value;
	output[pos + 2] = value;	
    }
	
}

A kernel forrásának letöltése itt.

Feladat

  • Fordítsuk le a programot a negyedik lecke végén található útmutatás szerint!
  • Módosítsuk az eszköz kódját úgy, hogy más mintázatot kapjunk!