Работа с 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