consoledevСтатьи

Работа с PSP GE

Автор:

Попробую немножко просветить по поводу GE процессора , и минимальной обвязки для работы без sceGu библиотеки.

Текста писать много не буду, лучше на примере.

1. Инициализация GE

Перехват VBlankHandler
Установка callbacks от GE процессора

SceVoid GE_Initialize()
{
  sceDisplaySetVblankCallback(1,GE_DisplayVBlank,SCE_NULL);

  SceGeCbParam cb_param;
  cb_param.pSignalFunc   = GE_Signal;
  cb_param.pSignalCookie = SCE_NULL;
  cb_param.pFinishFunc   = GE_Finish;
  cb_param.pFinishCookie = SCE_NULL;
  dsEngine::GE.SetInterruptID(sceGeSetCallback(&cb_param));

}

Теперь рассмотрим сам VBlankHandler

BEGIN_CRITICAL,END_CRITICAL - это семафорная обвязка. (sceKernelWaitSema,sceKernelSignalSema)
здесь происходит установка нового front буффера , также подсчитываеться FPS

/// VBlank Interrupt
SceVoid dsEngine::dsGeCore::VBlankInterrupt(SceUInt32 )
{
  BEGIN_CRITICAL();
  {
    m_VBlankCount++;

    if (m_DrawLock == SCE_TRUE)
    {
      END_CRITICAL();
      return;
    }

  }
  END_CRITICAL();
  
  UpdateCommandBufferState();

  BEGIN_CRITICAL();
  {
    if (m_Locked == SCE_FALSE && m_RequestFlip == SCE_TRUE)
    {
      m_FlipFrameBufferCount++;

      sceDisplaySetFrameBuf(m_FlipFrameBufferAddress,512,
        SCE_DISPLAY_PIXEL_RGBA8888,SCE_DISPLAY_UPDATETIMING_NEXTHSYNC);

      m_CurrFrontBufferAddress = m_FlipFrameBufferAddress;

      m_RequestFlip = SCE_FALSE;
    }
    else
    {
      profile.missedVBlankCount++;
    }

    if (m_VBlankCount > 60)
    {
      m_fCurrentFPS = (static_cast<SceFloat32>(m_FlipFrameBufferCount) * 60.0f) /
         static_cast<SceFloat32>(m_VBlankCount);

      m_VBlankCount = 0;
      m_FlipFrameBufferCount = 0;
    }
  }
  END_CRITICAL();

  UpdateCommandBufferState();
}


Теперь самое интересное (UpdateCommandBufferState)
Это как раз display double buffer list.
Непосредственная работа с GE

/// Обновление статусов command push buffer.
/// проверка на окончание отрисовки, и проверка на 
/// отсылку нового command push buffer
SceVoid dsEngine::dsGeCore::UpdateCommandBufferState()
{
  BEGIN_CRITICAL();
  {
    // если этот командный буффер уже отослан на исполнение,
    // тогда проверяем статус его отрисовки.
    if (m_CommandBufferStatus[m_DrawCommandBufferIdx] == CMDBUFFSTATUS_QUEUED)
    {
      // если список уже отрисовался
      if (sceGeDrawSync(1) == SCE_GE_LIST_COMPLETED)
      {
        // если нет запроса на флип front/back буффер,
        // завершаем данный список и просим сделать флип на
        // следующем vblank interrupt
        if (m_RequestFlip == SCE_FALSE)
        {
          // запоминаем новый адрес frame buffer для флипа и делаем запрос на флип.
          m_FlipFrameBufferAddress = static_cast<SceUInt32*>
            (m_CommandBufferFrameAddress[m_DrawCommandBufferIdx]);
          m_RequestFlip            = SCE_TRUE;

          // освобождаем command push buffer
          m_CommandBufferStatus[m_DrawCommandBufferIdx] = CMDBUFFSTATUS_FREE;

          m_DrawCommandBufferIdx = 1 - m_DrawCommandBufferIdx;
        }
      }
    }

    // если коммандный буффер подготовлен к отcылке, отправляем его на GE
    if (m_Locked == SCE_FALSE && 
        m_CommandBufferStatus[m_DrawCommandBufferIdx] == CMDBUFFSTATUS_READY)
    {
      if (m_RequestFlip == SCE_FALSE)
      {
        profile.startGPUTime = sceKernelGetSystemTimeLow();

        m_CommandBufferStatus[m_DrawCommandBufferIdx] = CMDBUFFSTATUS_QUEUED;

        m_QueueDispListId[m_DrawCommandBufferIdx] = sceGeListEnQueue
        (
          m_CommandBuffer[m_DrawCommandBufferIdx],
          SCE_NULL,
          m_InterruptID,
          SCE_NULL
        );
      }
    }
  }
  END_CRITICAL();
}


Все что осталось подсмотреть, это реализация callbacks GE

CMD_FINISH

