Display functions optimization

Libraries, utilities, bootloaders...

Re: Display functions optimization

Postby Myndale » Sat Jan 24, 2015 8:44 am

Doing a screen boundary check per-pixel would have a huge effect on performance but it doesn't actually need to be done. All that needs to be done is clipping the destination rectangle to the screen at the start of the routine and then adjusting the initial screen and bitmap address/masks accordingly.

Fortunately we're in a long weekend here in Australia so gimme another day or two and I'll give you something you can drop into the library. :)
Myndale
 
Posts: 507
Joined: Sat Mar 01, 2014 1:25 am

Re: Display functions optimization

Postby rodot » Sat Jan 24, 2015 9:19 am

Awesome Myndale, thanks ! :)
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: Display functions optimization

Postby rodot » Sat Jan 24, 2015 12:58 pm

The following does gray with alternating vertical lines (not as good as the alternative checker pattern), but still works pretty well:
Code: Select all
        if(gb.frameCount & 0x01) { //odd frames
          if (pixels & 0x80) ptr[0] |= mask;
          if (pixels & 0x40) ptr[1] &= mask;
          if (pixels & 0x20) ptr[2] |= mask;
          if (pixels & 0x10) ptr[3] &= mask;
          if (pixels & 0x08) ptr[4] |= mask;
          if (pixels & 0x04) ptr[5] &= mask;
          if (pixels & 0x02) ptr[6] |= mask;
          if (pixels & 0x01) ptr[7] &= mask;
        } else {
          if (pixels & 0x80) ptr[0] &= mask;
          if (pixels & 0x40) ptr[1] |= mask;
          if (pixels & 0x20) ptr[2] &= mask;
          if (pixels & 0x10) ptr[3] |= mask;
          if (pixels & 0x08) ptr[4] &= mask;
          if (pixels & 0x04) ptr[5] |= mask;
          if (pixels & 0x02) ptr[6] &= mask;
          if (pixels & 0x01) ptr[7] |= mask;
        }


This would allow to add the GRAY color to the library.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: Display functions optimization

Postby Skyrunner65 » Sat Jan 24, 2015 4:24 pm

rodot wrote:This would allow to add the GRAY color to the library.


:mrgreen: Please add this. It would make the production of sprites so much easier and with detail.
User avatar
Skyrunner65
 
Posts: 371
Joined: Thu Mar 20, 2014 5:37 pm
Location: NC,USA

Re: Display functions optimization

Postby rodot » Sun Jan 25, 2015 9:48 am

The GRAY color is added to the beta branch of the library, although the new bitmap optimized routines are not committed yet.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Re: Display functions optimization

Postby cyberic » Mon Jan 26, 2015 5:26 pm

fillRect is now slower than drawBitmap...
Can't we just draw a black sprite?
cyberic
 
Posts: 27
Joined: Thu May 08, 2014 5:36 pm

Re: Display functions optimization

Postby Myndale » Tue Jan 27, 2015 1:52 am

Here's a drop-in replacement for drawBitmap that supports clipping and all colors including grayscale (if you enable it). I've tested it with Crabator and it seems to work fine:

Code: Select all
void Display::drawBitmap(int8_t x, int8_t y, const uint8_t *bitmap) {
   int8_t w = pgm_read_byte(bitmap);
   int8_t h = pgm_read_byte(bitmap + 1);
   bitmap = bitmap + 2; //add an offset to the pointer to start after the width and height
#if (ENABLE_BITMAPS > 0)
/*   original code
    int8_t i, j, byteWidth = (w + 7) / 8;
    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (B10000000 >> (i % 8))) {
                drawPixel(x + i, y + j);
            }
        }
    }
  */
  uint8_t * buffer = getBuffer();
  const uint8_t col = color;
  const uint8_t bw = (w+7) / 8;
 
  // clip
  if (x >= LCDWIDTH)
    return;
  if (x + w <= 0)
    return;
  if (y >= LCDHEIGHT)
    return;
  if (y + h <= 0)
    return;
  if (y < 0)
    h += y, bitmap -= bw * y, y = 0;
  if (y + h > LCDHEIGHT)
    h = LCDHEIGHT - y; 
  uint8_t x1 = max(0, x);
  uint8_t x2 = min(LCDWIDTH, x + w);
 
#ifdef ENABLE_GREYSCALE
   uint8_t g = y ^ frameCount;
#endif 

  // draw
  uint8_t first_bitmap_mask = 0x80 >> ((x1 - x) & 7);
  const uint8_t * bitmap_line = bitmap + (x1 - x) / 8;
  uint8_t screen_mask = 0x01 << (y % 8);
  uint8_t * screen_row = buffer + (y / 8) * LCDWIDTH + x1; 
  for (uint8_t dy=0; dy<h; dy++, bitmap_line+=bw)
  {
    const uint8_t * bitmap_ptr = bitmap_line;   
    uint8_t bitmap_mask = first_bitmap_mask;   
    uint8_t pixels = pgm_read_byte(bitmap_ptr);
    uint8_t * dst = screen_row;
   
    if (col == BLACK)
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst |= screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
    else if (col == WHITE)
    {
      uint8_t inv_screen_mask = ~screen_mask;
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst &= inv_screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
    }
#ifdef ENABLE_GREYSCALE
    else if (col == GRAY)
    {
      uint8_t inv_screen_mask = ~screen_mask;
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
        {
         if ((sx^g) & 1)
            *dst |= screen_mask;
          else
           *dst &= inv_screen_mask;
        }
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
       g ^= 1;
    }
#endif
   else // invert
      for (uint8_t sx=x1; sx<x2; sx++, dst++)
      {
        if (pixels & bitmap_mask)
          *dst ^= screen_mask;
        bitmap_mask >>= 1;
        if (!bitmap_mask)
        {
          bitmap_mask = 0x80;
          pixels = pgm_read_byte(++bitmap_ptr);
        }
      }
   
    screen_mask <<= 1;
    if (!screen_mask)
    {
      screen_mask = 1;
      screen_row += LCDWIDTH;
    }
  }
#else
   drawRect(x, y, w, h);
#endif
}


If you want greyscale then you'll need to define ENABLE_GREYSCALE (or just comment those lines back in), you'll also need to provide a way of passing in the frameCount variable.

As is usually the case with optimization this code tries to strike a balance between speed and code size. It currently adds about 400 bytes to the size of the function and runs my test blt code in around 1500ms.
Myndale
 
Posts: 507
Joined: Sat Mar 01, 2014 1:25 am

Re: Display functions optimization

Postby erico » Tue Jan 27, 2015 2:12 am

:O
User avatar
erico
 
Posts: 671
Joined: Thu Mar 27, 2014 9:29 pm
Location: Brazil

Re: Display functions optimization

Postby rodot » Wed Jan 28, 2015 11:30 am

Thanks a lot Myndale, I just commited it to the beta branch and give your credit in the changelog. Along with this update I did a fix of the horizontal bitmap flip (there was an offset), and added examples for drawBitmap and setColor.
User avatar
rodot
Site Admin
 
Posts: 1290
Joined: Mon Nov 19, 2012 11:54 pm
Location: France

Previous

Return to Software Development

Who is online

Users browsing this forum: No registered users and 25 guests

cron