From 079e19b58b73d31393200f3b36c006409c5a436d Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Sun, 18 Jun 2017 20:25:57 +1000 Subject: [PATCH] Make threading work queue platform abstracted --- src/DTRenderer.cpp | 9 +- src/DTRendererPlatform.h | 55 ++++++++-- src/DTRendererRender.cpp | 115 +++++++++++++++++---- src/DTRendererRender.h | 4 +- src/Win32DTRenderer.cpp | 214 +++++++++++++++++++++------------------ 5 files changed, 263 insertions(+), 134 deletions(-) diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index bb52fd5..1da7a85 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -957,7 +957,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, renderBuffer.zBuffer = (f32 *)DqnMemStack_Push(&memory->tempStack, zBufferSize * sizeof(*renderBuffer.zBuffer)); - for (u32 i = 0; i < zBufferSize; i++) + for (u32 i = 0; i < zBufferSize; i++) renderBuffer.zBuffer[i] = DQN_F32_MIN; //////////////////////////////////////////////////////////////////////////// // Update and Render @@ -986,7 +986,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, DTRRenderTransform rotatingXform = DTRRender_DefaultTriangleTransform(); rotatingXform.rotation = rotation; - if (1) + if (0) { DTRDebug_BeginCycleCount("DTR_Update_RenderPrimitiveTriangles", DTRDebugCycleCount_DTR_Update_RenderPrimitiveTriangles); @@ -1000,7 +1000,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, DTRDebug_EndCycleCount(DTRDebugCycleCount_DTR_Update_RenderPrimitiveTriangles); } - if (0) + if (1) { LOCAL_PERSIST bool runTinyRendererOnce = false; if (1 && runTinyRendererOnce) @@ -1033,7 +1033,8 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, lighting.vector = LIGHT; lighting.color = DqnV4_4f(1, 1, 1, 1); - DTRRender_Mesh(&renderBuffer, mesh, lighting, modelP, transform); + DTRRender_Mesh(&renderBuffer, &memory->tempStack, &input->api, input->jobQueue, + mesh, lighting, modelP, transform); DTRDebug_EndCycleCount(DTRDebugCycleCount_DTR_Update_RenderModel); } } diff --git a/src/DTRendererPlatform.h b/src/DTRendererPlatform.h index f70f399..c162f18 100644 --- a/src/DTRendererPlatform.h +++ b/src/DTRendererPlatform.h @@ -4,6 +4,9 @@ #include "dqn.h" #include +//////////////////////////////////////////////////////////////////////////////// +// Platform File I/O +//////////////////////////////////////////////////////////////////////////////// enum PlatformFilePermissionFlag { PlatformFilePermissionFlag_Read = (1 << 0), @@ -24,20 +27,49 @@ typedef struct PlatformFile u32 permissionFlags; } PlatformFile; +// File I/O API typedef bool PlatformAPI_FileOpen (const char *const path, PlatformFile *const file, const u32 permissionFlags, const enum PlatformFileAction actionFlags); -typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf, const size_t bytesToRead); // Return bytes read +typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf, const size_t bytesToRead); // Return bytes read typedef size_t PlatformAPI_FileWrite(PlatformFile *const file, u8 *const buf, const size_t numBytesToWrite); // Return bytes read typedef void PlatformAPI_FileClose(PlatformFile *const file); typedef void PlatformAPI_Print (const char *const string); + +//////////////////////////////////////////////////////////////////////////////// +// Platform Multithreading +//////////////////////////////////////////////////////////////////////////////// +// PlatformJobQueue must be implemented in platform code. It simply needs to +// fullfill the API and be able to accept PlatformJob structs and execute them. +typedef struct PlatformJobQueue PlatformJobQueue; + +typedef void PlatformJob_Callback(PlatformJobQueue *const queue, void *const userData); +typedef struct PlatformJob +{ + PlatformJob_Callback *callback; + void *userData; +} PlatformJob; + +// Multithreading API +typedef bool PlatformAPI_QueueAddJob (PlatformJobQueue *const queue, const PlatformJob job); +typedef bool PlatformAPI_QueueTryExecuteNextJob(PlatformJobQueue *const queue); + +//////////////////////////////////////////////////////////////////////////////// +// Platform API for Game to Use +//////////////////////////////////////////////////////////////////////////////// typedef struct PlatformAPI { - PlatformAPI_FileOpen *FileOpen; - PlatformAPI_FileRead *FileRead; - PlatformAPI_FileWrite *FileWrite; - PlatformAPI_FileClose *FileClose; - PlatformAPI_Print *Print; + PlatformAPI_FileOpen *FileOpen; + PlatformAPI_FileRead *FileRead; + PlatformAPI_FileWrite *FileWrite; + PlatformAPI_FileClose *FileClose; + PlatformAPI_Print *Print; + + PlatformAPI_QueueAddJob *QueueAddJob; + PlatformAPI_QueueTryExecuteNextJob *QueueTryExecuteNextJob; } PlatformAPI; +//////////////////////////////////////////////////////////////////////////////// +// Platform Input +//////////////////////////////////////////////////////////////////////////////// enum Key { key_up, @@ -96,8 +128,9 @@ typedef struct PlatformInput f64 timeNowInS; PlatformFlags flags; - PlatformAPI api; - PlatformMouse mouse; + PlatformAPI api; + PlatformMouse mouse; + PlatformJobQueue *jobQueue; union { KeyState key[key_count]; struct @@ -131,6 +164,9 @@ typedef struct PlatformInput }; } PlatformInput; +//////////////////////////////////////////////////////////////////////////////// +// Platform Memory +//////////////////////////////////////////////////////////////////////////////// typedef struct PlatformMemory { union { @@ -146,6 +182,9 @@ typedef struct PlatformMemory void *context; } PlatformMemory; +//////////////////////////////////////////////////////////////////////////////// +// Platform Frame Buffer +//////////////////////////////////////////////////////////////////////////////// typedef struct PlatformRenderBuffer { i32 width; diff --git a/src/DTRendererRender.cpp b/src/DTRendererRender.cpp index daf8689..40c1046 100644 --- a/src/DTRendererRender.cpp +++ b/src/DTRendererRender.cpp @@ -963,7 +963,7 @@ FILE_SCOPE void SIMDTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1 f32 currZValue = renderBuffer->zBuffer[zBufferIndex]; if (pixelZValue > currZValue) { - renderBuffer->zBuffer[zBufferIndex] = pixelZValue; + // renderBuffer->zBuffer[zBufferIndex] = pixelZValue; __m128 finalColor = simdColor; if (!ignoreLight) { @@ -988,7 +988,7 @@ FILE_SCOPE void SIMDTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1 __m128 texSampledColor = SIMDSampleTextureForTriangle(texture, uv1, uv2SubUv1, uv3SubUv1, barycentric); finalColor = _mm_mul_ps(texSampledColor, finalColor); } - SIMDSetPixel(renderBuffer, posX, posY, finalColor, ColorSpace_Linear); + // SIMDSetPixel(renderBuffer, posX, posY, finalColor, ColorSpace_Linear); } DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_RasterisePixel); } @@ -1014,7 +1014,7 @@ FILE_SCOPE void SIMDTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1 f32 currZValue = renderBuffer->zBuffer[zBufferIndex]; if (pixelZValue > currZValue) { - renderBuffer->zBuffer[zBufferIndex] = pixelZValue; + // renderBuffer->zBuffer[zBufferIndex] = pixelZValue; __m128 finalColor = simdColor; if (!ignoreLight) @@ -1040,7 +1040,7 @@ FILE_SCOPE void SIMDTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1 __m128 texSampledColor = SIMDSampleTextureForTriangle(texture, uv1, uv2SubUv1, uv3SubUv1, barycentric); finalColor = _mm_mul_ps(texSampledColor, finalColor); } - SIMDSetPixel(renderBuffer, posX, posY, finalColor, ColorSpace_Linear); + // SIMDSetPixel(renderBuffer, posX, posY, finalColor, ColorSpace_Linear); } } signedArea2 = _mm_add_ps(signedArea2, signedAreaPixelDeltaX); @@ -1346,11 +1346,41 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, color, transform); } -void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, - DTRRenderLight lighting, const DqnV3 pos, - const DTRRenderTransform transform) +typedef struct RenderMeshJob { - if (!mesh) return; + DTRRenderBuffer *renderBuffer; + DTRBitmap *tex; + RenderLightInternal lighting; + + DqnV3 v1; + DqnV3 v2; + DqnV3 v3; + DqnV2 uv1; + DqnV2 uv2; + DqnV2 uv3; + DqnV4 color; +} RenderMeshJob; + +void MultiThreadedRenderMesh(struct PlatformJobQueue *const queue, void *const userData) +{ + if (!queue || !userData) + { + DQN_ASSERT(DQN_INVALID_CODE_PATH); + return; + } + + RenderMeshJob *job = (RenderMeshJob *)userData; +#if 1 + TexturedTriangleInternal(job->renderBuffer, job->lighting, job->v1, job->v2, job->v3, job->uv1, + job->uv2, job->uv3, job->tex, job->color); +#endif +} + +void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DqnMemStack *const tempStack, + PlatformAPI *const api, PlatformJobQueue *const jobQueue, DTRMesh *const mesh, + DTRRenderLight lighting, const DqnV3 pos, const DTRRenderTransform transform) +{ + if (!mesh || !renderBuffer || !tempStack || !api || !jobQueue) return; DqnMat4 viewPModelViewProjection = {}; { @@ -1461,30 +1491,73 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, lightingInternal.normals[2] = norm3; lightingInternal.numNormals = 3; - bool DEBUG_NO_TEX = false; - if (DTR_DEBUG && DEBUG_NO_TEX) + bool DEBUG_NO_TEX = false; + bool RUN_MULTITHREADED = true; + + if (RUN_MULTITHREADED) { - TexturedTriangleInternal(renderBuffer, lightingInternal, v1.xyz, v2.xyz, v3.xyz, uv1, - uv2, uv3, NULL, color); + RenderMeshJob *jobData = (RenderMeshJob *)DqnMemStack_Push(tempStack, sizeof(*jobData)); + if (jobData) + { + jobData->v1 = v1.xyz; + jobData->v2 = v2.xyz; + jobData->v3 = v3.xyz; + jobData->uv1 = uv1; + jobData->uv2 = uv2; + jobData->uv3 = uv3; + jobData->color = color; + jobData->renderBuffer = renderBuffer; + jobData->lighting = lightingInternal; + + if (DTR_DEBUG && DEBUG_NO_TEX) + { + jobData->tex = NULL; + } + else + { + jobData->tex = &mesh->tex; + } + + PlatformJob renderJob = {}; + renderJob.callback = MultiThreadedRenderMesh; + renderJob.userData = jobData; + while (!api->QueueAddJob(jobQueue, renderJob)) + { + api->QueueTryExecuteNextJob(jobQueue); + } + } + else + { + // TODO(doyle): Allocation error + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + } else { - TexturedTriangleInternal(renderBuffer, lightingInternal, v1.xyz, v2.xyz, v3.xyz, uv1, - uv2, uv3, &mesh->tex, color); + if (DTR_DEBUG && DEBUG_NO_TEX) + { + TexturedTriangleInternal(renderBuffer, lightingInternal, v1.xyz, v2.xyz, v3.xyz, + uv1, uv2, uv3, NULL, color); + } + else + { + TexturedTriangleInternal(renderBuffer, lightingInternal, v1.xyz, v2.xyz, v3.xyz, + uv1, uv2, uv3, &mesh->tex, color); + } } - bool DEBUG_WIREFRAME = false; if (DTR_DEBUG && DEBUG_WIREFRAME) { DqnV4 wireColor = DqnV4_4f(1.0f, 1.0f, 1.0f, 0.01f); - DTRRender_Line(renderBuffer, DqnV2i_V2(v1.xy), DqnV2i_V2(v2.xy), - wireColor); - DTRRender_Line(renderBuffer, DqnV2i_V2(v2.xy), DqnV2i_V2(v3.xy), - wireColor); - DTRRender_Line(renderBuffer, DqnV2i_V2(v3.xy), DqnV2i_V2(v1.xy), - wireColor); + DTRRender_Line(renderBuffer, DqnV2i_V2(v1.xy), DqnV2i_V2(v2.xy), wireColor); + DTRRender_Line(renderBuffer, DqnV2i_V2(v2.xy), DqnV2i_V2(v3.xy), wireColor); + DTRRender_Line(renderBuffer, DqnV2i_V2(v3.xy), DqnV2i_V2(v1.xy), wireColor); } } + + while (api->QueueTryExecuteNextJob(jobQueue)) + ; } void DTRRender_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, diff --git a/src/DTRendererRender.h b/src/DTRendererRender.h index 8a573ac..8341d59 100644 --- a/src/DTRendererRender.h +++ b/src/DTRendererRender.h @@ -2,10 +2,10 @@ #define DTRENDERER_RENDER_H #include "dqn.h" +#include "DTRendererPlatform.h" #define DTRRENDER_INV_255 1.0f/255.0f -typedef struct DTRRenderBuffer DTRRenderBuffer; typedef struct DTRBitmap DTRBitmap; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,7 +79,7 @@ typedef struct DTRRenderLight void DTRRender_Text (DTRRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1); void DTRRender_Line (DTRRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color); void DTRRender_Rectangle (DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform()); -void DTRRender_Mesh (DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, DTRRenderLight lighting, const DqnV3 pos, const DTRRenderTransform transform); +void DTRRender_Mesh (DTRRenderBuffer *const renderBuffer, DqnMemStack *const tempStack, PlatformAPI *const api, PlatformJobQueue *const jobQueue, DTRMesh *const mesh, DTRRenderLight lighting, const DqnV3 pos, const DTRRenderTransform transform); void DTRRender_Triangle (DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); void DTRRender_Bitmap (DTRRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1)); diff --git a/src/Win32DTRenderer.cpp b/src/Win32DTRenderer.cpp index ef25f78..291ec1f 100644 --- a/src/Win32DTRenderer.cpp +++ b/src/Win32DTRenderer.cpp @@ -12,11 +12,80 @@ const char *const DLL_NAME = "dtrenderer.dll"; const char *const DLL_TMP_NAME = "dtrenderer_temp.dll"; //////////////////////////////////////////////////////////////////////////////// -// Platform API Implementation +// Platform Multi Threading +//////////////////////////////////////////////////////////////////////////////// +struct PlatformJobQueue +{ + PlatformJob volatile *jobList; + LONG size; + + // NOTE: Modified by main+worker threads + LONG volatile jobToExecuteIndex; + HANDLE volatile win32Semaphore; + + // NOTE: Modified by main thread ONLY + LONG volatile jobInsertIndex; +}; + +bool Platform_QueueAddJob(PlatformJobQueue *const queue, const PlatformJob job) +{ + LONG newJobInsertIndex = (queue->jobInsertIndex + 1) % queue->size; + if (newJobInsertIndex == queue->jobToExecuteIndex) return false; + + queue->jobList[queue->jobInsertIndex] = job; + + _WriteBarrier(); + _mm_sfence(); + + queue->jobInsertIndex = newJobInsertIndex; + ReleaseSemaphore(queue->win32Semaphore, 1, NULL); + + return true; +} + +bool Platform_QueueTryExecuteNextJob(PlatformJobQueue *const queue) +{ + LONG originalJobToExecute = queue->jobToExecuteIndex; + if (originalJobToExecute != queue->jobInsertIndex) + { + LONG newJobIndexForNextThread = (originalJobToExecute + 1) % queue->size; + LONG index = InterlockedCompareExchange(&queue->jobToExecuteIndex, newJobIndexForNextThread, + originalJobToExecute); + + // NOTE: If we weren't successful at the interlock, another thread has + // taken the work and we can't know if there's more work or not. So + // irrespective of that result, return true to let the thread check + // again for more work. + if (index == originalJobToExecute) + { + PlatformJob job = queue->jobList[index]; + job.callback(queue, job.userData); + } + + return true; + } + + return false; +} + +DWORD WINAPI Win32ThreadCallback(void *lpParameter) +{ + PlatformJobQueue *queue = (PlatformJobQueue *)lpParameter; + for (;;) + { + if (!Platform_QueueTryExecuteNextJob(queue)) + { + WaitForSingleObjectEx(queue->win32Semaphore, INFINITE, false); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Platform I/O //////////////////////////////////////////////////////////////////////////////// FILE_SCOPE inline PlatformFile DqnFileToPlatformFileInternal(const DqnFile file) { - PlatformFile result = {}; + PlatformFile result = {}; result.handle = file.handle; result.size = file.size; result.permissionFlags = file.permissionFlags; @@ -441,65 +510,11 @@ i32 Win32GetModuleDirectory(char *const buf, const u32 bufLen) return lastSlashIndex; } -typedef struct Win32Thread +FILE_SCOPE void DebugWin32JobPrintNumber(PlatformJobQueue *const queue, void *const userData) { - HANDLE handle; - i32 logicalIndex; - DWORD id; -} Win32Thread; - -typedef struct Win32ThreadContext -{ - Win32Thread *threadList; - i32 size; -} Win32ThreadContext; - -typedef struct ThreadJob -{ - i32 numberToPrint; -} ThreadJob; - -typedef struct Win32ThreadJobQueue { - ThreadJob *jobList; - - LONG volatile jobInsertIndex; - LONG volatile currJobIndex; - LONG size; -} Win32ThreadJobQueue; - -FILE_SCOPE Win32ThreadJobQueue globalJobQueue; -FILE_SCOPE Win32ThreadContext globalThreadContext; -FILE_SCOPE HANDLE globalSemaphore; - -void PushThreadJob(Win32ThreadJobQueue *queue, ThreadJob job) -{ - DQN_ASSERT(queue->jobInsertIndex < queue->size); - queue->jobList[queue->jobInsertIndex] = job; - - queue->jobInsertIndex++; - ReleaseSemaphore(globalSemaphore, 1, NULL); -} - -DWORD WINAPI Win32ThreadCallback(void *lpParameter) -{ - Win32Thread *thread = (Win32Thread *)lpParameter; - DQN_ASSERT(thread); - - for (;;) - { - if (globalJobQueue.currJobIndex < globalJobQueue.jobInsertIndex) - { - LONG workIndex = InterlockedIncrement(&globalJobQueue.currJobIndex) - 1; - ThreadJob job = globalJobQueue.jobList[workIndex]; - - DqnWin32_OutputDebugString("Thread %d: Printing number: %d\n", thread->logicalIndex, - job.numberToPrint); - } - else - { - WaitForSingleObjectEx(globalSemaphore, INFINITE, false); - } - } + i32 numberToPrint = *((i32 *)userData); + DqnWin32_OutputDebugString("Thread %d: Printing number: %d\n", GetCurrentThreadId(), + numberToPrint); } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) @@ -612,15 +627,21 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi DqnMemStack_Init(&globalPlatformMemory.assetStack, DQN_MEGABYTE(4), true, 4) ); - PlatformAPI platformAPI = {}; - platformAPI.FileOpen = Platform_FileOpen; - platformAPI.FileRead = Platform_FileRead; - platformAPI.FileWrite = Platform_FileWrite; - platformAPI.FileClose = Platform_FileClose; - platformAPI.Print = Platform_Print; + PlatformAPI platformAPI = {}; + platformAPI.FileOpen = Platform_FileOpen; + platformAPI.FileRead = Platform_FileRead; + platformAPI.FileWrite = Platform_FileWrite; + platformAPI.FileClose = Platform_FileClose; + platformAPI.Print = Platform_Print; + + platformAPI.QueueAddJob = Platform_QueueAddJob; + platformAPI.QueueTryExecuteNextJob = Platform_QueueTryExecuteNextJob; + + PlatformJobQueue jobQueue = {}; PlatformInput platformInput = {}; platformInput.api = platformAPI; + platformInput.jobQueue = &jobQueue; platformInput.flags.canUseSSE2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE); platformInput.flags.canUseRdtsc = IsProcessorFeaturePresent(PF_RDTSC_INSTRUCTION_AVAILABLE); @@ -696,55 +717,50 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi //////////////////////////////////////////////////////////////////////// // Threading //////////////////////////////////////////////////////////////////////// - const i32 QUEUE_SIZE = 20; - globalJobQueue.jobList = (ThreadJob *)DqnMemStack_Push(&globalPlatformMemory.tempStack, - sizeof(ThreadJob) * QUEUE_SIZE); + const i32 QUEUE_SIZE = 512; + jobQueue.jobList = (PlatformJob *)DqnMemStack_Push(&globalPlatformMemory.mainStack, + sizeof(*jobQueue.jobList) * QUEUE_SIZE); - if (globalJobQueue.jobList) + // NOTE: InterlockedIncrement requires things to be on 32bit boundaries. + DQN_ASSERT(((size_t)&jobQueue.jobToExecuteIndex) % 4 == 0); + + if (jobQueue.jobList) { // NOTE: (numCores - 1), 1 core is already exclusively for main thread i32 availableThreads = (numCores - 1) * numLogicalCores; + // TODO(doyle): Logic for single core/thread processors DQN_ASSERT(availableThreads > 0); - globalSemaphore = CreateSemaphore(NULL, 0, availableThreads, NULL); - if (globalSemaphore) + jobQueue.win32Semaphore = CreateSemaphore(NULL, 0, availableThreads, NULL); + if (jobQueue.win32Semaphore) { - // Create jobs - globalJobQueue.size = QUEUE_SIZE; - for (i32 i = 0; i < 10; i++) + // Create threads + for (i32 i = 0; i < availableThreads; i++) { - ThreadJob job = {}; - job.numberToPrint = i; - PushThreadJob(&globalJobQueue, job); + const i32 USE_DEFAULT_STACK_SIZE = 0; + void *threadParam = &jobQueue; + HANDLE handle = CreateThread(NULL, USE_DEFAULT_STACK_SIZE, Win32ThreadCallback, + threadParam, 0, NULL); + CloseHandle(handle); } - // Create threads - globalThreadContext.threadList = (Win32Thread *)DqnMemStack_Push( - &globalPlatformMemory.tempStack, sizeof(Win32Thread) * availableThreads); - if (globalThreadContext.threadList) + // Create jobs + jobQueue.size = QUEUE_SIZE; + for (i32 i = 0; i < 20; i++) { - globalThreadContext.size = availableThreads; - for (i32 i = 0; i < globalThreadContext.size; i++) + PlatformJob job = {}; + job.callback = DebugWin32JobPrintNumber; + job.userData = DqnMemStack_Push(&globalPlatformMemory.tempStack, sizeof(i32)); + if (job.userData) { - const i32 USE_DEFAULT_STACK_SIZE = 0; - Win32Thread *thread = &globalThreadContext.threadList[i]; - thread->logicalIndex = i; - thread->handle = - CreateThread(NULL, USE_DEFAULT_STACK_SIZE, Win32ThreadCallback, - (void *)thread, 0, &thread->id); - - if (!thread->handle) - { - DqnWin32_DisplayLastError("CreateThread() failed"); - } + *((i32 *)job.userData) = i; + Platform_QueueAddJob(&jobQueue, job); } } - else - { - // TODO(doyle): Failed allocation - } + while (Platform_QueueTryExecuteNextJob(&jobQueue)) + ; } else {