SceVoid dsEngine::dsGeCore::FinishCallback(SceUInt32 )
{
  profile.currentGPUTime = sceKernelGetSystemTimeLow() - profile.startGPUTime;

  UpdateCommandBufferState();
}

CMD_SIGNAL

static SceVoid GE_Signal(SceInt32 interruptCode,SceVoid* cookie,const SceVoid* memoryAddress)
{
  register SceUShort16 intrCode = static_cast<SceUShort16>(interruptCode);
  // внутренние GE library сообщения
  if ((intrCode & 0xf000) == 0xf000)
  {

    // сохранения контекста
    if (intrCode == GECORE_SIGNAL_STORECONTEXT)
    {
      sceGeSaveContext(&geContext);
    }
    // восстановление контекста
    else if (intrCode == GECORE_SIGNAL_RESTORECONTEXT)
    {
      sceGeRestoreContext(&geContext);
    }
    // изменение ширины трансляции адресов GE процессора
    else if (intrCode == GECORE_SIGNAL_SETADDRESSTRANSLATION)
    {
      sceGeEdramSetAddrTranslation(sceGeGetCmd(SCE_GE_CMD_END)&0xFFFF);
    }
    // системные SIGNAL идут через паузу в GE
    // поэтому продолжаем работу GE процессора.
    sceGeContinue();
  }
  else
  {
    if (geSignalCallback != SCE_NULL)
    {
      geSignalCallback(intrCode);
    }
  }
}


Работа с COMMAND PUSH BUFFER

Делаем такую функцию в .h

inline SceVoid GE_PUTCOMMAND(SceUInt32 command)
{
  *dsEngine::geCmdBuffer++ = command;
}

теперь пример заполнения PUSH BUFFER

static SceVoid GECORE_SetViewport()
{
  SceFloat32 sx = static_cast<SceFloat32>( geDisplayWidth  / 2);
  SceFloat32 sy = static_cast<SceFloat32>(-geDisplayHeight / 2);
  SceFloat32 tx = 2048.0f;
  SceFloat32 ty = 2048.0f;

  GE_PUTCOMMAND(SCE_GE_SET_SX_FLOAT24(sx));
  GE_PUTCOMMAND(SCE_GE_SET_SY_FLOAT24(sy));
  GE_PUTCOMMAND(SCE_GE_SET_TX_FLOAT24(tx));
  GE_PUTCOMMAND(SCE_GE_SET_TY_FLOAT24(ty));
}

.....

SceVoid GECORE_SetDepthRange(SceInt32 depthMin,SceInt32 depthMax)
{
  GE_PUTCOMMAND(SCE_GE_SET_MINZ(depthMin));
  GE_PUTCOMMAND(SCE_GE_SET_MAXZ(depthMax));

  SceFloat32 fDepthMin = static_cast<SceFloat32>(depthMin);
  SceFloat32 fDepthMax = static_cast<SceFloat32>(depthMax);

  SceFloat32 sz = (fDepthMax - fDepthMin) * 0.5f;
  SceFloat32 tz = (fDepthMax + fDepthMin) * 0.5f;

  GE_PUTCOMMAND(SCE_GE_SET_SZ_FLOAT24(sz));
  GE_PUTCOMMAND(SCE_GE_SET_TZ_FLOAT24(tz));
}

....

BeginFrame/EndFrame

В BeginFrame, показанными выше коммандами делаем DefaultRenderState
Более интересен EndFrame

EndFrame
1. Подводим статистику для профилирования (эмуляция perfhud :) )
  и запоминаем значения статистики (например последних 128 значений), что бы выводить графики CPU,GPU выполнения отдельных GE list , etc....

2. Отрисовка интерфейса профилирования

И в конце

  geCmdBuffer       = SCE_NULL;
  m_geCmdBufferStart  = SCE_NULL;

  BEGIN_CRITICAL();
  {
    m_CommandBufferStatus[m_FillCommandBufferIdx] = CMDBUFFSTATUS_READY;

    if (m_CommandBufferStatus[1 - m_FillCommandBufferIdx] == CMDBUFFSTATUS_FREE)
    {
      m_DrawCommandBufferIdx = m_FillCommandBufferIdx;
    }
  }
  END_CRITICAL();

  m_bFrontBufferValid = SCE_FALSE;

  UpdateCommandBufferState();

  m_FillCommandBufferIdx = 1 - m_FillCommandBufferIdx;


В BeginFrame соотв. будет

  geCmdBuffer = reinterpret_cast<SceUInt32*>
    (0x40000000 | reinterpret_cast<SceUInt32>(m_CommandBuffer[m_FillCommandBufferIdx]));
  m_geCmdBufferStart  = geCmdBuffer;


Вообщем-то про остальное спрашивайте, и так слил много :))
Так как возможно этот код будет использоваться в engine посему !!!
(c) 2006 SME Dynamic Systems Ltd.

12 июня 2006 (Обновление: 24 сен 2006)

Комментарии [15]