Skip to content

Commit

Permalink
Adding more coroutine glue, and cleaned up demo.
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasnoble committed Dec 29, 2024
1 parent 930a154 commit 46b397d
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 110 deletions.
45 changes: 39 additions & 6 deletions src/mips/psyqo-paths/cdrom-loader.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ SOFTWARE.
#include <EASTL/vector.h>
#include <stdint.h>

#include <coroutine>

#include "psyqo/gpu.hh"
#include "psyqo/iso9660-parser.hh"
#include "psyqo/task.hh"
Expand All @@ -45,10 +47,34 @@ namespace psyqo::paths {
* the data of the file, or an empty vector if the file could not be read.
* This is going to allocate memory in different places. Only one file can
* be read at a time, but it is safe to call readFile() again from the
* callback.
* callback. If preferred, the loader can be cascaded into another `TaskQueue`.
* Also, for convenience, readFile() can be awaited on using the co_await
* keyword in a coroutine.
*/

class CDRomLoader {
struct ReadFileAwaiter {
ReadFileAwaiter(eastl::string_view path, GPU &gpu, ISO9660Parser &parser, CDRomLoader &loader)
: m_path(path), m_gpu(gpu), m_parser(parser), m_loader(loader) {}
~ReadFileAwaiter() {}
constexpr bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_loader.readFile(m_path, m_gpu, m_parser, [handle, this](eastl::vector<uint8_t> &&data) {
m_data = eastl::move(data);
handle.resume();
});
}
eastl::vector<uint8_t> await_resume() { return eastl::move(m_data); }

private:
eastl::string_view m_path;
GPU &m_gpu;
ISO9660Parser &m_parser;
CDRomLoader &m_loader;
eastl::vector<uint8_t> m_data;
};

public:
/**
* @brief Reads a file from the CDRom.
Expand All @@ -61,16 +87,23 @@ class CDRomLoader {
* will be called with the data of the file, or an empty vector if the file
* could not be read.
*/
void readFile(eastl::string_view path, GPU& gpu, ISO9660Parser& parser,
eastl::function<void(eastl::vector<uint8_t>&&)>&& callback) {
void readFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser,
eastl::function<void(eastl::vector<uint8_t> &&)> &&callback) {
setupQueue(path, gpu, parser, eastl::move(callback));
m_queue.run();
}
psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser) {
setupQueue(path, gpu, parser, {});
return m_queue.schedule();
}
ReadFileAwaiter readFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser) {
return {path, gpu, parser, *this};
}

private:
void setupQueue(eastl::string_view path, GPU& gpu, ISO9660Parser& parser,
eastl::function<void(eastl::vector<uint8_t>&&)>&& callback);
eastl::function<void(eastl::vector<uint8_t>&&)> m_callback;
void setupQueue(eastl::string_view path, GPU &gpu, ISO9660Parser &parser,
eastl::function<void(eastl::vector<uint8_t> &&)> &&callback);
eastl::function<void(eastl::vector<uint8_t> &&)> m_callback;
psyqo::TaskQueue m_queue;
ISO9660Parser::ReadRequest m_request;
eastl::vector<uint8_t> m_data;
Expand Down
118 changes: 116 additions & 2 deletions src/mips/psyqo/cdrom-device.hh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SOFTWARE.
#include <stdint.h>

#include <concepts>
#include <coroutine>
#include <cstdint>
#include <type_traits>

Expand Down Expand Up @@ -69,7 +70,10 @@ concept IsCDRomDeviceStateEnum =
* quickly, and should not be used in performance-critical code, as they
* can still block the system for several milliseconds. The callbacks
* will be called from the main thread, and have a boolean parameter
* that indicates whether the operation was successful.
* that indicates whether the operation was successful. Last but not
* least, the class provides a coroutine-friendly API, which allows
* the use of the `co_await` keyword to suspend the coroutine until
* the operation is complete.
*
*/
class CDRomDevice final : public CDRom {
Expand All @@ -82,6 +86,111 @@ class CDRomDevice final : public CDRom {
unsigned index;
};

struct ResetAwaiter {
ResetAwaiter(CDRomDevice &device) : m_device(device) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.reset([handle, this](bool result) {
m_result = result;
handle.resume();
});
}
bool await_resume() { return m_result; }

private:
CDRomDevice &m_device;
bool m_result;
};

