Troubles with 2D texture transfers from main ram to vram

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
Post Reply
sumolx
DC Developer
DC Developer
Posts: 12
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Thu May 07, 2009 11:24 am
Has thanked: 0
Been thanked: 0

Troubles with 2D texture transfers from main ram to vram

Post by sumolx »

I've been trying to replace OpenBOR's video interface from SDL to PVR/KOS calls so I can have a lower level of control and have libSDL memory usage returned back to the system. However, I'm not having any luck and I was hoping a second pair of eyes or more could help me in determining where I went wrong. Any help would be greatly appreciated!

Code: Select all

#include "types.h"
#include "video.h"
#include "vga.h"
#include "screen.h"
#include "openbor.h"
#include "filecache.h"

static pvr_ptr_t screen;
static int width, height, bpp=0;

int video_set_mode(s_videomodes videomodes)
{
	if(screen != NULL) return 1;
	
	bpp = videomodes.pixel;
	width = videomodes.hRes;
	height = videomodes.vRes;

	pvr_dma_init();
	vid_init(DM_320x240, PM_RGB888);
	screen = pvr_mem_malloc(width*height);
	pvr_set_pal_format(PVR_PAL_ARGB8888);
    
	return 1;
}

int video_copy_screen(s_screen* src)
{
	pvr_poly_cxt_t cxt;
	pvr_poly_hdr_t hdr;
	pvr_vertex_t vert;
	
    filecache_process();
	
	pvr_scene_begin();
	pvr_list_begin(PVR_LIST_OP_POLY);
	
	pvr_txr_load_ex(src->data, screen, width, height, PVR_TXRLOAD_8BPP | PVR_TXRLOAD_FMT_TWIDDLED);

	pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_PAL8BPP, 512, 512, screen, PVR_FILTER_NONE);
	pvr_poly_compile(&hdr, &cxt);
	pvr_prim(&hdr, sizeof(hdr));
	
	vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
	vert.oargb = 0;
	vert.z = 0;
	vert.u = 0;
	vert.v = 0;

	vert.flags = PVR_CMD_VERTEX;
	vert.x = 0;
	vert.y = height;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = 0;
	vert.y = 0;
	pvr_prim(&vert, sizeof(vert));

	vert.x = width;
	vert.y = height;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = width;
	vert.y = 0;
	vert.flags = PVR_CMD_VERTEX_EOL;
	pvr_prim(&vert, sizeof(vert));
	
	pvr_list_finish();
	pvr_scene_finish();
    return 1;
}

void video_clearscreen()
{
}

#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) )

void vga_setpalette(char* palette)
{
	int i;
	for (i = 0; i < 256; i++)
	{
		pvr_set_pal_entry(i, PACK_ARGB8888(0, palette[0], palette[1], palette[2]));
		palette+=3;
	}
}

void vga_vwait(void)
{
	vid_waitvbl();
}

Now here is the same code running using the SDL library which works great.

Code: Select all

#include <SDL.h>
#include "types.h"
#include "video.h"
#include "vga.h"
#include "screen.h"
#include "openbor.h"
#include "filecache.h"

static SDL_Surface *screen = NULL;
static SDL_Color colors[256];
static int width=0, height=0, bpp=0, mode=0;

int video_set_mode(s_videomodes videomodes)
{
    bpp = videomodes.pixel;
    width = videomodes.hRes;
    height = videomodes.vRes;
    if(screen) SDL_FreeSurface(screen);
    screen = SDL_SetVideoMode(width,height,8*bpp,SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_FULLSCREEN);
	if(screen==NULL) return 0;
	video_clearscreen();
	SDL_ShowCursor(SDL_DISABLE);
    return 1;
}

int video_copy_screen(s_screen* src)
{	
    filecache_process();
	screen->pixels = src->data;
    SDL_Flip(screen);
    return 1;
}

void video_clearscreen()
{
    memset(screen->pixels, 0, screen->pitch*screen->h);
}

