Начинаю выкладывать занятные статьи с ps2dev
--------------------------------------------------------------------------------------
Попробую немножко просветить по поводу 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;