Simple UI for frame seeking.
This commit is contained in:
parent
5227fe72b5
commit
07c592942f
|
@ -17,22 +17,21 @@ namespace poly {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
std::string CanonicalizePath(const std::string& original_path) {
|
std::string CanonicalizePath(const std::string& original_path) {
|
||||||
char path_separator('\\');
|
char path_sep('\\');
|
||||||
std::string path(poly::fix_path_separators(original_path, path_separator));
|
std::string path(poly::fix_path_separators(original_path, path_sep));
|
||||||
|
|
||||||
std::vector<std::string::size_type> path_breaks;
|
std::vector<std::string::size_type> path_breaks;
|
||||||
|
|
||||||
std::string::size_type pos(path.find_first_of(path_separator));
|
std::string::size_type pos(path.find_first_of(path_sep));
|
||||||
std::string::size_type pos_n(std::string::npos);
|
std::string::size_type pos_n(std::string::npos);
|
||||||
|
|
||||||
while (pos != std::string::npos) {
|
while (pos != std::string::npos) {
|
||||||
if ((pos_n = path.find_first_of(path_separator, pos + 1)) == std::string::npos) {
|
if ((pos_n = path.find_first_of(path_sep, pos + 1)) == std::string::npos) {
|
||||||
pos_n = path.size();
|
pos_n = path.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto diff(pos_n - pos);
|
auto diff(pos_n - pos);
|
||||||
switch (diff)
|
switch (diff) {
|
||||||
{
|
|
||||||
case 0:
|
case 0:
|
||||||
pos_n = std::string::npos;
|
pos_n = std::string::npos;
|
||||||
break;
|
break;
|
||||||
|
@ -46,8 +45,7 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
if (path[pos + 1] == '.') {
|
if (path[pos + 1] == '.') {
|
||||||
path.erase(pos, 2);
|
path.erase(pos, 2);
|
||||||
pos_n -= 2;
|
pos_n -= 2;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
path_breaks.push_back(pos);
|
path_breaks.push_back(pos);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -61,13 +59,11 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
if (loc == std::string::npos || loc > req) {
|
if (loc == std::string::npos || loc > req) {
|
||||||
path.erase(0, req);
|
path.erase(0, req);
|
||||||
pos_n -= req;
|
pos_n -= req;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
path.erase(loc + 1, req - (loc + 1));
|
path.erase(loc + 1, req - (loc + 1));
|
||||||
pos_n -= req - (loc + 1);
|
pos_n -= req - (loc + 1);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
auto last(path_breaks.back());
|
auto last(path_breaks.back());
|
||||||
auto last_diff((pos + 3) - last);
|
auto last_diff((pos + 3) - last);
|
||||||
path.erase(last, last_diff);
|
path.erase(last, last_diff);
|
||||||
|
@ -75,8 +71,7 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
// Also remove path reference
|
// Also remove path reference
|
||||||
path_breaks.erase(path_breaks.end() - 1);
|
path_breaks.erase(path_breaks.end() - 1);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
path_breaks.push_back(pos);
|
path_breaks.push_back(pos);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -90,13 +85,13 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing seperator
|
// Remove trailing seperator
|
||||||
if (!path.empty() && path.back() == path_separator) {
|
if (!path.empty() && path.back() == path_sep) {
|
||||||
path.erase(path.size() - 1);
|
path.erase(path.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final sanity check for dead paths
|
// Final sanity check for dead paths
|
||||||
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_separator))
|
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) ||
|
||||||
|| (path.size() == 2 && path[0] == '.' && path[1] == '.')) {
|
(path.size() == 2 && path[0] == '.' && path[1] == '.')) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,25 +101,19 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
WildcardFlags WildcardFlags::FIRST(true, false);
|
WildcardFlags WildcardFlags::FIRST(true, false);
|
||||||
WildcardFlags WildcardFlags::LAST(false, true);
|
WildcardFlags WildcardFlags::LAST(false, true);
|
||||||
|
|
||||||
WildcardFlags::WildcardFlags()
|
WildcardFlags::WildcardFlags() : FromStart(false), ToEnd(false) {}
|
||||||
: FromStart(false)
|
|
||||||
, ToEnd(false)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
WildcardFlags::WildcardFlags(bool start, bool end)
|
WildcardFlags::WildcardFlags(bool start, bool end)
|
||||||
: FromStart(start)
|
: FromStart(start), ToEnd(end) {}
|
||||||
, ToEnd(end)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
WildcardRule::WildcardRule(const std::string& str_match, const WildcardFlags& flags)
|
WildcardRule::WildcardRule(const std::string& str_match,
|
||||||
: match(str_match)
|
const WildcardFlags& flags)
|
||||||
, rules(flags)
|
: match(str_match), rules(flags) {
|
||||||
{
|
|
||||||
std::transform(match.begin(), match.end(), match.begin(), tolower);
|
std::transform(match.begin(), match.end(), match.begin(), tolower);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WildcardRule::Check(const std::string& str_lower, std::string::size_type& offset) const
|
bool WildcardRule::Check(const std::string& str_lower,
|
||||||
{
|
std::string::size_type& offset) const {
|
||||||
if (match.empty()) {
|
if (match.empty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -151,8 +140,7 @@ bool WildcardRule::Check(const std::string& str_lower, std::string::size_type& o
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WildcardEngine::PreparePattern(const std::string& pattern)
|
void WildcardEngine::PreparePattern(const std::string& pattern) {
|
||||||
{
|
|
||||||
rules.clear();
|
rules.clear();
|
||||||
|
|
||||||
WildcardFlags flags(WildcardFlags::FIRST);
|
WildcardFlags flags(WildcardFlags::FIRST);
|
||||||
|
@ -172,13 +160,11 @@ void WildcardEngine::PreparePattern(const std::string& pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WildcardEngine::SetRule(const std::string& pattern)
|
void WildcardEngine::SetRule(const std::string& pattern) {
|
||||||
{
|
|
||||||
PreparePattern(pattern);
|
PreparePattern(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WildcardEngine::Match(const std::string& str) const
|
bool WildcardEngine::Match(const std::string& str) const {
|
||||||
{
|
|
||||||
std::string str_lc;
|
std::string str_lc;
|
||||||
std::transform(str.begin(), str.end(), std::back_inserter(str_lc), tolower);
|
std::transform(str.begin(), str.end(), std::back_inserter(str_lc), tolower);
|
||||||
|
|
||||||
|
|
|
@ -126,16 +126,13 @@ std::string fix_path_separators(const std::string& source, char new_sep) {
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string find_name_from_path(const std::string& path)
|
std::string find_name_from_path(const std::string& path) {
|
||||||
{
|
|
||||||
std::string name(path);
|
std::string name(path);
|
||||||
|
|
||||||
if (!path.empty()) {
|
if (!path.empty()) {
|
||||||
std::string::size_type from(std::string::npos);
|
std::string::size_type from(std::string::npos);
|
||||||
if (path.back() == '\\') {
|
if (path.back() == '\\') {
|
||||||
from = path.size() - 2;
|
from = path.size() - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pos(path.find_last_of('\\', from));
|
auto pos(path.find_last_of('\\', from));
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
if (from == std::string::npos) {
|
if (from == std::string::npos) {
|
||||||
|
@ -146,7 +143,26 @@ std::string find_name_from_path(const std::string& path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring find_name_from_path(const std::wstring& path) {
|
||||||
|
std::wstring name(path);
|
||||||
|
if (!path.empty()) {
|
||||||
|
std::wstring::size_type from(std::wstring::npos);
|
||||||
|
if (path.back() == '\\') {
|
||||||
|
from = path.size() - 2;
|
||||||
|
}
|
||||||
|
auto pos(path.find_last_of('\\', from));
|
||||||
|
if (pos != std::wstring::npos) {
|
||||||
|
if (from == std::wstring::npos) {
|
||||||
|
name = path.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
auto len(from - pos);
|
||||||
|
name = path.substr(pos + 1, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ std::string fix_path_separators(const std::string& source,
|
||||||
|
|
||||||
// Find the top directory name or filename from a path
|
// Find the top directory name or filename from a path
|
||||||
std::string find_name_from_path(const std::string& path);
|
std::string find_name_from_path(const std::string& path);
|
||||||
|
std::wstring find_name_from_path(const std::wstring& path);
|
||||||
|
|
||||||
} // namespace poly
|
} // namespace poly
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ class Fence {
|
||||||
|
|
||||||
// Gets the current high-performance tick count.
|
// Gets the current high-performance tick count.
|
||||||
uint64_t ticks();
|
uint64_t ticks();
|
||||||
|
uint64_t ticks_per_second();
|
||||||
|
|
||||||
// TODO(benvanik): processor info API.
|
// TODO(benvanik): processor info API.
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,14 @@ uint64_t ticks() {
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t ticks_per_second() {
|
||||||
|
static LARGE_INTEGER freq = {0};
|
||||||
|
if (!freq.QuadPart) {
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
}
|
||||||
|
return freq.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t current_thread_id() {
|
uint32_t current_thread_id() {
|
||||||
return static_cast<uint32_t>(GetCurrentThreadId());
|
return static_cast<uint32_t>(GetCurrentThreadId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,8 @@ void Control::OnResize(UIEvent& e) { on_resize(e); }
|
||||||
|
|
||||||
void Control::OnLayout(UIEvent& e) { on_layout(e); }
|
void Control::OnLayout(UIEvent& e) { on_layout(e); }
|
||||||
|
|
||||||
|
void Control::OnPaint(UIEvent& e) { on_paint(e); }
|
||||||
|
|
||||||
void Control::OnVisible(UIEvent& e) { on_visible(e); }
|
void Control::OnVisible(UIEvent& e) { on_visible(e); }
|
||||||
|
|
||||||
void Control::OnHidden(UIEvent& e) { on_hidden(e); }
|
void Control::OnHidden(UIEvent& e) { on_hidden(e); }
|
||||||
|
|
|
@ -31,6 +31,8 @@ class Control {
|
||||||
|
|
||||||
Control* parent() const { return parent_; }
|
Control* parent() const { return parent_; }
|
||||||
|
|
||||||
|
size_t child_count() const { return children_.size(); }
|
||||||
|
Control* child(size_t i) const { return children_[i].get(); }
|
||||||
void AddChild(Control* child_control);
|
void AddChild(Control* child_control);
|
||||||
void AddChild(std::unique_ptr<Control> child_control);
|
void AddChild(std::unique_ptr<Control> child_control);
|
||||||
void AddChild(ControlPtr child_control);
|
void AddChild(ControlPtr child_control);
|
||||||
|
@ -65,6 +67,7 @@ class Control {
|
||||||
public:
|
public:
|
||||||
poly::Delegate<UIEvent> on_resize;
|
poly::Delegate<UIEvent> on_resize;
|
||||||
poly::Delegate<UIEvent> on_layout;
|
poly::Delegate<UIEvent> on_layout;
|
||||||
|
poly::Delegate<UIEvent> on_paint;
|
||||||
|
|
||||||
poly::Delegate<UIEvent> on_visible;
|
poly::Delegate<UIEvent> on_visible;
|
||||||
poly::Delegate<UIEvent> on_hidden;
|
poly::Delegate<UIEvent> on_hidden;
|
||||||
|
@ -94,6 +97,7 @@ class Control {
|
||||||
|
|
||||||
virtual void OnResize(UIEvent& e);
|
virtual void OnResize(UIEvent& e);
|
||||||
virtual void OnLayout(UIEvent& e);
|
virtual void OnLayout(UIEvent& e);
|
||||||
|
virtual void OnPaint(UIEvent& e);
|
||||||
|
|
||||||
virtual void OnVisible(UIEvent& e);
|
virtual void OnVisible(UIEvent& e);
|
||||||
virtual void OnHidden(UIEvent& e);
|
virtual void OnHidden(UIEvent& e);
|
||||||
|
|
|
@ -93,8 +93,6 @@ uint64_t CommandProcessor::QueryTime() {
|
||||||
bool CommandProcessor::Initialize(std::unique_ptr<GLContext> context) {
|
bool CommandProcessor::Initialize(std::unique_ptr<GLContext> context) {
|
||||||
context_ = std::move(context);
|
context_ = std::move(context);
|
||||||
|
|
||||||
pending_fn_event_ = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
||||||
|
|
||||||
worker_running_ = true;
|
worker_running_ = true;
|
||||||
worker_thread_ = std::thread([this]() {
|
worker_thread_ = std::thread([this]() {
|
||||||
poly::threading::set_name("GL4 Worker");
|
poly::threading::set_name("GL4 Worker");
|
||||||
|
@ -118,8 +116,6 @@ void CommandProcessor::Shutdown() {
|
||||||
shader_cache_.clear();
|
shader_cache_.clear();
|
||||||
|
|
||||||
context_.reset();
|
context_.reset();
|
||||||
|
|
||||||
CloseHandle(pending_fn_event_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::BeginTracing(const std::wstring& root_path) {
|
void CommandProcessor::BeginTracing(const std::wstring& root_path) {
|
||||||
|
@ -130,10 +126,12 @@ void CommandProcessor::BeginTracing(const std::wstring& root_path) {
|
||||||
void CommandProcessor::EndTracing() { trace_writer_.Close(); }
|
void CommandProcessor::EndTracing() { trace_writer_.Close(); }
|
||||||
|
|
||||||
void CommandProcessor::CallInThread(std::function<void()> fn) {
|
void CommandProcessor::CallInThread(std::function<void()> fn) {
|
||||||
assert_null(pending_fn_);
|
if (pending_fns_.empty() &&
|
||||||
pending_fn_ = std::move(fn);
|
worker_thread_.get_id() == std::this_thread::get_id()) {
|
||||||
WaitForSingleObject(pending_fn_event_, INFINITE);
|
fn();
|
||||||
ResetEvent(pending_fn_event_);
|
} else {
|
||||||
|
pending_fns_.push(std::move(fn));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::WorkerMain() {
|
void CommandProcessor::WorkerMain() {
|
||||||
|
@ -144,11 +142,10 @@ void CommandProcessor::WorkerMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (worker_running_) {
|
while (worker_running_) {
|
||||||
if (pending_fn_) {
|
while (!pending_fns_.empty()) {
|
||||||
auto fn = std::move(pending_fn_);
|
auto fn = std::move(pending_fns_.front());
|
||||||
pending_fn_ = nullptr;
|
pending_fns_.pop();
|
||||||
fn();
|
fn();
|
||||||
SetEvent(pending_fn_event_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t write_ptr_index = write_ptr_index_.load();
|
uint32_t write_ptr_index = write_ptr_index_.load();
|
||||||
|
@ -166,10 +163,10 @@ void CommandProcessor::WorkerMain() {
|
||||||
SwitchToThread();
|
SwitchToThread();
|
||||||
MemoryBarrier();
|
MemoryBarrier();
|
||||||
write_ptr_index = write_ptr_index_.load();
|
write_ptr_index = write_ptr_index_.load();
|
||||||
} while (!pending_fn_ && (write_ptr_index == 0xBAADF00D ||
|
} while (pending_fns_.empty() && (write_ptr_index == 0xBAADF00D ||
|
||||||
read_ptr_index_ == write_ptr_index));
|
read_ptr_index_ == write_ptr_index));
|
||||||
// ReturnFromWait();
|
// ReturnFromWait();
|
||||||
if (pending_fn_) {
|
if (!pending_fns_.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -212,8 +213,7 @@ class CommandProcessor {
|
||||||
std::atomic<bool> worker_running_;
|
std::atomic<bool> worker_running_;
|
||||||
std::unique_ptr<GLContext> context_;
|
std::unique_ptr<GLContext> context_;
|
||||||
SwapHandler swap_handler_;
|
SwapHandler swap_handler_;
|
||||||
std::function<void()> pending_fn_;
|
std::queue<std::function<void()>> pending_fns_;
|
||||||
HANDLE pending_fn_event_;
|
|
||||||
|
|
||||||
SwapMode swap_mode_;
|
SwapMode swap_mode_;
|
||||||
|
|
||||||
|
|
|
@ -126,11 +126,13 @@ void GL4GraphicsSystem::RequestSwap() {
|
||||||
command_processor_->CallInThread([&]() { command_processor_->IssueSwap(); });
|
command_processor_->CallInThread([&]() { command_processor_->IssueSwap(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data,
|
void GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
size_t trace_size,
|
|
||||||
TracePlaybackMode playback_mode) {
|
TracePlaybackMode playback_mode) {
|
||||||
|
command_processor_->CallInThread(
|
||||||
|
[this, trace_data, trace_size, playback_mode]() {
|
||||||
|
command_processor_->set_swap_mode(SwapMode::kIgnored);
|
||||||
|
|
||||||
auto trace_ptr = trace_data;
|
auto trace_ptr = trace_data;
|
||||||
command_processor_->CallInThread([&]() {
|
|
||||||
bool pending_break = false;
|
bool pending_break = false;
|
||||||
const PacketStartCommand* pending_packet = nullptr;
|
const PacketStartCommand* pending_packet = nullptr;
|
||||||
while (trace_ptr < trace_data + trace_size) {
|
while (trace_ptr < trace_data + trace_size) {
|
||||||
|
@ -152,8 +154,8 @@ const uint8_t* GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TraceCommandType::kIndirectBufferStart: {
|
case TraceCommandType::kIndirectBufferStart: {
|
||||||
auto cmd =
|
auto cmd = reinterpret_cast<const IndirectBufferStartCommand*>(
|
||||||
reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr);
|
trace_ptr);
|
||||||
//
|
//
|
||||||
trace_ptr += sizeof(*cmd) + cmd->count * 4;
|
trace_ptr += sizeof(*cmd) + cmd->count * 4;
|
||||||
break;
|
break;
|
||||||
|
@ -217,8 +219,9 @@ const uint8_t* GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
command_processor_->set_swap_mode(SwapMode::kNormal);
|
||||||
});
|
});
|
||||||
return trace_ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL4GraphicsSystem::MarkVblank() {
|
void GL4GraphicsSystem::MarkVblank() {
|
||||||
|
|
|
@ -38,7 +38,7 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
||||||
|
|
||||||
void RequestSwap() override;
|
void RequestSwap() override;
|
||||||
|
|
||||||
const uint8_t* PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode) override;
|
TracePlaybackMode playback_mode) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -91,6 +91,9 @@ LRESULT WGLControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||||
current_paint_callback_ = nullptr;
|
current_paint_callback_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
poly::ui::UIEvent e(this);
|
||||||
|
OnPaint(e);
|
||||||
|
|
||||||
// TODO(benvanik): profiler present.
|
// TODO(benvanik): profiler present.
|
||||||
Profiler::Present();
|
Profiler::Present();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,10 +47,8 @@ class GraphicsSystem {
|
||||||
kUntilEnd,
|
kUntilEnd,
|
||||||
kBreakOnSwap,
|
kBreakOnSwap,
|
||||||
};
|
};
|
||||||
virtual const uint8_t* PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
virtual void PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode) {
|
TracePlaybackMode playback_mode) {}
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GraphicsSystem();
|
GraphicsSystem();
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include "poly/main.h"
|
#include "poly/main.h"
|
||||||
#include "poly/mapped_memory.h"
|
#include "poly/mapped_memory.h"
|
||||||
|
#include "third_party/imgui/imgui.h"
|
||||||
|
#include "xenia/gpu/gl4/gl_context.h"
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
#include "xenia/gpu/tracing.h"
|
#include "xenia/gpu/tracing.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
|
@ -20,6 +23,271 @@ DEFINE_string(target_trace_file, "", "Specifies the trace file to load.");
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
// TODO(benvanik): move to tracing.h/cc
|
||||||
|
|
||||||
|
class TraceReader {
|
||||||
|
public:
|
||||||
|
struct Frame {
|
||||||
|
const uint8_t* start_ptr;
|
||||||
|
const uint8_t* end_ptr;
|
||||||
|
int command_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
TraceReader() : trace_data_(nullptr), trace_size_(0) {}
|
||||||
|
~TraceReader() = default;
|
||||||
|
|
||||||
|
const Frame* frame(int n) const { return &frames_[n]; }
|
||||||
|
int frame_count() const { return int(frames_.size()); }
|
||||||
|
|
||||||
|
bool Open(const std::wstring& path) {
|
||||||
|
Close();
|
||||||
|
|
||||||
|
mmap_ = poly::MappedMemory::Open(path, poly::MappedMemory::Mode::kRead);
|
||||||
|
if (!mmap_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_data_ = reinterpret_cast<const uint8_t*>(mmap_->data());
|
||||||
|
trace_size_ = mmap_->size();
|
||||||
|
|
||||||
|
ParseTrace();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
mmap_.reset();
|
||||||
|
trace_data_ = nullptr;
|
||||||
|
trace_size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// void Foo() {
|
||||||
|
// auto trace_ptr = trace_data;
|
||||||
|
// while (trace_ptr < trace_data + trace_size) {
|
||||||
|
// auto cmd_type = *reinterpret_cast<const TraceCommandType*>(trace_ptr);
|
||||||
|
// switch (cmd_type) {
|
||||||
|
// case TraceCommandType::kPrimaryBufferStart:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kPrimaryBufferEnd:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kIndirectBufferStart:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kIndirectBufferEnd:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kPacketStart:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kPacketEnd:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kMemoryRead:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kMemoryWrite:
|
||||||
|
// break;
|
||||||
|
// case TraceCommandType::kEvent:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// /*trace_ptr = graphics_system->PlayTrace(
|
||||||
|
// trace_ptr, trace_size - (trace_ptr - trace_data),
|
||||||
|
// GraphicsSystem::TracePlaybackMode::kBreakOnSwap);*/
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ParseTrace() {
|
||||||
|
auto trace_ptr = trace_data_;
|
||||||
|
Frame current_frame = {
|
||||||
|
trace_ptr, nullptr, 0,
|
||||||
|
};
|
||||||
|
bool pending_break = false;
|
||||||
|
while (trace_ptr < trace_data_ + trace_size_) {
|
||||||
|
++current_frame.command_count;
|
||||||
|
auto type =
|
||||||
|
static_cast<TraceCommandType>(poly::load<uint32_t>(trace_ptr));
|
||||||
|
switch (type) {
|
||||||
|
case TraceCommandType::kPrimaryBufferStart: {
|
||||||
|
auto cmd =
|
||||||
|
reinterpret_cast<const PrimaryBufferStartCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd) + cmd->count * 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kPrimaryBufferEnd: {
|
||||||
|
auto cmd =
|
||||||
|
reinterpret_cast<const PrimaryBufferEndCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kIndirectBufferStart: {
|
||||||
|
auto cmd =
|
||||||
|
reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd) + cmd->count * 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kIndirectBufferEnd: {
|
||||||
|
auto cmd =
|
||||||
|
reinterpret_cast<const IndirectBufferEndCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kPacketStart: {
|
||||||
|
auto cmd = reinterpret_cast<const PacketStartCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd) + cmd->count * 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kPacketEnd: {
|
||||||
|
auto cmd = reinterpret_cast<const PacketEndCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd);
|
||||||
|
if (pending_break) {
|
||||||
|
current_frame.end_ptr = trace_ptr;
|
||||||
|
frames_.push_back(std::move(current_frame));
|
||||||
|
current_frame.start_ptr = trace_ptr;
|
||||||
|
current_frame.end_ptr = nullptr;
|
||||||
|
current_frame.command_count = 0;
|
||||||
|
pending_break = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kMemoryRead: {
|
||||||
|
auto cmd = reinterpret_cast<const MemoryReadCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd) + cmd->length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kMemoryWrite: {
|
||||||
|
auto cmd = reinterpret_cast<const MemoryWriteCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd) + cmd->length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraceCommandType::kEvent: {
|
||||||
|
auto cmd = reinterpret_cast<const EventCommand*>(trace_ptr);
|
||||||
|
trace_ptr += sizeof(*cmd);
|
||||||
|
switch (cmd->event_type) {
|
||||||
|
case EventType::kSwap: {
|
||||||
|
pending_break = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pending_break) {
|
||||||
|
current_frame.end_ptr = trace_ptr;
|
||||||
|
frames_.push_back(std::move(current_frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<poly::MappedMemory> mmap_;
|
||||||
|
const uint8_t* trace_data_;
|
||||||
|
size_t trace_size_;
|
||||||
|
std::vector<Frame> frames_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TracePlayer : public TraceReader {
|
||||||
|
public:
|
||||||
|
TracePlayer(poly::ui::Loop* loop, GraphicsSystem* graphics_system)
|
||||||
|
: loop_(loop),
|
||||||
|
graphics_system_(graphics_system),
|
||||||
|
current_frame_index_(0),
|
||||||
|
current_command_index_(-1) {}
|
||||||
|
~TracePlayer() = default;
|
||||||
|
|
||||||
|
int current_frame_index() const { return current_frame_index_; }
|
||||||
|
|
||||||
|
const Frame* current_frame() const {
|
||||||
|
if (current_frame_index_ > frame_count()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return frame(current_frame_index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeekFrame(int target_frame) {
|
||||||
|
if (current_frame_index_ == target_frame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_frame_index_ = target_frame;
|
||||||
|
auto frame = current_frame();
|
||||||
|
current_command_index_ = frame->command_count - 1;
|
||||||
|
|
||||||
|
graphics_system_->PlayTrace(
|
||||||
|
frame->start_ptr, frame->end_ptr - frame->start_ptr,
|
||||||
|
GraphicsSystem::TracePlaybackMode::kBreakOnSwap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int current_command_index() const { return current_command_index_; }
|
||||||
|
|
||||||
|
void SeekCommand(int target_command) {
|
||||||
|
current_command_index_ = target_command;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
poly::ui::Loop* loop_;
|
||||||
|
GraphicsSystem* graphics_system_;
|
||||||
|
int current_frame_index_;
|
||||||
|
int current_command_index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void DrawUI(xe::ui::MainWindow* window, TracePlayer& player) {
|
||||||
|
ImGui::ShowTestWindow();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(5, 5), ImGuiSetCondition_FirstUseEver);
|
||||||
|
if (ImGui::Begin("Controller", nullptr, ImVec2(0, 0), -1.0f,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
int target_frame = player.current_frame_index();
|
||||||
|
if (ImGui::Button("|<<")) {
|
||||||
|
target_frame = 0;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(">>", ImVec2(0, 0), true)) {
|
||||||
|
if (target_frame + 1 < player.frame_count()) {
|
||||||
|
++target_frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(">>|")) {
|
||||||
|
target_frame = player.frame_count() - 1;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SliderInt("", &target_frame, 0, player.frame_count() - 1);
|
||||||
|
if (target_frame != player.current_frame_index()) {
|
||||||
|
player.SeekFrame(target_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 5),
|
||||||
|
ImGuiSetCondition_FirstUseEver);
|
||||||
|
if (ImGui::Begin("Frame Inspector", nullptr, ImVec2(500, 300))) {
|
||||||
|
ImGui::Columns(2, "frame_inspector");
|
||||||
|
ImGui::Text("Frame #%d", player.current_frame_index());
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::BeginChild("Sub1");
|
||||||
|
ImGui::PushID(-1);
|
||||||
|
bool is_selected = player.current_command_index() == -1;
|
||||||
|
if (ImGui::Selectable("<start>", &is_selected)) {
|
||||||
|
player.SeekCommand(-1);
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
int column_width = int(ImGui::GetContentRegionMax().x);
|
||||||
|
auto frame = player.current_frame();
|
||||||
|
for (int i = 0; i < frame->command_count; ++i) {
|
||||||
|
ImGui::PushID(i);
|
||||||
|
is_selected = i == player.current_command_index();
|
||||||
|
if (ImGui::Selectable("command", &is_selected)) {
|
||||||
|
player.SeekCommand(i);
|
||||||
|
}
|
||||||
|
ImGui::SameLine(column_width - 30);
|
||||||
|
ImGui::Text("bar");
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::Text("right side");
|
||||||
|
ImGui::Columns(1);
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImImpl_Setup();
|
||||||
|
void ImImpl_Shutdown();
|
||||||
|
|
||||||
int trace_viewer_main(std::vector<std::wstring>& args) {
|
int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
// Create the emulator.
|
// Create the emulator.
|
||||||
auto emulator = std::make_unique<Emulator>(L"");
|
auto emulator = std::make_unique<Emulator>(L"");
|
||||||
|
@ -42,30 +310,336 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
path = args[1];
|
path = args[1];
|
||||||
}
|
}
|
||||||
// Normalize the path and make absolute.
|
// Normalize the path and make absolute.
|
||||||
std::wstring abs_path = poly::to_absolute_path(path);
|
auto abs_path = poly::to_absolute_path(path);
|
||||||
|
|
||||||
|
auto window = emulator->main_window();
|
||||||
|
auto loop = window->loop();
|
||||||
|
auto file_name = poly::find_name_from_path(path);
|
||||||
|
window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
|
||||||
|
|
||||||
// TODO(benvanik): UI? replay control on graphics system?
|
|
||||||
auto graphics_system = emulator->graphics_system();
|
auto graphics_system = emulator->graphics_system();
|
||||||
auto mmap =
|
|
||||||
poly::MappedMemory::Open(abs_path, poly::MappedMemory::Mode::kRead);
|
|
||||||
auto trace_data = reinterpret_cast<const uint8_t*>(mmap->data());
|
|
||||||
auto trace_size = mmap->size();
|
|
||||||
|
|
||||||
auto trace_ptr = trace_data;
|
TracePlayer player(loop, emulator->graphics_system());
|
||||||
while (trace_ptr < trace_data + trace_size) {
|
if (!player.Open(abs_path)) {
|
||||||
trace_ptr = graphics_system->PlayTrace(
|
XELOGE("Could not load trace file");
|
||||||
trace_ptr, trace_size - (trace_ptr - trace_data),
|
return 1;
|
||||||
GraphicsSystem::TracePlaybackMode::kBreakOnSwap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto control = window->child(0);
|
||||||
|
control->on_key_down.AddListener([](poly::ui::KeyEvent& e) {});
|
||||||
|
control->on_key_up.AddListener([](poly::ui::KeyEvent& e) {});
|
||||||
|
control->on_mouse_down.AddListener([](poly::ui::MouseEvent& e) {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
|
switch (e.button()) {
|
||||||
|
case poly::ui::MouseEvent::Button::kLeft:
|
||||||
|
io.MouseDown[0] = true;
|
||||||
|
break;
|
||||||
|
case poly::ui::MouseEvent::Button::kRight:
|
||||||
|
io.MouseDown[1] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
control->on_mouse_move.AddListener([](poly::ui::MouseEvent& e) {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
|
});
|
||||||
|
control->on_mouse_up.AddListener([](poly::ui::MouseEvent& e) {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
|
switch (e.button()) {
|
||||||
|
case poly::ui::MouseEvent::Button::kLeft:
|
||||||
|
io.MouseDown[0] = false;
|
||||||
|
break;
|
||||||
|
case poly::ui::MouseEvent::Button::kRight:
|
||||||
|
io.MouseDown[1] = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
control->on_mouse_wheel.AddListener([](poly::ui::MouseEvent& e) {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
|
io.MouseWheel += float(e.dy() / 120.0f);
|
||||||
|
});
|
||||||
|
|
||||||
|
control->on_paint.AddListener([&](poly::ui::UIEvent& e) {
|
||||||
|
static bool imgui_setup = false;
|
||||||
|
if (!imgui_setup) {
|
||||||
|
ImImpl_Setup();
|
||||||
|
imgui_setup = true;
|
||||||
|
}
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
auto current_ticks = poly::threading::ticks();
|
||||||
|
static uint64_t last_ticks = 0;
|
||||||
|
io.DeltaTime = (current_ticks - last_ticks) /
|
||||||
|
float(poly::threading::ticks_per_second());
|
||||||
|
last_ticks = current_ticks;
|
||||||
|
|
||||||
|
io.DisplaySize =
|
||||||
|
ImVec2(float(e.control()->width()), float(e.control()->height()));
|
||||||
|
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
DrawUI(window, player);
|
||||||
|
|
||||||
|
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||||
|
ImGui::Render();
|
||||||
|
|
||||||
|
graphics_system->RequestSwap();
|
||||||
|
});
|
||||||
|
graphics_system->RequestSwap();
|
||||||
|
|
||||||
// Wait until we are exited.
|
// Wait until we are exited.
|
||||||
emulator->main_window()->loop()->AwaitQuit();
|
emulator->main_window()->loop()->AwaitQuit();
|
||||||
|
|
||||||
|
ImImpl_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.reset();
|
emulator.reset();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(benvanik): move to another file.
|
||||||
|
|
||||||
|
extern "C" GLEWContext* glewGetContext();
|
||||||
|
extern "C" WGLEWContext* wglewGetContext();
|
||||||
|
|
||||||
|
static int shader_handle, vert_handle, frag_handle;
|
||||||
|
static int texture_location, proj_mtx_location;
|
||||||
|
static int position_location, uv_location, colour_location;
|
||||||
|
static size_t vbo_max_size = 20000;
|
||||||
|
static unsigned int vbo_handle, vao_handle;
|
||||||
|
void ImImpl_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count);
|
||||||
|
void ImImpl_Setup() {
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
const GLchar* vertex_shader =
|
||||||
|
"#version 330\n"
|
||||||
|
"uniform mat4 ProjMtx;\n"
|
||||||
|
"in vec2 Position;\n"
|
||||||
|
"in vec2 UV;\n"
|
||||||
|
"in vec4 Color;\n"
|
||||||
|
"out vec2 Frag_UV;\n"
|
||||||
|
"out vec4 Frag_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" Frag_UV = UV;\n"
|
||||||
|
" Frag_Color = Color;\n"
|
||||||
|
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const GLchar* fragment_shader =
|
||||||
|
"#version 330\n"
|
||||||
|
"uniform sampler2D Texture;\n"
|
||||||
|
"in vec2 Frag_UV;\n"
|
||||||
|
"in vec4 Frag_Color;\n"
|
||||||
|
"out vec4 Out_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
shader_handle = glCreateProgram();
|
||||||
|
vert_handle = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(vert_handle, 1, &vertex_shader, 0);
|
||||||
|
glShaderSource(frag_handle, 1, &fragment_shader, 0);
|
||||||
|
glCompileShader(vert_handle);
|
||||||
|
glCompileShader(frag_handle);
|
||||||
|
glAttachShader(shader_handle, vert_handle);
|
||||||
|
glAttachShader(shader_handle, frag_handle);
|
||||||
|
glLinkProgram(shader_handle);
|
||||||
|
|
||||||
|
texture_location = glGetUniformLocation(shader_handle, "Texture");
|
||||||
|
proj_mtx_location = glGetUniformLocation(shader_handle, "ProjMtx");
|
||||||
|
position_location = glGetAttribLocation(shader_handle, "Position");
|
||||||
|
uv_location = glGetAttribLocation(shader_handle, "UV");
|
||||||
|
colour_location = glGetAttribLocation(shader_handle, "Color");
|
||||||
|
|
||||||
|
glGenBuffers(1, &vbo_handle);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_handle);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vbo_max_size, NULL, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &vao_handle);
|
||||||
|
glBindVertexArray(vao_handle);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_handle);
|
||||||
|
glEnableVertexAttribArray(position_location);
|
||||||
|
glEnableVertexAttribArray(uv_location);
|
||||||
|
glEnableVertexAttribArray(colour_location);
|
||||||
|
|
||||||
|
glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE,
|
||||||
|
sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos));
|
||||||
|
glVertexAttribPointer(uv_location, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert),
|
||||||
|
(GLvoid*)offsetof(ImDrawVert, uv));
|
||||||
|
glVertexAttribPointer(colour_location, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
||||||
|
sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col));
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glDisableVertexAttribArray(position_location);
|
||||||
|
glDisableVertexAttribArray(uv_location);
|
||||||
|
glDisableVertexAttribArray(colour_location);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(
|
||||||
|
&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo
|
||||||
|
// because it is more likely to be compatible
|
||||||
|
// with user's existing shader.
|
||||||
|
|
||||||
|
GLuint tex_id;
|
||||||
|
glCreateTextures(GL_TEXTURE_2D, 1, &tex_id);
|
||||||
|
glTextureParameteri(tex_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTextureParameteri(tex_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTextureStorage2D(tex_id, 1, GL_RGBA8, width, height);
|
||||||
|
glTextureSubImage2D(tex_id, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||||
|
pixels);
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->TexID = (void*)(intptr_t)tex_id;
|
||||||
|
|
||||||
|
io.DeltaTime = 1.0f / 60.0f;
|
||||||
|
io.RenderDrawListsFn = ImImpl_RenderDrawLists;
|
||||||
|
|
||||||
|
auto& style = ImGui::GetStyle();
|
||||||
|
style.WindowRounding = 0;
|
||||||
|
|
||||||
|
style.Colors[ImGuiCol_Text] = ImVec4(0.89f, 0.90f, 0.90f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
style.Colors[ImGuiCol_Border] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f);
|
||||||
|
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.22f);
|
||||||
|
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 1.00f, 0.00f, 0.78f);
|
||||||
|
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.58f, 0.00f, 0.61f);
|
||||||
|
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.00f, 0.40f, 0.11f, 0.59f);
|
||||||
|
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.00f, 0.68f, 0.00f, 0.68f);
|
||||||
|
style.Colors[ImGuiCol_ScrollbarGrabHovered] =
|
||||||
|
ImVec4(0.00f, 1.00f, 0.15f, 0.62f);
|
||||||
|
style.Colors[ImGuiCol_ScrollbarGrabActive] =
|
||||||
|
ImVec4(0.00f, 0.91f, 0.09f, 0.40f);
|
||||||
|
style.Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
|
||||||
|
style.Colors[ImGuiCol_CheckHovered] = ImVec4(0.23f, 0.64f, 0.13f, 0.45f);
|
||||||
|
style.Colors[ImGuiCol_CheckActive] = ImVec4(0.21f, 0.93f, 0.13f, 0.55f);
|
||||||
|
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.74f, 0.90f, 0.72f, 0.50f);
|
||||||
|
style.Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
|
||||||
|
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.34f, 0.75f, 0.11f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_Button] = ImVec4(0.15f, 0.56f, 0.11f, 0.60f);
|
||||||
|
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.72f, 0.09f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.19f, 0.60f, 0.09f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_Header] = ImVec4(0.00f, 0.40f, 0.00f, 0.71f);
|
||||||
|
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.60f, 0.26f, 0.80f);
|
||||||
|
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.00f, 0.75f, 0.00f, 0.80f);
|
||||||
|
style.Colors[ImGuiCol_Column] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.36f, 0.89f, 0.38f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.13f, 0.50f, 0.11f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
|
||||||
|
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
|
||||||
|
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
|
||||||
|
style.Colors[ImGuiCol_CloseButton] = ImVec4(0.00f, 0.72f, 0.00f, 0.96f);
|
||||||
|
style.Colors[ImGuiCol_CloseButtonHovered] =
|
||||||
|
ImVec4(0.38f, 1.00f, 0.42f, 0.60f);
|
||||||
|
style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.56f, 1.00f, 0.64f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_PlotHistogramHovered] =
|
||||||
|
ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||||
|
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
|
||||||
|
style.Colors[ImGuiCol_TooltipBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
|
||||||
|
}
|
||||||
|
void ImImpl_Shutdown() {
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (vao_handle) glDeleteVertexArrays(1, &vao_handle);
|
||||||
|
if (vbo_handle) glDeleteBuffers(1, &vbo_handle);
|
||||||
|
glDetachShader(shader_handle, vert_handle);
|
||||||
|
glDetachShader(shader_handle, frag_handle);
|
||||||
|
glDeleteShader(vert_handle);
|
||||||
|
glDeleteShader(frag_handle);
|
||||||
|
glDeleteProgram(shader_handle);
|
||||||
|
auto tex_id = static_cast<GLuint>(intptr_t(io.Fonts->TexID));
|
||||||
|
glDeleteTextures(1, &tex_id);
|
||||||
|
ImGui::Shutdown();
|
||||||
|
}
|
||||||
|
void ImImpl_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) {
|
||||||
|
if (cmd_lists_count == 0) return;
|
||||||
|
|
||||||
|
// Setup render state: alpha-blending enabled, no face culling, no depth
|
||||||
|
// testing, scissor enabled
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
// Setup orthographic projection matrix
|
||||||
|
const float width = ImGui::GetIO().DisplaySize.x;
|
||||||
|
const float height = ImGui::GetIO().DisplaySize.y;
|
||||||
|
const float ortho_projection[4][4] = {
|
||||||
|
{2.0f / width, 0.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 2.0f / -height, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||||
|
{-1.0f, 1.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
glProgramUniform1i(shader_handle, texture_location, 0);
|
||||||
|
glProgramUniformMatrix4fv(shader_handle, proj_mtx_location, 1, GL_FALSE,
|
||||||
|
&ortho_projection[0][0]);
|
||||||
|
|
||||||
|
// Grow our buffer according to what we need
|
||||||
|
size_t total_vtx_count = 0;
|
||||||
|
for (int n = 0; n < cmd_lists_count; n++)
|
||||||
|
total_vtx_count += cmd_lists[n]->vtx_buffer.size();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_handle);
|
||||||
|
size_t neededBufferSize = total_vtx_count * sizeof(ImDrawVert);
|
||||||
|
if (neededBufferSize > vbo_max_size) {
|
||||||
|
vbo_max_size = neededBufferSize + 5000; // Grow buffer
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vbo_max_size, NULL, GL_STREAM_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and convert all vertices into a single contiguous buffer
|
||||||
|
unsigned char* buffer_data =
|
||||||
|
(unsigned char*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
||||||
|
if (!buffer_data) return;
|
||||||
|
for (int n = 0; n < cmd_lists_count; n++) {
|
||||||
|
const ImDrawList* cmd_list = cmd_lists[n];
|
||||||
|
memcpy(buffer_data, &cmd_list->vtx_buffer[0],
|
||||||
|
cmd_list->vtx_buffer.size() * sizeof(ImDrawVert));
|
||||||
|
buffer_data += cmd_list->vtx_buffer.size() * sizeof(ImDrawVert);
|
||||||
|
}
|
||||||
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glBindVertexArray(vao_handle);
|
||||||
|
glUseProgram(shader_handle);
|
||||||
|
|
||||||
|
int cmd_offset = 0;
|
||||||
|
ImTextureID prev_texture_id = 0;
|
||||||
|
for (int n = 0; n < cmd_lists_count; n++) {
|
||||||
|
const ImDrawList* cmd_list = cmd_lists[n];
|
||||||
|
int vtx_offset = cmd_offset;
|
||||||
|
const ImDrawCmd* pcmd_end = cmd_list->commands.end();
|
||||||
|
for (const ImDrawCmd* pcmd = cmd_list->commands.begin(); pcmd != pcmd_end;
|
||||||
|
pcmd++) {
|
||||||
|
if (pcmd->texture_id != prev_texture_id) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->texture_id);
|
||||||
|
prev_texture_id = pcmd->texture_id;
|
||||||
|
}
|
||||||
|
glScissor((int)pcmd->clip_rect.x, (int)(height - pcmd->clip_rect.w),
|
||||||
|
(int)(pcmd->clip_rect.z - pcmd->clip_rect.x),
|
||||||
|
(int)(pcmd->clip_rect.w - pcmd->clip_rect.y));
|
||||||
|
glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count);
|
||||||
|
vtx_offset += pcmd->vtx_count;
|
||||||
|
}
|
||||||
|
cmd_offset = vtx_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore modified state
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glUseProgram(0);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue