bitluni / ESP32-S3-VGA

115 stars 10 forks source link

How to update the screen? #2

Open UKTailwind opened 1 year ago

UKTailwind commented 1 year ago

Hi

I've breadboarded some H/W and got the code working nicely in 800x600x16 on a S3N32R8 including drawing circles. The bit I'm missing is how to update the screen in the main loop. If I call the drawing routines in the main loop I lose sync and even when done slowly the screen isn't stable. Are updates in the main loop possible or are we hitting a bandwidth limitation on the PSRAM? Alternatively is there something else I'm missing?

davidos81 commented 11 months ago

How did you even manage to update the screen after the initial setup routine with the random colors etc...? each time a do a call to vgaDot(....); after vgaShow(); vgaStart(); - nothing happens...

Regards the PSRAM bandwidth issue , I think you are correct - you are close to hitting the limits , I am surprised this even works in the first place as PSRAM is a great deal slower than SRAM !

bitluni commented 8 months ago

An example showing how to draw in the loop can be found here: https://github.com/bitluni/ESP32-S3-VGA/blob/main/examples/MQTTTextDisplay/MQTTTextDisplay.ino

The sync issues are worked on. There is another lib that seems more stable with 8bits: https://github.com/bitfixer/esp32s3vga

also check out https://github.com/bitluni/ESP32-S3-VGA/issues/8

davidos81 commented 8 months ago

this is brilliant bitluni , but to be honest , I preferred your old lib using i2s hack . that had the advantage of sending the pixels in smaller buffers (at a performance cost) VS the one huge buffer the new one has. Maybe one day you could get it working that way , the bitfixer one has that , but he uses the LCD peripheral and currently arduino is not supported as it uses a modern API not available yet to arduino devs.

amazing effort by you , keep up the good work!

cheers

sobieh commented 8 months ago

I2S hack is no longer working in ESP32S3. Now you got the advantage of having at least 2MB of PSRAM which is way superior ;) You can still update framebuffer in small buffers (there's no need to repaint the whole thing each frame). I get the impression that you guys are trying to "reinvent the wheel" and solve problems already solved in IDF by clinging to the obsolete arduino libs.

davidos81 commented 8 months ago

hi sobeih , I dont quite understand your last sentence , who is re-inventing the wheel , who is you guys ? ( me ?? ) and who is solving problems in idf ?

for a start , you have a choice , I shared my opinion , as someone who has written his own lcd driver , I know the limitations of both techniques , There is a distinct advantage in both.

for the buffers point , i meant bounce buffers , its a call back that the hardware issues when updating a portion of the screen in real-time , this lib does not do that (yet)

sobieh commented 8 months ago

hi sobeih , I dont quite understand your last sentence , who is re-inventing the wheel , who is you guys ? ( me ?? ) and who is solving problems in idf ?

Oh, sorry, I didn't mean to offend anyone. I'm not telling that any method is good or wrong just that ESP32S2 has flexible I2S and ESP32S3 doesn't but gives you other peripherals and memory to fill that gap. You cannot implement S2 method on S3 and vice versa. Additionally S3 is performing much better at VGA signal generation (that's why imho it's superior to S2).

as someone who has written his own lcd driver , I know the limitations of both techniques

Everyone ends up writing their own VGA driver at some point :)

for the buffers point , i meant bounce buffers , its a call back that the hardware issues when updating a portion of the screen in real-time , this lib does not do that (yet)

Yeah i know what bounce buffer is. It's supported by default in IDF LCD driver when you configure to have your framebuffer in psram. That's what i meant by "reinventing the wheel".

bitluni commented 8 months ago

The issue with the LCD driver at least version 4.x.x of the idf was that the transfer was restarted on interrupt. that interrupt slightly delays the next frame and causes screens to glitch and tear. I showed that in the video. I didn't test the 5.x.x version yet

On Sun, Oct 15, 2023 at 8:51 PM sobieh @.***> wrote:

hi sobeih , I dont quite understand your last sentence , who is re-inventing the wheel , who is you guys ? ( me ?? ) and who is solving problems in idf ? Oh, sorry, I didn't mean to offend anyone. I'm not telling that any method is good or wrong just that ESP32S2 has flexible I2S and ESP32S3 doesn't but gives you other peripherals and memory to fill that gap. You cannot implement S2 method on S3 and vice versa. Additionally S3 is performing much better at VGA signal generation (that's why imho it's superior to S2).

as someone who has written his own lcd driver , I know the limitations of both techniques Everyone ends up writing their own VGA driver at some point :)

for the buffers point , i meant bounce buffers , its a call back that the hardware issues when updating a portion of the screen in real-time , this lib does not do that (yet) Yeah i know what bounce buffer is. It's supported by default in IDF LCD driver when you configure to have your framebuffer in psram. That's what i meant by "reinventing the wheel".

— Reply to this email directly, view it on GitHub https://github.com/bitluni/ESP32-S3-VGA/issues/2#issuecomment-1763474001, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADCGI2ZVIXWPOHSY4PSUMKLX7QWDLANCNFSM6AAAAAA2CDQUEY . You are receiving this because you commented.Message ID: @.***>