void vga_setpalette(char* palette)
{
    int i;
    for(i=0;i<256;i++){
        colors[i].r=palette[0];
        colors[i].g=palette[1];
        colors[i].b=palette[2];
        palette+=3;
    }
    SDL_SetColors(screen,colors,0,256);
}

void video_fullscreen_flip()
{
}

void vga_vwait(void)
{
}
User avatar
Quzar
Dream Coder
Dream Coder
Posts: 7499
Joined: Wed Jul 31, 2002 12:14 am
Location: Miami, FL
Has thanked: 4 times
Been thanked: 10 times
Contact:

Re: Troubles with 2D texture transfers from main ram to vram

Post by Quzar »

Paletted textures are only supported in 16-bit mode (regardless of if you use 8bpp or 4bpp). The difference between the two is simply how much space you have left.
"When you post fewer lines of text than your signature, consider not posting at all." - A Wise Man
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Troubles with 2D texture transfers from main ram to vram

Post by BlackAura »

First thing that comes to mind - your texture sizes are probably wrong.

The PVR only supports textures where both width and height are a power of two. From what I remember of BoR, the screen size is something like 320x240 or thereabouts. If that's the case, you're allocating texture memory for a 320x240 texture, trying to upload that (which will fail - pvr_txr_load_ex checks the sizes), and then specifying a 512x512 texture. That isn't going to work.

The usual way to handle this is to take your 320x240 image, and pack it into the upper-left corner of the next available power-of-two texture (in this case, 512x256).

Second problem - you're using paletted images. This is a bad idea. Paletted images need to be twiddled, but the twiddling operation is inherently slow, particularly the way KOS implements it. It also absolutely requires square textures - in your case, you would have to upload an entire 512x512 buffer, even if you only really wanted to change a 320x240 chunk of it.

Third problem - your texture coordinates are wrong. Texture coordinates are floating point values, ranging from 0.0 (top / left) to 1.0 (bottom / right). Divide the pixel values by the width (or height) of the texture. So if you have a 320 pixel image in a 512 pixel wide texture, your U texture coordinate will be (320.0f / 512.0f), which is 0.625f.

The best way to handle all of these problems is to convert the 320x240 8bpp buffer into a 512x256 16bpp texture in software, then upload that to VRAM, and disable twiddling entirely.

So, as an (unoptimized, but still easily fast enough) example, you might do something like this:

Code: Select all

// Source buffer - this is the 8bpp paletted buffer BoR will draw into
int src_width = 320;
int src_height = 240;
uint8 *src_buf = (uint8 *)malloc(src_width * src_height);

// Temporary buffer - the 16bpp buffer we're using for conversion
// We're using a buffer in main memory because directly writing to VRAM is very
// slow (technical explanation - it's an uncached area, so every time you write
// anything to VRAM, it fetches a 32-byte chunk into the CPU cache, modifies it
// and then writes it back).
int tmp_width = 512;
int tmp_height = 256;
int tmp_size = 2 * tmp_width * tmp_height;
uint16 *tmp_buf = (uint16 *)malloc(tmp_size);

// Texture pointer - in VRAM
pvr_ptr_t tex_ptr = pvr_mem_malloc(tmp_size);

// Colour palette - this needs to be filled with 16bpp RGB565 colour values
// You can do this inside the vga_setpalette function
uint16 dc_palette[256];

// Process each scanline
for(int y = 0; y < src_height; y++)
{
	// Calculate the position of the start of each scanline
	uint8 *src_ptr = src_buf + (y * src_width);
	uint16 *tmp_ptr = tmp_buf + (y * tmp_width);

	// For each pixel, look up the palette entry, and copy to the output
	for(int x = 0; x < src_width; x++)
	{
		tmp_ptr[x] = dc_palette[src_ptr[x]];

		// You can do this by incrementing pointers instead. That should be
		// faster, but it's a bit more difficult to read, and I still feel a
		// bit dirty because of that pointer manipulation in the outer loop...
	}
}

