Double buffer and shader fixes (#273)

* DoubleBufferFixes

* Add NeoBitmapFile Render
This commit is contained in:
Michael Miller
2019-06-12 23:10:42 -07:00
committed by GitHub
parent 0eed256d33
commit 2d3609e15d
12 changed files with 113 additions and 34 deletions

View File

@@ -57,8 +57,9 @@ License along with NeoPixel. If not, see
#include "internal/NeoBufferMethods.h" #include "internal/NeoBufferMethods.h"
#include "internal/NeoBuffer.h" #include "internal/NeoBuffer.h"
#include "internal/NeoSpriteSheet.h" #include "internal/NeoSpriteSheet.h"
#include "internal/NeoBitmapFile.h"
#include "internal/NeoDib.h" #include "internal/NeoDib.h"
#include "internal/NeoBitmapFile.h"
#include "internal/NeoEase.h" #include "internal/NeoEase.h"
#include "internal/NeoGamma.h" #include "internal/NeoGamma.h"
@@ -145,14 +146,14 @@ public:
Dirty(); Dirty();
} }
void Show() void Show(bool maintainBufferConsistency = true)
{ {
if (!IsDirty()) if (!IsDirty())
{ {
return; return;
} }
_method.Update(); _method.Update(maintainBufferConsistency);
ResetDirty(); ResetDirty();
} }

View File

