Skip to content

Commit

Permalink
misc: simplify queue locking strategy to prevent stutter
Browse files Browse the repository at this point in the history
  • Loading branch information
jsm174 committed Jan 16, 2024
1 parent a132267 commit b8eaffe
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 108 deletions.
2 changes: 0 additions & 2 deletions include/DMDUtil/DMD.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

#if defined(__APPLE__)
#include <TargetConditionals.h>
Expand Down Expand Up @@ -120,7 +119,6 @@ class DMDUTILAPI DMD
std::thread* m_pThread;
std::queue<DMDUpdate*> m_updates;
std::mutex m_mutex;
std::condition_variable m_condVar;
bool m_running;

static bool m_finding;
Expand Down
64 changes: 31 additions & 33 deletions src/DMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,19 @@ DMD::DMD(int width, int height, bool sam, const char* name)
DMD::~DMD()
{
if (m_pThread) {
Stop();
m_running = false;

m_pThread->join();
delete m_pThread;
m_pThread = nullptr;
}

{
std::lock_guard<std::mutex> lock(m_mutex);
while (!m_updates.empty()) {
DMDUpdate* const pUpdate = m_updates.front();
m_updates.pop();
free(pUpdate->pData);
free(pUpdate->pData2);
delete pUpdate;
}
while (!m_updates.empty()) {
DMDUpdate* const pUpdate = m_updates.front();
m_updates.pop();
free(pUpdate->pData);
free(pUpdate->pData2);
delete pUpdate;
}

free(m_pData);
Expand Down Expand Up @@ -109,7 +106,6 @@ bool DMD::HasDisplay() const

void DMD::UpdateData(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b)
{
std::lock_guard<std::mutex> lock(m_mutex);
DMDUpdate* const pUpdate = new DMDUpdate();
memset(pUpdate, 0, sizeof(DMDUpdate));
pUpdate->mode = DmdMode::Data;
Expand All @@ -122,13 +118,14 @@ void DMD::UpdateData(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint
pUpdate->g = g;
pUpdate->b = b;

m_updates.push(pUpdate);
m_condVar.notify_one();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_updates.push(pUpdate);
}
}

void DMD::UpdateRGB24Data(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b)
{
std::lock_guard<std::mutex> lock(m_mutex);
DMDUpdate* const pUpdate = new DMDUpdate();
memset(pUpdate, 0, sizeof(DMDUpdate));
pUpdate->mode = DmdMode::RGB24;
Expand All @@ -141,13 +138,14 @@ void DMD::UpdateRGB24Data(const uint8_t* pData, int depth, uint8_t r, uint8_t g,
pUpdate->g = g;
pUpdate->b = b;

m_updates.push(pUpdate);
m_condVar.notify_one();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_updates.push(pUpdate);
}
}

void DMD::UpdateAlphaNumericData(AlphaNumericLayout layout, const uint16_t* pData1, const uint16_t* pData2, uint8_t r, uint8_t g, uint8_t b)
{
std::lock_guard<std::mutex> lock(m_mutex);
DMDUpdate* const pUpdate = new DMDUpdate();
memset(pUpdate, 0, sizeof(DMDUpdate));
pUpdate->mode = DmdMode::AlphaNumeric;
Expand All @@ -163,8 +161,10 @@ void DMD::UpdateAlphaNumericData(AlphaNumericLayout layout, const uint16_t* pDat
pUpdate->g = g;
pUpdate->b = b;

m_updates.push(pUpdate);
m_condVar.notify_one();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_updates.push(pUpdate);
}
}

void DMD::FindDevices()
Expand Down Expand Up @@ -223,7 +223,6 @@ void DMD::FindDevices()

void DMD::Run()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_running)
return;

Expand All @@ -235,13 +234,17 @@ void DMD::Run()
DmdMode mode = DmdMode::Unknown;

while (m_running) {
std::unique_lock<std::mutex> lock(m_mutex);
m_condVar.wait(lock, [this]{ return !m_updates.empty() || !m_running; });

while (!m_updates.empty()) {
DMDUpdate* const pUpdate = m_updates.front();
m_updates.pop();
DMDUpdate* pUpdate = nullptr;

{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_updates.empty()) {
pUpdate = m_updates.front();
m_updates.pop();
}
}

if (pUpdate) {
const bool update = (mode != pUpdate->mode);
mode = pUpdate->mode;

Expand All @@ -256,19 +259,14 @@ void DMD::Run()
free(pUpdate->pData2);
delete pUpdate;
}
else
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

Log("DMD run thread finished");
});
}