// Upload the texture directly, using store queues.
// This bypasses KOS's texture loading, and uploads the image data directly.
// We can do this because the texture we're uploading is already in the correct
// format. This is the second fastest way to upload a texture that you've just
// generated, but it's also by far the simplest, and has no weird side effects.
sq_cpy(tex_ptr, tmp_ptr, tmp_size);
From there, you draw much the same way you are now. Just remember to use a texture format of PVR_TXRFMT_RGB565 to pvr_poly_cxt_txr, and remember to normalize the texture coordinates.
sumolx
DC Developer
DC Developer
Posts: 12
Joined: Thu May 07, 2009 11:24 am
Has thanked: 0
Been thanked: 0

Re: Troubles with 2D texture transfers from main ram to vram

Post by sumolx »

Hmmm, I'm still not doing something right as the Image looks garbled up on screen. Here is the updated changes. Thanks for the help thus far!

Code: Select all

#include "dc/pvr.h"
#include "types.h"
#include "video.h"
#include "vga.h"
#include "screen.h"
#include "openbor.h"
#include "filecache.h"

static pvr_ptr_t tex_ptr;
static uint16 tmp_buf[512*256*2]; 
static uint16 palette[256]; 
static int width, height, bpp=0;


int video_set_mode(s_videomodes videomodes)
{
	if(tex_ptr) return 1;
	
	bpp = videomodes.pixel;
	width = videomodes.hRes;
	height = videomodes.vRes;

	pvr_dma_init();
	vid_init(DM_640x480, PM_RGB565);
	tex_ptr = pvr_mem_malloc(512*256*2);
    
	return 1;
}

int video_copy_screen(s_screen* src)
{
	int x, y;
	uint8 *src_ptr = NULL;
	uint16 *tmp_ptr = NULL;
	
	pvr_poly_cxt_t cxt;
	pvr_poly_hdr_t hdr;
	pvr_vertex_t vert;
	
    filecache_process();
	
	// Process each scanline
	for(y=0; y<height; y++)
	{
		// Calculate the position of the start of each scanline
		src_ptr = src->data + (y * width);
		tmp_ptr = tmp_buf + (y * 512);
		
		// For each pixel, look up the palette entry, and copy to the output
		for(x=0; x<width; x++)
		{
			tmp_ptr[x] = palette[src_ptr[x]];
			
			// You can do this by incrementing pointers instead. That should be
			// faster, but it's a bit more difficult to read, and I still feel a
			// bit dirty because of that pointer manipulation in the outer loop...
		}
	}
	
	pvr_scene_begin();
	pvr_list_begin(PVR_LIST_OP_POLY);
	
	// Upload the texture directly, using store queues.
	// This bypasses KOS's texture loading, and uploads the image data directly.
	// We can do this because the texture we're uploading is already in the correct
	// format. This is the second fastest way to upload a texture that you've just
	// generated, but it's also by far the simplest, and has no weird side effects.
	sq_cpy(tex_ptr, tmp_ptr, 512*256*2);
	
	pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565, 512, 256, tex_ptr, PVR_FILTER_NONE);
	pvr_poly_compile(&hdr, &cxt);
	pvr_prim(&hdr, sizeof(hdr));
		
	vert.flags = PVR_CMD_VERTEX;
	vert.x = 0;
	vert.y = 0;
	vert.z = 0;
	vert.u = 0;
	vert.v = 0;
	vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
	vert.oargb = 0;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = width;
	vert.y = 0;
	vert.z = 0;
	vert.u = width/512;
	vert.v = 0;
	pvr_prim(&vert, sizeof(vert));

	vert.x = 0;
	vert.y = height;
	vert.z = 0;
	vert.u = 0;
	vert.v = height/256;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = width;
	vert.y = height;
	vert.z = 0;
	vert.u = width/512;
	vert.v = height/256;
	vert.flags = PVR_CMD_VERTEX_EOL;
	pvr_prim(&vert, sizeof(vert));
	
	pvr_list_finish();
	pvr_scene_finish();
    return 1;
}

void video_clearscreen()
{
}

#define PACK_PIXEL(r, g, b) ( ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) )
#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) )

void vga_setpalette(char* pal)
{
	int i;
	for(i=0;i<256;i++)
	{
		palette[i] = PACK_PIXEL(pal[0], pal[1], pal[2]);
		pal+=3;
	}
}

void vga_vwait(void)
{
	vid_waitvbl();
}
OneThirty8
Damn Dirty Ape
Damn Dirty Ape
Posts: 5031
Joined: Thu Nov 07, 2002 11:11 pm
Location: Saugerties, NY
Has thanked: 0
Been thanked: 0

Re: Troubles with 2D texture transfers from main ram to vram

Post by OneThirty8 »

Try changing this:

Code: Select all

pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565, 512, 256, tex_ptr, PVR_FILTER_NONE);
to this:

Code: Select all

pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565|PVR_TXRFMT_NONTWIDDLED, 512, 256, tex_ptr, PVR_FILTER_NONE);
Unless I read wrong, you're uploading your image using sq_cpy, which should work just fine, but it doesn't twiddle the texture.

*EDIT*
Also, I noticed this line, where you copy the image to the memory you've allocated for the texture...

Code: Select all

sq_cpy(tex_ptr, tmp_ptr, 512*256*2);
Shouldn't that be tmp_buf? By the time you reach this line, tmp_ptr points to the last scanline you have drawn, whereas tmp_buf should be the start of the first scanline.
sumolx
DC Developer
DC Developer
Posts: 12
Joined: Thu May 07, 2009 11:24 am
Has thanked: 0
Been thanked: 0

Re: Troubles with 2D texture transfers from main ram to vram

Post by sumolx »

I believe you are correct about tmp_buf, but even after making both changes, the screen is still garbled up. Keep the ideas coming as I am at a loss, since my only experience I have has been with the PSP's GPU and SDL.

All of your help is extremely appreciated and it is an invaluable learning experience on my end without a doubt.
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Troubles with 2D texture transfers from main ram to vram

Post by BlackAura »

In what way does it look garbled?

If it looks like the pixels have been shuffled around, then it's probably a twiddling issue.

If you're only getting a single triangle showing up, or if you have a triangular chunk missing, you're probably submitting the vertices in the wrong order. Since you're submitting a triangle strip, you have to make sure that you submit the vertices in the right order. This looks to be OK in the code you posted.

If the resulting image looks stretched, or has become all one colour, it could be something to do with the texture coordinates. From the code you posted, it looks like you might be using integer division to generate the texture coordinates, which won't work - the result will always be 0. Try something like:

Code: Select all

vert.u = (float)width / 512.0f;
vert.v = (float)height / 256.0f;
If the image is basically OK, but some of the pixels (possibly towards the bottom of the screen) look like they've been replaced with something else, that could indicate that the texture is being read at the same time it's being written to. It looks kind of like tearing, but doesn't have the same kind of clean dividing line - instead, you have a mixture of pixels from two separate frames, spread over something like 32 pixels high. I've only ever had this happen when using DMA to transfer textures, since DMA transfers are asynchronous. However, I usually update textures before calling pvr_scene_begin (which waits for a vblank), so I'm not sure what would happen if you updated the textures afterwards.

If there's nothing recognisable at all on screen, then it could be almost anything, but it's probably a problem with the texture upload. The wrong data could be getting uploaded to VRAM. It could be getting uploaded to the wrong location in VRAM.
sumolx
DC Developer
DC Developer
Posts: 12
Joined: Thu May 07, 2009 11:24 am
Has thanked: 0
Been thanked: 0

Re: Troubles with 2D texture transfers from main ram to vram

Post by sumolx »

I've made some slight tweaks for now such as disreguarding the palette indexes.... but the image should still be readable on screen with the colors not being set correctly. I believe the vertices are set correctly but its still not making a difference and the image is garbled up.

Personally I have run out of ideas, and I think my next step is to reverse engineer how SDL uses video (hoping they are using pvr and not writing directly to vram).

{EDIT}
Seems SDL writes directly to vram after all..... Let me see if Chui's SDL library is different.
{EDIT}

Thank You so much for everyone's help thus far!

This is the image I'm currently seeing:
Image

Here is the code I'm working with:

Code: Select all

#include "dc/pvr.h"
#include "types.h"
#include "video.h"
#include "vga.h"
#include "screen.h"
#include "openbor.h"
#include "filecache.h"

static pvr_ptr_t tex_ptr;
static uint16 palette[256]; 
static int width, height, bpp=0;


int video_set_mode(s_videomodes videomodes)
{
	if(tex_ptr) return 1;
	
	bpp = videomodes.pixel;
	width = videomodes.hRes;
	height = videomodes.vRes;

	pvr_dma_init();
	vid_init(DM_320x240, PM_RGB888);
	tex_ptr = pvr_mem_malloc(512*512*2);
    
	return 1;
}

int video_copy_screen(s_screen* src)
{	
	pvr_poly_cxt_t cxt;
	pvr_poly_hdr_t hdr;
	pvr_vertex_t vert;
	
    filecache_process();
	
	pvr_scene_begin();
	pvr_list_begin(PVR_LIST_OP_POLY);
	
	// Upload the texture directly, using store queues.
	// This bypasses KOS's texture loading, and uploads the image data directly.
	// We can do this because the texture we're uploading is already in the correct
	// format. This is the second fastest way to upload a texture that you've just
	// generated, but it's also by far the simplest, and has no weird side effects.
	sq_cpy(tex_ptr, src->data, src->width * src->height);
	
	pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_PAL8BPP | PVR_TXRFMT_8BPP_PAL(1), 512, 512, tex_ptr, PVR_FILTER_NONE);
	pvr_poly_compile(&hdr, &cxt);
	pvr_prim(&hdr, sizeof(hdr));
		
	vert.flags = PVR_CMD_VERTEX;
	vert.x = 0.0f;
	vert.y = 0.0f;
	vert.z = 0.0f;
	vert.u = 0.0f;
	vert.v = 0.0f;
	vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
	vert.oargb = 0;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = (float)src->width;
	vert.y = 0.0f;
	vert.z = 0.0f;
	vert.u = (float)src->width/512.0f;
	vert.v = 0.0f;
	pvr_prim(&vert, sizeof(vert));

	vert.x = 0.0f;
	vert.y = (float)src->height;
	vert.z = 0.0f;
	vert.u = 0.0f;
	vert.v = (float)src->height/512.0f;
	pvr_prim(&vert, sizeof(vert));
	
	vert.x = (float)src->width;
	vert.y = (float)src->height;
	vert.z = 0.0f;
	vert.u = (float)src->width/512.0f;
	vert.v = (float)src->height/512.0f;
	vert.flags = PVR_CMD_VERTEX_EOL;
	pvr_prim(&vert, sizeof(vert));
	
	pvr_list_finish();
	pvr_scene_finish();
    return 1;
}

void video_clearscreen()
{
}

#define PACK_PIXEL(r, g, b) ( ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) )
#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) )

void vga_setpalette(char* pal)
{
	int i;
	for(i=0;i<256;i++)
	{
		palette[i] = PACK_PIXEL(pal[0], pal[1], pal[2]);
		pal+=3;
	}
}

void vga_vwait(void)
{
	vid_waitvbl();
}
User avatar
Christuserloeser
Moderator
Moderator
Posts: 5948
Joined: Thu Aug 28, 2003 12:16 am
Location: DCEvolution.net
Has thanked: 10 times
Been thanked: 0
Contact:

Re: Troubles with 2D texture transfers from main ram to vram

Post by Christuserloeser »

Btw, for anyone interested - the SVN is located here: http://lavalit.com:8080/index.php?action=svn
Insane homebrew collector.
Post Reply