Enable Graphics arraybuffer optimisations on all but low-end devices

Add specific Graphics optimisations for 1 and 8 bit rendering
This commit is contained in:
Gordon Williams 2020-05-04 16:59:46 +01:00
parent 278abac468
commit d29a7cdf09
8 changed files with 182 additions and 55 deletions

View File

@ -11,6 +11,8 @@
Puck.js v1: Fix regression that stopped Puck.IR(data) working - Puck.IR(data,D26,D25) required
Puck.js v2: Ensure FET is used for IR output, fix selfTest errors for IR and Blue LED
Bangle.js: Big speed improvements for 120x120 and 80x80 gfx modes
Enable Graphics arraybuffer optimisations on all but low-end devices
Add specific Graphics optimisations for 1 and 8 bit rendering
2v05 : Add Array.includes
Fix (Number.toFixed) rounding, eg (1234.505).toFixed(2)

View File

@ -31,15 +31,6 @@
#include "bitmap_font_4x6.h"
#include "bitmap_font_6x8.h"
#ifndef SAVE_ON_FLASH
#ifndef ESPRUINOBOARD
#define GRAPHICS_DRAWIMAGE_ROTATED
#endif
#endif
#if defined(LINUX) || defined(BANGLEJS)
#define GRAPHICS_FAST_PATHS // execute more optimised code when no rotation/etc
#endif
#ifdef GRAPHICS_PALETTED_IMAGES
// 16 color MAC OS palette
const uint16_t PALETTE_4BIT[16] = { 0x0,0x4228,0x8c51,0xbdd7,0x9b26,0x6180,0x320,0x540,0x4df,0x19,0x3013,0xf813,0xd800,0xfb20,0xffe0,0xffff };

View File

@ -17,6 +17,15 @@
#include "jsvar.h"
#include "graphics.h"
#ifndef SAVE_ON_FLASH
#ifndef ESPRUINOBOARD
#define GRAPHICS_DRAWIMAGE_ROTATED
#endif
#endif
#if defined(LINUX) || defined(BANGLEJS)
#define GRAPHICS_FAST_PATHS // execute more optimised code when no rotation/etc
#endif
#ifdef GRAPHICS_PALETTED_IMAGES
// 16 color MAC OS palette
extern const uint16_t PALETTE_4BIT[16];

View File

@ -12,6 +12,7 @@
* ----------------------------------------------------------------------------
*/
#include "jswrap_arraybuffer.h"
#include "jswrap_graphics.h"
#include "lcd_arraybuffer.h"
#include "jsvar.h"
#include "jsvariterator.h"
@ -129,7 +130,7 @@ void lcdFillRect_ArrayBuffer(struct JsGraphics *gfx, int x1, int y1, int x2, in
lcdSetPixels_ArrayBuffer(gfx, x1, y, 1+x2-x1, col);
}
#ifndef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
#ifdef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
// Faster implementation for where we have a flat memory area
unsigned int lcdGetPixel_ArrayBuffer_flat(JsGraphics *gfx, int x, int y) {
unsigned int col = 0;
@ -216,8 +217,53 @@ void lcdFillRect_ArrayBuffer_flat(struct JsGraphics *gfx, int x1, int y1, int x
for (y=y1;y<=y2;y++)
lcdSetPixels_ArrayBuffer_flat(gfx, x1, y, 1+x2-x1, col);
}
#ifdef GRAPHICS_FAST_PATHS
void lcdSetPixel_ArrayBuffer_flat1(JsGraphics *gfx, int x, int y, unsigned int col) {
int p = x + y*gfx->data.width;
if (col) ((uint8_t*)gfx->backendData)[p>>3] |= (uint8_t)(0x80 >> (p&7));
else ((uint8_t*)gfx->backendData)[p>>3] &= (uint8_t)(0xFF7F >> (p&7));
}
void lcdFillRect_ArrayBuffer_flat1(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned int col) {
for (int y=y1;y<=y2;y++) {
int p = x1 + y*gfx->data.width;
for (int x=x1;x<=x2;x++) {
if (col) ((uint8_t*)gfx->backendData)[p>>3] |= (uint8_t)(0x80 >> (p&7));
else ((uint8_t*)gfx->backendData)[p>>3] &= (uint8_t)(0xFF7F >> (p&7));
p++;
}
}
}
void lcdSetPixel_ArrayBuffer_flat8(JsGraphics *gfx, int x, int y, unsigned int col) {
((uint8_t*)gfx->backendData)[x + y*gfx->data.width] = col;
}
unsigned int lcdGetPixel_ArrayBuffer_flat8(struct JsGraphics *gfx, int x, int y) {
return ((uint8_t*)gfx->backendData)[x + y*gfx->data.width];
}
void lcdFillRect_ArrayBuffer_flat8(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned int col) {
for (int y=y1;y<=y2;y++) {
uint8_t *p = &((uint8_t*)gfx->backendData)[x1 + y*gfx->data.width];
for (int x=x1;x<=x2;x++)
*(p++) = col;
}
}
void lcdScroll_ArrayBuffer_flat8(JsGraphics *gfx, int xdir, int ydir) {
int pixels = -(xdir + ydir*gfx->data.width);
int l = gfx->data.width*gfx->data.height;
if (pixels>0) memcpy(&((uint8_t*)gfx->backendData)[0],&((uint8_t*)gfx->backendData)[pixels],l-pixels);
else if (pixels<0) memcpy(&((uint8_t*)gfx->backendData)[-pixels],&((uint8_t*)gfx->backendData)[0],l+pixels);
}
#endif
#endif // GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
void lcdInit_ArrayBuffer(JsGraphics *gfx) {
// create buffer
JsVar *buf = jswrap_arraybuffer_constructor(graphicsGetMemoryRequired(gfx));
@ -226,18 +272,37 @@ void lcdInit_ArrayBuffer(JsGraphics *gfx) {
void lcdSetCallbacks_ArrayBuffer(JsGraphics *gfx) {
JsVar *buf = jsvObjectGetChild(gfx->graphicsVar, "buffer", 0);
#ifndef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
#ifdef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
size_t len = 0;
char *dataPtr = jsvGetDataPointer(buf, &len);
#endif
jsvUnLock(buf);
#ifndef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
if (dataPtr && len>=graphicsGetMemoryRequired(gfx)) {
// nice fast mode
#ifdef GRAPHICS_ARRAYBUFFER_OPTIMISATIONS
if (dataPtr && len>=graphicsGetMemoryRequired(gfx) && !(gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG)) {
gfx->backendData = dataPtr;
gfx->setPixel = lcdSetPixel_ArrayBuffer_flat;
gfx->getPixel = lcdGetPixel_ArrayBuffer_flat;
gfx->fillRect = lcdFillRect_ArrayBuffer_flat;
#ifdef GRAPHICS_FAST_PATHS
if (gfx->data.bpp==1 &&
(gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_MSB) &&
!(gfx->data.flags & (JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG|JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE|JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX))
) { // super fast path for 1 bit
gfx->setPixel = lcdSetPixel_ArrayBuffer_flat1;
gfx->getPixel = lcdSetPixel_ArrayBuffer_flat;
gfx->fillRect = lcdFillRect_ArrayBuffer_flat1;
} else if (gfx->data.bpp==8 &&
!(gfx->data.flags & (JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG|JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE|JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX))
) { // super fast path for 8 bits
gfx->setPixel = lcdSetPixel_ArrayBuffer_flat8;
gfx->getPixel = lcdGetPixel_ArrayBuffer_flat8;
gfx->fillRect = lcdFillRect_ArrayBuffer_flat8;
gfx->scroll = lcdScroll_ArrayBuffer_flat8;
} else
#endif
{
// nice fast mode
gfx->setPixel = lcdSetPixel_ArrayBuffer_flat;
gfx->getPixel = lcdGetPixel_ArrayBuffer_flat;
gfx->fillRect = lcdFillRect_ArrayBuffer_flat;
}
#else
if (false) {
#endif

View File

@ -15,3 +15,9 @@
void lcdInit_ArrayBuffer(JsGraphics *gfx);
void lcdSetCallbacks_ArrayBuffer(JsGraphics *gfx);
// these use gfx->backendData as a pointer to data. They're exported so lcd_st7789_8bit can use them for fast offscreen rendering
void lcdSetPixel_ArrayBuffer_flat8(JsGraphics *gfx, int x, int y, unsigned int col);
unsigned int lcdGetPixel_ArrayBuffer_flat8(struct JsGraphics *gfx, int x, int y);
void lcdFillRect_ArrayBuffer_flat8(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned int col);
void lcdScroll_ArrayBuffer_flat8(JsGraphics *gfx, int xdir, int ydir);

View File

@ -16,6 +16,7 @@
#include "jsutils.h"
#include "lcd_st7789_8bit.h"
#include "jswrap_graphics.h"
#include "lcd_arraybuffer.h" // for lcd*_ArrayBuffer_flat8
#ifndef EMSCRIPTEN
#include "jshardware.h"
#include "nrf_gpio.h"
@ -203,14 +204,12 @@ LCDST7789Mode lcdST7789_getMode() {
}
void lcdST7789_flip(JsGraphics *gfx) {
unsigned char buf[2];
switch (lcdMode) {
case LCDST7789_MODE_UNBUFFERED:
// unbuffered - flip has no effect
break;
case LCDST7789_MODE_DOUBLEBUFFERED: {
// buffered - flip using LCD itself
unsigned short offs;
if (lcdScrollY==0) {
lcdScrollY = 160;
} else {
@ -467,34 +466,6 @@ void lcdST7789_scroll(JsGraphics *gfx, int xdir, int ydir) {
lcdST7789_scrollCmd();
}
// ====================================================================================
void lcdST7789buf_setPixel(JsGraphics *gfx, int x, int y, unsigned int col) {
if (!gfx->backendData) return;
((uint8_t*)gfx->backendData)[x + y*gfx->data.width] = col;
}
unsigned int lcdST7789buf_getPixel(struct JsGraphics *gfx, int x, int y) {
if (!gfx->backendData) return 0;
return ((uint8_t*)gfx->backendData)[x + y*gfx->data.width];
}
void lcdST7789buf_fillRect(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned int col) {
if (!gfx->backendData) return;
for (int y=y1;y<=y2;y++) {
uint8_t *p = &((uint8_t*)gfx->backendData)[x1 + y*gfx->data.width];
for (int x=x1;x<=x2;x++)
*(p++) = col;
}
}
void lcdST7789buf_scroll(JsGraphics *gfx, int xdir, int ydir) {
if (!gfx->backendData) return;
int pixels = -(xdir + ydir*gfx->data.width);
int l = gfx->data.width*gfx->data.height;
if (pixels>0) memcpy(&((uint8_t*)gfx->backendData)[0],&((uint8_t*)gfx->backendData)[pixels],l-pixels);
else if (pixels<0) memcpy(&((uint8_t*)gfx->backendData)[-pixels],&((uint8_t*)gfx->backendData)[0],l+pixels);
}
// ====================================================================================
void lcdST7789_init(JsGraphics *gfx) {
@ -531,10 +502,10 @@ void lcdST7789_setCallbacks(JsGraphics *gfx) {
jsvUnLock(buf);
if (dataPtr && len>=expectedLen) {
gfx->backendData = dataPtr;
gfx->setPixel = lcdST7789buf_setPixel;
gfx->getPixel = lcdST7789buf_getPixel;
gfx->fillRect = lcdST7789buf_fillRect;
gfx->scroll = lcdST7789buf_scroll;
gfx->setPixel = lcdSetPixel_ArrayBuffer_flat8;
gfx->getPixel = lcdGetPixel_ArrayBuffer_flat8;
gfx->fillRect = lcdFillRect_ArrayBuffer_flat8;
gfx->scroll = lcdScroll_ArrayBuffer_flat8;
}
} else {
gfx->setPixel = lcdST7789_setPixel;

View File

@ -1,5 +1,11 @@
var LCD = Graphics.createArrayBuffer(8,8,1);
LCD.drawLine(0,0,8,8);
print(LCD.buffer);
result = LCD.buffer == "1,2,4,8,16,32,64,128";
var g;
g = Graphics.createArrayBuffer(8,8,1);
g.drawLine(0,0,8,8);
print(g.buffer);
result = g.buffer == "1,2,4,8,16,32,64,128";
g = Graphics.createArrayBuffer(8,8,1,{msb:true});
g.drawLine(0,0,8,8);
print(g.buffer);
result &= g.buffer == "128,64,32,16,8,4,2,1";

View File

@ -0,0 +1,77 @@
var g = Graphics.createArrayBuffer(32,32,8);
g.dump = _=>{
var s = "";
var b = new Uint8Array(g.buffer);
var n = 0;
for (var y=0;y<g.getHeight();y++) {
s+="\n";
for (var x=0;x<g.getWidth();x++)
s+=".#0"[b[n++]];
}
return s;
}
g.print = _=>{
print("`"+g.dump()+"`");
}
var ok = true;
function SHOULD_BE(a) {
var b = g.dump();
if (a!=b) {
console.log("GOT :"+b+"\nSHOULD BE:"+a+"\n================");
ok = false;
}
}
var img = {
width : 8, height : 8, bpp : 8,
transparent : 0,
buffer : new Uint8Array([
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,2,2,2,0,0,0,1,
1,2,2,0,0,0,0,1,
1,2,0,0,0,0,0,1,
1,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
]).buffer
};
g.clear();
g.drawImage(img,2,2,{scale:2});
//g.print();
SHOULD_BE(`
................................
................................
..################..............
..################..............
..################..............
..################..............
..##000000......##..............
..##000000......##..............
..##0000........##..............
..##0000........##..............
..##00..........##..............
..##00..........##..............
..##............##..............
..##............##..............
..##............##..............
..##............##..............
..################..............
..################..............
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................`);
result = ok;