Welcome! Log In Create A New Profile

Advanced

How can I draw textured and flat/gouraud shaded primitives in the same frame?

Posted by TheCodingBrony 
I'm working on a tiny GX test program so I can learn more about how the GX functions work and I was able to figure out how to draw texture mapped primitives in a 2D plane. But I don't know how to 'unbind' from a texture so I can draw plain flat/gouraud shaded quads on the same frame and none of the included samples demonstrate this.

Here's the code of my test program:
#include stdio.h
#include stdlib.h
#include string.h
#include malloc.h
#include math.h
#include gccore.h
#include wiiuse/wpad.h

// My test texture (any 128x256 texture should do)
#include "sampletex_tpl.h"
#include "sampletex.h"


#define DEFAULT_FIFO_SIZE	(256*1024)


GXRModeObj	*rmode		= NULL;
void		*gp_fifo	= NULL;
GXColor		ClearColor	= {0, 0, 0, 0xff};

Mtx			myView, myModel, myModelView;
Mtx44		myPersp;
void		*myFB[2]	= { NULL, NULL};
int			ActiveFB	= 0;


TPLFile		sampleTPL;
GXTexObj	texture;

// Function prototypes
int main();
void init();


int main() {
	
	
	// Initialize everything
	init();
	
	
	while(1) {
		
		WPAD_ScanPads();
		
		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
		
		
		// Prepare for rendering
		GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
		
		
		// Set appropriate texturing modes
		GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
		GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
		
		
		// 'Bind' to our texture
		GX_LoadTexObj(&texture, GX_TEXMAP0);
		
		
		
		// Set geometry stuff
		guMtxIdentity(myModelView);
		guMtxTransApply (myModelView, myModelView, 0.0F, 0.0F, 0.0F);
		GX_LoadPosMtxImm(myModelView, GX_PNMTX0);
		
		
		// Draw the texture
		GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
			
			// Top-left
			GX_Position2f32(0, 0);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			GX_TexCoord2f32(0, 0);
			
			// Top-right
			GX_Position2f32(256, 0);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			GX_TexCoord2f32(1, 0);
			
			// Bottom-right
			GX_Position2f32(256, 512);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			GX_TexCoord2f32(1, 1);
			
			// Bottom-left
			GX_Position2f32(0, 512);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			GX_TexCoord2f32(0, 1);
			
		GX_End();
		
		
		/*
		// Draw a plain quad
		GX_Begin(GX_QUADS, GX_VTXFMT1, 4);
			
			// Top-left
			GX_Position2f32(300, 40);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			
			// Top-right
			GX_Position2f32(500, 40);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			
			// Bottom-right
			GX_Position2f32(500, 80);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			
			// Bottom-left
			GX_Position2f32(300, 80);
			GX_Color3f32(1.0f, 1.0f, 1.0f);
			
		GX_End();
		*/
		
		
		// Display the rendered scene
		
		
		ActiveFB ^= 1;
		GX_SetZMode(GX_FALSE, GX_LEQUAL, GX_FALSE);
		GX_SetColorUpdate(GX_TRUE);
		GX_CopyDisp(myFB[ActiveFB], GX_TRUE);
		
		GX_DrawDone();
		
		VIDEO_SetNextFramebuffer(myFB[ActiveFB]);
		VIDEO_Flush();
		VIDEO_WaitVSync();
		
		
	}
	
	
	return(0);
	
}

void init() {
	
	int		aspect=0;
	int		xfbHeight=0;
	float	yscale=0;

	
	if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) {
		aspect = 1;
	} else {
		aspect = 0;
	}
	
	
	// Setup video
	VIDEO_Init();
	
	
	// Allocate memory blocks as our framebuffers (two for double buffering)
	rmode = VIDEO_GetPreferredMode(NULL);
	myFB[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	myFB[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	
	
	// To fully get rid of the border on widescreen TVs
	if (aspect) {
		rmode->viWidth += 58;
		if (rmode->viWidth > 720)	rmode->viWidth = 720;
	}
	
	
	// More video init stuff
	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(myFB[ActiveFB]);
	VIDEO_SetBlack(FALSE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
	
	
	// Setup fifo and the 'flipper'
	gp_fifo = memalign(32, DEFAULT_FIFO_SIZE);
	memset(gp_fifo, 0, DEFAULT_FIFO_SIZE);
	
	
	// Initialize the GX system
	GX_Init(gp_fifo, DEFAULT_FIFO_SIZE);
	
	
	// Clears the bg to color and z buffer
	GX_SetCopyClear(ClearColor, 0x00ffffff);
	
	
	// More GX setup stuff
	GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
	yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight);
	xfbHeight = GX_SetDispCopyYScale(yscale);
	GX_SetScissor(0, 0, rmode->fbWidth, rmode->efbHeight);
	GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
	GX_SetDispCopyDst(rmode->fbWidth, xfbHeight);
	GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter);
	GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
	
	if (rmode->aa)
		GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
	else
		GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
	
	GX_SetCullMode(GX_CULL_NONE);
	GX_CopyDisp(myFB[ActiveFB], GX_TRUE);
	GX_SetDispCopyGamma(GX_GM_1_0);
	
	
	GX_ClearVtxDesc();
	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
	GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);

	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGB8, 0);

	
	
	GX_SetNumChans(1);
	GX_SetNumTexGens(1);
	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
	
	
	GX_InvVtxCache();
	GX_InvalidateTexAll();
	
	
	// Upload our texture	
	TPL_OpenTPLFromMemory(&sampleTPL, (void*)sampletex_tpl, sampletex_tpl_size);
	TPL_GetTexture(&sampleTPL, sampletex, &texture);
	
	
	// Setup orthogonal projection matrix
	if (aspect) {	// Anamorphic widescreen
		guOrtho(myPersp, 0, rmode->viHeight - 1, 0, (rmode->viWidth - 1) + 140, 0, 10);
	} else {		// Normal
		guOrtho(myPersp, 0, rmode->viHeight - 1, 0, rmode->viWidth - 1, 0, 10);
	}
	
	GX_LoadProjectionMtx(myPersp, GX_ORTHOGRAPHIC);
	
	
	WPAD_Init();
	
}

Any help will be gladly appreciated.
- TheCodingBrony



Edited 1 time(s). Last edit at 04/04/2014 04:17PM by TheCodingBrony.
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 04, 2014 08:22PM
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); // data for texture co-ordinate 0 will be passed directly
GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); // texture co-ordinate 0 will not be used (no texturing)

I don't think you should be using GX_VTXFMT1 for the plain quad, it's not being initialized anywhere.
I once had GX_VTXFMT1 initialized with GX_VA_POS and GX_VA_CLR0 only but I somehow removed those lines...

So, once I set the vertex descriptor of GX_VA_TEX0 to GX_NONE, do I still have to call GX_TexCoord2f32() during a GX_Begin() session or do I have to use another vertex format that doesn't have GX_VA_TEX0 initialized so I wouldn't have to call GX_TexCoord2f32() when drawing a flat/gouraud shaded primitive.
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 05, 2014 10:13AM
Just don't call GX_TexCoord* at all.
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, ...) only specifies the data format (float, int, etc) for the GX_VA_TEX0 slot in GX_VTXFMT0.
GX_SetVtxDesc() sets the source of the data for a particular attribute (direct, indirect, none). If the source is GX_NONE the attribute is disabled.
Just some general tips concerning rendering:

Use vertexbuffers (GX_SetArray) and displaylists (GX_CallDisplayList, ...) whereever possible, because immediate rendering like in your example does not utilize the Wii's vertex cache and wastes CPU cycles. Even if you don't want to use displaylists, use vertexbuffers, since it greatly reduces CPU load as only the index (instead of all vertex attributes) has to be submitted per vertex by the CPU then.
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 07, 2014 08:05PM
Unfortunately there are two bugs to watch out for with display lists:

- On gamecube, display list overflow detection is broken. The same bug can cause issues when manually redirecting/restoring the write-gather pipe on wii.
- GX_Flush() flushes too many zeroes through the write-gather pipe; only 31 bytes max. are required to flush any remaining data, 32 bytes will unavoidably cause the pipe to flush and unnecessarily inflate the size of the display list (this can also cause issues when manually redirecting the pipe/fifo, such as using it for texture swizzling).
Yes, there are a few pitfalls when using display lists, however the second one is not a bug, since it is documented behaviour in libogc and the official manual.

Basically it works like this:
* allocate memory aligned to 32bytes with a size multiple of 32 bytes + 32 bytes for padding (will be filled with GX_NOP, i.e. zero). So memory inflation will be a max of 63 bytes, which is neglectable in anything but the smallest meshes.

* call DCInvalidateRange() on the memory and size you allocated

* call GX_BeginDisplayList() and execute your GX commands to be recorded in the display list (there's no need to call GX_Flush() as GX_BeginDisplayList() does that for you)

* call GX_EndDisplayList()

* call DCFlushRange() on the memory you allocated

Later, when you want to execute the display list, call GX_CallDisplayList()
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 08, 2014 12:05PM
No, it's definitely a bug. The 32 bytes of trailing padding is unnecessary and causes errors when using the write-gather pipe for other purposes besides display lists. The WG pipe triggers a burst when there are at least 32 bytes waiting to be sent, therefore it is only necessary to flush 31 bytes to ensure remaining data is sent to the FIFO. If you compare libogc's GX_RestoreWriteGatherPipe() vs. the official SDK's GXRestoreWriteGatherPipe(), you can see the official SDK only flushes 31 bytes instead of 32.

You shouldn't be calling DCFlushRange on a display list after writing it; it's a waste of time since DCInvalidateRange will already remove the memory used for the list from the cache.



Edited 1 time(s). Last edit at 04/08/2014 12:09PM by tueidj.
Thanks for the information, Tueidj! Good to know that DCFlushRange is not necessary in this scenario. I thought I needed to force writeback from cache.

As I have never redirected the write gather pipe, I have never run into issues when using displaylists.
So if you don't mess with the write gather pipe, displaylists are still the way to go in terms of performance

EDIT: I remember now why I used DCFlushRange. After creating the display list, I changed its content, so in that case before calling the display list, I had to do DCFlushRange.



Edited 2 time(s). Last edit at 04/08/2014 12:55PM by antibyte.
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 08, 2014 01:27PM
If you touch the display list manually then yes, you'd want to flush the changes.
If you need to convert image data to texture format (rearranging tile pixels to linear storage) using the write-gather pipe is the fastest method; it's roughly 3x faster than using ordinary writes+cache flushing. The bug makes it unusable though since it causes junk to get written after the last output tile.
Good to know, thanks.

In what scenario do you typically need to convert image data that way at runtime?
Re: How can I draw textured and flat/gouraud shaded primitives in the same frame?
April 08, 2014 05:32PM
Anything that uses software rendering (old DOS games, emulators, etc) or otherwise treats the display as one large texture (movie players, SDL based apps).
Sorry, only registered users may post in this forum.

Click here to login