Skip to content

Commit

Permalink
dummy_plug: Fixes for determinism
Browse files Browse the repository at this point in the history
  • Loading branch information
hbirchtree committed Aug 14, 2024
1 parent 919c1dc commit 4d41286
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 74 deletions.
9 changes: 6 additions & 3 deletions .github/tests/dummy_plug.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
{"time": 0, "type":"screenshot", "name":"start"},
{"time": 5000, "type":"key", "key": "CK_s", "duration": 3000},
{"time": 5000, "type":"key", "key": "CK_d", "duration": 3000},
{"time": 4500, "type":"mouse_button", "x": 540, "y": 360, "duration": 4000},
{"time": 5000, "type":"mouse_move", "x": 540, "y": 360, "dx": 300, "dy": 300, "duration": 3000},
{"time": 4500, "type":"mouse_button", "x": 320, "y": 180, "duration": 4000},
{"time": 5000, "type":"mouse_move", "x": 320, "y": 180, "dx": 160, "dy": 90, "duration": 3000},
{"time": 10000, "type":"screenshot", "name":"moved"},
{"time": 10100, "type":"controller_connect", "index":0},
{"time": 10200, "type":"controller_connect", "index":1},
{"time": 10500, "type":"screenshot", "name":"splitscreen"}
{"time": 11000, "type":"controller_button", "index":0, "button": "LB", "duration": 500},
{"time": 12000, "type":"controller_button", "index":0, "button": "A", "duration": 500},
{"time": 13000, "type":"screenshot", "name":"splitscreen"}
],
"frame_delta": 16.67,
"end_time": 15000
}
1 change: 1 addition & 0 deletions .github/workflows/10-compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ jobs:
run: |
source/.github/tests/test_blam_graphics.sh
- name: Uploading test artifacts
if: matrix.variant == 'desktop:x86_64-buildroot-linux-gnu:multi:rel'
uses: actions/upload-artifact@v4
with:
name: dummy_plug_test
Expand Down
11 changes: 7 additions & 4 deletions src/coffee/comp_app/bundle_include/coffee/comp_app/dummy_plug.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ struct Config : comp_app::Config<Config>
audio_format_t format{audio_format_t::pcm};
libc_types::u16 bits{16};
} audio_config;
nlohmann::json graphics_config{};
nlohmann::json config{};
bool enabled{false};
bool swrender{false};

nlohmann::json graphics_config{};
nlohmann::json config{};
libc_types::u64 frame_index{0};
compo::clock::duration frame_delta{std::chrono::microseconds{16667}};
bool enabled{false};
bool swrender{false};
};

