Async-load support for assets

This commit is contained in:
Attila Uygun 2023-06-21 23:24:29 +02:00
parent 8e6589ec67
commit 709029f22c
16 changed files with 304 additions and 256 deletions

View File

@ -13,7 +13,6 @@
#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"
DECLARE_GAME_BEGIN DECLARE_GAME_BEGIN
DECLARE_GAME(Demo) DECLARE_GAME(Demo)
@ -47,16 +46,38 @@ Demo::~Demo() {
saved_data_.Save(); saved_data_.Save();
} }
bool Demo::Initialize() { bool Demo::PreInitialize() {
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")) if (!font_.Load("PixelCaps!.ttf"))
return false; 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)) { if (!sky_.Create(false)) {
LOG << "Could not create the sky."; LOG << "Could not create the sky.";
return false; return false;
@ -87,18 +108,10 @@ bool Demo::Initialize() {
return false; return false;
} }
auto sound = std::make_unique<Sound>(); music_.SetSound("music");
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_.SetMaxAplitude(0.5f); music_.SetMaxAplitude(0.5f);
boss_music_.SetSound(std::move(boss_sound)); boss_music_.SetSound("boss_music");
boss_music_.SetMaxAplitude(0.5f); 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())
@ -172,6 +185,7 @@ 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() {}

View File

@ -23,14 +23,12 @@ 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);

View File

@ -13,7 +13,6 @@
#include "engine/font.h" #include "engine/font.h"
#include "engine/image.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"
@ -78,46 +77,44 @@ Enemy::Enemy() = default;
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() { 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_sound_); boss_intro_.SetSound("boss_intro");
boss_intro_.SetVariate(false); boss_intro_.SetVariate(false);
boss_intro_.SetSimulateStereo(false); boss_intro_.SetSimulateStereo(false);
@ -729,29 +726,29 @@ 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(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.SetMaxAplitude(2.0f);
e.spawn.Play(false); e.spawn.Play(false);
} else { } else {
e.explosion.SetSound(explosion_sound_); e.explosion.SetSound("explosion");
e.explosion.SetVariate(true); e.explosion.SetVariate(true);
e.explosion.SetSimulateStereo(true); e.explosion.SetSimulateStereo(true);
e.explosion.SetMaxAplitude(0.9f); e.explosion.SetMaxAplitude(0.9f);
} }
e.stealth.SetSound(stealth_sound_); e.stealth.SetSound("stealth");
e.stealth.SetVariate(false); e.stealth.SetVariate(false);
e.stealth.SetSimulateStereo(false); e.stealth.SetSimulateStereo(false);
e.stealth.SetMaxAplitude(0.7f); 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.SetVariate(false);
e.shield_on.SetSimulateStereo(false); e.shield_on.SetSimulateStereo(false);
e.shield_on.SetMaxAplitude(0.5f); e.shield_on.SetMaxAplitude(0.5f);
e.hit.SetSound(hit_sound_); e.hit.SetSound("hit");
e.hit.SetVariate(true); e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false); e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f); e.hit.SetMaxAplitude(0.5f);
@ -825,11 +822,11 @@ 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_sound_); e.explosion.SetSound("boss_explosion");
e.explosion.SetVariate(false); e.explosion.SetVariate(false);
e.explosion.SetSimulateStereo(false); e.explosion.SetSimulateStereo(false);
e.hit.SetSound(hit_sound_); e.hit.SetSound("hit");
e.hit.SetVariate(true); e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false); e.hit.SetSimulateStereo(false);
e.hit.SetMaxAplitude(0.5f); e.hit.SetMaxAplitude(0.5f);
@ -1185,30 +1182,6 @@ 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);

View File

