Compare commits
No commits in common. "master" and "release-1.0.4" have entirely different histories.
master
...
release-1.
|
@ -1,2 +1,7 @@
|
||||||
BasedOnStyle: Chromium
|
BasedOnStyle: Chromium
|
||||||
Standard: Cpp11
|
Standard: Cpp11
|
||||||
|
|
||||||
|
MacroBlockBegin: "^\
|
||||||
|
RENDER_COMMAND_BEGIN$"
|
||||||
|
MacroBlockEnd: "^\
|
||||||
|
RENDER_COMMAND_END$"
|
||||||
|
|
|
@ -4,4 +4,8 @@ build/android/*.idsig
|
||||||
build/android/app/.cxx
|
build/android/app/.cxx
|
||||||
build/android/app/build
|
build/android/app/build
|
||||||
build/android/build
|
build/android/build
|
||||||
|
build/linux/demo_x86_64_debug
|
||||||
|
build/linux/demo_x86_64_release
|
||||||
|
build/linux/obj
|
||||||
|
build/linux/woom
|
||||||
out
|
out
|
||||||
|
|
2
.gn
|
@ -1,2 +0,0 @@
|
||||||
# The location of the build configuration file.
|
|
||||||
buildconfig = "//build/BUILDCONFIG.gn"
|
|
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Linux",
|
|
||||||
"includePath": [
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"defines": [
|
|
||||||
"OS_LINUX"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mac",
|
|
||||||
"includePath": [
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"defines": [
|
|
||||||
"OS_MAC"
|
|
||||||
],
|
|
||||||
"macFrameworkPath": [
|
|
||||||
"/System/Library/Frameworks",
|
|
||||||
"/Library/Frameworks"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Win32",
|
|
||||||
"includePath": [
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"defines": [
|
|
||||||
"OS_WIN"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version": 4
|
|
||||||
}
|
|
|
@ -5,13 +5,13 @@
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Debug demo - Linux",
|
"name": "(gdb) Launch",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/out/debug/demo",
|
"program": "${workspaceFolder}/build/linux/demo_x86_64_debug",
|
||||||
"args": [],
|
"args": [],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}/out/debug",
|
"cwd": "${workspaceFolder}/build/linux",
|
||||||
"environment": [],
|
"environment": [],
|
||||||
"console": "externalTerminal",
|
"console": "externalTerminal",
|
||||||
"MIMode": "gdb",
|
"MIMode": "gdb",
|
||||||
|
@ -22,19 +22,6 @@
|
||||||
"ignoreFailures": true
|
"ignoreFailures": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"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",
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,82 +0,0 @@
|
||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"bit": "cpp",
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"chrono": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"codecvt": "cpp",
|
|
||||||
"compare": "cpp",
|
|
||||||
"concepts": "cpp",
|
|
||||||
"condition_variable": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"list": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"set": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"future": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iomanip": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"mutex": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"numbers": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"semaphore": "cpp",
|
|
||||||
"shared_mutex": "cpp",
|
|
||||||
"span": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"stop_token": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"variant": "cpp"
|
|
||||||
}
|
|
||||||
"git.ignoreLimitWarning": true,
|
|
||||||
|
|
||||||
// These settings should really be in github sync or something, if only it
|
|
||||||
// worked in linux...
|
|
||||||
"debug.openExplorerOnEnd": true,
|
|
||||||
"debug.console.closeOnEnd": true,
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.rulers": [
|
|
||||||
80,
|
|
||||||
],
|
|
||||||
"workbench.editor.closeEmptyGroups": false,
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "Build project",
|
|
||||||
"type": "shell",
|
|
||||||
"linux": {
|
|
||||||
"command": "~/code/work/chromium/src/third_party/ninja/ninja",
|
|
||||||
"problemMatcher": [
|
|
||||||
"$gcc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osx": {
|
|
||||||
"command": "ninja",
|
|
||||||
"problemMatcher": [
|
|
||||||
"$gcc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"windows": {
|
|
||||||
"command": "ninja.exe",
|
|
||||||
"problemMatcher": [
|
|
||||||
"$msCompile"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"args": [
|
|
||||||
"-C",
|
|
||||||
"${workspaceFolder}/out/debug"
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
},
|
|
||||||
"detail": "Build the project with ninja."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
86
README.md
|
@ -1,87 +1,20 @@
|
||||||
# Kaliber
|
|
||||||
|
|
||||||
A simple, cross-platform 2D game engine with OpenGL and Vulkan renderers.
|
A simple, cross-platform 2D game engine with OpenGL and Vulkan renderers.
|
||||||
Supports Linux, Windows and Android platforms.
|
Supports Linux and Android platforms.
|
||||||
This is a personal hobby project. I've published a little game on
|
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)
|
[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.
|
based on this engine. Full game code and assets are included in this repository.
|
||||||
|
#### Building the demo
|
||||||
## Pre-requisites:
|
Linux:
|
||||||
|
|
||||||
GN build system is required for all platforms:\
|
|
||||||
https://gn.googlesource.com/gn/
|
|
||||||
|
|
||||||
## Building from the command-line:
|
|
||||||
|
|
||||||
### All platforms except Android:
|
|
||||||
#### Setup:
|
|
||||||
Generate build files for Ninja in release and debug modes.
|
|
||||||
```text
|
```text
|
||||||
gn gen out/release
|
cd build/linux
|
||||||
gn gen --args='is_debug=true' out/debug
|
make
|
||||||
```
|
```
|
||||||
#### Building and running:
|
Android:
|
||||||
Build all games in release mode and run "hello_world".
|
|
||||||
```text
|
|
||||||
ninja -C out/release
|
|
||||||
./out/release/hello_world
|
|
||||||
```
|
|
||||||
Build "demo" in debug mode and run.
|
|
||||||
```text
|
|
||||||
ninja -C out/debug demo
|
|
||||||
./out/debug/demo
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android:
|
|
||||||
Build "hello_world" in debug mode for all ABIs and install. GN will be run by
|
|
||||||
Gradle so no setup is required. Location of gn and ninja executables can be
|
|
||||||
specified via "gn" and "ninja" properties (-Pgn="path/gn").
|
|
||||||
```text
|
```text
|
||||||
cd build/android
|
cd build/android
|
||||||
./gradlew :app:installHelloWorldAllArchsDebug
|
./gradlew :app:assembleRelease
|
||||||
```
|
```
|
||||||
Build "demo" in debug mode for x86_64 ABI and install. Valid ABI targets are
|
#### Third-party libraries:
|
||||||
Arm7, Arm8, X86, X86_64, AllArchs, ArmOnly, X86Only.
|
|
||||||
```text
|
|
||||||
./gradlew :app:installDemoX86_64Debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate Visual Studio solution:
|
|
||||||
```text
|
|
||||||
gn.exe gen --args="is_debug=true" --ide=vs2022 out\vs
|
|
||||||
devenv out\vs\all.sln
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hello World example:
|
|
||||||
|
|
||||||
Shows a smoothly rotating "Hello World".
|
|
||||||
```cpp
|
|
||||||
class HelloWorld final : public eng::Game {
|
|
||||||
public:
|
|
||||||
bool Initialize() final {
|
|
||||||
eng::Engine::Get().SetImageSource(
|
|
||||||
"hello_world_image",
|
|
||||||
std::bind(&eng::Engine::Print, &eng::Engine::Get(), "Hello World",
|
|
||||||
/*bg_color*/ base::Vector4f(1, 1, 1, 0)));
|
|
||||||
|
|
||||||
hello_world_.Create("hello_world_image").SetVisible(true);
|
|
||||||
animator_.Attach(&hello_world_);
|
|
||||||
animator_.SetRotation(base::PI2f, /*duration*/ 3,
|
|
||||||
std::bind(base::SmootherStep, std::placeholders::_1));
|
|
||||||
animator_.Play(eng::Animator::kRotation, /*loop*/ true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
eng::ImageQuad hello_world_;
|
|
||||||
eng::Animator animator_;
|
|
||||||
};
|
|
||||||
|
|
||||||
GAME_FACTORIES{GAME_CLASS(HelloWorld)};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Third-party libraries:
|
|
||||||
|
|
||||||
[glew](https://github.com/nigels-com/glew),
|
[glew](https://github.com/nigels-com/glew),
|
||||||
[jsoncpp](https://github.com/open-source-parsers/jsoncpp),
|
[jsoncpp](https://github.com/open-source-parsers/jsoncpp),
|
||||||
[minimp3](https://github.com/lieff/minimp3),
|
[minimp3](https://github.com/lieff/minimp3),
|
||||||
|
@ -93,5 +26,4 @@ GAME_FACTORIES{GAME_CLASS(HelloWorld)};
|
||||||
[spirv-reflect](https://github.com/KhronosGroup/SPIRV-Reflect),
|
[spirv-reflect](https://github.com/KhronosGroup/SPIRV-Reflect),
|
||||||
[vma](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator),
|
[vma](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator),
|
||||||
[vulkan-sdk](https://vulkan.lunarg.com),
|
[vulkan-sdk](https://vulkan.lunarg.com),
|
||||||
[volk](https://github.com/zeux/volk),
|
[volk](https://github.com/zeux/volk)
|
||||||
[imgui](https://github.com/ocornut/imgui)
|
|
||||||
|
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 507 B |
|
@ -1,44 +0,0 @@
|
||||||
copy("demo") {
|
|
||||||
sources = [
|
|
||||||
"bead.png",
|
|
||||||
"boss_explosion.mp3",
|
|
||||||
"boss_intro.mp3",
|
|
||||||
"Boss_ok_lvl2.png",
|
|
||||||
"Boss_ok_lvl3.png",
|
|
||||||
"Boss_ok.png",
|
|
||||||
"chromatic_aberration.glsl_fragment",
|
|
||||||
"chromatic_aberration.glsl_vertex",
|
|
||||||
"enemy_anims_01_frames_ok.png",
|
|
||||||
"enemy_anims_02_frames_ok.png",
|
|
||||||
"enemy_anims_blast_ok.png",
|
|
||||||
"enemy_anims_flare_ok.png",
|
|
||||||
"enemy_ray_ok.png",
|
|
||||||
"enemy_target_single_ok.png",
|
|
||||||
"explosion.mp3",
|
|
||||||
"Game_2_Boss.mp3",
|
|
||||||
"Game_2_Main.mp3",
|
|
||||||
"hit.mp3",
|
|
||||||
"laser.mp3",
|
|
||||||
"menu_click.mp3",
|
|
||||||
"menu_icons.png",
|
|
||||||
"no_nuke.mp3",
|
|
||||||
"nuke_frames.png",
|
|
||||||
"nuke_pack_OK.png",
|
|
||||||
"nuke.mp3",
|
|
||||||
"PixelCaps!.ttf",
|
|
||||||
"powerup-pick.mp3",
|
|
||||||
"powerup-spawn.mp3",
|
|
||||||
"renderer_logo.png",
|
|
||||||
"shield.mp3",
|
|
||||||
"sky_without_nebula.glsl_fragment",
|
|
||||||
"sky_without_nebula.glsl_vertex",
|
|
||||||
"sky.glsl_fragment",
|
|
||||||
"sky.glsl_vertex",
|
|
||||||
"stealth.mp3",
|
|
||||||
"woom_enemy_shield.png",
|
|
||||||
"woom_logo_start_frames_01.png",
|
|
||||||
"woom_logo_start_frames_02-03.png",
|
|
||||||
]
|
|
||||||
|
|
||||||
outputs = ["$root_out_dir/assets/demo/{{source_file_part}}"]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 332 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -1,13 +0,0 @@
|
||||||
copy("engine") {
|
|
||||||
sources = [
|
|
||||||
"RobotoMono-Regular.ttf",
|
|
||||||
"imgui.glsl_fragment",
|
|
||||||
"imgui.glsl_vertex",
|
|
||||||
"pass_through.glsl_fragment",
|
|
||||||
"pass_through.glsl_vertex",
|
|
||||||
"solid.glsl_fragment",
|
|
||||||
"solid.glsl_vertex",
|
|
||||||
]
|
|
||||||
|
|
||||||
outputs = [ "$root_out_dir/assets/engine/{{source_file_part}}" ]
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#ifdef GL_ES
|
|
||||||
precision mediump float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IN(0) vec2 tex_coord_0;
|
|
||||||
IN(1) vec4 color;
|
|
||||||
|
|
||||||
UNIFORM_BEGIN
|
|
||||||
UNIFORM_V(mat4 projection)
|
|
||||||
UNIFORM_END
|
|
||||||
|
|
||||||
SAMPLER(0, sampler2D texture_0)
|
|
||||||
|
|
||||||
FRAG_COLOR_OUT(frag_color)
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
FRAG_COLOR(frag_color) = TEXTURE(texture_0, tex_coord_0) * color;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
IN(0) vec2 in_position;
|
|
||||||
IN(1) vec2 in_tex_coord_0;
|
|
||||||
IN(2) vec4 in_color;
|
|
||||||
|
|
||||||
UNIFORM_BEGIN
|
|
||||||
UNIFORM_V(mat4 projection)
|
|
||||||
UNIFORM_END
|
|
||||||
|
|
||||||
OUT(0) vec2 tex_coord_0;
|
|
||||||
OUT(1) vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
tex_coord_0 = in_tex_coord_0;
|
|
||||||
color = in_color;
|
|
||||||
|
|
||||||
gl_Position = PARAM(projection) * vec4(in_position, 0.0, 1.0);
|
|
||||||
}
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 255 KiB |
280
build/BUILD.gn
|
@ -1,280 +0,0 @@
|
||||||
config("default") {
|
|
||||||
if (is_android) {
|
|
||||||
defines = [ "OS_ANDROID" ]
|
|
||||||
} else if (is_ios) {
|
|
||||||
defines = [ "OS_IOS" ]
|
|
||||||
} else if (is_linux) {
|
|
||||||
defines = [ "OS_LINUX" ]
|
|
||||||
} else if (is_mac) {
|
|
||||||
defines = [ "OS_MAC" ]
|
|
||||||
} else if (is_win) {
|
|
||||||
defines = [ "OS_WIN" ]
|
|
||||||
}
|
|
||||||
if (is_ios || is_mac) {
|
|
||||||
defines += [ "OS_APPLE" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
cflags_cc = []
|
|
||||||
if (is_win) {
|
|
||||||
cflags_cc += [ "/std:c++20" ]
|
|
||||||
} else {
|
|
||||||
cflags_cc += [ "-std=c++20" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
cflags = []
|
|
||||||
if (is_win) {
|
|
||||||
cflags += [
|
|
||||||
"/permissive-", # Don't enable nonconforming code to compile.
|
|
||||||
"/EHsc", # Use C++ exception handling.
|
|
||||||
"/Gd", # __cdecl calling convention.
|
|
||||||
"/utf-8", # Set Source and Executable character set.
|
|
||||||
]
|
|
||||||
defines += [
|
|
||||||
# Windows headers from MS and third party expects these to be set.
|
|
||||||
"_WINDOWS",
|
|
||||||
"_UNICODE",
|
|
||||||
"UNICODE",
|
|
||||||
|
|
||||||
# Don't want MS specific extensions for C stdlib functions.
|
|
||||||
"_CRT_SECURE_NO_WARNINGS",
|
|
||||||
|
|
||||||
# Clean up the Windows headers.
|
|
||||||
"WIN32_LEAN_AND_MEAN",
|
|
||||||
"NOMINMAX",
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
# cflags += [
|
|
||||||
# "-fstrict-aliasing", # Optimizations allowed for type aliasing.
|
|
||||||
# "-fvisibility=hidden", # Only let symbols be visible inside it's own
|
|
||||||
# # library.
|
|
||||||
# ]
|
|
||||||
# cflags_cc += [
|
|
||||||
# "-fvisibility-inlines-hidden", # Inline methods may not generate symbols.
|
|
||||||
# # "-stdlib=libc++",
|
|
||||||
# ]
|
|
||||||
|
|
||||||
if (is_linux) {
|
|
||||||
cflags += [
|
|
||||||
"-fPIC", # Emit position-independent code suitable for dynamic linking.
|
|
||||||
]
|
|
||||||
libs = [
|
|
||||||
"pthread", # Use the POSIX thread library.
|
|
||||||
]
|
|
||||||
} else if (is_mac) {
|
|
||||||
asmflags = [
|
|
||||||
"-target",
|
|
||||||
"arm64-apple-macos11",
|
|
||||||
]
|
|
||||||
cflags += [
|
|
||||||
# "-fPIC", # Emit position-independent code suitable for dynamic linking.
|
|
||||||
"-target",
|
|
||||||
"arm64-apple-macos11",
|
|
||||||
]
|
|
||||||
ldflags = [
|
|
||||||
"-target",
|
|
||||||
"arm64-apple-macos11",
|
|
||||||
]
|
|
||||||
} else if (is_android) {
|
|
||||||
cflags += [
|
|
||||||
"--sysroot=$ndk/toolchains/llvm/prebuilt/$ndk_host/sysroot",
|
|
||||||
"-fPIC", # Emit position-independent code suitable for dynamic linking.
|
|
||||||
]
|
|
||||||
ldflags = [ "-static-libstdc++" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Avoid relative paths everywhere.
|
|
||||||
include_dirs = [
|
|
||||||
"//src",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
config("debug") {
|
|
||||||
defines = [ "_DEBUG" ]
|
|
||||||
|
|
||||||
if (is_win) {
|
|
||||||
cflags = [
|
|
||||||
"/GS", # Buffer security checks (buffer overruns).
|
|
||||||
"/sdl", # Enable additional security features and warnings.
|
|
||||||
"/RTC1", # Enable fast run-time checks.
|
|
||||||
"/Od", # Disable optimizations.
|
|
||||||
"/fp:precise", # Floating points are predictable.
|
|
||||||
"/MDd", # Link with multithread debug DLL run-time libraries.
|
|
||||||
"/Z7", # Produce obj files that contain full symbolic debugging
|
|
||||||
# information.
|
|
||||||
]
|
|
||||||
ldflags = [ "/DEBUG:FASTLINK" ] # Reference the debug symbols in the obj
|
|
||||||
# files instead of a PDB file.
|
|
||||||
} else {
|
|
||||||
cflags = [
|
|
||||||
"-g", # Debug symbols.
|
|
||||||
# TODO: This makes it hard to step through code on Linux.
|
|
||||||
# "-Og", # Optimizations that works well with debugger.
|
|
||||||
]
|
|
||||||
|
|
||||||
if (is_unix) {
|
|
||||||
defines += [
|
|
||||||
"_GLIBCXX_DEBUG=1", # Enable asserts in stl headers.
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config("release") {
|
|
||||||
defines = [ "NDEBUG" ]
|
|
||||||
|
|
||||||
if (is_win) {
|
|
||||||
cflags = [
|
|
||||||
"/GL", # Link-time code generation / Whole program optimization.
|
|
||||||
"/Gy", # Allows converting inline functions to separate functions.
|
|
||||||
"/O2", # Optimize for maximum speed.
|
|
||||||
"/Oi", # Enable intrinsics.
|
|
||||||
"/fp:fast", # Floating points are fast.
|
|
||||||
"/MD", # Link with multithread release DLL run-time libraries.
|
|
||||||
|
|
||||||
# "/arch:AVX2", # Minimum CPU architecture.
|
|
||||||
]
|
|
||||||
ldflags = [
|
|
||||||
"/LTCG", # Link time code generation.
|
|
||||||
"/OPT:ICF", # COMDAT (common data) folding, e.g. vtables and big inline
|
|
||||||
# functions that can't be inlined, but also if identical code
|
|
||||||
# happens to be generated though common math operations or
|
|
||||||
# similar.
|
|
||||||
"/OPT:REF", # Eliminate unreferenced code and data.
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
cflags = [
|
|
||||||
"-Ofast", # Full optimization and disregard strict standard compliance.
|
|
||||||
"-fno-math-errno", # Do not check or set errno for invalid input to sqrt
|
|
||||||
# and other math functions.
|
|
||||||
]
|
|
||||||
|
|
||||||
if (is_apple) {
|
|
||||||
ldflags = [ "-dead-strip" ]
|
|
||||||
} else {
|
|
||||||
# Place both data and functions in it's their own sections.
|
|
||||||
# Linker optimization that allows for smaller binaries.
|
|
||||||
cflags += [
|
|
||||||
"-fdata-sections",
|
|
||||||
"-ffunction-sections",
|
|
||||||
]
|
|
||||||
ldflags = [ "-Wl,--gc-sections" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config("warnings") {
|
|
||||||
if (is_win) {
|
|
||||||
cflags = [
|
|
||||||
"/WX", # Treat warnings as errors.
|
|
||||||
"/Wall", # Enable all warnings.
|
|
||||||
|
|
||||||
# Disable the following warnings:
|
|
||||||
"/wd4061", # enumerator in switch with a default case not explicitly
|
|
||||||
# handled by a case label.
|
|
||||||
"/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
|
|
||||||
"/wd4514", # unreferenced inline function has been removed.
|
|
||||||
"/wd4710", # function not inlined
|
|
||||||
"/wd4711", # function selected for automatic inline expansion
|
|
||||||
"/wd4820", # padding added after data member.
|
|
||||||
"/wd5045", # compiler will insert Spectre mitigation for memory load if
|
|
||||||
# switch specified.
|
|
||||||
"/wd4355", # 'this': used in base member initializer list
|
|
||||||
|
|
||||||
# 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'
|
|
||||||
"/wd4245", # conversion from 'int' to 'const unsigned int'
|
|
||||||
"/wd4305", # truncation from 'double' to 'float'.
|
|
||||||
"/wd4365", # conversion, signed/unsigned mismatch.
|
|
||||||
"/wd4722", # destructor never returns, potential memory leak
|
|
||||||
"/wd4702", # unreachable code
|
|
||||||
"/wd4625", # copy constructor was implicitly defined as deleted
|
|
||||||
"/wd4626", # assignment operator was implicitly defined as deleted
|
|
||||||
|
|
||||||
# Possible compiler bug? Needs investigation.
|
|
||||||
"/wd4668", # '__STDC_WANT_SECURE_LIB__' is not defined as a preprocessor
|
|
||||||
# macro, replacing with '0' for '#if/#elif'
|
|
||||||
]
|
|
||||||
|
|
||||||
defines = [
|
|
||||||
"_CRT_NONSTDC_NO_DEPRECATE",
|
|
||||||
"_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING",
|
|
||||||
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
|
|
||||||
]
|
|
||||||
} else if (is_mac) {
|
|
||||||
cflags = [
|
|
||||||
"-Wno-deprecated-declarations",
|
|
||||||
"-Wno-inconsistent-missing-override",
|
|
||||||
"-Wno-pointer-sign",
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
cflags = [
|
|
||||||
# Enable as much warnings as possible.
|
|
||||||
"-Werror", # Make all warnings into errors.
|
|
||||||
"-Wall", # Enable (almost) all warnings.
|
|
||||||
"-Wextra", # Enable even more warnings.
|
|
||||||
|
|
||||||
# forbidden extensions.
|
|
||||||
"-Wvla", # Warn if variable-length array is used in code.
|
|
||||||
|
|
||||||
# Disable the following warnings:
|
|
||||||
"-Wno-unused-parameter",
|
|
||||||
"-Wno-sign-compare",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config("executable") {
|
|
||||||
if (is_android) {
|
|
||||||
ldflags = [
|
|
||||||
"-pie",
|
|
||||||
"-rdynamic",
|
|
||||||
]
|
|
||||||
} else if (is_linux) {
|
|
||||||
ldflags = [
|
|
||||||
"-Wl,-rpath,\$ORIGIN", # Add directory to runtime library search path.
|
|
||||||
]
|
|
||||||
} else if (is_mac) {
|
|
||||||
ldflags = [ "-Wl,-rpath,@loader_path/." ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config("shared_library") {
|
|
||||||
if (is_android) {
|
|
||||||
ldflags = [
|
|
||||||
"-Wl,--build-id=sha1",
|
|
||||||
"-Wl,--no-rosegment",
|
|
||||||
"-Wl,--fatal-warnings",
|
|
||||||
"-Wl,--no-undefined",
|
|
||||||
"-Qunused-arguments",
|
|
||||||
"-u ANativeActivity_onCreate",
|
|
||||||
]
|
|
||||||
} else if (is_linux) {
|
|
||||||
ldflags = [
|
|
||||||
"-Wl,-rpath,\$ORIGIN", # Add directory to runtime library search path.
|
|
||||||
]
|
|
||||||
} else if (is_mac) {
|
|
||||||
ldflags = [ "-Wl,-rpath,@loader_path/." ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: This needs some more investigation.
|
|
||||||
# Is it possible to avoid setting it and rely on defaults?
|
|
||||||
# Some tools will be console apps while games will be gui apps.
|
|
||||||
# GLFW seems to be using console in debug mode and gui in release mode.
|
|
||||||
if (is_win) {
|
|
||||||
config("win_gui") {
|
|
||||||
ldflags = [ "/SUBSYSTEM:WINDOWS" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
config("win_console") {
|
|
||||||
ldflags = [ "/SUBSYSTEM:CONSOLE" ]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
declare_args() {
|
|
||||||
is_debug = false
|
|
||||||
|
|
||||||
# Note that this uses the 'cc' and 'c++' links that should map to GCC on linux
|
|
||||||
# systems and clang on macs.
|
|
||||||
ar = "ar"
|
|
||||||
cc = "cc"
|
|
||||||
cxx = "c++"
|
|
||||||
|
|
||||||
ndk = ""
|
|
||||||
ndk_api = 24
|
|
||||||
}
|
|
||||||
|
|
||||||
# Platform detection
|
|
||||||
if (target_os == "") {
|
|
||||||
target_os = host_os
|
|
||||||
}
|
|
||||||
if (current_os == "") {
|
|
||||||
current_os = target_os
|
|
||||||
}
|
|
||||||
|
|
||||||
is_android = current_os == "android"
|
|
||||||
is_ios = current_os == "ios"
|
|
||||||
is_linux = current_os == "linux"
|
|
||||||
is_mac = current_os == "mac"
|
|
||||||
is_win = current_os == "win"
|
|
||||||
|
|
||||||
is_apple = is_mac || is_ios
|
|
||||||
is_desktop = is_mac || is_linux || is_win
|
|
||||||
is_mobile = is_android || is_ios
|
|
||||||
is_unix = is_android || is_linux
|
|
||||||
|
|
||||||
if (target_cpu == "") {
|
|
||||||
if (is_mobile) {
|
|
||||||
target_cpu = "arm64"
|
|
||||||
} else {
|
|
||||||
target_cpu = host_cpu
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (target_cpu == "x86_64") {
|
|
||||||
target_cpu = "x64"
|
|
||||||
}
|
|
||||||
if (current_cpu == "") {
|
|
||||||
current_cpu = target_cpu
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_android) {
|
|
||||||
assert(ndk != "", "NDK path argument is empty")
|
|
||||||
|
|
||||||
ndk_host = ""
|
|
||||||
ndk_target = ""
|
|
||||||
|
|
||||||
if (host_os == "linux") {
|
|
||||||
ndk_host = "linux-x86_64"
|
|
||||||
} else if (host_os == "mac") {
|
|
||||||
ndk_host = "darwin-x86_64"
|
|
||||||
} else if (host_os == "win") {
|
|
||||||
ndk_host = "windows-x86_64"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_cpu == "arm64") {
|
|
||||||
ndk_target = "aarch64-linux-android"
|
|
||||||
} else if (target_cpu == "arm") {
|
|
||||||
ndk_target = "armv7a-linux-androideabi"
|
|
||||||
} else if (target_cpu == "x64") {
|
|
||||||
ndk_target = "x86_64-linux-android"
|
|
||||||
} else if (target_cpu == "x86") {
|
|
||||||
ndk_target = "i686-linux-android"
|
|
||||||
}
|
|
||||||
|
|
||||||
_prefix = "$ndk/toolchains/llvm/prebuilt/$ndk_host/bin"
|
|
||||||
if (host_os == "win") {
|
|
||||||
ar = "$_prefix/llvm-ar.exe"
|
|
||||||
cc = "$_prefix/clang.exe --target=$ndk_target$ndk_api -fno-addrsig"
|
|
||||||
cxx = "$_prefix/clang++.exe --target=$ndk_target$ndk_api -fno-addrsig"
|
|
||||||
} else {
|
|
||||||
ar = "$_prefix/llvm-ar"
|
|
||||||
cc = "$_prefix/$ndk_target$ndk_api-clang"
|
|
||||||
cxx = "$_prefix/$ndk_target$ndk_api-clang++"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set defaults
|
|
||||||
_default_config = [
|
|
||||||
"//build:default",
|
|
||||||
"//build:warnings",
|
|
||||||
]
|
|
||||||
|
|
||||||
if (is_debug) {
|
|
||||||
_default_config += [ "//build:debug" ]
|
|
||||||
} else {
|
|
||||||
_default_config += [ "//build:release" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
set_defaults("executable") {
|
|
||||||
configs = [ "//build:executable" ] + _default_config
|
|
||||||
}
|
|
||||||
set_defaults("static_library") {
|
|
||||||
configs = _default_config
|
|
||||||
}
|
|
||||||
set_defaults("shared_library") {
|
|
||||||
configs = [ "//build:shared_library" ] + _default_config
|
|
||||||
}
|
|
||||||
set_defaults("source_set") {
|
|
||||||
configs = _default_config
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_win) {
|
|
||||||
set_default_toolchain("//build/toolchain/msvc")
|
|
||||||
} else {
|
|
||||||
# Clang behaves like GCC in most cases.
|
|
||||||
set_default_toolchain("//build/toolchain/gcc")
|
|
||||||
}
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
# common_config
|
||||||
|
add_library(common_config INTERFACE)
|
||||||
|
|
||||||
|
target_compile_options(common_config INTERFACE
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>:-std=c++20>
|
||||||
|
#$<$<NOT:$<CONFIG:DEBUG>>:-Ofast>
|
||||||
|
-Wall
|
||||||
|
-Werror
|
||||||
|
-Wno-unused-but-set-variable
|
||||||
|
-Wno-deprecated-enum-enum-conversion
|
||||||
|
-Wno-unsequenced
|
||||||
|
-Wno-nullability-completeness
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(common_config INTERFACE
|
||||||
|
VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
VMA_STATIC_VULKAN_FUNCTIONS=1
|
||||||
|
$<$<CONFIG:DEBUG>:_DEBUG>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(common_config INTERFACE
|
||||||
|
../../../src
|
||||||
|
../../../src/third_party/glslang
|
||||||
|
../../../src/third_party/vulkan/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# oboe
|
||||||
|
set (OBOE_DIR ../../../src/third_party/oboe)
|
||||||
|
add_subdirectory(${OBOE_DIR} ./oboe-bin)
|
||||||
|
|
||||||
|
# cpufeatures
|
||||||
|
add_library(cpufeatures STATIC ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
|
||||||
|
target_include_directories(cpufeatures PUBLIC ${ANDROID_NDK}/sources/android/cpufeatures)
|
||||||
|
|
||||||
|
# native_app_glue
|
||||||
|
add_library(native_app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
|
||||||
|
target_include_directories(native_app_glue PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue)
|
||||||
|
set_property(TARGET native_app_glue PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
# Export ANativeActivity_onCreate(),
|
||||||
|
# Refer to: https://github.com/android-ndk/ndk/issues/381.
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
|
||||||
|
|
||||||
|
# kaliber
|
||||||
|
project(kaliber)
|
||||||
|
|
||||||
|
add_library(kaliber SHARED
|
||||||
|
../../../src/base/collusion_test.cc
|
||||||
|
../../../src/base/log.cc
|
||||||
|
../../../src/base/task_runner.cc
|
||||||
|
../../../src/base/thread_pool.cc
|
||||||
|
../../../src/base/timer.cc
|
||||||
|
../../../src/demo/credits.cc
|
||||||
|
../../../src/demo/demo.cc
|
||||||
|
../../../src/demo/enemy.cc
|
||||||
|
../../../src/demo/hud.cc
|
||||||
|
../../../src/demo/menu.cc
|
||||||
|
../../../src/demo/player.cc
|
||||||
|
../../../src/demo/sky_quad.cc
|
||||||
|
../../../src/engine/animatable.cc
|
||||||
|
../../../src/engine/animator.cc
|
||||||
|
../../../src/engine/audio/audio_bus.cc
|
||||||
|
../../../src/engine/audio/audio_mixer.cc
|
||||||
|
../../../src/engine/audio/audio_sink_oboe.cc
|
||||||
|
../../../src/engine/audio/sinc_resampler.cc
|
||||||
|
../../../src/engine/drawable.cc
|
||||||
|
../../../src/engine/engine.cc
|
||||||
|
../../../src/engine/font.cc
|
||||||
|
../../../src/engine/image_quad.cc
|
||||||
|
../../../src/engine/image.cc
|
||||||
|
../../../src/engine/mesh.cc
|
||||||
|
../../../src/engine/persistent_data.cc
|
||||||
|
../../../src/engine/platform/asset_file_android.cc
|
||||||
|
../../../src/engine/platform/asset_file.cc
|
||||||
|
../../../src/engine/platform/platform_android.cc
|
||||||
|
../../../src/engine/renderer/geometry.cc
|
||||||
|
../../../src/engine/renderer/opengl/render_command.cc
|
||||||
|
../../../src/engine/renderer/opengl/renderer_opengl_android.cc
|
||||||
|
../../../src/engine/renderer/opengl/renderer_opengl.cc
|
||||||
|
../../../src/engine/renderer/renderer_types.cc
|
||||||
|
../../../src/engine/renderer/shader.cc
|
||||||
|
../../../src/engine/renderer/texture.cc
|
||||||
|
../../../src/engine/renderer/vulkan/renderer_vulkan_android.cc
|
||||||
|
../../../src/engine/renderer/vulkan/renderer_vulkan.cc
|
||||||
|
../../../src/engine/renderer/vulkan/vulkan_context_android.cc
|
||||||
|
../../../src/engine/renderer/vulkan/vulkan_context.cc
|
||||||
|
../../../src/engine/shader_source.cc
|
||||||
|
../../../src/engine/solid_quad.cc
|
||||||
|
../../../src/engine/sound_player.cc
|
||||||
|
../../../src/engine/sound.cc
|
||||||
|
../../../src/third_party/android/gl3stub.c
|
||||||
|
../../../src/third_party/android/GLContext.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/GenericCodeGen/Link.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/attribute.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/Constant.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/Initialize.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/intermOut.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/iomapper.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/limits.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/parseConst.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/reflection.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/Scan.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/MachineIndependent/Versions.cpp
|
||||||
|
../../../src/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp
|
||||||
|
../../../src/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/disassemble.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/doc.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/GlslangToSpv.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/InReadableOrder.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/Logger.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/SpvBuilder.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/SpvPostProcess.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/SPVRemapper.cpp
|
||||||
|
../../../src/third_party/glslang/SPIRV/SpvTools.cpp
|
||||||
|
../../../src/third_party/jsoncpp/jsoncpp.cpp
|
||||||
|
../../../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.c
|
||||||
|
../../../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
|
||||||
|
../../../src/third_party/texture_compressor/texture_compressor.cc
|
||||||
|
../../../src/third_party/vma/vk_mem_alloc.cpp
|
||||||
|
../../../src/third_party/volk/volk.c
|
||||||
|
)
|
||||||
|
|
||||||
|
if (ANDROID_ABI STREQUAL armeabi-v7a)
|
||||||
|
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc)
|
||||||
|
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc)
|
||||||
|
set_source_files_properties(../../../src/third_party/texture_compressor/dxt_encoder_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon)
|
||||||
|
set_source_files_properties(../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (ANDROID_ABI STREQUAL arm64-v8a)
|
||||||
|
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc)
|
||||||
|
target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(kaliber PRIVATE
|
||||||
|
common_config
|
||||||
|
android
|
||||||
|
native_app_glue
|
||||||
|
oboe
|
||||||
|
cpufeatures
|
||||||
|
EGL
|
||||||
|
GLESv2
|
||||||
|
log
|
||||||
|
z
|
||||||
|
)
|
|
@ -1,131 +1,45 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: Utils
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk rootProject.ext.compileSdk
|
compileSdk 33
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
ndkVersion '25.2.9519653'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk rootProject.ext.minSdk
|
applicationId = 'com.woom.game'
|
||||||
targetSdk rootProject.ext.targetSdk
|
minSdk 24
|
||||||
|
targetSdk 33
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments '-DANDROID_STL=c++_static'
|
||||||
|
}
|
||||||
|
}
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters = []
|
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||||
'proguard-rules.pro'
|
'proguard-rules.pro'
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorDimensions 'game', 'arch'
|
|
||||||
|
|
||||||
productFlavors {
|
|
||||||
helloWorld {
|
|
||||||
dimension 'game'
|
|
||||||
applicationId 'com.kaliber.helloworld'
|
|
||||||
resValue "string", "app_name", "Kaliber Hello World"
|
|
||||||
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
||||||
ext {
|
|
||||||
gnTarget = "hello_world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
demo {
|
|
||||||
dimension 'game'
|
|
||||||
applicationId 'com.kaliber.woom'
|
|
||||||
resValue "string", "app_name", "Kaliber Demo"
|
|
||||||
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
||||||
ext {
|
|
||||||
gnTarget = "demo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
woom {
|
|
||||||
dimension 'game'
|
|
||||||
applicationId 'com.woom.game'
|
|
||||||
resValue "string", "app_name", "woom"
|
|
||||||
resValue "string", "interstitial_ad_unit_id", "ca-app-pub-1321063817979967/8373182022"
|
|
||||||
resValue "string", "admob_application_id", "ca-app-pub-1321063817979967~1100949243"
|
|
||||||
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher"]
|
|
||||||
ext {
|
|
||||||
gnTarget = "demo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arm7 {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters = ["armeabi-v7a"]
|
debugSymbolLevel 'FULL'
|
||||||
}
|
|
||||||
}
|
|
||||||
arm8 {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["arm64-v8a"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x86 {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["x86"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x86_64 {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["x86_64"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allArchs {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["armeabi-v7a", "arm64-v8a", "x86_64", "x86"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
armOnly {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["armeabi-v7a", "arm64-v8a"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x86Only {
|
|
||||||
dimension 'arch'
|
|
||||||
ndk {
|
|
||||||
abiFilters = ["x86_64", "x86"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Native library name is same as GN target name.
|
|
||||||
android.productFlavors.each { flavor ->
|
|
||||||
if (flavor.dimension == 'game') {
|
|
||||||
"${flavor.name}" {
|
|
||||||
resValue "string", "lib_name", flavor.ext.gnTarget
|
|
||||||
buildConfigField 'String', 'NATIVE_LIBRARY', "\"${flavor.ext.gnTarget}\""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
version '3.22.1'
|
||||||
|
path 'CMakeLists.txt'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base']
|
java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base']
|
||||||
android.buildTypes.each { buildType ->
|
assets.srcDirs = ['../../../assets']
|
||||||
"${buildType.name}" {
|
|
||||||
assets.srcDirs = ["${utils.getGnOutDir(buildType.name)}/assets"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
namespace 'com.woom.game'
|
||||||
android.buildTypes.each { buildType ->
|
|
||||||
"${buildType.name}" {
|
|
||||||
jniLibs.srcDirs = ["${utils.getGnOutDir(buildType.name)}/jniLibs"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace "com.kaliber.base"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -134,247 +48,3 @@ dependencies {
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'com.google.android.gms:play-services-ads:22.0.0'
|
implementation 'com.google.android.gms:play-services-ads:22.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Tasks for GN build
|
|
||||||
//
|
|
||||||
|
|
||||||
// Generate `args.gn` which is used by GN to take in build arguments.
|
|
||||||
utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch ->
|
|
||||||
task(taskName, type: WriteFileTask) {
|
|
||||||
content = utils.generateGnArgsContent(buildType, arch)
|
|
||||||
target = project.layout.file(provider { new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn') })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run `gn gen` to generate ninja files.
|
|
||||||
utils.addTask('runGnFor') { String taskName, String buildType, String arch ->
|
|
||||||
task(taskName, type: Exec) {
|
|
||||||
dependsOn "generateGnArgsFor${arch}${buildType}"
|
|
||||||
|
|
||||||
executable rootProject.ext.gn
|
|
||||||
args '--fail-on-unused-args', 'gen', "${utils.getGnOutDir(buildType)}/${arch}"
|
|
||||||
|
|
||||||
// Need to run gn only once unless the configuration in `args.gn` changes.
|
|
||||||
inputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn'))
|
|
||||||
outputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'build.ninja'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the native library for the target game using ninja.
|
|
||||||
utils.addGameTask('runNinjaFor') { String taskName, String buildType, String arch, String game ->
|
|
||||||
task(taskName, type: Exec) {
|
|
||||||
dependsOn "runGnFor${arch}${buildType}"
|
|
||||||
|
|
||||||
executable rootProject.ext.ninja
|
|
||||||
args '-C', "${utils.getGnOutDir(buildType)}/${arch}", "src/${utils.getGnTargetFor(game)}"
|
|
||||||
|
|
||||||
// Always run ninja and let it figure out what needs to be compiled.
|
|
||||||
outputs.upToDateWhen { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assets can be obtained from the output directory of any arch but it would be good to combine them
|
|
||||||
// in a single directory in case we are buildig multi-arch and some build config have different assets.
|
|
||||||
utils.addGameTask('copyAssetsFor') { String taskName, String buildType, String arch, String game ->
|
|
||||||
task(taskName, type: Copy) {
|
|
||||||
dependsOn "runNinjaFor${game}${arch}${buildType}"
|
|
||||||
|
|
||||||
from "${utils.getGnOutDir(buildType)}/${arch}/assets"
|
|
||||||
into "${utils.getGnOutDir(buildType)}/assets"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the native library to a directory denoting its arch code as the Android Gradle plugin expects.
|
|
||||||
utils.addGameTask('copyJniLibsFor') { String taskName, String buildType, String arch, String game ->
|
|
||||||
task(taskName, type: Copy) {
|
|
||||||
dependsOn "runNinjaFor${game}${arch}${buildType}"
|
|
||||||
|
|
||||||
from("${utils.getGnOutDir(buildType)}/${arch}") {
|
|
||||||
include "lib${utils.getGnTargetFor(game)}.so"
|
|
||||||
}
|
|
||||||
into "${utils.getGnOutDir(buildType)}/jniLibs/${utils.getAbiCodeFor(arch)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.configureEach { task ->
|
|
||||||
def variantPattern = /(\w+)(${utils.getArchTypesRegExp()})(${utils.getBuildTypesRegExp()})/
|
|
||||||
|
|
||||||
def match = task.name =~ /^merge/ + variantPattern + /JniLibFolders$/
|
|
||||||
if (match) {
|
|
||||||
utils.project.android.productFlavors.find { arch ->
|
|
||||||
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
||||||
// Depends on each arch type for multi-arch build flavors.
|
|
||||||
arch.ndk.abiFilters.each { abi ->
|
|
||||||
task.dependsOn "copyJniLibsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
match = task.name =~ /^generate/ + variantPattern + /Assets$/
|
|
||||||
if (match) {
|
|
||||||
utils.project.android.productFlavors.find { arch ->
|
|
||||||
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
||||||
// Depends on each arch type for multi-arch build flavors.
|
|
||||||
arch.ndk.abiFilters.each { abi ->
|
|
||||||
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
match = task.name =~ /^lintVitalAnalyze/ + variantPattern + /$/
|
|
||||||
if (match) {
|
|
||||||
utils.project.android.productFlavors.find { arch ->
|
|
||||||
if (arch.dimension == 'arch' && arch.name.capitalize() == match.group(2)) {
|
|
||||||
// Depends on each arch type for multi-arch build flavors.
|
|
||||||
arch.ndk.abiFilters.each { abi ->
|
|
||||||
task.dependsOn "copyAssetsFor${match.group(1)}${utils.ARCH_CODES[abi]}${match.group(3)}"
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Utils plugin
|
|
||||||
//
|
|
||||||
|
|
||||||
class Utils implements Plugin<Project> {
|
|
||||||
final def ARCH_CODES = ["armeabi-v7a": "Arm7",
|
|
||||||
"arm64-v8a": "Arm8",
|
|
||||||
"x86_64": "X86_64",
|
|
||||||
"x86": "X86"].asImmutable()
|
|
||||||
final def GN_CPU_CODES = ["Arm7": "arm",
|
|
||||||
"Arm8": "arm64",
|
|
||||||
"X86_64": "x64",
|
|
||||||
"X86": "x86"].asImmutable()
|
|
||||||
|
|
||||||
def project
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
Utils(Project project) {
|
|
||||||
this.project = project
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply(Project project) {
|
|
||||||
project.extensions.create('utils', Utils)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a task for archs and buildTypes variants.
|
|
||||||
void addTask(String prefix, Closure taskClosure) {
|
|
||||||
forEachBuildVariant { String arch, String buildType ->
|
|
||||||
def taskName = "${prefix}${arch}${buildType}"
|
|
||||||
taskClosure(taskName, buildType, arch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a task for games, archs and buildTypes variants.
|
|
||||||
void addGameTask(String prefix, Closure taskClosure) {
|
|
||||||
forEachGameBuildVariant { String game, String arch, String buildType ->
|
|
||||||
def taskName = "${prefix}${game}${arch}${buildType}"
|
|
||||||
taskClosure(taskName, buildType, arch, game)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void forEachBuildVariant(Closure callback) {
|
|
||||||
project.android.productFlavors.each { arch ->
|
|
||||||
// Only need to add tasks for arch types which maps to a single ABI
|
|
||||||
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
|
|
||||||
project.android.buildTypes.each { buildType ->
|
|
||||||
callback(arch.name.capitalize(), buildType.name.capitalize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void forEachGameBuildVariant(Closure callback) {
|
|
||||||
project.android.productFlavors.each { game ->
|
|
||||||
if (game.dimension == 'game') {
|
|
||||||
project.android.productFlavors.each { arch ->
|
|
||||||
// Only need to add tasks for arch types which maps to a single ABI
|
|
||||||
if (arch.dimension == 'arch' && arch.ndk.abiFilters.size() == 1) {
|
|
||||||
project.android.buildTypes.each { buildType ->
|
|
||||||
callback(game.name.capitalize(), arch.name.capitalize(), buildType.name.capitalize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getBuildTypesRegExp() {
|
|
||||||
def outList = []
|
|
||||||
project.android.buildTypes.each { buildType ->
|
|
||||||
outList += buildType.name.capitalize()
|
|
||||||
}
|
|
||||||
return outList.join('|')
|
|
||||||
}
|
|
||||||
|
|
||||||
def getArchTypesRegExp() {
|
|
||||||
def outList = []
|
|
||||||
project.android.productFlavors.each { flavor ->
|
|
||||||
if (flavor.dimension == 'arch') {
|
|
||||||
outList += flavor.name.capitalize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outList.join('|')
|
|
||||||
}
|
|
||||||
|
|
||||||
def getAbiCodeFor(String arch) {
|
|
||||||
def outStr = ''
|
|
||||||
project.android.productFlavors.find { flavor ->
|
|
||||||
if (flavor.name.capitalize() == arch) {
|
|
||||||
outStr = flavor.ndk.abiFilters.first()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outStr
|
|
||||||
}
|
|
||||||
|
|
||||||
def getGnTargetFor(String game) {
|
|
||||||
def outStr = ''
|
|
||||||
project.android.productFlavors.find { flavor ->
|
|
||||||
if (flavor.dimension == 'game' && flavor.name.capitalize() == game) {
|
|
||||||
outStr = flavor.ext.gnTarget
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outStr
|
|
||||||
}
|
|
||||||
|
|
||||||
def generateGnArgsContent(String buildType, String arch) {
|
|
||||||
def content = 'target_os="android"\n'
|
|
||||||
content += 'target_cpu="' + GN_CPU_CODES[arch] + '"\n'
|
|
||||||
content += "is_debug=${buildType != 'Release'}\n"
|
|
||||||
content += 'ndk="' + project.android.ndkDirectory + '"\n'
|
|
||||||
content += "ndk_api=${project.rootProject.ext.minSdk}\n"
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
def getGnOutDir(String buildType) {
|
|
||||||
return "${project.buildDir}/gn_out/${buildType.toLowerCase()}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class WriteFileTask extends DefaultTask {
|
|
||||||
@Input
|
|
||||||
abstract Property<String> getContent()
|
|
||||||
|
|
||||||
@OutputFile
|
|
||||||
abstract RegularFileProperty getTarget()
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
void run() {
|
|
||||||
def file = target.get().asFile
|
|
||||||
file.parentFile.mkdirs()
|
|
||||||
def text = content.get()
|
|
||||||
if (!file.exists() || text != file.text)
|
|
||||||
file.text = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:fullBackupContent="false"
|
android:fullBackupContent="false"
|
||||||
android:icon="${appIcon}"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -27,16 +27,16 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.lib_name"
|
android:name="android.app.lib_name"
|
||||||
android:value="@string/lib_name" />
|
android:value="kaliber" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||||
android:value="@string/admob_application_id" />
|
android:value="ca-app-pub-1321063817979967~1100949243" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="com.woom.game.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Kaliber app</string>
|
<string name="app_name">woom</string>
|
||||||
<string name="interstitial_ad_unit_id"></string>
|
<string name="interstitial_ad_unit_id">ca-app-pub-1321063817979967/8373182022</string>
|
||||||
<string name="admob_application_id">ca-app-pub-3940256099942544~3347511713</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -5,7 +5,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
classpath 'com.android.tools.build:gradle:8.0.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,17 +19,3 @@ allprojects {
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
|
||||||
ndkVersion = '25.2.9519653'
|
|
||||||
minSdk = 24
|
|
||||||
compileSdk = 33
|
|
||||||
targetSdk = 33
|
|
||||||
|
|
||||||
if (!project.hasProperty('gn')) {
|
|
||||||
gn = "gn"
|
|
||||||
}
|
|
||||||
if (!project.hasProperty('ninja')) {
|
|
||||||
ninja = "ninja"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
@rem
|
|
||||||
@rem Copyright 2015 the original author or authors.
|
|
||||||
@rem
|
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
@rem you may not use this file except in compliance with the License.
|
|
||||||
@rem You may obtain a copy of the License at
|
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@rem
|
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
@rem See the License for the specific language governing permissions and
|
|
||||||
@rem limitations under the License.
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
|
||||||
@rem Gradle startup script for Windows
|
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
|
||||||
|
|
||||||
@rem Find java.exe
|
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
|
||||||
|
# --- Input variables ---
|
||||||
|
|
||||||
|
BUILD ?= release
|
||||||
|
ifeq ($(findstring $(BUILD),debug release),)
|
||||||
|
$(error BUILD must be set to debug or release)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Build all executables by default.
|
||||||
|
APPS ?= demo
|
||||||
|
|
||||||
|
# If the VERBOSE flag isn't set, then mute superfluous output.
|
||||||
|
ifeq ($(VERBOSE),)
|
||||||
|
HUSH_COMPILE = @echo "Compiling $<";
|
||||||
|
HUSH_LINK = @echo "Linking $@";
|
||||||
|
HUSH_GENERATE = @echo "Generating $@";
|
||||||
|
HUSH_CLEAN = @
|
||||||
|
endif
|
||||||
|
|
||||||
|
# --- Internal variables ---
|
||||||
|
|
||||||
|
ARCH := $(shell uname -p)
|
||||||
|
SRC_ROOT := $(abspath ../../src)
|
||||||
|
OUTPUT_DIR := $(abspath .)
|
||||||
|
INTERMEDIATE_DIR := $(OUTPUT_DIR)/obj
|
||||||
|
BUILD_DIR := $(INTERMEDIATE_DIR)/$(BUILD)
|
||||||
|
|
||||||
|
ARFLAGS = r
|
||||||
|
LDFLAGS = -lX11 -lGL -pthread -lasound -ldl
|
||||||
|
|
||||||
|
# Always enable debug information.
|
||||||
|
CFLAGS += -g
|
||||||
|
|
||||||
|
# Enable extra warnings and make all warnings into errors.
|
||||||
|
CFLAGS += -Wextra -Werror
|
||||||
|
|
||||||
|
# Flags to generate dependency information.
|
||||||
|
CFLAGS += -MD -MP -MT $@
|
||||||
|
|
||||||
|
# Predefined flags.
|
||||||
|
ifeq ($(BUILD), debug)
|
||||||
|
CFLAGS += -D_DEBUG
|
||||||
|
CFLAGS += -D_GLIBCXX_DEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Enable compiler optimizations for everything except debug.
|
||||||
|
# Note that a very aggresssive optimization level is used and it may not be
|
||||||
|
# valid for all standard compliant programs. Reduce this level on individual
|
||||||
|
# files or modules as needed.
|
||||||
|
ifneq ($(BUILD), debug)
|
||||||
|
CFLAGS += -Ofast
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Flag to turn on extended instruction sets for the compiler.
|
||||||
|
CFLAGS += -msse2
|
||||||
|
|
||||||
|
# Let C++ inherit all C flags.
|
||||||
|
CXXFLAGS = $(CFLAGS) -I$(SRC_ROOT)
|
||||||
|
|
||||||
|
# Enable C++20
|
||||||
|
CXXFLAGS += -std=c++20
|
||||||
|
|
||||||
|
# Vulkan config
|
||||||
|
CFLAGS += -DVK_USE_PLATFORM_XLIB_KHR
|
||||||
|
CFLAGS += -DVMA_STATIC_VULKAN_FUNCTIONS=1
|
||||||
|
CFLAGS += -I$(SRC_ROOT)/third_party/vulkan/include
|
||||||
|
CXXFLAGS += -I$(SRC_ROOT)/third_party/glslang
|
||||||
|
|
||||||
|
# --- Internal functions ---
|
||||||
|
|
||||||
|
app_exe = $(OUTPUT_DIR)/$(1)_$(ARCH)_$(BUILD)
|
||||||
|
objs_from_src = $(patsubst $(SRC_ROOT)/%, $(BUILD_DIR)/%.o, $(basename $(1)))
|
||||||
|
objs_from_src_in = $(call objs_from_src, $(shell find $(1) -name "*.cc" -o -name "*.cpp" -o -name "*.c"))
|
||||||
|
|
||||||
|
# --- Base ---
|
||||||
|
|
||||||
|
BASE_SRC := \
|
||||||
|
$(SRC_ROOT)/base/collusion_test.cc \
|
||||||
|
$(SRC_ROOT)/base/log.cc \
|
||||||
|
$(SRC_ROOT)/base/task_runner.cc \
|
||||||
|
$(SRC_ROOT)/base/thread_pool.cc \
|
||||||
|
$(SRC_ROOT)/base/timer.cc
|
||||||
|
|
||||||
|
BASE_LIB := $(BUILD_DIR)/libengine.a
|
||||||
|
BASE_OBJS := $(call objs_from_src, $(BASE_SRC))
|
||||||
|
LIBS += $(BASE_LIB)
|
||||||
|
OBJS += $(BASE_OBJS)
|
||||||
|
|
||||||
|
$(BASE_LIB): $(BASE_OBJS)
|
||||||
|
|
||||||
|
# --- Engine ---
|
||||||
|
|
||||||
|
ENGINE_SRC := \
|
||||||
|
$(SRC_ROOT)/engine/animatable.cc \
|
||||||
|
$(SRC_ROOT)/engine/animator.cc \
|
||||||
|
$(SRC_ROOT)/engine/audio/audio_bus.cc \
|
||||||
|
$(SRC_ROOT)/engine/audio/audio_mixer.cc \
|
||||||
|
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
|
||||||
|
$(SRC_ROOT)/engine/audio/sinc_resampler.cc \
|
||||||
|
$(SRC_ROOT)/engine/drawable.cc \
|
||||||
|
$(SRC_ROOT)/engine/engine.cc \
|
||||||
|
$(SRC_ROOT)/engine/font.cc \
|
||||||
|
$(SRC_ROOT)/engine/image_quad.cc \
|
||||||
|
$(SRC_ROOT)/engine/image.cc \
|
||||||
|
$(SRC_ROOT)/engine/mesh.cc \
|
||||||
|
$(SRC_ROOT)/engine/persistent_data.cc \
|
||||||
|
$(SRC_ROOT)/engine/platform/asset_file_linux.cc \
|
||||||
|
$(SRC_ROOT)/engine/platform/asset_file.cc \
|
||||||
|
$(SRC_ROOT)/engine/platform/platform_linux.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/geometry.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/opengl/render_command.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/opengl/renderer_opengl_linux.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/opengl/renderer_opengl.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/renderer_types.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/shader.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/texture.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/vulkan/renderer_vulkan_linux.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/vulkan/renderer_vulkan.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/vulkan/vulkan_context_linux.cc \
|
||||||
|
$(SRC_ROOT)/engine/renderer/vulkan/vulkan_context.cc \
|
||||||
|
$(SRC_ROOT)/engine/shader_source.cc \
|
||||||
|
$(SRC_ROOT)/engine/solid_quad.cc \
|
||||||
|
$(SRC_ROOT)/engine/sound_player.cc \
|
||||||
|
$(SRC_ROOT)/engine/sound.cc
|
||||||
|
|
||||||
|
ENGINE_LIB := $(BUILD_DIR)/libengine.a
|
||||||
|
ENGINE_OBJS := $(call objs_from_src, $(ENGINE_SRC))
|
||||||
|
LIBS += $(ENGINE_LIB)
|
||||||
|
OBJS += $(ENGINE_OBJS)
|
||||||
|
|
||||||
|
$(ENGINE_LIB): $(ENGINE_OBJS)
|
||||||
|
|
||||||
|
# --- Third-party ---
|
||||||
|
|
||||||
|
# Ignore warnings.
|
||||||
|
THIRD_PARTY_CFLAGS = $(CFLAGS)
|
||||||
|
THIRD_PARTY_CFLAGS := $(filter-out -Wextra -Werror, $(THIRD_PARTY_CFLAGS))
|
||||||
|
|
||||||
|
THIRD_PARTY_CXXFLAGS = $(CXXFLAGS)
|
||||||
|
THIRD_PARTY_CXXFLAGS := $(filter-out -Wextra -Werror, $(THIRD_PARTY_CXXFLAGS))
|
||||||
|
|
||||||
|
THIRD_PARTY_SRC := \
|
||||||
|
$(SRC_ROOT)/third_party/glew/glew.c \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/GenericCodeGen/Link.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/attribute.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Constant.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Initialize.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/intermOut.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/iomapper.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/limits.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/parseConst.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/reflection.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Scan.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/MachineIndependent/Versions.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/disassemble.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/doc.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/GlslangToSpv.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/InReadableOrder.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/Logger.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvBuilder.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvPostProcess.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/SPVRemapper.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/glslang/SPIRV/SpvTools.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/jsoncpp/jsoncpp.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/spirv-reflect/spirv_reflect.c \
|
||||||
|
$(SRC_ROOT)/third_party/stb/stb_image.c \
|
||||||
|
$(SRC_ROOT)/third_party/texture_compressor/dxt_encoder_internals.cc \
|
||||||
|
$(SRC_ROOT)/third_party/texture_compressor/dxt_encoder.cc \
|
||||||
|
$(SRC_ROOT)/third_party/texture_compressor/texture_compressor_etc1.cc \
|
||||||
|
$(SRC_ROOT)/third_party/texture_compressor/texture_compressor.cc \
|
||||||
|
$(SRC_ROOT)/third_party/vma/vk_mem_alloc.cpp \
|
||||||
|
$(SRC_ROOT)/third_party/volk/volk.c
|
||||||
|
|
||||||
|
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.c
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CC) -c $(THIRD_PARTY_CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.cc
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CXX) -c $(THIRD_PARTY_CXXFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/third_party/%.o: $(SRC_ROOT)/third_party/%.cpp
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CXX) -c $(THIRD_PARTY_CXXFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
THIRD_PARTY_LIB := $(BUILD_DIR)/libthirdparty.a
|
||||||
|
THIRD_PARTY_OBJS := $(call objs_from_src, $(THIRD_PARTY_SRC))
|
||||||
|
LIBS += $(THIRD_PARTY_LIB)
|
||||||
|
OBJS += $(THIRD_PARTY_OBJS)
|
||||||
|
|
||||||
|
$(THIRD_PARTY_LIB): $(THIRD_PARTY_OBJS)
|
||||||
|
|
||||||
|
# --- demo application ---
|
||||||
|
|
||||||
|
ifneq ($(filter demo,$(APPS)),)
|
||||||
|
|
||||||
|
DEMO_SRC := \
|
||||||
|
$(SRC_ROOT)/demo/credits.cc \
|
||||||
|
$(SRC_ROOT)/demo/demo.cc \
|
||||||
|
$(SRC_ROOT)/demo/enemy.cc \
|
||||||
|
$(SRC_ROOT)/demo/hud.cc \
|
||||||
|
$(SRC_ROOT)/demo/menu.cc \
|
||||||
|
$(SRC_ROOT)/demo/player.cc \
|
||||||
|
$(SRC_ROOT)/demo/sky_quad.cc
|
||||||
|
|
||||||
|
DEMO_EXE := $(call app_exe,demo)
|
||||||
|
DEMO_OBJS := $(call objs_from_src, $(DEMO_SRC))
|
||||||
|
EXES += $(DEMO_EXE)
|
||||||
|
OBJS += $(DEMO_OBJS)
|
||||||
|
|
||||||
|
$(DEMO_EXE): $(DEMO_OBJS) $(LIBS)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# --- Build rules ---
|
||||||
|
|
||||||
|
# Dependencies.
|
||||||
|
DEPS = $(OBJS:.o=.d)
|
||||||
|
-include $(DEPS)
|
||||||
|
|
||||||
|
.PHONY: all clean cleanall help
|
||||||
|
|
||||||
|
all: $(EXES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning..."
|
||||||
|
$(HUSH_CLEAN) $(RM) -r $(BUILD_DIR)
|
||||||
|
|
||||||
|
cleanall:
|
||||||
|
@echo "Cleaning all..."
|
||||||
|
$(HUSH_CLEAN) $(RM) -r $(INTERMEDIATE_DIR)
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "BUILD = Build mode. One of:"
|
||||||
|
@echo " debug (no optimizations)"
|
||||||
|
@echo " release (optimizations, the default)"
|
||||||
|
@echo "APPS = Applications to build. Defaults to all."
|
||||||
|
@echo "VERBOSE = Full output from commands if set."
|
||||||
|
|
||||||
|
# It's important that libraries are specified last as Ubuntu uses "ld --as-needed" by default.
|
||||||
|
# Only the static libraries referenced by the object files will be linked into the executable.
|
||||||
|
# Beware that circular dependencies doesn't work with this flag.
|
||||||
|
$(EXES):
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_LINK) $(CXX) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.a:
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_GENERATE) $(AR) $(ARFLAGS) $@ $^
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.c
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CC) -c $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.cc
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CXX) -c $(CXXFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.cpp
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(HUSH_COMPILE) $(CXX) -c $(CXXFLAGS) -o $@ $<
|
|
@ -1,11 +0,0 @@
|
||||||
# Build a shared library for Android. Build an executable for other platforms.
|
|
||||||
template("game") {
|
|
||||||
if (target_os == "android") {
|
|
||||||
_target_type = "shared_library"
|
|
||||||
} else {
|
|
||||||
_target_type = "executable"
|
|
||||||
}
|
|
||||||
target(_target_type, target_name) {
|
|
||||||
forward_variables_from(invoker, "*")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
|
|
||||||
src, dst = sys.argv[1:]
|
|
||||||
|
|
||||||
if os.path.exists(dst):
|
|
||||||
if os.path.isdir(dst):
|
|
||||||
shutil.rmtree(dst)
|
|
||||||
else:
|
|
||||||
os.remove(dst)
|
|
||||||
|
|
||||||
if os.path.isdir(src):
|
|
||||||
shutil.copytree(src, dst)
|
|
||||||
else:
|
|
||||||
shutil.copy2(src, dst)
|
|
||||||
|
|
||||||
# https://github.com/ninja-build/ninja/issues/1554
|
|
||||||
os.utime(dst, None)
|
|
|
@ -1,130 +0,0 @@
|
||||||
toolchain("gcc") {
|
|
||||||
lib_switch = "-l"
|
|
||||||
lib_dir_switch = "-L"
|
|
||||||
|
|
||||||
tool("asm") {
|
|
||||||
depfile = "{{output}}.d"
|
|
||||||
command = "$cc -MD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
|
|
||||||
depsformat = "gcc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
|
|
||||||
description = "assemble {{source}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("cc") {
|
|
||||||
depfile = "{{output}}.d"
|
|
||||||
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
|
|
||||||
depsformat = "gcc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
|
|
||||||
description = "compile {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("cxx") {
|
|
||||||
depfile = "{{output}}.d"
|
|
||||||
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
|
|
||||||
depsformat = "gcc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
|
|
||||||
description = "compile {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("objc") {
|
|
||||||
depfile = "{{output}}.d"
|
|
||||||
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_c}} {{cflags_objc}} -c {{source}} -o {{output}}"
|
|
||||||
depsformat = "gcc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
|
|
||||||
description = "compile {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("objcxx") {
|
|
||||||
depfile = "{{output}}.d"
|
|
||||||
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_cc}} {{cflags_objcc}} -c {{source}} -o {{output}}"
|
|
||||||
depsformat = "gcc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
|
|
||||||
description = "compile {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_apple) {
|
|
||||||
not_needed([ "ar" ]) # libtool is used instead.
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("alink") {
|
|
||||||
if (is_apple) {
|
|
||||||
command =
|
|
||||||
"libtool -static -o {{output}} -no_warning_for_no_symbols {{inputs}}"
|
|
||||||
} else {
|
|
||||||
command = "rm -f {{output}} && $ar rcs {{output}} {{inputs}}"
|
|
||||||
}
|
|
||||||
outputs =
|
|
||||||
[ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
|
|
||||||
default_output_extension = ".a"
|
|
||||||
output_prefix = "lib"
|
|
||||||
description = "link {{target_output_name}}{{output_extension}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("solink") {
|
|
||||||
soname = "{{target_output_name}}{{output_extension}}"
|
|
||||||
sofile = "{{output_dir}}/$soname"
|
|
||||||
rspfile = soname + ".rsp"
|
|
||||||
if (is_apple) {
|
|
||||||
os_specific_option = "-install_name @executable_path/$sofile"
|
|
||||||
rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
|
|
||||||
} else {
|
|
||||||
os_specific_option = "-Wl,-soname=$soname"
|
|
||||||
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
|
|
||||||
}
|
|
||||||
command = "$cxx -shared {{ldflags}} {{frameworks}} -o $sofile $os_specific_option @$rspfile"
|
|
||||||
default_output_extension = ".so"
|
|
||||||
default_output_dir = "{{root_out_dir}}"
|
|
||||||
outputs = [ sofile ]
|
|
||||||
link_output = sofile
|
|
||||||
depend_output = sofile
|
|
||||||
output_prefix = "lib"
|
|
||||||
description = "link $soname"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("link") {
|
|
||||||
outfile = "{{target_output_name}}{{output_extension}}"
|
|
||||||
rspfile = "$outfile.rsp"
|
|
||||||
rspfile_content = "{{inputs}}"
|
|
||||||
if (is_apple) {
|
|
||||||
command = "$cxx {{ldflags}} {{solibs}} {{libs}} {{frameworks}} -o $outfile @$rspfile"
|
|
||||||
} else {
|
|
||||||
command = "$cxx {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
|
|
||||||
}
|
|
||||||
default_output_dir = "{{root_out_dir}}"
|
|
||||||
outputs = [ outfile ]
|
|
||||||
description = "link $outfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host_os == "win") {
|
|
||||||
tool("stamp") {
|
|
||||||
command = "cmd.exe /c echo > {{output}}"
|
|
||||||
description = "stamp {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("copy") {
|
|
||||||
# Note: The build in copy command can't handle forward slashes as path separators.
|
|
||||||
# Use a python script as a work around.
|
|
||||||
# command = "cmd.exe /c copy /Y {{source}} {{output}}"
|
|
||||||
|
|
||||||
copy_cmd = rebase_path("../copy.py")
|
|
||||||
command = "python \"$copy_cmd\" {{source}} {{output}}"
|
|
||||||
|
|
||||||
description = "copy {{source}} {{output}}"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tool("stamp") {
|
|
||||||
command = "touch {{output}}"
|
|
||||||
description = "stamp {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("copy") {
|
|
||||||
command = "cp -af {{source}} {{output}}"
|
|
||||||
description = "copy {{source}} {{output}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
toolchain("msvc") {
|
|
||||||
lib_switch = ""
|
|
||||||
lib_dir_switch = "/LIBPATH:"
|
|
||||||
|
|
||||||
tool("asm") {
|
|
||||||
command = "ml.exe {{asmflags}} /nologo /c /Fo {{output}} {{source}}"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
|
|
||||||
description = "assemble {{source}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("cc") {
|
|
||||||
pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb"
|
|
||||||
command = "cl.exe /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
|
|
||||||
depsformat = "msvc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
|
|
||||||
description = "compile {{source}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("cxx") {
|
|
||||||
pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb"
|
|
||||||
command = "cl.exe /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
|
|
||||||
depsformat = "msvc"
|
|
||||||
outputs =
|
|
||||||
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ]
|
|
||||||
description = "compile {{source}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("alink") {
|
|
||||||
rspfile = "{{output}}.rsp"
|
|
||||||
command = "lib.exe /nologo {{arflags}} /OUT:{{output}} @$rspfile"
|
|
||||||
outputs = [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ]
|
|
||||||
default_output_extension = ".lib"
|
|
||||||
default_output_dir = "{{target_out_dir}}"
|
|
||||||
rspfile_content = "{{inputs_newline}}"
|
|
||||||
description = "link {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("solink") {
|
|
||||||
dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
|
|
||||||
libname = "${dllname}.lib"
|
|
||||||
pdbname = "${dllname}.pdb"
|
|
||||||
rspfile = "${dllname}.rsp"
|
|
||||||
command = "link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:$pdbname @$rspfile"
|
|
||||||
outputs = [
|
|
||||||
dllname,
|
|
||||||
libname,
|
|
||||||
pdbname,
|
|
||||||
]
|
|
||||||
default_output_extension = ".dll"
|
|
||||||
default_output_dir = "{{root_out_dir}}"
|
|
||||||
link_output = libname
|
|
||||||
depend_output = libname
|
|
||||||
runtime_outputs = [
|
|
||||||
dllname,
|
|
||||||
pdbname,
|
|
||||||
]
|
|
||||||
rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}"
|
|
||||||
description = "link {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("link") {
|
|
||||||
exename = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"
|
|
||||||
pdbname = "$exename.pdb"
|
|
||||||
rspfile = "$exename.rsp"
|
|
||||||
command = "link.exe /nologo /OUT:$exename /PDB:$pdbname @$rspfile"
|
|
||||||
default_output_extension = ".exe"
|
|
||||||
default_output_dir = "{{root_out_dir}}"
|
|
||||||
outputs = [ exename ]
|
|
||||||
rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}"
|
|
||||||
description = "link {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("stamp") {
|
|
||||||
command = "cmd.exe /c echo > {{output}}"
|
|
||||||
description = "stamp {{output}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
tool("copy") {
|
|
||||||
# Note: The build in copy command can't handle forward slashes as path separators.
|
|
||||||
# Use a python script as a work around.
|
|
||||||
# command = "cmd.exe /c copy /Y {{source}} {{output}}"
|
|
||||||
|
|
||||||
copy_cmd = rebase_path("../copy.py")
|
|
||||||
command = "python \"$copy_cmd\" {{source}} {{output}}"
|
|
||||||
|
|
||||||
description = "copy {{source}} {{output}}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
source_set("base") {
|
|
||||||
sources = [
|
|
||||||
"closure.h",
|
|
||||||
"collusion_test.cc",
|
|
||||||
"collusion_test.h",
|
|
||||||
"file.h",
|
|
||||||
"hash.h",
|
|
||||||
"interpolation.h",
|
|
||||||
"log.cc",
|
|
||||||
"log.h",
|
|
||||||
"mem.cc",
|
|
||||||
"mem.h",
|
|
||||||
"misc.h",
|
|
||||||
"random.h",
|
|
||||||
"task_runner.cc",
|
|
||||||
"task_runner.h",
|
|
||||||
"thread_pool.cc",
|
|
||||||
"thread_pool.h",
|
|
||||||
"timer.h",
|
|
||||||
"vecmath.h",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = []
|
|
||||||
}
|
|
|
@ -10,13 +10,14 @@
|
||||||
|
|
||||||
#define HERE std::make_tuple(__func__, __FILE__, __LINE__)
|
#define HERE std::make_tuple(__func__, __FILE__, __LINE__)
|
||||||
|
|
||||||
// Helper for logging location info, e.g. LOG(0) << LOCATION(from)
|
// Helper for logging location info, e.g. LOG << LOCATION(from)
|
||||||
#define LOCATION(from) \
|
#define LOCATION(from) \
|
||||||
std::get<0>(from) << "() [" << [](std::string path) -> std::string { \
|
std::get<0>(from) << "() [" << [](const char* path) -> std::string { \
|
||||||
size_t last_slash_pos = path.find_last_of("\\/"); \
|
std::string file_name(path); \
|
||||||
|
size_t last_slash_pos = file_name.find_last_of("\\/"); \
|
||||||
if (last_slash_pos != std::string::npos) \
|
if (last_slash_pos != std::string::npos) \
|
||||||
path = path.substr(last_slash_pos + 1); \
|
file_name = file_name.substr(last_slash_pos + 1); \
|
||||||
return path; \
|
return file_name; \
|
||||||
}(std::get<1>(from)) << ":" \
|
}(std::get<1>(from)) << ":" \
|
||||||
<< std::get<2>(from) << "]"
|
<< std::get<2>(from) << "]"
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ using Closure = std::function<void()>;
|
||||||
|
|
||||||
// Provides location info (function name, file name and line number) where of a
|
// Provides location info (function name, file name and line number) where of a
|
||||||
// Closure was constructed.
|
// Closure was constructed.
|
||||||
using Location = std::tuple<std::string, std::string, int>;
|
using Location = std::tuple<const char*, const char*, int>;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,17 @@
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
// Compile-time string hashing function.
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
constexpr inline size_t KR2Hash(const char (&str)[N], size_t Len = N - 1) {
|
constexpr inline size_t Hash(const char (&str)[N], size_t Len = N - 1) {
|
||||||
size_t hash_value = 0;
|
size_t hash_value = 0;
|
||||||
for (int i = 0; str[i] != '\0'; ++i)
|
for (int i = 0; str[i] != '\0'; ++i)
|
||||||
hash_value = str[i] + 31 * hash_value;
|
hash_value = str[i] + 31 * hash_value;
|
||||||
return hash_value;
|
return hash_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t KR2Hash(const std::string& str) {
|
// The same hashing function for run-time.
|
||||||
|
inline size_t Hash(const std::string& str) {
|
||||||
size_t hash_value = 0;
|
size_t hash_value = 0;
|
||||||
for (std::string::value_type c : str)
|
for (std::string::value_type c : str)
|
||||||
hash_value = c + 31 * hash_value;
|
hash_value = c + 31 * hash_value;
|
||||||
|
|
|
@ -2,74 +2,52 @@
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#include <format>
|
|
||||||
#else
|
#else
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#endif
|
#endif
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
int g_max_log_verbosity_level = 0;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
|
// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
|
||||||
// an object of the correct type on the LHS of the unused part of the ternary
|
// an object of the correct type on the LHS of the unused part of the ternary
|
||||||
// operator.
|
// operator.
|
||||||
std::ostream* LogMessage::swallow_stream;
|
std::ostream* LogMessage::swallow_stream;
|
||||||
|
|
||||||
int GlobalMaxLogVerbosityLevel() {
|
LogMessage::LogMessage(const char* file, int line) : file_(file), line_(line) {}
|
||||||
return g_max_log_verbosity_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage::LogMessage(const char* file, int line, int verbosity_level)
|
|
||||||
: file_(file), line_(line), verbosity_level_(verbosity_level) {}
|
|
||||||
|
|
||||||
LogMessage::~LogMessage() {
|
LogMessage::~LogMessage() {
|
||||||
stream_ << std::endl;
|
stream_ << std::endl;
|
||||||
std::string message(stream_.str());
|
std::string text(stream_.str());
|
||||||
std::string filename(file_);
|
std::string filename(file_);
|
||||||
size_t last_slash_pos = filename.find_last_of("\\/");
|
size_t last_slash_pos = filename.find_last_of("\\/");
|
||||||
if (last_slash_pos != std::string::npos)
|
if (last_slash_pos != std::string::npos)
|
||||||
filename = filename.substr(last_slash_pos + 1);
|
filename = filename.substr(last_slash_pos + 1);
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "kaliber", "%d [%s:%d] %s",
|
__android_log_print(ANDROID_LOG_ERROR, "kaliber", "[%s:%d] %s",
|
||||||
verbosity_level_, filename.c_str(), line_,
|
filename.c_str(), line_, text.c_str());
|
||||||
message.c_str());
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
OutputDebugStringA(
|
|
||||||
std::format("{} [{}:{}] {}", verbosity_level_, filename, line_, message)
|
|
||||||
.c_str());
|
|
||||||
#else
|
#else
|
||||||
printf("%d [%s:%d] %s", verbosity_level_, filename.c_str(), line_,
|
printf("[%s:%d] %s", filename.c_str(), line_, text.c_str());
|
||||||
message.c_str());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
LogAbort LogAbort::Check(const char* file, int line, const char* expr) {
|
LogAbort LogAbort::Check(const char* file, int line, const char* expr) {
|
||||||
LogAbort instance(new LogMessage(file, line, 0));
|
LogAbort instance(new LogMessage(file, line));
|
||||||
instance.GetLog().stream() << "CHECK(" << expr << ") ";
|
instance.GetLog().stream() << "CHECK: "
|
||||||
|
<< "(" << expr << ") ";
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
LogAbort LogAbort::DCheck(const char* file, int line, const char* expr) {
|
LogAbort LogAbort::DCheck(const char* file, int line, const char* expr) {
|
||||||
LogAbort instance(new LogMessage(file, line, 0));
|
LogAbort instance(new LogMessage(file, line));
|
||||||
instance.GetLog().stream() << "DCHECK(" << expr << ") ";
|
instance.GetLog().stream() << "DCHECK: "
|
||||||
|
<< "(" << expr << ") ";
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
LogAbort LogAbort::NotReached(const char* file, int line) {
|
LogAbort LogAbort::NotReached(const char* file, int line) {
|
||||||
LogAbort instance(new LogMessage(file, line, 0));
|
LogAbort instance(new LogMessage(file, line));
|
||||||
instance.GetLog().stream() << "NOTREACHED() ";
|
instance.GetLog().stream() << "NOTREACHED ";
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,66 +11,45 @@
|
||||||
// CHECK(condition) terminates the process if the condition is false.
|
// CHECK(condition) terminates the process if the condition is false.
|
||||||
// NOTREACHED annotates unreachable codepaths and terminates the process if
|
// NOTREACHED annotates unreachable codepaths and terminates the process if
|
||||||
// reached.
|
// reached.
|
||||||
#define LOG(verbosity_level) \
|
#define LOG base::LogMessage(__FILE__, __LINE__).stream()
|
||||||
LAZY_STREAM( \
|
#define LOG_IF(condition) \
|
||||||
LOG_IS_ON(verbosity_level), \
|
LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream())
|
||||||
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
|
|
||||||
#define LOG_IF(verbosity_level, condition) \
|
|
||||||
LAZY_STREAM( \
|
|
||||||
LOG_IS_ON(verbosity_level) && (condition), \
|
|
||||||
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
|
|
||||||
#define CHECK(condition) \
|
#define CHECK(condition) \
|
||||||
LAZY_STREAM(!(condition), \
|
LAZY_STREAM( \
|
||||||
::base::LogAbort::Check(__FILE__, __LINE__, #condition) \
|
!(condition), \
|
||||||
.GetLog() \
|
base::LogAbort::Check(__FILE__, __LINE__, #condition).GetLog().stream())
|
||||||
.stream())
|
|
||||||
|
|
||||||
#define NOTREACHED() \
|
#define NOTREACHED \
|
||||||
::base::LogAbort::NotReached(__FILE__, __LINE__).GetLog().stream()
|
base::LogAbort::NotReached(__FILE__, __LINE__).GetLog().stream()
|
||||||
|
|
||||||
// Macros for logging which are active only in debug builds.
|
// Macros for logging which are active only in debug builds.
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#define DLOG(verbosity_level) \
|
#define DLOG base::LogMessage(__FILE__, __LINE__).stream()
|
||||||
LAZY_STREAM( \
|
#define DLOG_IF(condition) \
|
||||||
LOG_IS_ON(verbosity_level), \
|
LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream())
|
||||||
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
|
|
||||||
#define DLOG_IF(verbosity_level, condition) \
|
|
||||||
LAZY_STREAM( \
|
|
||||||
LOG_IS_ON(verbosity_level) && (condition), \
|
|
||||||
::base::LogMessage(__FILE__, __LINE__, verbosity_level).stream())
|
|
||||||
#define DCHECK(condition) \
|
#define DCHECK(condition) \
|
||||||
LAZY_STREAM(!(condition), \
|
LAZY_STREAM(!(condition), \
|
||||||
::base::LogAbort::DCheck(__FILE__, __LINE__, #condition) \
|
base::LogAbort::DCheck(__FILE__, __LINE__, #condition) \
|
||||||
.GetLog() \
|
.GetLog() \
|
||||||
.stream())
|
.stream())
|
||||||
#else
|
#else
|
||||||
// "debug mode" logging is compiled away to nothing for release builds.
|
// "debug mode" logging is compiled away to nothing for release builds.
|
||||||
#define DLOG(verbosity_level) EAT_STREAM_PARAMETERS
|
#define DLOG EAT_STREAM_PARAMETERS
|
||||||
#define DLOG_IF(verbosity_level, condition) EAT_STREAM_PARAMETERS
|
#define DLOG_IF(condition) EAT_STREAM_PARAMETERS
|
||||||
#define DCHECK(condition) EAT_STREAM_PARAMETERS
|
#define DCHECK(condition) EAT_STREAM_PARAMETERS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Helper macro which avoids evaluating the arguments to a stream if
|
// Helper macro which avoids evaluating the arguments to a stream if
|
||||||
// the condition doesn't hold.
|
// the condition doesn't hold.
|
||||||
#define LAZY_STREAM(condition, stream) \
|
#define LAZY_STREAM(condition, stream) \
|
||||||
!(condition) ? (void)0 : ::base::LogMessage::Voidify() & (stream)
|
!(condition) ? (void)0 : base::LogMessage::Voidify() & (stream)
|
||||||
|
|
||||||
// Avoid any pointless instructions to be emitted by the compiler.
|
// Avoid any pointless instructions to be emitted by the compiler.
|
||||||
#define EAT_STREAM_PARAMETERS \
|
#define EAT_STREAM_PARAMETERS \
|
||||||
LAZY_STREAM(false, *::base::LogMessage::swallow_stream)
|
LAZY_STREAM(false, *base::LogMessage::swallow_stream)
|
||||||
|
|
||||||
#if defined(MAX_LOG_VERBOSITY_LEVEL)
|
|
||||||
#define LOG_IS_ON(verbosity_level) \
|
|
||||||
((verbosity_level) <= MAX_LOG_VERBOSITY_LEVEL)
|
|
||||||
#else
|
|
||||||
#define LOG_IS_ON(verbosity_level) \
|
|
||||||
((verbosity_level) <= ::base::GlobalMaxLogVerbosityLevel())
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
int GlobalMaxLogVerbosityLevel();
|
|
||||||
|
|
||||||
class LogMessage {
|
class LogMessage {
|
||||||
public:
|
public:
|
||||||
class Voidify {
|
class Voidify {
|
||||||
|
@ -82,17 +61,18 @@ class LogMessage {
|
||||||
void operator&(std::ostream&) {}
|
void operator&(std::ostream&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
LogMessage(const char* file, int line, int verbosity_level);
|
LogMessage(const char* file, int line);
|
||||||
~LogMessage();
|
~LogMessage();
|
||||||
|
|
||||||
|
LogMessage& base() { return *this; }
|
||||||
|
|
||||||
std::ostream& stream() { return stream_; }
|
std::ostream& stream() { return stream_; }
|
||||||
|
|
||||||
static std::ostream* swallow_stream;
|
static std::ostream* swallow_stream;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* file_;
|
const char* file_;
|
||||||
int line_;
|
const int line_;
|
||||||
int verbosity_level_;
|
|
||||||
std::ostringstream stream_;
|
std::ostringstream stream_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
#include "base/mem.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
#include <malloc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
void* AlignedAlloc(size_t size, size_t alignment) {
|
|
||||||
DCHECK(size > 0U);
|
|
||||||
DCHECK(IsPow2(alignment));
|
|
||||||
DCHECK((alignment % sizeof(void*)) == 0U);
|
|
||||||
|
|
||||||
void* ptr = nullptr;
|
|
||||||
#if defined(_WIN32)
|
|
||||||
ptr = _aligned_malloc(size, alignment);
|
|
||||||
#elif defined(__ANDROID__)
|
|
||||||
ptr = memalign(alignment, size);
|
|
||||||
#else
|
|
||||||
int ret = posix_memalign(&ptr, alignment, size);
|
|
||||||
if (ret != 0) {
|
|
||||||
DLOG(0) << "posix_memalign() returned with error " << ret;
|
|
||||||
ptr = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Aligned allocations may fail for non-memory related reasons.
|
|
||||||
CHECK(ptr) << "Aligned allocation failed. "
|
|
||||||
<< "size=" << size << ", alignment=" << alignment;
|
|
||||||
DCHECK(IsAligned(ptr, alignment));
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* AlignedRealloc(void* ptr,
|
|
||||||
size_t old_size,
|
|
||||||
size_t new_size,
|
|
||||||
size_t alignment) {
|
|
||||||
auto* new_ptr = AlignedAlloc(new_size, alignment);
|
|
||||||
memmove(new_ptr, ptr, old_size);
|
|
||||||
AlignedFree(ptr);
|
|
||||||
return new_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
|
@ -1,33 +1,26 @@
|
||||||
#ifndef BASE_MEM_H
|
#ifndef BASE_MEM_H
|
||||||
#define BASE_MEM_H
|
#define BASE_MEM_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <cstdlib>
|
||||||
#include <stdint.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(__ANDROID__)
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#else
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/misc.h"
|
|
||||||
|
#define ALIGN_MEM(alignment) __attribute__((aligned(alignment)))
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
inline void AlignedFree(void* mem) {
|
|
||||||
#if defined(_WIN32)
|
|
||||||
_aligned_free(mem);
|
|
||||||
#else
|
|
||||||
free(mem);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct ScopedAlignedFree {
|
struct ScopedAlignedFree {
|
||||||
inline void operator()(void* x) const { AlignedFree(x); }
|
inline void operator()(void* x) const {
|
||||||
|
if (x)
|
||||||
|
free(x);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -35,16 +28,27 @@ struct ScopedAlignedFree {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using AlignedMemPtr = std::unique_ptr<T, internal::ScopedAlignedFree>;
|
using AlignedMemPtr = std::unique_ptr<T, internal::ScopedAlignedFree>;
|
||||||
|
|
||||||
void* AlignedAlloc(size_t size, size_t alignment);
|
template <int kAlignment>
|
||||||
|
inline void* AlignedAlloc(size_t size) {
|
||||||
|
void* ptr = NULL;
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
ptr = memalign(kAlignment, size);
|
||||||
|
#else
|
||||||
|
if (posix_memalign(&ptr, kAlignment, size))
|
||||||
|
ptr = NULL;
|
||||||
|
#endif
|
||||||
|
DCHECK(ptr);
|
||||||
|
// DCHECK(((unsigned)ptr & (kAlignment - 1)) == 0);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
void* AlignedRealloc(void* ptr,
|
inline void AlignedFree(void* mem) {
|
||||||
size_t old_size,
|
free(mem);
|
||||||
size_t new_size,
|
}
|
||||||
size_t alignment);
|
|
||||||
|
|
||||||
inline bool IsAligned(const void* val, size_t alignment) {
|
template <int kAlignment>
|
||||||
DCHECK(IsPow2(alignment)) << alignment << " is not a power of 2";
|
inline bool IsAligned(void* ptr) {
|
||||||
return (reinterpret_cast<uintptr_t>(val) & (alignment - 1)) == 0;
|
return (reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)) == 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
|
@ -1,42 +1,31 @@
|
||||||
#ifndef BASE_MISC_H
|
#ifndef BASE_MISC_H
|
||||||
#define BASE_MISC_H
|
#define BASE_MISC_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#define CRASH *((int*)nullptr) = 0;
|
#define CRASH *((int*)nullptr) = 0;
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
inline uint32_t GetHighestBitPos(uint32_t value) {
|
// ToDo: x86 has the bsr instruction.
|
||||||
uint32_t result = 0;
|
inline int GetHighestBitPos(int value) {
|
||||||
if (0xFFFF0000 & value) {
|
return (0xFFFF0000 & value ? value &= 0xFFFF0000, 1 : 0) * 0x10 +
|
||||||
value &= 0xFFFF0000;
|
(0xFF00FF00 & value ? value &= 0xFF00FF00, 1 : 0) * 0x08 +
|
||||||
result += 0x10;
|
(0xF0F0F0F0 & value ? value &= 0xF0F0F0F0, 1 : 0) * 0x04 +
|
||||||
}
|
(0xCCCCCCCC & value ? value &= 0xCCCCCCCC, 1 : 0) * 0x02 +
|
||||||
if (0xFF00FF00 & value) {
|
(0xAAAAAAAA & value ? 1 : 0) * 0x01;
|
||||||
value &= 0xFF00FF00;
|
|
||||||
result += 0x8;
|
|
||||||
}
|
|
||||||
if (0xF0F0F0F0 & value) {
|
|
||||||
value &= 0xF0F0F0F0;
|
|
||||||
result += 0x04;
|
|
||||||
}
|
|
||||||
if (0xCCCCCCCC & value) {
|
|
||||||
value &= 0xCCCCCCCC;
|
|
||||||
result += 0x02;
|
|
||||||
}
|
|
||||||
if (0xAAAAAAAA & value) {
|
|
||||||
result += 0x01;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the highest set bit in an integer number
|
||||||
|
inline int GetHighestBit(int value) {
|
||||||
|
return 0x1 << GetHighestBitPos(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the given integer is a power of two, ie if only one bit is set.
|
||||||
inline bool IsPow2(int value) {
|
inline bool IsPow2(int value) {
|
||||||
return ((value & (value - 1)) == 0);
|
return GetHighestBit(value) == value;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t RoundUpToPow2(uint32_t val) {
|
inline int RoundUpToPow2(int val) {
|
||||||
uint32_t i = 1 << GetHighestBitPos(val);
|
int i = GetHighestBit(val);
|
||||||
return val == i ? val : i << 1;
|
return val == i ? val : i << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,75 +4,55 @@
|
||||||
|
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void PostTaskAndReplyRelay(Location from,
|
void PostTaskAndReplyRelay(base::Location from,
|
||||||
Closure task_cb,
|
base::Closure task_cb,
|
||||||
Closure reply_cb,
|
base::Closure reply_cb,
|
||||||
std::shared_ptr<TaskRunner> destination,
|
base::TaskRunner* destination) {
|
||||||
bool front) {
|
|
||||||
task_cb();
|
task_cb();
|
||||||
|
|
||||||
if (reply_cb)
|
if (reply_cb)
|
||||||
destination->PostTask(from, std::move(reply_cb), front);
|
destination->PostTask(from, std::move(reply_cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// The task runner that belongs to the thread it's created in. Tasks to be run
|
namespace base {
|
||||||
// on a specific thread can be posted to this task runner.
|
|
||||||
// TaskRunner::GetThreadLocalTaskRunner()->RunTasks() is expected to be
|
thread_local std::unique_ptr<TaskRunner> TaskRunner::thread_local_task_runner;
|
||||||
// periodically called.
|
|
||||||
thread_local std::shared_ptr<TaskRunner> TaskRunner::thread_local_task_runner;
|
|
||||||
|
|
||||||
void TaskRunner::CreateThreadLocalTaskRunner() {
|
void TaskRunner::CreateThreadLocalTaskRunner() {
|
||||||
DCHECK(!thread_local_task_runner);
|
DCHECK(!thread_local_task_runner);
|
||||||
|
|
||||||
thread_local_task_runner = std::make_shared<TaskRunner>();
|
thread_local_task_runner = std::make_unique<TaskRunner>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TaskRunner> TaskRunner::GetThreadLocalTaskRunner() {
|
TaskRunner* TaskRunner::GetThreadLocalTaskRunner() {
|
||||||
return thread_local_task_runner;
|
return thread_local_task_runner.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskRunner::PostTask(Location from, Closure task, bool front) {
|
void TaskRunner::PostTask(const Location& from, Closure task) {
|
||||||
DCHECK(task) << LOCATION(from);
|
DCHECK(task) << LOCATION(from);
|
||||||
|
|
||||||
task_count_.fetch_add(1, std::memory_order_relaxed);
|
task_count_.fetch_add(1, std::memory_order_relaxed);
|
||||||
std::lock_guard<std::mutex> scoped_lock(lock_);
|
std::lock_guard<std::mutex> scoped_lock(lock_);
|
||||||
if (front)
|
|
||||||
queue_.emplace_front(from, std::move(task));
|
|
||||||
else
|
|
||||||
queue_.emplace_back(from, std::move(task));
|
queue_.emplace_back(from, std::move(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskRunner::PostTaskAndReply(Location from,
|
void TaskRunner::PostTaskAndReply(const Location& from,
|
||||||
Closure task,
|
Closure task,
|
||||||
Closure reply,
|
Closure reply) {
|
||||||
bool front) {
|
|
||||||
DCHECK(task) << LOCATION(from);
|
DCHECK(task) << LOCATION(from);
|
||||||
DCHECK(reply) << LOCATION(from);
|
DCHECK(reply) << LOCATION(from);
|
||||||
DCHECK(thread_local_task_runner) << LOCATION(from);
|
DCHECK(thread_local_task_runner) << LOCATION(from);
|
||||||
|
|
||||||
auto relay = std::bind(PostTaskAndReplyRelay, from, std::move(task),
|
auto relay = std::bind(::PostTaskAndReplyRelay, from, std::move(task),
|
||||||
std::move(reply), thread_local_task_runner, front);
|
std::move(reply), thread_local_task_runner.get());
|
||||||
PostTask(from, std::move(relay), front);
|
PostTask(from, std::move(relay));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskRunner::CancelTasks() {
|
void TaskRunner::MultiConsumerRun() {
|
||||||
std::lock_guard<std::mutex> scoped_lock(lock_);
|
|
||||||
task_count_.fetch_sub(queue_.size(), std::memory_order_release);
|
|
||||||
queue_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskRunner::WaitForCompletion() {
|
|
||||||
while (task_count_.load(std::memory_order_acquire) > 0)
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskRunner::RunTasks() {
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Task task;
|
Task task;
|
||||||
{
|
{
|
||||||
|
@ -86,12 +66,60 @@ void TaskRunner::RunTasks() {
|
||||||
auto [from, task_cb] = task;
|
auto [from, task_cb] = task;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
LOG(0) << __func__ << " from: " << LOCATION(from);
|
LOG << __func__ << " from: " << LOCATION(from);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
task_cb();
|
task_cb();
|
||||||
task_count_.fetch_sub(1, std::memory_order_release);
|
task_count_.fetch_sub(1, std::memory_order_release);
|
||||||
|
|
||||||
|
if (cancel_tasks_.load(std::memory_order_relaxed)) {
|
||||||
|
CancelTasksInternal();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskRunner::SingleConsumerRun() {
|
||||||
|
std::deque<Task> queue;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> scoped_lock(lock_);
|
||||||
|
if (queue_.empty())
|
||||||
|
return;
|
||||||
|
queue.swap(queue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!queue.empty()) {
|
||||||
|
auto [from, task_cb] = queue.front();
|
||||||
|
queue.pop_front();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
LOG << __func__ << " from: " << LOCATION(from);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
task_cb();
|
||||||
|
task_count_.fetch_sub(1, std::memory_order_release);
|
||||||
|
|
||||||
|
if (cancel_tasks_.load(std::memory_order_relaxed)) {
|
||||||
|
CancelTasksInternal();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskRunner::CancelTasks() {
|
||||||
|
cancel_tasks_.store(true, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskRunner::WaitForCompletion() {
|
||||||
|
while (task_count_.load(std::memory_order_acquire) > 0)
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskRunner::CancelTasksInternal() {
|
||||||
|
cancel_tasks_.store(false, std::memory_order_relaxed);
|
||||||
|
task_count_.store(0, std::memory_order_relaxed);
|
||||||
|
std::lock_guard<std::mutex> scoped_lock(lock_);
|
||||||
|
queue_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
|
@ -18,66 +18,55 @@ namespace internal {
|
||||||
// one that returns via an output parameter.
|
// one that returns via an output parameter.
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
void ReturnAsParamAdapter(std::function<ReturnType()> func,
|
void ReturnAsParamAdapter(std::function<ReturnType()> func,
|
||||||
std::shared_ptr<ReturnType> result) {
|
ReturnType* result) {
|
||||||
*result = func();
|
*result = func();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapts a ReturnType* result to a callback that expects a ReturnType.
|
// Adapts a ReturnType* result to a callblack that expects a ReturnType.
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
void ReplyAdapter(std::function<void(ReturnType)> callback,
|
void ReplyAdapter(std::function<void(ReturnType)> callback,
|
||||||
std::shared_ptr<ReturnType> result) {
|
ReturnType* result) {
|
||||||
callback(std::move(*result));
|
callback(*result);
|
||||||
|
delete result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// Runs queued tasks (in the form of Closure objects). All methods are
|
// Runs queued tasks (in the form of Closure objects). All methods are
|
||||||
// thread-safe and can be called on any thread.
|
// thread-safe and can be called on any thread.
|
||||||
// Tasks run in FIFO order when consumed by a single thread. When consumed
|
// Tasks run in FIFO order. When consumed concurrently by multiple threads, it
|
||||||
// concurrently by multiple threads, it doesn't guarantee whether tasks overlap,
|
// doesn't guarantee whether tasks overlap, or whether they run on a particular
|
||||||
// or whether they run on a particular thread.
|
// thread.
|
||||||
class TaskRunner {
|
class TaskRunner {
|
||||||
public:
|
public:
|
||||||
TaskRunner() = default;
|
TaskRunner() = default;
|
||||||
~TaskRunner() = default;
|
~TaskRunner() = default;
|
||||||
|
|
||||||
static void CreateThreadLocalTaskRunner();
|
void PostTask(const Location& from, Closure task);
|
||||||
static std::shared_ptr<TaskRunner> GetThreadLocalTaskRunner();
|
|
||||||
|
|
||||||
void PostTask(Location from, Closure task, bool front = false);
|
void PostTaskAndReply(const Location& from, Closure task, Closure reply);
|
||||||
|
|
||||||
void PostTaskAndReply(Location from,
|
|
||||||
Closure task,
|
|
||||||
Closure reply,
|
|
||||||
bool front = false);
|
|
||||||
|
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
void PostTaskAndReplyWithResult(Location from,
|
void PostTaskAndReplyWithResult(const Location& from,
|
||||||
std::function<ReturnType()> task,
|
std::function<ReturnType()> task,
|
||||||
std::function<void(ReturnType)> reply,
|
std::function<void(ReturnType)> reply) {
|
||||||
bool front = false) {
|
auto* result = new ReturnType;
|
||||||
auto result = std::make_shared<ReturnType>();
|
|
||||||
return PostTaskAndReply(
|
return PostTaskAndReply(
|
||||||
from,
|
from,
|
||||||
std::bind(internal::ReturnAsParamAdapter<ReturnType>, std::move(task),
|
std::bind(internal::ReturnAsParamAdapter<ReturnType>, std::move(task),
|
||||||
result),
|
result),
|
||||||
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply), result),
|
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply),
|
||||||
front);
|
result));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Posts a task that deletes the given object.
|
void MultiConsumerRun();
|
||||||
template <class T>
|
void SingleConsumerRun();
|
||||||
void Delete(Location from, std::unique_ptr<T> object) {
|
|
||||||
// std::function target must be copy-constructible
|
|
||||||
std::shared_ptr<T> owned = std::move(object);
|
|
||||||
PostTask(HERE, [owned]() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CancelTasks();
|
void CancelTasks();
|
||||||
|
|
||||||
void WaitForCompletion();
|
void WaitForCompletion();
|
||||||
|
|
||||||
void RunTasks();
|
static void CreateThreadLocalTaskRunner();
|
||||||
|
static TaskRunner* GetThreadLocalTaskRunner();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Task = std::tuple<Location, Closure>;
|
using Task = std::tuple<Location, Closure>;
|
||||||
|
@ -85,8 +74,11 @@ class TaskRunner {
|
||||||
std::deque<Task> queue_;
|
std::deque<Task> queue_;
|
||||||
mutable std::mutex lock_;
|
mutable std::mutex lock_;
|
||||||
std::atomic<size_t> task_count_{0};
|
std::atomic<size_t> task_count_{0};
|
||||||
|
std::atomic<bool> cancel_tasks_{false};
|
||||||
|
|
||||||
static thread_local std::shared_ptr<TaskRunner> thread_local_task_runner;
|
static thread_local std::unique_ptr<TaskRunner> thread_local_task_runner;
|
||||||
|
|
||||||
|
void CancelTasksInternal();
|
||||||
|
|
||||||
TaskRunner(TaskRunner const&) = delete;
|
TaskRunner(TaskRunner const&) = delete;
|
||||||
TaskRunner& operator=(TaskRunner const&) = delete;
|
TaskRunner& operator=(TaskRunner const&) = delete;
|
||||||
|
|
|
@ -40,30 +40,30 @@ void ThreadPool::Shutdown() {
|
||||||
threads_.clear();
|
threads_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::PostTask(Location from, Closure task, bool front) {
|
void ThreadPool::PostTask(const Location& from, Closure task) {
|
||||||
task_runner_.PostTask(from, std::move(task), front);
|
DCHECK((!threads_.empty()));
|
||||||
|
|
||||||
|
task_runner_.PostTask(from, std::move(task));
|
||||||
semaphore_.release();
|
semaphore_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::PostTaskAndReply(Location from,
|
void ThreadPool::PostTaskAndReply(const Location& from,
|
||||||
Closure task,
|
Closure task,
|
||||||
Closure reply,
|
Closure reply) {
|
||||||
bool front) {
|
DCHECK((!threads_.empty()));
|
||||||
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply), front);
|
|
||||||
semaphore_.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadPool::CancelTasks() {
|
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply));
|
||||||
task_runner_.CancelTasks();
|
semaphore_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::WorkerMain() {
|
void ThreadPool::WorkerMain() {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
semaphore_.acquire();
|
semaphore_.acquire();
|
||||||
|
|
||||||
if (quit_.load(std::memory_order_relaxed))
|
if (quit_.load(std::memory_order_relaxed))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
task_runner_.RunTasks();
|
task_runner_.MultiConsumerRun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
class TaskRunner;
|
||||||
|
|
||||||
// Feed the ThreadPool tasks (in the form of Closure objects) and they will be
|
// Feed the ThreadPool tasks (in the form of Closure objects) and they will be
|
||||||
// called on any thread from the pool.
|
// called on any thread from the pool.
|
||||||
class ThreadPool {
|
class ThreadPool {
|
||||||
|
@ -24,25 +26,19 @@ class ThreadPool {
|
||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
void PostTask(Location from, Closure task, bool front = false);
|
void PostTask(const Location& from, Closure task);
|
||||||
|
|
||||||
void PostTaskAndReply(Location from,
|
void PostTaskAndReply(const Location& from, Closure task, Closure reply);
|
||||||
Closure task,
|
|
||||||
Closure reply,
|
|
||||||
bool front = false);
|
|
||||||
|
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
void PostTaskAndReplyWithResult(Location from,
|
void PostTaskAndReplyWithResult(const Location& from,
|
||||||
std::function<ReturnType()> task,
|
std::function<ReturnType()> task,
|
||||||
std::function<void(ReturnType)> reply,
|
std::function<void(ReturnType)> reply) {
|
||||||
bool front = false) {
|
|
||||||
task_runner_.PostTaskAndReplyWithResult(from, std::move(task),
|
task_runner_.PostTaskAndReplyWithResult(from, std::move(task),
|
||||||
std::move(reply), front);
|
std::move(reply));
|
||||||
semaphore_.release();
|
semaphore_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelTasks();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::thread> threads_;
|
std::vector<std::thread> threads_;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
Timer::Timer() {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::Reset() {
|
||||||
|
gettimeofday(&last_time_, nullptr);
|
||||||
|
|
||||||
|
seconds_passed_ = 0.0f;
|
||||||
|
seconds_accumulated_ = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::Update() {
|
||||||
|
timeval currentTime;
|
||||||
|
gettimeofday(¤tTime, nullptr);
|
||||||
|
seconds_passed_ =
|
||||||
|
(float)(currentTime.tv_sec - last_time_.tv_sec) +
|
||||||
|
0.000001f * (float)(currentTime.tv_usec - last_time_.tv_usec);
|
||||||
|
|
||||||
|
last_time_ = currentTime;
|
||||||
|
|
||||||
|
seconds_accumulated_ += seconds_passed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::Sleep(float duration) {
|
||||||
|
Timer timer;
|
||||||
|
float accumulator = 0.0;
|
||||||
|
constexpr float epsilon = 0.0001f;
|
||||||
|
|
||||||
|
while (accumulator < duration) {
|
||||||
|
timer.Update();
|
||||||
|
accumulator += timer.GetSecondsPassed();
|
||||||
|
if (duration - accumulator > epsilon) {
|
||||||
|
float sleep_time = duration - accumulator - epsilon;
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::microseconds((int)(sleep_time * 1000000.0f)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
|
@ -1,47 +1,31 @@
|
||||||
#ifndef BASE_TIMER_H
|
#ifndef BASE_TIMER_H
|
||||||
#define BASE_TIMER_H
|
#define BASE_TIMER_H
|
||||||
|
|
||||||
#include <chrono>
|
#include <sys/time.h>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
class ElapsedTimer {
|
class Timer {
|
||||||
public:
|
public:
|
||||||
ElapsedTimer() { time_ = std::chrono::high_resolution_clock::now(); }
|
Timer();
|
||||||
|
~Timer() = default;
|
||||||
|
|
||||||
// Return seconds passed since creating the object.
|
void Reset();
|
||||||
double Elapsed() const {
|
|
||||||
auto current_time = std::chrono::high_resolution_clock::now();
|
void Update();
|
||||||
std::chrono::duration<double> diff = current_time - time_;
|
|
||||||
return diff.count();
|
static void Sleep(float duration);
|
||||||
}
|
|
||||||
|
float GetSecondsPassed() const { return seconds_passed_; }
|
||||||
|
float GetSecondsAccumulated() const { return seconds_accumulated_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> time_;
|
float seconds_passed_ = 0.0f;
|
||||||
|
float seconds_accumulated_ = 0.0f;
|
||||||
|
|
||||||
|
timeval last_time_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeltaTimer {
|
|
||||||
public:
|
|
||||||
DeltaTimer() { time_ = std::chrono::high_resolution_clock::now(); }
|
|
||||||
|
|
||||||
// Return seconds passed since the last call to this function.
|
|
||||||
double Delta() {
|
|
||||||
auto current_time = std::chrono::high_resolution_clock::now();
|
|
||||||
std::chrono::duration<double> diff = current_time - time_;
|
|
||||||
time_ = current_time;
|
|
||||||
return diff.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> time_;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void Sleep(double seconds) {
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::duration<double, std::milli>(seconds * 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_TIMER_H
|
#endif // BASE_TIMER_H
|
||||||
|
|
|
@ -1247,8 +1247,8 @@ class Matrix4 {
|
||||||
T fov_aspect,
|
T fov_aspect,
|
||||||
T width,
|
T width,
|
||||||
T height,
|
T height,
|
||||||
T near_plane,
|
T near,
|
||||||
T far_plane) {
|
T far) {
|
||||||
// Calc x and y scale from FOV.
|
// Calc x and y scale from FOV.
|
||||||
T scale =
|
T scale =
|
||||||
T(2.0) /
|
T(2.0) /
|
||||||
|
@ -1257,8 +1257,8 @@ class Matrix4 {
|
||||||
T x_scale = y_scale / (width / height);
|
T x_scale = y_scale / (width / height);
|
||||||
_M_SET_ROW(0, x_scale / T(2.0), 0, 0, 0);
|
_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(1, 0, (-y_scale) / T(2.0), 0, 0);
|
||||||
_M_SET_ROW(2, 0, 0, far_plane / (far_plane - near_plane), 1);
|
_M_SET_ROW(2, 0, 0, far / (far - near), 1);
|
||||||
_M_SET_ROW(3, 0, 0, -near_plane * far_plane / (far_plane - near_plane), 0);
|
_M_SET_ROW(3, 0, 0, -near * far / (far - near), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateTranslation(const Vector3<T>& t) {
|
void CreateTranslation(const Vector3<T>& t) {
|
||||||
|
@ -1343,7 +1343,7 @@ class Matrix4 {
|
||||||
M_x_RotZ(angles[2]);
|
M_x_RotZ(angles[2]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED();
|
NOTREACHED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1534,7 +1534,7 @@ class Matrix4 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
NOTREACHED();
|
NOTREACHED;
|
||||||
}
|
}
|
||||||
return -angles;
|
return -angles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import("//build/rules.gni")
|
|
||||||
|
|
||||||
game("demo") {
|
|
||||||
sources = [
|
|
||||||
"credits.cc",
|
|
||||||
"credits.h",
|
|
||||||
"damage_type.h",
|
|
||||||
"demo.cc",
|
|
||||||
"demo.h",
|
|
||||||
"enemy.cc",
|
|
||||||
"enemy.h",
|
|
||||||
"hud.cc",
|
|
||||||
"hud.h",
|
|
||||||
"menu.cc",
|
|
||||||
"menu.h",
|
|
||||||
"player.cc",
|
|
||||||
"player.h",
|
|
||||||
"sky_quad.cc",
|
|
||||||
"sky_quad.h",
|
|
||||||
]
|
|
||||||
deps = [
|
|
||||||
"//assets/demo",
|
|
||||||
"//src/base",
|
|
||||||
"//src/engine",
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -3,9 +3,9 @@
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/vecmath.h"
|
#include "base/vecmath.h"
|
||||||
#include "demo/demo.h"
|
#include "demo/demo.h"
|
||||||
#include "engine/asset/font.h"
|
|
||||||
#include "engine/asset/image.h"
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/font.h"
|
||||||
|
#include "engine/image.h"
|
||||||
#include "engine/input_event.h"
|
#include "engine/input_event.h"
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
|
@ -13,8 +13,11 @@
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
#include "engine/game_factory.h"
|
#include "engine/game_factory.h"
|
||||||
#include "engine/input_event.h"
|
#include "engine/input_event.h"
|
||||||
|
#include "engine/sound.h"
|
||||||
|
|
||||||
GAME_FACTORIES{GAME_CLASS(Demo)};
|
DECLARE_GAME_BEGIN
|
||||||
|
DECLARE_GAME(Demo)
|
||||||
|
DECLARE_GAME_END
|
||||||
|
|
||||||
// #define RECORD 15
|
// #define RECORD 15
|
||||||
// #define REPLAY
|
// #define REPLAY
|
||||||
|
@ -44,73 +47,59 @@ Demo::~Demo() {
|
||||||
saved_data_.Save();
|
saved_data_.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Demo::PreInitialize() {
|
|
||||||
if (!font_.Load("demo/PixelCaps!.ttf"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Engine::Get().SetShaderSource("sky_without_nebula",
|
|
||||||
"demo/sky_without_nebula.glsl");
|
|
||||||
Engine::Get().SetShaderSource("sky", "demo/sky.glsl");
|
|
||||||
|
|
||||||
Engine::Get().AsyncLoadSound("music", "demo/Game_2_Main.mp3", true);
|
|
||||||
Engine::Get().AsyncLoadSound("boss_music", "demo/Game_2_Boss.mp3", true);
|
|
||||||
|
|
||||||
if (!enemy_.PreInitialize()) {
|
|
||||||
LOG(0) << "Failed to create the enemy.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player_.PreInitialize()) {
|
|
||||||
LOG(0) << "Failed to create the enemy.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!menu_.PreInitialize()) {
|
|
||||||
LOG(0) << "Failed to create the menu.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Demo::Initialize() {
|
bool Demo::Initialize() {
|
||||||
saved_data_.Load(kSaveFileName);
|
saved_data_.Load(kSaveFileName);
|
||||||
|
|
||||||
|
Engine::Get().LoadCustomShader("sky_without_nebula",
|
||||||
|
"sky_without_nebula.glsl");
|
||||||
|
Engine::Get().LoadCustomShader("sky", "sky.glsl");
|
||||||
|
|
||||||
|
if (!font_.Load("PixelCaps!.ttf"))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!sky_.Create(false)) {
|
if (!sky_.Create(false)) {
|
||||||
LOG(0) << "Could not create the sky.";
|
LOG << "Could not create the sky.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enemy_.Initialize()) {
|
if (!enemy_.Initialize()) {
|
||||||
LOG(0) << "Failed to create the enemy.";
|
LOG << "Failed to create the enemy.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!player_.Initialize()) {
|
if (!player_.Initialize()) {
|
||||||
LOG(0) << "Failed to create the enemy.";
|
LOG << "Failed to create the enemy.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hud_.Initialize()) {
|
if (!hud_.Initialize()) {
|
||||||
LOG(0) << "Failed to create the hud.";
|
LOG << "Failed to create the hud.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!menu_.Initialize()) {
|
if (!menu_.Initialize()) {
|
||||||
LOG(0) << "Failed to create the menu.";
|
LOG << "Failed to create the menu.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!credits_.Initialize()) {
|
if (!credits_.Initialize()) {
|
||||||
LOG(0) << "Failed to create the credits.";
|
LOG << "Failed to create the credits.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
music_.SetSound("music");
|
auto sound = std::make_unique<Sound>();
|
||||||
music_.SetMaxAmplitude(0.5f);
|
if (!sound->Load("Game_2_Main.mp3", true))
|
||||||
|
return false;
|
||||||
|
|
||||||
boss_music_.SetSound("boss_music");
|
auto boss_sound = std::make_unique<Sound>();
|
||||||
boss_music_.SetMaxAmplitude(0.5f);
|
if (!boss_sound->Load("Game_2_Boss.mp3", true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
music_.SetSound(std::move(sound));
|
||||||
|
music_.SetMaxAplitude(0.5f);
|
||||||
|
|
||||||
|
boss_music_.SetSound(std::move(boss_sound));
|
||||||
|
boss_music_.SetMaxAplitude(0.5f);
|
||||||
|
|
||||||
if (!saved_data_.root().get("audio", Json::Value(true)).asBool())
|
if (!saved_data_.root().get("audio", Json::Value(true)).asBool())
|
||||||
Engine::Get().SetEnableAudio(false);
|
Engine::Get().SetEnableAudio(false);
|
||||||
|
@ -183,13 +172,12 @@ void Demo::ContextLost() {
|
||||||
num_benchmark_samples_ = 0;
|
num_benchmark_samples_ = 0;
|
||||||
avarage_fps_ = 0;
|
avarage_fps_ = 0;
|
||||||
}
|
}
|
||||||
menu_.SetRendererType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Demo::LostFocus() {}
|
void Demo::LostFocus() {}
|
||||||
|
|
||||||
void Demo::GainedFocus(bool from_interstitial_ad) {
|
void Demo::GainedFocus(bool from_interstitial_ad) {
|
||||||
DLOG(0) << __func__ << " from_interstitial_ad: " << from_interstitial_ad;
|
DLOG << __func__ << " from_interstitial_ad: " << from_interstitial_ad;
|
||||||
if (!from_interstitial_ad) {
|
if (!from_interstitial_ad) {
|
||||||
// if (saved_data_.root().get(kLaunchCount, Json::Value(0)).asInt() >
|
// if (saved_data_.root().get(kLaunchCount, Json::Value(0)).asInt() >
|
||||||
// kLaunchCountBeforeAd)
|
// kLaunchCountBeforeAd)
|
||||||
|
@ -325,7 +313,7 @@ void Demo::UpdateMenuState(float delta_time) {
|
||||||
Engine::Get().Exit();
|
Engine::Get().Exit();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "- Unknown menu option: " << menu_.selected_option();
|
NOTREACHED << "- Unknown menu option: " << menu_.selected_option();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +397,7 @@ void Demo::StartNextStage(bool boss) {
|
||||||
waiting_for_next_wave_ = true;
|
waiting_for_next_wave_ = true;
|
||||||
hud_.SetProgress(wave_ > 0 ? 0 : 1);
|
hud_.SetProgress(wave_ > 0 ? 0 : 1);
|
||||||
|
|
||||||
DLOG_IF(0, wave_ > 0 && stage_time_ > 0)
|
DLOG_IF(wave_ > 0 && stage_time_ > 0)
|
||||||
<< "wave: " << wave_ << " time: " << stage_time_ / 60.0f;
|
<< "wave: " << wave_ << " time: " << stage_time_ / 60.0f;
|
||||||
stage_time_ = 0;
|
stage_time_ = 0;
|
||||||
|
|
||||||
|
@ -430,7 +418,7 @@ void Demo::StartNextStage(bool boss) {
|
||||||
music_.Stop(10);
|
music_.Stop(10);
|
||||||
}
|
}
|
||||||
boss_fight_ = true;
|
boss_fight_ = true;
|
||||||
DLOG(0) << "Boss fight.";
|
DLOG << "Boss fight.";
|
||||||
} else {
|
} else {
|
||||||
size_t bonus_factor = [&]() -> size_t {
|
size_t bonus_factor = [&]() -> size_t {
|
||||||
if (wave_ <= 3)
|
if (wave_ <= 3)
|
||||||
|
@ -442,7 +430,7 @@ void Demo::StartNextStage(bool boss) {
|
||||||
return 100;
|
return 100;
|
||||||
}();
|
}();
|
||||||
size_t bonus_score = wave_score_ * (bonus_factor - 1);
|
size_t bonus_score = wave_score_ * (bonus_factor - 1);
|
||||||
DLOG(0) << "total_score_" << total_score_ << " wave " << wave_
|
DLOG << "total_score_" << total_score_ << " wave " << wave_
|
||||||
<< " score: " << wave_score_ << " bonus: " << bonus_score;
|
<< " score: " << wave_score_ << " bonus: " << bonus_score;
|
||||||
|
|
||||||
if (bonus_score > 0) {
|
if (bonus_score > 0) {
|
||||||
|
@ -482,7 +470,7 @@ void Demo::StartNextStage(bool boss) {
|
||||||
total_enemies_ = 23.0897f * log((float)wave_ + 1.0f) - 10.0f;
|
total_enemies_ = 23.0897f * log((float)wave_ + 1.0f) - 10.0f;
|
||||||
last_num_enemies_killed_ = 0;
|
last_num_enemies_killed_ = 0;
|
||||||
boss_fight_ = false;
|
boss_fight_ = false;
|
||||||
DLOG(0) << "wave: " << wave_ << " total_enemies_: " << total_enemies_;
|
DLOG << "wave: " << wave_ << " total_enemies_: " << total_enemies_;
|
||||||
}
|
}
|
||||||
|
|
||||||
hud_.SetScore(total_score_, true);
|
hud_.SetScore(total_score_, true);
|
||||||
|
@ -533,7 +521,7 @@ void Demo::SetDelayedWork(float seconds, base::Closure cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Demo::BenchmarkResult(int avarage_fps) {
|
void Demo::BenchmarkResult(int avarage_fps) {
|
||||||
LOG(0) << __func__ << " avarage_fps: " << avarage_fps;
|
LOG << __func__ << " avarage_fps: " << avarage_fps;
|
||||||
if (avarage_fps < 30)
|
if (avarage_fps < 30)
|
||||||
sky_.Create(true);
|
sky_.Create(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "base/closure.h"
|
#include "base/closure.h"
|
||||||
#include "engine/animator.h"
|
#include "engine/animator.h"
|
||||||
#include "engine/asset/font.h"
|
#include "engine/font.h"
|
||||||
#include "engine/game.h"
|
#include "engine/game.h"
|
||||||
#include "engine/persistent_data.h"
|
#include "engine/persistent_data.h"
|
||||||
#include "engine/solid_quad.h"
|
#include "engine/solid_quad.h"
|
||||||
|
@ -23,12 +23,14 @@ class Demo final : public eng::Game {
|
||||||
Demo();
|
Demo();
|
||||||
~Demo() final;
|
~Demo() final;
|
||||||
|
|
||||||
// Game interface
|
|
||||||
bool PreInitialize() final;
|
|
||||||
bool Initialize() final;
|
bool Initialize() final;
|
||||||
|
|
||||||
void Update(float delta_time) final;
|
void Update(float delta_time) final;
|
||||||
|
|
||||||
void ContextLost() final;
|
void ContextLost() final;
|
||||||
|
|
||||||
void LostFocus() final;
|
void LostFocus() final;
|
||||||
|
|
||||||
void GainedFocus(bool from_interstitial_ad) final;
|
void GainedFocus(bool from_interstitial_ad) final;
|
||||||
|
|
||||||
void AddScore(size_t score);
|
void AddScore(size_t score);
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
#include "base/collusion_test.h"
|
#include "base/collusion_test.h"
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "engine/asset/font.h"
|
|
||||||
#include "engine/asset/image.h"
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/font.h"
|
||||||
|
#include "engine/image.h"
|
||||||
#include "engine/renderer/geometry.h"
|
#include "engine/renderer/geometry.h"
|
||||||
|
#include "engine/sound.h"
|
||||||
|
|
||||||
#include "demo/demo.h"
|
#include "demo/demo.h"
|
||||||
|
|
||||||
|
@ -77,50 +78,46 @@ Enemy::Enemy() = default;
|
||||||
|
|
||||||
Enemy::~Enemy() = default;
|
Enemy::~Enemy() = default;
|
||||||
|
|
||||||
bool Enemy::PreInitialize() {
|
|
||||||
Engine::Get().SetImageSource("skull_tex", "demo/enemy_anims_01_frames_ok.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("bug_tex", "demo/enemy_anims_02_frames_ok.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("boss_tex1", "demo/Boss_ok.png", true);
|
|
||||||
Engine::Get().SetImageSource("boss_tex2", "demo/Boss_ok_lvl2.png", true);
|
|
||||||
Engine::Get().SetImageSource("boss_tex3", "demo/Boss_ok_lvl3.png", true);
|
|
||||||
Engine::Get().SetImageSource("target_tex", "demo/enemy_target_single_ok.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("blast_tex", "demo/enemy_anims_blast_ok.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("shield_tex", "demo/woom_enemy_shield.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("crate_tex", "demo/nuke_pack_OK.png", true);
|
|
||||||
|
|
||||||
for (int i = 0; i < kEnemyType_Max; ++i) {
|
|
||||||
if (i == kEnemyType_PowerUp)
|
|
||||||
continue;
|
|
||||||
Engine::Get().SetImageSource(
|
|
||||||
"score_tex"s + std::to_string(i),
|
|
||||||
std::bind(&Enemy::GetScoreImage, this, (EnemyType)i), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine::Get().SetShaderSource("chromatic_aberration",
|
|
||||||
"demo/chromatic_aberration.glsl");
|
|
||||||
|
|
||||||
Engine::Get().AsyncLoadSound("boss_intro", "demo/boss_intro.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("boss_explosion", "demo/boss_explosion.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("explosion", "demo/explosion.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("stealth", "demo/stealth.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("shield", "demo/shield.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("hit", "demo/hit.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("powerup-spawn", "demo/powerup-spawn.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("powerup-pick", "demo/powerup-pick.mp3");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Enemy::Initialize() {
|
bool Enemy::Initialize() {
|
||||||
|
boss_intro_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!boss_intro_sound_->Load("boss_intro.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_explosion_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!boss_explosion_sound_->Load("boss_explosion.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
explosion_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!explosion_sound_->Load("explosion.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stealth_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!stealth_sound_->Load("stealth.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
shield_on_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!shield_on_sound_->Load("shield.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hit_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!hit_sound_->Load("hit.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
power_up_spawn_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!power_up_spawn_sound_->Load("powerup-spawn.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
power_up_pick_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!power_up_pick_sound_->Load("powerup-pick.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!CreateRenderResources())
|
||||||
|
return false;
|
||||||
|
|
||||||
boss_.SetZOrder(10);
|
boss_.SetZOrder(10);
|
||||||
boss_animator_.Attach(&boss_);
|
boss_animator_.Attach(&boss_);
|
||||||
|
|
||||||
boss_intro_.SetSound("boss_intro");
|
boss_intro_.SetSound(boss_intro_sound_);
|
||||||
boss_intro_.SetVariate(false);
|
boss_intro_.SetVariate(false);
|
||||||
boss_intro_.SetSimulateStereo(false);
|
boss_intro_.SetSimulateStereo(false);
|
||||||
|
|
||||||
|
@ -516,7 +513,7 @@ void Enemy::OnWaveStarted(int wave, bool boss_fight) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
return 1.6f;
|
return 1.6f;
|
||||||
}();
|
}();
|
||||||
DLOG(0) << "boss_spawn_time_factor_: " << boss_spawn_time_factor_;
|
DLOG << "boss_spawn_time_factor_: " << boss_spawn_time_factor_;
|
||||||
SpawnBoss();
|
SpawnBoss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -607,7 +604,7 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
|
||||||
e.sprite.Create("crate_tex", {8, 3});
|
e.sprite.Create("crate_tex", {8, 3});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "- Unkown enemy type: " << enemy_type;
|
NOTREACHED << "- Unkown enemy type: " << enemy_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sprite.SetZOrder(11);
|
e.sprite.SetZOrder(11);
|
||||||
|
@ -653,13 +650,11 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
|
||||||
e.health_bar.PlaceToBottomOf(e.sprite);
|
e.health_bar.PlaceToBottomOf(e.sprite);
|
||||||
e.health_bar.SetColor({0.161f, 0.89f, 0.322f, 1});
|
e.health_bar.SetColor({0.161f, 0.89f, 0.322f, 1});
|
||||||
|
|
||||||
if (enemy_type != kEnemyType_PowerUp) {
|
|
||||||
e.score.Create("score_tex"s + std::to_string(e.enemy_type));
|
e.score.Create("score_tex"s + std::to_string(e.enemy_type));
|
||||||
e.score.SetZOrder(12);
|
e.score.SetZOrder(12);
|
||||||
e.score.Scale(1.1f);
|
e.score.Scale(1.1f);
|
||||||
e.score.SetColor({1, 1, 1, 1});
|
e.score.SetColor({1, 1, 1, 1});
|
||||||
e.score.SetPosition(spawn_pos);
|
e.score.SetPosition(spawn_pos);
|
||||||
}
|
|
||||||
|
|
||||||
e.target_animator.Attach(&e.target);
|
e.target_animator.Attach(&e.target);
|
||||||
|
|
||||||
|
@ -734,32 +729,32 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
|
||||||
e.movement_animator.Play(Animator::kMovement, false);
|
e.movement_animator.Play(Animator::kMovement, false);
|
||||||
|
|
||||||
if (e.enemy_type == kEnemyType_PowerUp) {
|
if (e.enemy_type == kEnemyType_PowerUp) {
|
||||||
e.explosion.SetSound("powerup-pick");
|
e.explosion.SetSound(power_up_pick_sound_);
|
||||||
|
|
||||||
e.spawn.SetSound("powerup-spawn");
|
e.spawn.SetSound(power_up_spawn_sound_);
|
||||||
e.spawn.SetMaxAmplitude(2.0f);
|
e.spawn.SetMaxAplitude(2.0f);
|
||||||
e.spawn.Play(false);
|
e.spawn.Play(false);
|
||||||
} else {
|
} else {
|
||||||
e.explosion.SetSound("explosion");
|
e.explosion.SetSound(explosion_sound_);
|
||||||
e.explosion.SetVariate(true);
|
e.explosion.SetVariate(true);
|
||||||
e.explosion.SetSimulateStereo(true);
|
e.explosion.SetSimulateStereo(true);
|
||||||
e.explosion.SetMaxAmplitude(0.9f);
|
e.explosion.SetMaxAplitude(0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
e.stealth.SetSound("stealth");
|
e.stealth.SetSound(stealth_sound_);
|
||||||
e.stealth.SetVariate(false);
|
e.stealth.SetVariate(false);
|
||||||
e.stealth.SetSimulateStereo(false);
|
e.stealth.SetSimulateStereo(false);
|
||||||
e.stealth.SetMaxAmplitude(0.7f);
|
e.stealth.SetMaxAplitude(0.7f);
|
||||||
|
|
||||||
e.shield_on.SetSound("shield");
|
e.shield_on.SetSound(shield_on_sound_);
|
||||||
e.shield_on.SetVariate(false);
|
e.shield_on.SetVariate(false);
|
||||||
e.shield_on.SetSimulateStereo(false);
|
e.shield_on.SetSimulateStereo(false);
|
||||||
e.shield_on.SetMaxAmplitude(0.5f);
|
e.shield_on.SetMaxAplitude(0.5f);
|
||||||
|
|
||||||
e.hit.SetSound("hit");
|
e.hit.SetSound(hit_sound_);
|
||||||
e.hit.SetVariate(true);
|
e.hit.SetVariate(true);
|
||||||
e.hit.SetSimulateStereo(false);
|
e.hit.SetSimulateStereo(false);
|
||||||
e.hit.SetMaxAmplitude(0.5f);
|
e.hit.SetMaxAplitude(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::SpawnBoss() {
|
void Enemy::SpawnBoss() {
|
||||||
|
@ -786,7 +781,7 @@ void Enemy::SpawnBoss() {
|
||||||
e.enemy_type = kEnemyType_Boss;
|
e.enemy_type = kEnemyType_Boss;
|
||||||
e.damage_type = kDamageType_Any;
|
e.damage_type = kDamageType_Any;
|
||||||
e.total_health = e.hit_points = 41.1283f * log((float)game->wave()) - 20.0f;
|
e.total_health = e.hit_points = 41.1283f * log((float)game->wave()) - 20.0f;
|
||||||
DLOG(0) << " Boss health: " << e.total_health;
|
DLOG << " Boss health: " << e.total_health;
|
||||||
|
|
||||||
Vector2f hit_box_pos =
|
Vector2f hit_box_pos =
|
||||||
boss_.GetPosition() - boss_.GetSize() * Vector2f(0, 0.2f);
|
boss_.GetPosition() - boss_.GetSize() * Vector2f(0, 0.2f);
|
||||||
|
@ -830,14 +825,14 @@ void Enemy::SpawnBoss() {
|
||||||
Animator::kMovement, [&]() -> void { e.marked_for_removal = true; });
|
Animator::kMovement, [&]() -> void { e.marked_for_removal = true; });
|
||||||
e.score_animator.Attach(&e.score);
|
e.score_animator.Attach(&e.score);
|
||||||
|
|
||||||
e.explosion.SetSound("boss_explosion");
|
e.explosion.SetSound(boss_explosion_sound_);
|
||||||
e.explosion.SetVariate(false);
|
e.explosion.SetVariate(false);
|
||||||
e.explosion.SetSimulateStereo(false);
|
e.explosion.SetSimulateStereo(false);
|
||||||
|
|
||||||
e.hit.SetSound("hit");
|
e.hit.SetSound(hit_sound_);
|
||||||
e.hit.SetVariate(true);
|
e.hit.SetVariate(true);
|
||||||
e.hit.SetSimulateStereo(false);
|
e.hit.SetSimulateStereo(false);
|
||||||
e.hit.SetMaxAmplitude(0.5f);
|
e.hit.SetMaxAplitude(0.5f);
|
||||||
});
|
});
|
||||||
boss_animator_.Play(Animator::kFrames, true);
|
boss_animator_.Play(Animator::kFrames, true);
|
||||||
boss_animator_.Play(Animator::kMovement, false);
|
boss_animator_.Play(Animator::kMovement, false);
|
||||||
|
@ -1172,7 +1167,11 @@ int Enemy::GetScore(EnemyType enemy_type) {
|
||||||
std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
|
std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
|
||||||
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
||||||
|
|
||||||
std::string text = std::to_string(GetScore(enemy_type));
|
int score = GetScore(enemy_type);
|
||||||
|
if (!score)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::string text = std::to_string(score);
|
||||||
int width, height;
|
int width, height;
|
||||||
font.CalculateBoundingBox(text.c_str(), width, height);
|
font.CalculateBoundingBox(text.c_str(), width, height);
|
||||||
|
|
||||||
|
@ -1186,6 +1185,30 @@ std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Enemy::CreateRenderResources() {
|
||||||
|
Engine::Get().SetImageSource("skull_tex", "enemy_anims_01_frames_ok.png",
|
||||||
|
true);
|
||||||
|
Engine::Get().SetImageSource("bug_tex", "enemy_anims_02_frames_ok.png", true);
|
||||||
|
Engine::Get().SetImageSource("boss_tex1", "Boss_ok.png", true);
|
||||||
|
Engine::Get().SetImageSource("boss_tex2", "Boss_ok_lvl2.png", true);
|
||||||
|
Engine::Get().SetImageSource("boss_tex3", "Boss_ok_lvl3.png", true);
|
||||||
|
Engine::Get().SetImageSource("target_tex", "enemy_target_single_ok.png",
|
||||||
|
true);
|
||||||
|
Engine::Get().SetImageSource("blast_tex", "enemy_anims_blast_ok.png", true);
|
||||||
|
Engine::Get().SetImageSource("shield_tex", "woom_enemy_shield.png", true);
|
||||||
|
Engine::Get().SetImageSource("crate_tex", "nuke_pack_OK.png", true);
|
||||||
|
|
||||||
|
for (int i = 0; i < kEnemyType_Max; ++i)
|
||||||
|
Engine::Get().SetImageSource(
|
||||||
|
"score_tex"s + std::to_string(i),
|
||||||
|
std::bind(&Enemy::GetScoreImage, this, (EnemyType)i), true);
|
||||||
|
|
||||||
|
Engine::Get().LoadCustomShader("chromatic_aberration",
|
||||||
|
"chromatic_aberration.glsl");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Enemy::TranslateEnemyUnit(EnemyUnit& e, const Vector2f& delta) {
|
void Enemy::TranslateEnemyUnit(EnemyUnit& e, const Vector2f& delta) {
|
||||||
e.sprite.Translate(delta);
|
e.sprite.Translate(delta);
|
||||||
e.target.Translate(delta);
|
e.target.Translate(delta);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
class Image;
|
class Image;
|
||||||
|
class Sound;
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
||||||
class Enemy {
|
class Enemy {
|
||||||
|
@ -22,7 +23,6 @@ class Enemy {
|
||||||
Enemy();
|
Enemy();
|
||||||
~Enemy();
|
~Enemy();
|
||||||
|
|
||||||
bool PreInitialize();
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
void Update(float delta_time);
|
void Update(float delta_time);
|
||||||
|
@ -109,6 +109,15 @@ class Enemy {
|
||||||
eng::Animator boss_animator_;
|
eng::Animator boss_animator_;
|
||||||
eng::SoundPlayer boss_intro_;
|
eng::SoundPlayer boss_intro_;
|
||||||
|
|
||||||
|
std::shared_ptr<eng::Sound> boss_intro_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> boss_explosion_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> explosion_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> stealth_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> shield_on_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> hit_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> power_up_spawn_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> power_up_pick_sound_;
|
||||||
|
|
||||||
std::list<EnemyUnit> enemies_;
|
std::list<EnemyUnit> enemies_;
|
||||||
|
|
||||||
int num_enemies_killed_in_current_wave_ = 0;
|
int num_enemies_killed_in_current_wave_ = 0;
|
||||||
|
@ -155,6 +164,8 @@ class Enemy {
|
||||||
|
|
||||||
std::unique_ptr<eng::Image> GetScoreImage(EnemyType enemy_type);
|
std::unique_ptr<eng::Image> GetScoreImage(EnemyType enemy_type);
|
||||||
|
|
||||||
|
bool CreateRenderResources();
|
||||||
|
|
||||||
void TranslateEnemyUnit(EnemyUnit& e, const base::Vector2f& delta);
|
void TranslateEnemyUnit(EnemyUnit& e, const base::Vector2f& delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/vecmath.h"
|
#include "base/vecmath.h"
|
||||||
#include "engine/asset/font.h"
|
|
||||||
#include "engine/asset/image.h"
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/font.h"
|
||||||
|
#include "engine/image.h"
|
||||||
|
|
||||||
#include "demo/demo.h"
|
#include "demo/demo.h"
|
||||||
|
|
||||||
|
@ -198,6 +198,7 @@ void Hud::ShowMessage(const std::string& text, float duration) {
|
||||||
message_text_ = text;
|
message_text_ = text;
|
||||||
Engine::Get().RefreshImage("message");
|
Engine::Get().RefreshImage("message");
|
||||||
|
|
||||||
|
message_.AutoScale();
|
||||||
message_.Scale(1.5f);
|
message_.Scale(1.5f);
|
||||||
message_.SetColor({1, 1, 1, 0});
|
message_.SetColor({1, 1, 1, 0});
|
||||||
message_.SetVisible(true);
|
message_.SetVisible(true);
|
||||||
|
@ -214,6 +215,7 @@ void Hud::ShowMessage(const std::string& text, float duration) {
|
||||||
void Hud::ShowBonus(size_t bonus) {
|
void Hud::ShowBonus(size_t bonus) {
|
||||||
bonus_score_ = bonus;
|
bonus_score_ = bonus;
|
||||||
Engine::Get().RefreshImage("bonus_tex");
|
Engine::Get().RefreshImage("bonus_tex");
|
||||||
|
bonus_.AutoScale();
|
||||||
bonus_.Scale(1.3f);
|
bonus_.Scale(1.3f);
|
||||||
bonus_.SetColor({1, 1, 1, 1});
|
bonus_.SetColor({1, 1, 1, 1});
|
||||||
bonus_.SetVisible(true);
|
bonus_.SetVisible(true);
|
||||||
|
@ -243,20 +245,26 @@ std::unique_ptr<Image> Hud::CreateMessageImage() {
|
||||||
font.Print(x, 0, message_text_.c_str(), image->GetBuffer(),
|
font.Print(x, 0, message_text_.c_str(), image->GetBuffer(),
|
||||||
image->GetWidth());
|
image->GetWidth());
|
||||||
image->Compress();
|
image->Compress();
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Image> Hud::CreateBonusImage() {
|
std::unique_ptr<Image> Hud::CreateBonusImage() {
|
||||||
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
||||||
|
|
||||||
auto image = CreateImage();
|
if (bonus_score_ == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
std::string text = std::to_string(bonus_score_);
|
std::string text = std::to_string(bonus_score_);
|
||||||
int w, h;
|
int width, height;
|
||||||
font.CalculateBoundingBox(text.c_str(), w, h);
|
font.CalculateBoundingBox(text.c_str(), width, height);
|
||||||
float x = (image->GetWidth() - w) / 2;
|
|
||||||
|
auto image = std::make_unique<Image>();
|
||||||
|
image->Create(width, height);
|
||||||
|
image->Clear({1, 1, 1, 0});
|
||||||
|
|
||||||
|
font.Print(0, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
|
||||||
|
|
||||||
font.Print(x, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
|
|
||||||
image->Compress();
|
image->Compress();
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +282,7 @@ std::unique_ptr<Image> Hud::Print(int i, const std::string& text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
font.Print(x, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
|
font.Print(x, 0, text.c_str(), image->GetBuffer(), image->GetWidth());
|
||||||
image->Compress();
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
122
src/demo/menu.cc
|
@ -7,12 +7,12 @@
|
||||||
#include "base/collusion_test.h"
|
#include "base/collusion_test.h"
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "engine/asset/font.h"
|
|
||||||
#include "engine/asset/image.h"
|
|
||||||
#include "engine/asset/sound.h"
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/font.h"
|
||||||
|
#include "engine/image.h"
|
||||||
#include "engine/input_event.h"
|
#include "engine/input_event.h"
|
||||||
#include "engine/renderer/renderer.h"
|
#include "engine/renderer/renderer.h"
|
||||||
|
#include "engine/sound.h"
|
||||||
|
|
||||||
#include "demo/demo.h"
|
#include "demo/demo.h"
|
||||||
|
|
||||||
|
@ -53,9 +53,9 @@ Menu::Menu() = default;
|
||||||
|
|
||||||
Menu::~Menu() = default;
|
Menu::~Menu() = default;
|
||||||
|
|
||||||
bool Menu::PreInitialize() {
|
bool Menu::Initialize() {
|
||||||
click_sound_ = std::make_shared<Sound>();
|
click_sound_ = std::make_shared<Sound>();
|
||||||
if (!click_sound_->Load("demo/menu_click.mp3", false))
|
if (!click_sound_->Load("menu_click.mp3", false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Demo* game = static_cast<Demo*>(Engine::Get().GetGame());
|
Demo* game = static_cast<Demo*>(Engine::Get().GetGame());
|
||||||
|
@ -70,59 +70,8 @@ bool Menu::PreInitialize() {
|
||||||
max_text_width_ = width;
|
max_text_width_ = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine::Get().SetImageSource("menu_tex",
|
if (!CreateRenderResources())
|
||||||
std::bind(&Menu::CreateMenuImage, this), true);
|
return false;
|
||||||
Engine::Get().SetImageSource("logo_tex0",
|
|
||||||
"demo/woom_logo_start_frames_01.png", true);
|
|
||||||
Engine::Get().SetImageSource("logo_tex1",
|
|
||||||
"demo/woom_logo_start_frames_02-03.png", true);
|
|
||||||
Engine::Get().SetImageSource("buttons_tex", "demo/menu_icons.png", true);
|
|
||||||
Engine::Get().SetImageSource("renderer_logo", "demo/renderer_logo.png", true);
|
|
||||||
|
|
||||||
Engine::Get().SetImageSource(
|
|
||||||
"version_tex",
|
|
||||||
[]() -> std::unique_ptr<Image> {
|
|
||||||
const Font* font = Engine::Get().GetSystemFont();
|
|
||||||
|
|
||||||
int w, h;
|
|
||||||
font->CalculateBoundingBox(kVersionStr, w, h);
|
|
||||||
|
|
||||||
auto image = std::make_unique<Image>();
|
|
||||||
image->Create(w, font->GetLineHeight());
|
|
||||||
image->Clear({1, 1, 1, 0});
|
|
||||||
|
|
||||||
font->Print(0, 0, kVersionStr, image->GetBuffer(), image->GetWidth());
|
|
||||||
|
|
||||||
image->Compress();
|
|
||||||
return image;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
|
|
||||||
Engine::Get().SetImageSource("high_score_tex",
|
|
||||||
std::bind(&Menu::CreateHighScoreImage, this));
|
|
||||||
Engine::Get().SetImageSource("wave_up_tex", []() -> std::unique_ptr<Image> {
|
|
||||||
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
|
||||||
|
|
||||||
constexpr char btn_text[] = "[ ]";
|
|
||||||
|
|
||||||
int w, h;
|
|
||||||
font.CalculateBoundingBox(btn_text, w, h);
|
|
||||||
|
|
||||||
auto image = std::make_unique<Image>();
|
|
||||||
image->Create(w, h);
|
|
||||||
image->Clear({1, 1, 1, 0});
|
|
||||||
|
|
||||||
font.Print(0, 0, btn_text, image->GetBuffer(), image->GetWidth());
|
|
||||||
|
|
||||||
image->Compress();
|
|
||||||
return image;
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Menu::Initialize() {
|
|
||||||
Demo* game = static_cast<Demo*>(Engine::Get().GetGame());
|
|
||||||
|
|
||||||
for (int i = 0; i < kOption_Max; ++i) {
|
for (int i = 0; i < kOption_Max; ++i) {
|
||||||
items_[i].text.Create("menu_tex", {1, 4});
|
items_[i].text.Create("menu_tex", {1, 4});
|
||||||
|
@ -149,7 +98,7 @@ bool Menu::Initialize() {
|
||||||
click_.SetSound(click_sound_);
|
click_.SetSound(click_sound_);
|
||||||
click_.SetVariate(false);
|
click_.SetVariate(false);
|
||||||
click_.SetSimulateStereo(false);
|
click_.SetSimulateStereo(false);
|
||||||
click_.SetMaxAmplitude(1.5f);
|
click_.SetMaxAplitude(1.5f);
|
||||||
|
|
||||||
logo_[0].Create("logo_tex0", {3, 8});
|
logo_[0].Create("logo_tex0", {3, 8});
|
||||||
logo_[0].SetZOrder(41);
|
logo_[0].SetZOrder(41);
|
||||||
|
@ -250,6 +199,9 @@ bool Menu::Initialize() {
|
||||||
Engine::Get().CreateRenderer(renderer_type_.enabled()
|
Engine::Get().CreateRenderer(renderer_type_.enabled()
|
||||||
? RendererType::kVulkan
|
? RendererType::kVulkan
|
||||||
: RendererType::kOpenGL);
|
: RendererType::kOpenGL);
|
||||||
|
renderer_type_.SetEnabled(
|
||||||
|
(Engine::Get().GetRendererType() == RendererType::kVulkan));
|
||||||
|
Engine::Get().ConsumeInputEvents();
|
||||||
},
|
},
|
||||||
true, Engine::Get().GetRendererType() == RendererType::kVulkan,
|
true, Engine::Get().GetRendererType() == RendererType::kVulkan,
|
||||||
kColorFadeOut, {Vector4f{1, 1, 1, 1}, Vector4f{1, 1, 1, 1}});
|
kColorFadeOut, {Vector4f{1, 1, 1, 1}, Vector4f{1, 1, 1, 1}});
|
||||||
|
@ -369,11 +321,6 @@ void Menu::SetOptionEnabled(Option o, bool enable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::SetRendererType() {
|
|
||||||
renderer_type_.SetEnabled(
|
|
||||||
(Engine::Get().GetRendererType() == RendererType::kVulkan));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Menu::Show() {
|
void Menu::Show() {
|
||||||
logo_[1].SetColor(kColorNormal);
|
logo_[1].SetColor(kColorNormal);
|
||||||
logo_animator_[0].SetVisible(true);
|
logo_animator_[0].SetVisible(true);
|
||||||
|
@ -494,6 +441,53 @@ void Menu::Hide(Closure cb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Menu::CreateRenderResources() {
|
||||||
|
Engine::Get().SetImageSource("menu_tex",
|
||||||
|
std::bind(&Menu::CreateMenuImage, this));
|
||||||
|
Engine::Get().SetImageSource("logo_tex0", "woom_logo_start_frames_01.png");
|
||||||
|
Engine::Get().SetImageSource("logo_tex1", "woom_logo_start_frames_02-03.png");
|
||||||
|
Engine::Get().SetImageSource("buttons_tex", "menu_icons.png");
|
||||||
|
Engine::Get().SetImageSource("high_score_tex",
|
||||||
|
std::bind(&Menu::CreateHighScoreImage, this));
|
||||||
|
Engine::Get().SetImageSource("renderer_logo", "renderer_logo.png");
|
||||||
|
|
||||||
|
Engine::Get().SetImageSource("wave_up_tex", []() -> std::unique_ptr<Image> {
|
||||||
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
||||||
|
|
||||||
|
constexpr char btn_text[] = "[ ]";
|
||||||
|
|
||||||
|
int w, h;
|
||||||
|
font.CalculateBoundingBox(btn_text, w, h);
|
||||||
|
|
||||||
|
auto image = std::make_unique<Image>();
|
||||||
|
image->Create(w, h);
|
||||||
|
image->Clear({1, 1, 1, 0});
|
||||||
|
|
||||||
|
font.Print(0, 0, btn_text, image->GetBuffer(), image->GetWidth());
|
||||||
|
|
||||||
|
image->Compress();
|
||||||
|
return image;
|
||||||
|
});
|
||||||
|
|
||||||
|
Engine::Get().SetImageSource("version_tex", []() -> std::unique_ptr<Image> {
|
||||||
|
const Font* font = Engine::Get().GetSystemFont();
|
||||||
|
|
||||||
|
int w, h;
|
||||||
|
font->CalculateBoundingBox(kVersionStr, w, h);
|
||||||
|
|
||||||
|
auto image = std::make_unique<Image>();
|
||||||
|
image->Create(w, font->GetLineHeight());
|
||||||
|
image->Clear({1, 1, 1, 0});
|
||||||
|
|
||||||
|
font->Print(0, 0, kVersionStr, image->GetBuffer(), image->GetWidth());
|
||||||
|
|
||||||
|
image->Compress();
|
||||||
|
return image;
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Image> Menu::CreateMenuImage() {
|
std::unique_ptr<Image> Menu::CreateMenuImage() {
|
||||||
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,11 @@ class Menu {
|
||||||
Menu();
|
Menu();
|
||||||
~Menu();
|
~Menu();
|
||||||
|
|
||||||
bool PreInitialize();
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
void OnInputEvent(std::unique_ptr<eng::InputEvent> event);
|
void OnInputEvent(std::unique_ptr<eng::InputEvent> event);
|
||||||
|
|
||||||
void SetOptionEnabled(Option o, bool enable);
|
void SetOptionEnabled(Option o, bool enable);
|
||||||
void SetRendererType();
|
|
||||||
|
|
||||||
void Show();
|
void Show();
|
||||||
void Hide(base::Closure cb = nullptr);
|
void Hide(base::Closure cb = nullptr);
|
||||||
|
@ -147,6 +145,9 @@ class Menu {
|
||||||
|
|
||||||
Radio starting_wave_;
|
Radio starting_wave_;
|
||||||
Button wave_up_;
|
Button wave_up_;
|
||||||
|
Button wave_down_;
|
||||||
|
|
||||||
|
bool CreateRenderResources();
|
||||||
|
|
||||||
std::unique_ptr<eng::Image> CreateMenuImage();
|
std::unique_ptr<eng::Image> CreateMenuImage();
|
||||||
std::unique_ptr<eng::Image> CreateHighScoreImage();
|
std::unique_ptr<eng::Image> CreateHighScoreImage();
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/vecmath.h"
|
|
||||||
#include "engine/asset/font.h"
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/font.h"
|
||||||
#include "engine/input_event.h"
|
#include "engine/input_event.h"
|
||||||
|
#include "engine/sound.h"
|
||||||
|
|
||||||
#include "demo/demo.h"
|
#include "demo/demo.h"
|
||||||
|
|
||||||
|
@ -29,21 +29,22 @@ Player::Player() = default;
|
||||||
|
|
||||||
Player::~Player() = default;
|
Player::~Player() = default;
|
||||||
|
|
||||||
bool Player::PreInitialize() {
|
|
||||||
Engine::Get().SetImageSource("weapon_tex", "demo/enemy_anims_flare_ok.png",
|
|
||||||
true);
|
|
||||||
Engine::Get().SetImageSource("beam_tex", "demo/enemy_ray_ok.png", true);
|
|
||||||
Engine::Get().SetImageSource("nuke_symbol_tex", "demo/nuke_frames.png", true);
|
|
||||||
Engine::Get().SetImageSource("health_bead", "demo/bead.png", true);
|
|
||||||
|
|
||||||
Engine::Get().AsyncLoadSound("laser", "demo/laser.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("nuke", "demo/nuke.mp3");
|
|
||||||
Engine::Get().AsyncLoadSound("no_nuke", "demo/no_nuke.mp3");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Player::Initialize() {
|
bool Player::Initialize() {
|
||||||
|
if (!CreateRenderResources())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
laser_shot_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!laser_shot_sound_->Load("laser.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nuke_explosion_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!nuke_explosion_sound_->Load("nuke.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
no_nuke_sound_ = std::make_shared<Sound>();
|
||||||
|
if (!no_nuke_sound_->Load("no_nuke.mp3", false))
|
||||||
|
return false;
|
||||||
|
|
||||||
SetupWeapons();
|
SetupWeapons();
|
||||||
|
|
||||||
Vector2f hb_pos = Engine::Get().GetScreenSize() / Vector2f(2, -2) +
|
Vector2f hb_pos = Engine::Get().GetScreenSize() / Vector2f(2, -2) +
|
||||||
|
@ -74,12 +75,12 @@ bool Player::Initialize() {
|
||||||
|
|
||||||
nuke_symbol_animator_.Attach(&nuke_symbol_);
|
nuke_symbol_animator_.Attach(&nuke_symbol_);
|
||||||
|
|
||||||
nuke_explosion_.SetSound("nuke");
|
nuke_explosion_.SetSound(nuke_explosion_sound_);
|
||||||
nuke_explosion_.SetVariate(false);
|
nuke_explosion_.SetVariate(false);
|
||||||
nuke_explosion_.SetSimulateStereo(false);
|
nuke_explosion_.SetSimulateStereo(false);
|
||||||
nuke_explosion_.SetMaxAmplitude(0.8f);
|
nuke_explosion_.SetMaxAplitude(0.8f);
|
||||||
|
|
||||||
no_nuke_.SetSound("no_nuke");
|
no_nuke_.SetSound(no_nuke_sound_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +153,7 @@ void Player::AddNuke(int n) {
|
||||||
|
|
||||||
if (!nuke_symbol_animator_.IsPlaying(Animator::kRotation)) {
|
if (!nuke_symbol_animator_.IsPlaying(Animator::kRotation)) {
|
||||||
nuke_symbol_animator_.SetRotation(
|
nuke_symbol_animator_.SetRotation(
|
||||||
PIf * 5, 2, std::bind(SmootherStep, std::placeholders::_1));
|
M_PI * 5, 2, std::bind(SmootherStep, std::placeholders::_1));
|
||||||
nuke_symbol_animator_.Play(Animator::kRotation, false);
|
nuke_symbol_animator_.Play(Animator::kRotation, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +223,7 @@ void Player::Fire(DamageType type, Vector2f dir) {
|
||||||
|
|
||||||
dir.Normalize();
|
dir.Normalize();
|
||||||
float cos_theta = dir.DotProduct(Vector2f(1, 0));
|
float cos_theta = dir.DotProduct(Vector2f(1, 0));
|
||||||
float theta = acos(cos_theta) + PIf;
|
float theta = acos(cos_theta) + M_PI;
|
||||||
beam_[type].SetTheta(theta);
|
beam_[type].SetTheta(theta);
|
||||||
auto offset = beam_[type].GetRotation() * (len / 2);
|
auto offset = beam_[type].GetRotation() * (len / 2);
|
||||||
beam_[type].Translate({offset.y, -offset.x});
|
beam_[type].Translate({offset.y, -offset.x});
|
||||||
|
@ -287,7 +288,7 @@ void Player::SetupWeapons() {
|
||||||
|
|
||||||
weapon_[i].SetFrame(wepon_warmup_frame[i]);
|
weapon_[i].SetFrame(wepon_warmup_frame[i]);
|
||||||
warmup_animator_[i].SetFrames(wepon_warmup_frame_count, wepon_anim_speed);
|
warmup_animator_[i].SetFrames(wepon_warmup_frame_count, wepon_anim_speed);
|
||||||
warmup_animator_[i].SetRotation(PIf * 2, 20.0f);
|
warmup_animator_[i].SetRotation(M_PI * 2, 20.0f);
|
||||||
warmup_animator_[i].Attach(&weapon_[i]);
|
warmup_animator_[i].Attach(&weapon_[i]);
|
||||||
warmup_animator_[i].Play(Animator::kRotation, true);
|
warmup_animator_[i].Play(Animator::kRotation, true);
|
||||||
|
|
||||||
|
@ -305,10 +306,10 @@ void Player::SetupWeapons() {
|
||||||
beam_animator_[i].SetBlending({1, 1, 1, 0}, 0.16f);
|
beam_animator_[i].SetBlending({1, 1, 1, 0}, 0.16f);
|
||||||
beam_animator_[i].Attach(&beam_[i]);
|
beam_animator_[i].Attach(&beam_[i]);
|
||||||
|
|
||||||
laser_shot_[i].SetSound("laser");
|
laser_shot_[i].SetSound(laser_shot_sound_);
|
||||||
laser_shot_[i].SetVariate(true);
|
laser_shot_[i].SetVariate(true);
|
||||||
laser_shot_[i].SetSimulateStereo(false);
|
laser_shot_[i].SetSimulateStereo(false);
|
||||||
laser_shot_[i].SetMaxAmplitude(0.4f);
|
laser_shot_[i].SetMaxAplitude(0.4f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,3 +485,12 @@ void Player::NavigateBack() {
|
||||||
Engine& engine = Engine::Get();
|
Engine& engine = Engine::Get();
|
||||||
static_cast<Demo*>(engine.GetGame())->EnterMenuState();
|
static_cast<Demo*>(engine.GetGame())->EnterMenuState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Player::CreateRenderResources() {
|
||||||
|
Engine::Get().SetImageSource("weapon_tex", "enemy_anims_flare_ok.png", true);
|
||||||
|
Engine::Get().SetImageSource("beam_tex", "enemy_ray_ok.png", true);
|
||||||
|
Engine::Get().SetImageSource("nuke_symbol_tex", "nuke_frames.png", true);
|
||||||
|
Engine::Get().SetImageSource("health_bead", "bead.png", true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
class InputEvent;
|
class InputEvent;
|
||||||
|
class Sound;
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
||||||
class Player {
|
class Player {
|
||||||
|
@ -20,7 +21,6 @@ class Player {
|
||||||
Player();
|
Player();
|
||||||
~Player();
|
~Player();
|
||||||
|
|
||||||
bool PreInitialize();
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
void Update(float delta_time);
|
void Update(float delta_time);
|
||||||
|
@ -41,6 +41,10 @@ class Player {
|
||||||
int nuke_count() { return nuke_count_; }
|
int nuke_count() { return nuke_count_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::shared_ptr<eng::Sound> nuke_explosion_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> no_nuke_sound_;
|
||||||
|
std::shared_ptr<eng::Sound> laser_shot_sound_;
|
||||||
|
|
||||||
eng::ImageQuad drag_sign_[2];
|
eng::ImageQuad drag_sign_[2];
|
||||||
eng::ImageQuad weapon_[2];
|
eng::ImageQuad weapon_[2];
|
||||||
eng::ImageQuad beam_[2];
|
eng::ImageQuad beam_[2];
|
||||||
|
@ -97,6 +101,8 @@ class Player {
|
||||||
bool ValidateDrag(int i);
|
bool ValidateDrag(int i);
|
||||||
|
|
||||||
void NavigateBack();
|
void NavigateBack();
|
||||||
|
|
||||||
|
bool CreateRenderResources();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEMO_PLAYER_H
|
#endif // DEMO_PLAYER_H
|
||||||
|
|
|
@ -21,8 +21,8 @@ SkyQuad::~SkyQuad() = default;
|
||||||
bool SkyQuad::Create(bool without_nebula) {
|
bool SkyQuad::Create(bool without_nebula) {
|
||||||
without_nebula_ = without_nebula;
|
without_nebula_ = without_nebula;
|
||||||
scale_ = Engine::Get().GetScreenSize();
|
scale_ = Engine::Get().GetScreenSize();
|
||||||
shader_ =
|
shader_ = Engine::Get().GetCustomShader(
|
||||||
Engine::Get().GetShader(without_nebula ? "sky_without_nebula" : "sky");
|
without_nebula ? "sky_without_nebula" : "sky");
|
||||||
|
|
||||||
color_animator_.Attach(this);
|
color_animator_.Attach(this);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
#include "engine/animatable.h"
|
#include "engine/animatable.h"
|
||||||
#include "engine/animator.h"
|
#include "engine/animator.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
class Shader;
|
class Shader;
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
source_set("engine") {
|
|
||||||
sources = [
|
|
||||||
"animatable.cc",
|
|
||||||
"animatable.h",
|
|
||||||
"animator.cc",
|
|
||||||
"animator.h",
|
|
||||||
"asset/font.cc",
|
|
||||||
"asset/font.h",
|
|
||||||
"asset/image.cc",
|
|
||||||
"asset/image.h",
|
|
||||||
"asset/mesh.cc",
|
|
||||||
"asset/mesh.h",
|
|
||||||
"asset/shader_source.cc",
|
|
||||||
"asset/shader_source.h",
|
|
||||||
"asset/sound.cc",
|
|
||||||
"asset/sound.h",
|
|
||||||
"drawable.cc",
|
|
||||||
"drawable.h",
|
|
||||||
"engine.cc",
|
|
||||||
"engine.h",
|
|
||||||
"game.h",
|
|
||||||
"game_factory.h",
|
|
||||||
"image_quad.cc",
|
|
||||||
"image_quad.h",
|
|
||||||
"imgui_backend.cc",
|
|
||||||
"imgui_backend.h",
|
|
||||||
"input_event.h",
|
|
||||||
"persistent_data.cc",
|
|
||||||
"persistent_data.h",
|
|
||||||
"solid_quad.cc",
|
|
||||||
"solid_quad.h",
|
|
||||||
"sound_player.cc",
|
|
||||||
"sound_player.h",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
|
||||||
"//assets/engine",
|
|
||||||
"//src/base",
|
|
||||||
"//src/engine/audio",
|
|
||||||
"//src/engine/platform",
|
|
||||||
"//src/engine/renderer",
|
|
||||||
"//src/third_party/imgui",
|
|
||||||
"//src/third_party/jsoncpp",
|
|
||||||
"//src/third_party/minimp3",
|
|
||||||
"//src/third_party/stb",
|
|
||||||
"//src/third_party/texture_compressor",
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -51,7 +51,7 @@ class Animatable : public Drawable {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
base::Vector2f position_ = {0, 0};
|
base::Vector2f position_ = {0, 0};
|
||||||
base::Vector2f size_ = {0, 0};
|
base::Vector2f size_ = {1, 1};
|
||||||
base::Vector2f scale_ = {1, 1};
|
base::Vector2f scale_ = {1, 1};
|
||||||
base::Vector2f rotation_ = {0, 1};
|
base::Vector2f rotation_ = {0, 1};
|
||||||
float theta_ = 0;
|
float theta_ = 0;
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
source_set("audio") {
|
|
||||||
sources = [
|
|
||||||
"audio_bus.cc",
|
|
||||||
"audio_bus.h",
|
|
||||||
"audio_device.h",
|
|
||||||
"audio_mixer.cc",
|
|
||||||
"audio_mixer.h",
|
|
||||||
"mixer_input.cc",
|
|
||||||
"mixer_input.h",
|
|
||||||
"sinc_resampler.cc",
|
|
||||||
"sinc_resampler.h",
|
|
||||||
]
|
|
||||||
|
|
||||||
libs = []
|
|
||||||
deps = [ "//src/base" ]
|
|
||||||
|
|
||||||
if (target_os == "linux") {
|
|
||||||
sources += [
|
|
||||||
"audio_device_alsa.cc",
|
|
||||||
"audio_device_alsa.h",
|
|
||||||
]
|
|
||||||
libs += [ "asound" ]
|
|
||||||
} else if (target_os == "win") {
|
|
||||||
sources += [
|
|
||||||
"audio_device_wasapi.cc",
|
|
||||||
"audio_device_wasapi.h",
|
|
||||||
]
|
|
||||||
} else if (target_os == "android") {
|
|
||||||
sources += [
|
|
||||||
"audio_device_oboe.cc",
|
|
||||||
"audio_device_oboe.h",
|
|
||||||
]
|
|
||||||
deps += [ "//src/third_party/oboe" ]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
#ifndef ENGINE_AUDIO_AUDIO_DEVICE_H
|
|
||||||
#define ENGINE_AUDIO_AUDIO_DEVICE_H
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
// Models an audio device sending mixed audio to the audio driver. Audio data
|
|
||||||
// from the mixer source is delivered on a pull model using Delegate.
|
|
||||||
class AudioDevice {
|
|
||||||
public:
|
|
||||||
class Delegate {
|
|
||||||
public:
|
|
||||||
Delegate() = default;
|
|
||||||
virtual ~Delegate() = default;
|
|
||||||
|
|
||||||
virtual int GetChannelCount() = 0;
|
|
||||||
|
|
||||||
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioDevice() = default;
|
|
||||||
virtual ~AudioDevice() = default;
|
|
||||||
|
|
||||||
virtual bool Initialize() = 0;
|
|
||||||
|
|
||||||
virtual void Suspend() = 0;
|
|
||||||
virtual void Resume() = 0;
|
|
||||||
|
|
||||||
virtual size_t GetHardwareSampleRate() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
AudioDevice(const AudioDevice&) = delete;
|
|
||||||
AudioDevice& operator=(const AudioDevice&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace eng
|
|
||||||
|
|
||||||
#endif // ENGINE_AUDIO_AUDIO_DEVICE_H
|
|
|
@ -1,85 +0,0 @@
|
||||||
#include "engine/audio/audio_device_oboe.h"
|
|
||||||
|
|
||||||
#include "base/log.h"
|
|
||||||
#include "third_party/oboe/include/oboe/Oboe.h"
|
|
||||||
|
|
||||||
using namespace base;
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
AudioDeviceOboe::AudioDeviceOboe(AudioDevice::Delegate* delegate)
|
|
||||||
: callback_(std::make_unique<StreamCallback>(this)), delegate_(delegate) {}
|
|
||||||
|
|
||||||
AudioDeviceOboe::~AudioDeviceOboe() {
|
|
||||||
LOG(0) << "Shutting down audio.";
|
|
||||||
stream_->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioDeviceOboe::Initialize() {
|
|
||||||
LOG(0) << "Initializing audio.";
|
|
||||||
return RestartStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceOboe::Suspend() {
|
|
||||||
stream_->pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceOboe::Resume() {
|
|
||||||
stream_->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AudioDeviceOboe::GetHardwareSampleRate() {
|
|
||||||
return stream_->getSampleRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioDeviceOboe::StreamCallback::StreamCallback(AudioDeviceOboe* audio_device)
|
|
||||||
: audio_device_(audio_device) {}
|
|
||||||
|
|
||||||
AudioDeviceOboe::StreamCallback::~StreamCallback() = default;
|
|
||||||
|
|
||||||
oboe::DataCallbackResult AudioDeviceOboe::StreamCallback::onAudioReady(
|
|
||||||
oboe::AudioStream* oboe_stream,
|
|
||||||
void* audio_data,
|
|
||||||
int32_t num_frames) {
|
|
||||||
float* output_buffer = static_cast<float*>(audio_data);
|
|
||||||
audio_device_->delegate_->RenderAudio(output_buffer, num_frames);
|
|
||||||
return oboe::DataCallbackResult::Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceOboe::StreamCallback::onErrorAfterClose(
|
|
||||||
oboe::AudioStream* oboe_stream,
|
|
||||||
oboe::Result error) {
|
|
||||||
LOG(0) << "Error after close. Error: " << oboe::convertToText(error);
|
|
||||||
|
|
||||||
audio_device_->RestartStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioDeviceOboe::RestartStream() {
|
|
||||||
oboe::AudioStreamBuilder builder;
|
|
||||||
oboe::Result result =
|
|
||||||
builder.setSharingMode(oboe::SharingMode::Exclusive)
|
|
||||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
|
||||||
->setFormat(oboe::AudioFormat::Float)
|
|
||||||
->setChannelCount(delegate_->GetChannelCount())
|
|
||||||
->setDirection(oboe::Direction::Output)
|
|
||||||
->setUsage(oboe::Usage::Game)
|
|
||||||
->setCallback(callback_.get())
|
|
||||||
->openManagedStream(stream_);
|
|
||||||
|
|
||||||
LOG(0) << "Oboe Audio Stream:";
|
|
||||||
LOG(0) << " performance mode: " << (int)stream_->getPerformanceMode();
|
|
||||||
LOG(0) << " format: " << (int)stream_->getFormat();
|
|
||||||
LOG(0) << " channel count: " << stream_->getChannelCount();
|
|
||||||
LOG(0) << " sample rate: " << stream_->getSampleRate();
|
|
||||||
|
|
||||||
if (result != oboe::Result::OK) {
|
|
||||||
LOG(0) << "Failed to create the playback stream. Error: "
|
|
||||||
<< oboe::convertToText(result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_->start();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace eng
|
|
|
@ -1,210 +0,0 @@
|
||||||
#include "engine/audio/audio_device_wasapi.h"
|
|
||||||
|
|
||||||
#include "base/log.h"
|
|
||||||
|
|
||||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
|
||||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
|
||||||
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
|
||||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
|
||||||
|
|
||||||
using namespace base;
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
AudioDeviceWASAPI::AudioDeviceWASAPI(AudioDevice::Delegate* delegate)
|
|
||||||
: delegate_(delegate) {}
|
|
||||||
|
|
||||||
AudioDeviceWASAPI::~AudioDeviceWASAPI() {
|
|
||||||
LOG(0) << "Shutting down audio.";
|
|
||||||
|
|
||||||
TerminateAudioThread();
|
|
||||||
|
|
||||||
if (shutdown_event_)
|
|
||||||
CloseHandle(shutdown_event_);
|
|
||||||
if (ready_event_)
|
|
||||||
CloseHandle(ready_event_);
|
|
||||||
if (device_)
|
|
||||||
device_->Release();
|
|
||||||
if (audio_client_)
|
|
||||||
audio_client_->Release();
|
|
||||||
if (render_client_)
|
|
||||||
render_client_->Release();
|
|
||||||
if (device_enumerator_)
|
|
||||||
device_enumerator_->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioDeviceWASAPI::Initialize() {
|
|
||||||
LOG(0) << "Initializing audio.";
|
|
||||||
|
|
||||||
HRESULT hr;
|
|
||||||
do {
|
|
||||||
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
|
||||||
IID_IMMDeviceEnumerator, (void**)&device_enumerator_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to instantiate device enumerator: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = device_enumerator_->GetDefaultAudioEndpoint(eRender, eConsole,
|
|
||||||
&device_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to get default audio endpoint: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = device_->Activate(IID_IAudioClient, CLSCTX_ALL, NULL,
|
|
||||||
(void**)&audio_client_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to activate audio client: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use float format.
|
|
||||||
WAVEFORMATEX* closest_match = nullptr;
|
|
||||||
WAVEFORMATEXTENSIBLE wfxex = {0};
|
|
||||||
wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
||||||
wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
||||||
wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
|
||||||
wfxex.Format.nChannels = 2;
|
|
||||||
wfxex.Format.nSamplesPerSec = 48000;
|
|
||||||
wfxex.Format.wBitsPerSample = 32;
|
|
||||||
wfxex.Samples.wValidBitsPerSample = 32;
|
|
||||||
wfxex.Format.nBlockAlign =
|
|
||||||
wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
|
|
||||||
wfxex.Format.nAvgBytesPerSec =
|
|
||||||
wfxex.Format.nBlockAlign * wfxex.Format.nSamplesPerSec;
|
|
||||||
wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
||||||
hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
|
|
||||||
&wfxex.Format, &closest_match);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unsupported sample format.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
WAVEFORMATEX* format = closest_match ? closest_match : &wfxex.Format;
|
|
||||||
if ((format->wFormatTag != WAVE_FORMAT_IEEE_FLOAT &&
|
|
||||||
(format->wFormatTag != WAVE_FORMAT_EXTENSIBLE ||
|
|
||||||
reinterpret_cast<WAVEFORMATEXTENSIBLE*>(format)->SubFormat !=
|
|
||||||
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) ||
|
|
||||||
format->nChannels != 2) {
|
|
||||||
LOG(0) << "Unsupported sample format.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample_rate_ = format->nSamplesPerSec;
|
|
||||||
|
|
||||||
HRESULT hr = audio_client_->Initialize(
|
|
||||||
AUDCLNT_SHAREMODE_SHARED,
|
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, 0, 0,
|
|
||||||
format, NULL);
|
|
||||||
if (closest_match)
|
|
||||||
CoTaskMemFree(closest_match);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to initialize audio client: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual size of the allocated buffer.
|
|
||||||
hr = audio_client_->GetBufferSize(&buffer_size_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to get audio client buffer size: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = audio_client_->GetService(IID_IAudioRenderClient,
|
|
||||||
(void**)&render_client_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to get audio render client: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown_event_ =
|
|
||||||
CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
|
||||||
if (shutdown_event_ == NULL) {
|
|
||||||
LOG(0) << "Unable to create shutdown event: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ready_event_ =
|
|
||||||
CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
|
||||||
if (ready_event_ == NULL) {
|
|
||||||
LOG(0) << "Unable to create samples ready event: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = audio_client_->SetEventHandle(ready_event_);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
LOG(0) << "Unable to set ready event: " << hr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartAudioThread();
|
|
||||||
|
|
||||||
hr = audio_client_->Start();
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceWASAPI::Suspend() {
|
|
||||||
audio_client_->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceWASAPI::Resume() {
|
|
||||||
audio_client_->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AudioDeviceWASAPI::GetHardwareSampleRate() {
|
|
||||||
return sample_rate_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceWASAPI::StartAudioThread() {
|
|
||||||
DCHECK(!audio_thread_.joinable());
|
|
||||||
|
|
||||||
LOG(0) << "Starting audio thread.";
|
|
||||||
audio_thread_ = std::thread(&AudioDeviceWASAPI::AudioThreadMain, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceWASAPI::TerminateAudioThread() {
|
|
||||||
LOG(0) << "Terminating audio thread";
|
|
||||||
SetEvent(shutdown_event_);
|
|
||||||
audio_thread_.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioDeviceWASAPI::AudioThreadMain() {
|
|
||||||
DCHECK(delegate_);
|
|
||||||
|
|
||||||
HANDLE wait_array[] = {shutdown_event_, ready_event_};
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
switch (WaitForMultipleObjects(2, wait_array, FALSE, INFINITE)) {
|
|
||||||
case WAIT_OBJECT_0 + 0: // shutdown_event_
|
|
||||||
return;
|
|
||||||
|
|
||||||
case WAIT_OBJECT_0 + 1: { // ready_event_
|
|
||||||
UINT32 padding;
|
|
||||||
HRESULT hr = audio_client_->GetCurrentPadding(&padding);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
BYTE* pData;
|
|
||||||
UINT32 frames_available = buffer_size_ - padding;
|
|
||||||
hr = render_client_->GetBuffer(frames_available, &pData);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
delegate_->RenderAudio(reinterpret_cast<float*>(pData),
|
|
||||||
frames_available);
|
|
||||||
hr = render_client_->ReleaseBuffer(frames_available, 0);
|
|
||||||
if (!SUCCEEDED(hr))
|
|
||||||
DLOG(0) << "Unable to release buffer: " << hr;
|
|
||||||
} else {
|
|
||||||
DLOG(0) << "Unable to get buffer: " << hr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace eng
|
|
|
@ -1,49 +0,0 @@
|
||||||
#ifndef ENGINE_AUDIO_AUDIO_DEVICE_WASAPI_H
|
|
||||||
#define ENGINE_AUDIO_AUDIO_DEVICE_WASAPI_H
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <AudioClient.h>
|
|
||||||
#include <MMDeviceAPI.h>
|
|
||||||
|
|
||||||
#include "engine/audio/audio_device.h"
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
class AudioDeviceWASAPI final : public AudioDevice {
|
|
||||||
public:
|
|
||||||
AudioDeviceWASAPI(AudioDevice::Delegate* delegate);
|
|
||||||
~AudioDeviceWASAPI() final;
|
|
||||||
|
|
||||||
bool Initialize() final;
|
|
||||||
|
|
||||||
void Suspend() final;
|
|
||||||
void Resume() final;
|
|
||||||
|
|
||||||
size_t GetHardwareSampleRate() final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
IMMDevice* device_ = nullptr;
|
|
||||||
IAudioClient* audio_client_ = nullptr;
|
|
||||||
IAudioRenderClient* render_client_ = nullptr;
|
|
||||||
IMMDeviceEnumerator* device_enumerator_ = nullptr;
|
|
||||||
|
|
||||||
HANDLE shutdown_event_ = nullptr;
|
|
||||||
HANDLE ready_event_ = nullptr;
|
|
||||||
|
|
||||||
std::thread audio_thread_;
|
|
||||||
|
|
||||||
UINT32 buffer_size_ = 0;
|
|
||||||
size_t sample_rate_ = 0;
|
|
||||||
|
|
||||||
AudioDevice::Delegate* delegate_ = nullptr;
|
|
||||||
|
|
||||||
void StartAudioThread();
|
|
||||||
void TerminateAudioThread();
|
|
||||||
|
|
||||||
void AudioThreadMain();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace eng
|
|
||||||
|
|
||||||
#endif // ENGINE_AUDIO_AUDIO_DEVICE_WASAPI_H
|
|
|
@ -4,15 +4,13 @@
|
||||||
|
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/task_runner.h"
|
#include "base/task_runner.h"
|
||||||
|
#include "base/thread_pool.h"
|
||||||
#include "engine/audio/audio_bus.h"
|
#include "engine/audio/audio_bus.h"
|
||||||
#include "engine/audio/mixer_input.h"
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include "engine/audio/audio_device_oboe.h"
|
#include "engine/audio/audio_sink_oboe.h"
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include "engine/audio/audio_device_alsa.h"
|
#include "engine/audio/audio_sink_alsa.h"
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include "engine/audio/audio_device_wasapi.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
@ -22,54 +20,168 @@ namespace eng {
|
||||||
AudioMixer::AudioMixer()
|
AudioMixer::AudioMixer()
|
||||||
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()),
|
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()),
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
audio_device_{std::make_unique<AudioDeviceOboe>(this)} {
|
audio_sink_{std::make_unique<AudioSinkOboe>(this)} {
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
audio_device_{std::make_unique<AudioDeviceAlsa>(this)} {
|
audio_sink_{std::make_unique<AudioSinkAlsa>(this)} {
|
||||||
#elif defined(_WIN32)
|
|
||||||
audio_device_{std::make_unique<AudioDeviceWASAPI>(this)} {
|
|
||||||
#endif
|
#endif
|
||||||
bool res = audio_device_->Initialize();
|
bool res = audio_sink_->Initialize();
|
||||||
CHECK(res) << "Failed to initialize audio device.";
|
CHECK(res) << "Failed to initialize audio sink.";
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioMixer::~AudioMixer() {
|
AudioMixer::~AudioMixer() = default;
|
||||||
audio_device_.reset();
|
|
||||||
|
uint64_t AudioMixer::CreateResource() {
|
||||||
|
uint64_t resource_id = ++last_resource_id_;
|
||||||
|
resources_[resource_id] = std::make_shared<Resource>();
|
||||||
|
return resource_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::AddInput(std::shared_ptr<MixerInput> mixer_input) {
|
void AudioMixer::DestroyResource(uint64_t resource_id) {
|
||||||
DCHECK(audio_enabled_);
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it->second->active) {
|
||||||
|
it->second->restart_cb = nullptr;
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
resources_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::Play(uint64_t resource_id,
|
||||||
|
std::shared_ptr<AudioBus> audio_bus,
|
||||||
|
float amplitude,
|
||||||
|
bool reset_pos) {
|
||||||
|
if (!audio_enabled_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it->second->active) {
|
||||||
|
if (reset_pos)
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (it->second->flags.load(std::memory_order_relaxed) & kStopped)
|
||||||
|
it->second->restart_cb = [&, resource_id, audio_bus, amplitude,
|
||||||
|
reset_pos]() -> void {
|
||||||
|
Play(resource_id, audio_bus, amplitude, reset_pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reset_pos) {
|
||||||
|
it->second->src_index = 0;
|
||||||
|
it->second->accumulator = 0;
|
||||||
|
audio_bus->ResetStream();
|
||||||
|
} else if (it->second->src_index >= audio_bus->samples_per_channel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->active = true;
|
||||||
|
it->second->flags.fetch_and(~kStopped, std::memory_order_relaxed);
|
||||||
|
it->second->audio_bus = audio_bus;
|
||||||
|
if (amplitude >= 0)
|
||||||
|
it->second->amplitude = amplitude;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> scoped_lock(lock_);
|
std::lock_guard<std::mutex> scoped_lock(lock_);
|
||||||
inputs_[0].push_back(mixer_input);
|
play_list_[0].push_back(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::Stop(uint64_t resource_id) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it->second->active) {
|
||||||
|
it->second->restart_cb = nullptr;
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetLoop(uint64_t resource_id, bool loop) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
it->second->flags.fetch_or(kLoop, std::memory_order_relaxed);
|
||||||
|
else
|
||||||
|
it->second->flags.fetch_and(~kLoop, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetSimulateStereo(uint64_t resource_id, bool simulate) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (simulate)
|
||||||
|
it->second->flags.fetch_or(kSimulateStereo, std::memory_order_relaxed);
|
||||||
|
else
|
||||||
|
it->second->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetResampleStep(uint64_t resource_id, size_t step) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->step.store(step + 100, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->max_amplitude.store(max_amplitude, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetEndCallback(uint64_t resource_id, base::Closure cb) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->end_cb = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::Suspend() {
|
void AudioMixer::Suspend() {
|
||||||
audio_device_->Suspend();
|
audio_sink_->Suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::Resume() {
|
void AudioMixer::Resume() {
|
||||||
audio_device_->Resume();
|
audio_sink_->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioMixer::GetHardwareSampleRate() {
|
size_t AudioMixer::GetHardwareSampleRate() {
|
||||||
return audio_device_->GetHardwareSampleRate();
|
return audio_sink_->GetHardwareSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock);
|
std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock);
|
||||||
if (scoped_lock)
|
if (scoped_lock)
|
||||||
inputs_[1].splice(inputs_[1].end(), inputs_[0]);
|
play_list_[1].splice(play_list_[1].end(), play_list_[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount);
|
memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount);
|
||||||
|
|
||||||
for (auto it = inputs_[1].begin(); it != inputs_[1].end();) {
|
for (auto it = play_list_[1].begin(); it != play_list_[1].end();) {
|
||||||
auto* audio_bus = (*it)->GetAudioBus().get();
|
auto audio_bus = it->get()->audio_bus.get();
|
||||||
unsigned flags = (*it)->GetFlags();
|
unsigned flags = it->get()->flags.load(std::memory_order_relaxed);
|
||||||
bool marked_for_removal = false;
|
bool marked_for_removal = false;
|
||||||
|
|
||||||
if (flags & MixerInput::kStopped) {
|
if (flags & kStopped) {
|
||||||
marked_for_removal = true;
|
marked_for_removal = true;
|
||||||
} else {
|
} else {
|
||||||
const float* src[2] = {audio_bus->GetChannelData(0),
|
const float* src[2] = {audio_bus->GetChannelData(0),
|
||||||
|
@ -78,20 +190,20 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
|
|
||||||
size_t num_samples = audio_bus->samples_per_channel();
|
size_t num_samples = audio_bus->samples_per_channel();
|
||||||
size_t src_index = (*it)->GetSrcIndex();
|
size_t src_index = it->get()->src_index;
|
||||||
size_t step = (*it)->GetStep();
|
size_t step = it->get()->step.load(std::memory_order_relaxed);
|
||||||
size_t accumulator = (*it)->GetAccumulator();
|
size_t accumulator = it->get()->accumulator;
|
||||||
float amplitude = (*it)->GetAmplitude();
|
float amplitude = it->get()->amplitude;
|
||||||
float amplitude_inc = (*it)->GetAmplitudeInc();
|
float amplitude_inc =
|
||||||
float max_amplitude = (*it)->GetMaxAmplitude();
|
it->get()->amplitude_inc.load(std::memory_order_relaxed);
|
||||||
size_t channel_offset = (flags & MixerInput::kSimulateStereo)
|
float max_amplitude =
|
||||||
? audio_bus->sample_rate() / 10
|
it->get()->max_amplitude.load(std::memory_order_relaxed);
|
||||||
: 0;
|
size_t channel_offset =
|
||||||
|
(flags & kSimulateStereo) ? audio_bus->sample_rate() / 10 : 0;
|
||||||
|
|
||||||
DCHECK(num_samples > 0);
|
DCHECK(num_samples > 0);
|
||||||
|
|
||||||
for (size_t i = 0; i < num_frames * kChannelCount;) {
|
for (size_t i = 0; i < num_frames * kChannelCount;) {
|
||||||
if (src_index < num_samples) {
|
|
||||||
// Mix the 1st channel.
|
// Mix the 1st channel.
|
||||||
output_buffer[i++] += src[0][src_index] * amplitude;
|
output_buffer[i++] += src[0][src_index] * amplitude;
|
||||||
|
|
||||||
|
@ -99,7 +211,7 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
size_t ind = channel_offset + src_index;
|
size_t ind = channel_offset + src_index;
|
||||||
if (ind < num_samples)
|
if (ind < num_samples)
|
||||||
output_buffer[i++] += src[1][ind] * amplitude;
|
output_buffer[i++] += src[1][ind] * amplitude;
|
||||||
else if (flags & MixerInput::kLoop)
|
else if (flags & kLoop)
|
||||||
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
||||||
else
|
else
|
||||||
i++;
|
i++;
|
||||||
|
@ -117,50 +229,76 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
accumulator += step;
|
accumulator += step;
|
||||||
src_index += accumulator / 100;
|
src_index += accumulator / 100;
|
||||||
accumulator %= 100;
|
accumulator %= 100;
|
||||||
} else {
|
|
||||||
if (audio_bus->EndOfStream()) {
|
// Remove, loop or stream if the source data is consumed
|
||||||
if (!(flags & MixerInput::kLoop))
|
if (src_index >= num_samples) {
|
||||||
marked_for_removal = true;
|
|
||||||
else
|
|
||||||
src_index %= num_samples;
|
src_index %= num_samples;
|
||||||
|
|
||||||
|
if (audio_bus->EndOfStream()) {
|
||||||
|
marked_for_removal = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*it)->OnMoreData(!!(flags & MixerInput::kLoop))) {
|
if (!it->get()->streaming_in_progress.load(
|
||||||
src_index %= num_samples;
|
std::memory_order_acquire)) {
|
||||||
|
it->get()->streaming_in_progress.store(true,
|
||||||
|
std::memory_order_relaxed);
|
||||||
|
|
||||||
|
// Swap buffers and start streaming in background.
|
||||||
|
audio_bus->SwapBuffers();
|
||||||
src[0] = audio_bus->GetChannelData(0);
|
src[0] = audio_bus->GetChannelData(0);
|
||||||
src[1] = audio_bus->GetChannelData(1);
|
src[1] = audio_bus->GetChannelData(1);
|
||||||
if (!src[1])
|
if (!src[1])
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
num_samples = audio_bus->samples_per_channel();
|
num_samples = audio_bus->samples_per_channel();
|
||||||
|
|
||||||
|
ThreadPool::Get().PostTask(
|
||||||
|
HERE,
|
||||||
|
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop));
|
||||||
} else {
|
} else {
|
||||||
DLOG(0) << "Mixer buffer underrun!";
|
DLOG << "Mixer buffer underrun!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember last sample position and volume.
|
it->get()->src_index = src_index;
|
||||||
(*it)->SetPosition(src_index, accumulator);
|
it->get()->accumulator = accumulator;
|
||||||
(*it)->SetAmplitude(amplitude);
|
it->get()->amplitude = amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marked_for_removal) {
|
if (marked_for_removal) {
|
||||||
removed_inputs_.push_back(*it);
|
end_list_.push_back(*it);
|
||||||
it = inputs_[1].erase(it);
|
it = play_list_[1].erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = removed_inputs_.begin(); it != removed_inputs_.end();) {
|
for (auto it = end_list_.begin(); it != end_list_.end();) {
|
||||||
if (!(*it)->IsStreamingInProgress()) {
|
if (!it->get()->streaming_in_progress.load(std::memory_order_relaxed)) {
|
||||||
main_thread_task_runner_->PostTask(
|
main_thread_task_runner_->PostTask(
|
||||||
HERE, std::bind(&MixerInput::OnRemovedFromMixer, *it));
|
HERE, std::bind(&AudioMixer::EndCallback, this, *it));
|
||||||
it = removed_inputs_.erase(it);
|
it = end_list_.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioMixer::DoStream(std::shared_ptr<Resource> resource, bool loop) {
|
||||||
|
resource->audio_bus->Stream(loop);
|
||||||
|
resource->streaming_in_progress.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::EndCallback(std::shared_ptr<Resource> resource) {
|
||||||
|
resource->active = false;
|
||||||
|
|
||||||
|
if (resource->end_cb)
|
||||||
|
resource->end_cb();
|
||||||
|
if (resource->restart_cb) {
|
||||||
|
resource->restart_cb();
|
||||||
|
resource->restart_cb = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|