void DMD::Stop()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_running = false;
m_condVar.notify_all();
}

bool DMD::UpdatePalette(const DMDUpdate* pUpdate)
{
if (pUpdate->depth != 2 && pUpdate->depth != 4)
Expand Down
119 changes: 51 additions & 68 deletions src/Pixelcade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ Pixelcade::Pixelcade(struct sp_port* pSerialPort, int width, int height)
m_width = width;
m_height = height;
m_length = width * height;
for (int i = 0; i < PIXELCADE_MAX_QUEUE_FRAMES; ++i)
m_framePool.push((uint16_t*)malloc(m_length * sizeof(uint16_t)));
m_pThread = nullptr;
m_running = false;

Expand All @@ -31,31 +29,17 @@ Pixelcade::Pixelcade(struct sp_port* pSerialPort, int width, int height)
Pixelcade::~Pixelcade()
{
if (m_pThread) {
Stop();
m_running = false;

m_pThread->join();
delete m_pThread;
m_pThread = nullptr;
}

{
std::lock_guard<std::mutex> lock(m_mutex);
while (!m_frames.empty()) {
uint16_t* pFrame = m_frames.front();
m_frames.pop();
free(pFrame);
}

while (!m_framePool.empty()) {
uint16_t* pFrame = m_framePool.front();
m_framePool.pop();
free(pFrame);
}
while (!m_frames.empty()) {
free(m_frames.front());
m_frames.pop();
}

sp_set_dtr(m_pSerialPort, SP_DTR_OFF);
sp_close(m_pSerialPort);
sp_free_port(m_pSerialPort);
}

Pixelcade* Pixelcade::Connect(const char* pDevice, int width, int height)
Expand Down Expand Up @@ -146,20 +130,13 @@ Pixelcade* Pixelcade::Open(const char* pDevice, int width, int height)

void Pixelcade::Update(uint16_t* pData)
{
std::lock_guard<std::mutex> lock(m_mutex);
uint16_t* pFrame;

if (!m_framePool.empty()) {
pFrame = m_framePool.front();
m_framePool.pop();
}
else
pFrame = (uint16_t*)malloc(m_length * sizeof(uint16_t));

uint16_t* pFrame = (uint16_t*)malloc(m_length * sizeof(uint16_t));
memcpy(pFrame, pData, m_length * sizeof(uint16_t));

m_frames.push(pFrame);
m_condVar.notify_one();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_frames.push(pFrame);
}
}

void Pixelcade::EnableRgbLedMatrix(int shifterLen32, int rows)
Expand All @@ -170,7 +147,6 @@ void Pixelcade::EnableRgbLedMatrix(int shifterLen32, int rows)

void Pixelcade::Run()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_running)
return;

Expand All @@ -180,57 +156,64 @@ void Pixelcade::Run()
Log("Pixelcade run thread starting");
EnableRgbLedMatrix(4, 16);

int errors = 0;

