Compare commits
4 Commits
2c359bae0f
...
day-25
| Author | SHA1 | Date | |
|---|---|---|---|
| f70ac0595c | |||
| 8f93c13807 | |||
| 9ffb5088f5 | |||
| 28f07009c2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/build
|
/build
|
||||||
/.vs
|
/.vs
|
||||||
HidCerberus.Lib.log
|
HidCerberus.Lib.log
|
||||||
|
replay.ipt
|
||||||
|
|||||||
@@ -2,22 +2,39 @@
|
|||||||
|
|
||||||
#define PI32 3.141592653589f
|
#define PI32 3.141592653589f
|
||||||
|
|
||||||
|
internal void renderSmallRect(GameOffscreenBuffer *buffer, int playerX, int playerY) {
|
||||||
|
uint8 *endOfBuffer = (uint8 *)buffer->memory + buffer->pitch*buffer->height;
|
||||||
|
uint32 color = 0xFFFFFFFF;
|
||||||
|
int top = playerY;
|
||||||
|
int bottom = playerY + 10;
|
||||||
|
for (int x = playerX; x < playerX + 10; x++) {
|
||||||
|
uint8 *pixel = ((uint8 *)buffer->memory + x * buffer->bytesPerPixel + top*buffer->pitch);
|
||||||
|
for (int y = top; y < bottom; y++) {
|
||||||
|
if ((pixel >= buffer->memory) && (pixel + 4 < endOfBuffer)) {
|
||||||
|
*(uint32 *)pixel = color;
|
||||||
|
}
|
||||||
|
pixel += buffer->pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) {
|
internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) {
|
||||||
int16 toneVolume = 3000;
|
int16 toneVolume = 3000;
|
||||||
int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz;
|
int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz;
|
||||||
|
|
||||||
|
#if 0
|
||||||
int16 *sampleOut = soundBuffer->samples;
|
int16 *sampleOut = soundBuffer->samples;
|
||||||
for (int sampleIndex = 0; sampleIndex < soundBuffer->sampleCount; sampleIndex++) {
|
for (int sampleIndex = 0; sampleIndex < soundBuffer->sampleCount; sampleIndex++) {
|
||||||
|
state->tSine += 2.0f * PI32 / (real32)wavePeriod;
|
||||||
int16 sampleValue = (int16)(sin(state->tSine) * (real32)toneVolume);
|
int16 sampleValue = (int16)(sin(state->tSine) * (real32)toneVolume);
|
||||||
*sampleOut++ = sampleValue;
|
*sampleOut++ = sampleValue;
|
||||||
*sampleOut++ = sampleValue;
|
*sampleOut++ = sampleValue;
|
||||||
state->tSine += 2.0f * PI32 / (real32)wavePeriod;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int yOffset) {
|
internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int yOffset) {
|
||||||
int bytesPerPixel = 4;
|
|
||||||
int pitch = buffer->width*bytesPerPixel;
|
|
||||||
uint8 *row = (uint8 *)buffer->memory;
|
uint8 *row = (uint8 *)buffer->memory;
|
||||||
for (int y = 0; y < buffer->height; y++) {
|
for (int y = 0; y < buffer->height; y++) {
|
||||||
uint32 *pixel = (uint32*)row;
|
uint32 *pixel = (uint32*)row;
|
||||||
@@ -26,7 +43,7 @@ internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int
|
|||||||
uint8 green = (uint8)(y + yOffset);
|
uint8 green = (uint8)(y + yOffset);
|
||||||
*pixel++ = (green << 8) | blue;
|
*pixel++ = (green << 8) | blue;
|
||||||
}
|
}
|
||||||
row += pitch;
|
row += buffer->pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,42 +53,52 @@ extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) {
|
|||||||
GameState *state = (GameState*)memory->permanentStorage;
|
GameState *state = (GameState*)memory->permanentStorage;
|
||||||
|
|
||||||
if (!memory->isInitialised) {
|
if (!memory->isInitialised) {
|
||||||
DebugReadFileResult bmpMem = memory->debugReadEntireFile(__FILE__);
|
DebugReadFileResult bmpMem = memory->debugReadEntireFile(ctx, __FILE__);
|
||||||
if (bmpMem.contents) {
|
if (bmpMem.contents) {
|
||||||
if (false) {
|
if (false) {
|
||||||
memory->debugWriteEntireFile("c:/source/repos/handmade/src/test.cpp", bmpMem.contentsSize, bmpMem.contents);
|
memory->debugWriteEntireFile(ctx, "c:/source/repos/handmade/src/test.cpp", bmpMem.contentsSize, bmpMem.contents);
|
||||||
}
|
}
|
||||||
memory->debugFreeFileMemory(bmpMem.contents);
|
memory->debugFreeFileMemory(ctx, bmpMem.contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
state->toneHz = 440;
|
state->toneHz = 440;
|
||||||
state->tSine = 0;
|
state->tSine = 0;
|
||||||
|
state->playerY = 100;
|
||||||
|
state->playerX = 100;
|
||||||
|
state->blueOffset = 0;
|
||||||
|
state->greenOffset = 0;
|
||||||
memory->isInitialised = true;
|
memory->isInitialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int controllerIndex = 0; controllerIndex < ArrayCount(input->controllers); controllerIndex++) {
|
for (int controllerIndex = 0; controllerIndex < ArrayCount(input->controllers); controllerIndex++) {
|
||||||
GameControllerInput *controllerInput = &input->controllers[controllerIndex];
|
GameControllerInput *controllerInput = &input->controllers[controllerIndex];
|
||||||
if (controllerInput->isAnalog) {
|
|
||||||
state->toneHz = 440 + (int)(128.0f*controllerInput->stickAvgX);
|
if (controllerInput->btnDown.endedDown) {
|
||||||
state->greenOffset -= (int)(20.0f*controllerInput->stickAvgX);
|
state->playerY -= 30;
|
||||||
state->blueOffset += (int)(20.0f*controllerInput->stickAvgY);
|
}
|
||||||
} else {
|
|
||||||
if (controllerInput->stickRight.endedDown) {
|
if (controllerInput->stickRight.endedDown) {
|
||||||
state->toneHz = 440 + 128;
|
state->playerX += 10;
|
||||||
state->greenOffset -= 10;
|
}
|
||||||
} else if (controllerInput->stickLeft.endedDown) {
|
if (controllerInput->stickLeft.endedDown) {
|
||||||
state->toneHz = 440 - 128;
|
state->playerX -= 10;
|
||||||
state->greenOffset += 10;
|
}
|
||||||
}
|
if (controllerInput->stickDown.endedDown) {
|
||||||
if (controllerInput->stickUp.endedDown) {
|
state->playerY += 10;
|
||||||
state->blueOffset += 10;
|
}
|
||||||
} else if (controllerInput->stickDown.endedDown) {
|
if (controllerInput->stickUp.endedDown) {
|
||||||
state->blueOffset -= 10;
|
state->playerY -= 10;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset);
|
renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset);
|
||||||
|
renderSmallRect(videoBuf, state->playerX, state->playerY);
|
||||||
|
renderSmallRect(videoBuf, input->mouseX, input->mouseY);
|
||||||
|
for (int i = 0; i < ArrayCount(input->mouseButtons); i++) {
|
||||||
|
if (input->mouseButtons[i].endedDown) {
|
||||||
|
renderSmallRect(videoBuf, 10 + i * 20, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) {
|
extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -64,6 +63,10 @@ inline uint32 safeTruncateUInt64(uint64 val) {
|
|||||||
return (uint32)val;
|
return (uint32)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ThreadContext {
|
||||||
|
int placeholder;
|
||||||
|
};
|
||||||
|
|
||||||
// === Platform to game services ===
|
// === Platform to game services ===
|
||||||
#if HANDMADE_INTERNAL
|
#if HANDMADE_INTERNAL
|
||||||
|
|
||||||
@@ -72,13 +75,13 @@ struct DebugReadFileResult {
|
|||||||
void *contents;
|
void *contents;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEBUG_PLATFORM_READ_ENTIRE_FILE(name) DebugReadFileResult name(char *filename)
|
#define DEBUG_PLATFORM_READ_ENTIRE_FILE(name) DebugReadFileResult name(ThreadContext *ctx, char *filename)
|
||||||
typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(DebugPlatformReadEntireFileFn);
|
typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(DebugPlatformReadEntireFileFn);
|
||||||
|
|
||||||
#define DEBUG_PLATFORM_FREE_FILE_MEMORY(name) void name(void *fileMemory)
|
#define DEBUG_PLATFORM_FREE_FILE_MEMORY(name) void name(ThreadContext *ctx, void *fileMemory)
|
||||||
typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(DebugPlatformFreeFileMemoryFn);
|
typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(DebugPlatformFreeFileMemoryFn);
|
||||||
|
|
||||||
#define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(name) bool32 name(char *filename, uint32 memorySize, void *memory)
|
#define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(name) bool32 name(ThreadContext *ctx, char *filename, uint32 memorySize, void *memory)
|
||||||
typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DebugPlatformWriteEntireFileFn);
|
typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DebugPlatformWriteEntireFileFn);
|
||||||
|
|
||||||
#define DEBUG_PLATFORM_PRINTF(name) void name(wchar_t* format, ...)
|
#define DEBUG_PLATFORM_PRINTF(name) void name(wchar_t* format, ...)
|
||||||
@@ -97,6 +100,8 @@ struct GameOffscreenBuffer {
|
|||||||
void *memory;
|
void *memory;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
int bytesPerPixel;
|
||||||
|
int pitch; // Bytes per row
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GameButtonState {
|
struct GameButtonState {
|
||||||
@@ -128,18 +133,19 @@ struct GameControllerInput {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GameInput {
|
struct GameInput {
|
||||||
|
GameButtonState mouseButtons[5];
|
||||||
|
int32 mouseX;
|
||||||
|
int32 mouseY;
|
||||||
|
int32 mouseZ;
|
||||||
GameControllerInput controllers[5];
|
GameControllerInput controllers[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GameMemory {
|
struct GameMemory {
|
||||||
bool32 isInitialised;
|
bool32 isInitialised;
|
||||||
|
|
||||||
uint64 permanentStorageSize;
|
uint64 permanentStorageSize;
|
||||||
void *permanentStorage; // required to be initialised to zero at startup
|
void *permanentStorage; // required to be initialised to zero at startup
|
||||||
|
|
||||||
uint64 transientStorageSize;
|
uint64 transientStorageSize;
|
||||||
void *transientStorage; // required to be initialised to zero at startup
|
void *transientStorage; // required to be initialised to zero at startup
|
||||||
|
|
||||||
DebugPlatformReadEntireFileFn *debugReadEntireFile;
|
DebugPlatformReadEntireFileFn *debugReadEntireFile;
|
||||||
DebugPlatformFreeFileMemoryFn *debugFreeFileMemory;
|
DebugPlatformFreeFileMemoryFn *debugFreeFileMemory;
|
||||||
DebugPlatformWriteEntireFileFn *debugWriteEntireFile;
|
DebugPlatformWriteEntireFileFn *debugWriteEntireFile;
|
||||||
@@ -151,14 +157,16 @@ struct GameState {
|
|||||||
int greenOffset;
|
int greenOffset;
|
||||||
int blueOffset;
|
int blueOffset;
|
||||||
real32 tSine;
|
real32 tSine;
|
||||||
|
int playerY;
|
||||||
|
int playerX;
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Game to platform services ===
|
// === Game to platform services ===
|
||||||
|
|
||||||
#define GAME_UPDATE_AND_RENDER(name) void name(GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input)
|
#define GAME_UPDATE_AND_RENDER(name) void name(ThreadContext *ctx, GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input)
|
||||||
typedef GAME_UPDATE_AND_RENDER(GameUpdateAndRenderFn);
|
typedef GAME_UPDATE_AND_RENDER(GameUpdateAndRenderFn);
|
||||||
GAME_UPDATE_AND_RENDER(gameUpdateAndRenderStub) {}
|
GAME_UPDATE_AND_RENDER(gameUpdateAndRenderStub) {}
|
||||||
|
|
||||||
#define GAME_GET_SOUND_SAMPLES(name) void name(GameMemory *memory, GameSoundOutputBuffer *soundBuf)
|
#define GAME_GET_SOUND_SAMPLES(name) void name(ThreadContext *ctx, GameMemory *memory, GameSoundOutputBuffer *soundBuf)
|
||||||
typedef GAME_GET_SOUND_SAMPLES(GameGetSoundSamplesFn);
|
typedef GAME_GET_SOUND_SAMPLES(GameGetSoundSamplesFn);
|
||||||
GAME_GET_SOUND_SAMPLES(gameGetSoundSamplesStub) {}
|
GAME_GET_SOUND_SAMPLES(gameGetSoundSamplesStub) {}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ DEBUG_PLATFORM_READ_ENTIRE_FILE(debugReadEntireFile) {
|
|||||||
if (ReadFile(fileHandle, result.contents, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize32 == (uint32)bytesRead)) {
|
if (ReadFile(fileHandle, result.contents, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize32 == (uint32)bytesRead)) {
|
||||||
result.contentsSize = fileSize32;
|
result.contentsSize = fileSize32;
|
||||||
} else {
|
} else {
|
||||||
debugFreeFileMemory(result.contents);
|
debugFreeFileMemory(ctx, result.contents);
|
||||||
result.contents = NULL;
|
result.contents = NULL;
|
||||||
// logging
|
// logging
|
||||||
}
|
}
|
||||||
@@ -104,6 +104,7 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig
|
|||||||
buffer->width = width;
|
buffer->width = width;
|
||||||
buffer->height = height;
|
buffer->height = height;
|
||||||
buffer->bytesPerPixel = 4;
|
buffer->bytesPerPixel = 4;
|
||||||
|
buffer->pitch = buffer->width*buffer->bytesPerPixel;
|
||||||
|
|
||||||
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader);
|
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader);
|
||||||
buffer->info.bmiHeader.biWidth = buffer->width;
|
buffer->info.bmiHeader.biWidth = buffer->width;
|
||||||
@@ -138,14 +139,6 @@ internal void win32DrawBufferInWindow(Win32OffscreenBuffer *buffer, HWND window)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Win32GameCode {
|
|
||||||
HMODULE gameCodeLib;
|
|
||||||
GameUpdateAndRenderFn *updateAndRender;
|
|
||||||
GameGetSoundSamplesFn *getSoundSamples;
|
|
||||||
bool isValid;
|
|
||||||
FILETIME lastWriteTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline FILETIME win32GetLastWriteTime(char *filename) {
|
inline FILETIME win32GetLastWriteTime(char *filename) {
|
||||||
FILETIME lastWriteTime = {};
|
FILETIME lastWriteTime = {};
|
||||||
WIN32_FIND_DATA findData;
|
WIN32_FIND_DATA findData;
|
||||||
@@ -201,8 +194,10 @@ internal void win32LoadXInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal void win32ProcessKeyboardKeypress(GameButtonState *newState, bool32 isDown) {
|
internal void win32ProcessKeyboardKeypress(GameButtonState *newState, bool32 isDown) {
|
||||||
newState->endedDown = isDown;
|
if (newState->endedDown != isDown) {
|
||||||
newState->halfTransitionCount++;
|
newState->endedDown = isDown;
|
||||||
|
newState->halfTransitionCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void win32ProcessXInputDigitalButton(DWORD xInputButtonState, GameButtonState *oldState, GameButtonState *newState, DWORD buttonBit) {
|
internal void win32ProcessXInputDigitalButton(DWORD xInputButtonState, GameButtonState *oldState, GameButtonState *newState, DWORD buttonBit) {
|
||||||
@@ -257,12 +252,7 @@ internal void win32InitSound(HWND window, int32 samplesPerSec, int bufferSize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT mainWindowCallback(
|
LRESULT mainWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||||
HWND window,
|
|
||||||
UINT message,
|
|
||||||
WPARAM wParam,
|
|
||||||
LPARAM lParam
|
|
||||||
) {
|
|
||||||
LRESULT result = 0;
|
LRESULT result = 0;
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case WM_SIZE: {
|
case WM_SIZE: {
|
||||||
@@ -359,7 +349,63 @@ internal void win32FillSoundBuffer(Win32SoundOutput *soundOutput, DWORD byteToLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void win32ProcessPendingMessages(GameControllerInput *keyboardController) {
|
internal Win32ReplayBuffer* win32GetReplayBuffer(Win32State *state, int unsigned index) {
|
||||||
|
Win32ReplayBuffer *result = &state->replayBuffers[index - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32BeginRecordingInput(Win32State *state, int inputRecordingIndex) {
|
||||||
|
Win32ReplayBuffer *replayBuffer = win32GetReplayBuffer(state, inputRecordingIndex);
|
||||||
|
if (replayBuffer->memoryBlock) {
|
||||||
|
state->inputRecordingIndex = inputRecordingIndex;
|
||||||
|
state->recordingHandle = replayBuffer->fileHandle;
|
||||||
|
LARGE_INTEGER filePosition;
|
||||||
|
filePosition.QuadPart = state->gameMemoryTotalSize;
|
||||||
|
SetFilePointerEx(state->recordingHandle, filePosition, NULL, FILE_BEGIN);
|
||||||
|
CopyMemory(replayBuffer->memoryBlock, state->gameMemoryBlock, state->gameMemoryTotalSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32EndRecordingInput(Win32State *win32State) {
|
||||||
|
win32State->inputRecordingIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32RecordInput(Win32State *win32State, GameInput *newInput) {
|
||||||
|
DWORD bytesWritten;
|
||||||
|
WriteFile(win32State->recordingHandle, newInput, sizeof(*newInput), &bytesWritten, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32BeginInputPlayback(Win32State *state, int inputPlayingIndex) {
|
||||||
|
Win32ReplayBuffer *replayBuffer = win32GetReplayBuffer(state, inputPlayingIndex);
|
||||||
|
if (replayBuffer->memoryBlock) {
|
||||||
|
state->inputPlayingIndex = inputPlayingIndex;
|
||||||
|
state->playbackHandle = replayBuffer->fileHandle;
|
||||||
|
LARGE_INTEGER filePosition;
|
||||||
|
filePosition.QuadPart = state->gameMemoryTotalSize;
|
||||||
|
SetFilePointerEx(state->playbackHandle, filePosition, NULL, FILE_BEGIN);
|
||||||
|
CopyMemory(state->gameMemoryBlock, replayBuffer->memoryBlock, state->gameMemoryTotalSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32EndInputPlayback(Win32State *win32State) {
|
||||||
|
win32State->inputPlayingIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32PlaybackInput(Win32State *win32State, GameInput *newInput) {
|
||||||
|
DWORD bytesRead;
|
||||||
|
if (ReadFile(win32State->playbackHandle, newInput, sizeof(*newInput), &bytesRead, NULL)) {
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
// Restart
|
||||||
|
int playingIndex = win32State->inputPlayingIndex;
|
||||||
|
win32EndInputPlayback(win32State);
|
||||||
|
win32BeginInputPlayback(win32State, playingIndex);
|
||||||
|
} else {
|
||||||
|
// there's still input, read next input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32ProcessPendingMessages(Win32State *win32State, GameControllerInput *keyboardController) {
|
||||||
MSG message;
|
MSG message;
|
||||||
while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) {
|
while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) {
|
||||||
if (message.message == WM_QUIT) {
|
if (message.message == WM_QUIT) {
|
||||||
@@ -397,6 +443,17 @@ internal void win32ProcessPendingMessages(GameControllerInput *keyboardControlle
|
|||||||
} else if (VKCode == VK_RIGHT) {
|
} else if (VKCode == VK_RIGHT) {
|
||||||
win32ProcessKeyboardKeypress(&keyboardController->btnRight, isDown);
|
win32ProcessKeyboardKeypress(&keyboardController->btnRight, isDown);
|
||||||
} else if (VKCode == VK_SPACE) {
|
} else if (VKCode == VK_SPACE) {
|
||||||
|
} else if (VKCode == 'L') {
|
||||||
|
if (isDown) {
|
||||||
|
if (win32State->inputRecordingIndex == 0 && win32State->inputPlayingIndex == 0) {
|
||||||
|
win32BeginRecordingInput(win32State, 1);
|
||||||
|
} else if (win32State->inputRecordingIndex != 0 && win32State->inputPlayingIndex == 0) {
|
||||||
|
win32EndRecordingInput(win32State);
|
||||||
|
win32BeginInputPlayback(win32State, 1);
|
||||||
|
} else if (win32State->inputRecordingIndex == 0 && win32State->inputPlayingIndex != 0) {
|
||||||
|
win32EndInputPlayback(win32State);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,18 +481,11 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) {
|
|||||||
return (real32)(end.QuadPart - start.QuadPart) / (real32)globalPerfCountFrequency;
|
return (real32)(end.QuadPart - start.QuadPart) / (real32)globalPerfCountFrequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// int monitorRefreshHz = 60;
|
internal void win32DebugDrawVertical(Win32OffscreenBuffer *buffer, int x, int top, int bottom, int32 color) {
|
||||||
// int gameUpdateHz = monitorRefreshHz / 2;
|
uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel;
|
||||||
|
|
||||||
#define monitorRefreshHz 60
|
|
||||||
#define gameUpdateHz (monitorRefreshHz / 2)
|
|
||||||
|
|
||||||
internal void win32DebugDrawVertical(Win32OffscreenBuffer *screenBuffer, int x, int top, int bottom, int32 color) {
|
|
||||||
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel;
|
|
||||||
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel;
|
|
||||||
for (int y = top; y < bottom; y++) {
|
for (int y = top; y < bottom; y++) {
|
||||||
*(uint32 *)pixel = color;
|
*(uint32 *)pixel = color;
|
||||||
pixel += pitch;
|
pixel += buffer->pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,28 +495,6 @@ inline void win32DrawSoundBufferMarker(Win32OffscreenBuffer *buffer, Win32SoundO
|
|||||||
win32DebugDrawVertical(buffer, x, top, bottom, color);
|
win32DebugDrawVertical(buffer, x, top, bottom, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) {
|
|
||||||
int padX = 16;
|
|
||||||
int padY = 16;
|
|
||||||
|
|
||||||
int top = padY;
|
|
||||||
int bottom = screenBuffer->height - padY;
|
|
||||||
int renderWidth = screenBuffer->width - 2 *padX;
|
|
||||||
|
|
||||||
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize;
|
|
||||||
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel;
|
|
||||||
|
|
||||||
for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) {
|
|
||||||
Win32DebugTimeMarker *thisMarker = &markers[markerIndex];
|
|
||||||
real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount);
|
|
||||||
int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor);
|
|
||||||
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel;
|
|
||||||
uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel));
|
|
||||||
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel);
|
|
||||||
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) {
|
void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) {
|
||||||
for (int i = 0; i < sourceACount; i++) {
|
for (int i = 0; i < sourceACount; i++) {
|
||||||
*dest++ = *sourceA++;
|
*dest++ = *sourceA++;
|
||||||
@@ -477,27 +505,34 @@ void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *s
|
|||||||
*dest++ = 0;
|
*dest++ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow) {
|
void win32GetExeFilename(Win32State *win32State) {
|
||||||
char exeFileName[MAX_PATH];
|
DWORD sizeOfFilename = GetModuleFileNameA(NULL, win32State->exeFilename, sizeof(win32State->exeFilename));
|
||||||
DWORD sizeOfFilename = GetModuleFileNameA(NULL, exeFileName, sizeof(exeFileName));
|
win32State->onePastLastExeFilenameSlash = win32State->exeFilename;
|
||||||
char *onePastLastSlash = exeFileName;
|
for (char *scan = win32State->exeFilename; *scan; scan++) {
|
||||||
for (char *scan = exeFileName; *scan; scan++) {
|
|
||||||
if (*scan == '\\') {
|
if (*scan == '\\') {
|
||||||
onePastLastSlash = scan + 1;
|
win32State->onePastLastExeFilenameSlash = scan + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char sourceGameCodeDLLFilename[] = "handmade.dll";
|
void win32GetFullPathToLocalFile(Win32State *state, char *filename, int filenameSize, char *maxPathBuffer) {
|
||||||
|
catStrings(state->onePastLastExeFilenameSlash - state->exeFilename, state->exeFilename, filenameSize, filename, (size_t)MAX_PATH, maxPathBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WINDOW_WIDTH 1280
|
||||||
|
#define WINDOW_HEIGHT 720
|
||||||
|
|
||||||
|
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow) {
|
||||||
|
Win32State win32State = {};
|
||||||
|
win32GetExeFilename(&win32State);
|
||||||
|
|
||||||
|
char *sourceGameCodeDLLFilename = "handmade.dll";
|
||||||
char sourceGameCodeDLLFullPath[MAX_PATH];
|
char sourceGameCodeDLLFullPath[MAX_PATH];
|
||||||
catStrings(onePastLastSlash - exeFileName, exeFileName,
|
win32GetFullPathToLocalFile(&win32State, sourceGameCodeDLLFilename, sizeof(sourceGameCodeDLLFullPath) - 1, sourceGameCodeDLLFullPath);
|
||||||
sizeof(sourceGameCodeDLLFilename) - 1, sourceGameCodeDLLFilename,
|
|
||||||
sizeof(sourceGameCodeDLLFullPath) - 1, sourceGameCodeDLLFullPath);
|
|
||||||
|
|
||||||
char tempGameCodeDLLFilename[] = "handmade_temp.dll";
|
char tempGameCodeDLLFilename[] = "handmade_temp.dll";
|
||||||
char tempGameCodeDLLFullPath[MAX_PATH];
|
char tempGameCodeDLLFullPath[MAX_PATH];
|
||||||
catStrings(onePastLastSlash - exeFileName, exeFileName,
|
win32GetFullPathToLocalFile(&win32State, tempGameCodeDLLFilename, sizeof(tempGameCodeDLLFullPath) - 1, tempGameCodeDLLFullPath);
|
||||||
sizeof(tempGameCodeDLLFilename) - 1, tempGameCodeDLLFilename,
|
|
||||||
sizeof(tempGameCodeDLLFullPath) - 1, tempGameCodeDLLFullPath);
|
|
||||||
|
|
||||||
LARGE_INTEGER performanceFrequencyResult;
|
LARGE_INTEGER performanceFrequencyResult;
|
||||||
QueryPerformanceFrequency(&performanceFrequencyResult);
|
QueryPerformanceFrequency(&performanceFrequencyResult);
|
||||||
@@ -511,38 +546,45 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
windowClass.lpfnWndProc = &mainWindowCallback;
|
windowClass.lpfnWndProc = &mainWindowCallback;
|
||||||
windowClass.hInstance = instance;
|
windowClass.hInstance = instance;
|
||||||
windowClass.lpszClassName = "HandmadeHeroWindowClass";
|
windowClass.lpszClassName = "HandmadeHeroWindowClass";
|
||||||
|
resizeDIBSection(&globalBackBuffer, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
resizeDIBSection(&globalBackBuffer, 1280, 720);
|
|
||||||
|
|
||||||
real32 targetSecondsPerFrame = 1.0f / (real32)gameUpdateHz;
|
|
||||||
|
|
||||||
if (RegisterClass(&windowClass)) {
|
if (RegisterClass(&windowClass)) {
|
||||||
|
DWORD windowFlags = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
|
||||||
|
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
|
||||||
|
AdjustWindowRectEx(&rect, windowFlags, false, NULL);
|
||||||
HWND window = CreateWindowExA(
|
HWND window = CreateWindowExA(
|
||||||
NULL,
|
NULL,
|
||||||
windowClass.lpszClassName,
|
windowClass.lpszClassName,
|
||||||
"Handmade Hero",
|
"Handmade Hero",
|
||||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
windowFlags,
|
||||||
CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT,
|
CW_USEDEFAULT,
|
||||||
CW_USEDEFAULT,
|
CW_USEDEFAULT,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
instance,
|
instance,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
int monitorRefreshHz = 60;
|
||||||
|
int win32RefreshRate = GetDeviceCaps(GetDC(window), VREFRESH);
|
||||||
|
if (win32RefreshRate > 1) {
|
||||||
|
monitorRefreshHz = win32RefreshRate;
|
||||||
|
}
|
||||||
|
real32 gameUpdateHz = monitorRefreshHz / 2.0f;
|
||||||
|
real32 targetSecondsPerFrame = 1.0f / (real32)gameUpdateHz;
|
||||||
|
|
||||||
HH_CTRLW = GlobalAddAtomA("HH_CTRLW");
|
HH_CTRLW = GlobalAddAtomA("HH_CTRLW");
|
||||||
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W');
|
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W');
|
||||||
|
|
||||||
globalRunning = true;
|
globalRunning = true;
|
||||||
|
|
||||||
GameInput input[2] = {};
|
GameInput input[2] = {};
|
||||||
GameInput *oldInput = &input[0];
|
GameInput *oldInput = &input[0];
|
||||||
GameInput *newInput = &input[1];
|
GameInput *newInput = &input[1];
|
||||||
|
|
||||||
int debugTimeMarkerIndex = 0;
|
|
||||||
Win32DebugTimeMarker debugMarkers[gameUpdateHz / 2] = {};
|
|
||||||
|
|
||||||
win32LoadXInput();
|
win32LoadXInput();
|
||||||
|
|
||||||
// sound test
|
// sound test
|
||||||
@@ -551,7 +593,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
soundOutput.runningSampleIndex = 0;
|
soundOutput.runningSampleIndex = 0;
|
||||||
soundOutput.bytesPerSample = sizeof(int16)*2;
|
soundOutput.bytesPerSample = sizeof(int16)*2;
|
||||||
soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample;
|
soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample;
|
||||||
soundOutput.safetyBytes = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 2;
|
soundOutput.safetyBytes = (int)((soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 2.0f);
|
||||||
|
|
||||||
int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE);
|
int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
|
||||||
@@ -562,16 +604,36 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
#endif
|
#endif
|
||||||
GameMemory gameMemory = {};
|
GameMemory gameMemory = {};
|
||||||
gameMemory.permanentStorageSize = Megabytes(64);
|
gameMemory.permanentStorageSize = Megabytes(64);
|
||||||
gameMemory.transientStorageSize = Gigabytes((uint64)4);
|
gameMemory.transientStorageSize = Gigabytes((uint64)1);
|
||||||
gameMemory.debugFreeFileMemory = debugFreeFileMemory;
|
gameMemory.debugFreeFileMemory = debugFreeFileMemory;
|
||||||
gameMemory.debugWriteEntireFile = debugWriteEntireFile;
|
gameMemory.debugWriteEntireFile = debugWriteEntireFile;
|
||||||
gameMemory.debugReadEntireFile = debugReadEntireFile;
|
gameMemory.debugReadEntireFile = debugReadEntireFile;
|
||||||
gameMemory.debug_printf = debug_printf;
|
gameMemory.debug_printf = debug_printf;
|
||||||
|
|
||||||
uint64 totalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize;
|
win32State.gameMemoryTotalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize;
|
||||||
gameMemory.permanentStorage = VirtualAlloc(baseAddress, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
win32State.gameMemoryBlock = VirtualAlloc(baseAddress, win32State.gameMemoryTotalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
gameMemory.permanentStorage = win32State.gameMemoryBlock;
|
||||||
gameMemory.transientStorage = ((uint8 *)gameMemory.permanentStorage + gameMemory.permanentStorageSize);
|
gameMemory.transientStorage = ((uint8 *)gameMemory.permanentStorage + gameMemory.permanentStorageSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < ArrayCount(win32State.replayBuffers); i++) {
|
||||||
|
// Filename
|
||||||
|
char prefix[] = "replay-";
|
||||||
|
char suffix[] = ".hmr";
|
||||||
|
const int totalLen = ArrayCount(prefix) + 1 + ArrayCount(suffix);
|
||||||
|
char replayFilename[totalLen] = {};
|
||||||
|
char digit[] = {(char)(48 + i)};
|
||||||
|
catStrings(ArrayCount(prefix) - 1, prefix, 1, digit, ArrayCount(replayFilename) - 1, replayFilename);
|
||||||
|
catStrings(ArrayCount(prefix), replayFilename, ArrayCount(suffix) - 1, suffix, ArrayCount(replayFilename) - 1, replayFilename);
|
||||||
|
win32GetFullPathToLocalFile(&win32State, replayFilename, sizeof(replayFilename) - 1, win32State.replayBuffers[i].replayFilename);
|
||||||
|
Win32ReplayBuffer *replayBuffer = &win32State.replayBuffers[i];
|
||||||
|
|
||||||
|
replayBuffer->fileHandle = CreateFileA(replayBuffer->replayFilename, GENERIC_WRITE | GENERIC_READ, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
|
||||||
|
DWORD maxSizeLow = (win32State.gameMemoryTotalSize & 0xFFFFFFFF);
|
||||||
|
DWORD maxSizeHigh = (win32State.gameMemoryTotalSize >> 32);
|
||||||
|
replayBuffer->memoryMap = CreateFileMappingA(replayBuffer->fileHandle, NULL, PAGE_READWRITE, maxSizeHigh, maxSizeLow, NULL);
|
||||||
|
replayBuffer->memoryBlock = MapViewOfFile(replayBuffer->memoryMap, FILE_MAP_ALL_ACCESS, NULL, NULL, win32State.gameMemoryTotalSize);
|
||||||
|
}
|
||||||
|
|
||||||
win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize);
|
win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize);
|
||||||
win32ClearBuffer(&soundOutput);
|
win32ClearBuffer(&soundOutput);
|
||||||
globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
|
globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
|
||||||
@@ -579,7 +641,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
LARGE_INTEGER lastWorkCounter = win32GetWallClock();
|
LARGE_INTEGER lastWorkCounter = win32GetWallClock();
|
||||||
LARGE_INTEGER flipWallClock = win32GetWallClock();
|
LARGE_INTEGER flipWallClock = win32GetWallClock();
|
||||||
|
|
||||||
|
|
||||||
DWORD audioLatencyBytes = 0;
|
DWORD audioLatencyBytes = 0;
|
||||||
real32 audioLatencySeconds = 0;
|
real32 audioLatencySeconds = 0;
|
||||||
bool soundIsValid = false;
|
bool soundIsValid = false;
|
||||||
@@ -602,7 +663,19 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
newKeyboardController->buttons[buttonIndex].endedDown = oldKeyboardController->buttons[buttonIndex].endedDown;
|
newKeyboardController->buttons[buttonIndex].endedDown = oldKeyboardController->buttons[buttonIndex].endedDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
win32ProcessPendingMessages(newKeyboardController);
|
win32ProcessPendingMessages(&win32State, newKeyboardController);
|
||||||
|
|
||||||
|
POINT mousePos;
|
||||||
|
GetCursorPos(&mousePos);
|
||||||
|
ScreenToClient(window, &mousePos);
|
||||||
|
newInput->mouseX = mousePos.x;
|
||||||
|
newInput->mouseY = mousePos.y;
|
||||||
|
newInput->mouseZ = 0; // mouse wheel
|
||||||
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[0], GetKeyState(VK_LBUTTON) & (1 << 15));
|
||||||
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[1], GetKeyState(VK_MBUTTON) & (1 << 15));
|
||||||
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[2], GetKeyState(VK_RBUTTON) & (1 << 15));
|
||||||
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[3], GetKeyState(VK_XBUTTON1) & (1 << 15));
|
||||||
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[4], GetKeyState(VK_XBUTTON2) & (1 << 15));
|
||||||
|
|
||||||
XINPUT_STATE controllerState;
|
XINPUT_STATE controllerState;
|
||||||
int maxControllerCount = XUSER_MAX_COUNT;
|
int maxControllerCount = XUSER_MAX_COUNT;
|
||||||
@@ -666,24 +739,41 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadContext threadCtx = {};
|
||||||
|
|
||||||
GameOffscreenBuffer videoBuffer = {};
|
GameOffscreenBuffer videoBuffer = {};
|
||||||
videoBuffer.memory = globalBackBuffer.memory;
|
videoBuffer.memory = globalBackBuffer.memory;
|
||||||
videoBuffer.width = globalBackBuffer.width;
|
videoBuffer.width = globalBackBuffer.width;
|
||||||
videoBuffer.height = globalBackBuffer.height;
|
videoBuffer.height = globalBackBuffer.height;
|
||||||
game.updateAndRender(&gameMemory, &videoBuffer, newInput);
|
videoBuffer.pitch = globalBackBuffer.pitch;
|
||||||
|
videoBuffer.bytesPerPixel = globalBackBuffer.bytesPerPixel;
|
||||||
|
|
||||||
|
if (win32State.inputRecordingIndex) {
|
||||||
|
win32RecordInput(&win32State, newInput);
|
||||||
|
}
|
||||||
|
if (win32State.inputPlayingIndex) {
|
||||||
|
win32PlaybackInput(&win32State, newInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.updateAndRender) {
|
||||||
|
game.updateAndRender(&threadCtx, &gameMemory, &videoBuffer, newInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
LARGE_INTEGER audioWallClock = win32GetWallClock();
|
||||||
|
real32 fromBeginToAudioSeconds = win32GetSecondsElapsed(flipWallClock, audioWallClock);
|
||||||
|
|
||||||
DWORD playCursor = 0;
|
DWORD playCursor = 0;
|
||||||
DWORD writeCursor = 0;
|
DWORD writeCursor = 0;
|
||||||
|
|
||||||
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
|
||||||
if (!soundIsValid) {
|
if (!soundIsValid) {
|
||||||
soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample;
|
soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample;
|
||||||
soundIsValid = true;
|
soundIsValid = true;
|
||||||
}
|
}
|
||||||
DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize;
|
DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize;
|
||||||
|
DWORD expectedSoundBytesPerFrame = (DWORD)((soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz);
|
||||||
DWORD expectedSoundBytesPerFrame = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz;
|
real32 secondsLeftUntilFlip = targetSecondsPerFrame - fromBeginToAudioSeconds;
|
||||||
DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame;
|
DWORD expectedBytesUntilFlip = (DWORD)((secondsLeftUntilFlip/targetSecondsPerFrame) * (real32)expectedSoundBytesPerFrame);
|
||||||
|
DWORD expectedFrameBoundaryByte = playCursor + expectedBytesUntilFlip;
|
||||||
|
|
||||||
DWORD safeWriteCursor = writeCursor;
|
DWORD safeWriteCursor = writeCursor;
|
||||||
if (safeWriteCursor < playCursor) {
|
if (safeWriteCursor < playCursor) {
|
||||||
@@ -713,25 +803,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond;
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond;
|
||||||
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample;
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample;
|
||||||
soundBuffer.samples = samples;
|
soundBuffer.samples = samples;
|
||||||
game.getSoundSamples(&gameMemory, &soundBuffer);
|
game.getSoundSamples(&threadCtx, &gameMemory, &soundBuffer);
|
||||||
#if HANDMADE_INTERNAL
|
|
||||||
Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers));
|
|
||||||
|
|
||||||
Win32DebugTimeMarker *marker = &debugMarkers[debugTimeMarkerIndex++];
|
|
||||||
|
|
||||||
DWORD unwrappedWriteCursor = writeCursor;
|
|
||||||
if (unwrappedWriteCursor < targetCursor) {
|
|
||||||
unwrappedWriteCursor += soundOutput.secondaryBufferSize;
|
|
||||||
}
|
|
||||||
audioLatencyBytes = unwrappedWriteCursor - playCursor;
|
|
||||||
audioLatencySeconds = (((real32)audioLatencyBytes / (real32)soundOutput.bytesPerSample) / (real32)soundOutput.samplesPerSecond);
|
|
||||||
|
|
||||||
if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) {
|
|
||||||
debugTimeMarkerIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor);
|
|
||||||
#endif
|
|
||||||
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer);
|
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer);
|
||||||
} else {
|
} else {
|
||||||
soundIsValid = false;
|
soundIsValid = false;
|
||||||
@@ -755,10 +827,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
|
|
||||||
lastWorkCounter = win32GetWallClock();
|
lastWorkCounter = win32GetWallClock();
|
||||||
|
|
||||||
#if HANDMADE_INTERNAL
|
|
||||||
win32DebugSyncDisplay(&globalBackBuffer, ArrayCount(debugMarkers), debugMarkers, &soundOutput, targetSecondsPerFrame);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
win32DrawBufferInWindow(&globalBackBuffer, window);
|
win32DrawBufferInWindow(&globalBackBuffer, window);
|
||||||
flipWallClock = win32GetWallClock();
|
flipWallClock = win32GetWallClock();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "handmade.h"
|
#include "handmade.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
struct Win32OffscreenBuffer {
|
struct Win32OffscreenBuffer {
|
||||||
BITMAPINFO info;
|
BITMAPINFO info;
|
||||||
@@ -6,6 +7,7 @@ struct Win32OffscreenBuffer {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int bytesPerPixel;
|
int bytesPerPixel;
|
||||||
|
int pitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Win32WindowDimensions {
|
struct Win32WindowDimensions {
|
||||||
@@ -23,7 +25,39 @@ struct Win32SoundOutput {
|
|||||||
int safetyBytes;
|
int safetyBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Win32DebugTimeMarker {
|
struct Win32RecordedInput {
|
||||||
DWORD playCursor;
|
int inputCount;
|
||||||
DWORD writeCursor;
|
GameInput *inputStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define WIN32_STATE_FILE_NAME_LENGTH MAX_PATH
|
||||||
|
struct Win32ReplayBuffer {
|
||||||
|
HANDLE memoryMap;
|
||||||
|
HANDLE fileHandle;
|
||||||
|
void *memoryBlock;
|
||||||
|
char replayFilename[WIN32_STATE_FILE_NAME_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Win32State {
|
||||||
|
HANDLE recordingHandle;
|
||||||
|
uint32 inputRecordingIndex;
|
||||||
|
|
||||||
|
HANDLE playbackHandle;
|
||||||
|
uint32 inputPlayingIndex;
|
||||||
|
|
||||||
|
uint64 gameMemoryTotalSize;
|
||||||
|
void *gameMemoryBlock;
|
||||||
|
Win32ReplayBuffer replayBuffers[4];
|
||||||
|
|
||||||
|
char exeFilename[WIN32_STATE_FILE_NAME_LENGTH];
|
||||||
|
char *onePastLastExeFilenameSlash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Win32GameCode {
|
||||||
|
HMODULE gameCodeLib;
|
||||||
|
GameUpdateAndRenderFn *updateAndRender;
|
||||||
|
GameGetSoundSamplesFn *getSoundSamples;
|
||||||
|
bool isValid;
|
||||||
|
FILETIME lastWriteTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user