Compare commits

...

8 Commits

Author SHA1 Message Date
Attila Uygun f6f67d7e53 Cleanup renderer 2023-05-26 22:54:14 +02:00
Attila Uygun 43afdf855f Move renderer ownership to engine 2023-05-26 22:54:14 +02:00
Attila Uygun 66557f07e3 Do not reinitialize Vulcan context unnecessarily. 2023-05-26 22:45:24 +02:00
Attila Uygun d5ee1c7313 Rename AudioDriver to AudioSink 2023-05-24 23:14:51 +02:00
Attila Uygun 8d28d85a09 Move AudioDriver ownership to AudioMixer 2023-05-24 23:14:14 +02:00
Attila Uygun b160ae0bd3 Support for switching between renderers in run-time 2023-05-24 23:14:14 +02:00
Attila Uygun b7f1f7faa2 Shutdown renderer in destructor 2023-05-23 00:01:19 +02:00
Attila Uygun 94242fca0b Revert "Try Vulkan renderer first, fallback to OpenGL if it fails."
This reverts commit daa3a8c06f.
2023-05-23 00:01:19 +02:00
34 changed files with 627 additions and 636 deletions

View File

@ -62,8 +62,8 @@ add_library(kaliber SHARED
../../../src/demo/sky_quad.cc ../../../src/demo/sky_quad.cc
../../../src/engine/animatable.cc ../../../src/engine/animatable.cc
../../../src/engine/animator.cc ../../../src/engine/animator.cc
../../../src/engine/audio/audio_driver_oboe.cc
../../../src/engine/audio/audio_mixer.cc ../../../src/engine/audio/audio_mixer.cc
../../../src/engine/audio/audio_sink_oboe.cc
../../../src/engine/drawable.cc ../../../src/engine/drawable.cc
../../../src/engine/engine.cc ../../../src/engine/engine.cc
../../../src/engine/font.cc ../../../src/engine/font.cc
@ -74,7 +74,6 @@ add_library(kaliber SHARED
../../../src/engine/platform/asset_file_android.cc ../../../src/engine/platform/asset_file_android.cc
../../../src/engine/platform/asset_file.cc ../../../src/engine/platform/asset_file.cc
../../../src/engine/platform/platform_android.cc ../../../src/engine/platform/platform_android.cc
../../../src/engine/platform/platform.cc
../../../src/engine/renderer/geometry.cc ../../../src/engine/renderer/geometry.cc
../../../src/engine/renderer/opengl/render_command.cc ../../../src/engine/renderer/opengl/render_command.cc
../../../src/engine/renderer/opengl/renderer_opengl_android.cc ../../../src/engine/renderer/opengl/renderer_opengl_android.cc

View File

@ -95,8 +95,8 @@ $(BASE_LIB): $(BASE_OBJS)
ENGINE_SRC := \ ENGINE_SRC := \
$(SRC_ROOT)/engine/animatable.cc \ $(SRC_ROOT)/engine/animatable.cc \
$(SRC_ROOT)/engine/animator.cc \ $(SRC_ROOT)/engine/animator.cc \
$(SRC_ROOT)/engine/audio/audio_driver_alsa.cc \
$(SRC_ROOT)/engine/audio/audio_mixer.cc \ $(SRC_ROOT)/engine/audio/audio_mixer.cc \
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
$(SRC_ROOT)/engine/drawable.cc \ $(SRC_ROOT)/engine/drawable.cc \
$(SRC_ROOT)/engine/engine.cc \ $(SRC_ROOT)/engine/engine.cc \
$(SRC_ROOT)/engine/font.cc \ $(SRC_ROOT)/engine/font.cc \
@ -106,7 +106,6 @@ ENGINE_SRC := \
$(SRC_ROOT)/engine/persistent_data.cc \ $(SRC_ROOT)/engine/persistent_data.cc \
$(SRC_ROOT)/engine/platform/asset_file_linux.cc \ $(SRC_ROOT)/engine/platform/asset_file_linux.cc \
$(SRC_ROOT)/engine/platform/asset_file.cc \ $(SRC_ROOT)/engine/platform/asset_file.cc \
$(SRC_ROOT)/engine/platform/platform.cc \
$(SRC_ROOT)/engine/platform/platform_linux.cc \ $(SRC_ROOT)/engine/platform/platform_linux.cc \
$(SRC_ROOT)/engine/renderer/geometry.cc \ $(SRC_ROOT)/engine/renderer/geometry.cc \
$(SRC_ROOT)/engine/renderer/opengl/render_command.cc \ $(SRC_ROOT)/engine/renderer/opengl/render_command.cc \

View File

@ -95,7 +95,7 @@ void Credits::Show() {
void Credits::Hide() { void Credits::Hide() {
text_animator_.SetEndCallback(Animator::kBlending, [&]() -> void { text_animator_.SetEndCallback(Animator::kBlending, [&]() -> void {
for (int i = 0; i < kNumLines; ++i) for (int i = 0; i < kNumLines; ++i)
text_[i].Destory(); text_[i].Destroy();
text_animator_.SetEndCallback(Animator::kBlending, nullptr); text_animator_.SetEndCallback(Animator::kBlending, nullptr);
text_animator_.SetVisible(false); text_animator_.SetVisible(false);
}); });

View File

@ -1,35 +0,0 @@
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_H
#define ENGINE_AUDIO_AUDIO_DRIVER_H
#include "engine/audio/audio_driver.h"
namespace eng {
class AudioDriverDelegate;
// Models an audio sink sending mixed audio to the audio driver. Audio data from
// the mixer source is delivered on a pull model using AudioDriverDelegate.
class AudioDriver {
public:
AudioDriver() = default;
virtual ~AudioDriver() = default;
virtual void SetDelegate(AudioDriverDelegate* delegate) = 0;
virtual bool Initialize() = 0;
virtual void Shutdown() = 0;
virtual void Suspend() = 0;
virtual void Resume() = 0;
virtual int GetHardwareSampleRate() = 0;
private:
AudioDriver(const AudioDriver&) = delete;
AudioDriver& operator=(const AudioDriver&) = delete;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DRIVER_H

View File

@ -1,18 +0,0 @@
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H
#define ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H
#include <stddef.h>
namespace eng {
class AudioDriverDelegate {
public:
AudioDriverDelegate() = default;
virtual ~AudioDriverDelegate() = default;
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H

View File

@ -7,12 +7,26 @@
#include "base/thread_pool.h" #include "base/thread_pool.h"
#include "engine/sound.h" #include "engine/sound.h"
#if defined(__ANDROID__)
#include "engine/audio/audio_sink_oboe.h"
#elif defined(__linux__)
#include "engine/audio/audio_sink_alsa.h"
#endif
using namespace base; using namespace base;
namespace eng { namespace eng {
AudioMixer::AudioMixer() AudioMixer::AudioMixer()
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {} : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()),
#if defined(__ANDROID__)
audio_sink_{std::make_unique<AudioSinkOboe>(this)} {
#elif defined(__linux__)
audio_sink_{std::make_unique<AudioSinkAlsa>(this)} {
#endif
bool res = audio_sink_->Initialize();
CHECK(res) << "Failed to initialize audio sink.";
}
AudioMixer::~AudioMixer() = default; AudioMixer::~AudioMixer() = default;
@ -139,6 +153,18 @@ void AudioMixer::SetEndCallback(uint64_t resource_id, base::Closure cb) {
it->second->end_cb = std::move(cb); it->second->end_cb = std::move(cb);
} }
void AudioMixer::Suspend() {
audio_sink_->Suspend();
}
void AudioMixer::Resume() {
audio_sink_->Resume();
}
int AudioMixer::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);
@ -238,7 +264,7 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
ThreadPool::Get().PostTask( ThreadPool::Get().PostTask(
HERE, HERE,
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop)); std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop));
} else if (num_samples) { } else {
DLOG << "Mixer buffer underrun!"; DLOG << "Mixer buffer underrun!";
} }
} }

View File

@ -8,7 +8,7 @@
#include <unordered_map> #include <unordered_map>
#include "base/closure.h" #include "base/closure.h"
#include "engine/audio/audio_driver_delegate.h" #include "engine/audio/audio_sink_delegate.h"
namespace base { namespace base {
class TaskRunner; class TaskRunner;
@ -16,12 +16,13 @@ class TaskRunner;
namespace eng { namespace eng {
class AudioSink;
class Sound; class Sound;
// Mix and render audio with low overhead. A platform specific AudioDriver // Mix and render audio with low overhead. A platform specific AudioSink
// implementation is expected to periodically call Render() in a background // implementation is expected to periodically call RenderAudio() in a background
// thread. // thread.
class AudioMixer : public AudioDriverDelegate { class AudioMixer : public AudioSinkDelegate {
public: public:
AudioMixer(); AudioMixer();
~AudioMixer(); ~AudioMixer();
@ -44,10 +45,16 @@ class AudioMixer : public AudioDriverDelegate {
void SetEnableAudio(bool enable) { audio_enabled_ = enable; } void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
void Suspend();
void Resume();
int GetHardwareSampleRate();
private: private:
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 }; enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
static constexpr int kChannelCount = 2; static constexpr int kChannelCount = 2;
// An audio resource that gets mixed and rendered to the audio sink.
struct Resource { struct Resource {
// Accessed by main thread only. // Accessed by main thread only.
bool active = false; bool active = false;
@ -80,9 +87,12 @@ class AudioMixer : public AudioDriverDelegate {
base::TaskRunner* main_thread_task_runner_; base::TaskRunner* main_thread_task_runner_;
std::unique_ptr<AudioSink> audio_sink_;
bool audio_enabled_ = true; bool audio_enabled_ = true;
// AudioDriverDelegate implementation // AudioSinkDelegate implementation
int GetChannelCount() final { return kChannelCount; }
void RenderAudio(float* output_buffer, size_t num_frames) final; void RenderAudio(float* output_buffer, size_t num_frames) final;
void DoStream(std::shared_ptr<Resource> sample, bool loop); void DoStream(std::shared_ptr<Resource> sample, bool loop);

View File

@ -0,0 +1,27 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_H
#define ENGINE_AUDIO_AUDIO_SINK_H
namespace eng {
// Models an audio sink sending mixed audio to the audio driver. Audio data from
// the mixer source is delivered on a pull model using AudioSinkDelegate.
class AudioSink {
public:
AudioSink() = default;
virtual ~AudioSink() = default;
virtual bool Initialize() = 0;
virtual void Suspend() = 0;
virtual void Resume() = 0;
virtual int GetHardwareSampleRate() = 0;
private:
AudioSink(const AudioSink&) = delete;
AudioSink& operator=(const AudioSink&) = delete;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_H

View File

@ -1,27 +1,29 @@
#include "engine/audio/audio_driver_alsa.h" #include "engine/audio/audio_sink_alsa.h"
#include <memory> #include <memory>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "base/log.h" #include "base/log.h"
#include "engine/audio/audio_driver_delegate.h" #include "engine/audio/audio_sink_delegate.h"
using namespace base; using namespace base;
namespace eng { namespace eng {
AudioDriverAlsa::AudioDriverAlsa() = default; AudioSinkAlsa::AudioSinkAlsa(AudioSinkDelegate* delegate)
: delegate_(delegate) {}
AudioDriverAlsa::~AudioDriverAlsa() = default; AudioSinkAlsa::~AudioSinkAlsa() {
LOG << "Shutting down audio.";
void AudioDriverAlsa::SetDelegate(AudioDriverDelegate* delegate) { TerminateAudioThread();
delegate_ = delegate; snd_pcm_drop(device_);
Resume(); snd_pcm_close(device_);
} }
bool AudioDriverAlsa::Initialize() { bool AudioSinkAlsa::Initialize() {
LOG << "Initializing audio system."; LOG << "Initializing audio.";
int err; int err;
@ -142,35 +144,29 @@ bool AudioDriverAlsa::Initialize() {
return false; return false;
} }
void AudioDriverAlsa::Shutdown() { void AudioSinkAlsa::Suspend() {
LOG << "Shutting down audio system.";
TerminateAudioThread();
snd_pcm_drop(device_);
snd_pcm_close(device_);
}
void AudioDriverAlsa::Suspend() {
suspend_audio_thread_.store(true, std::memory_order_relaxed); suspend_audio_thread_.store(true, std::memory_order_relaxed);
} }
void AudioDriverAlsa::Resume() { void AudioSinkAlsa::Resume() {
suspend_audio_thread_.store(false, std::memory_order_relaxed); suspend_audio_thread_.store(false, std::memory_order_relaxed);
} }
int AudioDriverAlsa::GetHardwareSampleRate() { int AudioSinkAlsa::GetHardwareSampleRate() {
return sample_rate_; return sample_rate_;
} }
void AudioDriverAlsa::StartAudioThread() { void AudioSinkAlsa::StartAudioThread() {
DCHECK(!audio_thread_.joinable());
LOG << "Starting audio thread."; LOG << "Starting audio thread.";
terminate_audio_thread_.store(false, std::memory_order_relaxed); terminate_audio_thread_.store(false, std::memory_order_relaxed);
suspend_audio_thread_.store(delegate_ ? false : true, suspend_audio_thread_.store(false, std::memory_order_relaxed);
std::memory_order_relaxed); audio_thread_ = std::thread(&AudioSinkAlsa::AudioThreadMain, this);
audio_thread_ = std::thread(&AudioDriverAlsa::AudioThreadMain, this);
} }
void AudioDriverAlsa::TerminateAudioThread() { void AudioSinkAlsa::TerminateAudioThread() {
if (terminate_audio_thread_.load(std::memory_order_relaxed)) if (!audio_thread_.joinable())
return; return;
LOG << "Terminating audio thread"; LOG << "Terminating audio thread";
@ -179,7 +175,9 @@ void AudioDriverAlsa::TerminateAudioThread() {
audio_thread_.join(); audio_thread_.join();
} }
void AudioDriverAlsa::AudioThreadMain() { void AudioSinkAlsa::AudioThreadMain() {
DCHECK(delegate_);
size_t num_frames = period_size_ / (num_channels_ * sizeof(float)); size_t num_frames = period_size_ / (num_channels_ * sizeof(float));
auto buffer = std::make_unique<float[]>(num_frames * 2); auto buffer = std::make_unique<float[]>(num_frames * 2);

View File

@ -1,26 +1,24 @@
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H #ifndef ENGINE_AUDIO_AUDIO_SINK_ALSA_H
#define ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H #define ENGINE_AUDIO_AUDIO_SINK_ALSA_H
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include "engine/audio/audio_driver.h" #include "engine/audio/audio_sink.h"
typedef struct _snd_pcm snd_pcm_t; typedef struct _snd_pcm snd_pcm_t;
namespace eng { namespace eng {
class AudioDriverAlsa final : public AudioDriver { class AudioSinkDelegate;
public:
AudioDriverAlsa();
~AudioDriverAlsa() final;
void SetDelegate(AudioDriverDelegate* delegate) final; class AudioSinkAlsa final : public AudioSink {
public:
AudioSinkAlsa(AudioSinkDelegate* delegate);
~AudioSinkAlsa() final;
bool Initialize() final; bool Initialize() final;
void Shutdown() final;
void Suspend() final; void Suspend() final;
void Resume() final; void Resume() final;
@ -38,7 +36,7 @@ class AudioDriverAlsa final : public AudioDriver {
int sample_rate_ = 0; int sample_rate_ = 0;
size_t period_size_ = 0; size_t period_size_ = 0;
AudioDriverDelegate* delegate_ = nullptr; AudioSinkDelegate* delegate_ = nullptr;
void StartAudioThread(); void StartAudioThread();
void TerminateAudioThread(); void TerminateAudioThread();
@ -48,4 +46,4 @@ class AudioDriverAlsa final : public AudioDriver {
} // namespace eng } // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H #endif // ENGINE_AUDIO_AUDIO_SINK_ALSA_H

View File

@ -0,0 +1,20 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_DELEGATE_H
#define ENGINE_AUDIO_AUDIO_SINK_DELEGATE_H
#include <stddef.h>
namespace eng {
class AudioSinkDelegate {
public:
AudioSinkDelegate() = default;
virtual ~AudioSinkDelegate() = default;
virtual int GetChannelCount() = 0;
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_DELEGATE_H

View File

@ -1,76 +1,67 @@
#include "engine/audio/audio_driver_oboe.h" #include "engine/audio/audio_sink_oboe.h"
#include "base/log.h" #include "base/log.h"
#include "engine/audio/audio_driver_delegate.h" #include "engine/audio/audio_sink_delegate.h"
#include "third_party/oboe/include/oboe/Oboe.h" #include "third_party/oboe/include/oboe/Oboe.h"
using namespace base; using namespace base;
namespace eng { namespace eng {
AudioDriverOboe::AudioDriverOboe() AudioSinkOboe::AudioSinkOboe(AudioSinkDelegate* delegate)
: callback_(std::make_unique<StreamCallback>(this)) {} : callback_(std::make_unique<StreamCallback>(this)), delegate_(delegate) {}
AudioDriverOboe::~AudioDriverOboe() = default;
void AudioDriverOboe::SetDelegate(AudioDriverDelegate* delegate) {
delegate_ = delegate;
stream_->start();
}
bool AudioDriverOboe::Initialize() {
LOG << "Initializing audio system.";
return RestartStream();
}
void AudioDriverOboe::Shutdown() {
LOG << "Shutting down audio system.";
AudioSinkOboe::~AudioSinkOboe() {
LOG << "Shutting down audio.";
stream_->stop(); stream_->stop();
} }
void AudioDriverOboe::Suspend() { bool AudioSinkOboe::Initialize() {
LOG << "Initializing audio.";
return RestartStream();
}
void AudioSinkOboe::Suspend() {
stream_->pause(); stream_->pause();
} }
void AudioDriverOboe::Resume() { void AudioSinkOboe::Resume() {
stream_->start(); stream_->start();
} }
int AudioDriverOboe::GetHardwareSampleRate() { int AudioSinkOboe::GetHardwareSampleRate() {
return stream_->getSampleRate(); return stream_->getSampleRate();
} }
AudioDriverOboe::StreamCallback::StreamCallback(AudioDriverOboe* driver) AudioSinkOboe::StreamCallback::StreamCallback(AudioSinkOboe* audio_sink)
: driver_(driver) {} : audio_sink_(audio_sink) {}
AudioDriverOboe::StreamCallback::~StreamCallback() = default; AudioSinkOboe::StreamCallback::~StreamCallback() = default;
oboe::DataCallbackResult AudioDriverOboe::StreamCallback::onAudioReady( oboe::DataCallbackResult AudioSinkOboe::StreamCallback::onAudioReady(
oboe::AudioStream* oboe_stream, oboe::AudioStream* oboe_stream,
void* audio_data, void* audio_data,
int32_t num_frames) { int32_t num_frames) {
float* output_buffer = static_cast<float*>(audio_data); float* output_buffer = static_cast<float*>(audio_data);
driver_->delegate_->RenderAudio(output_buffer, num_frames); audio_sink_->delegate_->RenderAudio(output_buffer, num_frames);
return oboe::DataCallbackResult::Continue; return oboe::DataCallbackResult::Continue;
} }
void AudioDriverOboe::StreamCallback::onErrorAfterClose( void AudioSinkOboe::StreamCallback::onErrorAfterClose(
oboe::AudioStream* oboe_stream, oboe::AudioStream* oboe_stream,
oboe::Result error) { oboe::Result error) {
LOG << "Error after close. Error: " << oboe::convertToText(error); LOG << "Error after close. Error: " << oboe::convertToText(error);
driver_->RestartStream(); audio_sink_->RestartStream();
} }
bool AudioDriverOboe::RestartStream() { bool AudioSinkOboe::RestartStream() {
oboe::AudioStreamBuilder builder; oboe::AudioStreamBuilder builder;
oboe::Result result = oboe::Result result =
builder.setSharingMode(oboe::SharingMode::Exclusive) builder.setSharingMode(oboe::SharingMode::Exclusive)
->setPerformanceMode(oboe::PerformanceMode::LowLatency) ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setFormat(oboe::AudioFormat::Float) ->setFormat(oboe::AudioFormat::Float)
->setChannelCount(kChannelCount) ->setChannelCount(delegate_->GetChannelCount())
->setDirection(oboe::Direction::Output) ->setDirection(oboe::Direction::Output)
->setUsage(oboe::Usage::Game) ->setUsage(oboe::Usage::Game)
->setCallback(callback_.get()) ->setCallback(callback_.get())
@ -88,7 +79,6 @@ bool AudioDriverOboe::RestartStream() {
return false; return false;
} }
if (delegate_)
stream_->start(); stream_->start();
return true; return true;
} }

View File

@ -1,37 +1,33 @@
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H #ifndef ENGINE_AUDIO_AUDIO_SINK_OBOE_H
#define ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H #define ENGINE_AUDIO_AUDIO_SINK_OBOE_H
#include <memory> #include <memory>
#include "third_party/oboe/include/oboe/AudioStream.h" #include "third_party/oboe/include/oboe/AudioStream.h"
#include "third_party/oboe/include/oboe/AudioStreamCallback.h" #include "third_party/oboe/include/oboe/AudioStreamCallback.h"
#include "engine/audio/audio_driver.h" #include "engine/audio/audio_sink.h"
namespace eng { namespace eng {
class AudioDriverOboe final : public AudioDriver { class AudioSinkDelegate;
public:
AudioDriverOboe();
~AudioDriverOboe() final;
void SetDelegate(AudioDriverDelegate* delegate) final; class AudioSinkOboe final : public AudioSink {
public:
AudioSinkOboe(AudioSinkDelegate* delegate);
~AudioSinkOboe() final;
bool Initialize() final; bool Initialize() final;
void Shutdown() final;
void Suspend() final; void Suspend() final;
void Resume() final; void Resume() final;
int GetHardwareSampleRate() final; int GetHardwareSampleRate() final;
private: private:
static constexpr int kChannelCount = 2;
class StreamCallback final : public oboe::AudioStreamCallback { class StreamCallback final : public oboe::AudioStreamCallback {
public: public:
StreamCallback(AudioDriverOboe* audio); StreamCallback(AudioSinkOboe* audio);
~StreamCallback() final; ~StreamCallback() final;
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream, oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream,
@ -42,17 +38,17 @@ class AudioDriverOboe final : public AudioDriver {
oboe::Result error) final; oboe::Result error) final;
private: private:
AudioDriverOboe* driver_; AudioSinkOboe* audio_sink_;
}; };
oboe::ManagedStream stream_; oboe::ManagedStream stream_;
std::unique_ptr<StreamCallback> callback_; std::unique_ptr<StreamCallback> callback_;
AudioDriverDelegate* delegate_ = nullptr; AudioSinkDelegate* delegate_ = nullptr;
bool RestartStream(); bool RestartStream();
}; };
} // namespace eng } // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H #endif // ENGINE_AUDIO_AUDIO_SINK_OBOE_H

View File

@ -1,8 +1,8 @@
#include "engine/engine.h" #include "engine/engine.h"
#include "base/log.h" #include "base/log.h"
#include "base/task_runner.h"
#include "engine/animator.h" #include "engine/animator.h"
#include "engine/audio/audio_driver.h"
#include "engine/audio/audio_mixer.h" #include "engine/audio/audio_mixer.h"
#include "engine/drawable.h" #include "engine/drawable.h"
#include "engine/font.h" #include "engine/font.h"
@ -14,40 +14,52 @@
#include "engine/mesh.h" #include "engine/mesh.h"
#include "engine/platform/platform.h" #include "engine/platform/platform.h"
#include "engine/renderer/geometry.h" #include "engine/renderer/geometry.h"
#include "engine/renderer/opengl/renderer_opengl.h"
#include "engine/renderer/renderer.h" #include "engine/renderer/renderer.h"
#include "engine/renderer/shader.h" #include "engine/renderer/shader.h"
#include "engine/renderer/texture.h" #include "engine/renderer/texture.h"
#include "engine/renderer/vulkan/renderer_vulkan.h"
#include "engine/shader_source.h" #include "engine/shader_source.h"
#include "third_party/texture_compressor/texture_compressor.h" #include "third_party/texture_compressor/texture_compressor.h"
#define USE_VULKAN_RENDERER 1
using namespace base; using namespace base;
namespace eng { namespace eng {
extern void KaliberMain(Platform* platform) {
TaskRunner::CreateThreadLocalTaskRunner();
Engine(platform).Run();
}
Engine* Engine::singleton = nullptr; Engine* Engine::singleton = nullptr;
Engine::Engine(Platform* platform, Engine::Engine(Platform* platform)
Renderer* renderer,
AudioDriver* audio_driver)
: platform_(platform), : platform_(platform),
renderer_(renderer), audio_mixer_{std::make_unique<AudioMixer>()},
audio_driver_(audio_driver), quad_{std::make_unique<Geometry>(nullptr)},
audio_mixer_{std::make_unique<AudioMixer>()} { pass_through_shader_{std::make_unique<Shader>(nullptr)},
solid_shader_{std::make_unique<Shader>(nullptr)} {
DCHECK(!singleton); DCHECK(!singleton);
singleton = this; singleton = this;
audio_driver_->SetDelegate(audio_mixer_.get()); platform_->SetObserver(this);
renderer_->SetContextLostCB(std::bind(&Engine::ContextLost, this));
quad_ = CreateRenderResource<Geometry>();
pass_through_shader_ = CreateRenderResource<Shader>();
solid_shader_ = CreateRenderResource<Shader>();
stats_ = std::make_unique<ImageQuad>(); stats_ = std::make_unique<ImageQuad>();
} }
Engine::~Engine() { Engine::~Engine() {
LOG << "Shutting down engine.";
game_.reset();
stats_.reset(); stats_.reset();
textures_.clear();
shaders_.clear();
quad_.reset();
pass_through_shader_.reset();
solid_shader_.reset();
renderer_.reset();
singleton = nullptr; singleton = nullptr;
} }
@ -55,7 +67,43 @@ Engine& Engine::Get() {
return *singleton; return *singleton;
} }
bool Engine::Initialize() { void Engine::Run() {
Initialize();
timer_.Reset();
float accumulator = 0.0;
float frame_frac = 0.0f;
for (;;) {
Draw(frame_frac);
// Accumulate time.
timer_.Update();
accumulator += timer_.GetSecondsPassed();
// Subdivide the frame time using fixed time steps.
while (accumulator >= time_step_) {
TaskRunner::GetThreadLocalTaskRunner()->SingleConsumerRun();
platform_->Update();
Update(time_step_);
accumulator -= time_step_;
if (platform_->should_exit())
return;
};
// Calculate frame fraction from remainder of the frame time.
frame_frac = accumulator / time_step_;
}
}
void Engine::Initialize() {
LOG << "Initializing engine.";
thread_pool_.Initialize();
CreateRenderer(true);
// Normalize viewport. // Normalize viewport.
if (GetScreenWidth() > GetScreenHeight()) { if (GetScreenWidth() > GetScreenHeight()) {
float aspect_ratio = (float)GetScreenWidth() / (float)GetScreenHeight(); float aspect_ratio = (float)GetScreenWidth() / (float)GetScreenHeight();
@ -71,52 +119,16 @@ bool Engine::Initialize() {
LOG << "image scale factor: " << GetImageScaleFactor(); LOG << "image scale factor: " << GetImageScaleFactor();
if (renderer_->SupportsDXT5()) {
tex_comp_alpha_ = TextureCompressor::Create(TextureCompressor::kFormatDXT5);
} else if (renderer_->SupportsATC()) {
tex_comp_alpha_ =
TextureCompressor::Create(TextureCompressor::kFormatATCIA);
}
if (renderer_->SupportsDXT1()) {
tex_comp_opaque_ =
TextureCompressor::Create(TextureCompressor::kFormatDXT1);
} else if (renderer_->SupportsATC()) {
tex_comp_opaque_ = TextureCompressor::Create(TextureCompressor::kFormatATC);
} else if (renderer_->SupportsETC1()) {
tex_comp_opaque_ =
TextureCompressor::Create(TextureCompressor::kFormatETC1);
}
system_font_ = std::make_unique<Font>(); system_font_ = std::make_unique<Font>();
system_font_->Load("engine/RobotoMono-Regular.ttf"); system_font_->Load("engine/RobotoMono-Regular.ttf");
if (!CreateRenderResources())
return false;
SetImageSource("stats_tex", std::bind(&Engine::PrintStats, this)); SetImageSource("stats_tex", std::bind(&Engine::PrintStats, this));
stats_->SetZOrder(std::numeric_limits<int>::max()); stats_->SetZOrder(std::numeric_limits<int>::max());
game_ = GameFactoryBase::CreateGame(""); game_ = GameFactoryBase::CreateGame("");
if (!game_) { CHECK(game_) << "No game found to run.";
LOG << "No game found to run.";
return false;
}
if (!game_->Initialize()) { CHECK(game_->Initialize()) << "Failed to initialize the game.";
LOG << "Failed to initialize the game.";
return false;
}
return true;
}
void Engine::Shutdown() {
LOG << "Shutting down engine.";
game_.reset();
stats_->Destory();
textures_.clear();
shaders_.clear();
} }
void Engine::Update(float delta_time) { void Engine::Update(float delta_time) {
@ -157,20 +169,6 @@ void Engine::Draw(float frame_frac) {
renderer_->Present(); renderer_->Present();
} }
void Engine::LostFocus() {
audio_driver_->Suspend();
if (game_)
game_->LostFocus();
}
void Engine::GainedFocus(bool from_interstitial_ad) {
audio_driver_->Resume();
if (game_)
game_->GainedFocus(from_interstitial_ad);
}
void Engine::AddDrawable(Drawable* drawable) { void Engine::AddDrawable(Drawable* drawable) {
DCHECK(std::find(drawables_.begin(), drawables_.end(), drawable) == DCHECK(std::find(drawables_.begin(), drawables_.end(), drawable) ==
drawables_.end()); drawables_.end());
@ -199,6 +197,32 @@ void Engine::RemoveAnimator(Animator* animator) {
} }
} }
void Engine::CreateRenderer(bool vulkan) {
if ((dynamic_cast<RendererVulkan*>(renderer_.get()) && vulkan) ||
(dynamic_cast<RendererOpenGL*>(renderer_.get()) && !vulkan))
return;
if (vulkan)
renderer_ =
std::make_unique<RendererVulkan>(std::bind(&Engine::ContextLost, this));
else
renderer_ =
std::make_unique<RendererOpenGL>(std::bind(&Engine::ContextLost, this));
bool result = renderer_->Initialize(platform_);
if (!result && vulkan) {
LOG << "Failed to initialize " << renderer_->GetDebugName() << " renderer.";
LOG << "Fallback to OpenGL renderer.";
CreateRenderer(false);
return;
}
CHECK(result) << "Failed to initialize " << renderer_->GetDebugName()
<< " renderer.";
CreateTextureCompressors();
ContextLost();
}
void Engine::Exit() { void Engine::Exit() {
platform_->Exit(); platform_->Exit();
} }
@ -321,40 +345,6 @@ void Engine::RemoveCustomShader(const std::string& asset_name) {
shaders_.erase(it); shaders_.erase(it);
} }
void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
if (replaying_)
return;
switch (event->GetType()) {
case InputEvent::kDragEnd:
if (((GetScreenSize() / 2) * 0.9f - event->GetVector()).Length() <=
0.25f) {
SetSatsVisible(!stats_->IsVisible());
// TODO: Enqueue DragCancel so we can consume this event.
}
break;
case InputEvent::kKeyPress:
if (event->GetKeyPress() == 's') {
SetSatsVisible(!stats_->IsVisible());
// Consume event.
return;
}
break;
case InputEvent::kDrag:
if (stats_->IsVisible()) {
if ((stats_->GetPosition() - event->GetVector()).Length() <=
stats_->GetSize().y)
stats_->SetPosition(event->GetVector());
// TODO: Enqueue DragCancel so we can consume this event.
}
break;
default:
break;
}
input_queue_.push_back(std::move(event));
}
std::unique_ptr<InputEvent> Engine::GetNextInputEvent() { std::unique_ptr<InputEvent> Engine::GetNextInputEvent() {
std::unique_ptr<InputEvent> event; std::unique_ptr<InputEvent> event;
@ -481,32 +471,107 @@ const std::string& Engine::GetSharedDataPath() const {
} }
int Engine::GetAudioHardwareSampleRate() { int Engine::GetAudioHardwareSampleRate() {
return audio_driver_->GetHardwareSampleRate(); return audio_mixer_->GetHardwareSampleRate();
} }
bool Engine::IsMobile() const { bool Engine::IsMobile() const {
return platform_->mobile_device(); return platform_->mobile_device();
} }
void Engine::OnWindowCreated() {
renderer_->Initialize(platform_);
}
void Engine::OnWindowDestroyed() {
renderer_->Shutdown();
}
void Engine::OnWindowResized(int width, int height) {
if (width != renderer_->screen_width() ||
height != renderer_->screen_height()) {
renderer_->Shutdown();
renderer_->Initialize(platform_);
}
}
void Engine::LostFocus() {
audio_mixer_->Suspend();
if (game_)
game_->LostFocus();
}
void Engine::GainedFocus(bool from_interstitial_ad) {
timer_.Reset();
audio_mixer_->Resume();
if (game_)
game_->GainedFocus(from_interstitial_ad);
}
void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
if (replaying_)
return;
event->SetVector(ToPosition(event->GetVector()) * Vector2f(1, -1));
switch (event->GetType()) {
case InputEvent::kDragEnd:
if (((GetScreenSize() / 2) * 0.9f - event->GetVector()).Length() <=
0.25f) {
SetSatsVisible(!stats_->IsVisible());
// TODO: Enqueue DragCancel so we can consume this event.
}
break;
case InputEvent::kKeyPress:
if (event->GetKeyPress() == 's') {
SetSatsVisible(!stats_->IsVisible());
// Consume event.
return;
}
break;
case InputEvent::kDrag:
if (stats_->IsVisible()) {
if ((stats_->GetPosition() - event->GetVector()).Length() <=
stats_->GetSize().y)
stats_->SetPosition(event->GetVector());
// TODO: Enqueue DragCancel so we can consume this event.
}
break;
default:
break;
}
input_queue_.push_back(std::move(event));
}
void Engine::CreateTextureCompressors() {
tex_comp_alpha_.reset();
tex_comp_opaque_.reset();
if (renderer_->SupportsDXT5()) {
tex_comp_alpha_ = TextureCompressor::Create(TextureCompressor::kFormatDXT5);
} else if (renderer_->SupportsATC()) {
tex_comp_alpha_ =
TextureCompressor::Create(TextureCompressor::kFormatATCIA);
}
if (renderer_->SupportsDXT1()) {
tex_comp_opaque_ =
TextureCompressor::Create(TextureCompressor::kFormatDXT1);
} else if (renderer_->SupportsATC()) {
tex_comp_opaque_ = TextureCompressor::Create(TextureCompressor::kFormatATC);
} else if (renderer_->SupportsETC1()) {
tex_comp_opaque_ =
TextureCompressor::Create(TextureCompressor::kFormatETC1);
}
}
void Engine::ContextLost() { void Engine::ContextLost() {
CreateRenderResources(); quad_->SetRenderer(renderer_.get());
pass_through_shader_->SetRenderer(renderer_.get());
solid_shader_->SetRenderer(renderer_.get());
for (auto& t : textures_) {
t.second.texture->Destroy();
RefreshImage(t.first);
}
for (auto& s : shaders_) {
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);
}
game_->ContextLost();
}
bool Engine::CreateRenderResources() {
// This creates a normalized unit sized quad. // This creates a normalized unit sized quad.
static const char vertex_description[] = "p2f;t2f"; static const char vertex_description[] = "p2f;t2f";
static const float vertices[] = { static const float vertices[] = {
@ -521,23 +586,37 @@ bool Engine::CreateRenderResources() {
// Create the shader we can reuse for texture rendering. // Create the shader we can reuse for texture rendering.
auto source = std::make_unique<ShaderSource>(); auto source = std::make_unique<ShaderSource>();
if (!source->Load("engine/pass_through.glsl")) { if (source->Load("engine/pass_through.glsl")) {
LOG << "Could not create pass through shader.";
return false;
}
pass_through_shader_->Create(std::move(source), quad_->vertex_description(), pass_through_shader_->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false); quad_->primitive(), false);
} else {
LOG << "Could not create pass through shader.";
}
// Create the shader we can reuse for solid rendering. // Create the shader we can reuse for solid rendering.
source = std::make_unique<ShaderSource>(); source = std::make_unique<ShaderSource>();
if (!source->Load("engine/solid.glsl")) { if (source->Load("engine/solid.glsl")) {
LOG << "Could not create solid shader.";
return false;
}
solid_shader_->Create(std::move(source), quad_->vertex_description(), solid_shader_->Create(std::move(source), quad_->vertex_description(),
quad_->primitive(), false); quad_->primitive(), false);
} else {
LOG << "Could not create solid shader.";
}
return true; for (auto& t : textures_) {
t.second.texture->SetRenderer(renderer_.get());
RefreshImage(t.first);
}
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);
}
if (game_)
game_->ContextLost();
} }
void Engine::SetSatsVisible(bool visible) { void Engine::SetSatsVisible(bool visible) {
@ -545,7 +624,7 @@ void Engine::SetSatsVisible(bool visible) {
if (visible) if (visible)
stats_->Create("stats_tex"); stats_->Create("stats_tex");
else else
stats_->Destory(); stats_->Destroy();
} }
std::unique_ptr<Image> Engine::PrintStats() { std::unique_ptr<Image> Engine::PrintStats() {

View File

@ -8,15 +8,17 @@
#include <unordered_map> #include <unordered_map>
#include "base/random.h" #include "base/random.h"
#include "base/thread_pool.h"
#include "base/timer.h"
#include "base/vecmath.h" #include "base/vecmath.h"
#include "engine/persistent_data.h" #include "engine/persistent_data.h"
#include "engine/platform/platform_observer.h"
class TextureCompressor; class TextureCompressor;
namespace eng { namespace eng {
class Animator; class Animator;
class AudioDriver;
class AudioMixer; class AudioMixer;
class Font; class Font;
class Game; class Game;
@ -30,24 +32,16 @@ class Shader;
class Texture; class Texture;
class Platform; class Platform;
class Engine { class Engine : public PlatformObserver {
public: public:
using CreateImageCB = std::function<std::unique_ptr<Image>()>; using CreateImageCB = std::function<std::unique_ptr<Image>()>;
Engine(Platform* platform, Renderer* renderer, AudioDriver* audio_driver); Engine(Platform* platform);
~Engine(); ~Engine();
static Engine& Get(); static Engine& Get();
bool Initialize(); void Run();
void Shutdown();
void Update(float delta_time);
void Draw(float frame_frac);
void LostFocus();
void GainedFocus(bool from_interstitial_ad);
void AddDrawable(Drawable* drawable); void AddDrawable(Drawable* drawable);
void RemoveDrawable(Drawable* drawable); void RemoveDrawable(Drawable* drawable);
@ -55,6 +49,8 @@ class Engine {
void AddAnimator(Animator* animator); void AddAnimator(Animator* animator);
void RemoveAnimator(Animator* animator); void RemoveAnimator(Animator* animator);
void CreateRenderer(bool vulkan);
void Exit(); void Exit();
// Convert size from pixels to viewport scale. // Convert size from pixels to viewport scale.
@ -65,7 +61,7 @@ class Engine {
template <typename T> template <typename T>
std::unique_ptr<T> CreateRenderResource() { std::unique_ptr<T> CreateRenderResource() {
return std::unique_ptr<T>(static_cast<T*>(new T(renderer_))); return std::unique_ptr<T>(static_cast<T*>(new T(renderer_.get())));
} }
void SetImageSource(const std::string& asset_name, void SetImageSource(const std::string& asset_name,
@ -85,7 +81,6 @@ class Engine {
Shader* GetCustomShader(const std::string& asset_name); Shader* GetCustomShader(const std::string& asset_name);
void RemoveCustomShader(const std::string& asset_name); void RemoveCustomShader(const std::string& asset_name);
void AddInputEvent(std::unique_ptr<InputEvent> event);
std::unique_ptr<InputEvent> GetNextInputEvent(); std::unique_ptr<InputEvent> GetNextInputEvent();
void StartRecording(const Json::Value& payload); void StartRecording(const Json::Value& payload);
@ -173,9 +168,8 @@ class Engine {
static Engine* singleton; static Engine* singleton;
Platform* platform_ = nullptr; Platform* platform_ = nullptr;
Renderer* renderer_ = nullptr;
AudioDriver* audio_driver_ = nullptr;
std::unique_ptr<Renderer> renderer_;
std::unique_ptr<AudioMixer> audio_mixer_; std::unique_ptr<AudioMixer> audio_mixer_;
std::unique_ptr<Game> game_; std::unique_ptr<Game> game_;
@ -220,11 +214,26 @@ class Engine {
bool replaying_ = false; bool replaying_ = false;
unsigned int replay_index_ = 0; unsigned int replay_index_ = 0;
base::ThreadPool thread_pool_;
base::Timer timer_;
base::Randomf random_; base::Randomf random_;
void ContextLost(); void Initialize();
bool CreateRenderResources(); void Update(float delta_time);
void Draw(float frame_frac);
// PlatformObserver implementation
void OnWindowCreated() final;
void OnWindowDestroyed() final;
void OnWindowResized(int width, int height) final;
void LostFocus() final;
void GainedFocus(bool from_interstitial_ad) final;
void AddInputEvent(std::unique_ptr<InputEvent> event) final;
void CreateTextureCompressors();
void ContextLost();
void SetSatsVisible(bool visible); void SetSatsVisible(bool visible);
std::unique_ptr<Image> PrintStats(); std::unique_ptr<Image> PrintStats();

View File

@ -13,7 +13,7 @@ namespace eng {
ImageQuad::ImageQuad() = default; ImageQuad::ImageQuad() = default;
ImageQuad::~ImageQuad() { ImageQuad::~ImageQuad() {
Destory(); Destroy();
} }
void ImageQuad::Create(const std::string& asset_name, void ImageQuad::Create(const std::string& asset_name,
@ -32,7 +32,8 @@ void ImageQuad::Create(const std::string& asset_name,
asset_name_ = asset_name; asset_name_ = asset_name;
} }
void ImageQuad::Destory() { // TODO: typo
void ImageQuad::Destroy() {
if (texture_) { if (texture_) {
Engine::Get().ReleaseTexture(asset_name_); Engine::Get().ReleaseTexture(asset_name_);
texture_ = nullptr; texture_ = nullptr;

View File

@ -24,7 +24,7 @@ class ImageQuad final : public Animatable {
int frame_width = 0, int frame_width = 0,
int frame_height = 0); int frame_height = 0);
void Destory(); void Destroy();
void AutoScale(); void AutoScale();

View File

@ -27,6 +27,8 @@ class InputEvent {
InputEvent(Type type, char key) : type_(type), key_(key) {} InputEvent(Type type, char key) : type_(type), key_(key) {}
~InputEvent() = default; ~InputEvent() = default;
void SetVector(base::Vector2f vec) { vec_ = vec; }
Type GetType() const { return type_; } Type GetType() const { return type_; }
size_t GetPointerId() const { return pointer_id_; } size_t GetPointerId() const { return pointer_id_; }

View File

@ -1,94 +0,0 @@
#include "engine/platform/platform.h"
#include "base/log.h"
#include "base/task_runner.h"
#include "engine/engine.h"
#include "engine/renderer/opengl/renderer_opengl.h"
#include "engine/renderer/vulkan/renderer_vulkan.h"
#if defined(__ANDROID__)
#include "engine/audio/audio_driver_oboe.h"
#elif defined(__linux__)
#include "engine/audio/audio_driver_alsa.h"
#endif
using namespace base;
namespace eng {
Platform::Platform() = default;
Platform::~Platform() = default;
void Platform::InitializeCommon() {
LOG << "Initializing platform.";
thread_pool_.Initialize();
TaskRunner::CreateThreadLocalTaskRunner();
#if defined(__ANDROID__)
audio_driver_ = std::make_unique<AudioDriverOboe>();
#elif defined(__linux__)
audio_driver_ = std::make_unique<AudioDriverAlsa>();
#endif
bool res = audio_driver_->Initialize();
CHECK(res) << "Failed to initialize audio driver.";
auto context = std::make_unique<VulkanContext>();
if (context->Initialize()) {
renderer_ = std::make_unique<RendererVulkan>(std::move(context));
} else {
LOG << "Failed to initialize Vulkan context. Fallback to OpenGL.";
renderer_ = std::make_unique<RendererOpenGL>();
}
}
void Platform::ShutdownCommon() {
LOG << "Shutting down platform.";
audio_driver_->Shutdown();
renderer_->Shutdown();
}
void Platform::RunMainLoop() {
engine_ =
std::make_unique<Engine>(this, renderer_.get(), audio_driver_.get());
bool res = engine_->Initialize();
CHECK(res) << "Failed to initialize the engine.";
// Use fixed time steps.
float time_step = engine_->time_step();
timer_.Reset();
float accumulator = 0.0;
float frame_frac = 0.0f;
for (;;) {
engine_->Draw(frame_frac);
// Accumulate time.
timer_.Update();
accumulator += timer_.GetSecondsPassed();
// Subdivide the frame time.
while (accumulator >= time_step) {
TaskRunner::GetThreadLocalTaskRunner()->SingleConsumerRun();
Update();
engine_->Update(time_step);
if (should_exit_) {
thread_pool_.Shutdown();
engine_->Shutdown();
engine_.reset();
return;
}
accumulator -= time_step;
};
// Calculate frame fraction from remainder of the frame time.
frame_frac = accumulator / time_step;
}
}
} // namespace eng

View File

@ -1,12 +1,8 @@
#ifndef ENGINE_PLATFORM_PLATFORM_H #ifndef ENGINE_PLATFORM_PLATFORM_H
#define ENGINE_PLATFORM_PLATFORM_H #define ENGINE_PLATFORM_PLATFORM_H
#include <memory>
#include <string> #include <string>
#include "base/thread_pool.h"
#include "base/timer.h"
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "../../base/vecmath.h" #include "../../base/vecmath.h"
@ -24,27 +20,23 @@ struct ANativeWindow;
namespace eng { namespace eng {
class AudioDriver; class PlatformObserver;
class Renderer;
class Engine;
class Platform { class Platform {
public: public:
Platform();
~Platform();
#if defined(__ANDROID__) #if defined(__ANDROID__)
void Initialize(android_app* app); Platform(android_app* app);
#elif defined(__linux__) #elif defined(__linux__)
void Initialize(); Platform();
#endif #endif
~Platform();
void Shutdown();
void Update(); void Update();
void Exit(); void Exit();
void SetObserver(PlatformObserver* observer) { observer_ = observer; }
void Vibrate(int duration); void Vibrate(int duration);
void ShowInterstitialAd(); void ShowInterstitialAd();
@ -53,8 +45,6 @@ class Platform {
void SetKeepScreenOn(bool keep_screen_on); void SetKeepScreenOn(bool keep_screen_on);
void RunMainLoop();
int GetDeviceDpi() const { return device_dpi_; } int GetDeviceDpi() const { return device_dpi_; }
const std::string& GetRootPath() const { return root_path_; } const std::string& GetRootPath() const { return root_path_; }
@ -65,9 +55,16 @@ class Platform {
bool mobile_device() const { return mobile_device_; } bool mobile_device() const { return mobile_device_; }
protected: bool should_exit() const { return should_exit_; }
base::Timer timer_;
#if defined(__ANDROID__)
ANativeWindow* GetWindow();
#elif defined(__linux__)
Display* GetDisplay();
Window GetWindow();
#endif
private:
bool mobile_device_ = false; bool mobile_device_ = false;
int device_dpi_ = 100; int device_dpi_ = 100;
std::string root_path_; std::string root_path_;
@ -77,11 +74,7 @@ class Platform {
bool has_focus_ = false; bool has_focus_ = false;
bool should_exit_ = false; bool should_exit_ = false;
std::unique_ptr<AudioDriver> audio_driver_; PlatformObserver* observer_ = nullptr;
std::unique_ptr<Renderer> renderer_;
std::unique_ptr<Engine> engine_;
base::ThreadPool thread_pool_;
#if defined(__ANDROID__) #if defined(__ANDROID__)
@ -116,10 +109,9 @@ class Platform {
bool CreateWindow(int width, int height); bool CreateWindow(int width, int height);
void DestroyWindow(); void DestroyWindow();
#endif XVisualInfo* GetXVisualInfo(Display* display);
void InitializeCommon(); #endif
void ShutdownCommon();
Platform(const Platform&) = delete; Platform(const Platform&) = delete;
Platform& operator=(const Platform&) = delete; Platform& operator=(const Platform&) = delete;

View File

@ -1,15 +1,15 @@
#include "engine/platform/platform.h" #include "engine/platform/platform.h"
#include <memory>
#include <android_native_app_glue.h> #include <android_native_app_glue.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <jni.h> #include <jni.h>
#include <unistd.h> #include <unistd.h>
#include "base/log.h" #include "base/log.h"
#include "base/task_runner.h"
#include "engine/engine.h"
#include "engine/input_event.h" #include "engine/input_event.h"
#include "engine/renderer/renderer.h" #include "engine/platform/platform_observer.h"
using namespace base; using namespace base;
@ -201,10 +201,12 @@ int32_t GetDensityDpi(android_app* app) {
namespace eng { namespace eng {
void KaliberMain(Platform* platform);
int32_t Platform::HandleInput(android_app* app, AInputEvent* event) { int32_t Platform::HandleInput(android_app* app, AInputEvent* event) {
Platform* platform = reinterpret_cast<Platform*>(app->userData); Platform* platform = reinterpret_cast<Platform*>(app->userData);
if (!platform->engine_) if (!platform->observer_)
return 0; return 0;
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY && if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY &&
@ -212,7 +214,7 @@ int32_t Platform::HandleInput(android_app* app, AInputEvent* event) {
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) { if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) {
auto input_event = auto input_event =
std::make_unique<InputEvent>(InputEvent::kNavigateBack); std::make_unique<InputEvent>(InputEvent::kNavigateBack);
platform->engine_->AddInputEvent(std::move(input_event)); platform->observer_->AddInputEvent(std::move(input_event));
} }
return 1; return 1;
} else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
@ -227,7 +229,6 @@ int32_t Platform::HandleInput(android_app* app, AInputEvent* event) {
int32_t id = AMotionEvent_getPointerId(event, i); int32_t id = AMotionEvent_getPointerId(event, i);
if (id < 2) { if (id < 2) {
pos[id] = {AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)}; pos[id] = {AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)};
pos[id] = platform->engine_->ToPosition(pos[id]);
} }
} }
@ -239,34 +240,30 @@ int32_t Platform::HandleInput(android_app* app, AInputEvent* event) {
switch (flags) { switch (flags) {
case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_DOWN:
// DLOG << "AMOTION_EVENT_ACTION_DOWN - pointer_id: " << pointer_id;
platform->pointer_pos_[pointer_id] = pos[pointer_id]; platform->pointer_pos_[pointer_id] = pos[pointer_id];
platform->pointer_down_[pointer_id] = true; platform->pointer_down_[pointer_id] = true;
input_event = input_event = std::make_unique<InputEvent>(InputEvent::kDragStart,
std::make_unique<InputEvent>(InputEvent::kDragStart, pointer_id, pointer_id, pos[pointer_id]);
pos[pointer_id] * Vector2f(1, -1));
break; break;
case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_POINTER_UP:
// DLOG << "AMOTION_EVENT_ACTION_UP - pointer_id: " << pointer_id;
platform->pointer_pos_[pointer_id] = pos[pointer_id]; platform->pointer_pos_[pointer_id] = pos[pointer_id];
platform->pointer_down_[pointer_id] = false; platform->pointer_down_[pointer_id] = false;
input_event = input_event = std::make_unique<InputEvent>(InputEvent::kDragEnd,
std::make_unique<InputEvent>(InputEvent::kDragEnd, pointer_id, pointer_id, pos[pointer_id]);
pos[pointer_id] * Vector2f(1, -1));
break; break;
case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_MOVE:
if (platform->pointer_down_[0] && pos[0] != platform->pointer_pos_[0]) { if (platform->pointer_down_[0] && pos[0] != platform->pointer_pos_[0]) {
platform->pointer_pos_[0] = pos[0]; platform->pointer_pos_[0] = pos[0];
input_event = std::make_unique<InputEvent>(InputEvent::kDrag, 0, input_event =
pos[0] * Vector2f(1, -1)); std::make_unique<InputEvent>(InputEvent::kDrag, 0, pos[0]);
} }
if (platform->pointer_down_[1] && pos[1] != platform->pointer_pos_[1]) { if (platform->pointer_down_[1] && pos[1] != platform->pointer_pos_[1]) {
platform->pointer_pos_[1] = pos[1]; platform->pointer_pos_[1] = pos[1];
input_event = std::make_unique<InputEvent>(InputEvent::kDrag, 1, input_event =
pos[1] * Vector2f(1, -1)); std::make_unique<InputEvent>(InputEvent::kDrag, 1, pos[1]);
} }
break; break;
@ -276,7 +273,7 @@ int32_t Platform::HandleInput(android_app* app, AInputEvent* event) {
} }
if (input_event) { if (input_event) {
platform->engine_->AddInputEvent(std::move(input_event)); platform->observer_->AddInputEvent(std::move(input_event));
return 1; return 1;
} }
} }
@ -295,30 +292,23 @@ void Platform::HandleCmd(android_app* app, int32_t cmd) {
DLOG << "APP_CMD_INIT_WINDOW"; DLOG << "APP_CMD_INIT_WINDOW";
if (app->window != NULL) { if (app->window != NULL) {
platform->SetFrameRate(60); platform->SetFrameRate(60);
bool res = platform->renderer_->Initialize(app->window); if (platform->observer_)
CHECK(res) << "Failed to initialize " platform->observer_->OnWindowCreated();
<< platform->renderer_->GetDebugName() << " renderer.";
} }
break; break;
case APP_CMD_TERM_WINDOW: case APP_CMD_TERM_WINDOW:
DLOG << "APP_CMD_TERM_WINDOW"; DLOG << "APP_CMD_TERM_WINDOW";
platform->renderer_->Shutdown(); if (platform->observer_)
platform->observer_->OnWindowDestroyed();
break; break;
case APP_CMD_CONFIG_CHANGED: case APP_CMD_CONFIG_CHANGED:
DLOG << "APP_CMD_CONFIG_CHANGED"; DLOG << "APP_CMD_CONFIG_CHANGED";
if (platform->app_->window != NULL) { if (platform->app_->window != NULL && platform->observer_)
int width = platform->renderer_->screen_width(); platform->observer_->OnWindowResized(
int height = platform->renderer_->screen_height(); ANativeWindow_getWidth(app->window),
if (width != ANativeWindow_getWidth(app->window) || ANativeWindow_getHeight(app->window));
height != ANativeWindow_getHeight(app->window)) {
platform->renderer_->Shutdown();
bool res = platform->renderer_->Initialize(platform->app_->window);
CHECK(res) << "Failed to initialize "
<< platform->renderer_->GetDebugName() << " renderer.";
}
}
break; break;
case APP_CMD_STOP: case APP_CMD_STOP:
@ -327,18 +317,18 @@ void Platform::HandleCmd(android_app* app, int32_t cmd) {
case APP_CMD_GAINED_FOCUS: case APP_CMD_GAINED_FOCUS:
DLOG << "APP_CMD_GAINED_FOCUS"; DLOG << "APP_CMD_GAINED_FOCUS";
platform->timer_.Reset(); // platform->timer_.Reset();
platform->has_focus_ = true; platform->has_focus_ = true;
if (platform->engine_) if (platform->observer_)
platform->engine_->GainedFocus(g_showing_interstitial_ad); platform->observer_->GainedFocus(g_showing_interstitial_ad);
g_showing_interstitial_ad = false; g_showing_interstitial_ad = false;
break; break;
case APP_CMD_LOST_FOCUS: case APP_CMD_LOST_FOCUS:
DLOG << "APP_CMD_LOST_FOCUS"; DLOG << "APP_CMD_LOST_FOCUS";
platform->has_focus_ = false; platform->has_focus_ = false;
if (platform->engine_) if (platform->observer_)
platform->engine_->LostFocus(); platform->observer_->LostFocus();
break; break;
case APP_CMD_LOW_MEMORY: case APP_CMD_LOW_MEMORY:
@ -347,11 +337,10 @@ void Platform::HandleCmd(android_app* app, int32_t cmd) {
} }
} }
void Platform::Initialize(android_app* app) { Platform::Platform(android_app* app) {
Platform::InitializeCommon(); LOG << "Initializing platform.";
app_ = app; app_ = app;
mobile_device_ = true; mobile_device_ = true;
root_path_ = ::GetApkPath(app->activity); root_path_ = ::GetApkPath(app->activity);
@ -385,8 +374,8 @@ void Platform::Initialize(android_app* app) {
Update(); Update();
} }
void Platform::Shutdown() { Platform::~Platform() {
Platform::ShutdownCommon(); LOG << "Shutting down platform.";
} }
void Platform::Update() { void Platform::Update() {
@ -439,12 +428,14 @@ void Platform::SetFrameRate(float frame_rate) {
} }
} }
ANativeWindow* Platform::GetWindow() {
return app_->window;
}
} // namespace eng } // namespace eng
void android_main(android_app* app) { void android_main(android_app* app) {
eng::Platform platform; eng::Platform platform(app);
platform.Initialize(app); eng::KaliberMain(&platform);
platform.RunMainLoop();
platform.Shutdown();
_exit(0); _exit(0);
} }

View File

@ -1,18 +1,20 @@
#include "engine/platform/platform.h" #include "engine/platform/platform.h"
#include <memory>
#include "base/log.h" #include "base/log.h"
#include "base/task_runner.h"
#include "base/vecmath.h" #include "base/vecmath.h"
#include "engine/engine.h"
#include "engine/input_event.h" #include "engine/input_event.h"
#include "engine/renderer/renderer.h" #include "engine/platform/platform_observer.h"
using namespace base; using namespace base;
namespace eng { namespace eng {
void Platform::Initialize() { void KaliberMain(Platform* platform);
Platform::InitializeCommon();
Platform::Platform() {
LOG << "Initializing platform.";
root_path_ = "../../"; root_path_ = "../../";
LOG << "Root path: " << root_path_.c_str(); LOG << "Root path: " << root_path_.c_str();
@ -26,10 +28,6 @@ void Platform::Initialize() {
bool res = CreateWindow(800, 1205); bool res = CreateWindow(800, 1205);
CHECK(res) << "Failed to create window."; CHECK(res) << "Failed to create window.";
res = renderer_->Initialize(display_, window_);
CHECK(res) << "Failed to initialize " << renderer_->GetDebugName()
<< " renderer.";
XSelectInput(display_, window_, XSelectInput(display_, window_,
KeyPressMask | Button1MotionMask | ButtonPressMask | KeyPressMask | Button1MotionMask | ButtonPressMask |
ButtonReleaseMask | FocusChangeMask); ButtonReleaseMask | FocusChangeMask);
@ -37,9 +35,8 @@ void Platform::Initialize() {
XSetWMProtocols(display_, window_, &WM_DELETE_WINDOW, 1); XSetWMProtocols(display_, window_, &WM_DELETE_WINDOW, 1);
} }
void Platform::Shutdown() { Platform::~Platform() {
Platform::ShutdownCommon(); LOG << "Shutting down platform.";
DestroyWindow(); DestroyWindow();
} }
@ -52,47 +49,41 @@ void Platform::Update() {
KeySym key = XLookupKeysym(&e.xkey, 0); KeySym key = XLookupKeysym(&e.xkey, 0);
auto input_event = auto input_event =
std::make_unique<InputEvent>(InputEvent::kKeyPress, (char)key); std::make_unique<InputEvent>(InputEvent::kKeyPress, (char)key);
engine_->AddInputEvent(std::move(input_event)); observer_->AddInputEvent(std::move(input_event));
// TODO: e.xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)) // TODO: e.xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask))
break; break;
} }
case MotionNotify: { case MotionNotify: {
Vector2f v(e.xmotion.x, e.xmotion.y); Vector2f v(e.xmotion.x, e.xmotion.y);
v = engine_->ToPosition(v); auto input_event =
// DLOG << "drag: " << v; std::make_unique<InputEvent>(InputEvent::kDrag, 0, v);
auto input_event = std::make_unique<InputEvent>(InputEvent::kDrag, 0, observer_->AddInputEvent(std::move(input_event));
v * Vector2f(1, -1));
engine_->AddInputEvent(std::move(input_event));
break; break;
} }
case ButtonPress: { case ButtonPress: {
if (e.xbutton.button == 1) { if (e.xbutton.button == 1) {
Vector2f v(e.xbutton.x, e.xbutton.y); Vector2f v(e.xbutton.x, e.xbutton.y);
v = engine_->ToPosition(v); auto input_event =
// DLOG << "drag-start: " << v; std::make_unique<InputEvent>(InputEvent::kDragStart, 0, v);
auto input_event = std::make_unique<InputEvent>( observer_->AddInputEvent(std::move(input_event));
InputEvent::kDragStart, 0, v * Vector2f(1, -1));
engine_->AddInputEvent(std::move(input_event));
} }
break; break;
} }
case ButtonRelease: { case ButtonRelease: {
if (e.xbutton.button == 1) { if (e.xbutton.button == 1) {
Vector2f v(e.xbutton.x, e.xbutton.y); Vector2f v(e.xbutton.x, e.xbutton.y);
v = engine_->ToPosition(v); auto input_event =
// DLOG << "drag-end!"; std::make_unique<InputEvent>(InputEvent::kDragEnd, 0, v);
auto input_event = std::make_unique<InputEvent>( observer_->AddInputEvent(std::move(input_event));
InputEvent::kDragEnd, 0, v * Vector2f(1, -1));
engine_->AddInputEvent(std::move(input_event));
} }
break; break;
} }
case FocusOut: { case FocusOut: {
engine_->LostFocus(); observer_->LostFocus();
break; break;
} }
case FocusIn: { case FocusIn: {
engine_->GainedFocus(false); observer_->GainedFocus(false);
break; break;
} }
case ClientMessage: { case ClientMessage: {
@ -127,7 +118,7 @@ bool Platform::CreateWindow(int width, int height) {
Window root_window = DefaultRootWindow(display_); Window root_window = DefaultRootWindow(display_);
XVisualInfo* visual_info = renderer_->GetXVisualInfo(display_); XVisualInfo* visual_info = GetXVisualInfo(display_);
if (!visual_info) { if (!visual_info) {
LOG << "No appropriate visual found."; LOG << "No appropriate visual found.";
return false; return false;
@ -151,18 +142,35 @@ bool Platform::CreateWindow(int width, int height) {
void Platform::DestroyWindow() { void Platform::DestroyWindow() {
if (display_) { if (display_) {
XDestroyWindow(display_, window_); XDestroyWindow(display_, window_);
#if 0 // TODO: Figure out why XCloseDisplay is crashing
XCloseDisplay(display_); XCloseDisplay(display_);
#endif
display_ = nullptr; display_ = nullptr;
window_ = 0; window_ = 0;
} }
} }
Display* Platform::GetDisplay() {
return display_;
}
Window Platform::GetWindow() {
return window_;
}
XVisualInfo* Platform::GetXVisualInfo(Display* display) {
long visual_mask = VisualScreenMask;
int num_visuals;
XVisualInfo visual_info_template = {};
visual_info_template.screen = DefaultScreen(display);
return XGetVisualInfo(display, visual_mask, &visual_info_template,
&num_visuals);
}
} // namespace eng } // namespace eng
int main(int argc, char** argv) { int main(int argc, char** argv) {
eng::Platform platform; eng::Platform platform;
platform.Initialize(); eng::KaliberMain(&platform);
platform.RunMainLoop();
platform.Shutdown();
return 0; return 0;
} }

View File

@ -0,0 +1,23 @@
#ifndef ENGINE_PLATFORM_PLATFORM_OBSERVER_H
#define ENGINE_PLATFORM_PLATFORM_OBSERVER_H
namespace eng {
class InputEvent;
class PlatformObserver {
public:
PlatformObserver() = default;
virtual ~PlatformObserver() = default;
virtual void OnWindowCreated() = 0;
virtual void OnWindowDestroyed() = 0;
virtual void OnWindowResized(int width, int height) = 0;
virtual void LostFocus() = 0;
virtual void GainedFocus(bool from_interstitial_ad) = 0;
virtual void AddInputEvent(std::unique_ptr<InputEvent> event) = 0;
};
} // namespace eng
#endif // ENGINE_PLATFORM_PLATFORM_OBSERVER_H

View File

@ -40,13 +40,35 @@ const std::string kAttributeNames[eng::kAttribType_Max] = {
namespace eng { namespace eng {
#ifdef THREADED_RENDERING #ifdef THREADED_RENDERING
RendererOpenGL::RendererOpenGL() RendererOpenGL::RendererOpenGL(base::Closure context_lost_cb)
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {} : Renderer(context_lost_cb),
main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {}
#else #else
RendererOpenGL::RendererOpenGL() = default; RendererOpenGL::RendererOpenGL() = default;
#endif // THREADED_RENDERING #endif // THREADED_RENDERING
RendererOpenGL::~RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() {
Shutdown();
OnDestroy();
}
void RendererOpenGL::Shutdown() {
#ifdef THREADED_RENDERING
if (terminate_render_thread_)
return;
LOG << "Shutting down renderer.";
{
std::unique_lock<std::mutex> scoped_lock(mutex_);
terminate_render_thread_ = true;
}
cv_.notify_one();
LOG << "Terminating render thread";
render_thread_.join();
#else
ShutdownInternal();
#endif // THREADED_RENDERING
}
uint64_t RendererOpenGL::CreateGeometry(std::unique_ptr<Mesh> mesh) { uint64_t RendererOpenGL::CreateGeometry(std::unique_ptr<Mesh> mesh) {
auto cmd = std::make_unique<CmdCreateGeometry>(); auto cmd = std::make_unique<CmdCreateGeometry>();
@ -348,24 +370,6 @@ bool RendererOpenGL::StartRenderThread() {
#endif // THREADED_RENDERING #endif // THREADED_RENDERING
} }
void RendererOpenGL::TerminateRenderThread() {
#ifdef THREADED_RENDERING
if (terminate_render_thread_)
return;
// Notify worker thread and wait for it to terminate.
{
std::unique_lock<std::mutex> scoped_lock(mutex_);
terminate_render_thread_ = true;
}
cv_.notify_one();
LOG << "Terminating render thread";
render_thread_.join();
#else
ShutdownInternal();
#endif // THREADED_RENDERING
}
#ifdef THREADED_RENDERING #ifdef THREADED_RENDERING
void RendererOpenGL::RenderThreadMain(std::promise<bool> promise) { void RendererOpenGL::RenderThreadMain(std::promise<bool> promise) {

View File

@ -37,15 +37,10 @@ struct RenderCommand;
class RendererOpenGL final : public Renderer { class RendererOpenGL final : public Renderer {
public: public:
RendererOpenGL(); RendererOpenGL(base::Closure context_lost_cb);
~RendererOpenGL() final; ~RendererOpenGL() final;
#if defined(__ANDROID__) virtual bool Initialize(Platform* platform) final;
bool Initialize(ANativeWindow* window) final;
#elif defined(__linux__)
bool Initialize(Display* display, Window window) final;
#endif
void Shutdown() final; void Shutdown() final;
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final; uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
@ -89,10 +84,6 @@ class RendererOpenGL final : public Renderer {
const char* GetDebugName() final { return "OpenGL"; } const char* GetDebugName() final { return "OpenGL"; }
#if defined(__linux__) && !defined(__ANDROID__)
XVisualInfo* GetXVisualInfo(Display* display) final;
#endif
private: private:
struct GeometryOpenGL { struct GeometryOpenGL {
struct Element { struct Element {
@ -165,12 +156,13 @@ class RendererOpenGL final : public Renderer {
bool InitCommon(); bool InitCommon();
void ShutdownInternal(); void ShutdownInternal();
void OnDestroy();
void ContextLost(); void ContextLost();
void DestroyAllResources(); void DestroyAllResources();
bool StartRenderThread(); bool StartRenderThread();
void TerminateRenderThread();
#ifdef THREADED_RENDERING #ifdef THREADED_RENDERING
void RenderThreadMain(std::promise<bool> promise); void RenderThreadMain(std::promise<bool> promise);

View File

@ -3,20 +3,20 @@
#include <android/native_window.h> #include <android/native_window.h>
#include "base/log.h" #include "base/log.h"
#include "engine/platform/platform.h"
#include "third_party/android/GLContext.h" #include "third_party/android/GLContext.h"
namespace eng { namespace eng {
bool RendererOpenGL::Initialize(ANativeWindow* window) { bool RendererOpenGL::Initialize(Platform* platform) {
LOG << "Initializing renderer."; LOG << "Initializing renderer.";
window_ = window; window_ = platform->GetWindow();
return StartRenderThread(); return StartRenderThread();
} }
void RendererOpenGL::Shutdown() { void RendererOpenGL::OnDestroy() {
LOG << "Shutting down renderer."; ndk_helper::GLContext::GetInstance()->Invalidate();
TerminateRenderThread();
} }
bool RendererOpenGL::InitInternal() { bool RendererOpenGL::InitInternal() {

View File

@ -1,14 +1,15 @@
#include "engine/renderer/opengl/renderer_opengl.h" #include "engine/renderer/opengl/renderer_opengl.h"
#include "base/log.h" #include "base/log.h"
#include "engine/platform/platform.h"
namespace eng { namespace eng {
bool RendererOpenGL::Initialize(Display* display, Window window) { bool RendererOpenGL::Initialize(Platform* platform) {
LOG << "Initializing renderer."; LOG << "Initializing renderer.";
display_ = display; display_ = platform->GetDisplay();
window_ = window; window_ = platform->GetWindow();
XWindowAttributes xwa; XWindowAttributes xwa;
XGetWindowAttributes(display_, window_, &xwa); XGetWindowAttributes(display_, window_, &xwa);
@ -18,16 +19,13 @@ bool RendererOpenGL::Initialize(Display* display, Window window) {
return StartRenderThread(); return StartRenderThread();
} }
void RendererOpenGL::Shutdown() { void RendererOpenGL::OnDestroy() {}
LOG << "Shutting down renderer.";
TerminateRenderThread();
}
bool RendererOpenGL::InitInternal() { bool RendererOpenGL::InitInternal() {
// Create the OpenGL context. GLint glx_attributes[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER,
glx_context_ = None};
glXCreateContext(display_, GetXVisualInfo(display_), NULL, GL_TRUE); XVisualInfo* visual_info = glXChooseVisual(display_, 0, glx_attributes);
glx_context_ = glXCreateContext(display_, visual_info, NULL, GL_TRUE);
if (!glx_context_) { if (!glx_context_) {
LOG << "Couldn't create the glx context."; LOG << "Couldn't create the glx context.";
return false; return false;
@ -63,11 +61,4 @@ void RendererOpenGL::HandleCmdPresent(RenderCommand* cmd) {
} }
} }
XVisualInfo* RendererOpenGL::GetXVisualInfo(Display* display) {
// Look for the right visual to set up the OpenGL context.
GLint glx_attributes[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER,
None};
return glXChooseVisual(display, 0, glx_attributes);
}
} // namespace eng } // namespace eng

View File

@ -15,6 +15,11 @@ class RenderResource {
uint64_t resource_id() { return resource_id_; } uint64_t resource_id() { return resource_id_; }
void SetRenderer(Renderer* renderer) {
renderer_ = renderer;
resource_id_ = 0;
}
protected: protected:
uint64_t resource_id_ = 0; uint64_t resource_id_ = 0;
Renderer* renderer_ = nullptr; Renderer* renderer_ = nullptr;

View File

@ -4,40 +4,26 @@
#include <memory> #include <memory>
#include <string> #include <string>
#if defined(__linux__) && !defined(__ANDROID__)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
#include "base/closure.h" #include "base/closure.h"
#include "base/vecmath.h" #include "base/vecmath.h"
#include "engine/renderer/renderer_types.h" #include "engine/renderer/renderer_types.h"
#if defined(__ANDROID__)
struct ANativeWindow;
#endif
namespace eng { namespace eng {
class Image; class Image;
class ShaderSource; class ShaderSource;
class Mesh; class Mesh;
class Platform;
class Renderer { class Renderer {
public: public:
const unsigned kInvalidId = 0; const unsigned kInvalidId = 0;
Renderer() = default; Renderer(base::Closure context_lost_cb)
: context_lost_cb_{std::move(context_lost_cb)} {}
virtual ~Renderer() = default; virtual ~Renderer() = default;
void SetContextLostCB(base::Closure cb) { context_lost_cb_ = std::move(cb); } virtual bool Initialize(Platform* platform) = 0;
#if defined(__ANDROID__)
virtual bool Initialize(ANativeWindow* window) = 0;
#elif defined(__linux__)
virtual bool Initialize(Display* display, Window window) = 0;
#endif
virtual void Shutdown() = 0; virtual void Shutdown() = 0;
virtual uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) = 0; virtual uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) = 0;
@ -94,10 +80,6 @@ class Renderer {
virtual const char* GetDebugName() = 0; virtual const char* GetDebugName() = 0;
#if defined(__linux__) && !defined(__ANDROID__)
virtual XVisualInfo* GetXVisualInfo(Display* display) = 0;
#endif
protected: protected:
struct TextureCompression { struct TextureCompression {
unsigned etc1 : 1; unsigned etc1 : 1;

View File

@ -370,10 +370,12 @@ std::pair<int, int> GetNumBlocksForImageFormat(VkFormat format,
namespace eng { namespace eng {
RendererVulkan::RendererVulkan(std::unique_ptr<VulkanContext> context) RendererVulkan::RendererVulkan(base::Closure context_lost_cb)
: context_{std::move(context)} {} : Renderer(context_lost_cb) {}
RendererVulkan::~RendererVulkan() = default; RendererVulkan::~RendererVulkan() {
Shutdown();
}
uint64_t RendererVulkan::CreateGeometry(std::unique_ptr<Mesh> mesh) { uint64_t RendererVulkan::CreateGeometry(std::unique_ptr<Mesh> mesh) {
auto& geometry = geometries_[++last_resource_id_] = {}; auto& geometry = geometries_[++last_resource_id_] = {};
@ -707,7 +709,7 @@ uint64_t RendererVulkan::CreateShader(
pipeline_info.pDepthStencilState = &depth_stencil; pipeline_info.pDepthStencilState = &depth_stencil;
pipeline_info.pDynamicState = &dynamic_state_create_info; pipeline_info.pDynamicState = &dynamic_state_create_info;
pipeline_info.layout = shader.pipeline_layout; pipeline_info.layout = shader.pipeline_layout;
pipeline_info.renderPass = context_->GetRenderPass(); pipeline_info.renderPass = context_.GetRenderPass();
pipeline_info.subpass = 0; pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
@ -828,7 +830,7 @@ void RendererVulkan::UploadUniforms(uint64_t resource_id) {
} }
void RendererVulkan::PrepareForDrawing() { void RendererVulkan::PrepareForDrawing() {
context_->PrepareBuffers(); context_.PrepareBuffers();
DrawListBegin(); DrawListBegin();
} }
@ -840,20 +842,20 @@ void RendererVulkan::Present() {
bool RendererVulkan::InitializeInternal() { bool RendererVulkan::InitializeInternal() {
glslang::InitializeProcess(); glslang::InitializeProcess();
device_ = context_->GetDevice(); device_ = context_.GetDevice();
// Allocate one extra frame to ensure it's unused at any time without having // Allocate one extra frame to ensure it's unused at any time without having
// to use a fence. // to use a fence.
int frame_count = context_->GetSwapchainImageCount() + 1; int frame_count = context_.GetSwapchainImageCount() + 1;
frames_.resize(frame_count); frames_.resize(frame_count);
frames_drawn_ = frame_count; frames_drawn_ = frame_count;
// Initialize allocator // Initialize allocator
VmaAllocatorCreateInfo allocator_info; VmaAllocatorCreateInfo allocator_info;
memset(&allocator_info, 0, sizeof(VmaAllocatorCreateInfo)); memset(&allocator_info, 0, sizeof(VmaAllocatorCreateInfo));
allocator_info.physicalDevice = context_->GetPhysicalDevice(); allocator_info.physicalDevice = context_.GetPhysicalDevice();
allocator_info.device = device_; allocator_info.device = device_;
allocator_info.instance = context_->GetInstance(); allocator_info.instance = context_.GetInstance();
vmaCreateAllocator(&allocator_info, &allocator_); vmaCreateAllocator(&allocator_info, &allocator_);
for (size_t i = 0; i < frames_.size(); i++) { for (size_t i = 0; i < frames_.size(); i++) {
@ -861,7 +863,7 @@ bool RendererVulkan::InitializeInternal() {
VkCommandPoolCreateInfo cmd_pool_info; VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = nullptr; cmd_pool_info.pNext = nullptr;
cmd_pool_info.queueFamilyIndex = context_->GetGraphicsQueue(); cmd_pool_info.queueFamilyIndex = context_.GetGraphicsQueue();
cmd_pool_info.flags = 0; cmd_pool_info.flags = 0;
VkResult err = vkCreateCommandPool(device_, &cmd_pool_info, nullptr, VkResult err = vkCreateCommandPool(device_, &cmd_pool_info, nullptr,
@ -1014,8 +1016,8 @@ void RendererVulkan::Shutdown() {
current_staging_buffer_ = 0; current_staging_buffer_ = 0;
staging_buffer_used_ = false; staging_buffer_used_ = false;
context_->DestroyWindow(); context_.DestroyWindow();
context_->Shutdown(); context_.Shutdown();
glslang::FinalizeProcess(); glslang::FinalizeProcess();
} }
@ -1023,8 +1025,8 @@ void RendererVulkan::Shutdown() {
void RendererVulkan::BeginFrame() { void RendererVulkan::BeginFrame() {
FreePendingResources(current_frame_); FreePendingResources(current_frame_);
context_->AppendCommandBuffer(frames_[current_frame_].setup_command_buffer); context_.AppendCommandBuffer(frames_[current_frame_].setup_command_buffer);
context_->AppendCommandBuffer(frames_[current_frame_].draw_command_buffer); context_.AppendCommandBuffer(frames_[current_frame_].draw_command_buffer);
vkResetCommandPool(device_, frames_[current_frame_].setup_command_pool, 0); vkResetCommandPool(device_, frames_[current_frame_].setup_command_pool, 0);
vkResetCommandPool(device_, frames_[current_frame_].draw_command_pool, 0); vkResetCommandPool(device_, frames_[current_frame_].draw_command_pool, 0);
@ -1063,7 +1065,7 @@ void RendererVulkan::BeginFrame() {
void RendererVulkan::FlushSetupBuffer() { void RendererVulkan::FlushSetupBuffer() {
vkEndCommandBuffer(frames_[current_frame_].setup_command_buffer); vkEndCommandBuffer(frames_[current_frame_].setup_command_buffer);
context_->Flush(false); context_.Flush(false);
vkResetCommandPool(device_, frames_[current_frame_].setup_command_pool, 0); vkResetCommandPool(device_, frames_[current_frame_].setup_command_pool, 0);
@ -1079,7 +1081,7 @@ void RendererVulkan::FlushSetupBuffer() {
DLOG << "vkBeginCommandBuffer failed with error " << string_VkResult(err); DLOG << "vkBeginCommandBuffer failed with error " << string_VkResult(err);
return; return;
} }
context_->AppendCommandBuffer(frames_[current_frame_].setup_command_buffer, context_.AppendCommandBuffer(frames_[current_frame_].setup_command_buffer,
true); true);
} }
@ -1907,8 +1909,8 @@ void RendererVulkan::DrawListBegin() {
VkRenderPassBeginInfo render_pass_begin; VkRenderPassBeginInfo render_pass_begin;
render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin.pNext = nullptr; render_pass_begin.pNext = nullptr;
render_pass_begin.renderPass = context_->GetRenderPass(); render_pass_begin.renderPass = context_.GetRenderPass();
render_pass_begin.framebuffer = context_->GetFramebuffer(); render_pass_begin.framebuffer = context_.GetFramebuffer();
render_pass_begin.renderArea.extent.width = screen_width_; render_pass_begin.renderArea.extent.width = screen_width_;
render_pass_begin.renderArea.extent.height = screen_height_; render_pass_begin.renderArea.extent.height = screen_height_;
@ -1974,7 +1976,7 @@ void RendererVulkan::SwapBuffers() {
vkEndCommandBuffer(frames_[current_frame_].setup_command_buffer); vkEndCommandBuffer(frames_[current_frame_].setup_command_buffer);
vkEndCommandBuffer(frames_[current_frame_].draw_command_buffer); vkEndCommandBuffer(frames_[current_frame_].draw_command_buffer);
context_->SwapBuffers(); context_.SwapBuffers();
current_frame_ = (current_frame_ + 1) % frames_.size(); current_frame_ = (current_frame_ + 1) % frames_.size();
active_pipeline_ = VK_NULL_HANDLE; active_pipeline_ = VK_NULL_HANDLE;
@ -2033,13 +2035,13 @@ bool RendererVulkan::SetUniformInternal(ShaderVulkan& shader,
bool RendererVulkan::IsFormatSupported(VkFormat format) { bool RendererVulkan::IsFormatSupported(VkFormat format) {
VkFormatProperties properties; VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(context_->GetPhysicalDevice(), format, vkGetPhysicalDeviceFormatProperties(context_.GetPhysicalDevice(), format,
&properties); &properties);
return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
} }
size_t RendererVulkan::GetAndResetFPS() { size_t RendererVulkan::GetAndResetFPS() {
return context_->GetAndResetFPS(); return context_.GetAndResetFPS();
} }
void RendererVulkan::DestroyAllResources() { void RendererVulkan::DestroyAllResources() {

View File

@ -18,19 +18,12 @@
namespace eng { namespace eng {
class Image;
class RendererVulkan final : public Renderer { class RendererVulkan final : public Renderer {
public: public:
RendererVulkan(std::unique_ptr<VulkanContext> context); RendererVulkan(base::Closure context_lost_cb);
~RendererVulkan() final; ~RendererVulkan() final;
#if defined(__ANDROID__) virtual bool Initialize(Platform* platform) final;
bool Initialize(ANativeWindow* window) final;
#elif defined(__linux__)
bool Initialize(Display* display, Window window) final;
#endif
void Shutdown() final; void Shutdown() final;
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final; uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
@ -74,10 +67,6 @@ class RendererVulkan final : public Renderer {
const char* GetDebugName() final { return "Vulkan"; } const char* GetDebugName() final { return "Vulkan"; }
#if defined(__linux__) && !defined(__ANDROID__)
XVisualInfo* GetXVisualInfo(Display* display) final;
#endif
private: private:
// VkBuffer or VkImage with allocator. // VkBuffer or VkImage with allocator.
template <typename T> template <typename T>
@ -156,7 +145,7 @@ class RendererVulkan final : public Renderer {
bool context_lost_ = false; bool context_lost_ = false;
std::unique_ptr<VulkanContext> context_; VulkanContext context_;
VmaAllocator allocator_ = nullptr; VmaAllocator allocator_ = nullptr;

View File

@ -3,16 +3,22 @@
#include <android/native_window.h> #include <android/native_window.h>
#include "base/log.h" #include "base/log.h"
#include "engine/platform/platform.h"
namespace eng { namespace eng {
bool RendererVulkan::Initialize(ANativeWindow* window) { bool RendererVulkan::Initialize(Platform* platform) {
LOG << "Initializing renderer."; LOG << "Initializing renderer.";
screen_width_ = ANativeWindow_getWidth(window); screen_width_ = ANativeWindow_getWidth(platform->GetWindow());
screen_height_ = ANativeWindow_getHeight(window); screen_height_ = ANativeWindow_getHeight(platform->GetWindow());
if (!context_->CreateWindow(window, screen_width_, screen_height_)) { if (!context_.Initialize()) {
LOG << "Failed to initialize Vulkan context.";
return false;
}
if (!context_.CreateWindow(platform->GetWindow(), screen_width_,
screen_height_)) {
LOG << "Vulkan context failed to create window."; LOG << "Vulkan context failed to create window.";
return false; return false;
} }

View File

@ -1,18 +1,24 @@
#include "engine/renderer/vulkan/renderer_vulkan.h" #include "engine/renderer/vulkan/renderer_vulkan.h"
#include "base/log.h" #include "base/log.h"
#include "engine/platform/platform.h"
namespace eng { namespace eng {
bool RendererVulkan::Initialize(Display* display, Window window) { bool RendererVulkan::Initialize(Platform* platform) {
LOG << "Initializing renderer."; LOG << "Initializing renderer.";
XWindowAttributes xwa; XWindowAttributes xwa;
XGetWindowAttributes(display, window, &xwa); XGetWindowAttributes(platform->GetDisplay(), platform->GetWindow(), &xwa);
screen_width_ = xwa.width; screen_width_ = xwa.width;
screen_height_ = xwa.height; screen_height_ = xwa.height;
if (!context_->CreateWindow(display, window, screen_width_, screen_height_)) { if (!context_.Initialize()) {
LOG << "Failed to initialize Vulkan context.";
return false;
}
if (!context_.CreateWindow(platform->GetDisplay(), platform->GetWindow(),
screen_width_, screen_height_)) {
LOG << "Vulkan context failed to create window."; LOG << "Vulkan context failed to create window.";
return false; return false;
} }
@ -20,13 +26,4 @@ bool RendererVulkan::Initialize(Display* display, Window window) {
return InitializeInternal(); return InitializeInternal();
} }
XVisualInfo* RendererVulkan::GetXVisualInfo(Display* display) {
long visual_mask = VisualScreenMask;
int num_visuals;
XVisualInfo visual_info_template = {};
visual_info_template.screen = DefaultScreen(display);
return XGetVisualInfo(display, visual_mask, &visual_info_template,
&num_visuals);
}
} // namespace eng } // namespace eng

View File

@ -35,9 +35,11 @@ VulkanContext::~VulkanContext() {
} }
bool VulkanContext::Initialize() { bool VulkanContext::Initialize() {
if (volkInitialize() != VK_SUCCESS) { if (instance_ != VK_NULL_HANDLE)
return true;
if (volkInitialize() != VK_SUCCESS)
return false; return false;
}
if (!CreatePhysicalDevice()) if (!CreatePhysicalDevice())
return false; return false;