The tracy directory now has an experiment in getting Tracy to work. It's _not_ as easy as it is touted to be.
parent
308fe4bed2
commit
c98aa936ad
@ -0,0 +1,253 @@ |
|||||||
|
#ifndef AMT_THREAD_HPP |
||||||
|
#define AMT_THREAD_HPP |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <concepts> |
||||||
|
#include <cstddef> |
||||||
|
#include <deque> |
||||||
|
#include <mutex> |
||||||
|
#include <type_traits> |
||||||
|
#include <thread> |
||||||
|
#include <condition_variable> |
||||||
|
#include <atomic> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
namespace amt { |
||||||
|
|
||||||
|
// NOTE: Could implement lock-free queue.
|
||||||
|
template <typename T> |
||||||
|
struct Queue { |
||||||
|
using base_type = std::deque<T>; |
||||||
|
using value_type = typename base_type::value_type; |
||||||
|
using pointer = typename base_type::pointer; |
||||||
|
using const_pointer = typename base_type::const_pointer; |
||||||
|
using reference = typename base_type::reference; |
||||||
|
using const_reference = typename base_type::const_reference; |
||||||
|
using iterator = typename base_type::iterator; |
||||||
|
using const_iterator = typename base_type::const_iterator; |
||||||
|
using reverse_iterator = typename base_type::reverse_iterator; |
||||||
|
using const_reverse_iterator = typename base_type::const_reverse_iterator; |
||||||
|
using difference_type = typename base_type::difference_type; |
||||||
|
using size_type = typename base_type::size_type; |
||||||
|
|
||||||
|
constexpr Queue() noexcept = default; |
||||||
|
constexpr Queue(Queue const&) noexcept = delete; |
||||||
|
constexpr Queue(Queue &&) noexcept = default; |
||||||
|
constexpr Queue& operator=(Queue const&) noexcept = delete; |
||||||
|
constexpr Queue& operator=(Queue &&) noexcept = default; |
||||||
|
constexpr ~Queue() noexcept = default; |
||||||
|
|
||||||
|
template <typename U> |
||||||
|
requires std::same_as<std::decay_t<U>, value_type> |
||||||
|
void push(U&& u) { |
||||||
|
std::lock_guard m(m_mutex); |
||||||
|
m_data.push_back(std::forward<U>(u)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename... Args> |
||||||
|
void emplace(Args&&... args) { |
||||||
|
std::lock_guard m(m_mutex); |
||||||
|
m_data.emplace_back(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
std::optional<value_type> pop() { |
||||||
|
std::lock_guard m(m_mutex); |
||||||
|
if (empty_unsafe()) return std::nullopt; |
||||||
|
auto el = std::move(m_data.front()); |
||||||
|
m_data.pop_front(); |
||||||
|
return std::move(el); |
||||||
|
} |
||||||
|
|
||||||
|
auto size() const noexcept -> size_type { |
||||||
|
std::lock_guard m(m_mutex); |
||||||
|
return m_data.size(); |
||||||
|
} |
||||||
|
auto empty() const noexcept -> bool { |
||||||
|
std::lock_guard m(m_mutex); |
||||||
|
return m_data.empty(); |
||||||
|
} |
||||||
|
constexpr auto size_unsafe() const noexcept -> size_type { return m_data.size(); } |
||||||
|
constexpr auto empty_unsafe() const noexcept -> bool { return m_data.empty(); } |
||||||
|
|
||||||
|
private: |
||||||
|
base_type m_data; |
||||||
|
mutable std::mutex m_mutex; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Fn> |
||||||
|
struct ThreadPool; |
||||||
|
|
||||||
|
template <typename Fn> |
||||||
|
struct Worker { |
||||||
|
using parent_t = ThreadPool<Fn>*; |
||||||
|
using work_t = Fn; |
||||||
|
using size_type = std::size_t; |
||||||
|
constexpr Worker() noexcept = default; |
||||||
|
constexpr Worker(Worker const&) noexcept = default; |
||||||
|
constexpr Worker(Worker &&) noexcept = default; |
||||||
|
constexpr Worker& operator=(Worker const&) noexcept = default; |
||||||
|
constexpr Worker& operator=(Worker &&) noexcept = default; |
||||||
|
~Worker() { |
||||||
|
stop(); |
||||||
|
} |
||||||
|
|
||||||
|
void start(parent_t pool, size_type id) { |
||||||
|
assert((m_running.load(std::memory_order::acquire) == false) && "Thread is already running"); |
||||||
|
m_running.store(true); |
||||||
|
m_parent.store(pool); |
||||||
|
m_id = id; |
||||||
|
m_thread = std::thread([this]() { |
||||||
|
while (m_running.load(std::memory_order::relaxed)) { |
||||||
|
std::unique_lock lk(m_mutex); |
||||||
|
m_cv.wait(lk, [this] { |
||||||
|
return !m_queue.empty_unsafe() || !m_running.load(std::memory_order::relaxed); |
||||||
|
}); |
||||||
|
auto item = pop_task(); |
||||||
|
if (!item) { |
||||||
|
item = try_steal(); |
||||||
|
if (!item) continue; |
||||||
|
} |
||||||
|
|
||||||
|
process_work(std::move(*item)); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
void process_work(work_t&& work) const noexcept { |
||||||
|
std::invoke(std::move(work)); |
||||||
|
auto ptr = m_parent.load(); |
||||||
|
if (ptr) ptr->task_completed(); |
||||||
|
} |
||||||
|
|
||||||
|
void stop() { |
||||||
|
if (!m_running.load()) return; |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> lock(m_mutex); |
||||||
|
m_running.store(false); |
||||||
|
} |
||||||
|
m_cv.notify_all(); |
||||||
|
m_thread.join(); |
||||||
|
m_parent.store(nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
void add(work_t&& work) { |
||||||
|
std::lock_guard<std::mutex> lock(m_mutex); |
||||||
|
m_queue.push(std::move(work)); |
||||||
|
m_cv.notify_one(); |
||||||
|
} |
||||||
|
|
||||||
|
std::optional<work_t> pop_task() noexcept { |
||||||
|
return m_queue.pop(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
std::optional<work_t> try_steal() noexcept { |
||||||
|
auto ptr = m_parent.load(); |
||||||
|
if (ptr) return ptr->try_steal(m_id); |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool empty() const noexcept { return m_queue.empty_unsafe(); } |
||||||
|
constexpr size_type size() const noexcept { return m_queue.size_unsafe(); } |
||||||
|
constexpr size_type id() const noexcept { return m_id; } |
||||||
|
constexpr bool running() const noexcept { return m_running.load(std::memory_order::relaxed); } |
||||||
|
|
||||||
|
private: |
||||||
|
Queue<work_t> m_queue{}; |
||||||
|
std::thread m_thread; |
||||||
|
std::atomic<bool> m_running{false}; |
||||||
|
std::mutex m_mutex{}; |
||||||
|
std::condition_variable m_cv{}; |
||||||
|
std::atomic<parent_t> m_parent{nullptr}; |
||||||
|
size_type m_id; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Fn> |
||||||
|
struct ThreadPool { |
||||||
|
using worker_t = Worker<Fn>; |
||||||
|
using work_t = typename worker_t::work_t; |
||||||
|
using size_type = std::size_t; |
||||||
|
|
||||||
|
constexpr ThreadPool(ThreadPool const&) noexcept = delete; |
||||||
|
constexpr ThreadPool(ThreadPool &&) noexcept = default; |
||||||
|
constexpr ThreadPool& operator=(ThreadPool const&) noexcept = delete; |
||||||
|
constexpr ThreadPool& operator=(ThreadPool &&) noexcept = default; |
||||||
|
~ThreadPool() { |
||||||
|
stop(); |
||||||
|
} |
||||||
|
|
||||||
|
ThreadPool(size_type n = std::thread::hardware_concurrency()) |
||||||
|
: m_workers(std::max(n, size_type{1})) |
||||||
|
{ |
||||||
|
for (auto i = 0ul; i < m_workers.size(); ++i) { |
||||||
|
m_workers[i].start(this, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void stop() { |
||||||
|
for (auto& w: m_workers) w.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
void add(Fn&& work) { |
||||||
|
m_active_tasks.fetch_add(1, std::memory_order::relaxed); |
||||||
|
m_workers[m_last_added].add(std::move(work)); |
||||||
|
m_last_added = (m_last_added + 1) % m_workers.size(); |
||||||
|
} |
||||||
|
|
||||||
|
std::optional<work_t> try_steal(size_type id) { |
||||||
|
for (auto& w: m_workers) { |
||||||
|
if (w.id() == id) continue; |
||||||
|
auto item = w.pop_task(); |
||||||
|
if (item) return item; |
||||||
|
} |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
void task_completed() { |
||||||
|
if (m_active_tasks.fetch_sub(1, std::memory_order::release) == 1) { |
||||||
|
m_wait_cv.notify_all(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void wait() { |
||||||
|
std::unique_lock lock(m_wait_mutex); |
||||||
|
m_wait_cv.wait(lock, [this] { |
||||||
|
return m_active_tasks.load(std::memory_order::acquire) == 0; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<worker_t> m_workers; |
||||||
|
size_type m_last_added{}; |
||||||
|
std::mutex m_wait_mutex; |
||||||
|
std::condition_variable m_wait_cv; |
||||||
|
std::atomic<size_t> m_active_tasks{0}; |
||||||
|
}; |
||||||
|
|
||||||
|
using thread_pool_t = ThreadPool<std::function<void()>>; |
||||||
|
|
||||||
|
// WARNING: Do not capture the stack variable if you're defering wait on pool.
|
||||||
|
// If you want to capture them, either capture them value or do "pool.wait()" at the end of the scope.
|
||||||
|
template <std::size_t Split, typename Fn> |
||||||
|
requires (std::is_invocable_v<Fn, std::size_t>) |
||||||
|
constexpr auto parallel_for(thread_pool_t& pool, std::size_t start, std::size_t end, Fn&& body) noexcept { |
||||||
|
if (start >= end) return; |
||||||
|
|
||||||
|
auto const size = (end - start); |
||||||
|
auto const chunk_size = std::max(size_t{1}, (size + Split - 1) / Split); |
||||||
|
auto const num_chunks = (size + chunk_size - 1) / chunk_size; |
||||||
|
|
||||||
|
for (auto chunk = 0ul; chunk < num_chunks; ++chunk) { |
||||||
|
auto const chunk_start = std::min(start + (chunk * chunk_size), end); |
||||||
|
auto const chunk_end = std::min(chunk_start + (chunk_size), end); |
||||||
|
pool.add([chunk_start, chunk_end, body] { |
||||||
|
for (auto i = chunk_start; i < chunk_end; ++i) { |
||||||
|
std::invoke(body, i); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} // nsmespace amt
|
||||||
|
|
||||||
|
#endif // AMT_THREAD_HPP
|
@ -0,0 +1,61 @@ |
|||||||
|
//
|
||||||
|
// Tracy profiler
|
||||||
|
// ----------------
|
||||||
|
//
|
||||||
|
// For fast integration, compile and
|
||||||
|
// link with this source file (and none
|
||||||
|
// other) in your executable (or in the
|
||||||
|
// main DLL / shared object on multi-DLL
|
||||||
|
// projects).
|
||||||
|
//
|
||||||
|
|
||||||
|
// Define TRACY_ENABLE to enable profiler.
|
||||||
|
|
||||||
|
#include "common/TracySystem.cpp" |
||||||
|
|
||||||
|
#ifdef TRACY_ENABLE |
||||||
|
|
||||||
|
#ifdef _MSC_VER |
||||||
|
# pragma warning(push, 0) |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "common/tracy_lz4.cpp" |
||||||
|
#include "client/TracyProfiler.cpp" |
||||||
|
#include "client/TracyCallstack.cpp" |
||||||
|
#include "client/TracySysPower.cpp" |
||||||
|
#include "client/TracySysTime.cpp" |
||||||
|
#include "client/TracySysTrace.cpp" |
||||||
|
#include "common/TracySocket.cpp" |
||||||
|
#include "client/tracy_rpmalloc.cpp" |
||||||
|
#include "client/TracyDxt1.cpp" |
||||||
|
#include "client/TracyAlloc.cpp" |
||||||
|
#include "client/TracyOverride.cpp" |
||||||
|
#include "client/TracyKCore.cpp" |
||||||
|
|
||||||
|
#if defined(TRACY_HAS_CALLSTACK) |
||||||
|
# if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 |
||||||
|
# include "libbacktrace/alloc.cpp" |
||||||
|
# include "libbacktrace/dwarf.cpp" |
||||||
|
# include "libbacktrace/fileline.cpp" |
||||||
|
# include "libbacktrace/mmapio.cpp" |
||||||
|
# include "libbacktrace/posix.cpp" |
||||||
|
# include "libbacktrace/sort.cpp" |
||||||
|
# include "libbacktrace/state.cpp" |
||||||
|
# if TRACY_HAS_CALLSTACK == 4 |
||||||
|
# include "libbacktrace/macho.cpp" |
||||||
|
# else |
||||||
|
# include "libbacktrace/elf.cpp" |
||||||
|
# endif |
||||||
|
# include "common/TracyStackFrames.cpp" |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef _MSC_VER |
||||||
|
# pragma comment(lib, "ws2_32.lib") |
||||||
|
# pragma comment(lib, "dbghelp.lib") |
||||||
|
# pragma comment(lib, "advapi32.lib") |
||||||
|
# pragma comment(lib, "user32.lib") |
||||||
|
# pragma warning(pop) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,77 @@ |
|||||||
|
project('raycaster', 'cpp', |
||||||
|
version: '0.1.0', |
||||||
|
default_options: [ |
||||||
|
'cpp_std=c++20', |
||||||
|
#'cpp_args=-DTRACY_ENABLE=1 -D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', |
||||||
|
'cpp_args=-DTRACY_ENABLE=1', |
||||||
|
]) |
||||||
|
|
||||||
|
#exe_defaults = ['warning_level=2', 'werror=true'] |
||||||
|
exe_defaults = [] |
||||||
|
cc = meson.get_compiler('cpp') |
||||||
|
|
||||||
|
tracy = dependency('tracy', static: true) |
||||||
|
catch2 = dependency('catch2-with-main') |
||||||
|
fmt = dependency('fmt', allow_fallback: true) |
||||||
|
freetype2 = dependency('freetype2') |
||||||
|
json = dependency('nlohmann_json') |
||||||
|
opengl32 = cc.find_library('opengl32', required: true) |
||||||
|
winmm = cc.find_library('winmm', required: true) |
||||||
|
gdi32 = cc.find_library('gdi32', required: true) |
||||||
|
ws2_32 = cc.find_library('ws2_32', required: true) |
||||||
|
dbghelp = cc.find_library('dbghelp', required: true) |
||||||
|
|
||||||
|
sfml_audio = dependency('sfml_audio') |
||||||
|
sfml_graphics = dependency('sfml_graphics') |
||||||
|
sfml_main = dependency('sfml_main') |
||||||
|
sfml_network = dependency('sfml_network') |
||||||
|
sfml_system = dependency('sfml_system') |
||||||
|
sfml_window = dependency('sfml_window') |
||||||
|
|
||||||
|
if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized' |
||||||
|
warning('Profiling builds should set --buildtype=debugoptimized') |
||||||
|
endif |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
fmt, json, opengl32, freetype2, |
||||||
|
winmm, gdi32, sfml_audio, sfml_graphics, |
||||||
|
sfml_main, sfml_network, sfml_system, |
||||||
|
sfml_window, ws2_32, dbghelp, tracy |
||||||
|
] |
||||||
|
|
||||||
|
# use this for common options only for our executables |
||||||
|
cpp_args=[ ] |
||||||
|
|
||||||
|
executable('runtests', [ |
||||||
|
'dbc.cpp', |
||||||
|
'matrix.cpp', |
||||||
|
'TracyClient.cpp', |
||||||
|
'tests/base.cpp', |
||||||
|
], override_options: exe_defaults, |
||||||
|
dependencies: dependencies + [catch2]) |
||||||
|
|
||||||
|
executable('zedcaster', [ |
||||||
|
'dbc.cpp', |
||||||
|
'matrix.cpp', |
||||||
|
'config.cpp', |
||||||
|
'texture.cpp', |
||||||
|
'raycaster.cpp', |
||||||
|
'TracyClient.cpp', |
||||||
|
'stats.cpp', |
||||||
|
'main.cpp' |
||||||
|
], |
||||||
|
cpp_args: cpp_args, |
||||||
|
override_options: exe_defaults, |
||||||
|
dependencies: dependencies) |
||||||
|
|
||||||
|
executable('amtcaster', [ |
||||||
|
'dbc.cpp', |
||||||
|
'config.cpp', |
||||||
|
'amt/texture.cpp', |
||||||
|
'TracyClient.cpp', |
||||||
|
'amt/raycaster.cpp', |
||||||
|
'amt/main.cpp' |
||||||
|
], |
||||||
|
cpp_args: ['-std=c++23'], |
||||||
|
override_options: exe_defaults, |
||||||
|
dependencies: dependencies) |
@ -0,0 +1 @@ |
|||||||
|
option('tracy_enable', type: 'boolean', value: false, description: 'Enable profiling') |
Loading…
Reference in new issue