olikraus / u8glib

Arduino Monochrom Graphics Library for LCDs and OLEDs
https://github.com/olikraus/u8glib/wiki
Other
1.24k stars 313 forks source link

Q: How to get the current page bounds #447

Closed thinkyhead closed 7 years ago

thinkyhead commented 7 years ago

Hi @olikraus — Sorry if this has been asked and answered elsewhere. I wasn't able to find it.

I simply want to know how to ask u8glib for the current page's bounds during the draw loop so that we can have the screen update code in Marlin draw only the items that are within the page being rendered. This way we can save cycles in the draw loop, which is a major bottleneck, especially on deltabots with graphical displays.

Or, if we can't ask for the current page bounds directly, then at least be able to pre-determine the page size, number of loops, whether it's being drawn bottom-to-top, etc.

Thanks for any guidance you can provide!

olikraus commented 7 years ago

I think it should be this:

((u8g_pb_t *)((u8g.getU8g())->dev->dev_mem))->p.page_height

At least if you have not applied any rotation. Will this work?

thinkyhead commented 7 years ago

That seems about right. I'll have to pass it on to one of my enthusiastic minions. At the moment I don't have access to a graphical display.

The key thing will be to figure out precisely the starting and ending Y positions of the current page, allowing for total optimization. According to our display guru, the RepRap Discount Smart Graphic Controller (by far the most common) updates in 2 segments, while most (all?) others update in 8 segments.

Apart from picking the correct segment to display, there's some other smart caching we can do. And I've also proposed unrolling the loop in the page drawing function.

olikraus commented 7 years ago

The current cols are also there, if I remember correctly: p.y0 and p.y1 y0 is the first and y1 the last row, e.g. 0 and 7 for the first page.

thinkyhead commented 7 years ago

That's handy. (With 0-7 I assume you're referring to a screen that draws in 8 segments.)

