Page 1 of 1

How to split view

Posted: February 12th, 2018, 5:26 am
by Xavi92
Hello everyone

My WIP video game "Airport" will feature a 2-player mode, where screen is split horizontally. My intention was to perform the following steps:

1. Modify drawing environment to the left half of the screen;
2. Draw everything related to player 1's view.
3. Modify drawing environment to the right half of the screen.
4. Draw everything related to player 2's view.
5. Modify drawing environment to occupy the whole screen.
6. Draw common GUI elements (clock, score, etc.)
7. Go to step 1.

All instructions are being passed to the DMA, even modifying the drawing environment. While it works perfevtly under emulation, it makes screen flickering under real HW. I discovered the following statement on Nocash's PSX specs:
GP0(E3h..E5h) do not take up space in the FIFO, so they are probably executed immediately (even if there're still other commands in the FIFO). Best use them only if you are sure that the FIFO is empty (otherwise the new Drawing Area settings might accidently affect older Rendering Commands in the FIFO).
I've tried inserting NOPs (GP0(00h)) both before and after without success. See source code here:
Game.c (see GameGraphics())
https://github.com/XaviDCR92/Airport/bl ... rce/Game.c
https://github.com/XaviDCR92/psxsdk-201 ... /src/gpu.c

Any ideas?

Thanks for reading,
Xavi

Re: How to split view

Posted: February 12th, 2018, 11:48 am
by LameGuy64
I haven't really tried split screen rendering myself but I believe the easiest method to achieve it is to have 2 OTs for each view, each OT starting with a DR_AREA (command 0x10) packet to set the draw area for each view. You can then link the 2 OTs together and draw both OTs once.

Re: How to split view

Posted: February 15th, 2018, 3:59 am
by Xavi92
AFAIK, GPU command GP1(0x10) returns read-only information about the GPU, according to nocash's specs:

GP1(10h) - Get GPU Info
GP1(11h..1Fh) - Mirrors of GP1(10h), Get GPU Info
After sending the command, the result can be read (immediately) from GPUREAD register (there's no NOP or other delay required) (namely GPUSTAT.Bit27 is used only for VRAM-Reads, but NOT for GPU-Info-Reads, so do not try to wait for that flag).

0-23 Select Information which is to be retrieved (via following GPUREAD)

On Old 180pin GPUs, following values can be selected:

00h-01h = Returns Nothing (old value in GPUREAD remains unchanged)
02h = Read Texture Window setting ;GP0(E2h) ;20bit/MSBs=Nothing
03h = Read Draw area top left ;GP0(E3h) ;19bit/MSBs=Nothing
04h = Read Draw area bottom right ;GP0(E4h) ;19bit/MSBs=Nothing
05h = Read Draw offset ;GP0(E5h) ;22bit
06h-07h = Returns Nothing (old value in GPUREAD remains unchanged)
08h-FFFFFFh = Mirrors of 00h..07h

On New 208pin GPUs, following values can be selected:

00h-01h = Returns Nothing (old value in GPUREAD remains unchanged)
02h = Read Texture Window setting ;GP0(E2h) ;20bit/MSBs=Nothing
03h = Read Draw area top left ;GP0(E3h) ;20bit/MSBs=Nothing
04h = Read Draw area bottom right ;GP0(E4h) ;20bit/MSBs=Nothing
05h = Read Draw offset ;GP0(E5h) ;22bit
06h = Returns Nothing (old value in GPUREAD remains unchanged)
07h = Read GPU Type (usually 2) ;see "GPU Versions" chapter
08h = Unknown (Returns 00000000h) (lightgun on some GPUs?)
09h-0Fh = Returns Nothing (old value in GPUREAD remains unchanged)
10h-FFFFFFh = Mirrors of 00h..0Fh
On the other hand, using 2 linked OTs starting with GP1(10h) would be pretty much the same as using a bigger one with 2 GP1(10h), if I understood correctly. Actually, that would be what I'm already doing, but using the following GPU commands instead:

Code: Select all

void GsSetDrawEnv_DMA(GsDrawEnv* drawenv)
{
	unsigned int orig_pos = linked_list_pos;

    //GsDrawListPIO();

    linked_list[linked_list_pos++] = 0x05000000;
    
	linked_list[linked_list_pos++] = (0xE1 << 24) |(drawenv->draw_on_display>=1)<<10|(drawenv->dither>=1)<<9;
	linked_list[linked_list_pos++] = (0xE2 << 24);
	linked_list[linked_list_pos++] = ((0xE3 << 24) | (drawenv->x & 0x7FF) | ((drawenv->y & 0x3FF) << 10));
	linked_list[linked_list_pos++] = ((0xE4 << 24) | ((drawenv->x + drawenv->w - 1) & 0x3FF) | (((drawenv->y + drawenv->h - 1) & 0x3FF) << 10));
	linked_list[linked_list_pos++] = ((0xE5 << 24) | ((drawenv->x) & 0x7FF) | (((drawenv->y ) & 0x7FF) << 11));
	
	linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;

	GsCurDrawEnvW = drawenv->w;
	GsCurDrawEnvH = drawenv->h;
}

Re: How to split view

Posted: February 17th, 2018, 8:44 pm
by gwald
Hola colega!


Your splitscreen function looks wrong to me, but i haven't used that SDK.
https://github.com/XaviDCR92/Airport/bl ... Gfx.c#L882

Looks too simple to work :shrug

Code: Select all

case PLAYER_TWO:
			DrawEnv.x = X_SCREEN_RESOLUTION >> 1;
			DrawEnv.w = X_SCREEN_RESOLUTION >> 1;
break;
you should set correct x,Y and w,H on both players


From memory on my net yaroze game, you need 2 order Table & 2 packet lists per screen (2splits + 1 hud = 3 screens) and I also configured 3 DrawEnv

so,

PutDrawEnv(&Env[buf]); // set up top half for drawing
setpacket(packet[buf]);
SetView P1 view
draw P1

if player2, then
{
PutDrawEnv(&Env[buf+2]); // set up bottom half for drawing
setpacket(packet[buf+2]);
SetView P2 view
draw P2
}

PutDrawEnv(&HudEnv[buf]); // set up fullscreen or top screen or whatever for drawing HUD
setpacket(hudPacket[buf]);
SetView reset view to ID matrix // i think sprites ignore the 3D stuff.. i can't remember tho
draw Hud

wait for draw sync maybe vsync too
swap display buffer

PITA, to get working, but just do a screen at a time, it's a cool effect and it's fun!
[BBvideo=560,315]https://www.youtube.com/watch?v=37n_fBhRUY0[/BBvideo]

Saludos :dance

Re: How to split view

Posted: February 24th, 2018, 4:30 am
by Xavi92
Aren't you waiting for GPU to finish each time you call "draw p#"? It looks like you are telling the GPU to draw even if the previous screen (e.g.: player 1) hasn't been fully rendered yet. Could you please provide source code for that?

Re: How to split view

Posted: February 27th, 2018, 3:19 pm
by gwald
Xavi92 wrote: February 24th, 2018, 4:30 am Aren't you waiting for GPU to finish each time you call "draw p#"? It looks like you are telling the GPU to draw even if the previous screen (e.g.: player 1) hasn't been fully rendered yet.
No wait, it's splitscreen, the different parts of the screen get drawn at the same time.. you're just drawing to different places on screen.
Then when the draw list is finished it's DMA'ed to GPU on swapbuffer.
There's only one place to wait, as per normal.
Xavi92 wrote: February 24th, 2018, 4:30 am Could you please provide source code for that?
ahh.. I've packed it up ages ago, I'll have to go though a few harddrives... but it's not that hard..
Just try with 2 screen's then add the 3rd as per the pseudo code
I haven't looked at PSXSDK, so don't even know if it can :shrug

But reading your original post, sounds like you have split screen working, just a flickering problem.. which means your doing something extra, ie wait or swapping, when you don't need to, or your new draw env properties aren't correct, like I pointed out before.