use floating job for playback wait
This commit is contained in:
parent
0ce5a1ed87
commit
e02858fd21
@ -2605,7 +2605,7 @@ struct gp_swapchain *gp_swapchain_alloc(struct sys_window *window, struct v2i32
|
||||
HWND hwnd = (HWND)sys_window_get_internal_handle(window);
|
||||
struct command_queue *cq = G.command_queues[DX12_QUEUE_DIRECT];
|
||||
|
||||
struct swapchain *swapchain = NULL;
|
||||
struct swapchain *swapchain = 0;
|
||||
{
|
||||
struct snc_lock lock = snc_lock_e(&G.swapchains_mutex);
|
||||
if (G.first_free_swapchain) {
|
||||
|
||||
@ -20,12 +20,9 @@
|
||||
#include <initguid.h>
|
||||
#include <objbase.h>
|
||||
#include <uuids.h>
|
||||
#include <avrt.h>
|
||||
#include <Audioclient.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
#pragma comment(lib, "avrt")
|
||||
|
||||
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e);
|
||||
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6);
|
||||
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2);
|
||||
@ -43,8 +40,8 @@ GLOBAL struct {
|
||||
HANDLE event;
|
||||
IAudioRenderClient *playback;
|
||||
WAVEFORMATEX *buffer_format;
|
||||
struct sys_thread *playback_scheduler_thread;
|
||||
u32 buffer_frames;
|
||||
struct snc_counter playback_job_counter;
|
||||
} G = ZI, DEBUG_ALIAS(G, G_playback_wasapi);
|
||||
|
||||
/* ========================== *
|
||||
@ -53,14 +50,15 @@ GLOBAL struct {
|
||||
|
||||
INTERNAL void wasapi_initialize(void);
|
||||
INTERNAL SYS_EXIT_FUNC(playback_shutdown);
|
||||
INTERNAL SYS_THREAD_DEF(playback_scheduler_entry, _);
|
||||
INTERNAL SYS_JOB_DEF(playback_job, _);
|
||||
|
||||
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr)
|
||||
{
|
||||
(UNUSED)mixer_sr;
|
||||
|
||||
wasapi_initialize();
|
||||
G.playback_scheduler_thread = sys_thread_alloc(playback_scheduler_entry, 0, LIT("Playback scheduler thread"), PROF_THREAD_GROUP_SCHEDULER);
|
||||
/* Start playback job */
|
||||
sys_run(1, playback_job, 0, SYS_POOL_AUDIO, SYS_PRIORITY_HIGH, &G.playback_job_counter);
|
||||
sys_on_exit(&playback_shutdown);
|
||||
|
||||
return (struct playback_startup_receipt) { 0 };
|
||||
@ -70,7 +68,7 @@ INTERNAL SYS_EXIT_FUNC(playback_shutdown)
|
||||
{
|
||||
__prof;
|
||||
atomic32_fetch_set(&G.shutdown, 1);
|
||||
sys_thread_wait_release(G.playback_scheduler_thread);
|
||||
snc_counter_wait(&G.playback_job_counter);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
@ -223,41 +221,34 @@ INTERNAL void wasapi_update_end(struct wasapi_buffer *wspbuf, struct mixed_pcm_f
|
||||
* Playback thread entry
|
||||
* ========================== */
|
||||
|
||||
INTERNAL SYS_JOB_DEF(playback_mix_job, _)
|
||||
INTERNAL SYS_JOB_DEF(playback_wait_job, _)
|
||||
{
|
||||
__prof;
|
||||
WaitForSingleObject(G.event, INFINITE);
|
||||
}
|
||||
|
||||
INTERNAL SYS_JOB_DEF(playback_job, _)
|
||||
{
|
||||
__prof;
|
||||
(UNUSED)_;
|
||||
|
||||
/* FIXME: If playback fails at any point and mixer stops advancing, we
|
||||
* need to halt mixer to prevent memory leak when sounds are played. */
|
||||
/* TODO: Signal counter that running job wiats on, rather than scheduling job manually */
|
||||
while (!atomic32_fetch(&G.shutdown)) {
|
||||
struct arena_temp scratch = scratch_begin_no_conflict();
|
||||
{
|
||||
__profn("Wasapi wait");
|
||||
struct snc_counter counter = ZI;
|
||||
sys_run(1, playback_wait_job, 0, SYS_POOL_FLOATING, SYS_PRIORITY_INHERIT, &counter);
|
||||
snc_counter_wait(&counter);
|
||||
}
|
||||
{
|
||||
__profn("Fill sample buffer");
|
||||
struct wasapi_buffer wspbuf = wasapi_update_begin();
|
||||
struct mixed_pcm_f32 pcm = mixer_update(scratch.arena, wspbuf.frames_count);
|
||||
wasapi_update_end(&wspbuf, pcm);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
INTERNAL SYS_THREAD_DEF(playback_scheduler_entry, _)
|
||||
{
|
||||
(UNUSED)_;
|
||||
/* https://learn.microsoft.com/en-us/windows/win32/procthread/multimedia-class-scheduler-service#registry-settings */
|
||||
DWORD task = 0;
|
||||
HANDLE mmc_handle = AvSetMmThreadCharacteristicsW(L"Pro Audio", &task);
|
||||
ASSERT(mmc_handle);
|
||||
(UNUSED)mmc_handle;
|
||||
|
||||
/* FIXME: If playback fails at any point and mixer stops advancing, we
|
||||
* need to halt mixer to prevent memory leak when sounds are played. */
|
||||
/* TODO: Signal counter that running job wiats on, rather than scheduling job manually */
|
||||
while (!atomic32_fetch(&G.shutdown)) {
|
||||
{
|
||||
__profn("Wasapi wait");
|
||||
WaitForSingleObject(G.event, INFINITE);
|
||||
}
|
||||
{
|
||||
__profn("Run mix job & wait");
|
||||
struct snc_counter counter = ZI;
|
||||
sys_run(1, playback_mix_job, 0, SYS_POOL_DEDICATED, SYS_PRIORITY_CRITICAL, &counter);
|
||||
snc_counter_wait(&counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
src/sys.h
22
src/sys.h
@ -58,17 +58,14 @@ void sys_make_current_job_unyielding(void);
|
||||
enum sys_pool {
|
||||
SYS_POOL_INHERIT = -1,
|
||||
|
||||
/* This pool contains workers that rougly map to dedicated performance cores on the CPU.
|
||||
* Time critical jobs should be pushed here. */
|
||||
SYS_POOL_DEDICATED = 0,
|
||||
/* The floating pool contains a large number of lower priority threads that have affinity over the entire CPU.
|
||||
* Other pools should push jobs that only block and do no work here so that they can yield on the blocking job rather than blocking themselves. */
|
||||
SYS_POOL_FLOATING = 0,
|
||||
|
||||
/* This pool contains workers that that will not interfere with the dedicated pool.
|
||||
* Asynchronous jobs (e.g. as asset streaming) should be pushed here. */
|
||||
SYS_POOL_BACKGROUND = 1,
|
||||
|
||||
/* This pool contains a large number of floating low priority worker threads with the intent that these threads will only block and do no actual work.
|
||||
* Blocking operations (e.g. opening a file) should be isolated to jobs that get pushed to this pool. They can then be yielded on by jobs in other pools that actually do work. */
|
||||
SYS_POOL_FLOATING = 2,
|
||||
SYS_POOL_AUDIO = 2,
|
||||
SYS_POOL_USER = 3,
|
||||
SYS_POOL_SIM = 4,
|
||||
|
||||
NUM_SYS_POOLS
|
||||
};
|
||||
@ -76,10 +73,9 @@ enum sys_pool {
|
||||
/* Job execution order within a pool is based on priority. */
|
||||
enum sys_priority {
|
||||
SYS_PRIORITY_INHERIT = -1,
|
||||
SYS_PRIORITY_CRITICAL = 0,
|
||||
SYS_PRIORITY_HIGH = 1,
|
||||
SYS_PRIORITY_NORMAL = 2,
|
||||
SYS_PRIORITY_LOW = 3,
|
||||
SYS_PRIORITY_HIGH = 0,
|
||||
SYS_PRIORITY_NORMAL = 1,
|
||||
SYS_PRIORITY_LOW = 2,
|
||||
|
||||
NUM_SYS_PRIORITIES
|
||||
};
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
# include <fileapi.h>
|
||||
# include <dwmapi.h>
|
||||
# include <bcrypt.h>
|
||||
# include <avrt.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
#pragma comment(lib, "kernel32")
|
||||
@ -29,6 +30,7 @@
|
||||
#pragma comment(lib, "dwmapi")
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "synchronization")
|
||||
#pragma comment(lib, "avrt")
|
||||
|
||||
#define SYS_WINDOW_EVENT_LISTENERS_MAX 512
|
||||
#define WINDOW_CLASS_NAME L"power_play_window_class"
|
||||
@ -248,6 +250,7 @@ struct alignas(64) job_pool {
|
||||
i32 num_worker_threads;
|
||||
i32 thread_priority;
|
||||
u64 thread_affinity_mask;
|
||||
char *thread_mm_characteristics;
|
||||
struct arena *worker_threads_arena;
|
||||
struct sys_thread **worker_threads;
|
||||
struct worker_ctx *worker_contexts;
|
||||
@ -782,10 +785,11 @@ INTERNAL struct fiber *fiber_alloc(struct job_pool *pool)
|
||||
return fiber;
|
||||
}
|
||||
|
||||
INTERNAL void fiber_release(struct job_pool *pool, struct fiber *fiber, i16 fiber_id)
|
||||
INTERNAL void fiber_release(struct job_pool *pool, struct fiber *fiber)
|
||||
{
|
||||
tm_lock(&pool->free_fibers_lock);
|
||||
{
|
||||
i16 fiber_id = fiber->id;
|
||||
fiber->parent_id = pool->first_free_fiber_id;
|
||||
pool->first_free_fiber_id = fiber_id;
|
||||
}
|
||||
@ -876,7 +880,7 @@ INTERNAL void job_fiber_yield(struct fiber *fiber, struct fiber *parent_fiber)
|
||||
MemoryBarrier();
|
||||
SwitchToFiber(parent_fiber->addr);
|
||||
MemoryBarrier();
|
||||
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS + MEBI(fiber->job_pool) + fiber->id + 1);
|
||||
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS - MEBI(fiber->job_pool) + fiber->id + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,7 +899,7 @@ INTERNAL void job_fiber_entry(void *id_ptr)
|
||||
{
|
||||
i16 id = (i32)(i64)id_ptr;
|
||||
struct fiber *fiber = fiber_from_id(id);
|
||||
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS + MEBI(fiber->job_pool) + fiber->id + 1);
|
||||
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS - MEBI(fiber->job_pool) + fiber->id + 1);
|
||||
for (;;) {
|
||||
/* Run job */
|
||||
{
|
||||
@ -947,6 +951,14 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
|
||||
ASSERT(success);
|
||||
(UNUSED)success;
|
||||
}
|
||||
|
||||
if (pool->thread_mm_characteristics) {
|
||||
/* https://learn.microsoft.com/en-us/windows/win32/procthread/multimedia-class-scheduler-service#registry-settings */
|
||||
DWORD task = 0;
|
||||
HANDLE mmc_handle = AvSetMmThreadCharacteristicsA(pool->thread_mm_characteristics, &task);
|
||||
ASSERT(mmc_handle);
|
||||
(UNUSED)mmc_handle;
|
||||
}
|
||||
}
|
||||
|
||||
i32 worker_fiber_id = sys_current_fiber_id();
|
||||
@ -1016,7 +1028,7 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
|
||||
/* Use resumed fiber if present */
|
||||
if (job_fiber_id > 0) {
|
||||
if (job_fiber) {
|
||||
fiber_release(pool, job_fiber, job_fiber->id);
|
||||
fiber_release(pool, job_fiber);
|
||||
}
|
||||
job_fiber = fiber_from_id(job_fiber_id);
|
||||
}
|
||||
@ -1028,7 +1040,8 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
|
||||
}
|
||||
job_fiber_id = job_fiber->id;
|
||||
{
|
||||
__profnc("Run fiber", RGB32_F(0.25, 0.75, 0));
|
||||
//__profnc("Run fiber", RGB32_F(0.25, 0.75, 0));
|
||||
__profnc("Run fiber", RGB32_F(1, 1, 1));
|
||||
__profvalue(job_fiber->id);
|
||||
struct yield_param yield = ZI;
|
||||
job_fiber->parent_id = worker_fiber_id;
|
||||
@ -1219,9 +1232,6 @@ INTERNAL SYS_THREAD_DEF(job_scheduler_entry, _)
|
||||
b32 success = SetThreadPriority(GetCurrentThread(), priority);
|
||||
(UNUSED)success;
|
||||
ASSERT(success);
|
||||
|
||||
success = SetThreadAffinityMask(GetCurrentThread(), (1 << 14)) != 0;
|
||||
ASSERT(success);
|
||||
}
|
||||
|
||||
/* Create high resolution timer */
|
||||
@ -1297,32 +1307,48 @@ INTERNAL SYS_THREAD_DEF(test_entry, _)
|
||||
for (enum sys_pool pool_kind = 0; pool_kind < (i32)countof(G.job_pools); ++pool_kind) {
|
||||
struct job_pool *pool = &G.job_pools[pool_kind];
|
||||
struct string name_fmt = ZI;
|
||||
i32 prof_group = PROF_THREAD_GROUP_FIBERS + MEBI(pool_kind);
|
||||
i32 prof_group = PROF_THREAD_GROUP_FIBERS - MEBI(pool_kind);
|
||||
switch (pool_kind) {
|
||||
default: ASSERT(0); break;
|
||||
|
||||
case SYS_POOL_DEDICATED:
|
||||
case SYS_POOL_SIM:
|
||||
{
|
||||
name_fmt = LIT("Dedicated worker #%F");
|
||||
pool->thread_affinity_mask = 0xFFFFFFFFFFFFFFFFULL;
|
||||
name_fmt = LIT("Sim worker #%F");
|
||||
pool->thread_affinity_mask = 0x000000000000000Full;
|
||||
pool->thread_priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
pool->num_worker_threads = 4;
|
||||
} break;
|
||||
|
||||
case SYS_POOL_USER:
|
||||
{
|
||||
name_fmt = LIT("User worker #%F");
|
||||
pool->thread_affinity_mask = 0x00000000000000F0ull;
|
||||
pool->thread_priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
pool->num_worker_threads = 4;
|
||||
} break;
|
||||
|
||||
case SYS_POOL_AUDIO:
|
||||
{
|
||||
name_fmt = LIT("Audio worker #%F");
|
||||
pool->thread_affinity_mask = 0x0000000000000300ull;
|
||||
pool->thread_priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
pool->thread_mm_characteristics = "Pro Audio";
|
||||
pool->num_worker_threads = 2;
|
||||
} break;
|
||||
|
||||
case SYS_POOL_BACKGROUND:
|
||||
{
|
||||
name_fmt = LIT("Background worker #%F");
|
||||
pool->thread_affinity_mask = 0xFFFFFFFFFFFFFFFFULL;
|
||||
pool->thread_priority = 0;
|
||||
pool->num_worker_threads = 4;
|
||||
pool->thread_affinity_mask = 0x0000000000003000ull;
|
||||
pool->num_worker_threads = 2;
|
||||
} break;
|
||||
|
||||
case SYS_POOL_FLOATING:
|
||||
{
|
||||
name_fmt = LIT("Floating worker #%F");
|
||||
pool->thread_affinity_mask = 0xFFFFFFFFFFFFFFFFULL;
|
||||
pool->thread_affinity_mask = 0xFFFFFFFFFFFFFFFFull;
|
||||
pool->thread_priority = 0;
|
||||
pool->num_worker_threads = 4;
|
||||
pool->num_worker_threads = 32;
|
||||
} break;
|
||||
}
|
||||
pool->worker_threads_arena = arena_alloc(GIBI(64));
|
||||
@ -2299,7 +2325,7 @@ INTERNAL SYS_THREAD_DEF(window_present_thread_entry_point, arg)
|
||||
struct sys_window_present_job_sig sig = ZI;
|
||||
sig.window = (struct sys_window *)window;
|
||||
sig.events = events;
|
||||
sys_run(1, window->present_job, &sig, SYS_POOL_DEDICATED, SYS_PRIORITY_HIGH, &counter);
|
||||
sys_run(1, window->present_job, &sig, SYS_POOL_USER, SYS_PRIORITY_HIGH, &counter);
|
||||
snc_counter_wait(&counter);
|
||||
}
|
||||
arena_reset(events_arena);
|
||||
|
||||
@ -243,7 +243,7 @@ struct user_startup_receipt user_startup(struct font_startup_receipt *font_sr,
|
||||
log_register_callback(debug_console_log_callback, LOG_LEVEL_DEBUG);
|
||||
|
||||
/* Start sim job */
|
||||
sys_run(1, local_sim_job, 0, SYS_POOL_DEDICATED, SYS_PRIORITY_HIGH, &G.shutdown_job_counters);
|
||||
sys_run(1, local_sim_job, 0, SYS_POOL_SIM, SYS_PRIORITY_HIGH, &G.shutdown_job_counters);
|
||||
sys_on_exit(&user_shutdown);
|
||||
|
||||
G.window = sys_window_alloc(user_update_job);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user