Files
Main_MiSTer/offload.cpp
Martin Donlon ad695d0848 Video mode switching optimizations.
Reduce cost of setting video mode information when using
vsync_adjust=1/2 or vscale_mode=4/5 from 90ms to 5ms (worst case, most
instances are less than 1ms).

Split video initialization into video_init and video_set_mode.
video_init is called once, video_set_mode is called whenever the mode
changes.

Split hdmi_config into hdmi_config_init and hdmi_config_set_mode. Same
as video_, hdmi_config_init does the bulk of the initialization,
hdmi_config_set_mode is just for parameters that can change based on the
mode.

Load video filter data in loadScalerCfg and persist it.

Calculate a digest for scaler data and use that to determine whether new
data needs to be sent.

Only send gamma information if the filename has changed.

Offload fb module parameter writing to a separate thread via the new
offload system.

Reduce the amount of work being done in set_vrr_mode when vrr is
disabled.
2022-07-18 16:30:00 -07:00

122 lines
2.5 KiB
C++

#include "offload.h"
#include "profiling.h"
#include <pthread.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
static constexpr uint32_t QUEUE_SIZE = 8;
static pthread_t s_thread_handle;
static pthread_cond_t s_cond_work, s_cond_available;
static pthread_mutex_t s_queue_lock;
struct Work
{
std::function<void()> handler;
};
static Work s_queue[QUEUE_SIZE];
static uint32_t s_queue_head, s_queue_tail;
static bool s_quit;
static void *worker_thread(void *)
{
while (true)
{
Work *current_work = nullptr;
// Wait for work
pthread_mutex_lock(&s_queue_lock);
if (s_queue_head == s_queue_tail)
{
// queue empty and quit flag set, exit
if (s_quit)
{
pthread_mutex_unlock(&s_queue_lock);
break;
}
// wait for work signal
pthread_cond_wait(&s_cond_work, &s_queue_lock);
// quit flag was set and queue still empty, quit
if (s_quit && (s_queue_head == s_queue_tail))
{
pthread_mutex_unlock(&s_queue_lock);
break;
}
}
// get work
current_work = &s_queue[s_queue_tail % QUEUE_SIZE];
pthread_mutex_unlock(&s_queue_lock);
// execute
current_work->handler();
current_work->handler = nullptr;
// lock and move tail forward
pthread_mutex_lock(&s_queue_lock);
s_queue_tail++;
pthread_cond_signal(&s_cond_available);
pthread_mutex_unlock(&s_queue_lock);
}
return (void *)0;
}
void offload_start()
{
pthread_cond_init(&s_cond_available, nullptr);
pthread_cond_init(&s_cond_work, nullptr);
pthread_mutex_init(&s_queue_lock, nullptr);
s_queue_head = s_queue_tail = 0;
s_quit = false;
pthread_attr_t attr;
pthread_attr_init(&attr);
// Set affinity to core #0 since main runs on core #1
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
pthread_attr_setaffinity_np(&attr, sizeof(set), &set);
pthread_create(&s_thread_handle, &attr, worker_thread, nullptr);
}
void offload_stop()
{
pthread_mutex_lock(&s_queue_lock);
s_quit = true;
pthread_cond_signal(&s_cond_work);
pthread_mutex_unlock(&s_queue_lock);
printf("Waiting for offloaded work to finish...");
pthread_join(s_thread_handle, nullptr);
printf("Done\n");
}
void offload_add_work(std::function<void()> handler)
{
PROFILE_FUNCTION();
pthread_mutex_lock(&s_queue_lock);
if ((s_queue_head - s_queue_tail) == QUEUE_SIZE)
{
pthread_cond_wait(&s_cond_available, &s_queue_lock);
}
Work *work = &s_queue[s_queue_head % QUEUE_SIZE];
work->handler = handler;
s_queue_head++;
pthread_cond_signal(&s_cond_work);
pthread_mutex_unlock(&s_queue_lock);
}