Duhjoker
Well-known member
[SOLVED] Using frame buffering to solidify tft displayed objects
This thread has been solved. For more info on the GameR-iot arduino driven multiple MCU gaming platform thing, please catch me on github or join us on Facebook at GameR-Iot advanced multiple MCU gaming.
Ok guys I'm almost done with my project. For those that need a catch up..... I've been building a library for easy 8 to 32bit gaming on a console you build yourself using most Arduino driven MCU's but is focused around T3 MCU's. Its been shaky getting going. I have had to upgrade and update the original Gamebuino library to run on a teensy and with full color. But with the dedicated patience of the people here ive almost got everything working...... properly at least.
We fixed the tile map and fill screen functions and every thing displays but I'm having problems with flashing when the player sprite is set up over a tilemap. Both the tilemap and the character sprites flash. This was originally fixed in another library with kurts spin and frame buffer functions which kept the player sprites from flashing and making it solid. But sitting on top of the tilemapis another matter entirely.
I think this is frame buffer problem, maybe it cant catch up or maybe I'm using the functions wrong. Shouldn't it kind of clear the screen at a certain rate of speed so it looks solid? I also have a setoff frame rate commands that can be utilized but I need a lil working out the problem.
I have all that and more in my library as seen here. So first lets look at some OG Gamebuino functions.....
///from gamebuino display.h
the cpp counterparts from display.cpp
as you can see the bitmap function uses a buffer and framecount to help make grey scale and animations. The update uses the framecount as well but has the collision built into it as well. so I need to move some of that into the updateScreen() we will get to shortly. the problem with the gamebuino version is due to the pcd stuff and I'm not quite sure how to update the parts I need.
Here are the Gamebuino.h stuff
and its .cpp counterparts
now we can see right away in begin that the frame count stuff needs to be added to my libraries begin. My update all command has all the stuff as the update above but I'm using kurts update screen command but its not working as it should. but now I have all this included in my librariesa like so.....
and all that in cpp
cpp is too long to post everything but everything can be found any of my libraries on GitHub. I use the same hanfle every where so it should be easy to find
any way I have more than enough stuff but I don't how to use it in a way that solidifies whats being displayed
This thread has been solved. For more info on the GameR-iot arduino driven multiple MCU gaming platform thing, please catch me on github or join us on Facebook at GameR-Iot advanced multiple MCU gaming.
Ok guys I'm almost done with my project. For those that need a catch up..... I've been building a library for easy 8 to 32bit gaming on a console you build yourself using most Arduino driven MCU's but is focused around T3 MCU's. Its been shaky getting going. I have had to upgrade and update the original Gamebuino library to run on a teensy and with full color. But with the dedicated patience of the people here ive almost got everything working...... properly at least.
We fixed the tile map and fill screen functions and every thing displays but I'm having problems with flashing when the player sprite is set up over a tilemap. Both the tilemap and the character sprites flash. This was originally fixed in another library with kurts spin and frame buffer functions which kept the player sprites from flashing and making it solid. But sitting on top of the tilemapis another matter entirely.
I think this is frame buffer problem, maybe it cant catch up or maybe I'm using the functions wrong. Shouldn't it kind of clear the screen at a certain rate of speed so it looks solid? I also have a setoff frame rate commands that can be utilized but I need a lil working out the problem.
I have all that and more in my library as seen here. So first lets look at some OG Gamebuino functions.....
///from gamebuino display.h
Code:
extern uint8_t _displayBuffer[];
uint8_t* getBuffer();
void clear(void);
void update();
boolean persistence;
byte frameCount;
typedef struct{ //line 171 "Public Variables - ADD by Summoner123
int x; //X coordinate - ADD by Summoner123
int y; //Y coordinate - ADD by Summoner123
const byte *spritecol; //Sprite of object - ADD by Summoner123
}object;
object solid[60]; // Matriz were saved a Sprite, X and Y cordinates of all tiles on the screen - ADD by Summoner123
byte numcolision = 0; //count of solid objects indacat how many tiles drawed on the screen - ADD by Summoner123
bool flagcollision = true;
inline uint8_t* Display::getBuffer(){
return _displayBuffer;
}
the cpp counterparts from display.cpp
Code:
void Display::clear(void) {
memset(_displayBuffer, 0, LCDWIDTH * LCDHEIGHT / 8);
cursorY = cursorX = 0;
}
void Display::update(void) {
frameCount ++;
uint8_t col, maxcol, p;
for (p = 0; p < 6; p++) {
command(PCD8544_SETYADDR | p);
// start at the beginning of the row
col = 0;
maxcol = LCDWIDTH_NOROT - 1;
command(PCD8544_SETXADDR | col);
digitalWrite(dc, HIGH);
if (cs > 0)
digitalWrite(cs, LOW);
for (; col <= maxcol; col++) {
SPI.transfer(_displayBuffer[(LCDWIDTH_NOROT * p) + col]);
}
if (cs > 0)
digitalWrite(cs, HIGH);
}
command(PCD8544_SETYADDR); // no idea why this is necessary but it is to finish the last byte?
}
void Display::drawBitmap(int8_t x, int8_t y, int8_t w, int8_t h , const uint8_t *bitmap) {
#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_GRAYSCALE
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_GRAYSCALE
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
}
as you can see the bitmap function uses a buffer and framecount to help make grey scale and animations. The update uses the framecount as well but has the collision built into it as well. so I need to move some of that into the updateScreen() we will get to shortly. the problem with the gamebuino version is due to the pcd stuff and I'm not quite sure how to update the parts I need.
Here are the Gamebuino.h stuff
Code:
uint8_t startMenuTimer;
uint32_t frameCount;
void setFrameRate(uint8_t fps)
uint8_t getCpuLoad();
uint16_t getFreeRam();
uint16_t frameDurationMicros;
uint32_t frameStartMicros, frameEndMicros;
private:
uint8_t timePerFrame;
uint32_t nextFrameMillis;
and its .cpp counterparts
Code:
void Gamebuino::begin() {
timePerFrame = 50;
//nextFrameMillis = 0;
//frameCount = 0;
frameEndMicros = 1;
startMenuTimer = 255;
//read default settings from flash memory (set using settings.hex)
readSettings();
//init everything
backlight.begin();
backlight.set(BACKLIGHT_MAX);
buttons.begin();
buttons.update();
battery.begin();
display.begin(SCR_CLK, SCR_DIN, SCR_DC, SCR_CS, SCR_RST);
sound.begin();
//mute when B is held during start up
if(buttons.pressed(BTN_B)){
sound.setVolume(0);
}
else{ //play the startup sound on each channel for it to be louder
#if(NUM_CHANNELS > 0)
sound.playPattern(startupSound, 0);
#endif
#if(NUM_CHANNELS > 1)
sound.playPattern(startupSound, 1);
#endif
#if(NUM_CHANNELS > 2)
sound.playPattern(startupSound, 2);
#endif
#if(NUM_CHANNELS > 3)
sound.playPattern(startupSound, 3);
#endif
}
}
boolean Gamebuino::update() {
if (((nextFrameMillis - millis()) > timePerFrame) && frameEndMicros) { //if time to render a new frame is reached and the frame end has ran once
nextFrameMillis = millis() + timePerFrame;
frameCount++;
frameEndMicros = 0;
frameStartMicros = micros();
backlight.update();
buttons.update();
battery.update();
return true;
} else {
if (!frameEndMicros) { //runs once at the end of the frame
sound.updateTrack();
sound.updatePattern();
sound.updateNote();
updatePopup();
displayBattery();
display.update(); //send the buffer to the screen
if(!display.persistence)
display.clear(); //clear the buffer
frameEndMicros = micros(); //measure the frame's end time
frameDurationMicros = frameEndMicros - frameStartMicros;
// display.setTextColor(BLACK);
// display.setCursor(0, 40);
// display.print(frameDurationMicros / timePerFrame);
// display.print(" ");
// display.print(2048 - freeRam());
// display.setCursor(0, 32);
// display.print("CPU:");
// display.print(frameDurationMicros / timePerFrame);
// display.println("/1000");
// display.print("RAM:");
// display.print(2048 - freeRam());
// display.println("/2048");
}
return false;
}
}
void Gamebuino::setFrameRate(uint8_t fps) {
timePerFrame = 1000 / fps;
sound.prescaler = fps / 20;
sound.prescaler = max(1, sound.prescaler);
}
uint8_t Gamebuino::getCpuLoad(){
return(frameDurationMicros/(10*timePerFrame));
}
uint16_t Gamebuino::getFreeRam() {
//from http://www.controllerprojects.com/2011/05/23/determining-sram-usage-on-arduino/
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
boolean Gamebuino::collidePointRect(int16_t x1, int16_t y1 ,int16_t x2 ,int16_t y2, int16_t w, int16_t h){
if((x1>=x2)&&(x1<x2+w))
if((y1>=y2)&&(y1<y2+h))
return true;
return false;
}
boolean Gamebuino::collideRectRect(int16_t x1, int16_t y1, int16_t w1, int16_t h1 ,int16_t x2 ,int16_t y2, int16_t w2, int16_t h2){
return !( x2 >= x1+w1 ||
x2+w2 <= x1 ||
y2 >= y1+h1 ||
y2+h2 <= y1 );
}
boolean Gamebuino::collideBitmapBitmap(int16_t x1, int16_t y1, const uint8_t* b1, int16_t x2, int16_t y2, const uint8_t* b2){
int16_t w1 = pgm_read_byte(b1);
int16_t h1 = pgm_read_byte(b1 + 1);
int16_t w2 = pgm_read_byte(b2);
int16_t h2 = pgm_read_byte(b2 + 1);
if(collideRectRect(x1, y1, w1, h1, x2, y2, w2, h2) == false){
return false;
}
int16_t xmin = (x1>=x2)? 0 : x2-x1;
int16_t ymin = (y1>=y2)? 0 : y2-y1;
int16_t xmax = (x1+w1>=x2+w2)? x2+w2-x1 : w1;
int16_t ymax = (y1+h1>=y2+h2)? y2+h2-y1 : h1;
for(uint8_t y = ymin; y < ymax; y++){
for(uint8_t x = xmin; x < xmax; x++){
if(display.getBitmapPixel(b1, x, y) && display.getBitmapPixel(b2, x1+x-x2, y1+y-y2)){
return true;
}
}
}
return false;
}
now we can see right away in begin that the frame count stuff needs to be added to my libraries begin. My update all command has all the stuff as the update above but I'm using kurts update screen command but its not working as it should. but now I have all this included in my librariesa like so.....
Code:
// added support to use optional Frame buffer KurtE
uint8_t useFrameBuffer(boolean b); // use the frame buffer? First call will allocate
void freeFrameBuffer(void); // explicit call to release the buffer
void updateScreen(void); // call to say update the screen now.
uint16_t frameDurationMicros; ////DUHJOKER GAMEBUINO
uint32_t frameStartMicros, frameEndMicros; ////DUHJOKER GAMEBUINO
uint32_t frameCount; ////DUHJOKER GAMEBUINO
void setFrameRate(uint8_t fps); ////DUHJOKER GAMEBUINO
inline void drawPixel(int16_t x, int16_t y, uint16_t color);
uint8_t startMenuTimer;
uint8_t* getBuffer();
boolean persistence; //disable clean() at each frame if true //Duhjoker
uint8_t timePerFrame; ////DUHJOKER GAMEBUINO
uint32_t nextFrameMillis; ////DUHJOKER GAMEBUINO
inline uint8_t* Grafx::getBuffer(){
return _displayBuffer;
}
and all that in cpp
Code:
//=======================================================================
// Add optinal support for using frame buffer to speed up complex outputs
//=======================================================================
uint8_t Grafx::useFrameBuffer(boolean b) // use the frame buffer? First call will allocate
{
#ifdef ENABLE_ILI9341_FRAMEBUFFER
if (b) {
// First see if we need to allocate buffer
if (_pfbtft == NULL) {
_pfbtft = (uint16_t *)malloc(Grafx_TFTHEIGHT* Grafx_TFTWIDTH * 2);
if (_pfbtft == NULL)
return 0; // failed
memset(_pfbtft, 0, Grafx_TFTHEIGHT* Grafx_TFTWIDTH * 2);
}
_use_fbtft = 1;
}
else
_use_fbtft = 0;
return _use_fbtft;
#else
return 0;
#endif
}
void Grafx::freeFrameBuffer(void) // explicit call to release the buffer
{
#ifdef ENABLE_ILI9341_FRAMEBUFFER
if (_pfbtft != NULL) {
free(_pfbtft);
_pfbtft = NULL;
_use_fbtft = 0; // make sure the use is turned off
}
#endif
}
void Grafx::updateScreen(void) // call to say update the screen now.
{
frameCount ++;
// Not sure if better here to check flag or check existence of buffer.
// Will go by buffer as maybe can do interesting things?
// buttons.update();
#ifdef ENABLE_ILI9341_FRAMEBUFFER
if (_use_fbtft) {
beginSPITransaction();
if (_standard) {
// Doing full window.
setAddr(0, 0, _width - 1, _height - 1);
writecommand_cont(Grafx_RAMWR);
// BUGBUG doing as one shot. Not sure if should or not or do like
// main code and break up into transactions...
uint16_t *pfbtft_end = &_pfbtft[(Grafx_TFTWIDTH* Grafx_TFTHEIGHT) - 1]; // setup
uint16_t *pftbft = _pfbtft;
// Quick write out the data;
while (pftbft < pfbtft_end) {
writedata16_cont(*pftbft++);
}
writedata16_last(*pftbft);
}
else {
// setup just to output the clip rectangle area.
setAddr(_displayclipx1, _displayclipy1, _displayclipx2 - 1, _displayclipy2 - 1);
writecommand_cont(Grafx_RAMWR);
// BUGBUG doing as one shot. Not sure if should or not or do like
// main code and break up into transactions...
uint16_t * pfbPixel_row = &_pfbtft[_displayclipy1*_width + _displayclipx1];
for (uint16_t y = _displayclipy1; y < _displayclipy2; y++) {
uint16_t * pfbPixel = pfbPixel_row;
for (uint16_t x = _displayclipx1; x < (_displayclipx2 - 1); x++) {
writedata16_cont(*pfbPixel++);
}
if (y < (_displayclipy2 - 1))
writedata16_cont(*pfbPixel);
else
writedata16_last(*pfbPixel);
pfbPixel_row += _width; // setup for the next row.
}
}
endSPITransaction();
}
#endif
}
boolean Grafx::updateAll() {
if (((nextFrameMillis - millis()) > timePerFrame) && frameEndMicros) { //if time to render a new frame is reached and the frame end has ran once
nextFrameMillis = millis() + timePerFrame;
frameCount++;
frameEndMicros = 0;
frameStartMicros = micros();
// backlight.update();
// sound.updateTrack();
// sound.updatePattern();
// sound.updateNote();
// buttons.update();
// battery.update();
return true;
}
else {
if (!frameEndMicros) { //runs once at the end of the frame
// sound.updateTrack();
// sound.updatePattern();
// sound.updateNote();
// updatePopup();
// displayBattery();
update(); //send the buffer to the screen
if (!persistence)
freeFrameBuffer(); //clear the buffer
frameEndMicros = micros(); //measure the frame's end time
frameDurationMicros = frameEndMicros - frameStartMicros;
// display.setTextColor(BLACK);
// display.setCursor(0, 40);
// display.print(frameDurationMicros / timePerFrame);
// display.print(" ");
// display.print(2048 - freeRam());
// display.setCursor(0, 32);
// display.print("CPU:");
// display.print(frameDurationMicros / timePerFrame);
// display.println("/1000");
// display.print("RAM:");
// display.print(2048 - freeRam());
// display.println("/2048");
}
return false;
}
}
void Grafx::setFrameRate(uint8_t fps) {
timePerFrame = 1000 / fps;
// sound.prescaler = fps / 20;
// sound.prescaler = max(1, sound.prescaler);
}
cpp is too long to post everything but everything can be found any of my libraries on GitHub. I use the same hanfle every where so it should be easy to find
any way I have more than enough stuff but I don't how to use it in a way that solidifies whats being displayed
Last edited: