devkitPro/citro3d

strange soft lock

namkazt opened this issue · 3 comments

it run follow step:

Loop:

while (aptMainLoop())
{
        gspWaitForVBlank();
        PPGraphics::Get()->BeginRender();
        PPGraphics::Get()->RenderOn(GFX_TOP);
	sm->UpdateFrameTracker();   // Step 1 
	PPGraphics::Get()->DrawTopScreenSprite(); // Step 2
        PPGraphics::Get()->EndRender();
}

1, Update frame function

void PPGraphics::UpdateTopScreenSprite(u8* data, u32 size, u32 width, u32 height)
{
	mTopScreenSprite->width = width;
	mTopScreenSprite->height = height;

	u8* linearData = (u8*)linearAlloc(sizeof(u8) * size);
	memcpy(linearData, data, size);

	if(!mTopScreenSprite->initialized)
	{
		mTopScreenSprite->initialized = true;
		GSPGPU_FlushDataCache(linearData, size);
		C3D_TexInit(&mTopScreenSprite->spriteTexture, width, height, GPU_RGB8);
		u32 dim = GX_BUFFER_DIM(width, height);
		C3D_SafeDisplayTransfer((u32*)linearData, dim, (u32*)mTopScreenSprite->spriteTexture.data, dim, TEXTURE_TRANSFER_FLAGS);
		gspWaitForPPF();
		C3D_TexSetFilter(&mTopScreenSprite->spriteTexture, GPU_LINEAR, GPU_NEAREST);

	}else
	{
		GSPGPU_FlushDataCache(linearData, size);
		u32 dim = GX_BUFFER_DIM(width, height);
		C3D_SafeDisplayTransfer((u32*)linearData, dim, (u32*)mTopScreenSprite->spriteTexture.data, dim, TEXTURE_TRANSFER_FLAGS);
		gspWaitForPPF();
		C3D_TexSetFilter(&mTopScreenSprite->spriteTexture, GPU_LINEAR, GPU_NEAREST);
	}
	linearFree(linearData);
}

2, Draw frame if texture is initialized


void PPGraphics::DrawTopScreenSprite()
{
	if (!mTopScreenSprite->initialized) {
		return;
	}
	
	ppVertexPosTex* vertices = (ppVertexPosTex*)allocMemoryPoolAligned(sizeof(ppVertexPosTex) * 4, 8);
	if (!vertices)
		return; // out of memory in pool
	float x = 0, y = 0, w = 400.0f, h = 240.0f;
	// set position
	vertices[0].position = (ppVector3) { x, y, 0.5f };
	vertices[1].position = (ppVector3) { x + w, y, 0.5f };
	vertices[2].position = (ppVector3) { x, y + h, 0.5f };
	vertices[3].position = (ppVector3) { x + w, y + h, 0.5f };

	// set color
	vertices[0].textcoord = (ppVector2) { 0.0f, 0.0f };
	vertices[1].textcoord = (ppVector2) { 1.0f, 0.0f };
	vertices[2].textcoord = (ppVector2) { 0.0f, 1.0f};
	vertices[3].textcoord = (ppVector2) { 1.0f, 1.0f };

	// setup env
	C3D_TexBind(getTextUnit(GPU_TEXUNIT0), &mTopScreenSprite->spriteTexture);
	C3D_TexEnv* env = C3D_GetTexEnv(0);
	C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
	C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
	C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);

	C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
	AttrInfo_Init(attrInfo);
	AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
	AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);

	C3D_BufInfo* bufInfo = C3D_GetBufInfo();
	BufInfo_Init(bufInfo);
	BufInfo_Add(bufInfo, vertices, sizeof(ppVertexPosTex), 2, 0x10);

	C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4);
}
fincs commented

I had to dig into your 'PinBox' project to confirm my suspicions:

I acknowledge this is a common source of confusion (and the lack of documentation doesn't help). For this reason, the next version of citro3d will deprecate the C3D_Safe* commands, and replace them with a new set of functions that actually work properly regardless of whether you're inside frame rendering or not.

In the meantime, assuming you will always call UpdateTopScreenSprite inside frame rendering, you can replace C3D_SafeDisplayTransfer by GX_DisplayTransfer and remove the gspWaitForPPF call afterwards (since that is not needed if in frame rendering as the command is added to the GX queue and its execution is deferred until C3D_FrameEnd is called). Since execution of GX commands is deferred, you will need to make sure that the staging buffer for the copy never gets freed; that is, move the memory management out of the rendering loop. The memory management shouldn't really be inside the loop because memory allocation is a slow operation. Try to use static buffers for this purpose.

An alternative solution is to move the UpdateTopScreenSprite call outside of the frame rendering logic, which may or may not be a safer bet.

@fincs : thanks for you help. I have just 1 more question:
do you have any ideas if there is any way faster to change texture data

i do this function but only get around 15~16FPS if i decode about 30 frames per second.

GSPGPU_FlushDataCache(linearData, size);
u32 dim = GX_BUFFER_DIM(width, height);
C3D_SafeDisplayTransfer((u32*)linearData, dim, (u32*)mTopScreenSprite->spriteTexture.data, dim, TEXTURE_TRANSFER_FLAGS);
gspWaitForPPF();
C3D_TexSetFilter(&mTopScreenSprite->spriteTexture, GPU_LINEAR, GPU_NEAREST);

i got around 30FPS for now but still need to know if there is any way to optimize it more.