void print_counter( gen_counter* c ) { printf("Generation Counters\n" ); printf("CPU time used: %f\n", c->cpu_time_used ); printf("Database size: %f\n", (double)c->database_size/megabyte(1) ); printf("Total boards: %ld\n", c->total_boards ); printf("Unique boards: %ld\n", c->unique_boards ); printf("Wins white: %ld\n", c->wins_white ); printf("Wins black: %ld\n", c->wins_black ); printf("Draws: %ld\n", c->draws ); }
int32_t CALLBACK wWinMain( HINSTANCE window_instance, HINSTANCE, // hPrevInst is useless LPWSTR, // not using lpCmdLine int) // not using nCmdShow { LARGE_INTEGER perf_count_freq_result; QueryPerformanceFrequency(&perf_count_freq_result); int64_t perf_count_freq = perf_count_freq_result.QuadPart; win32_load_xinput(); wchar_t *wnd_class_name = L"Handmade Hero Window Class"; WNDCLASSEXW wnd_class = {}; // c++11 aggregate init to zero the struct wnd_class.cbSize = sizeof(wnd_class); wnd_class.style = CS_HREDRAW | CS_VREDRAW; wnd_class.lpfnWndProc = win32_wnd_proc; wnd_class.hInstance = window_instance; wnd_class.lpszClassName = wnd_class_name; ATOM wnd_class_atom = RegisterClassExW(&wnd_class); if (0 == wnd_class_atom) { win32_debug_print_last_error(); HANDMADE_ASSERT(0 != wnd_class_atom); return 0; } HWND hwnd = CreateWindowExW(0, wnd_class_name, L"Handmade Hero", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, window_instance, nullptr); if (hwnd == nullptr) { win32_debug_print_last_error(); HANDMADE_ASSERT(nullptr != hwnd); return 0; } win32_resize_backbuffer(&g_backbuffer, 1280, 720); // test sound win32_sound_output sound_output {}; sound_output.running_sample_index = 0; sound_output.num_sound_ch = 2; sound_output.samples_per_sec = 48000; sound_output.sec_to_buffer = 2; sound_output.latency_sample_count = sound_output.samples_per_sec / 15; sound_output.bytes_per_sample = sizeof(int16_t) * sound_output.num_sound_ch; sound_output.sound_buffer_size = sound_output.samples_per_sec * sound_output.bytes_per_sample * sound_output.sec_to_buffer; // create buffer for 2 sec win32_init_direct_sound(hwnd, sound_output.num_sound_ch, sound_output.samples_per_sec, sound_output.sound_buffer_size); int16_t *samples = nullptr; if (g_sound_buffer) { // fill the whole buffer and started playing sound. win32_clear_sound_buffer(g_sound_buffer); g_sound_buffer->Play(0, 0, DSBPLAY_LOOPING); // allocate sound buffer sample // Guarantee to be allocation granularity (64KB) aligned // commited to page boundary (4KB), but the rest are wasted space // memory auto clears to 0 // freed automatically when app terminates samples = static_cast<int16_t*>(win32_alloc_zeroed( nullptr, sound_output.sound_buffer_size)); } // input const real32 left_thumb_norm_deadzone = win32_get_xinput_stick_normalized_deadzone( XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); const real32 right_thumb_norm_deadzone = win32_get_xinput_stick_normalized_deadzone( XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); game_input input[2] = {}; game_input *new_input = &input[0]; game_input *old_input = &input[1]; // game memory #if HANDMADE_INTERNAL_BUILD void *base_memory_ptr = reinterpret_cast<void*>(terabyte(2ULL)); #else void *base_memory_ptr = nullptr; #endif game_memory memory {}; memory.permanent_storage_size = megabyte(64ULL); memory.transient_storage_size = gigabyte(1ULL); uint64_t total_size = memory.permanent_storage_size + memory.transient_storage_size; // Guarantee to be allocation granularity (64KB) aligned // commited to page boundary (4KB), but the rest are wasted space // memory auto clears to 0 // freed automatically when app terminates memory.permanent_storage = win32_alloc_zeroed(base_memory_ptr, total_size); memory.transient_storage = static_cast<int8_t*>(memory.permanent_storage) + memory.permanent_storage_size; if (g_backbuffer.memory && samples && memory.permanent_storage && memory.transient_storage) { g_running = true; uint64_t last_cycle_count = __rdtsc(); LARGE_INTEGER last_perf_counter; QueryPerformanceCounter(&last_perf_counter); while (g_running) { // We don't really need old input for keyboard as all keyboard // events are processed by wm msgs. So, just copy the old state. game_controller_input *kbd_controller = get_controller(new_input, game_input::kbd_controller_index); *kbd_controller = *get_controller(old_input, game_input::kbd_controller_index); win32_process_wm_msg_synchonously(kbd_controller); if (!g_running) { break; } constexpr uint32_t max_controller_count = std::min( XUSER_MAX_COUNT, game_input::max_controller_count - 1); for (DWORD controller_index = 0; controller_index < max_controller_count; ++controller_index) { // controller index 0 is reserved for keyboard DWORD true_controller_index = controller_index + 1; XINPUT_STATE controller_state {}; game_controller_input *new_controller = get_controller(new_input, true_controller_index); // Simply get the state of the controller from XInput. if (ERROR_SUCCESS == XInputGetState(controller_index, &controller_state)) { // Controller is connected new_controller->is_connected = true; const game_controller_input *old_controller = get_controller(old_input, true_controller_index); // dwPacketNumber indicates if there have been state changes controller_state.dwPacketNumber; const XINPUT_GAMEPAD *pad = &controller_state.Gamepad; // these are what we care about win32_process_xinput_digital_button( &new_controller->move_up, &old_controller->move_up, pad, XINPUT_GAMEPAD_DPAD_UP); win32_process_xinput_digital_button( &new_controller->move_down, &old_controller->move_down, pad, XINPUT_GAMEPAD_DPAD_DOWN); win32_process_xinput_digital_button( &new_controller->move_left, &old_controller->move_left, pad, XINPUT_GAMEPAD_DPAD_LEFT); win32_process_xinput_digital_button( &new_controller->move_right, &old_controller->move_right, pad, XINPUT_GAMEPAD_DPAD_RIGHT); win32_process_xinput_digital_button( &new_controller->action_up, &old_controller->action_up, pad, XINPUT_GAMEPAD_Y); win32_process_xinput_digital_button( &new_controller->action_down, &old_controller->action_down, pad, XINPUT_GAMEPAD_A); win32_process_xinput_digital_button( &new_controller->action_left, &old_controller->action_left, pad, XINPUT_GAMEPAD_X); win32_process_xinput_digital_button( &new_controller->action_right, &old_controller->action_right, pad, XINPUT_GAMEPAD_B); win32_process_xinput_digital_button( &new_controller->left_shoulder, &old_controller->left_shoulder, pad, XINPUT_GAMEPAD_LEFT_SHOULDER); win32_process_xinput_digital_button( &new_controller->right_shoulder, &old_controller->right_shoulder, pad, XINPUT_GAMEPAD_RIGHT_SHOULDER); // bool32 start = pad->wButtons & XINPUT_GAMEPAD_START; // bool32 back = pad->wButtons & XINPUT_GAMEPAD_BACK; win32_process_xinput_digital_button( &new_controller->start, &old_controller->start, pad, XINPUT_GAMEPAD_START); win32_process_xinput_digital_button( &new_controller->back, &old_controller->back, pad, XINPUT_GAMEPAD_BACK); // process stick new_controller->is_analog = true; new_controller->left_stick.avg_x = win32_xinput_thumb_resolve_deadzone_normalize( pad->sThumbLX, left_thumb_norm_deadzone); new_controller->left_stick.avg_y = win32_xinput_thumb_resolve_deadzone_normalize( pad->sThumbLY, left_thumb_norm_deadzone); new_controller->right_stick.avg_x = win32_xinput_thumb_resolve_deadzone_normalize( pad->sThumbRX, right_thumb_norm_deadzone); new_controller->right_stick.avg_y = win32_xinput_thumb_resolve_deadzone_normalize( pad->sThumbRY, right_thumb_norm_deadzone); } else { // Controller is not connected new_controller->is_connected = false; } } // DirectSound output test uint32_t byte_to_lock = 0; uint32_t bytes_to_write = 0; if (g_sound_buffer) { DWORD play_cursor; DWORD write_cursor; if (SUCCEEDED(g_sound_buffer->GetCurrentPosition(&play_cursor, &write_cursor))) { // fill the buffer till the play cursor + latency, // start location is the last location we wrote to byte_to_lock = (sound_output.running_sample_index * sound_output.bytes_per_sample) % sound_output.sound_buffer_size; // TODO: this may be dangerous! overwriting part of memory // between play cursor and write cursor DWORD target_to_cursor = (play_cursor + sound_output.latency_sample_count * sound_output.bytes_per_sample) % sound_output.sound_buffer_size; if (byte_to_lock > target_to_cursor) { bytes_to_write = sound_output.sound_buffer_size - byte_to_lock + target_to_cursor; } else { bytes_to_write = target_to_cursor - byte_to_lock; } } } game_sound_buffer game_sound_buffer {}; if (bytes_to_write > 0) { game_sound_buffer.samples = samples; game_sound_buffer.sample_count = bytes_to_write / sound_output.bytes_per_sample; game_sound_buffer.samples_per_sec = sound_output.samples_per_sec; } game_offscreen_buffer buffer {}; buffer.width = g_backbuffer.width; buffer.height = g_backbuffer.height; buffer.pitch = g_backbuffer.pitch; buffer.memory = g_backbuffer.memory; game_update_and_render(&memory, &buffer, &game_sound_buffer, new_input); if (bytes_to_write > 0) { win32_fill_sound_buffer(&sound_output, &game_sound_buffer, byte_to_lock, bytes_to_write); } HDC device_context = GetDC(hwnd); win32_window_dimension dimension = win32_get_window_dimension(hwnd); win32_display_offscreen_buffer(&g_backbuffer, device_context, dimension.width, dimension.height); ReleaseDC(hwnd, device_context); // swap game input game_input *tmp_input = new_input; new_input = old_input; old_input = tmp_input; uint64_t end_cycle_count = __rdtsc(); LARGE_INTEGER end_perf_counter; QueryPerformanceCounter(&end_perf_counter); // use signed, as it may go backward int64_t cycles_elapsed = end_cycle_count - last_cycle_count; int64_t counter_elapsed = end_perf_counter.QuadPart - last_perf_counter.QuadPart; real32 mega_cycles_per_frame = static_cast<real32>(cycles_elapsed) / 1000000.0f; real32 ms_per_frame = 1000.0f * static_cast<real32>(counter_elapsed) / static_cast<real32>(perf_count_freq); real32 fps = static_cast<real32>(perf_count_freq) / static_cast<real32>(counter_elapsed); // char buf[256]; // sprintf_s(buf, sizeof(buf), "%.2f Mc/f, %.2f ms/f, %.2f fps\n", // mega_cycles_per_frame, ms_per_frame, fps); // OutputDebugStringA(buf); last_perf_counter = end_perf_counter; last_cycle_count = end_cycle_count; } } else { // fail to allocate memory, no game. OutputDebugStringA("Fail to alloc memory to backbuffer, sound buffer, " "or game memory.\n"); } // MessageBoxW(nullptr, L"hello world", L"hello", MB_OK | MB_ICONINFORMATION); return 0; }