Chiharu の日記

絵描き C/C++ プログラマーの日記です。

Parallel force - パラレル・フォース 〜スプライト管理、並列描画のコードなど

なんとなく、並列描画のコードなど公開してみます。

//
// 描画スレッド プロシージャ
//
void Screen::drawerThreadProc(ThreadNotifier& iNotifier)
{
  // コア数取得
  const auto aProcessors = SystemInfoHelper::getProcessors();
  
  // 描画スレッド構築
  std::vector<TaskThread> aTaskThreads;
  aTaskThreads.reserve(aProcessors);
  
  for (std::uint32_t aCt = 0; aCt < aProcessors; aCt++) {
    // 合成バッファ構築
    const auto aRowbytes = ImageHelper::getRowbytes(mPrimaryScreen->getWidth(), imageB8G8R8X8);
    auto aPixelData = MemoryHelper::alloc<std::uint8_t>(aRowbytes * mPrimaryScreen->getBandHeight());
    std::shared_ptr<IImage> aImage(new Image(mPrimaryScreen->getWidth(), mPrimaryScreen->getBandHeight(), imageB8G8R8X8,
      aRowbytes, aPixelData));
    
    // スレッド構築
    std::shared_ptr<Thread> aThread(new Thread());
    std::shared_ptr<Event> aBeginEvent(new Event()), aEndEvent(new Event());
    TaskThread aTaskThread = { aThread, aBeginEvent, aEndEvent };
    aTaskThreads.push_back(aTaskThread);
    
    // 描画スレッド プロシージャ
    auto aThreadProc = [this, aBeginEvent, aEndEvent, aImage](ThreadNotifier& iNotifier) -> void {
      while (!iNotifier.isStopRequestIssued()) {
        aBeginEvent->wait(INFINITE), aBeginEvent->reset();
        try {
          if (mTaskInfo.screen) {
            // 描画
            auto aScreen = mTaskInfo.screen.get();
            const auto aMax = aScreen->getBandCount();
            const auto aBandHeight = aScreen->getBandHeight();
            for (;;) {
              const auto aBandIndex = mTaskInfo.getTargetBandIndex();
              if (aBandIndex < aMax) {
                // 描画
                Rect aDirtyArea = aScreen->getDirtyArea(aBandIndex);
                if (!RectHelper::isEmpty(aDirtyArea)) {
                  aScreen->draw(*aImage, aBandIndex);
                  // 表画面へ転送
                  auto aLock = pforce::lock(mTaskInfo.cs);  // GDI はスレッドセーフでないため転送はシーケンシャルに実施
                  blitImageToDeviceContext(mDeviceContext, *aImage, aDirtyArea, aBandIndex * aBandHeight);
                }
              } else {
                // 終了
                break;
              }
            }
          }
        } catch (std::exception&) {
          // do nothing.
        }
        aEndEvent->set();
      }
    };
    aThread->start(aThreadProc);
  }
  
  // 描画ループ
  while (!iNotifier.isStopRequestIssued()) {
    try {
      auto aLock = pforce::lock(mDrawerThreadCriticalSection);
      if (mPrimaryScreen->isDirty()) {
        // 描画準備
        mTaskInfo.reset(mPrimaryScreen);
        auto aScreenLock = pforce::lock(*mTaskInfo.screen);
        
        // 描画開始
        std::for_each(aTaskThreads.begin(), aTaskThreads.end(), [](TaskThread& iThread) {
          iThread.beginEvent->set();
        });
        
        // 描画終了
        std::for_each(aTaskThreads.begin(), aTaskThreads.end(), [](TaskThread& iThread) {
          iThread.endEvent->wait(INFINITE), iThread.endEvent->reset();
        });
      }
    } catch (std::exception&) {
      // do nothing.
    }
#if defined(PFORCE_STRESS_TEST)
    // ストレス テストの場合はスリープしない。
#else
    iNotifier.sleep(1);
#endif
  }
  
  // 描画スレッド終了
  mTaskInfo.reset();
  std::for_each(aTaskThreads.begin(), aTaskThreads.end(), [](TaskThread& iThread) {
    iThread.thread->invalidate(), iThread.beginEvent->set();
  });
  std::for_each(aTaskThreads.begin(), aTaskThreads.end(), [](TaskThread& iThread) {
    iThread.thread->stop();
  });
}

完全なコードではありませんが、なんとなく、C++0x っていいな、って気になるコードじゃないかと思ってます。ラムダとか、左辺値の型推論とか、右辺値参照によるロックの簡略化とか。C++03 にはない世界ですね。
改めて書くと、1 関数あたりが長いですね。描画スレッド プロシージャの生成だけ、関数分けようかな。