graphics_framebuffer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <QBoxLayout>
  5. #include <QComboBox>
  6. #include <QDebug>
  7. #include <QLabel>
  8. #include <QPushButton>
  9. #include <QSpinBox>
  10. #include "citra_qt/debugger/graphics_framebuffer.h"
  11. #include "citra_qt/util/spinbox.h"
  12. #include "common/color.h"
  13. #include "core/memory.h"
  14. #include "core/hw/gpu.h"
  15. #include "video_core/pica.h"
  16. #include "video_core/utils.h"
  17. GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
  18. QWidget* parent)
  19. : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
  20. framebuffer_source(Source::PicaTarget)
  21. {
  22. setObjectName("PicaFramebuffer");
  23. framebuffer_source_list = new QComboBox;
  24. framebuffer_source_list->addItem(tr("Active Render Target"));
  25. framebuffer_source_list->addItem(tr("Active Depth Buffer"));
  26. framebuffer_source_list->addItem(tr("Custom"));
  27. framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
  28. framebuffer_address_control = new CSpinBox;
  29. framebuffer_address_control->SetBase(16);
  30. framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
  31. framebuffer_address_control->SetPrefix("0x");
  32. framebuffer_width_control = new QSpinBox;
  33. framebuffer_width_control->setMinimum(1);
  34. framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
  35. framebuffer_height_control = new QSpinBox;
  36. framebuffer_height_control->setMinimum(1);
  37. framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
  38. framebuffer_format_control = new QComboBox;
  39. framebuffer_format_control->addItem(tr("RGBA8"));
  40. framebuffer_format_control->addItem(tr("RGB8"));
  41. framebuffer_format_control->addItem(tr("RGB5A1"));
  42. framebuffer_format_control->addItem(tr("RGB565"));
  43. framebuffer_format_control->addItem(tr("RGBA4"));
  44. framebuffer_format_control->addItem(tr("D16"));
  45. framebuffer_format_control->addItem(tr("D24"));
  46. framebuffer_format_control->addItem(tr("D24X8"));
  47. framebuffer_format_control->addItem(tr("X24S8"));
  48. framebuffer_format_control->addItem(tr("(unknown)"));
  49. // TODO: This QLabel should shrink the image to the available space rather than just expanding...
  50. framebuffer_picture_label = new QLabel;
  51. auto enlarge_button = new QPushButton(tr("Enlarge"));
  52. // Connections
  53. connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
  54. connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
  55. connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
  56. connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
  57. connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
  58. connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
  59. auto main_widget = new QWidget;
  60. auto main_layout = new QVBoxLayout;
  61. {
  62. auto sub_layout = new QHBoxLayout;
  63. sub_layout->addWidget(new QLabel(tr("Source:")));
  64. sub_layout->addWidget(framebuffer_source_list);
  65. main_layout->addLayout(sub_layout);
  66. }
  67. {
  68. auto sub_layout = new QHBoxLayout;
  69. sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
  70. sub_layout->addWidget(framebuffer_address_control);
  71. main_layout->addLayout(sub_layout);
  72. }
  73. {
  74. auto sub_layout = new QHBoxLayout;
  75. sub_layout->addWidget(new QLabel(tr("Width:")));
  76. sub_layout->addWidget(framebuffer_width_control);
  77. main_layout->addLayout(sub_layout);
  78. }
  79. {
  80. auto sub_layout = new QHBoxLayout;
  81. sub_layout->addWidget(new QLabel(tr("Height:")));
  82. sub_layout->addWidget(framebuffer_height_control);
  83. main_layout->addLayout(sub_layout);
  84. }
  85. {
  86. auto sub_layout = new QHBoxLayout;
  87. sub_layout->addWidget(new QLabel(tr("Format:")));
  88. sub_layout->addWidget(framebuffer_format_control);
  89. main_layout->addLayout(sub_layout);
  90. }
  91. main_layout->addWidget(framebuffer_picture_label);
  92. main_layout->addWidget(enlarge_button);
  93. main_widget->setLayout(main_layout);
  94. setWidget(main_widget);
  95. // Load current data - TODO: Make sure this works when emulation is not running
  96. if (debug_context && debug_context->at_breakpoint)
  97. emit Update();
  98. widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
  99. }
  100. void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
  101. {
  102. emit Update();
  103. widget()->setEnabled(true);
  104. }
  105. void GraphicsFramebufferWidget::OnResumed()
  106. {
  107. widget()->setEnabled(false);
  108. }
  109. void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
  110. {
  111. framebuffer_source = static_cast<Source>(new_value);
  112. emit Update();
  113. }
  114. void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
  115. {
  116. if (framebuffer_address != new_value) {
  117. framebuffer_address = static_cast<unsigned>(new_value);
  118. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  119. emit Update();
  120. }
  121. }
  122. void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
  123. {
  124. if (framebuffer_width != static_cast<unsigned>(new_value)) {
  125. framebuffer_width = static_cast<unsigned>(new_value);
  126. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  127. emit Update();
  128. }
  129. }
  130. void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
  131. {
  132. if (framebuffer_height != static_cast<unsigned>(new_value)) {
  133. framebuffer_height = static_cast<unsigned>(new_value);
  134. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  135. emit Update();
  136. }
  137. }
  138. void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
  139. {
  140. if (framebuffer_format != static_cast<Format>(new_value)) {
  141. framebuffer_format = static_cast<Format>(new_value);
  142. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  143. emit Update();
  144. }
  145. }
  146. void GraphicsFramebufferWidget::OnUpdate()
  147. {
  148. QPixmap pixmap;
  149. switch (framebuffer_source) {
  150. case Source::PicaTarget:
  151. {
  152. // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
  153. const auto& framebuffer = Pica::g_state.regs.framebuffer;
  154. framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
  155. framebuffer_width = framebuffer.GetWidth();
  156. framebuffer_height = framebuffer.GetHeight();
  157. switch (framebuffer.color_format) {
  158. case Pica::Regs::ColorFormat::RGBA8:
  159. framebuffer_format = Format::RGBA8;
  160. break;
  161. case Pica::Regs::ColorFormat::RGB8:
  162. framebuffer_format = Format::RGB8;
  163. break;
  164. case Pica::Regs::ColorFormat::RGB5A1:
  165. framebuffer_format = Format::RGB5A1;
  166. break;
  167. case Pica::Regs::ColorFormat::RGB565:
  168. framebuffer_format = Format::RGB565;
  169. break;
  170. case Pica::Regs::ColorFormat::RGBA4:
  171. framebuffer_format = Format::RGBA4;
  172. break;
  173. default:
  174. framebuffer_format = Format::Unknown;
  175. break;
  176. }
  177. break;
  178. }
  179. case Source::DepthBuffer:
  180. {
  181. const auto& framebuffer = Pica::g_state.regs.framebuffer;
  182. framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
  183. framebuffer_width = framebuffer.GetWidth();
  184. framebuffer_height = framebuffer.GetHeight();
  185. switch (framebuffer.depth_format) {
  186. case Pica::Regs::DepthFormat::D16:
  187. framebuffer_format = Format::D16;
  188. break;
  189. case Pica::Regs::DepthFormat::D24:
  190. framebuffer_format = Format::D24;
  191. break;
  192. case Pica::Regs::DepthFormat::D24S8:
  193. framebuffer_format = Format::D24X8;
  194. break;
  195. default:
  196. framebuffer_format = Format::Unknown;
  197. break;
  198. }
  199. break;
  200. }
  201. case Source::Custom:
  202. {
  203. // Keep user-specified values
  204. break;
  205. }
  206. default:
  207. qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
  208. break;
  209. }
  210. // TODO: Implement a good way to visualize alpha components!
  211. // TODO: Unify this decoding code with the texture decoder
  212. u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format);
  213. QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
  214. u8* buffer = Memory::GetPhysicalPointer(framebuffer_address);
  215. for (unsigned int y = 0; y < framebuffer_height; ++y) {
  216. for (unsigned int x = 0; x < framebuffer_width; ++x) {
  217. const u32 coarse_y = y & ~7;
  218. u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
  219. const u8* pixel = buffer + offset;
  220. Math::Vec4<u8> color = { 0, 0, 0, 0 };
  221. switch (framebuffer_format) {
  222. case Format::RGBA8:
  223. color = Color::DecodeRGBA8(pixel);
  224. break;
  225. case Format::RGB8:
  226. color = Color::DecodeRGB8(pixel);
  227. break;
  228. case Format::RGB5A1:
  229. color = Color::DecodeRGB5A1(pixel);
  230. break;
  231. case Format::RGB565:
  232. color = Color::DecodeRGB565(pixel);
  233. break;
  234. case Format::RGBA4:
  235. color = Color::DecodeRGBA4(pixel);
  236. break;
  237. case Format::D16:
  238. {
  239. u32 data = Color::DecodeD16(pixel);
  240. color.r() = data & 0xFF;
  241. color.g() = (data >> 8) & 0xFF;
  242. break;
  243. }
  244. case Format::D24:
  245. {
  246. u32 data = Color::DecodeD24(pixel);
  247. color.r() = data & 0xFF;
  248. color.g() = (data >> 8) & 0xFF;
  249. color.b() = (data >> 16) & 0xFF;
  250. break;
  251. }
  252. case Format::D24X8:
  253. {
  254. Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
  255. color.r() = data.x & 0xFF;
  256. color.g() = (data.x >> 8) & 0xFF;
  257. color.b() = (data.x >> 16) & 0xFF;
  258. break;
  259. }
  260. case Format::X24S8:
  261. {
  262. Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
  263. color.r() = color.g() = color.b() = data.y;
  264. break;
  265. }
  266. default:
  267. qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
  268. break;
  269. }
  270. decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
  271. }
  272. }
  273. pixmap = QPixmap::fromImage(decoded_image);
  274. framebuffer_address_control->SetValue(framebuffer_address);
  275. framebuffer_width_control->setValue(framebuffer_width);
  276. framebuffer_height_control->setValue(framebuffer_height);
  277. framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
  278. framebuffer_picture_label->setPixmap(pixmap);
  279. }
  280. u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
  281. switch (format) {
  282. case Format::RGBA8:
  283. case Format::D24X8:
  284. case Format::X24S8:
  285. return 4;
  286. case Format::RGB8:
  287. case Format::D24:
  288. return 3;
  289. case Format::RGB5A1:
  290. case Format::RGB565:
  291. case Format::RGBA4:
  292. case Format::D16:
  293. return 2;
  294. }
  295. }