@@ -68,7 +68,7 @@ public:
digitalWrite(_pinData, LOW); digitalWrite(_pinData, LOW);
} }
void Update() void Update(bool)
{ {
// start frame // start frame
for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++)

View File

@@ -60,7 +60,7 @@ public:
digitalWrite(_pinData, LOW); digitalWrite(_pinData, LOW);
} }
void Update() void Update(bool)
{ {
// start frame // start frame
for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++)

View File

@@ -63,7 +63,7 @@ public:
SPI.begin(); SPI.begin();
} }
void Update() void Update(bool)
{ {
SPI.beginTransaction(SPISettings(20000000L, MSBFIRST, SPI_MODE0)); SPI.beginTransaction(SPISettings(20000000L, MSBFIRST, SPI_MODE0));
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)

View File

@@ -66,7 +66,7 @@ public:
_endTime = micros(); _endTime = micros();
} }
void Update() void Update(bool)
{ {
// Data latch = 50+ microsecond pause in the output stream. Rather than // 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 // put a delay at the end of the function, the ending time is noted and

View File

@@ -148,7 +148,7 @@ public:
_endTime = micros(); _endTime = micros();
} }
void Update() void Update(bool)
{ {
// Data latch = 50+ microsecond pause in the output stream. Rather than // 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 // put a delay at the end of the function, the ending time is noted and

View File

@@ -189,7 +189,9 @@ public:
return color; 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, uint16_t indexPixel,
int16_t xSrc, int16_t xSrc,
int16_t ySrc, int16_t ySrc,
@@ -208,6 +210,7 @@ public:
{ {
if (readPixel(&color)) if (readPixel(&color))
{ {
color = shader.Apply(indexPixel, color);
xSrc++; xSrc++;
} }
} }
@@ -216,8 +219,20 @@ public:
} }
} }
} }
void Blt(NeoBufferContext<T_COLOR_FEATURE> destBuffer, 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 xDest,
int16_t yDest, int16_t yDest,
int16_t xSrc, int16_t xSrc,
@@ -238,15 +253,16 @@ public:
{ {
for (int16_t x = 0; x < wSrc; x++) for (int16_t x = 0; x < wSrc; x++)
{ {
uint16_t indexDest = layoutMap(xDest + x, yDest + y);
if ((uint16_t)xFile < _width) if ((uint16_t)xFile < _width)
{ {
if (readPixel(&color)) if (readPixel(&color))
{ {
color = shader.Apply(indexDest, color);
xFile++; xFile++;
} }
} }
uint16_t indexDest = layoutMap(xDest + x, yDest + y);
if (indexDest < destPixelCount) if (indexDest < destPixelCount)
{ {
@@ -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: private:
T_FILE_METHOD _file; T_FILE_METHOD _file;

View File

@@ -25,6 +25,32 @@ License along with NeoPixel. If not, see
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#pragma once #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 class NeoShaderBase
{ {
public: public:
@@ -118,7 +144,8 @@ public:
Dirty(); 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()) if (IsDirty() || shader.IsDirty())
{ {

View File

@@ -128,7 +128,7 @@ public:
i2sSetPins(T_BUS::I2sBusNumber, _pin, -1, -1, -1); i2sSetPins(T_BUS::I2sBusNumber, _pin, -1, -1, -1);
} }
void Update() void Update(bool)
{ {
// wait for not actively sending data // wait for not actively sending data
while (!IsReadyToUpdate()) while (!IsReadyToUpdate())

View File

@@ -51,7 +51,7 @@ public:
public: public:
// ClkDiv of 2 provides for good resolution and plenty of reset resolution; but // 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 // 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; const static uint8_t RmtClockDivider = 2;
inline constexpr static uint32_t FromNs(uint32_t ns) inline constexpr static uint32_t FromNs(uint32_t ns)
@@ -207,20 +207,26 @@ public:
rmt_driver_install(T_CHANNEL::RmtChannelNumber, 0, 0); rmt_driver_install(T_CHANNEL::RmtChannelNumber, 0, 0);
rmt_translator_init(T_CHANNEL::RmtChannelNumber, _translate); rmt_translator_init(T_CHANNEL::RmtChannelNumber, _translate);
} }
void Update(bool maintainBufferConsistency) void Update(bool maintainBufferConsistency)
{ {
// wait for not actively sending data // wait for not actively sending data
// this will time out at 10 seconds, an arbitrarily long period of time // this will time out at 10 seconds, an arbitrarily long period of time
// and do nothing if this happens // and do nothing if this happens
if (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS)) if (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS))
{ {
// copy editing to sending, // now start the RMT transmit with the editing buffer before we swap
// this maintains the contract that colors present before this will
// be the same as it just was using GetPixelColor
rmt_write_sample(T_CHANNEL::RmtChannelNumber, _pixelsEditing, _pixelsSize, false); 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); std::swap(_pixelsSending, _pixelsEditing);
} }
} }

View File

@@ -184,7 +184,7 @@ protected:
T_UARTFEATURE::Init(uartBaud); T_UARTFEATURE::Init(uartBaud);
} }
void UpdateUart() void UpdateUart(bool)
{ {
// Since the UART can finish sending queued bytes in the FIFO in // Since the UART can finish sending queued bytes in the FIFO in
// the background, instead of waiting for the FIFO to flush // the background, instead of waiting for the FIFO to flush
@@ -221,12 +221,12 @@ protected:
NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) : NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) :
NeoEsp8266UartBase(pixelCount, elementSize) NeoEsp8266UartBase(pixelCount, elementSize)
{ {
_asyncPixels = (uint8_t*)malloc(_sizePixels); _pixelsSending = (uint8_t*)malloc(_sizePixels);
} }
~NeoEsp8266AsyncUart() ~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()) while (_context.IsSending())
{ {
yield(); yield();
@@ -234,7 +234,7 @@ protected:
// detach context, which will disable intr, may disable ISR // detach context, which will disable intr, may disable ISR
_context.Detach(T_UARTFEATURE::Index); _context.Detach(T_UARTFEATURE::Index);
free(_asyncPixels); free(_pixelsSending);
} }
void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud) void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud)
@@ -245,7 +245,7 @@ protected:
_context.Attach(T_UARTFEATURE::Index); _context.Attach(T_UARTFEATURE::Index);
} }
void UpdateUart() void UpdateUart(bool maintainBufferConsistency)
{ {
// Instruct ESP8266 hardware uart to send the pixels asynchronously // Instruct ESP8266 hardware uart to send the pixels asynchronously
_context.StartSending(T_UARTFEATURE::Index, _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 // Annotate when we started to send bytes, so we can calculate when we are ready to send again
_startTime = micros(); _startTime = micros();
// Copy the pixels to the idle buffer and swap them if (maintainBufferConsistency)
memcpy(_asyncPixels, _pixels, _sizePixels); {
std::swap(_asyncPixels, _pixels); // 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: private:
T_UARTCONTEXT _context; 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 class NeoEsp8266UartSpeed800KbpsBase
@@ -334,7 +341,7 @@ public:
this->_startTime = micros() - getPixelTime(); this->_startTime = micros() - getPixelTime();
} }
void Update() void Update(bool maintainBufferConsistency)
{ {
// Data latch = 50+ microsecond pause in the output stream. Rather than // 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 // put a delay at the end of the function, the ending time is noted and
@@ -346,7 +353,7 @@ public:
{ {
yield(); yield();
} }
this->UpdateUart(); this->UpdateUart(maintainBufferConsistency);
} }
uint8_t* getPixels() const uint8_t* getPixels() const

View File

@@ -113,7 +113,7 @@ public:
_endTime = micros(); _endTime = micros();
} }
void Update() void Update(bool)
{ {
// Data latch = 50+ microsecond pause in the output stream. Rather than // 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 // put a delay at the end of the function, the ending time is noted and