diff --git a/.vscode/launch.json b/.vscode/launch.json index 1b9f2e6..59cb3b3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Launch", + "name": "Debug demo - Linux", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/out/debug/demo", @@ -23,6 +23,18 @@ } ] "preLaunchTask": "Build project", + }, + { + "name": "Debug demo - Windows", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}\\out\\debug\\demo.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}\\out\\debug", + "environment": [], + "externalConsole": false, + "preLaunchTask": "Build project", } - ] + ] } \ No newline at end of file diff --git a/README.md b/README.md index 3840fab..fcf125f 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # Kaliber A simple, cross-platform 2D game engine with OpenGL and Vulkan renderers. -Supports Linux and Android platforms. +Supports Linux, Windows and Android platforms. This is a personal hobby project. I've published a little game on [Google Play](https://play.google.com/store/apps/details?id=com.woom.game) based on this engine. Full game code and assets are included in this repository. ## Pre-requisites: -**GN build system** is required for all platforms except Android (support for -APKs, Java code etc. is to be added to the GN configuration). \ -Building GN from source: -https://gn.googlesource.com/gn/ \ -Pre-built GN binaries: -https://chrome-infra-packages.appspot.com/p/gn/gn/ +**GN build system** is required for all platforms except Android:\ +https://gn.googlesource.com/gn/ -Linux is the supported host platform to build Android. **Gradle**, +**Build Tools** is required to build for Windows. if you prefer, you can install +**Visual Studio** which includes the **Build Tools**. + +Linux is the supported host platform to build for Android. **Gradle**, **Android SDK** and **NDK** are required. If you prefer, you can install **Android Studio** which includes all the requirements. @@ -25,7 +24,7 @@ Linux is the supported host platform to build Android. **Gradle**, Setup: ```text gn gen out/release -gn gen --args='is_debug=true' out/debug +gn gen --args="is_debug=true" out/debug ``` Building and running: ```text @@ -33,6 +32,9 @@ ninja -C out/debug ./out/debug/hello_world ./out/debug/demo ``` +Building and debugging from VS Code: +* Select "Debug demo - [platform]" from the "Run and Debug" drop down. +* Press F5. ### Android: ```text cd build/android diff --git a/build/BUILD.gn b/build/BUILD.gn index eb9367d..b1209c4 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -164,6 +164,14 @@ config("release") { } } +config("third_party_warnings") { + if (is_win) { + cflags = [ + "/wd4242", # tmp:conversion from 'int' to 'uint8_t' + ] + } +} + config("warnings") { if (is_win) { cflags = [ @@ -173,7 +181,9 @@ config("warnings") { # Disable the following warnings: "/wd4061", # enumerator in switch with a default case not explicitly # handled by a case label. - "/wd4100", # unreferenced format parameter. + "/wd4100", # nonstandard extension used: nameless struct/union. + "/wd4201", # enumerator in switch with a default case not explicitly + # handled by a case label. "/wd4324", # structure was padded due to alignment specifier "/wd4371", # layout of class may have changed from a previous version of # the compiler due to better packing of member @@ -185,53 +195,59 @@ config("warnings") { # switch specified. # TODO: Not sure how I feel about these conversion warnings. + "/Wv:18", + "/wd4191", # 'type cast': unsafe conversion "/wd4244", # conversion, possible loss of data. 'int' to 'float' - "/wd4267", # conversion, possible loss of data. + "/wd4245", # tmp:conversion from 'int' to 'const unsigned int' + # "/wd4267", # conversion, possible loss of data. "/wd4305", # truncation from 'double' to 'float'. "/wd4365", # conversion, signed/unsigned mismatch. - "/wd5219", # conversion, possible loss of data. 'int' to 'float' + # "/wd5219", # conversion, possible loss of data. 'int' to 'float' + "/wd4722", # destructor never returns, potential memory leak + "/wd4702", # unreachable code # Possible compiler bug? Needs investigation. "/wd4668", # '__STDC_WANT_SECURE_LIB__' is not defined as a preprocessor # macro, replacing with '0' for '#if/#elif' # TODO: - "/wd4263", # member function does not override any base class virtual + # "/wd4263", # member function does not override any base class virtual # member function - "/wd4264", # no override available for virtual member function, function + # "/wd4264", # no override available for virtual member function, function # is hidden - "/wd4266", # no override available for virtual member function from base, + # "/wd4266", # no override available for virtual member function from base, # function is hidden - "/wd4355", # 'this' used in base member initializer list - "/wd4457", # declaration hides function parameter - "/wd4464", # relative include path contains '..'. + # "/wd4355", # 'this' used in base member initializer list + # "/wd4457", # declaration hides function parameter + # "/wd4464", # relative include path contains '..'. "/wd4625", # copy constructor was implicitly defined as deleted "/wd4626", # assignment operator was implicitly defined as deleted - "/wd4706", # assignment withing conditional expression. - "/wd4774", # format string expected in argument X is not a string literal - "/wd4828", # The file contains a character that is illegal in the current + # "/wd4706", # assignment withing conditional expression. + # "/wd4774", # format string expected in argument X is not a string literal + # "/wd4828", # The file contains a character that is illegal in the current # source character set - "/wd4946", # reinterpret_cast used between related - "/wd5026", # move constructor was implicitly defined as deleted - "/wd5027", # move assignment operator was implicitly defined as deleted - "/wd5039", # pointer or reference to potentially throwing function passed + # "/wd4946", # reinterpret_cast used between related + # "/wd5026", # move constructor was implicitly defined as deleted + # "/wd5027", # move assignment operator was implicitly defined as deleted + # "/wd5039", # pointer or reference to potentially throwing function passed # to 'extern "C"' function under -EHc. Undefined behavior may # occur if this function throws an exception. # TODO: I don't understand these or how to fix them: - "/wd4458", # declaration hides class member + # "/wd4458", # declaration hides class member # [ GameObject::rtti ] - "/wd4582", # constructor is not implicitly called + # "/wd4582", # constructor is not implicitly called # [ intersection.h Contact ] - "/wd4623", # default constructor was implicitly defined as deleted + # "/wd4623", # default constructor was implicitly defined as deleted # [ intersection.h Contact ] - "/wd5243", # using incomplete class can cause potential one definition + # "/wd5243", # using incomplete class can cause potential one definition # rule violation due to ABI limitation ] defines = [ "_CRT_NONSTDC_NO_DEPRECATE", "_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING", + "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS", ] } else if (is_mac) { cflags = [ diff --git a/build/android/app/CMakeLists.txt b/build/android/app/CMakeLists.txt index 94345ef..472f2b4 100644 --- a/build/android/app/CMakeLists.txt +++ b/build/android/app/CMakeLists.txt @@ -134,9 +134,11 @@ add_library(kaliber SHARED ../../../src/third_party/glslang/SPIRV/SPVRemapper.cpp ../../../src/third_party/glslang/SPIRV/SpvTools.cpp ../../../src/third_party/jsoncpp/jsoncpp.cpp + ../../../src/third_party/minimp3/minimp3.cc ../../../src/third_party/minizip/ioapi.c ../../../src/third_party/minizip/unzip.c ../../../src/third_party/spirv-reflect/spirv_reflect.c + ../../../src/third_party/stb/stb_image.cc ../../../src/third_party/texture_compressor/dxt_encoder_internals.cc ../../../src/third_party/texture_compressor/dxt_encoder.cc ../../../src/third_party/texture_compressor/texture_compressor_etc1.cc diff --git a/build/toolchain/gcc/BUILD.gn b/build/toolchain/gcc/BUILD.gn index 503fe29..6c323e4 100644 --- a/build/toolchain/gcc/BUILD.gn +++ b/build/toolchain/gcc/BUILD.gn @@ -54,7 +54,7 @@ toolchain("gcc") { } if (is_apple) { - not_needed([ "ar" ]) # libtool is used instead. + not_needed([ "ar" ]) # libtool is used instead. } tool("alink") { diff --git a/src/base/log.cc b/src/base/log.cc index df4bdc8..a937429 100644 --- a/src/base/log.cc +++ b/src/base/log.cc @@ -2,6 +2,9 @@ #if defined(__ANDROID__) #include +#elif defined(_WIN32) +#include +#include #else #include #endif @@ -30,17 +33,22 @@ LogMessage::LogMessage(const char* file, int line, int verbosity_level) LogMessage::~LogMessage() { stream_ << std::endl; - std::string text(stream_.str()); + std::string message(stream_.str()); std::string filename(file_); size_t last_slash_pos = filename.find_last_of("\\/"); if (last_slash_pos != std::string::npos) filename = filename.substr(last_slash_pos + 1); #if defined(__ANDROID__) __android_log_print(ANDROID_LOG_ERROR, "kaliber", "%d [%s:%d] %s", - verbosity_level_, filename.c_str(), line_, text.c_str()); + verbosity_level_, filename.c_str(), line_, + message.c_str()); +#elif defined(_WIN32) + OutputDebugStringA( + std::format("{} [{}:{}] {}", verbosity_level_, filename, line_, message) + .c_str()); #else printf("%d [%s:%d] %s", verbosity_level_, filename.c_str(), line_, - text.c_str()); + message.c_str()); #endif } diff --git a/src/base/vecmath.h b/src/base/vecmath.h index ce4164a..496b516 100644 --- a/src/base/vecmath.h +++ b/src/base/vecmath.h @@ -1247,8 +1247,8 @@ class Matrix4 { T fov_aspect, T width, T height, - T near, - T far) { + T near_plane, + T far_plane) { // Calc x and y scale from FOV. T scale = T(2.0) / @@ -1257,8 +1257,8 @@ class Matrix4 { T x_scale = y_scale / (width / height); _M_SET_ROW(0, x_scale / T(2.0), 0, 0, 0); _M_SET_ROW(1, 0, (-y_scale) / T(2.0), 0, 0); - _M_SET_ROW(2, 0, 0, far / (far - near), 1); - _M_SET_ROW(3, 0, 0, -near * far / (far - near), 0); + _M_SET_ROW(2, 0, 0, far_plane / (far_plane - near_plane), 1); + _M_SET_ROW(3, 0, 0, -near_plane * far_plane / (far_plane - near_plane), 0); } void CreateTranslation(const Vector3& t) { diff --git a/src/demo/player.cc b/src/demo/player.cc index 5025bf5..f259836 100644 --- a/src/demo/player.cc +++ b/src/demo/player.cc @@ -2,6 +2,7 @@ #include "base/interpolation.h" #include "base/log.h" +#include "base/vecmath.h" #include "engine/asset/font.h" #include "engine/engine.h" #include "engine/input_event.h" diff --git a/src/engine/BUILD.gn b/src/engine/BUILD.gn index 90c29e2..21efc8e 100644 --- a/src/engine/BUILD.gn +++ b/src/engine/BUILD.gn @@ -59,14 +59,16 @@ source_set("engine") { "sound_player.h", ] - ldflags = [] libs = [] + if (target_os == "linux" || target_os == "win") { + sources += [ "platform/asset_file_generic.cc" ] + } + if (target_os == "linux") { sources += [ "audio/audio_sink_alsa.cc", "audio/audio_sink_alsa.h", - "platform/asset_file_linux.cc", "platform/platform_linux.cc", "renderer/opengl/renderer_opengl_linux.cc", "renderer/vulkan/renderer_vulkan_linux.cc", @@ -80,6 +82,22 @@ source_set("engine") { ] } + if (target_os == "win") { + sources += [ + "audio/audio_sink_null.h", + "platform/platform_win.cc", + "renderer/opengl/renderer_opengl_win.cc", + "renderer/vulkan/renderer_vulkan_win.cc", + "renderer/vulkan/vulkan_context_win.cc", + ] + + libs = [ + "gdi32.lib", # Graphics + "user32.lib", # Win32 API core functionality. + "opengl32.lib", + ] + } + deps = [ "//assets/engine", "//src/base", diff --git a/src/engine/asset/image.cc b/src/engine/asset/image.cc index dcf7f94..ae289e5 100644 --- a/src/engine/asset/image.cc +++ b/src/engine/asset/image.cc @@ -12,12 +12,7 @@ #include "third_party/texture_compressor/texture_compressor.h" // Use aligned memory for SIMD in texture compressor. -#define STB_IMAGE_IMPLEMENTATION #define STBI_NO_STDIO -#define STBI_MALLOC(sz) base::AlignedAlloc(sz, 16) -#define STBI_REALLOC_SIZED(p, oldsz, newsz) \ - base::AlignedRealloc(p, oldsz, newsz, 16) -#define STBI_FREE(p) base::AlignedFree(p) #include "third_party/stb/stb_image.h" using namespace base; diff --git a/src/engine/asset/sound.cc b/src/engine/asset/sound.cc index 32aea30..436cc50 100644 --- a/src/engine/asset/sound.cc +++ b/src/engine/asset/sound.cc @@ -3,11 +3,8 @@ #include #include "base/log.h" -#define MINIMP3_ONLY_MP3 -#define MINIMP3_ONLY_SIMD #define MINIMP3_FLOAT_OUTPUT #define MINIMP3_NO_STDIO -#define MINIMP3_IMPLEMENTATION #include "engine/engine.h" #include "engine/platform/asset_file.h" #include "third_party/minimp3/minimp3_ex.h" diff --git a/src/engine/audio/audio_mixer.cc b/src/engine/audio/audio_mixer.cc index ac8c27b..71e2ba5 100644 --- a/src/engine/audio/audio_mixer.cc +++ b/src/engine/audio/audio_mixer.cc @@ -11,6 +11,8 @@ #include "engine/audio/audio_sink_oboe.h" #elif defined(__linux__) #include "engine/audio/audio_sink_alsa.h" +#elif defined(_WIN32) +#include "engine/audio/audio_sink_null.h" #endif using namespace base; @@ -23,6 +25,9 @@ AudioMixer::AudioMixer() audio_sink_{std::make_unique(this)} { #elif defined(__linux__) audio_sink_{std::make_unique(this)} { +#elif defined(_WIN32) + // TODO: Implement AudioSinkWindows + audio_sink_{std::make_unique()} { #endif bool res = audio_sink_->Initialize(); CHECK(res) << "Failed to initialize audio sink."; diff --git a/src/engine/audio/audio_sink_null.h b/src/engine/audio/audio_sink_null.h new file mode 100644 index 0000000..d8c2055 --- /dev/null +++ b/src/engine/audio/audio_sink_null.h @@ -0,0 +1,23 @@ +#ifndef ENGINE_AUDIO_AUDIO_SINK_NULL_H +#define ENGINE_AUDIO_AUDIO_SINK_NULL_H + +#include "engine/audio/audio_sink.h" + +namespace eng { + +class AudioSinkNull final : public AudioSink { + public: + AudioSinkNull() = default; + ~AudioSinkNull() final = default; + + bool Initialize() final { return true; } + + void Suspend() final {} + void Resume() final {} + + size_t GetHardwareSampleRate() final { return 0; } +}; + +} // namespace eng + +#endif // ENGINE_AUDIO_AUDIO_SINK_NULL_H diff --git a/src/engine/platform/asset_file.h b/src/engine/platform/asset_file.h index 3f846ac..8f17e6d 100644 --- a/src/engine/platform/asset_file.h +++ b/src/engine/platform/asset_file.h @@ -7,7 +7,7 @@ #if defined(__ANDROID__) #include #include "third_party/minizip/unzip.h" -#elif defined(__linux__) +#else #include "base/file.h" #endif @@ -34,7 +34,7 @@ class AssetFile { #if defined(__ANDROID__) unzFile archive_ = 0; size_t uncompressed_size_ = 0; -#elif defined(__linux) +#else base::ScopedFILE file_; #endif }; diff --git a/src/engine/platform/asset_file_linux.cc b/src/engine/platform/asset_file_generic.cc similarity index 100% rename from src/engine/platform/asset_file_linux.cc rename to src/engine/platform/asset_file_generic.cc diff --git a/src/engine/platform/platform.h b/src/engine/platform/platform.h index 324d65a..ca4a277 100644 --- a/src/engine/platform/platform.h +++ b/src/engine/platform/platform.h @@ -4,18 +4,15 @@ #include #if defined(__ANDROID__) - #include "../../base/vecmath.h" - struct android_app; struct AInputEvent; struct ANativeWindow; - #elif defined(__linux__) - #include #include - +#elif defined(_WIN32) +#include #endif namespace eng { @@ -28,6 +25,8 @@ class Platform { Platform(android_app* app); #elif defined(__linux__) Platform(); +#elif defined(_WIN32) + Platform(HINSTANCE instance, int cmd_show); #endif ~Platform(); @@ -64,6 +63,9 @@ class Platform { #elif defined(__linux__) Display* GetDisplay(); Window GetWindow(); +#elif defined(_WIN32) + HINSTANCE GetInstance(); + HWND GetWindow(); #endif private: @@ -113,6 +115,14 @@ class Platform { XVisualInfo* GetXVisualInfo(Display* display); +#elif defined(_WIN32) + + HINSTANCE instance_; + HWND wnd_; + int cmd_show_; + + static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + #endif Platform(const Platform&) = delete; diff --git a/src/engine/platform/platform_win.cc b/src/engine/platform/platform_win.cc new file mode 100644 index 0000000..83c8354 --- /dev/null +++ b/src/engine/platform/platform_win.cc @@ -0,0 +1,161 @@ +#include "engine/platform/platform.h" + +#include "base/log.h" +#include "base/vecmath.h" +#include "engine/input_event.h" +#include "engine/platform/platform_observer.h" + +using namespace base; + +namespace eng { + +void KaliberMain(Platform* platform); + +Platform::Platform(HINSTANCE instance, int cmd_show) + : instance_(instance), cmd_show_(cmd_show) { + LOG(0) << "Initializing platform."; + + root_path_ = ".\\"; + data_path_ = ".\\"; + shared_data_path_ = ".\\"; + + char dest[MAX_PATH]; + memset(dest, 0, sizeof(dest)); + if (GetModuleFileNameA(NULL, dest, MAX_PATH) > 0) { + std::string path = dest; + std::size_t last_slash_pos = path.find_last_of('\\'); + if (last_slash_pos != std::string::npos) + path = path.substr(0, last_slash_pos + 1); + + root_path_ = path; + data_path_ = path; + shared_data_path_ = path; + } + + LOG(0) << "Root path: " << root_path_.c_str(); + LOG(0) << "Data path: " << data_path_.c_str(); + LOG(0) << "Shared data path: " << shared_data_path_.c_str(); + + WNDCLASSEXW wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = instance_; + wcex.hIcon = nullptr; + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = nullptr; + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = L"KaliberWndClass"; + wcex.hIconSm = nullptr; + RegisterClassEx(&wcex); +} + +void Platform::CreateMainWindow() { + wnd_ = CreateWindow(L"KaliberWndClass", L"Kaliber", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 800, 1205, nullptr, nullptr, instance_, + this); + CHECK(wnd_); + + ShowWindow(wnd_, cmd_show_); + UpdateWindow(wnd_); +} + +Platform::~Platform() { + LOG(0) << "Shutting down platform."; +} + +void Platform::Update() { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + should_exit_ = true; + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void Platform::Exit() { + should_exit_ = true; +} + +void Platform::Vibrate(int duration) {} + +void Platform::ShowInterstitialAd() {} + +void Platform::ShareFile(const std::string& file_name) {} + +void Platform::SetKeepScreenOn(bool keep_screen_on) {} + +HINSTANCE Platform::GetInstance() { + return instance_; +} + +HWND Platform::GetWindow() { + return wnd_; +} + +LRESULT CALLBACK Platform::WndProc(HWND wnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + auto* platform = + reinterpret_cast(GetWindowLongPtr(wnd, GWL_USERDATA)); + + switch (message) { + case WM_CREATE: + SetWindowLongPtr(wnd, GWL_USERDATA, + (LONG_PTR)(((LPCREATESTRUCT)lparam)->lpCreateParams)); + break; + case WM_SIZE: + platform->observer_->OnWindowResized(LOWORD(lparam), HIWORD(lparam)); + break; + case WM_DESTROY: + platform->observer_->OnWindowDestroyed(); + PostQuitMessage(0); + break; + case WM_ACTIVATEAPP: + if (wparam == TRUE) + platform->observer_->GainedFocus(false); + else + platform->observer_->LostFocus(); + break; + case WM_MOUSEMOVE: + if (wparam == MK_LBUTTON) { + Vector2f v(MAKEPOINTS(lparam).x, MAKEPOINTS(lparam).y); + auto input_event = + std::make_unique(InputEvent::kDrag, 0, v); + platform->observer_->AddInputEvent(std::move(input_event)); + } + break; + case WM_LBUTTONDOWN: { + Vector2f v(MAKEPOINTS(lparam).x, MAKEPOINTS(lparam).y); + auto input_event = + std::make_unique(InputEvent::kDragStart, 0, v); + platform->observer_->AddInputEvent(std::move(input_event)); + } break; + case WM_LBUTTONUP: { + Vector2f v(MAKEPOINTS(lparam).x, MAKEPOINTS(lparam).y); + auto input_event = + std::make_unique(InputEvent::kDragEnd, 0, v); + platform->observer_->AddInputEvent(std::move(input_event)); + } break; + default: + return DefWindowProc(wnd, message, wparam, lparam); + } + return 0; +} + +} // namespace eng + +int WINAPI WinMain(HINSTANCE instance, + HINSTANCE prev_instance, + PSTR cmd_line, + int cmd_show) { + eng::Platform platform(instance, cmd_show); + eng::KaliberMain(&platform); + return 0; +} diff --git a/src/engine/renderer/opengl/opengl.h b/src/engine/renderer/opengl/opengl.h index 11ec572..8963538 100644 --- a/src/engine/renderer/opengl/opengl.h +++ b/src/engine/renderer/opengl/opengl.h @@ -2,21 +2,33 @@ #define ENGINE_RENDERER_OPENGL_OPENGL_H #if defined(__ANDROID__) + // Use the modified Khronos header from ndk-helper. This gives access to // additional functionality the drivers may expose but which the system headers // do not. #include "third_party/android/gl3stub.h" + #include #elif defined(__linux__) + #include "third_party/glew/glew.h" #include "third_party/glew/glxew.h" +#elif defined(_WIN32) + +#define GLEW_STATIC +#include "third_party/glew/glew.h" +#include "third_party/glew/wglew.h" + +#endif + +#if defined(__linux__) || defined(_WIN32) + // Define the missing format for the etc1 #ifndef GL_ETC1_RGB8_OES #define GL_ETC1_RGB8_OES 0x8D64 #endif - #endif #endif // ENGINE_RENDERER_OPENGL_OPENGL_H diff --git a/src/engine/renderer/opengl/renderer_opengl.h b/src/engine/renderer/opengl/renderer_opengl.h index 02676ad..a6cce5b 100644 --- a/src/engine/renderer/opengl/renderer_opengl.h +++ b/src/engine/renderer/opengl/renderer_opengl.h @@ -122,11 +122,15 @@ class RendererOpenGL final : public Renderer { int screen_height_ = 0; #if defined(__ANDROID__) - ANativeWindow* window_; + ANativeWindow* window_ = nullptr; #elif defined(__linux__) Display* display_ = NULL; Window window_ = 0; GLXContext glx_context_ = NULL; +#elif defined(_WIN32) + HWND wnd_ = nullptr; + HDC dc_ = nullptr; + HGLRC glrc_ = nullptr; #endif bool InitCommon(); diff --git a/src/engine/renderer/opengl/renderer_opengl_win.cc b/src/engine/renderer/opengl/renderer_opengl_win.cc new file mode 100644 index 0000000..a7eed2c --- /dev/null +++ b/src/engine/renderer/opengl/renderer_opengl_win.cc @@ -0,0 +1,79 @@ +#include "engine/renderer/opengl/renderer_opengl.h" + +#include "base/log.h" +#include "engine/platform/platform.h" + +namespace eng { + +bool RendererOpenGL::Initialize(Platform* platform) { + LOG(0) << "Initializing renderer."; + + wnd_ = platform->GetWindow(); + dc_ = GetDC(wnd_); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | // format must support window + PFD_SUPPORT_OPENGL | // format must support OpenGL + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, // color depth + // Color bits ignored + 0, 0, 0, 0, 0, 0, + 0, // alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + // Accumulation bits ignored + 0, 0, 0, 0, + 24, // z-buffer + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, + 0, // reserved + // layer masks ignored + 0, 0, 0}; + int pf = ChoosePixelFormat(dc_, &pfd); + if (pf == 0) { + LOG(0) << "ChoosePixelFormat failed."; + return false; + } + SetPixelFormat(dc_, pf, &pfd); + + glrc_ = wglCreateContext(dc_); + wglMakeCurrent(dc_, glrc_); + + if (GLEW_OK != glewInit()) { + LOG(0) << "Couldn't initialize OpenGL extension wrangler."; + return false; + } + + RECT rect; + GetClientRect(platform->GetWindow(), &rect); + OnWindowResized(rect.right, rect.bottom); + + return InitCommon(); +} + +void RendererOpenGL::OnDestroy() {} + +void RendererOpenGL::Shutdown() { + LOG(0) << "Shutting down renderer."; + is_initialized_ = false; + if (dc_ && glrc_) { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(glrc_); + ReleaseDC(wnd_, dc_); + dc_ = nullptr; + glrc_ = nullptr; + } +} + +void RendererOpenGL::Present() { + SwapBuffers(dc_); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + active_shader_id_ = 0; + active_texture_id_ = 0; + fps_++; +} + +} // namespace eng diff --git a/src/engine/renderer/vulkan/renderer_vulkan.cc b/src/engine/renderer/vulkan/renderer_vulkan.cc index 999f747..fce8d84 100644 --- a/src/engine/renderer/vulkan/renderer_vulkan.cc +++ b/src/engine/renderer/vulkan/renderer_vulkan.cc @@ -379,7 +379,7 @@ RendererVulkan::~RendererVulkan() { } void RendererVulkan::OnWindowResized(int width, int height) { - context_.ResizeWindow(width, height); + context_.ResizeSurface(width, height); } int RendererVulkan::GetScreenWidth() const { @@ -1041,7 +1041,7 @@ void RendererVulkan::Shutdown() { current_staging_buffer_ = 0; staging_buffer_used_ = false; - context_.DestroyWindow(); + context_.DestroySurface(); context_.Shutdown(); glslang::FinalizeProcess(); diff --git a/src/engine/renderer/vulkan/renderer_vulkan_android.cc b/src/engine/renderer/vulkan/renderer_vulkan_android.cc index 51f29a2..3078ea9 100644 --- a/src/engine/renderer/vulkan/renderer_vulkan_android.cc +++ b/src/engine/renderer/vulkan/renderer_vulkan_android.cc @@ -17,7 +17,7 @@ bool RendererVulkan::Initialize(Platform* platform) { LOG(0) << "Failed to initialize Vulkan context."; return false; } - if (!context_.CreateWindow(platform->GetWindow(), width, height)) { + if (!context_.CreateSurface(platform->GetWindow(), width, height)) { LOG(0) << "Vulkan context failed to create window."; return false; } diff --git a/src/engine/renderer/vulkan/renderer_vulkan_linux.cc b/src/engine/renderer/vulkan/renderer_vulkan_linux.cc index 9db7491..0662ce0 100644 --- a/src/engine/renderer/vulkan/renderer_vulkan_linux.cc +++ b/src/engine/renderer/vulkan/renderer_vulkan_linux.cc @@ -15,8 +15,8 @@ bool RendererVulkan::Initialize(Platform* platform) { LOG(0) << "Failed to initialize Vulkan context."; return false; } - if (!context_.CreateWindow(platform->GetDisplay(), platform->GetWindow(), - xwa.width, xwa.height)) { + if (!context_.CreateSurface(platform->GetDisplay(), platform->GetWindow(), + xwa.width, xwa.height)) { LOG(0) << "Vulkan context failed to create window."; return false; } diff --git a/src/engine/renderer/vulkan/renderer_vulkan_win.cc b/src/engine/renderer/vulkan/renderer_vulkan_win.cc new file mode 100644 index 0000000..f89e453 --- /dev/null +++ b/src/engine/renderer/vulkan/renderer_vulkan_win.cc @@ -0,0 +1,27 @@ +#include "engine/renderer/vulkan/renderer_vulkan.h" + +#include "base/log.h" +#include "engine/platform/platform.h" + +namespace eng { + +bool RendererVulkan::Initialize(Platform* platform) { + LOG(0) << "Initializing renderer."; + + RECT rect; + GetClientRect(platform->GetWindow(), &rect); + + if (!context_.Initialize()) { + LOG(0) << "Failed to initialize Vulkan context."; + return false; + } + if (!context_.CreateSurface(platform->GetInstance(), platform->GetWindow(), + rect.right, rect.bottom)) { + LOG(0) << "Vulkan context failed to create window."; + return false; + } + + return InitializeInternal(); +} + +} // namespace eng diff --git a/src/engine/renderer/vulkan/vulkan_context.cc b/src/engine/renderer/vulkan/vulkan_context.cc index 02f96ef..bba1407 100644 --- a/src/engine/renderer/vulkan/vulkan_context.cc +++ b/src/engine/renderer/vulkan/vulkan_context.cc @@ -703,7 +703,7 @@ bool VulkanContext::InitializeQueues(VkSurfaceKHR surface) { #if defined(__ANDROID__) VkFormat desired_format = VK_FORMAT_R8G8B8A8_UNORM; -#elif defined(__linux__) +#elif defined(__linux__) || defined(_WIN32) VkFormat desired_format = VK_FORMAT_B8G8R8A8_UNORM; #endif @@ -797,13 +797,13 @@ bool VulkanContext::CreateSemaphores() { return true; } -void VulkanContext::ResizeWindow(int width, int height) { +void VulkanContext::ResizeSurface(int width, int height) { window_.width = width; window_.height = height; UpdateSwapChain(&window_); } -void VulkanContext::DestroyWindow() { +void VulkanContext::DestroySurface() { CleanUpSwapChain(&window_); vkDestroySurfaceKHR(instance_, window_.surface, nullptr); } diff --git a/src/engine/renderer/vulkan/vulkan_context.h b/src/engine/renderer/vulkan/vulkan_context.h index 5537483..c8e7b91 100644 --- a/src/engine/renderer/vulkan/vulkan_context.h +++ b/src/engine/renderer/vulkan/vulkan_context.h @@ -24,13 +24,15 @@ class VulkanContext { void Shutdown(); #if defined(__ANDROID__) - bool CreateWindow(ANativeWindow* window, int width, int height); + bool CreateSurface(ANativeWindow* window, int width, int height); #elif defined(__linux__) - bool CreateWindow(Display* display, ::Window window, int width, int height); + bool CreateSurface(Display* display, ::Window window, int width, int height); +#elif defined(_WIN32) + bool CreateSurface(HINSTANCE hInstance, HWND hWnd, int width, int height); #endif - void ResizeWindow(int width, int height); - void DestroyWindow(); + void ResizeSurface(int width, int height); + void DestroySurface(); VkFramebuffer GetFramebuffer(); diff --git a/src/engine/renderer/vulkan/vulkan_context_android.cc b/src/engine/renderer/vulkan/vulkan_context_android.cc index 0d11b44..0d239ca 100644 --- a/src/engine/renderer/vulkan/vulkan_context_android.cc +++ b/src/engine/renderer/vulkan/vulkan_context_android.cc @@ -8,7 +8,9 @@ const char* VulkanContext::GetPlatformSurfaceExtension() const { return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; } -bool VulkanContext::CreateWindow(ANativeWindow* window, int width, int height) { +bool VulkanContext::CreateSurface(ANativeWindow* window, + int width, + int height) { VkAndroidSurfaceCreateInfoKHR surface_info; surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; surface_info.pNext = nullptr; diff --git a/src/engine/renderer/vulkan/vulkan_context_linux.cc b/src/engine/renderer/vulkan/vulkan_context_linux.cc index 586b7dc..ea837e2 100644 --- a/src/engine/renderer/vulkan/vulkan_context_linux.cc +++ b/src/engine/renderer/vulkan/vulkan_context_linux.cc @@ -8,10 +8,10 @@ const char* VulkanContext::GetPlatformSurfaceExtension() const { return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; } -bool VulkanContext::CreateWindow(Display* display, - ::Window window, - int width, - int height) { +bool VulkanContext::CreateSurface(Display* display, + ::Window window, + int width, + int height) { VkXlibSurfaceCreateInfoKHR surface_info; surface_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; surface_info.pNext = nullptr; diff --git a/src/engine/renderer/vulkan/vulkan_context_win.cc b/src/engine/renderer/vulkan/vulkan_context_win.cc new file mode 100644 index 0000000..9031478 --- /dev/null +++ b/src/engine/renderer/vulkan/vulkan_context_win.cc @@ -0,0 +1,43 @@ +#include "engine/renderer/vulkan/vulkan_context.h" + +#include "base/log.h" + +namespace eng { + +const char* VulkanContext::GetPlatformSurfaceExtension() const { + return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; +} + +bool VulkanContext::CreateSurface(HINSTANCE hInstance, + HWND hWnd, + int width, + int height) { + VkWin32SurfaceCreateInfoKHR create_info; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.pNext = nullptr; + create_info.flags = 0; + create_info.hinstance = hInstance; + create_info.hwnd = hWnd; + + VkSurfaceKHR surface; + VkResult err = + vkCreateWin32SurfaceKHR(instance_, &create_info, nullptr, &surface); + if (err != VK_SUCCESS) { + LOG(0) << "vkCreateWin32SurfaceKHR failed with error " + << std::to_string(err); + return false; + } + + if (!queues_initialized_ && !InitializeQueues(surface)) + return false; + + window_.surface = surface; + window_.width = width; + window_.height = height; + if (!UpdateSwapChain(&window_)) + return false; + + return true; +} + +} // namespace eng diff --git a/src/hello_world/BUILD.gn b/src/hello_world/BUILD.gn index ff4555c..29e1bea 100644 --- a/src/hello_world/BUILD.gn +++ b/src/hello_world/BUILD.gn @@ -1,7 +1,5 @@ executable("hello_world") { - sources = [ - "hello_world.cc", - ] + sources = [ "hello_world.cc" ] deps = [ "//src/base", diff --git a/src/third_party/BUILD.gn b/src/third_party/BUILD.gn index 4ad2dcf..58337a9 100644 --- a/src/third_party/BUILD.gn +++ b/src/third_party/BUILD.gn @@ -7,6 +7,10 @@ config("third_party_config") { if (target_os == "linux") { defines = [ "VK_USE_PLATFORM_XLIB_KHR" ] } + + if (target_os == "win") { + defines = [ "VK_USE_PLATFORM_WIN32_KHR" ] + } } source_set("third_party") { @@ -53,12 +57,13 @@ source_set("third_party") { "glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp", "glslang/glslang/MachineIndependent/propagateNoContraction.cpp", "glslang/glslang/MachineIndependent/reflection.cpp", - "glslang/glslang/OSDependent/Unix/ossource.cpp", "jsoncpp/json.h", "jsoncpp/jsoncpp.cpp", + "minimp3/minimp3.cc", "minimp3/minimp3.h", "minimp3/minimp3_ex.h", "spirv-reflect/spirv_reflect.c", + "stb/stb_image.cc", "stb/stb_image.h", "stb/stb_truetype.h", "texture_compressor/dxt_encoder.cc", @@ -74,17 +79,34 @@ source_set("third_party") { "volk/volk.c", ] - defines = [ "VMA_STATIC_VULKAN_FUNCTIONS=1" ] + defines = [ + "VMA_STATIC_VULKAN_FUNCTIONS=1", + "GLEW_STATIC", + ] - if (target_os == "linux") { + if (target_os == "linux" || target_os == "win") { sources += [ "glew/glew.c", "glew/glew.h", + ] + } + + if (target_os == "linux") { + sources += [ "glew/glxew.h", + "glslang/glslang/OSDependent/Unix/ossource.cpp", + ] + } + + if (target_os == "win") { + sources += [ + "glew/wglew.h", + "glslang/glslang/OSDependent/Windows/ossource.cpp", ] } configs -= [ "//build:warnings" ] + configs += [ "//build:third_party_warnings" ] deps = [] } diff --git a/src/third_party/glslang/glslang/MachineIndependent/Constant.cpp b/src/third_party/glslang/glslang/MachineIndependent/Constant.cpp index 5fc61db..a4d80bd 100644 --- a/src/third_party/glslang/glslang/MachineIndependent/Constant.cpp +++ b/src/third_party/glslang/glslang/MachineIndependent/Constant.cpp @@ -213,8 +213,8 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* right case EbtInt64: if (rightUnionArray[i] == 0ll) newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll); - else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)-0x8000000000000000ll) - newConstArray[i].setI64Const((long long)-0x8000000000000000ll); + else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)0x8000000000000000ll) + newConstArray[i].setI64Const((long long)0x8000000000000000ll); else newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const()); break; diff --git a/src/third_party/minimp3/minimp3.cc b/src/third_party/minimp3/minimp3.cc new file mode 100644 index 0000000..b782bd1 --- /dev/null +++ b/src/third_party/minimp3/minimp3.cc @@ -0,0 +1,6 @@ +#define MINIMP3_ONLY_MP3 +#define MINIMP3_ONLY_SIMD +#define MINIMP3_FLOAT_OUTPUT +#define MINIMP3_NO_STDIO +#define MINIMP3_IMPLEMENTATION +#include "third_party/minimp3/minimp3_ex.h" diff --git a/src/third_party/stb/stb_image.cc b/src/third_party/stb/stb_image.cc new file mode 100644 index 0000000..770f613 --- /dev/null +++ b/src/third_party/stb/stb_image.cc @@ -0,0 +1,10 @@ +#include "base/mem.h" + +// Use aligned memory for SIMD in texture compressor. +#define STB_IMAGE_IMPLEMENTATION +#define STBI_NO_STDIO +#define STBI_MALLOC(sz) base::AlignedAlloc(sz, 16) +#define STBI_REALLOC_SIZED(p, oldsz, newsz) \ + base::AlignedRealloc(p, oldsz, newsz, 16) +#define STBI_FREE(p) base::AlignedFree(p) +#include "third_party/stb/stb_image.h"