@ -15,7 +15,6 @@
namespace eng { namespace eng {
class Image; class Image;
class Sound;
} // namespace eng } // namespace eng
class Enemy { class Enemy {
@ -23,6 +22,7 @@ class Enemy {
Enemy(); Enemy();
~Enemy(); ~Enemy();
bool PreInitialize();
bool Initialize(); bool Initialize();
void Update(float delta_time); void Update(float delta_time);
@ -109,15 +109,6 @@ 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;
@ -164,8 +155,6 @@ 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);
}; };

View File

@ -53,7 +53,7 @@ Menu::Menu() = default;
Menu::~Menu() = default; Menu::~Menu() = default;
bool Menu::Initialize() { bool Menu::PreInitialize() {
click_sound_ = std::make_shared<Sound>(); click_sound_ = std::make_shared<Sound>();
if (!click_sound_->Load("menu_click.mp3", false)) if (!click_sound_->Load("menu_click.mp3", false))
return false; return false;
@ -70,8 +70,59 @@ bool Menu::Initialize() {
max_text_width_ = width; max_text_width_ = width;
} }
if (!CreateRenderResources()) Engine::Get().SetImageSource("menu_tex",
return false; 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) { for (int i = 0; i < kOption_Max; ++i) {
items_[i].text.Create("menu_tex", {1, 4}); items_[i].text.Create("menu_tex", {1, 4});
@ -199,8 +250,6 @@ 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));
}, },
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}});
@ -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() { void Menu::Show() {
logo_[1].SetColor(kColorNormal); logo_[1].SetColor(kColorNormal);
logo_animator_[0].SetVisible(true); 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() { 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();

View File

@ -30,11 +30,13 @@ 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);
@ -145,9 +147,6 @@ 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();

View File

@ -5,7 +5,6 @@
#include "engine/engine.h" #include "engine/engine.h"
#include "engine/font.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,22 +28,20 @@ Player::Player() = default;
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() { 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) +
@ -75,12 +72,12 @@ bool Player::Initialize() {
nuke_symbol_animator_.Attach(&nuke_symbol_); nuke_symbol_animator_.Attach(&nuke_symbol_);
nuke_explosion_.SetSound(nuke_explosion_sound_); nuke_explosion_.SetSound("nuke");
nuke_explosion_.SetVariate(false); nuke_explosion_.SetVariate(false);
nuke_explosion_.SetSimulateStereo(false); nuke_explosion_.SetSimulateStereo(false);
nuke_explosion_.SetMaxAplitude(0.8f); nuke_explosion_.SetMaxAplitude(0.8f);
no_nuke_.SetSound(no_nuke_sound_); no_nuke_.SetSound("no_nuke");
return true; return true;
} }
@ -306,7 +303,7 @@ 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_shot_sound_); laser_shot_[i].SetSound("laser");
laser_shot_[i].SetVariate(true); laser_shot_[i].SetVariate(true);
laser_shot_[i].SetSimulateStereo(false); laser_shot_[i].SetSimulateStereo(false);
laser_shot_[i].SetMaxAplitude(0.4f); laser_shot_[i].SetMaxAplitude(0.4f);
@ -485,12 +482,3 @@ 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;
}

View File

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

View File

@ -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_ = Engine::Get().GetCustomShader( shader_ =
without_nebula ? "sky_without_nebula" : "sky"); Engine::Get().GetShader(without_nebula ? "sky_without_nebula" : "sky");
color_animator_.Attach(this); color_animator_.Attach(this);

View File

