gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > double screen 3D & motion blur using display capture[sol

#154410 - Noda - Tue Apr 15, 2008 8:51 pm

Hello,

since I already use the display capture to use 3D on both screens, I though this should be easy to add a motion blur effect just by blending the new display with the old one on each screen, using the display capture register.

I don't really know why, but it doesn't work (flickers like hell on DS, and no trace of blending).

Here's my config:

Code:

void setRegCapture(bool enable, uint8 srcBlend, uint8 destBlend, uint8 bank, uint8 offset, uint8 size, uint8 source, uint8 srcOffset)
{
   uint32 value = 0;
   if (enable)
      value |= 1 << 31; // 31 is enable
   value |= 3 << 29; // 29-30 seems to have something to do with the blending
   value |= (srcOffset & 0x3) << 26; // capture source offset is 26-27
   value |= (source & 0x3) << 24; // capture source is 24-25
   value |= (size & 0x3) << 20; // capture data write size is 20-21
   value |= (offset & 0x3) << 18; // write offset is 18-19
   value |= (bank & 0x3) << 16; // vram bank select is 16-17
   value |= (srcBlend & 0xFF) << 8; // graphics blend evb is 8..12
   value |= (destBlend & 0xFF) << 0; // ram blend EVA is bits 0..4
   
   REG_CAPTURE = value;
}

void endFrame()      {
   if (ul_screenClippingChanged)
      ulResetScreenClipping();

   if (ul_dualScreenMode)         {
      if (ul_frameNumber & 1)         {
         vramSetBankC(VRAM_C_SUB_BG);
         vramSetBankD(VRAM_D_LCD);
         setRegCapture(true, 7, 8, 3, 0, 3, 0, 0);
      }
      else   {
         vramSetBankC(VRAM_C_LCD);
         vramSetBankD(VRAM_D_SUB_SPRITE);
         setRegCapture(true, 7, 8, 2, 0, 3, 0, 0);
      }
      
      lcdSwap();
      dmaCopy(ulDualScreenSprites, OAM_SUB, 128 * sizeof(SpriteEntry));
   }
   ul_frameNumber++;
}


The problem I think lies with the source A I'm trying to blend with. On gbatek, if it's set to 0 it said that the source is VRAM, but which one? the current one used for the capture (obviously)?

And I don't really get from where the flicker comes: if source B blending is < 15, the flickering occurs (seems like 1 frame on 2, screen is black).

Any idea?


Last edited by Noda on Wed Apr 16, 2008 10:27 pm; edited 1 time in total

#154445 - elhobbs - Wed Apr 16, 2008 1:29 pm

is VRAM_C_SUB_BG and VRAM_D_SUB_SPRITE correct?

#154450 - Noda - Wed Apr 16, 2008 3:18 pm

Yes, as it displays the screens correctly when I don't try to blend.

#154457 - elhobbs - Wed Apr 16, 2008 7:10 pm

gbatek mentions that you need to use VRAM display mode for this to work. would you need to be alternating display mode each frame - MODE_FB2/MODE_FB3 each frame? source A is fairly easy to set, but source B is where I start to get confused reading the documentation.

#154462 - Noda - Wed Apr 16, 2008 7:33 pm

Yes, that's the problem, gbatek info is quite unclear on the source B of the blending. But the VRAM display mode thing is just to say that the VRAM must be LCD (writable) mode, which is done.

I remember having seen motion blur demos using the capture using in the past, but I can't find those threads (damn search engine :/).

#154463 - Cydrak - Wed Apr 16, 2008 7:53 pm

The capture source is different than the write offset. See the GBATEK capture notes: bits 27-26 hold the low half, while the high bits are in DISPLAY_CR:19-18 (yes, that's the display source!). This gives the full A..D range.

Once you've got this, there's another problem: REG_CAPTURE doesn't actually display anything! In a dual-screen setup, it doesn't matter since the render is unchanged. But for blur, the blend only goes to VRAM, and you won't see it on the main screen. This can flicker subtly as it swaps with the sub every frame. (The other, worse cause of flicker is probably the capture just failing, where the sub is black as you said.)

Like elhobbs said the easiest fix is framebuffer mode. The idea is, you don't show any rendering at all--it happens behind the scenes, but you just see the final blend. As it happens, display and capture share the same VRAM source mentioned above. Fortunately, for a blur that's exactly what you want.

Here's about what I'm using (minus DMA and debug checks for brevity, hopefully I didn't break it):
Code:
#define dispSrcNone     0
#define dispSrcLayers   ((u16*)1)
#define capSize128x128  0
#define capSize256x64   1
#define capSize256x128  2
#define capSize256x192  3
#define capSrcNone      0
#define capSrcLayers    1
#define capSrc3D        2

void grSetDisplayTransfer(
    u16* displaySrc, u16* writeTo, int writeSize,
    int renderSrcA, u16* readSrcB, int evA, int evB)
{
    // Convert pointers - hw uses 32k steps from VRAM_A
    int dispOffset  = (int)(displaySrc - VRAM_A) / 0x4000;
    int writeOffset = (int)(writeTo - VRAM_A) / 0x4000;
    int srcOffset   = (int)(memSrcB - VRAM_A) / 0x4000;
   
    // Determine the intended display source..
    int dispMode = 0;  // None
    if(displaySrc == dispSrcLayers)
        dispMode = 1;  // 2D engine
    else if(0 <= dispOffset && dispOffset < 16)
        dispMode = 2;  // VRAM (framebuffer)
   
    // And what was chosen to blend..
    int blendMode = 2;
    if(!readSrcB)    blendMode = 0, cfB = 0;
    if(!renderSrcA) blendMode = 1, cfA = 0;
    else renderSrcA--;  // we have 1/2, hw wants 0/1
   
    // More bit arrangement for Nintendo's charming hw.. :b
    dispMode   |= (dispMode == 2 ? dispOffset : srcOffset) & 0xc;
    writeOffset = (writeOffset<<2 | writeOffset>>2) & 0xf;
    srcOffset  &= 3;
   
    DISPLAY_CR = (DISPLAY_CR & ~0xf0000) | dispMode<<16;
    if(!writeTo) return;
   
    REG_DISPCAPCNT = DCAP_ENABLE | blendMode<<29 | srcOffset<<26
        | renderSrcA<<24 | writeSize<<20 | writeOffset<<16 | evB<<8 | evA;
}

// Blur usage:
grSetDisplayTransfer(VRAM_C, VRAM_C, capSize256x192, capSrcLayers, VRAM_C, 12, 4);

// Restore normal 2D/3D output:
grSetDisplayTransfer(dispSrcLayers, 0, 0, 0, 0, 0, 0);

#154483 - Noda - Wed Apr 16, 2008 10:26 pm

Thanks a lot, after little modifications it worked :)