davidos81 commented 8 months ago

the esp32s3 lcd driver to vga is prettry simple , there are arduino libs available that can do it pretty well, however there is some cpu overhead involved ,

The code in this lib is a pretty clever hack , and I dont fully understand how bitluni got it to work , but it works pretty well! I have not used this code recently , so can I ask a naive question... I know there will be screen tear but can i update the framebuffer address rather than updating the pixels ? so I have say 8 framebuffers pre-made and just swap the start address ( using a timer ) rather than changing the pixels ?

cheers to everyone here...

dkm1978 commented 8 months ago

you need to count addresses for dma descriptors for each line then You need to connect the new descriptors under Dma.

davidos81 commented 8 months ago

brilliant thanks , I will give it a shot!

bitluni commented 8 months ago

Swapping memory addresses in the descriptor is one method. you can also use a descriptor table as long as the animation. but that will only loop at the actual refresh rate

On Mon, Oct 16, 2023 at 4:19 PM davidos81 @.***> wrote:

brilliant thanks , I will give it a shot!

— Reply to this email directly, view it on GitHub https://github.com/bitluni/ESP32-S3-VGA/issues/2#issuecomment-1764585216, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADCGI23RMKEE5ELG5XLBS33X7U65ZAVCNFSM6AAAAAA2CDQUE2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONRUGU4DKMRRGY . You are receiving this because you commented.Message ID: @.***>

davidos81 commented 8 months ago

So for the 1st method as explained by dkm1978 , all i need to do is modify the SHOW method here so it accepts a pointer to my new backbuffer , and I can use that in my LOOP method to constantly update. The caveat here is I have no way of knowing if I am in VSYNC , so understandably there will be a screen tear , However , Its something I can live with as long as I don't get other issues !

is that correct ?

bool VGA::show()
{
    //TODO check start
    dmaBuffer->flush(backBuffer);
    if(bufferCount <= 1) 
        return true;
    dmaBuffer->attachBuffer(backBuffer);
    backBuffer = (backBuffer + 1) % bufferCount;
    //TODO check end
    return true;
}
sobieh commented 8 months ago

You can use gdma_register_tx_event_callbacks to register DMA transfer finished callback and then rebuild the whole dma chain to point on your other framebuffer using attachBuffer(n) when callback is executed. This will not guarantee you are in vsync either but you will switch your buffer when the previous one is fully read by LCD peripheral so it's "kinda vsync". Bitluni's driver is not doing that so you would have to implement this yourself. Your code should work when called at right moment.

davidos81 commented 8 months ago

@sobieh thanks for that!

great idea , will look into it - i do have another idea though (using timing at the right moment) if that fails then try your callback method

cheers!

davidos81 commented 8 months ago

@dkm1978 just saw you youtube videos - amazing !! reminds of the old amiga demo days !!!

since you have some experience with this lib. would you say its pretty much stable now under load n low resolutions - 640x480x16 ? did you see any anytime picture blanked or horizontal or vertical got unsynced ?

Thing is I am working on a project and tied between this and bitfixers lib , I much prefer this as it works well with arduino

cheers

dkm1978 commented 8 months ago

I designed and ordered a new board on which I will use a 16-bit converter, I will write how I managed to test it. I didn't notice any errors or glitches in 8-bit mode.

davidos81 commented 8 months ago

cool

UKTailwind commented 8 months ago

Sorry to be an old-fashioned "C" programmer. But how do I write to the framebuffer direct without using vga.dot? I presume I need to change something in the class definition to expose it to the outside world?

dkm1978 commented 8 months ago

VGA.dot writes directly to the framebuffer. a frame buffer is not a uniform area but a collection of line buffers

sobieh commented 8 months ago

VGA.dot writes directly to the framebuffer. a frame buffer is not a uniform area but a collection of line buffers

Actually, could you @bitluni explain why is that ? Is there any particular reason to slice the framebuffer to per line separate allocations instead of one linear buffer ?

davidos81 commented 8 months ago

thats because the system uses dma , it is sent per line due to horizonal sync , and also there is a limit how much data you can send in a single dma transaction.

for those cleverer than me - please correct if I am wrong...

sobieh commented 8 months ago

You can use DMA on any dma capable memory, linear or not. Allocating line by line is actually not optimal as you cannot pass more data than a single line in one dma chain element which forces you to allocate much more chain links than required which in result is slow. I understand the concept of DMA and how things work but im just wondering if there is some secret logic hidden behind this like some trick to utilize lcd hardware better. That's really unusual way of allocating framebuffer so im just curious :)

davidos81 commented 8 months ago

A linear framebuffer is the most optimal , but the ( pointers ) line by line approach is not so unusual , I have seen it many times before. If performance is not critical then it works well , you can just copy memory line by line from your offscreen framebuffer to each line through a naive loop - it wont be 60fps fast , but it wont be terribly slow either.

dkm1978 commented 8 months ago

@davidos81 I tested the system in the 16-bit version and... unfortunately, the screen shifts occur :(