void fork_dummy_plugs(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct PerformanceMonitor
{
using readable_services = detail::subsystem_list<
PerformanceMonitor,
AppLoader,
CPUClockProvider,
CPUTempProvider,
GPUTempProvider,
Expand Down
8 changes: 5 additions & 3 deletions src/coffee/comp_app/private/bundle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ void addDefaults(
/* Selection of window/event manager */
cVerbose(10, "Loading windowing library");
#if defined(FEATURE_ENABLE_OSMesaComponent)
if(dummyPlug.enabled)
if(dummyPlug.enabled && dummyPlug.swrender)
{
loader.registerAll<type_safety::type_list_t<
comp_app::PtrNativeWindowInfoService,
Expand Down Expand Up @@ -580,7 +580,7 @@ void addDefaults(

/* Selection of (E)GL context */
#if defined(FEATURE_ENABLE_OSMesaComponent)
if(dummyPlug.enabled)
if(dummyPlug.enabled && dummyPlug.swrender)
{
loader.registerAll<osmesa::Services>(container, ec);
appInfo.add("gl:context", "OSMesa");
Expand Down Expand Up @@ -710,7 +710,9 @@ void PerformanceMonitor::start_restricted(proxy_type& p, time_point const&)
auto frametime = std::chrono::duration_cast<stl_types::Chrono::seconds_f32>(
time - m_prevFrame);

if(frametime > 200ms)
auto& dummy = p.service<AppLoader>()->config<dummy_plug::Config>();

if(frametime > 200ms && !dummy.enabled)
{
Coffee::Logging::cWarning(
"Frame hitch detected! {}ms", frametime.count() * 1000.f);
Expand Down
166 changes: 118 additions & 48 deletions src/coffee/comp_app/private/dummy_plug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ void queue_input_event(
type_t type,
nlohmann::json const& event)
{
using Coffee::Input::CIControllerAtomicEvent;
using Coffee::Input::CIControllerAtomicUpdateEvent;
using Coffee::Input::CIKeyEvent;
using Coffee::Input::CIMouseButtonEvent;
using Coffee::Input::CIMouseMoveEvent;
using libc_types::u8;

CIEvent ievent;
const auto start_time = std::chrono::milliseconds(event.value("time", 0u));
Expand All @@ -59,12 +61,31 @@ void queue_input_event(
.value();
};

using namespace Coffee::Input;
switch(type)
{
case type_t::controller_axis: {
const auto axis = magic_enum::enum_cast<CIControllerAxisMapping>(
"CK_AXIS_" + event.value("axis", std::string()))
.value_or(CK_AXIS_BASE);
ievent.type = CIEvent::ControllerUpdate;
break;
}
case type_t::controller_button: {
const auto button =
magic_enum::enum_cast<CIControllerButtonMapping>(
"CK_BUTTON_" + event.value("button", std::string()))
.value_or(CK_BUTTON_BASE);
ievent.type = CIEvent::ControllerUpdate;
CIControllerAtomicEvent controller = {
.index = static_cast<u8>(button - CK_BUTTON_BASE),
.controller = static_cast<u8>(event.value("index", 0u)),
.button_state = true,
.axis = false,
};
emit_future_event(start_time, ievent, controller);
controller.button_state = false;
emit_future_event(start_time, ievent, controller);
break;
}
case type_t::controller_connect: {
Expand All @@ -77,13 +98,16 @@ void queue_input_event(
}
case type_t::key: {
using Coffee::Input::Latin1Mapping;
using namespace Coffee::Input;

ievent.type = CIEvent::Keyboard;
CIKeyEvent key;
key.key = magic_enum::enum_cast<Latin1Mapping>(
event.value("key", std::string()))
.value_or(CK_Null);
const auto key_value = event.value("key", std::string());
key.key =
magic_enum::enum_cast<Latin1Mapping>(key_value).value_or(CK_Null);
if(key.key == CK_Null)
key.key =
magic_enum::enum_cast<CISpecialKeyMapping>(key_value).value_or(
static_cast<CISpecialKeyMapping>(0));
key.mod = CIKeyEvent::PressedModifier;
emit_future_event(start_time, ievent, key);
auto unpress_delay =
Expand Down Expand Up @@ -126,7 +150,28 @@ void fork_dummy_plugs(
Coffee::Logging::cFatal("Dummy plug config contains nothing");
}

#if defined(FEATURE_ENABLE_OSMesaComponent)
if(config.contains("frame_delta"))
{
dummy_plug.frame_delta =
std::chrono::microseconds(static_cast<libc_types::u32>(
config.value("frame_delta", 0.0) * 1000));
/* Replace the original timestamp provider, so we can step the frames
* consistently. As long as everything is based on ECS-provided
* time_points, it should be deterministic. */
container.set_timestamp_provider(
[epoch = compo::clock::time_point() + container.epoch_offset(),
&dummy_plug]() {
return epoch + dummy_plug.frame_delta * dummy_plug.frame_index;
});
container.add_frame_end_callback(
[&dummy_plug]() { dummy_plug.frame_index++; });
rq::runtime_queue::OverrideClock(
[epoch = rq::detail::time_point() + container.epoch_offset(),
&dummy_plug]() {
return epoch + dummy_plug.frame_delta * dummy_plug.frame_index;
});
}

if(!config.contains("graphics"))
return;

Expand All @@ -143,8 +188,17 @@ void fork_dummy_plugs(
dummy_plug.graphics_config["profile"] = "Core";
dummy_plug.graphics_config["major"] = 4u;
dummy_plug.graphics_config["minor"] = 5u;
} else
{
dummy_plug.graphics_config = nlohmann::json();
dummy_plug.graphics_config["profile"] =
magic_enum::enum_name(glConfig.profile);
dummy_plug.graphics_config["major"] = glConfig.version.major;
dummy_plug.graphics_config["minor"] = glConfig.version.minor;
return;
}

#if defined(FEATURE_ENABLE_OSMesaComponent)
struct version_t
{
libc_types::u32 major{}, minor{};
Expand Down Expand Up @@ -238,8 +292,6 @@ void fork_dummy_plugs(
proc::wait_for(proc::wait_by::child_pid, ec, child_pid, &result);
cDebug("------------ Returning to parent ------------");
children.push_back(std::make_tuple(child_pid, version, result));
if(result != 0)
break;
}
}

Expand Down Expand Up @@ -279,40 +331,69 @@ void insert_dummy_plug(
container.service<comp_app::BasicEventBus<Coffee::Input::CIEvent>>();
auto* window_bus =
container.service<comp_app::BasicEventBus<Coffee::Display::Event>>();
auto* app_bus = container.service<comp_app::BasicEventBus<AppEvent>>();
const auto type_to_enum = [](nlohmann::json const& event,
std::string_view const& key) {
return magic_enum::enum_cast<type_t>(
event.value("type", std::string_view()))
.value();
};
auto* app_bus = container.service<comp_app::BasicEventBus<AppEvent>>();

if(config.contains("events"))
for(auto const& event : config["events"])
{
if(!event.contains("type"))
continue;
const auto type = type_to_enum(event, "type");

cDebug("- {}: {}", magic_enum::enum_name(type), event.dump());

switch(type)
{
auto emit_events = [&config, input_bus, &container]() {
auto* app_info = container.service<comp_app::AppInfo>();
if(app_info->state() != comp_app::interfaces::AppInfo::loaded)
return;

rq::runtime_queue::CancelTask(
rq::runtime_queue::GetSelfId().value());

const auto type_to_enum = [](nlohmann::json const& event,
std::string_view const& key) {
return magic_enum::enum_cast<type_t>(
event.value("type", std::string_view()))
.value();
};
for(auto const& event : config["events"])
{
case type_t::controller_axis:
case type_t::controller_button:
case type_t::controller_connect:
case type_t::key:
case type_t::mouse_button:
case type_t::mouse_move:
queue_input_event(input_bus, type, event);
break;
case type_t::screenshot:
// ...
break;
case type_t::none:
break;
if(!event.contains("type"))
continue;
const auto type = type_to_enum(event, "type");

cDebug("- {}: {}", magic_enum::enum_name(type), event.dump());

switch(type)
{
case type_t::controller_axis:
case type_t::controller_button:
case type_t::controller_connect:
case type_t::key:
case type_t::mouse_button:
case type_t::mouse_move:
queue_input_event(input_bus, type, event);
break;
case type_t::screenshot:
// ...
break;
case type_t::none:
break;
}
}
}

auto end_time =
std::chrono::milliseconds(config.value("end_time", 0u));

rq::runtime_queue::QueueShot(
rq::runtime_queue::GetCurrentQueue().value(),
end_time,
[&container]() {
auto window = container.service<Windowing>();
window->close();
})
.assume_value();
};

rq::runtime_queue::QueuePeriodic(
rq::runtime_queue::GetCurrentQueue().value(),
std::chrono::milliseconds(100),
std::move(emit_events))
.assume_value();
}

if(config.contains("audio"))
{
Expand All @@ -326,17 +407,6 @@ void insert_dummy_plug(
audio_config.value("format", std::string()))
.value_or(audio_format_t::pcm);
}

auto end_time = std::chrono::milliseconds(config.value("end_time", 0u));

rq::runtime_queue::QueueShot(
rq::runtime_queue::GetCurrentQueue().value(),
end_time,
[&container]() {
auto window = container.service<Windowing>();
window->close();
})
.assume_value();
}

} // namespace comp_app::dummy_plug
3 changes: 2 additions & 1 deletion src/coffee/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ coffee_library(
coffee_library(
TARGET EntComp
LIBRARIES CoreCommon CoreProfiling
SOURCES private/components/allocators.cpp
SOURCES
private/components/allocators.cpp
HEADER_DIRS
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/coffee/components>
HEADER_BASE coffee
Expand Down
24 changes: 23 additions & 1 deletion src/coffee/core/include/coffee/components/entity_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,28 @@ struct EntityContainer : stl_types::non_copy
u64 tags_of(u64 id) const;

/* Time tracking */
void set_timestamp_provider(std::function<clock::time_point()>&& func)
{
time_stamp_source = std::move(func);
}
clock::duration epoch_offset() const
{
return time_offset;
}
clock::time_point relative_timestamp() const
{
auto current_time = clock::now();
auto current_time = time_stamp_source();
return current_time - time_offset;
}
size_t add_frame_end_callback(std::function<void()>&& cb)
{
frame_end_callbacks.emplace_back(std::move(cb));
return frame_end_callbacks.size() - 1u;
}
void remove_frame_end_callback(size_t i)
{
frame_end_callbacks.at(i) = {};
}

/* For optimizations */
using visitor_graph = std::set<std::vector<bool>>;
Expand All @@ -492,7 +509,12 @@ struct EntityContainer : stl_types::non_copy
std::vector<std::unique_ptr<EntityVisitorBase>> visitors;
std::unordered_map<type_hash, SubsystemBase*> services;

std::function<clock::time_point()> time_stamp_source = []()
{
return clock::now();
};
clock::duration time_offset;
std::vector<std::function<void()>> frame_end_callbacks;

public:
u32 debug_flags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ void EntityContainer::exec()
else
subsys.end_frame(proxy, time_now);
}

for(auto const& callback : frame_end_callbacks)
{
if(callback)
callback();
}
}

FORCEDINLINE EntityContainer::visitor_graph EntityContainer::create_task_graph()
Expand Down
Loading

0 comments on commit 4d41286

Please sign in to comment.