while (m_running) {
std::unique_lock<std::mutex> lock(m_mutex);
m_condVar.wait(lock, [this]{ return !m_frames.empty() || !m_running; });
uint16_t* pFrame = nullptr;

if (m_frames.size() > PIXELCADE_MAX_QUEUE_FRAMES) {
while (!m_frames.empty()) {
uint16_t* pFrame = m_frames.front();
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_frames.empty()) {
pFrame = m_frames.front();
m_frames.pop();
m_framePool.push(pFrame);
}
}
else {
while (!m_frames.empty()) {
uint16_t* pFrame = m_frames.front();
m_frames.pop();

static uint8_t command = PIXELCADE_COMMAND_RGB_LED_MATRIX_FRAME;
sp_blocking_write(m_pSerialPort, &command, 1, PIXELCADE_COMMAND_WRITE_TIMEOUT);

uint8_t planes[128 * 32 * 3 / 2];
if (m_width == 128 && m_height == 32)
FrameUtil::SplitIntoRgbPlanes(pFrame, 128 * 32, 128, 16, (uint8_t*)planes);
else {
uint16_t scaledFrame[128 * 32];
FrameUtil::ResizeRgb565Bilinear(pFrame, m_width, m_height, scaledFrame, 128, 32);
FrameUtil::SplitIntoRgbPlanes(scaledFrame, 128 * 32, 128, 16, (uint8_t*)planes);
}
while (m_frames.size() > PIXELCADE_MAX_QUEUE_FRAMES) {
free(m_frames.front());
m_frames.pop();
}
}

enum sp_return response = sp_blocking_write(m_pSerialPort, planes, 128 * 32 * 3 / 2, PIXELCADE_COMMAND_WRITE_TIMEOUT);
if (pFrame) {
static uint8_t command = PIXELCADE_COMMAND_RGB_LED_MATRIX_FRAME;
sp_blocking_write(m_pSerialPort, &command, 1, PIXELCADE_COMMAND_WRITE_TIMEOUT);

uint8_t planes[128 * 32 * 3 / 2];
if (m_width == 128 && m_height == 32)
FrameUtil::SplitIntoRgbPlanes(pFrame, 128 * 32, 128, 16, (uint8_t*)planes);
else {
uint16_t scaledFrame[128 * 32];
FrameUtil::ResizeRgb565Bilinear(pFrame, m_width, m_height, scaledFrame, 128, 32);
FrameUtil::SplitIntoRgbPlanes(scaledFrame, 128 * 32, 128, 16, (uint8_t*)planes);
}

m_framePool.push(pFrame);
enum sp_return response = sp_blocking_write(m_pSerialPort, planes, 128 * 32 * 3 / 2, PIXELCADE_COMMAND_WRITE_TIMEOUT);

if (response == SP_ERR_FAIL) {
char* pMessage = sp_last_error_message();
Log("Error while transmitting to Pixelcade: %s", pMessage);
sp_free_error_message(pMessage);
if (response == SP_ERR_FAIL) {
char* pMessage = sp_last_error_message();
Log("Error while transmitting to Pixelcade: %s", pMessage);
sp_free_error_message(pMessage);
m_running = false;
}
else if ((int)response == 0) {
if (++errors > PIXELCADE_MAX_NO_RESPONSE) {
Log("Error while transmitting to Pixelcade: no response for the past %d frames.", PIXELCADE_MAX_NO_RESPONSE);
m_running = false;
}
}
}
else
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

sp_set_dtr(m_pSerialPort, SP_DTR_OFF);
sp_close(m_pSerialPort);
sp_free_port(m_pSerialPort);

m_pSerialPort = nullptr;

Log("Pixelcade run thread finished");
});
}

void Pixelcade::Stop()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_running = false;
m_condVar.notify_all();
}

}
7 changes: 2 additions & 5 deletions src/Pixelcade.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

#define PIXELCADE_RESPONSE_ESTABLE_CONNECTION 0x00
#define PIXELCADE_COMMAND_RGB_LED_MATRIX_FRAME 0x1F
#define PIXELCADE_COMMAND_RGB_LED_MATRIX_ENABLE 0x1E
#define PIXELCADE_COMMAND_READ_TIMEOUT 100
#define PIXELCADE_COMMAND_WRITE_TIMEOUT 50
#define PIXELCADE_COMMAND_WRITE_TIMEOUT 100
#define PIXELCADE_MAX_QUEUE_FRAMES 4
#define PIXELCADE_MAX_NO_RESPONSE 10

namespace DMDUtil {

Expand All @@ -35,7 +35,6 @@ class Pixelcade
private:
static Pixelcade* Open(const char* pDevice, int width, int height);
void Run();
void Stop();
void EnableRgbLedMatrix(int shifterLen32, int rows);

struct sp_port* m_pSerialPort;
Expand All @@ -45,9 +44,7 @@ class Pixelcade

std::thread* m_pThread;
std::queue<uint16_t*> m_frames;
std::queue<uint16_t*> m_framePool;
std::mutex m_mutex;
std::condition_variable m_condVar;
bool m_running;
};

Expand Down

0 comments on commit b8eaffe

Please sign in to comment.