Compare commits

...

6 Commits

Author SHA1 Message Date
Attila Uygun 709029f22c Async-load support for assets 2023-06-25 16:48:56 +02:00
Attila Uygun 8e6589ec67 Fix memory leak when canceling tasks 2023-06-24 11:04:10 +02:00
Attila Uygun 69a05c00e9 Support for adding tasks in front of the task queue 2023-06-24 11:04:07 +02:00
Attila Uygun d7e444fa81 Fix compile error 2023-06-21 14:31:35 +02:00
Attila Uygun 8a87597911 Update third_party/stb
stb_image - v2.28
stb_truetype - v1.26
2023-06-21 14:00:21 +02:00
Attila Uygun 22d80d6152 Cleanup & refactor 2023-06-21 13:59:11 +02:00
29 changed files with 11666 additions and 5283 deletions

View File

@ -11,11 +11,12 @@ namespace {
void PostTaskAndReplyRelay(Location from,
Closure task_cb,
Closure reply_cb,
std::shared_ptr<TaskRunner> destination) {
std::shared_ptr<TaskRunner> destination,
bool front) {
task_cb();
if (reply_cb)
destination->PostTask(from, std::move(reply_cb));
destination->PostTask(from, std::move(reply_cb), front);
}
} // namespace
@ -36,22 +37,28 @@ std::shared_ptr<TaskRunner> TaskRunner::GetThreadLocalTaskRunner() {
return thread_local_task_runner;
}
void TaskRunner::PostTask(Location from, Closure task) {
void TaskRunner::PostTask(Location from, Closure task, bool front) {
DCHECK(task) << LOCATION(from);
task_count_.fetch_add(1, std::memory_order_relaxed);
std::lock_guard<std::mutex> scoped_lock(lock_);
queue_.emplace_back(from, std::move(task));
if (front)
queue_.emplace_front(from, std::move(task));
else
queue_.emplace_back(from, std::move(task));
}
void TaskRunner::PostTaskAndReply(Location from, Closure task, Closure reply) {
void TaskRunner::PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front) {
DCHECK(task) << LOCATION(from);
DCHECK(reply) << LOCATION(from);
DCHECK(thread_local_task_runner) << LOCATION(from);
auto relay = std::bind(PostTaskAndReplyRelay, from, std::move(task),
std::move(reply), thread_local_task_runner);
PostTask(from, std::move(relay));
std::move(reply), thread_local_task_runner, front);
PostTask(from, std::move(relay), front);
}
void TaskRunner::CancelTasks() {

View File

@ -18,16 +18,15 @@ namespace internal {
// one that returns via an output parameter.
template <typename ReturnType>
void ReturnAsParamAdapter(std::function<ReturnType()> func,
ReturnType* result) {
std::shared_ptr<ReturnType> result) {
*result = func();
}
// Adapts a ReturnType* result to a callblack that expects a ReturnType.
template <typename ReturnType>
void ReplyAdapter(std::function<void(ReturnType)> callback,
ReturnType* result) {
std::shared_ptr<ReturnType> result) {
callback(std::move(*result));
delete result;
}
} // namespace internal
@ -45,21 +44,32 @@ class TaskRunner {
static void CreateThreadLocalTaskRunner();
static std::shared_ptr<TaskRunner> GetThreadLocalTaskRunner();
void PostTask(Location from, Closure task);
void PostTask(Location from, Closure task, bool front = false);
void PostTaskAndReply(Location from, Closure task, Closure reply);
void PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front = false);
template <typename ReturnType>
void PostTaskAndReplyWithResult(Location from,
std::function<ReturnType()> task,
std::function<void(ReturnType)> reply) {
auto* result = new ReturnType;
std::function<void(ReturnType)> reply,
bool front = false) {
auto result = std::make_shared<ReturnType>();
return PostTaskAndReply(
from,
std::bind(internal::ReturnAsParamAdapter<ReturnType>, std::move(task),
result),
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply),
result));
std::bind(internal::ReplyAdapter<ReturnType>, std::move(reply), result),
front);
}
// Posts a task to delete the given object.
template <class T>
void Delete(Location from, std::unique_ptr<T> object) {
std::shared_ptr<T> owned = std::move(object);
PostTask(HERE, [owned]() {});
}
void CancelTasks();