struct GetTOCSizeAwaiter {
GetTOCSizeAwaiter(CDRomDevice &device) : m_device(device) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.getTOCSize(&m_result, [handle, this](bool success) {
m_success = success;
handle.resume();
});
}
unsigned await_resume() { return m_success ? m_result : 0; }

private:
CDRomDevice &m_device;
unsigned m_result;
bool m_success;
};

struct ReadTOCAwaiter {
ReadTOCAwaiter(CDRomDevice &device, MSF *toc, unsigned size) : m_device(device), m_toc(toc), m_size(size) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.readTOC(m_toc, m_size, [handle, this](bool result) {
m_result = result;
handle.resume();
});
}
bool await_resume() { return m_result; }

private:
CDRomDevice &m_device;
MSF *m_toc;
unsigned m_size;
bool m_result;
};

struct MuteAwaiter {
MuteAwaiter(CDRomDevice &device) : m_device(device) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.mute([handle, this](bool result) {
m_result = result;
handle.resume();
});
}
bool await_resume() { return m_result; }

private:
CDRomDevice &m_device;
bool m_result;
};

struct UnmuteAwaiter {
UnmuteAwaiter(CDRomDevice &device) : m_device(device) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.unmute([handle, this](bool result) {
m_result = result;
handle.resume();
});
}
bool await_resume() { return m_result; }

private:
CDRomDevice &m_device;
bool m_result;
};

struct GetPlaybackLocationAwaiter {
GetPlaybackLocationAwaiter(CDRomDevice &device) : m_device(device) {}
bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_device.getPlaybackLocation([handle, this](PlaybackLocation *location) {
m_location = location;
handle.resume();
});
}
PlaybackLocation *await_resume() { return m_location; }

private:
CDRomDevice &m_device;
PlaybackLocation *m_location;
};

private:
struct ActionBase {
const char *name() const { return m_name; }
Expand Down Expand Up @@ -133,6 +242,7 @@ class CDRomDevice final : public CDRom {
void reset(eastl::function<void(bool)> &&callback);
TaskQueue::Task scheduleReset();
bool resetBlocking(GPU &);
ResetAwaiter reset() { return {*this}; }

/**
* @brief Reads sectors from the CDRom.
Expand All @@ -151,7 +261,6 @@ class CDRomDevice final : public CDRom {
*
*/
void readSectors(uint32_t sector, uint32_t count, void *buffer, eastl::function<void(bool)> &&callback) override;
TaskQueue::Task scheduleReadSectors(uint32_t sector, uint32_t count, void *buffer);
bool readSectorsBlocking(uint32_t sector, uint32_t count, void *buffer, GPU &);

/**
Expand All @@ -166,6 +275,7 @@ class CDRomDevice final : public CDRom {
void getTOCSize(unsigned *size, eastl::function<void(bool)> &&callback);
TaskQueue::Task scheduleGetTOCSize(unsigned *size);
unsigned getTOCSizeBlocking(GPU &);
GetTOCSizeAwaiter getTOCSize() { return {*this}; }

/**
* @brief Reads the Table of Contents from the CDRom.
Expand All @@ -187,6 +297,7 @@ class CDRomDevice final : public CDRom {
void readTOC(MSF *toc, unsigned size, eastl::function<void(bool)> &&callback);
TaskQueue::Task scheduleReadTOC(MSF *toc, unsigned size);
bool readTOCBlocking(MSF *toc, unsigned size, GPU &);
ReadTOCAwaiter readTOC(MSF *toc, unsigned size) { return {*this, toc, size}; }

/**
* @brief Mutes the CD audio for both CDDA and CDXA.
Expand All @@ -196,6 +307,7 @@ class CDRomDevice final : public CDRom {
void mute(eastl::function<void(bool)> &&callback);
TaskQueue::Task scheduleMute();
void muteBlocking(GPU &);
MuteAwaiter mute() { return MuteAwaiter(*this); }

/**
* @brief Unmutes the CD audio for both CDDA and CDXA.
Expand All @@ -205,6 +317,7 @@ class CDRomDevice final : public CDRom {
void unmute(eastl::function<void(bool)> &&callback);
TaskQueue::Task scheduleUnmute();
void unmuteBlocking(GPU &);
UnmuteAwaiter unmute() { return {*this}; }

/**
* @brief Begins playing CDDA audio from a given starting point.
Expand Down Expand Up @@ -275,6 +388,7 @@ class CDRomDevice final : public CDRom {
void getPlaybackLocation(PlaybackLocation *location, eastl::function<void(PlaybackLocation *)> &&callback);
void getPlaybackLocation(eastl::function<void(PlaybackLocation *)> &&callback);
TaskQueue::Task scheduleGetPlaybackLocation(PlaybackLocation *location);
GetPlaybackLocationAwaiter getPlaybackLocation() { return {*this}; }

/**
* @brief Set the Volume of the CDDA audio.
Expand Down
40 changes: 40 additions & 0 deletions src/mips/psyqo/cdrom.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ SOFTWARE.
#include <EASTL/functional.h>
#include <stdint.h>

#include <coroutine>

#include "psyqo/task.hh"

namespace psyqo {
Expand All @@ -42,6 +44,28 @@ namespace psyqo {
*/

class CDRom {
struct ReadSectorsAwaiter {
ReadSectorsAwaiter(uint32_t sector, uint32_t count, void *buffer, CDRom &cdrom)
: m_sector(sector), m_count(count), m_buffer(buffer), m_cdrom(cdrom) {}
~ReadSectorsAwaiter() {}
constexpr bool await_ready() const { return false; }
template <typename U>
void await_suspend(std::coroutine_handle<U> handle) {
m_cdrom.readSectors(m_sector, m_count, m_buffer, [handle, this](bool result) {
m_result = result;
handle.resume();
});
}
bool await_resume() { return m_result; }

private:
uint32_t m_sector;
uint32_t m_count;
void *m_buffer;
CDRom &m_cdrom;
bool m_result;
};

public:
virtual ~CDRom() {}
/**
Expand Down Expand Up @@ -100,6 +124,22 @@ class CDRom {
*/
TaskQueue::Task scheduleReadRequest(ReadRequest *request);

/**
* @brief Wrapper around the readSectors method for coroutines.
*
* @details This method will return an `Awaiter` object that can be used to
* suspend the coroutine until the read operation is complete. This is
* meant to be used in conjunction with the `co_await` keyword, in a coroutine.
*
* @param sector The sector to read.
* @param count The number of sectors to read.
* @param buffer The buffer to read into.
* @return ReadSectorsAwaiter The awaitable object to be used with the `co_await` keyword.
*/
ReadSectorsAwaiter readSectorsForCoroutine(uint32_t sector, uint32_t count, void *buffer) {
return {sector, count, buffer, *this};
}

private:
ReadRequest m_readRequest;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,8 @@ void CoroutineDemoScene::start(StartReason reason) {
m_coroutine.resume();
}

using namespace psyqo::timer_literals;

psyqo::Coroutine<> CoroutineDemoScene::coroutine() {
using namespace psyqo::timer_literals;
m_text = "Coroutine... sleeping for 2s";
co_await gpu().delay(2_s);
m_text = "Waking up... sleeping for 1s";
Expand Down
Loading

0 comments on commit 46b397d

Please sign in to comment.