Improve assert with variadic macro for user msg

This commit is contained in:
Doyle Thai 2017-06-20 15:31:44 +10:00
parent 5397cdd9b9
commit eeb773747a
5 changed files with 160 additions and 289 deletions

View File

@ -4,6 +4,8 @@
#include "dqn.h" #include "dqn.h"
#include <intrin.h> #include <intrin.h>
typedef void PlatformAPI_DieGracefully();
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Platform File I/O // Platform File I/O
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -85,6 +87,8 @@ typedef struct PlatformAPI
PlatformAPI_LockAcquire *LockAcquire; PlatformAPI_LockAcquire *LockAcquire;
PlatformAPI_LockRelease *LockRelease; PlatformAPI_LockRelease *LockRelease;
PlatformAPI_LockDelete *LockDelete; PlatformAPI_LockDelete *LockDelete;
PlatformAPI_DieGracefully *DieGracefully;
} PlatformAPI; } PlatformAPI;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -1068,192 +1068,6 @@ FILE_SCOPE void SIMDTriangle(DTRRenderContext context, const DqnV3 p1, const Dqn
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle); DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle);
} }
FILE_SCOPE void SIMDBetterTriangle(DTRRenderContext context, const DqnV3 p1, const DqnV3 p2,
const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2,
const DqnV2 uv3, const f32 lightIntensity1,
const f32 lightIntensity2, const f32 lightIntensity3,
const bool ignoreLight, DTRBitmap *const texture, DqnV4 color,
const DqnV2i min, const DqnV2i max)
{
DTR_DEBUG_EP_TIMED_FUNCTION();
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle);
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble);
DTRRenderBuffer *const renderBuffer = context.renderBuffer;
////////////////////////////////////////////////////////////////////////////
// Convert color
////////////////////////////////////////////////////////////////////////////
__m128 simdColor = _mm_set_ps(color.a, color.b, color.g, color.r);
simdColor = SIMDSRGB1ToLinearSpace(simdColor);
simdColor = SIMDPreMultiplyAlpha1(simdColor);
f32 preserveAlpha = ((f32 *)&simdColor)[3];
const __m128 ZERO_4X = _mm_set_ps1(0.0f);
__m128 simdLightIntensity1 = _mm_set_ps1(lightIntensity1);
__m128 simdLightIntensity2 = _mm_set_ps1(lightIntensity2);
__m128 simdLightIntensity3 = _mm_set_ps1(lightIntensity3);
simdLightIntensity1 = _mm_max_ps(simdLightIntensity1, ZERO_4X);
simdLightIntensity2 = _mm_max_ps(simdLightIntensity2, ZERO_4X);
simdLightIntensity3 = _mm_max_ps(simdLightIntensity3, ZERO_4X);
__m128 p1Light = _mm_mul_ps(simdColor, simdLightIntensity1);
__m128 p2Light = _mm_mul_ps(simdColor, simdLightIntensity2);
__m128 p3Light = _mm_mul_ps(simdColor, simdLightIntensity3);
////////////////////////////////////////////////////////////////////////////
// Setup SIMD data
////////////////////////////////////////////////////////////////////////////
const u32 NUM_X_PIXELS_TO_SIMD = 1;
const u32 NUM_Y_PIXELS_TO_SIMD = 1;
// SignedArea: _mm_set_ps(unused, p3, p2, p1) ie 0=p1, 1=p1, 2=p3, 3=unused
__m128 signedAreaPixel1 = _mm_set_ps1(0);
// __m128 signedAreaPixel2 = _mm_set_ps1(0);
__m128 signedAreaPixelDeltaX = _mm_set_ps1(0);
__m128 signedAreaPixelDeltaY = _mm_set_ps1(0);
__m128 invSignedAreaParallelogram_4x = _mm_set_ps1(0);
__m128 triangleZ = _mm_set_ps(0, p3.z, p2.z, p1.z);
{
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble_SArea);
DTR_DEBUG_EP_TIMED_BLOCK("SIMDTriangle_Preamble_SArea");
DqnV2 startP = DqnV2_V2i(min);
f32 signedArea1Start = Triangle2TimesSignedArea(p2.xy, p3.xy, startP);
f32 signedArea1DeltaX = p2.y - p3.y;
f32 signedArea1DeltaY = p3.x - p2.x;
f32 signedArea2Start = Triangle2TimesSignedArea(p3.xy, p1.xy, startP);
f32 signedArea2DeltaX = p3.y - p1.y;
f32 signedArea2DeltaY = p1.x - p3.x;
f32 signedArea3Start = Triangle2TimesSignedArea(p1.xy, p2.xy, startP);
f32 signedArea3DeltaX = p1.y - p2.y;
f32 signedArea3DeltaY = p2.x - p1.x;
DTR_DEBUG_EP_TIMED_END_BLOCK();
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SArea);
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble_SIMDStep);
f32 signedAreaParallelogram = signedArea1Start + signedArea2Start + signedArea3Start;
if (signedAreaParallelogram == 0) return;
f32 invSignedAreaParallelogram = 1.0f / signedAreaParallelogram;
invSignedAreaParallelogram_4x = _mm_set_ps1(invSignedAreaParallelogram);
// NOTE: Order is important here!
signedAreaPixelDeltaX = _mm_set_ps(0, signedArea3DeltaX, signedArea2DeltaX, signedArea1DeltaX);
signedAreaPixelDeltaY = _mm_set_ps(0, signedArea3DeltaY, signedArea2DeltaY, signedArea1DeltaY);
signedAreaPixel1 = _mm_set_ps(0, signedArea3Start, signedArea2Start, signedArea1Start);
// signedAreaPixel2 = _mm_add_ps(signedAreaPixel1, signedAreaPixelDeltaX);
// NOTE: Increase step size to the number of pixels rasterised with SIMD
{
const __m128 STEP_X_4X = _mm_set_ps1((f32)NUM_X_PIXELS_TO_SIMD);
const __m128 STEP_Y_4X = _mm_set_ps1((f32)NUM_Y_PIXELS_TO_SIMD);
signedAreaPixelDeltaX = _mm_mul_ps(signedAreaPixelDeltaX, STEP_X_4X);
signedAreaPixelDeltaY = _mm_mul_ps(signedAreaPixelDeltaY, STEP_Y_4X);
}
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SIMDStep);
}
const DqnV2 uv2SubUv1 = uv2 - uv1;
const DqnV2 uv3SubUv1 = uv3 - uv1;
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble);
const u32 IS_GREATER_MASK = 0xF;
const u32 zBufferPitch = renderBuffer->width;
////////////////////////////////////////////////////////////////////////////
// Scan and Render
////////////////////////////////////////////////////////////////////////////
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Rasterise);
for (i32 bufferY = min.y; bufferY < max.y; bufferY += NUM_Y_PIXELS_TO_SIMD)
{
__m128 signedArea1 = signedAreaPixel1;
// __m128 signedArea2 = signedAreaPixel2;
for (i32 bufferX = min.x; bufferX < max.x; bufferX += NUM_X_PIXELS_TO_SIMD)
{
// Rasterise buffer(X, Y) pixel
{
__m128 checkArea = signedArea1;
__m128 isGreater = _mm_cmpge_ps(checkArea, ZERO_4X);
i32 isGreaterResult = _mm_movemask_ps(isGreater);
i32 posX = bufferX;
i32 posY = bufferY;
if ((isGreaterResult & IS_GREATER_MASK) == IS_GREATER_MASK)
{
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_RasterisePixel);
__m128 barycentric = _mm_mul_ps(checkArea, invSignedAreaParallelogram_4x);
__m128 barycentricZ = _mm_mul_ps(triangleZ, barycentric);
f32 pixelZDepth = ((f32 *)&barycentricZ)[0] +
((f32 *)&barycentricZ)[1] +
((f32 *)&barycentricZ)[2];
i32 zBufferIndex = posX + (posY * zBufferPitch);
if (context.multithread)
{
bool currLockValue;
do
{
currLockValue = (bool)context.api->AtomicCompareSwap(
(u32 *)&renderBuffer->pixelLockTable[zBufferIndex], (u32) true,
(u32) false);
} while (currLockValue != false);
}
if (pixelZDepth > renderBuffer->zBuffer[zBufferIndex])
{
__m128 finalColor = simdColor;
renderBuffer->zBuffer[zBufferIndex] = pixelZDepth;
if (!ignoreLight)
{
__m128 barycentricA_4x = _mm_set_ps1(((f32 *)&barycentric)[0]);
__m128 barycentricB_4x = _mm_set_ps1(((f32 *)&barycentric)[1]);
__m128 barycentricC_4x = _mm_set_ps1(((f32 *)&barycentric)[2]);
__m128 barycentricLight1 = _mm_mul_ps(p1Light, barycentricA_4x);
__m128 barycentricLight2 = _mm_mul_ps(p2Light, barycentricB_4x);
__m128 barycentricLight3 = _mm_mul_ps(p3Light, barycentricC_4x);
__m128 light =
_mm_add_ps(barycentricLight3,
_mm_add_ps(barycentricLight1, barycentricLight2));
finalColor = _mm_mul_ps(finalColor, light);
((f32 *)&finalColor)[3] = preserveAlpha;
}
if (texture)
{
__m128 texSampledColor = SIMDSampleTextureForTriangle(
texture, uv1, uv2SubUv1, uv3SubUv1, barycentric);
finalColor = _mm_mul_ps(texSampledColor, finalColor);
}
SIMDSetPixel(context, posX, posY, finalColor, ColorSpace_Linear);
}
renderBuffer->pixelLockTable[zBufferIndex] = false;
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_RasterisePixel);
}
signedArea1 = _mm_add_ps(signedArea1, signedAreaPixelDeltaX);
}
}
signedAreaPixel1 = _mm_add_ps(signedAreaPixel1, signedAreaPixelDeltaY);
// signedAreaPixel2 = _mm_add_ps(signedAreaPixel2, signedAreaPixelDeltaY);
}
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Rasterise);
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle);
}
FILE_SCOPE void SlowTriangle(DTRRenderContext context, const DqnV3 p1, const DqnV3 p2, FILE_SCOPE void SlowTriangle(DTRRenderContext context, const DqnV3 p1, const DqnV3 p2,
const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2, const DqnV2 uv3, const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2, const DqnV2 uv3,
const f32 lightIntensity1, const f32 lightIntensity2, const f32 lightIntensity1, const f32 lightIntensity2,

View File

@ -8,6 +8,11 @@
#define UNICODE #define UNICODE
#define _UNICODE #define _UNICODE
FILE_SCOPE PlatformMemory globalPlatformMemory;
FILE_SCOPE bool globalRunning;
void Platform_DieGracefully() { globalRunning = false; }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Platform Atomics // Platform Atomics
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -267,8 +272,6 @@ typedef struct Win32RenderBitmap
} Win32RenderBitmap; } Win32RenderBitmap;
FILE_SCOPE Win32RenderBitmap globalRenderBitmap; FILE_SCOPE Win32RenderBitmap globalRenderBitmap;
FILE_SCOPE PlatformMemory globalPlatformMemory;
FILE_SCOPE bool globalRunning;
typedef struct Win32ExternalCode typedef struct Win32ExternalCode
{ {
@ -659,32 +662,36 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
{ {
char exeDir[MAX_PATH] = {}; char exeDir[MAX_PATH] = {};
i32 lastSlashIndex = DqnWin32_GetEXEDirectory(exeDir, DQN_ARRAY_COUNT(exeDir)); i32 lastSlashIndex = DqnWin32_GetEXEDirectory(exeDir, DQN_ARRAY_COUNT(exeDir));
if (lastSlashIndex != -1) if (DQN_ASSERT_MSG(lastSlashIndex != -1, "Not enough space in buffer for exe path"))
{ {
DQN_ASSERT(lastSlashIndex + 1 < DQN_ARRAY_COUNT(exeDir));
exeDir[lastSlashIndex + 1] = 0; exeDir[lastSlashIndex + 1] = 0;
u32 numCopied = Dqn_sprintf(dllPath, "%s%s", exeDir, DLL_NAME); u32 dllNumCopied = Dqn_sprintf(dllPath, "%s%s", exeDir, DLL_NAME);
DQN_ASSERT(numCopied < DQN_ARRAY_COUNT(dllPath)); u32 dllTmpNumCopied = Dqn_sprintf(dllTmpPath, "%s%s", exeDir, DLL_TMP_NAME);
numCopied = Dqn_sprintf(dllTmpPath, "%s%s", exeDir, DLL_TMP_NAME); if (!DQN_ASSERT_MSG((dllNumCopied < DQN_ARRAY_COUNT(dllPath)) &&
DQN_ASSERT(numCopied < DQN_ARRAY_COUNT(dllTmpPath)); (dllTmpNumCopied < DQN_ARRAY_COUNT(dllPath)),
} "Out of space to form DLL path"))
else
{ {
DQN_ASSERT(DQN_INVALID_CODE_PATH); Platform_DieGracefully();
}
} }
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Platform Data Pre-amble // Platform Data Pre-amble
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
DQN_ASSERT(DqnMemStack_Init(&globalPlatformMemory.mainStack, DQN_MEGABYTE(4), true, 4) && bool memoryInitResult =
DqnMemStack_Init(&globalPlatformMemory.tempStack, DQN_MEGABYTE(4), true, 4) && DqnMemStack_Init(&globalPlatformMemory.mainStack, DQN_MEGABYTE(4), true, 4) |
DqnMemStack_Init(&globalPlatformMemory.assetStack, DQN_MEGABYTE(4), true, 4) DqnMemStack_Init(&globalPlatformMemory.tempStack, DQN_MEGABYTE(4), true, 4) |
); DqnMemStack_Init(&globalPlatformMemory.assetStack, DQN_MEGABYTE(4), true, 4);
if (!DQN_ASSERT_MSG(memoryInitResult, "Unable to allocate DTRenderer globalPlatformMemory stacks"))
{
Platform_DieGracefully();
}
PlatformAPI platformAPI = {}; PlatformAPI platformAPI = {};
platformAPI.DieGracefully = Platform_DieGracefully;
platformAPI.FileOpen = Platform_FileOpen; platformAPI.FileOpen = Platform_FileOpen;
platformAPI.FileRead = Platform_FileRead; platformAPI.FileRead = Platform_FileRead;
platformAPI.FileWrite = Platform_FileWrite; platformAPI.FileWrite = Platform_FileWrite;
@ -714,11 +721,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
PlatformJob jobQueueMemory[512] = {}; PlatformJob jobQueueMemory[512] = {};
{ {
DqnMemStackTempRegion memRegion; DqnMemStackTempRegion memRegion;
if (!DqnMemStackTempRegion_Begin(&memRegion, &globalPlatformMemory.tempStack)) if (!DQN_ASSERT(DqnMemStackTempRegion_Begin(&memRegion, &globalPlatformMemory.tempStack)))
{ Platform_DieGracefully();
DQN_WIN32_ERROR_BOX("DqnMemStackTempRegion_Begin() failed", NULL);
DQN_ASSERT(DQN_INVALID_CODE_PATH);
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Query CPU Cores // Query CPU Cores
@ -737,8 +741,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
// NOTE: (numCores - 1), 1 core is already exclusively for main thread // NOTE: (numCores - 1), 1 core is already exclusively for main thread
i32 availableThreads = (numCores - 1) * numThreadsPerCore; i32 availableThreads = (numCores - 1) * numThreadsPerCore;
// TODO(doyle): Logic for single core/thread processors if (availableThreads <= 0) availableThreads = 1;
DQN_ASSERT(availableThreads > 0);
jobQueue.win32Semaphore = CreateSemaphore(NULL, 0, availableThreads, NULL); jobQueue.win32Semaphore = CreateSemaphore(NULL, 0, availableThreads, NULL);
if (jobQueue.win32Semaphore) if (jobQueue.win32Semaphore)

View File

@ -16,6 +16,11 @@ if %errorlevel%==0 (
ctags -R ctags -R
) )
where /q gtags
if %errorlevel%==0 (
gtags
)
set ProjectName=dtrenderer set ProjectName=dtrenderer
ctime -begin ..\src\%ProjectName%.ctm ctime -begin ..\src\%ProjectName%.ctm
@ -56,7 +61,7 @@ goto :ReleaseFlags
:DebugFlags :DebugFlags
REM Od disables optimisations REM Od disables optimisations
REM RTC1 runtime error checks REM RTC1 runtime error checks, only possible with optimisations disabled
set CompileFlags=%CompileFlags% -Od -RTC1 set CompileFlags=%CompileFlags% -Od -RTC1
goto compile goto compile

171
src/dqn.h
View File

@ -64,16 +64,40 @@ typedef float f32;
#define DQN_SWAP(type, a, b) do { type tmp = a; a = b; b = tmp; } while(0) #define DQN_SWAP(type, a, b) do { type tmp = a; a = b; b = tmp; } while(0)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Dqn Error // DqnAssert
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#define DQN_ASSERT_HARD(expr) if (!(expr)) { *((int *)0) = 0; } // DQN_ASSERT() & DQN_ASSERT_MSG() will hard break the program but it can be
// disabled in DqnAssertInternal() for release whilst still allowing the assert
// expressions to be evaluated and instead send diagnostics to console.
// NOTE: "## __VA_ARGS__" is a GCC hack. Zero variadic arguments won't compile
// because there will be a trailing ',' at the end. Pasting it swallows it. MSVC
// implicitly swallows the trailing comma.
// Macro returns if the result was true or not.
#define DQN_ASSERT(expr) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, NULL) #define DQN_ASSERT(expr) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, NULL)
#define DQN_ASSERT_MSG(expr, msg) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, msg) #define DQN_ASSERT_MSG(expr, msg, ...) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, msg, ## __VA_ARGS__)
// Usage example. This protects code against asserts that should fire in release
// mode by still letting the expression evaluate and the ability to redirect
// the code flow to some recovery path.
#if 0
int *mem = (int *)malloc(sizeof(*mem));
if (DQN_ASSERT_MSG(mem, "Not enough memory for malloc")) {
// success
} else {
// failed
}
#endif
// Internal implementation should not be used as the macro above will handle it,
// but is required in header for visibility to external functions calling it.
DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum, DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum,
const char *const expr, const char *const msg); const char *const expr, const char *const msg, ...);
// Hard assert causes an immediate program break at point of assertion by trying
// to modify the 0th mem-address.
#define DQN_ASSERT_HARD(expr) if (!(expr)) { *((int *)0) = 0; }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// DqnMem - Memory // DqnMem - Memory
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -286,6 +310,9 @@ bool DqnArray_Remove (DqnArray<T> *array, u64 index);
bool DqnArray_RemoveStable(DqnArray<T> *array, u64 index); bool DqnArray_RemoveStable(DqnArray<T> *array, u64 index);
#endif #endif
FILE_SCOPE const char *const DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT =
"DqnMemAPICallbackResult type is incorrect";
// Implementation taken from Milton, developed by Serge at // Implementation taken from Milton, developed by Serge at
// https://github.com/serge-rgb/milton#license // https://github.com/serge-rgb/milton#license
template <typename T> template <typename T>
@ -321,7 +348,10 @@ bool DqnArray_Init(DqnArray<T> *array, size_t capacity,
DqnMemAPICallbackResult memResult = {0}; DqnMemAPICallbackResult memResult = {0};
DqnMemAPICallbackInfo info = DqnMemAPICallback_InfoAskAllocInternal(array->memAPI, allocateSize); DqnMemAPICallbackInfo info = DqnMemAPICallback_InfoAskAllocInternal(array->memAPI, allocateSize);
array->memAPI.callback(info, &memResult); array->memAPI.callback(info, &memResult);
DQN_ASSERT(memResult.type == DqnMemAPICallbackType_Alloc); if (!DQN_ASSERT_MSG(memResult.type == DqnMemAPICallbackType_Alloc, DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT))
{
return false;
}
array->data = (T *)memResult.newMemPtr; array->data = (T *)memResult.newMemPtr;
if (!array->data) return false; if (!array->data) return false;
@ -348,7 +378,11 @@ bool DqnArray_Grow(DqnArray<T> *array)
array->memAPI, array->data, oldSize, newSize); array->memAPI, array->data, oldSize, newSize);
array->memAPI.callback(info, &memResult); array->memAPI.callback(info, &memResult);
DQN_ASSERT(memResult.type == DqnMemAPICallbackType_Realloc); if (!DQN_ASSERT_MSG(memResult.type == DqnMemAPICallbackType_Realloc,
DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT))
{
return false;
}
if (memResult.newMemPtr) if (memResult.newMemPtr)
{ {
@ -1410,17 +1444,30 @@ STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri
#endif #endif
DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum, DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum,
const char *const expr, const char *const msg) const char *const expr, const char *const msg, ...)
{ {
if (!result) if (!(result))
{ {
const char *const formatStrNoMsg = "DqnAssert() failed: %s|%d| (%s)\n"; const char *const formatStrNoMsg = "DqnAssert() failed: %s|%d| (%s)\n";
const char *const formatStrWithMsg = "DqnAssert() failed: %s|%d| (%s): %s\n"; const char *const formatStrWithMsg = "DqnAssert() failed: %s|%d| (%s): %s\n";
const char *const formatStr = (msg) ? formatStrWithMsg : formatStrNoMsg; const char *const formatStr = (msg) ? formatStrWithMsg : formatStrNoMsg;
char userMsg[512] = {};
if (msg)
{
va_list argList;
va_start(argList, msg);
{
i32 numCopied = Dqn_vsprintf(userMsg, msg, argList);
DQN_ASSERT_HARD(numCopied < DQN_ARRAY_COUNT(userMsg));
}
va_end(argList);
}
#if (defined(_WIN32) || defined(_WIN64)) && defined(DQN_WIN32_IMPLEMENTATION) #if (defined(_WIN32) || defined(_WIN64)) && defined(DQN_WIN32_IMPLEMENTATION)
DqnWin32_OutputDebugString(formatStr, file, lineNum, expr, msg); DqnWin32_OutputDebugString(formatStr, file, lineNum, expr, userMsg);
#else #else
printf(formatStr, file, lineNum, expr, msg); printf(formatStr, file, lineNum, expr, userMsg);
#endif #endif
(*((i32 *)0)) = 0; (*((i32 *)0)) = 0;
@ -1547,9 +1594,13 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack,
{ {
if (!stack || !mem) return false; if (!stack || !mem) return false;
// TODO(doyle): Better logging if (!DQN_ASSERT_MSG(
if (memSize < sizeof(DqnMemStackBlock)) memSize > sizeof(DqnMemStackBlock),
DQN_ASSERT(DQN_INVALID_CODE_PATH); "memSize is insufficient to initialise a memstack, memSize: %d, requiredSize: %d",
memSize, sizeof(DqnMemStackBlock)))
{
return false;
}
stack->block = (DqnMemStackBlock *)mem; stack->block = (DqnMemStackBlock *)mem;
stack->block->memory = mem + sizeof(DqnMemStackBlock); stack->block->memory = mem + sizeof(DqnMemStackBlock);
@ -1569,10 +1620,12 @@ DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size,
const u32 byteAlign) const u32 byteAlign)
{ {
if (!stack || size <= 0) return false; if (!stack || size <= 0) return false;
DQN_ASSERT(!stack->block); if (!DQN_ASSERT_MSG(!stack->block, "MemStack has pre-existing block already attached"))
return false;
stack->block = DqnMemStack_AllocateBlockInternal(byteAlign, size); stack->block = DqnMemStack_AllocateBlockInternal(byteAlign, size);
if (!stack->block) return false; if (!DQN_ASSERT_MSG(stack->block, "MemStack failed to allocate block, not enough memory"))
return false;
stack->tempRegionCount = 0; stack->tempRegionCount = 0;
stack->byteAlign = byteAlign; stack->byteAlign = byteAlign;
@ -1613,17 +1666,14 @@ DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size)
DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, newBlockSize); DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, newBlockSize);
if (newBlock) if (newBlock)
{ {
if (!DqnMemStack_AttachBlock(stack, newBlock)) bool blockAttachResult = DqnMemStack_AttachBlock(stack, newBlock);
{
// IMPORTANT(doyle): This should be impossible, considering that // IMPORTANT(doyle): This should be impossible, considering that
// AllocateCompatibleBlock checks the preconditions that the new // AllocateCompatibleBlock checks the preconditions that the new
// block should be able to be attached. // block should be able to be attached.
// But if we somehow reach this, we need to free the block // But if we somehow reach this, we need to free the block
// otherwise memory is leaked. // otherwise memory is leaked.
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_HARD(blockAttachResult);
return NULL;
}
} }
else else
{ {
@ -1642,11 +1692,11 @@ DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size)
// subsequent allocations should also be aligned automatically. // subsequent allocations should also be aligned automatically.
// TODO(doyle): In the future, do we want to allow arbitrary alignment PER // TODO(doyle): In the future, do we want to allow arbitrary alignment PER
// allocation, not per MemStack? // allocation, not per MemStack?
DQN_ASSERT(alignmentOffset == 0); DQN_ASSERT_HARD(alignmentOffset == 0);
void *result = alignedResult; void *result = alignedResult;
stack->block->used += (alignedSize + alignmentOffset); stack->block->used += (alignedSize + alignmentOffset);
DQN_ASSERT(stack->block->used <= stack->block->size); DQN_ASSERT_HARD(stack->block->used <= stack->block->size);
return result; return result;
} }
@ -1655,14 +1705,18 @@ DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t
if (!stack || !stack->block) return false; if (!stack || !stack->block) return false;
u8 *currPtr = stack->block->memory + stack->block->used; u8 *currPtr = stack->block->memory + stack->block->used;
DQN_ASSERT((u8 *)ptr >= stack->block->memory && ptr < currPtr); if (DQN_ASSERT_MSG((u8 *)ptr >= stack->block->memory && ptr < currPtr,
"'ptr' to pop does not belong to current memStack attached block"))
{
size_t calcSize = (size_t)currPtr - (size_t)ptr; size_t calcSize = (size_t)currPtr - (size_t)ptr;
DQN_ASSERT(calcSize == size); if (DQN_ASSERT_MSG(calcSize == size, "'ptr' was not the last item allocated to memStack"))
{
stack->block->used -= size; stack->block->used -= size;
return true; return true;
}
}
return false;
} }
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block) DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block)
@ -1682,7 +1736,7 @@ DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemS
DqnMem_Free(blockToFree); DqnMem_Free(blockToFree);
// No more blocks, then last block has been freed // No more blocks, then last block has been freed
if (!stack->block) DQN_ASSERT(stack->tempRegionCount == 0); if (!stack->block) DQN_ASSERT_HARD(stack->tempRegionCount == 0);
return true; return true;
} }
@ -1704,7 +1758,7 @@ DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *stack)
// do is clear the block. // do is clear the block.
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser)
{ {
DQN_ASSERT(!stack->block->prevBlock); DQN_ASSERT_HARD(!stack->block->prevBlock);
DqnMemStack_ClearCurrBlock(stack, false); DqnMemStack_ClearCurrBlock(stack, false);
return; return;
} }
@ -1752,12 +1806,12 @@ DQN_FILE_SCOPE void DqnMemStackTempRegion_End(DqnMemStackTempRegion region)
if (stack->block) if (stack->block)
{ {
DQN_ASSERT(stack->block->used >= region.used); DQN_ASSERT_HARD(stack->block->used >= region.used);
stack->block->used = region.used; stack->block->used = region.used;
} }
stack->tempRegionCount--; stack->tempRegionCount--;
DQN_ASSERT(stack->tempRegionCount >= 0); DQN_ASSERT_HARD(stack->tempRegionCount >= 0);
} }
#ifdef DQN_CPP_MODE #ifdef DQN_CPP_MODE
@ -1769,15 +1823,7 @@ DqnMemStackTempRegionScoped::DqnMemStackTempRegionScoped(DqnMemStack *const stac
DqnMemStackTempRegionScoped::~DqnMemStackTempRegionScoped() DqnMemStackTempRegionScoped::~DqnMemStackTempRegionScoped()
{ {
if (this->isInit)
{
DqnMemStackTempRegion_End(this->tempMemStack); DqnMemStackTempRegion_End(this->tempMemStack);
}
else
{
// TODO(doyle): Report error
DQN_ASSERT(DQN_INVALID_CODE_PATH);
}
} }
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1820,23 +1866,23 @@ FILE_SCOPE DqnMemAPICallbackInfo DqnMemAPICallback_InfoAskFreeInternal(
return info; return info;
} }
void DqnMemAPI_ValidateCallbackInfo(DqnMemAPICallbackInfo info) void DqnMemAPI_ValidateCallbackInfoInternal(DqnMemAPICallbackInfo info)
{ {
DQN_ASSERT(info.type != DqnMemAPICallbackType_Invalid); DQN_ASSERT_HARD(info.type != DqnMemAPICallbackType_Invalid);
switch(info.type) switch(info.type)
{ {
case DqnMemAPICallbackType_Alloc: case DqnMemAPICallbackType_Alloc:
{ {
DQN_ASSERT(info.requestSize > 0); DQN_ASSERT_HARD(info.requestSize > 0);
} }
break; break;
case DqnMemAPICallbackType_Realloc: case DqnMemAPICallbackType_Realloc:
{ {
DQN_ASSERT(info.oldSize > 0); DQN_ASSERT_HARD(info.oldSize > 0);
DQN_ASSERT(info.requestSize > 0); DQN_ASSERT_HARD(info.requestSize > 0);
DQN_ASSERT(info.oldMemPtr); DQN_ASSERT_HARD(info.oldMemPtr);
} }
break; break;
@ -1852,8 +1898,9 @@ FILE_SCOPE void
DqnMemAPI_DefaultUseCallocCallbackInternal(DqnMemAPICallbackInfo info, DqnMemAPI_DefaultUseCallocCallbackInternal(DqnMemAPICallbackInfo info,
DqnMemAPICallbackResult *result) DqnMemAPICallbackResult *result)
{ {
DQN_ASSERT(!info.userContext); DQN_ASSERT_HARD(!info.userContext);
DqnMemAPI_ValidateCallbackInfo(info);
DqnMemAPI_ValidateCallbackInfoInternal(info);
switch(info.type) switch(info.type)
{ {
case DqnMemAPICallbackType_Alloc: case DqnMemAPICallbackType_Alloc:
@ -2859,7 +2906,7 @@ DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize)
// it is for positives, so ABS will fail. // it is for positives, so ABS will fail.
lastDigitDecremented = true; lastDigitDecremented = true;
val = DQN_ABS(val - 1); val = DQN_ABS(val - 1);
DQN_ASSERT(val >= 0); DQN_ASSERT_HARD(val >= 0);
} }
if (validBuffer) if (validBuffer)
@ -2971,7 +3018,7 @@ DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize)
if (i < bufSize) if (i < bufSize)
{ {
if (buf[i + 1] == '-') digitShiftIsPositive = false; if (buf[i + 1] == '-') digitShiftIsPositive = false;
DQN_ASSERT(buf[i + 1] == '-' || buf[i + 1] == '+'); DQN_ASSERT_HARD(buf[i + 1] == '-' || buf[i + 1] == '+');
i += 2; i += 2;
} }
@ -2997,7 +3044,7 @@ DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize)
// NOTE(doyle): If exponent not specified but this branch occurred, // NOTE(doyle): If exponent not specified but this branch occurred,
// the float string has a malformed scientific notation in the // the float string has a malformed scientific notation in the
// string, i.e. "e" followed by no number. // string, i.e. "e" followed by no number.
DQN_ASSERT(scientificNotation); DQN_ASSERT_HARD(scientificNotation);
numDigitsAfterDecimal += exponentPow; numDigitsAfterDecimal += exponentPow;
if (digitShiftIsPositive) digitShiftMultiplier = 10.0f; if (digitShiftIsPositive) digitShiftMultiplier = 10.0f;
@ -3443,10 +3490,8 @@ DQN_FILE_SCOPE void DqnWin32_GetNumThreadsAndCores(i32 *const numCores, i32 *con
&requiredSize); &requiredSize);
u8 *rawProcInfoArray = (u8 *)DqnMem_Calloc(requiredSize); u8 *rawProcInfoArray = (u8 *)DqnMem_Calloc(requiredSize);
if (!rawProcInfoArray) if (!DQN_ASSERT_MSG(rawProcInfoArray, "Calloc failed, could not allocate memory"))
{ {
DQN_WIN32_ERROR_BOX("DqnMem_Calloc() failed", NULL);
DQN_ASSERT(DQN_INVALID_CODE_PATH);
return; return;
} }
@ -3464,8 +3509,8 @@ DQN_FILE_SCOPE void DqnWin32_GetNumThreadsAndCores(i32 *const numCores, i32 *con
PROCESSOR_RELATIONSHIP *procInfo = &logicalProcInfo->Processor; PROCESSOR_RELATIONSHIP *procInfo = &logicalProcInfo->Processor;
u32 efficiency = procInfo->EfficiencyClass; u32 efficiency = procInfo->EfficiencyClass;
(*numCores)++; (*numCores)++;
DQN_ASSERT(logicalProcInfo->Relationship == RelationProcessorCore); DQN_ASSERT_HARD(logicalProcInfo->Relationship == RelationProcessorCore);
DQN_ASSERT(procInfo->GroupCount == 1); DQN_ASSERT_HARD(procInfo->GroupCount == 1);
bytesRead += logicalProcInfo->Size; bytesRead += logicalProcInfo->Size;
logicalProcInfo = logicalProcInfo =
@ -3595,7 +3640,7 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file,
{ {
size_t numBytesWritten = 0; size_t numBytesWritten = 0;
// TODO(doyle): Implement when it's needed // TODO(doyle): Implement when it's needed
DQN_ASSERT(fileOffset == 0); if (DQN_ASSERT_MSG(fileOffset != 0, "'fileOffset' not implemented yet")) return 0;
if (!file || !buffer) return numBytesToWrite; if (!file || !buffer) return numBytesToWrite;
#ifdef DQN_WIN32_IMPLEMENTATION #ifdef DQN_WIN32_IMPLEMENTATION
@ -3612,7 +3657,7 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file,
} }
#else #else
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented");
#endif #endif
return numBytesWritten; return numBytesWritten;
@ -3630,7 +3675,7 @@ DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file)
file->permissionFlags = 0; file->permissionFlags = 0;
} }
#else #else
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented");
#endif #endif
} }
@ -3757,7 +3802,7 @@ FILE_SCOPE f64 DqnWin32_QueryPerfCounterTimeInSInternal()
if (queryPerformanceFrequency.QuadPart == 0) if (queryPerformanceFrequency.QuadPart == 0)
{ {
QueryPerformanceFrequency(&queryPerformanceFrequency); QueryPerformanceFrequency(&queryPerformanceFrequency);
DQN_ASSERT(queryPerformanceFrequency.QuadPart != 0); DQN_ASSERT_HARD(queryPerformanceFrequency.QuadPart != 0);
} }
LARGE_INTEGER qpcResult; LARGE_INTEGER qpcResult;
@ -3777,7 +3822,7 @@ f64 DqnTime_NowInS()
result = DQN_MAX(DqnWin32_QueryPerfCounterTimeInSInternal(), 0); result = DQN_MAX(DqnWin32_QueryPerfCounterTimeInSInternal(), 0);
#else #else
result = 0; result = 0;
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented yet");
#endif #endif
return result; return result;
}; };
@ -3817,13 +3862,13 @@ FILE_SCOPE u32 DqnRnd_MakeSeedInternal()
__int64 numClockCycles = __rdtsc(); __int64 numClockCycles = __rdtsc();
return (u32)numClockCycles; return (u32)numClockCycles;
#elif __ANDROID__ #elif __ANDROID__
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Android path not implemented yet");
return 0; return 0;
#elif __linux__ #elif __linux__
unsigned long long numClockCycles = rdtsc(); unsigned long long numClockCycles = rdtsc();
return (u32)numClockCycles; return (u32)numClockCycles;
#else #else
DQN_ASSERT(DQN_INVALID_CODE_PATH); DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented yet");
return 0; return 0;
#endif #endif
} }