gpu_debugger.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <algorithm>
  6. #include <functional>
  7. #include <vector>
  8. #include "common/log.h"
  9. #include "core/hle/service/gsp.h"
  10. #include "pica.h"
  11. class GraphicsDebugger
  12. {
  13. public:
  14. // A few utility structs used to expose data
  15. // A vector of commands represented by their raw byte sequence
  16. struct PicaCommand : public std::vector<u32>
  17. {
  18. const Pica::CommandHeader& GetHeader() const
  19. {
  20. const u32& val = at(1);
  21. return *(Pica::CommandHeader*)&val;
  22. }
  23. };
  24. typedef std::vector<PicaCommand> PicaCommandList;
  25. // Base class for all objects which need to be notified about GPU events
  26. class DebuggerObserver
  27. {
  28. public:
  29. DebuggerObserver() : observed(nullptr) { }
  30. virtual ~DebuggerObserver()
  31. {
  32. if (observed)
  33. observed->UnregisterObserver(this);
  34. }
  35. /**
  36. * Called when a GX command has been processed and is ready for being
  37. * read via GraphicsDebugger::ReadGXCommandHistory.
  38. * @param total_command_count Total number of commands in the GX history
  39. * @note All methods in this class are called from the GSP thread
  40. */
  41. virtual void GXCommandProcessed(int total_command_count)
  42. {
  43. const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1);
  44. ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value());
  45. }
  46. /**
  47. * @param lst command list which triggered this call
  48. * @param is_new true if the command list was called for the first time
  49. * @todo figure out how to make sure called functions don't keep references around beyond their life time
  50. */
  51. virtual void OnCommandListCalled(const PicaCommandList& lst, bool is_new)
  52. {
  53. ERROR_LOG(GSP, "Command list called: %d", (int)is_new);
  54. }
  55. protected:
  56. const GraphicsDebugger* GetDebugger() const
  57. {
  58. return observed;
  59. }
  60. private:
  61. GraphicsDebugger* observed;
  62. bool in_destruction;
  63. friend class GraphicsDebugger;
  64. };
  65. void GXCommandProcessed(u8* command_data)
  66. {
  67. if (observers.empty())
  68. return;
  69. gx_command_history.push_back(GSP_GPU::Command());
  70. GSP_GPU::Command& cmd = gx_command_history[gx_command_history.size()-1];
  71. memcpy(&cmd, command_data, sizeof(GSP_GPU::Command));
  72. ForEachObserver([this](DebuggerObserver* observer) {
  73. observer->GXCommandProcessed(this->gx_command_history.size());
  74. } );
  75. }
  76. void CommandListCalled(u32 address, u32* command_list, u32 size_in_words)
  77. {
  78. if (observers.empty())
  79. return;
  80. PicaCommandList cmdlist;
  81. for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;)
  82. {
  83. const Pica::CommandHeader header = static_cast<Pica::CommandHeader>(parse_pointer[1]);
  84. cmdlist.push_back(PicaCommand());
  85. auto& cmd = cmdlist.back();
  86. size_t size = 2 + header.extra_data_length;
  87. size = (size + 1) / 2 * 2; // align to 8 bytes
  88. cmd.reserve(size);
  89. std::copy(parse_pointer, parse_pointer + size, std::back_inserter(cmd));
  90. parse_pointer += size;
  91. }
  92. auto obj = std::pair<u32,PicaCommandList>(address, cmdlist);
  93. auto it = std::find(command_lists.begin(), command_lists.end(), obj);
  94. bool is_new = (it == command_lists.end());
  95. if (is_new)
  96. command_lists.push_back(obj);
  97. ForEachObserver([&](DebuggerObserver* observer) {
  98. observer->OnCommandListCalled(obj.second, is_new);
  99. } );
  100. }
  101. const GSP_GPU::Command& ReadGXCommandHistory(int index) const
  102. {
  103. // TODO: Is this thread-safe?
  104. return gx_command_history[index];
  105. }
  106. const std::vector<std::pair<u32,PicaCommandList>>& GetCommandLists() const
  107. {
  108. return command_lists;
  109. }
  110. void RegisterObserver(DebuggerObserver* observer)
  111. {
  112. // TODO: Check for duplicates
  113. observers.push_back(observer);
  114. observer->observed = this;
  115. }
  116. void UnregisterObserver(DebuggerObserver* observer)
  117. {
  118. std::remove(observers.begin(), observers.end(), observer);
  119. observer->observed = nullptr;
  120. }
  121. private:
  122. void ForEachObserver(std::function<void (DebuggerObserver*)> func)
  123. {
  124. std::for_each(observers.begin(),observers.end(), func);
  125. }
  126. std::vector<DebuggerObserver*> observers;
  127. std::vector<GSP_GPU::Command> gx_command_history;
  128. // vector of pairs of command lists and their storage address
  129. std::vector<std::pair<u32,PicaCommandList>> command_lists;
  130. };