#71511 - HyperHacker - Mon Feb 13, 2006 7:06 am
This is a problem that has plauged every DS app I've written so far. I set both screens to mode 5 and use BG3 to display graphics; it simply works like a 256x192 16BPP bitmap. Because drawing directly into VRAM produces flicker, I keep a second bitmap in memory, and use dmaCopyWords to copy it into VRAM during VBlank.
The problem is, sometimes certain portions of the screen around the corners simply will not update. If I draw something, then erase it to black, pieces of what I drew are still visible even though reading from the bitmap in memory shows that the pixel value is 0x8000, which should be black. They keep displaying the old image until I draw something else there, which is especially odd, as it should be copying the bitmap into VRAM every VBlank whether I've modified it or not. The parts that don't get updated seem to be random, but have some patterns:
I can draw as many different things as I want, and they all show up fine, until I try to clear the screen to black (0x8000) at which point some of the pixels just don't change. This can be seen in my GBA Booter app; switching from any border to None will leave pieces of the border on the screen. What's more is it's not just black. If I try to clear the screen to any colour at all, it will do this, but when I'm actually drawing images, it works fine. Also this never happens if I only clear outside of those areas (as mentioned, it only happens in the corners). The problem isn't in the function that does the clearing either, as I've used several different methods including writing directly to the bitmap.
The problem seems to be that certain writes to the bitmap are being ignored. However I've used all manner of methods to ensure they aren't:
I'm completely stumped as to what the heck is going on here. @_@
Relevant code, all on ARM9:
Setting up video and interrupts:
VBlank handler:
(and yes, I am setting SwapBuffers, also tried ignoring it and just updating every frame.)
Graphic functions involved:
The problem is, sometimes certain portions of the screen around the corners simply will not update. If I draw something, then erase it to black, pieces of what I drew are still visible even though reading from the bitmap in memory shows that the pixel value is 0x8000, which should be black. They keep displaying the old image until I draw something else there, which is especially odd, as it should be copying the bitmap into VRAM every VBlank whether I've modified it or not. The parts that don't get updated seem to be random, but have some patterns:
- Starting from the very top left and very bottom left corners (positions 0,0 and 0,191) there are always at least two pixels in a horizontal line that don't update.
- The problem always occurrs near the corners of the screen.
- The non-updated portions are often rectangular, and always wrap around the edge of the screen (starting on the right, and continuing on to the next row on the left, as the bitmap is a one-dimensional array).
I can draw as many different things as I want, and they all show up fine, until I try to clear the screen to black (0x8000) at which point some of the pixels just don't change. This can be seen in my GBA Booter app; switching from any border to None will leave pieces of the border on the screen. What's more is it's not just black. If I try to clear the screen to any colour at all, it will do this, but when I'm actually drawing images, it works fine. Also this never happens if I only clear outside of those areas (as mentioned, it only happens in the corners). The problem isn't in the function that does the clearing either, as I've used several different methods including writing directly to the bitmap.
The problem seems to be that certain writes to the bitmap are being ignored. However I've used all manner of methods to ensure they aren't:
- Displaying the pixels' colour value on the screen and ensuring that it's correct
- Delaying DMA until all writes to the bitmap are complete
- Delaying writes to the bitmap until DMA is complete
- Adding a while loop to the function that clears the bitmap to keep writing each pixel until its value actually is the value being written
I'm completely stumped as to what the heck is going on here. @_@
Relevant code, all on ARM9:
Setting up video and interrupts:
Code: |
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE);
videoSetModeSub(DISPLAY_SCREEN_OFF); //Yes, I'm only using one screen on the DS. Crazy ain't it? vramSetMainBanks(VRAM_A_MAIN_BG_0x6000000, VRAM_B_MAIN_BG_0x6000000, VRAM_C_LCD, VRAM_D_LCD); //Put both VRAM banks at the same place, so borders work MainScreenBuf = CreateGraphicBuffer(SCREEN_WIDTH,SCREEN_HEIGHT); Coords = (8 << 16) | 16; while(IPC->arm9desc != A9_GBASCREEN); IPC->arm9desc = 0; if(IPC->arm9data) { TopScreen = false; lcdMainOnBottom(); IPC->arm7data = PM_BACKLIGHT_TOP; } else { TopScreen = true; lcdMainOnTop(); IPC->arm7data = PM_BACKLIGHT_BOTTOM; } IPC->arm7desc = A7_POWEROFF; while(IPC->arm9desc != A9_ACK); IPC->arm9desc = 0; BG3_CR = BG_BMP16_256x256; BG3_XDX = 1 << 8; BG3_XDY = 0; BG3_YDX = 0; BG3_YDY = 1 << 8; BG3_CX = 0; BG3_CY = 0; SUB_BG3_CR = BG_BMP16_256x256; SUB_BG3_XDX = 1 << 8; SUB_BG3_XDY = 0; SUB_BG3_YDX = 0; SUB_BG3_YDY = 1 << 8; SUB_BG3_CX = 0; SUB_BG3_CY = 0; //Init interrupts REG_IME = 0; //Disable interrupts while changing them IRQ_HANDLER = Interrupt; //Set handler callback REG_IE = IRQ_VBLANK | IRQ_HBLANK; //Interrupt on vblank only REG_IF = ~0; DISP_SR = DISP_VBLANK_IRQ | DISP_HBLANK_IRQ; REG_IME = 1; //Enable interrupts swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); //Let things set up |
VBlank handler:
Code: |
else if(REG_IF & IRQ_VBLANK) //VBlank interrupt
{ KeysPressed = IPC->buttons_pressed; //Best to keep a local copy, since the ARM7 may modify it KeysHeld = IPC->buttons_held; if(SwapBuffers) { //while(MainScreenBuf->InUse); //MainScreenBuf->InUse++; dmaCopyWords(0,MainScreenBuf->Pixels,BG_GFX,(SCREEN_WIDTH*SCREEN_HEIGHT) << 1); //while(dmaBusy(0)) swiDelay(1); //MainScreenBuf->InUse--; SwapBuffers = false; } VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK; //Signal that vblank interrupt has been processed REG_IF |= IRQ_VBLANK; //Signal that vblank interrupt processing is done. We need to trigger a write even though this shouldn't change the value. } |
(and yes, I am setting SwapBuffers, also tried ignoring it and just updating every frame.)
Graphic functions involved:
Code: |
/*
Creates a graphic buffer CPU: ARM9 Inputs: -Width, Height = Size of buffer Returns: Pointer to buffer or NULL on fail. Notes: -Only 16-bit-colour buffers are currently supported. -Be sure to free the buffer when you're done with it. (Just free(Buffer) will do.) -Buffer's content is undefined when created. If you need it to start filled with a given colour, fill it yourself after creating it. */ GraphicBuffer* CreateGraphicBuffer(uint16 Width, uint16 Height) { GraphicBuffer* G = (GraphicBuffer*)memalign(4,sizeof(GraphicBuffer) + (((uint32)Width * (uint32)Height) << 1)); if(!G) return NULL; G->Width = Width; G->Height = Height; return G; } /* Clears a graphic buffer to a specified colour CPU: ARM9 Inputs: -Buffer: Pointer to graphic buffer -Colour: RGB15 colour */ void ClearGraphicBuffer(GraphicBuffer* Dest, uint16 Colour) { Colour |= 0x8000; int i; uint16* P = Dest->Pixels; for(i=0;i<(Dest->Width * Dest->Height);i++) { (*P) = Colour; P++; } } /* Draws a solid rectangle on a graphic buffer CPU: ARM9 Inputs: -Dest = Destination buffer -DX, DY = Destination coords -W, H = Dimensions of rectangle -Colour = RGB15 colour */ void FillRect(GraphicBuffer* Dest, uint16 DX, uint16 DY, uint16 W, uint16 H, uint16 Colour) { Colour |= 0x8000; int x,y; uint16* DP = Dest->Pixels + DX + (DY * Dest->Width); for(y=0;y<H;y++) { for(x=0;x<W;x++) (*(DP + x)) = Colour; DP += Dest->Width; } } |