@ -20,6 +20,7 @@
#include "engine/renderer/texture.h" #include "engine/renderer/texture.h"
#include "engine/renderer/vulkan/renderer_vulkan.h" #include "engine/renderer/vulkan/renderer_vulkan.h"
#include "engine/shader_source.h" #include "engine/shader_source.h"
#include "engine/sound.h"
#include "third_party/texture_compressor/texture_compressor.h" #include "third_party/texture_compressor/texture_compressor.h"
using namespace base; using namespace base;
@ -84,6 +85,7 @@ void Engine::Run() {
if (!renderer_->IsInitialzed()) { if (!renderer_->IsInitialzed()) {
timer_.Reset(); timer_.Reset();
input_queue_.clear();
continue; continue;
} }
@ -109,7 +111,7 @@ void Engine::Initialize() {
thread_pool_.Initialize(); thread_pool_.Initialize();
CreateRenderer(RendererType::kVulkan); CreateRendererInternal(RendererType::kVulkan);
// Normalize viewport. // Normalize viewport.
if (GetScreenWidth() > GetScreenHeight()) { if (GetScreenWidth() > GetScreenHeight()) {
@ -134,7 +136,11 @@ void Engine::Initialize() {
game_ = GameFactoryBase::CreateGame(""); game_ = GameFactoryBase::CreateGame("");
CHECK(game_) << "No game found to run."; 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."; CHECK(game_->Initialize()) << "Failed to initialize the game.";
} }
@ -205,33 +211,12 @@ void Engine::RemoveAnimator(Animator* animator) {
} }
void Engine::CreateRenderer(RendererType type) { void Engine::CreateRenderer(RendererType type) {
if ((dynamic_cast<RendererVulkan*>(renderer_.get()) && // Create a new renderer next cycle.
type == RendererType::kVulkan) || TaskRunner::TaskRunner::GetThreadLocalTaskRunner()->PostTask(
(dynamic_cast<RendererOpenGL*>(renderer_.get()) && HERE, std::bind(&Engine::CreateRendererInternal, this, type));
type == RendererType::kOpenGL)) TaskRunner::TaskRunner::GetThreadLocalTaskRunner()->PostTask(
return; HERE, std::bind(&Engine::ContextLost, this));
input_queue_.clear();
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();
} }
RendererType Engine::GetRendererType() { RendererType Engine::GetRendererType() {
@ -323,41 +308,58 @@ void Engine::ReleaseTexture(const std::string& asset_name) {
it->second.texture->Destroy(); it->second.texture->Destroy();
} }
void Engine::LoadCustomShader(const std::string& asset_name, void Engine::SetShaderSource(const std::string& asset_name,
const std::string& file_name) { const std::string& file_name) {
if (shaders_.contains(asset_name)) { if (shaders_.contains(asset_name)) {
DLOG << "Shader already exists: " << asset_name; DLOG << "Shader already exists: " << asset_name;
return; return;
} }
auto& s = shaders_[asset_name] = {std::make_unique<Shader>(renderer_.get()), shaders_[asset_name] = {std::make_unique<Shader>(renderer_.get()), file_name};
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);
} }
Shader* Engine::GetCustomShader(const std::string& asset_name) { Shader* Engine::GetShader(const std::string& asset_name) {
auto it = shaders_.find(asset_name); auto it = shaders_.find(asset_name);
if (it == shaders_.end()) { if (it == shaders_.end()) {
DLOG << "Shader not found: " << asset_name; DLOG << "Shader not found: " << asset_name;
return nullptr; 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(); return it->second.shader.get();
} }
void Engine::RemoveCustomShader(const std::string& asset_name) { void Engine::AsyncLoadSound(const std::string& asset_name,
auto it = shaders_.find(asset_name); const std::string& file_name,
if (it == shaders_.end()) { bool stream) {
DLOG << "Shader not found: " << asset_name; if (audio_buses_.contains(asset_name)) {
DLOG << "AudioBus already exists: " << asset_name;
return; 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() { std::unique_ptr<InputEvent> Engine::GetNextInputEvent() {
@ -560,6 +562,35 @@ void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
input_queue_.push_back(std::move(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() { void Engine::CreateTextureCompressors() {
tex_comp_alpha_.reset(); tex_comp_alpha_.reset();
tex_comp_opaque_.reset(); tex_comp_opaque_.reset();
@ -583,6 +614,15 @@ void Engine::CreateTextureCompressors() {
} }
void Engine::ContextLost() { void Engine::ContextLost() {
CreateRenderResources();
WaitForAsyncWork();
input_queue_.clear();
if (game_)
game_->ContextLost();
}
void Engine::CreateRenderResources() {
quad_->SetRenderer(renderer_.get()); quad_->SetRenderer(renderer_.get());
pass_through_shader_->SetRenderer(renderer_.get()); pass_through_shader_->SetRenderer(renderer_.get());
solid_shader_->SetRenderer(renderer_.get()); solid_shader_->SetRenderer(renderer_.get());
@ -620,24 +660,44 @@ void Engine::ContextLost() {
for (auto& t : textures_) { for (auto& t : textures_) {
t.second.texture->SetRenderer(renderer_.get()); t.second.texture->SetRenderer(renderer_.get());
if (t.second.persistent || t.second.use_count > 0) { if (t.second.persistent || t.second.use_count > 0) {
auto image = t.second.create_image(); ++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) if (image)
t.second.texture->Update(std::move(image)); ptr->Update(std::move(image));
});
} }
} }
for (auto& s : shaders_) { for (auto& s : shaders_) {
s.second.shader->SetRenderer(renderer_.get()); s.second.shader->SetRenderer(renderer_.get());
++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>(); auto source = std::make_unique<ShaderSource>();
if (source->Load(s.second.file_name)) if (!source->Load(file_name))
s.second.shader->Create(std::move(source), quad_->vertex_description(), 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); quad_->primitive(), false);
});
}
} }
if (game_) void Engine::WaitForAsyncWork() {
game_->ContextLost(); while (async_work_count_ > 0) {
TaskRunner::GetThreadLocalTaskRunner()->RunTasks();
input_queue_.clear(); platform_->Update();
}
} }
void Engine::SetStatsVisible(bool visible) { void Engine::SetStatsVisible(bool visible) {

View File

@ -19,6 +19,7 @@ class TextureCompressor;
namespace eng { namespace eng {
class Animator; class Animator;
class AudioBus;
class AudioMixer; class AudioMixer;
class Drawable; class Drawable;
class Font; class Font;
@ -73,10 +74,14 @@ class Engine : public PlatformObserver {
Texture* AcquireTexture(const std::string& asset_name); Texture* AcquireTexture(const std::string& asset_name);
void ReleaseTexture(const std::string& asset_name); void ReleaseTexture(const std::string& asset_name);
void LoadCustomShader(const std::string& asset_name, void SetShaderSource(const std::string& asset_name,
const std::string& file_name); const std::string& file_name);
Shader* GetCustomShader(const std::string& asset_name); Shader* GetShader(const std::string& asset_name);
void RemoveCustomShader(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(); std::unique_ptr<InputEvent> GetNextInputEvent();
@ -186,9 +191,12 @@ class Engine : public PlatformObserver {
std::list<Animator*> animators_; 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, TextureResource> textures_;
std::unordered_map<std::string, ShaderResource> shaders_; 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_; std::unique_ptr<ImageQuad> stats_;
@ -227,10 +235,16 @@ class Engine : public PlatformObserver {
void GainedFocus(bool from_interstitial_ad) final; void GainedFocus(bool from_interstitial_ad) final;
void AddInputEvent(std::unique_ptr<InputEvent> event) final; void AddInputEvent(std::unique_ptr<InputEvent> event) final;
void CreateRendererInternal(RendererType type);
void CreateTextureCompressors(); void CreateTextureCompressors();
void ContextLost(); void ContextLost();
void CreateRenderResources();
void WaitForAsyncWork();
void SetStatsVisible(bool visible); void SetStatsVisible(bool visible);
std::unique_ptr<Image> PrintStats(); std::unique_ptr<Image> PrintStats();

View File

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

View File

@ -50,7 +50,7 @@ void ImageQuad::AutoScale() {
} }
void ImageQuad::SetCustomShader(const std::string& asset_name) { 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(); custom_uniforms_.clear();
} }

View File

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

View File

@ -17,6 +17,10 @@ SoundPlayer::~SoundPlayer() {
Engine::Get().GetAudioMixer()->DestroyResource(resource_id_); 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) { void SoundPlayer::SetSound(std::shared_ptr<AudioBus> sound) {
sound_ = sound; sound_ = sound;
} }

View File

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