Turns out unrolling the byte-sending loop (https://github.com/MarlinFirmware/Marlin/pull/5279/files#diff-fd4e126c88d2a0a2b188632d47501a43R115) is giving bad results. Apparently the overhead of the loop is providing a needed delay for SPI… maybe.

olikraus commented 7 years ago

Yes, it would be 0 and 15 if the page height is 15

olikraus commented 7 years ago

Hmm can not say much about the unrolling

thinkyhead commented 7 years ago

If the screen is rotated 180°, would y start with (for example) 56-63, then 48-55, etc. ?

olikraus commented 7 years ago

No, even more worse, you will not get the page height any more, because the dev member will point to a different data structure.

thinkyhead commented 7 years ago

the dev member will point to a different data structure

Interesting. So in the case of the rotated screen, is there simply an alternative way to access the .page, .page_y0, and .page_y1 data members?


Hmm. I just tried compiling with the VIKI2 display option (plus RAMPS as the board) and this combination has LCD_SCREEN_ROT_180 set by default. It compiled successfully.

I'm using the following to initialize the page reference and to test the current range:

// The current graphical page being rendered
u8g_page_t &page = ((u8g_pb_t *)((u8g.getU8g())->dev->dev_mem))->p;

// For selective rendering within a Y range
#define PAGE_CONTAINS(ya, yb) \
  (page.page_y0 <= (yb) && ((ya) ? page.page_y1 >= (ya) : true))

For example:

if (PAGE_CONTAINS(42 - (TALL_FONT_CORRECTION), 51 - (TALL_FONT_CORRECTION))) {
  // Upper box
  u8g.drawBox(42, 42 - (TALL_FONT_CORRECTION), 8, 7);     // 42-48 (or 41-47)
  // Right edge
  u8g.drawBox(50, 44 - (TALL_FONT_CORRECTION), 2, 5);     // 44-48 (or 43-47)
  // Bottom hollow box
  u8g.drawFrame(42, 49 - (TALL_FONT_CORRECTION), 10, 4);  // 49-52 (or 48-51)
  // Corner pixel
  u8g.drawPixel(50, 43 - (TALL_FONT_CORRECTION));         // 43 (or 42)
}
thinkyhead commented 7 years ago

One other question! Does the value of ((u8g_pb_t *)((u8g.getU8g())->dev->dev_mem))->p remain constant once initialized, or can it change during program run?

olikraus commented 7 years ago

Yes, it remains constant. Get the pointer to p just before you apply the rotation, then access the members through this new pointer later.

olikraus commented 7 years ago

The problem is, that dev gets changed by the rotation.

thinkyhead commented 7 years ago

I think I have it correct (*purely by luck). My implementation defines the page reference as a global, after the u8g global object is instantiated (of course), but before our lcd_implementation_init function is called.

I've posted the new code for testing at MarlinFirmware/Marlin#5288 and expect to get feedback on this optimization soon. I think this will be a huge improvement. It's amazing how easy this turned out to be — and to think I've been avoiding doing this for months because I thought it would be too difficult.

Thanks for the assistance!

olikraus commented 7 years ago

Btw... I think there is a function to check whether something is visible or not.

olikraus commented 7 years ago

Hmm the function is u8g_IsBBXIntersection from u8g_clip.c

olikraus commented 7 years ago

Ok, maybe because i have not worked for a long time on u8glib, after looking at u8g_clip.c i think there is a more simpler way to access the height and the current page boundaries.

u8g.GetU8g()->current_page

contains also some information on the current page. Especially this is independently from the applied rotation. "current_page" has the following members: struct _u8g_box_t { u8g_uint_t x0, y0, x1, y1; };

The height of the page is u8g.GetU8g()->current_page.y1 - u8g.GetU8g()->current_page.y0 + 1

u8g.GetU8g()->current_page.y0 and u8g.GetU8g()->current_page.y1 are actually just copied from page.page_y0 and page.page_y1, but are also corrected if the rotation is applied.

Please excause the confusion. I should have pointed out this solution more earlier. Actually this part of u8g was completly removed when I wrote the successor of u8glib (which is u8g2). The page drawing procedures are now much simpler (and hopefully also faster) in u8g2.

thinkyhead commented 7 years ago

I should have pointed out this solution more earlier.

No problem! After initial testing of my PR I'll try the in-built options and compare performance. I might end up needing to use your supplied functions if the screen rotation breaks my implementation.

The page drawing procedures are now much simpler (and hopefully also faster) in u8g2.

It was suggested to move to u8g2 for speed gains, but so far our testing has shown u8g2 to be much slower. (See https://www.youtube.com/watch?v=fyPpYOoHn0I) But perhaps we are missing some optimizations…?

olikraus commented 7 years ago

Oh, that video was done by the Marlin community. Nice. I commented this already on youtube. I wrote U8g2 to get rid of the hardware specific optimization. U8g2 now should work with all current and hopfully all upcoming Arduino boards. This is not true for the old u8glib library which had a lot of AVR (and SAM) specific optimizations and finally it was impossible to keep u8glib compatible to all these Arduino boards around. Mainly, i wanted to have a clear base implementation on which further optimization can be build. Indeed the same optimization can be reintroduced to u8g2. There are some topics which might be of interest for Marlin:

Of course there are still some work required:

thinkyhead commented 7 years ago

Initial testing of #5288 shows that the bounds-checking and string caching strategies are saving about 8000 cycles per update (i.e., 2ms) which is pretty significant! I'm sure optimization is possible. Perhaps even within the old U8glib. I'll be exploring all options as time allows.

U8g2 sounds like it's coming along well. Here's a list of displays we need to support:

DOGLCD (ST7565R)
REPRAPWORLD_GRAPHICAL_LCD (ST7920)
VIKI2 / miniVIKI (C12864)
ELB_FULL_GRAPHIC_CONTROLLER (ST7565 / LM6059_AF)
REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER (ST7920)
MAKRPANEL (C12864)
MINIPANEL (MINI12864)
CARTESIO_UI (DOGM128)
SAV_3DGLCD (SSD1306 / SH1106)

…or to put it another way…

U8GLIB_ST7920_128X64_4X
U8GLIB_ST7920_128X64_RRD
U8GLIB_DOGM128
U8GLIB_LM6059
U8GLIB_NHD_C12864
U8GLIB_SSD1306_128X64
U8GLIB_SH1106_128X64
U8GLIB_MINI12864

We've been wondering, too, whether for certain boards a hardware-based SPI could improve performance even more. Most boards are based on the Mega2560, so that's our main target for this. With that board especially in mind, we just added interrupt-based endstops, and that's showing some performance benefit. The more hardware-based we can go, the better it will be, especially for all those beleaguered deltabots with graphical displays out there.

olikraus commented 7 years ago

For U8g2 situation is like this:

SW SPI needs to be analyzed. paralle interface can be improved

U8GLIB_ST7920_128X64_4X --> Ported U8GLIB_ST7920_128X64_RRD --> Not sure what this is U8GLIB_DOGM128 --> Ported U8GLIB_LM6059 --> Controller is ported, but the specific display setup needs to be analysed U8GLIB_NHD_C12864 --> Controller is ported, but the specific display setup needs to be analysed U8GLIB_SSD1306_128X64 --> Ported U8GLIB_SH1106_128X64 --> Ported U8GLIB_MINI12864 --> Controller is ported, but the specific display setup needs to be analysed

Created an issue in u8g2: olikraus/u8g2#94

thinkyhead commented 7 years ago

Ah… The U8GLIB_ST7920_128X64_RRD type comes from here: https://github.com/MarlinFirmware/Marlin/blob/RCBugFix/Marlin/ultralcd_st7920_u8glib_rrd.h

thinkyhead commented 7 years ago

Thanks for all the help and attention. I think this question is resolved. We'll talk more as we get on with more optimization.