View File

@ -40,17 +40,20 @@ void ThreadPool::Shutdown() {
threads_.clear();
}
void ThreadPool::PostTask(Location from, Closure task) {
void ThreadPool::PostTask(Location from, Closure task, bool front) {
DCHECK((!threads_.empty()));
task_runner_.PostTask(from, std::move(task));
task_runner_.PostTask(from, std::move(task), front);
semaphore_.release();
}
void ThreadPool::PostTaskAndReply(Location from, Closure task, Closure reply) {
void ThreadPool::PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front) {
DCHECK((!threads_.empty()));
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply));
task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply), front);
semaphore_.release();
}

View File

@ -24,16 +24,20 @@ class ThreadPool {
void Shutdown();
void PostTask(Location from, Closure task);
void PostTask(Location from, Closure task, bool front = false);
void PostTaskAndReply(Location from, Closure task, Closure reply);
void PostTaskAndReply(Location from,
Closure task,
Closure reply,
bool front = false);
template <typename ReturnType>
void PostTaskAndReplyWithResult(Location from,
std::function<ReturnType()> task,
std::function<void(ReturnType)> reply) {
std::function<void(ReturnType)> reply,
bool front = false) {
task_runner_.PostTaskAndReplyWithResult(from, std::move(task),
std::move(reply));
std::move(reply), front);
semaphore_.release();
}

View File

