Wie mit GDI+ und OpenMP parallelisieren?

BAGZZlash

Mitglied
Ich zeichne mittels GDI+ und GdipDrawArc einige Kreisbögen. Mein Ziel ist, so eine ähnliche visuelle Ordnerdarstellung des Dateisystems wie bei Filelight zu bekommen. Im Grunde geht das schon ganz gut, siehe hier:

Jada.png


Da aber die Grafik bei Änderung der Fenstergröße jedes Mal neu gezeichnet wird, ist mir Performance wichtig. Daher dachte ich, ich werf' mal Parallelisierung drauf.

Nun, GDI+ ist prinzipiell threadsicher und muss, laut Doku, für jeden der parallelen Threads initialisiert werden. Das mach' ich zunächst, hole mir dann (wieder single-threaded) ein Handle auf mein DC, stelle Anti-Aliasing ein und zeichne den inneren Kreis. Dies wird immer korrekt ausgeführt. Danach geht's ans Zeichnen der Kreissegmente, und zwar jetzt eben parallel. Ich parallelisiere über die äußere Schleife.

Laut Doku darf man nicht aus mehreren Threads gleichzeitig auf GDI+ zugreifen. Macht man es doch, können meiner Erfahrung nach zwei Probleme auftreten:
1.) GDI+ gibt "ObjectBusy" zurück. Schön, dann versuche ich es eben weiter, bis das Objekt nicht mehr busy ist.
2.) GDI+ kann sich dann auch schonmal "verklemmen", und der Prozess stürzt ab.

Ersteres Problem löse ich mit einer while-Schleife, die versucht, sich immer weiter in die Funktion "reinzuhämmern", so lange, bis es eben klappt. Dies ist aber sicher nicht die feine englische Art. Zweiteres Problem könnte hierdurch ausgelöst werden. Vermute ich zumindest. Ich brauche dafür eine elegantere Lösung als trial-and-error und glaube, dass damit beide Probleme gleichzeitig verschwinden werden.

Hier der entscheidende Codeteil:

C:
omp_set_num_threads(omp_get_max_threads());
#pragma omp parallel
{ MyStatus = GdiplusStartup(&token, &input, NULL); }

MyStatus = GdipCreateFromHDC(MyDC, &graphics);
MyStatus = GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias8x8);

MyStatus = DLLDrawArc(0, MaxLevel, 1, 0, RadiusPercentage, TreeStruc[CurrentRootIndex].Color, ScaleWidth, graphics);

#pragma omp parallel for private(i) private(j) private(k) private(nBytes) private(Buff) private(Used) private(Percentage) private(RGBArr) private(MyStatus) private(Counter)
for (i = CurrentClassBase; i <= MaxLevel; i++) {
  for (j = 0; j < ArrSize; j++) {
    if (LocalArr[j].Level == i) {
      nBytes = WideCharToMultiByte(CP_ACP, 0, LocalArr[j].FolderName, -1, NULL, 0, NULL, NULL);
      Buff = (LPSTR)malloc(nBytes * sizeof(char));
      WideCharToMultiByte(CP_ACP, 0, LocalArr[j].FolderName, -1, Buff, nBytes, NULL, NULL);

      if (InStr(Buff, CurrentRoot)) {
        Used = LocalArr[j].UsedAsParent;
        for (k = j; k < ArrSize; k++) {
          if (LocalArr[k].ParentID == LocalArr[j].ID & LocalArr[k].UsedAsParent == 0) {
            LocalArr[k].UsedAsParent = Used;
            if (TotalSize > 0)
              Percentage = LocalArr[k].Size / TotalSize;
            else
              Percentage = 0.0f;
            if (Percentage >= SmallestSize) {
              if (Used > 1)
                Used = 1;

              RGBArr[0] = (i / (float)MaxLevel) * 255;
              RGBArr[1] = Used * 255;
              RGBArr[2] = 128 - Counter;

              TreeStruc[k].Color = RGBArr[2] + (RGBArr[1] * 0x100) + (RGBArr[0] * 0x10000);

              Counter++;
              if (Counter >= 128)
                Counter = 0;

              Used = Used + Percentage;

              do {
                MyStatus = DLLDrawArc(i + 1 - CurrentClassBase, MaxLevel, Percentage, Used, RadiusPercentage, TreeStruc[k].Color, ScaleWidth, graphics);
              } while (MyStatus != Ok);
            }
          }
        }
      }
      free(Buff);
    }
  }
}
 
Zuletzt bearbeitet:

german

Aktives Mitglied
devCommunity-Experte
Thread affinity of user interface objects, part 2: Device contexts | The Old New Thing (microsoft.com)
If you choose to use a DC in a multi-threaded manner, it’s your responsibility to coordinate the consumers of that device context so that only one thread uses it at a time.
Ich hab bislang kaum was mit GDI+ angestellt. GUI ist nicht so meins ...
Sieht so aus als könntest du Berechnungen in Threads machen, aber nicht parallel auf das DC zugreifen. Critical Section ist unter Windows eine einfache Möglichkeit für Mutual Exclusion innerhalb eines Prozesses. Läuft auf User Level und ist daher bzgl. Performance etwas besser als ein Mutex.
 

BAGZZlash

Mitglied
Critical Section ist unter Windows eine einfache Möglichkeit für Mutual Exclusion innerhalb eines Prozesses.

Das war das entscheidende Stichwort. ObjectBusy ist damit ebenso Geschichte wie diese merkwürdigen Abstürze. Hm, jetzt sieht's noch auf andere Weise etwas merkwürdig aus, aber da vermute ich race condition. Mal weiter forschen... Jedenfalls erstmal schonmal danke, das hat geholfen.

Ich würde die Bilder in einer Handvoll Auflösungen prerendern und bei Fenstergrößenänderung das nächst beste nehmen und runterskalieren.

Bestechende pragmatische Idee. Wenn das Threading läuft und mir dann immernoch zu langsam ist, ziehe ich das in Erwägung.
 
Oben Unten