forked from Makuna/NeoPixelBus
Double buffer and shader fixes (#273)
* DoubleBufferFixes * Add NeoBitmapFile Render
This commit is contained in:
@@ -57,8 +57,9 @@ License along with NeoPixel. If not, see
|
||||
#include "internal/NeoBufferMethods.h"
|
||||
#include "internal/NeoBuffer.h"
|
||||
#include "internal/NeoSpriteSheet.h"
|
||||
#include "internal/NeoBitmapFile.h"
|
||||
#include "internal/NeoDib.h"
|
||||
#include "internal/NeoBitmapFile.h"
|
||||
|
||||
#include "internal/NeoEase.h"
|
||||
#include "internal/NeoGamma.h"
|
||||
|
||||
@@ -145,14 +146,14 @@ public:
|
||||
Dirty();
|
||||
}
|
||||
|
||||
void Show()
|
||||
void Show(bool maintainBufferConsistency = true)
|
||||
{
|
||||
if (!IsDirty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_method.Update();
|
||||
_method.Update(maintainBufferConsistency);
|
||||
|
||||
ResetDirty();
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ public:
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// start frame
|
||||
for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++)
|
||||
|
@@ -60,7 +60,7 @@ public:
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// start frame
|
||||
for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++)
|
||||
|
@@ -63,7 +63,7 @@ public:
|
||||
SPI.begin();
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
SPI.beginTransaction(SPISettings(20000000L, MSBFIRST, SPI_MODE0));
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
|
@@ -66,7 +66,7 @@ public:
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
|
@@ -148,7 +148,7 @@ public:
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
|
@@ -189,7 +189,9 @@ public:
|
||||
return color;
|
||||
};
|
||||
|
||||
void Blt(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
|
||||
template <typename T_SHADER> void Render(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
T_SHADER& shader,
|
||||
uint16_t indexPixel,
|
||||
int16_t xSrc,
|
||||
int16_t ySrc,
|
||||
@@ -208,6 +210,7 @@ public:
|
||||
{
|
||||
if (readPixel(&color))
|
||||
{
|
||||
color = shader.Apply(indexPixel, color);
|
||||
xSrc++;
|
||||
}
|
||||
}
|
||||
@@ -218,6 +221,18 @@ public:
|
||||
}
|
||||
|
||||
void Blt(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
uint16_t indexPixel,
|
||||
int16_t xSrc,
|
||||
int16_t ySrc,
|
||||
int16_t wSrc)
|
||||
{
|
||||
NeoShaderNop<typename T_COLOR_FEATURE::ColorObject> shaderNop;
|
||||
|
||||
Render<NeoShaderNop<typename T_COLOR_FEATURE::ColorObject>>(destBuffer, shaderNop, indexPixel, xSrc, ySrc, wSrc);
|
||||
};
|
||||
|
||||
template <typename T_SHADER> void Render(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
T_SHADER& shader,
|
||||
int16_t xDest,
|
||||
int16_t yDest,
|
||||
int16_t xSrc,
|
||||
@@ -238,16 +253,17 @@ public:
|
||||
{
|
||||
for (int16_t x = 0; x < wSrc; x++)
|
||||
{
|
||||
uint16_t indexDest = layoutMap(xDest + x, yDest + y);
|
||||
|
||||
if ((uint16_t)xFile < _width)
|
||||
{
|
||||
if (readPixel(&color))
|
||||
{
|
||||
color = shader.Apply(indexDest, color);
|
||||
xFile++;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t indexDest = layoutMap(xDest + x, yDest + y);
|
||||
|
||||
if (indexDest < destPixelCount)
|
||||
{
|
||||
T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, indexDest, color);
|
||||
@@ -257,6 +273,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void Blt(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
int16_t xDest,
|
||||
int16_t yDest,
|
||||
int16_t xSrc,
|
||||
int16_t ySrc,
|
||||
int16_t wSrc,
|
||||
int16_t hSrc,
|
||||
LayoutMapCallback layoutMap)
|
||||
{
|
||||
NeoShaderNop<typename T_COLOR_FEATURE::ColorObject> shaderNop;
|
||||
|
||||
Render<NeoShaderNop<typename T_COLOR_FEATURE::ColorObject>>(destBuffer,
|
||||
shaderNop,
|
||||
xDest,
|
||||
yDest,
|
||||
xSrc,
|
||||
ySrc,
|
||||
wSrc,
|
||||
hSrc,
|
||||
layoutMap);
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
T_FILE_METHOD _file;
|
||||
|
@@ -25,6 +25,32 @@ License along with NeoPixel. If not, see
|
||||
-------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
template<typename T_COLOR_OBJECT> class NeoShaderNop
|
||||
{
|
||||
public:
|
||||
NeoShaderNop()
|
||||
{
|
||||
}
|
||||
|
||||
bool IsDirty() const
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void Dirty()
|
||||
{
|
||||
};
|
||||
|
||||
void ResetDirty()
|
||||
{
|
||||
};
|
||||
|
||||
T_COLOR_OBJECT Apply(uint16_t, T_COLOR_OBJECT color)
|
||||
{
|
||||
return color;
|
||||
};
|
||||
};
|
||||
|
||||
class NeoShaderBase
|
||||
{
|
||||
public:
|
||||
@@ -118,7 +144,8 @@ public:
|
||||
Dirty();
|
||||
};
|
||||
|
||||
template <typename T_COLOR_FEATURE, typename T_SHADER> void Render(NeoBufferContext<T_COLOR_FEATURE> destBuffer, T_SHADER& shader)
|
||||
template <typename T_COLOR_FEATURE, typename T_SHADER> void Render(NeoBufferContext<T_COLOR_FEATURE> destBuffer,
|
||||
T_SHADER& shader)
|
||||
{
|
||||
if (IsDirty() || shader.IsDirty())
|
||||
{
|
||||
|
@@ -128,7 +128,7 @@ public:
|
||||
i2sSetPins(T_BUS::I2sBusNumber, _pin, -1, -1, -1);
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
while (!IsReadyToUpdate())
|
||||
|
@@ -51,7 +51,7 @@ public:
|
||||
public:
|
||||
// ClkDiv of 2 provides for good resolution and plenty of reset resolution; but
|
||||
// a ClkDiv of 1 will provide enough space for the longest reset and does show
|
||||
// little better pulse accuracy
|
||||
// little better pulse accuracy
|
||||
const static uint8_t RmtClockDivider = 2;
|
||||
|
||||
inline constexpr static uint32_t FromNs(uint32_t ns)
|
||||
@@ -207,20 +207,26 @@ public:
|
||||
rmt_driver_install(T_CHANNEL::RmtChannelNumber, 0, 0);
|
||||
rmt_translator_init(T_CHANNEL::RmtChannelNumber, _translate);
|
||||
}
|
||||
|
||||
|
||||
void Update(bool maintainBufferConsistency)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
// this will time out at 10 seconds, an arbitrarily long period of time
|
||||
// and do nothing if this happens
|
||||
if (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS))
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that colors present before this will
|
||||
// be the same as it just was using GetPixelColor
|
||||
{
|
||||
// now start the RMT transmit with the editing buffer before we swap
|
||||
rmt_write_sample(T_CHANNEL::RmtChannelNumber, _pixelsEditing, _pixelsSize, false);
|
||||
|
||||
// now start the RMT transmit
|
||||
|
||||
if (maintainBufferConsistency)
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that "colors present before will
|
||||
// be the same after", otherwise GetPixelColor will be inconsistent
|
||||
memcpy(_pixelsSending, _pixelsEditing, _pixelsSize);
|
||||
}
|
||||
|
||||
// swap so the user can modify without affecting the async operation
|
||||
std::swap(_pixelsSending, _pixelsEditing);
|
||||
}
|
||||
}
|
||||
|
@@ -184,7 +184,7 @@ protected:
|
||||
T_UARTFEATURE::Init(uartBaud);
|
||||
}
|
||||
|
||||
void UpdateUart()
|
||||
void UpdateUart(bool)
|
||||
{
|
||||
// Since the UART can finish sending queued bytes in the FIFO in
|
||||
// the background, instead of waiting for the FIFO to flush
|
||||
@@ -221,12 +221,12 @@ protected:
|
||||
NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) :
|
||||
NeoEsp8266UartBase(pixelCount, elementSize)
|
||||
{
|
||||
_asyncPixels = (uint8_t*)malloc(_sizePixels);
|
||||
_pixelsSending = (uint8_t*)malloc(_sizePixels);
|
||||
}
|
||||
|
||||
~NeoEsp8266AsyncUart()
|
||||
{
|
||||
// Remember: the UART interrupt can be sending data from _asyncPixels in the background
|
||||
// Remember: the UART interrupt can be sending data from _pixelsSending in the background
|
||||
while (_context.IsSending())
|
||||
{
|
||||
yield();
|
||||
@@ -234,7 +234,7 @@ protected:
|
||||
// detach context, which will disable intr, may disable ISR
|
||||
_context.Detach(T_UARTFEATURE::Index);
|
||||
|
||||
free(_asyncPixels);
|
||||
free(_pixelsSending);
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud)
|
||||
@@ -245,7 +245,7 @@ protected:
|
||||
_context.Attach(T_UARTFEATURE::Index);
|
||||
}
|
||||
|
||||
void UpdateUart()
|
||||
void UpdateUart(bool maintainBufferConsistency)
|
||||
{
|
||||
// Instruct ESP8266 hardware uart to send the pixels asynchronously
|
||||
_context.StartSending(T_UARTFEATURE::Index,
|
||||
@@ -255,15 +255,22 @@ protected:
|
||||
// Annotate when we started to send bytes, so we can calculate when we are ready to send again
|
||||
_startTime = micros();
|
||||
|
||||
// Copy the pixels to the idle buffer and swap them
|
||||
memcpy(_asyncPixels, _pixels, _sizePixels);
|
||||
std::swap(_asyncPixels, _pixels);
|
||||
if (maintainBufferConsistency)
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that "colors present before will
|
||||
// be the same after", otherwise GetPixelColor will be inconsistent
|
||||
memcpy(_pixelsSending, _pixels, _sizePixels);
|
||||
}
|
||||
|
||||
// swap so the user can modify without affecting the async operation
|
||||
std::swap(_pixelsSending, _pixels);
|
||||
}
|
||||
|
||||
private:
|
||||
T_UARTCONTEXT _context;
|
||||
|
||||
uint8_t* _asyncPixels; // Holds a copy of LED color values taken when UpdateUart began
|
||||
uint8_t* _pixelsSending; // Holds a copy of LED color values taken when UpdateUart began
|
||||
};
|
||||
|
||||
class NeoEsp8266UartSpeed800KbpsBase
|
||||
@@ -334,7 +341,7 @@ public:
|
||||
this->_startTime = micros() - getPixelTime();
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool maintainBufferConsistency)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
@@ -346,7 +353,7 @@ public:
|
||||
{
|
||||
yield();
|
||||
}
|
||||
this->UpdateUart();
|
||||
this->UpdateUart(maintainBufferConsistency);
|
||||
}
|
||||
|
||||
uint8_t* getPixels() const
|
||||
|
@@ -113,7 +113,7 @@ public:
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
|
Reference in New Issue
Block a user