@ -13,7 +13,6 @@
#include "engine/engine.h"
#include "engine/game_factory.h"
#include "engine/input_event.h"
#include "engine/sound.h"
DECLARE_GAME_BEGIN
DECLARE_GAME(Demo)
@ -47,16 +46,38 @@ Demo::~Demo() {
saved_data_.Save();
}
bool Demo::Initialize() {
saved_data_.Load(kSaveFileName);
Engine::Get().LoadCustomShader("sky_without_nebula",
"sky_without_nebula.glsl");
Engine::Get().LoadCustomShader("sky", "sky.glsl");
bool Demo::PreInitialize() {
if (!font_.Load("PixelCaps!.ttf"))
return false;
Engine::Get().SetShaderSource("sky_without_nebula",
"sky_without_nebula.glsl");
Engine::Get().SetShaderSource("sky", "sky.glsl");
Engine::Get().AsyncLoadSound("music", "Game_2_Main.mp3");
Engine::Get().AsyncLoadSound("boss_music", "Game_2_Boss.mp3");
if (!enemy_.PreInitialize()) {
LOG << "Failed to create the enemy.";
return false;
}
if (!player_.PreInitialize()) {
LOG << "Failed to create the enemy.";
return false;
}
if (!menu_.PreInitialize()) {
LOG << "Failed to create the menu.";
return false;
}
return true;
}
bool Demo::Initialize() {
saved_data_.Load(kSaveFileName);
if (!sky_.Create(false)) {
LOG << "Could not create the sky.";
return false;
@ -87,18 +108,10 @@ bool Demo::Initialize() {
return false;
}
auto sound = std::make_unique<Sound>();
if (!sound->Load("Game_2_Main.mp3", true))
return false;
auto boss_sound = std::make_unique<Sound>();
if (!boss_sound->Load("Game_2_Boss.mp3", true))
return false;
music_.SetSound(std::move(sound));
music_.SetSound("music");
music_.SetMaxAplitude(0.5f);
boss_music_.SetSound(std::move(boss_sound));
boss_music_.SetSound("boss_music");
boss_music_.SetMaxAplitude(0.5f);
if (!saved_data_.root().get("audio", Json::Value(true)).asBool())
@ -172,6 +185,7 @@ void Demo::ContextLost() {
num_benchmark_samples_ = 0;
avarage_fps_ = 0;
}
menu_.SetRendererType();
}
void Demo::LostFocus() {}

View File

@ -23,14 +23,12 @@ class Demo final : public eng::Game {
Demo();
~Demo() final;
// Game interface
bool PreInitialize() final;
bool Initialize() final;
void Update(float delta_time) final;
void ContextLost() final;
void LostFocus() final;
void GainedFocus(bool from_interstitial_ad) final;
void AddScore(size_t score);

View File

@ -13,7 +13,6 @@
#include "engine/font.h"
#include "engine/image.h"
#include "engine/renderer/geometry.h"
#include "engine/sound.h"
#include "demo/demo.h"
@ -78,46 +77,44 @@ Enemy::Enemy() = default;
Enemy::~Enemy() = default;
bool Enemy::PreInitialize() {
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().SetShaderSource("chromatic_aberration",
"chromatic_aberration.glsl");
Engine::Get().AsyncLoadSound("boss_intro", "boss_intro.mp3");
Engine::Get().AsyncLoadSound("boss_explosion", "boss_explosion.mp3");
Engine::Get().AsyncLoadSound("explosion", "explosion.mp3");
Engine::Get().AsyncLoadSound("stealth", "stealth.mp3");
Engine::Get().AsyncLoadSound("shield", "shield.mp3");
Engine::Get().AsyncLoadSound("hit", "hit.mp3");
Engine::Get().AsyncLoadSound("powerup-spawn", "powerup-spawn.mp3");
Engine::Get().AsyncLoadSound("powerup-pick", "powerup-pick.mp3");
return true;
}
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_animator_.Attach(&boss_);
boss_intro_.SetSound(boss_intro_sound_);
boss_intro_.SetSound("boss_intro");
boss_intro_.SetVariate(false);
boss_intro_.SetSimulateStereo(false);
@ -729,29 +726,29 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
e.movement_animator.Play(Animator::kMovement, false);
if (e.enemy_type == kEnemyType_PowerUp) {
e.explosion.SetSound(power_up_pick_sound_);
e.explosion.SetSound("powerup-pick");
e.spawn.SetSound(power_up_spawn_sound_);
e.spawn.SetSound("powerup-spawn");
e.spawn.SetMaxAplitude(2.0f);
e.spawn.Play(false);
} else {
e.explosion.SetSound(explosion_sound_);
e.explosion.SetSound("explosion");
e.explosion.SetVariate(true);
e.explosion.SetSimulateStereo(true);
e.explosion.SetMaxAplitude(0.9f);
}
e.stealth.SetSound(stealth_sound_);
e.stealth.SetSound("stealth");
e.stealth.SetVariate(false);
e.stealth.SetSimulateStereo(false);
e.stealth.SetMaxAplitude(0.7f);
e.shield_on.SetSound(shield_on_sound_);
e.shield_on.SetSound("shield");
e.shield_on.SetVariate(false);
e.shield_on.SetSimulateStereo(false);
e.shield_on.SetMaxAplitude(0.5f);
e.hit.SetSound(hit_sound_);
e.hit.SetSound("hit");
e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f);
@ -825,11 +822,11 @@ void Enemy::SpawnBoss() {
Animator::kMovement, [&]() -> void { e.marked_for_removal = true; });
e.score_animator.Attach(&e.score);
e.explosion.SetSound(boss_explosion_sound_);
e.explosion.SetSound("boss_explosion");
e.explosion.SetVariate(false);
e.explosion.SetSimulateStereo(false);
e.hit.SetSound(hit_sound_);
e.hit.SetSound("hit");
e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f);
@ -1185,30 +1182,6 @@ std::unique_ptr<Image> Enemy::GetScoreImage(EnemyType enemy_type) {
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) {
e.sprite.Translate(delta);
e.target.Translate(delta);

View File

@ -15,7 +15,6 @@
namespace eng {
class Image;
class Sound;
} // namespace eng
class Enemy {
@ -23,6 +22,7 @@ class Enemy {
Enemy();
~Enemy();
bool PreInitialize();
bool Initialize();
void Update(float delta_time);
@ -109,15 +109,6 @@ class Enemy {
eng::Animator boss_animator_;
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_;
int num_enemies_killed_in_current_wave_ = 0;
@ -164,8 +155,6 @@ class Enemy {
std::unique_ptr<eng::Image> GetScoreImage(EnemyType enemy_type);
bool CreateRenderResources();
void TranslateEnemyUnit(EnemyUnit& e, const base::Vector2f& delta);
};

View File

@ -53,7 +53,7 @@ Menu::Menu() = default;
Menu::~Menu() = default;
bool Menu::Initialize() {
bool Menu::PreInitialize() {
click_sound_ = std::make_shared<Sound>();
if (!click_sound_->Load("menu_click.mp3", false))
return false;
@ -70,8 +70,59 @@ bool Menu::Initialize() {
max_text_width_ = width;
}
if (!CreateRenderResources())
return false;
Engine::Get().SetImageSource("menu_tex",
std::bind(&Menu::CreateMenuImage, this), true);
Engine::Get().SetImageSource("logo_tex0", "woom_logo_start_frames_01.png",
true);
Engine::Get().SetImageSource("logo_tex1", "woom_logo_start_frames_02-03.png",
true);
Engine::Get().SetImageSource("buttons_tex", "menu_icons.png", true);
Engine::Get().SetImageSource("renderer_logo", "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) {
items_[i].text.Create("menu_tex", {1, 4});
@ -199,8 +250,6 @@ bool Menu::Initialize() {
Engine::Get().CreateRenderer(renderer_type_.enabled()
? RendererType::kVulkan
: RendererType::kOpenGL);
renderer_type_.SetEnabled(
(Engine::Get().GetRendererType() == RendererType::kVulkan));
},
true, Engine::Get().GetRendererType() == RendererType::kVulkan,
kColorFadeOut, {Vector4f{1, 1, 1, 1}, Vector4f{1, 1, 1, 1}});
@ -320,6 +369,11 @@ void Menu::SetOptionEnabled(Option o, bool enable) {
}
}
void Menu::SetRendererType() {
renderer_type_.SetEnabled(
(Engine::Get().GetRendererType() == RendererType::kVulkan));
}
void Menu::Show() {
logo_[1].SetColor(kColorNormal);
logo_animator_[0].SetVisible(true);
@ -440,53 +494,6 @@ 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() {
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();

View File

@ -30,11 +30,13 @@ class Menu {
Menu();
~Menu();
bool PreInitialize();
bool Initialize();
void OnInputEvent(std::unique_ptr<eng::InputEvent> event);
void SetOptionEnabled(Option o, bool enable);
void SetRendererType();
void Show();
void Hide(base::Closure cb = nullptr);
@ -145,9 +147,6 @@ class Menu {
Radio starting_wave_;
Button wave_up_;
Button wave_down_;
bool CreateRenderResources();
std::unique_ptr<eng::Image> CreateMenuImage();
std::unique_ptr<eng::Image> CreateHighScoreImage();

View File

@ -5,7 +5,6 @@
#include "engine/engine.h"
#include "engine/font.h"
#include "engine/input_event.h"
#include "engine/sound.h"
#include "demo/demo.h"
@ -29,22 +28,20 @@ Player::Player() = default;
Player::~Player() = default;
bool Player::PreInitialize() {
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);
Engine::Get().AsyncLoadSound("laser", "laser.mp3");
Engine::Get().AsyncLoadSound("nuke", "nuke.mp3");
Engine::Get().AsyncLoadSound("no_nuke", "no_nuke.mp3");
return true;
}
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();
Vector2f hb_pos = Engine::Get().GetScreenSize() / Vector2f(2, -2) +
@ -75,12 +72,12 @@ bool Player::Initialize() {
nuke_symbol_animator_.Attach(&nuke_symbol_);
nuke_explosion_.SetSound(nuke_explosion_sound_);
nuke_explosion_.SetSound("nuke");
nuke_explosion_.SetVariate(false);
nuke_explosion_.SetSimulateStereo(false);
nuke_explosion_.SetMaxAplitude(0.8f);
no_nuke_.SetSound(no_nuke_sound_);
no_nuke_.SetSound("no_nuke");
return true;
}
@ -306,7 +303,7 @@ void Player::SetupWeapons() {
beam_animator_[i].SetBlending({1, 1, 1, 0}, 0.16f);
beam_animator_[i].Attach(&beam_[i]);
laser_shot_[i].SetSound(laser_shot_sound_);
laser_shot_[i].SetSound("laser");
laser_shot_[i].SetVariate(true);
laser_shot_[i].SetSimulateStereo(false);
laser_shot_[i].SetMaxAplitude(0.4f);
@ -485,12 +482,3 @@ void Player::NavigateBack() {
Engine& engine = Engine::Get();
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;
}

View File

@ -13,7 +13,6 @@
namespace eng {
class InputEvent;
class Sound;
} // namespace eng
class Player {
@ -21,6 +20,7 @@ class Player {
Player();
~Player();
bool PreInitialize();
bool Initialize();
void Update(float delta_time);
@ -41,10 +41,6 @@ class Player {
int nuke_count() { return nuke_count_; }
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 weapon_[2];
eng::ImageQuad beam_[2];
@ -101,8 +97,6 @@ class Player {
bool ValidateDrag(int i);
void NavigateBack();
bool CreateRenderResources();
};
#endif // DEMO_PLAYER_H

View File

@ -21,8 +21,8 @@ SkyQuad::~SkyQuad() = default;
bool SkyQuad::Create(bool without_nebula) {
without_nebula_ = without_nebula;
scale_ = Engine::Get().GetScreenSize();
shader_ = Engine::Get().GetCustomShader(
without_nebula ? "sky_without_nebula" : "sky");
shader_ =
Engine::Get().GetShader(without_nebula ? "sky_without_nebula" : "sky");
color_animator_.Attach(this);

View File

@ -5,11 +5,6 @@
#include "engine/animatable.h"
#include "engine/animator.h"
#include <array>
#include <memory>
#include <string>
#include <vector>
namespace eng {
class Shader;
} // namespace eng

View File

@ -254,7 +254,8 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
ThreadPool::Get().PostTask(
HERE,
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop));
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop),
true);
} else {
DLOG << "Mixer buffer underrun!";
}

View File

@ -20,6 +20,7 @@
#include "engine/renderer/texture.h"
#include "engine/renderer/vulkan/renderer_vulkan.h"
#include "engine/shader_source.h"
#include "engine/sound.h"
#include "third_party/texture_compressor/texture_compressor.h"
using namespace base;
@ -84,6 +85,7 @@ void Engine::Run() {
if (!renderer_->IsInitialzed()) {
timer_.Reset();
input_queue_.clear();
continue;
}
@ -109,7 +111,7 @@ void Engine::Initialize() {
thread_pool_.Initialize();
CreateRenderer(RendererType::kVulkan);
CreateRendererInternal(RendererType::kVulkan);
// Normalize viewport.
if (GetScreenWidth() > GetScreenHeight()) {
@ -134,7 +136,11 @@ void Engine::Initialize() {
game_ = GameFactoryBase::CreateGame("");
CHECK(game_) << "No game found to run.";
CHECK(game_->PreInitialize()) << "Failed to pre-initialize the game.";
// Create resources and let the game finalize initialization.
CreateRenderResources();
WaitForAsyncWork();
CHECK(game_->Initialize()) << "Failed to initialize the game.";
}
@ -205,33 +211,12 @@ void Engine::RemoveAnimator(Animator* animator) {
}
void Engine::CreateRenderer(RendererType type) {
if ((dynamic_cast<RendererVulkan*>(renderer_.get()) &&
type == RendererType::kVulkan) ||
(dynamic_cast<RendererOpenGL*>(renderer_.get()) &&
type == RendererType::kOpenGL))
return;
if (type == RendererType::kVulkan)
renderer_ =
std::make_unique<RendererVulkan>(std::bind(&Engine::ContextLost, this));
else if (type == RendererType::kOpenGL)
renderer_ =
std::make_unique<RendererOpenGL>(std::bind(&Engine::ContextLost, this));
else
NOTREACHED;
bool result = renderer_->Initialize(platform_);
if (!result && type == RendererType::kVulkan) {
LOG << "Failed to initialize " << renderer_->GetDebugName() << " renderer.";
LOG << "Fallback to OpenGL renderer.";
CreateRenderer(RendererType::kOpenGL);
return;
}
CHECK(result) << "Failed to initialize " << renderer_->GetDebugName()
<< " renderer.";
CreateTextureCompressors();
ContextLost();
// Create a new renderer next cycle.
TaskRunner::TaskRunner::GetThreadLocalTaskRunner()->PostTask(
HERE, std::bind(&Engine::CreateRendererInternal, this, type));
TaskRunner::TaskRunner::GetThreadLocalTaskRunner()->PostTask(
HERE, std::bind(&Engine::ContextLost, this));
input_queue_.clear();
}
RendererType Engine::GetRendererType() {
@ -276,14 +261,8 @@ void Engine::SetImageSource(const std::string& asset_name,
return;
}
auto& t = textures_[asset_name] = {CreateRenderResource<Texture>(),
create_image, persistent, 0};
if (persistent) {
auto image = create_image();
if (image)
t.texture->Update(std::move(image));
}
textures_[asset_name] = {std::make_unique<Texture>(renderer_.get()),
create_image, persistent, 0};
}
void Engine::RefreshImage(const std::string& asset_name) {
@ -297,8 +276,6 @@ void Engine::RefreshImage(const std::string& asset_name) {
auto image = it->second.create_image();
if (image)
it->second.texture->Update(std::move(image));
else
it->second.texture->Destroy();
}
}
@ -310,8 +287,11 @@ Texture* Engine::AcquireTexture(const std::string& asset_name) {
}
it->second.use_count++;
if (!it->second.texture->IsValid())
RefreshImage(it->first);
if (!it->second.texture->IsValid()) {
auto image = it->second.create_image();
if (image)
it->second.texture->Update(std::move(image));
}
return it->second.texture.get();
}
@ -328,40 +308,58 @@ void Engine::ReleaseTexture(const std::string& asset_name) {
it->second.texture->Destroy();
}
void Engine::LoadCustomShader(const std::string& asset_name,
const std::string& file_name) {
void Engine::SetShaderSource(const std::string& asset_name,
const std::string& file_name) {
if (shaders_.contains(asset_name)) {
DLOG << "Shader already exists: " << asset_name;
return;
}
auto& s = shaders_[asset_name] = {CreateRenderResource<Shader>(), file_name};
auto source = std::make_unique<ShaderSource>();
if (!source->Load(file_name))
return;
s.shader->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false);
shaders_[asset_name] = {std::make_unique<Shader>(renderer_.get()), file_name};
}
Shader* Engine::GetCustomShader(const std::string& asset_name) {
Shader* Engine::GetShader(const std::string& asset_name) {
auto it = shaders_.find(asset_name);
if (it == shaders_.end()) {
DLOG << "Shader not found: " << asset_name;
return nullptr;
}
if (!it->second.shader->IsValid()) {
auto source = std::make_unique<ShaderSource>();
if (source->Load(it->second.file_name))
it->second.shader->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false);
}
return it->second.shader.get();
}
void Engine::RemoveCustomShader(const std::string& asset_name) {
auto it = shaders_.find(asset_name);
if (it == shaders_.end()) {
DLOG << "Shader not found: " << asset_name;
void Engine::AsyncLoadSound(const std::string& asset_name,
const std::string& file_name,
bool stream) {
if (audio_buses_.contains(asset_name)) {
DLOG << "AudioBus already exists: " << asset_name;
return;
}
shaders_.erase(it);
auto sound = std::make_shared<Sound>();
audio_buses_[asset_name] = sound;
++async_work_count_;
thread_pool_.PostTaskAndReply(
HERE, std::bind(&Sound::Load, sound, file_name, stream),
[&]() -> void { --async_work_count_; });
}
std::shared_ptr<AudioBus> Engine::GetAudioBus(const std::string& asset_name) {
auto it = audio_buses_.find(asset_name);
if (it == audio_buses_.end()) {
DLOG << "AudioBus not found: " << asset_name;
return nullptr;
}
return it->second;
}
std::unique_ptr<InputEvent> Engine::GetNextInputEvent() {
@ -564,6 +562,35 @@ void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
input_queue_.push_back(std::move(event));
}
void Engine::CreateRendererInternal(RendererType type) {
if ((dynamic_cast<RendererVulkan*>(renderer_.get()) &&
type == RendererType::kVulkan) ||
(dynamic_cast<RendererOpenGL*>(renderer_.get()) &&
type == RendererType::kOpenGL))
return;
if (type == RendererType::kVulkan)
renderer_ =
std::make_unique<RendererVulkan>(std::bind(&Engine::ContextLost, this));
else if (type == RendererType::kOpenGL)
renderer_ =
std::make_unique<RendererOpenGL>(std::bind(&Engine::ContextLost, this));
else
NOTREACHED;
bool result = renderer_->Initialize(platform_);
if (!result && type == RendererType::kVulkan) {
LOG << "Failed to initialize " << renderer_->GetDebugName() << " renderer.";
LOG << "Fallback to OpenGL renderer.";
CreateRendererInternal(RendererType::kOpenGL);
return;
}
CHECK(result) << "Failed to initialize " << renderer_->GetDebugName()
<< " renderer.";
CreateTextureCompressors();
}
void Engine::CreateTextureCompressors() {
tex_comp_alpha_.reset();
tex_comp_opaque_.reset();
@ -587,6 +614,15 @@ void Engine::CreateTextureCompressors() {
}
void Engine::ContextLost() {
CreateRenderResources();
WaitForAsyncWork();
input_queue_.clear();
if (game_)
game_->ContextLost();
}
void Engine::CreateRenderResources() {
quad_->SetRenderer(renderer_.get());
pass_through_shader_->SetRenderer(renderer_.get());
solid_shader_->SetRenderer(renderer_.get());
@ -623,21 +659,45 @@ void Engine::ContextLost() {
for (auto& t : textures_) {
t.second.texture->SetRenderer(renderer_.get());
RefreshImage(t.first);
if (t.second.persistent || t.second.use_count > 0) {
++async_work_count_;
thread_pool_.PostTaskAndReplyWithResult<std::unique_ptr<Image>>(
HERE, t.second.create_image,
[&,
ptr = t.second.texture.get()](std::unique_ptr<Image> image) -> void {
--async_work_count_;
if (image)
ptr->Update(std::move(image));
});
}
}
for (auto& s : shaders_) {
s.second.shader->SetRenderer(renderer_.get());
auto source = std::make_unique<ShaderSource>();
if (source->Load(s.second.file_name))
s.second.shader->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false);
++async_work_count_;
thread_pool_.PostTaskAndReplyWithResult<std::unique_ptr<ShaderSource>>(
HERE,
[file_name = s.second.file_name]() -> std::unique_ptr<ShaderSource> {
auto source = std::make_unique<ShaderSource>();
if (!source->Load(file_name))
return nullptr;
return source;
},
[&, ptr = s.second.shader.get()](
std::unique_ptr<ShaderSource> source) -> void {
--async_work_count_;
if (source)
ptr->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false);
});
}
}
if (game_)
game_->ContextLost();
input_queue_.clear();
void Engine::WaitForAsyncWork() {
while (async_work_count_ > 0) {
TaskRunner::GetThreadLocalTaskRunner()->RunTasks();
platform_->Update();
}
}
void Engine::SetStatsVisible(bool visible) {

View File

@ -19,6 +19,7 @@ class TextureCompressor;
namespace eng {
class Animator;
class AudioBus;
class AudioMixer;
class Drawable;
class Font;
@ -61,11 +62,6 @@ class Engine : public PlatformObserver {
// Convert position form pixels to viewport coordinates.
base::Vector2f ToPosition(const base::Vector2f& vec);
template <typename T>
std::unique_ptr<T> CreateRenderResource() {
return std::unique_ptr<T>(static_cast<T*>(new T(renderer_.get())));
}
void SetImageSource(const std::string& asset_name,
const std::string& file_name,
bool persistent = false);
@ -78,10 +74,14 @@ class Engine : public PlatformObserver {
Texture* AcquireTexture(const std::string& asset_name);
void ReleaseTexture(const std::string& asset_name);
void LoadCustomShader(const std::string& asset_name,
const std::string& file_name);
Shader* GetCustomShader(const std::string& asset_name);
void RemoveCustomShader(const std::string& asset_name);
void SetShaderSource(const std::string& asset_name,
const std::string& file_name);
Shader* GetShader(const std::string& asset_name);
void AsyncLoadSound(const std::string& asset_name,
const std::string& file_name,
bool stream = false);
std::shared_ptr<AudioBus> GetAudioBus(const std::string& asset_name);
std::unique_ptr<InputEvent> GetNextInputEvent();
@ -191,9 +191,12 @@ class Engine : public PlatformObserver {
std::list<Animator*> animators_;
// Managed render resources mapped by asset name.
// Resources mapped by asset name.
std::unordered_map<std::string, TextureResource> textures_;
std::unordered_map<std::string, ShaderResource> shaders_;
std::unordered_map<std::string, std::shared_ptr<AudioBus>> audio_buses_;
size_t async_work_count_ = 0;
std::unique_ptr<ImageQuad> stats_;
@ -232,10 +235,16 @@ class Engine : public PlatformObserver {
void GainedFocus(bool from_interstitial_ad) final;
void AddInputEvent(std::unique_ptr<InputEvent> event) final;
void CreateRendererInternal(RendererType type);
void CreateTextureCompressors();
void ContextLost();
void CreateRenderResources();
void WaitForAsyncWork();
void SetStatsVisible(bool visible);
std::unique_ptr<Image> PrintStats();

View File

@ -8,6 +8,7 @@
#include "engine/platform/asset_file.h"
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#include "../third_party/stb/stb_truetype.h"
namespace eng {

View File

@ -8,6 +8,10 @@ class Game {
Game() = default;
virtual ~Game() = default;
// Called before async-loading assets.
virtual bool PreInitialize() = 0;
// Called after resources are created.
virtual bool Initialize() = 0;
virtual void Update(float delta_time) = 0;

View File

@ -12,7 +12,6 @@
// This 3rd party library is written in C and uses malloc, which means that we
// have to do the same.
#define STBI_NO_STDIO
#include "../third_party/stb/stb_image.h"
using namespace base;

View File

@ -32,7 +32,6 @@ void ImageQuad::Create(const std::string& asset_name,
asset_name_ = asset_name;
}
// TODO: typo
void ImageQuad::Destroy() {
if (texture_) {
Engine::Get().ReleaseTexture(asset_name_);
@ -51,7 +50,7 @@ void ImageQuad::AutoScale() {
}
void ImageQuad::SetCustomShader(const std::string& asset_name) {
custom_shader_ = Engine::Get().GetCustomShader(asset_name);
custom_shader_ = Engine::Get().GetShader(asset_name);
custom_uniforms_.clear();
}

View File

@ -25,6 +25,8 @@ Platform::Platform() {
shared_data_path_ = "./";
LOG << "Shared data path: " << shared_data_path_.c_str();
XInitThreads();
bool res = CreateWindow(800, 1205);
CHECK(res) << "Failed to create window.";

View File

@ -44,7 +44,8 @@ RendererOpenGL::RendererOpenGL(base::Closure context_lost_cb)
: Renderer(context_lost_cb),
main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {}
#else
RendererOpenGL::RendererOpenGL() = default;
RendererOpenGL::RendererOpenGL(base::Closure context_lost_cb)
: Renderer(context_lost_cb) {}
#endif // THREADED_RENDERING
RendererOpenGL::~RendererOpenGL() {

View File

@ -414,10 +414,7 @@ uint64_t RendererVulkan::CreateGeometry(std::unique_ptr<Mesh> mesh) {
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT));
task_runner_.PostTask(HERE, [&, mesh = mesh.release()]() {
// Transfer mesh ownership to the background thread.
std::unique_ptr<Mesh> own(mesh);
});
task_runner_.Delete(HERE, std::move(mesh));
semaphore_.release();
return last_resource_id_;
@ -506,10 +503,7 @@ void RendererVulkan::UpdateTexture(uint64_t resource_id,
0, VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
task_runner_.PostTask(HERE, [&, image = image.release()]() {
// Transfer image ownership to the background thread.
std::unique_ptr<Image> own(image);
});
task_runner_.Delete(HERE, std::move(image));
semaphore_.release();
}

View File

@ -17,6 +17,10 @@ SoundPlayer::~SoundPlayer() {
Engine::Get().GetAudioMixer()->DestroyResource(resource_id_);
}
void SoundPlayer::SetSound(const std::string& asset_name) {
sound_ = Engine::Get().GetAudioBus(asset_name);
}
void SoundPlayer::SetSound(std::shared_ptr<AudioBus> sound) {
sound_ = sound;
}

View File

@ -2,6 +2,7 @@
#define ENGINE_AUDIO_PLAYER_H
#include <memory>
#include <string>
#include "base/closure.h"
@ -14,6 +15,7 @@ class SoundPlayer {
SoundPlayer();
~SoundPlayer();
void SetSound(const std::string& asset_name);
void SetSound(std::shared_ptr<AudioBus> sound);
void Play(bool loop, float fade_in_duration = 0);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff