graphics_framebuffer.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 "common/color.h"
  11. #include "core/hw/gpu.h"
  12. #include "core/memory.h"
  13. #include "video_core/pica.h"
  14. #include "video_core/utils.h"
  15. #include "graphics_framebuffer.h"
  16. #include "util/spinbox.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("D24S8"));
  47. // TODO: This QLabel should shrink the image to the available space rather than just expanding...
  48. framebuffer_picture_label = new QLabel;
  49. auto enlarge_button = new QPushButton(tr("Enlarge"));
  50. // Connections
  51. connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
  52. connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
  53. connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
  54. connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
  55. connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
  56. connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
  57. auto main_widget = new QWidget;
  58. auto main_layout = new QVBoxLayout;
  59. {
  60. auto sub_layout = new QHBoxLayout;
  61. sub_layout->addWidget(new QLabel(tr("Source:")));
  62. sub_layout->addWidget(framebuffer_source_list);
  63. main_layout->addLayout(sub_layout);
  64. }
  65. {
  66. auto sub_layout = new QHBoxLayout;
  67. sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
  68. sub_layout->addWidget(framebuffer_address_control);
  69. main_layout->addLayout(sub_layout);
  70. }
  71. {
  72. auto sub_layout = new QHBoxLayout;
  73. sub_layout->addWidget(new QLabel(tr("Width:")));
  74. sub_layout->addWidget(framebuffer_width_control);
  75. main_layout->addLayout(sub_layout);
  76. }
  77. {
  78. auto sub_layout = new QHBoxLayout;
  79. sub_layout->addWidget(new QLabel(tr("Height:")));
  80. sub_layout->addWidget(framebuffer_height_control);
  81. main_layout->addLayout(sub_layout);
  82. }
  83. {
  84. auto sub_layout = new QHBoxLayout;
  85. sub_layout->addWidget(new QLabel(tr("Format:")));
  86. sub_layout->addWidget(framebuffer_format_control);
  87. main_layout->addLayout(sub_layout);
  88. }
  89. main_layout->addWidget(framebuffer_picture_label);
  90. main_layout->addWidget(enlarge_button);
  91. main_widget->setLayout(main_layout);
  92. setWidget(main_widget);
  93. // Load current data - TODO: Make sure this works when emulation is not running
  94. if (debug_context && debug_context->at_breakpoint)
  95. emit Update();
  96. widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
  97. }
  98. void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
  99. {
  100. emit Update();
  101. widget()->setEnabled(true);
  102. }
  103. void GraphicsFramebufferWidget::OnResumed()
  104. {
  105. widget()->setEnabled(false);
  106. }
  107. void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
  108. {
  109. framebuffer_source = static_cast<Source>(new_value);
  110. emit Update();
  111. }
  112. void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
  113. {
  114. if (framebuffer_address != new_value) {
  115. framebuffer_address = static_cast<unsigned>(new_value);
  116. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  117. emit Update();
  118. }
  119. }
  120. void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
  121. {
  122. if (framebuffer_width != static_cast<unsigned>(new_value)) {
  123. framebuffer_width = static_cast<unsigned>(new_value);
  124. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  125. emit Update();
  126. }
  127. }
  128. void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
  129. {
  130. if (framebuffer_height != static_cast<unsigned>(new_value)) {
  131. framebuffer_height = static_cast<unsigned>(new_value);
  132. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  133. emit Update();
  134. }
  135. }
  136. void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
  137. {
  138. if (framebuffer_format != static_cast<Format>(new_value)) {
  139. framebuffer_format = static_cast<Format>(new_value);
  140. framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
  141. emit Update();
  142. }
  143. }
  144. void GraphicsFramebufferWidget::OnUpdate()
  145. {
  146. QPixmap pixmap;
  147. switch (framebuffer_source) {
  148. case Source::PicaTarget:
  149. {
  150. // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
  151. const auto& framebuffer = Pica::g_state.regs.framebuffer;
  152. framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
  153. framebuffer_width = framebuffer.GetWidth();
  154. framebuffer_height = framebuffer.GetHeight();
  155. // TODO: It's unknown how this format is actually specified
  156. framebuffer_format = Format::RGBA8;
  157. break;
  158. }
  159. case Source::DepthBuffer:
  160. {
  161. const auto& framebuffer = Pica::g_state.regs.framebuffer;
  162. framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
  163. framebuffer_width = framebuffer.GetWidth();
  164. framebuffer_height = framebuffer.GetHeight();
  165. framebuffer_format = Format::D16;
  166. break;
  167. }
  168. case Source::Custom:
  169. {
  170. // Keep user-specified values
  171. break;
  172. }
  173. default:
  174. qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
  175. break;
  176. }
  177. // TODO: Implement a good way to visualize alpha components!
  178. // TODO: Unify this decoding code with the texture decoder
  179. u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format);
  180. QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
  181. u8* buffer = Memory::GetPhysicalPointer(framebuffer_address);
  182. for (unsigned int y = 0; y < framebuffer_height; ++y) {
  183. for (unsigned int x = 0; x < framebuffer_width; ++x) {
  184. const u32 coarse_y = y & ~7;
  185. u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
  186. const u8* pixel = buffer + offset;
  187. Math::Vec4<u8> color = { 0, 0, 0, 0 };
  188. switch (framebuffer_format) {
  189. case Format::RGBA8:
  190. color = Color::DecodeRGBA8(pixel);
  191. break;
  192. case Format::RGB8:
  193. color = Color::DecodeRGB8(pixel);
  194. break;
  195. case Format::RGB5A1:
  196. color = Color::DecodeRGB5A1(pixel);
  197. break;
  198. case Format::RGB565:
  199. color = Color::DecodeRGB565(pixel);
  200. break;
  201. case Format::RGBA4:
  202. color = Color::DecodeRGBA4(pixel);
  203. break;
  204. case Format::D16:
  205. {
  206. u32 data = Color::DecodeD16(pixel);
  207. color.r() = data & 0xFF;
  208. color.g() = (data >> 8) & 0xFF;
  209. break;
  210. }
  211. case Format::D24:
  212. {
  213. u32 data = Color::DecodeD24(pixel);
  214. color.r() = data & 0xFF;
  215. color.g() = (data >> 8) & 0xFF;
  216. color.b() = (data >> 16) & 0xFF;
  217. break;
  218. }
  219. case Format::D24S8:
  220. {
  221. Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
  222. color.r() = data.x & 0xFF;
  223. color.g() = (data.x >> 8) & 0xFF;
  224. color.b() = (data.x >> 16) & 0xFF;
  225. break;
  226. }
  227. default:
  228. qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
  229. break;
  230. }
  231. decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
  232. }
  233. }
  234. pixmap = QPixmap::fromImage(decoded_image);
  235. framebuffer_address_control->SetValue(framebuffer_address);
  236. framebuffer_width_control->setValue(framebuffer_width);
  237. framebuffer_height_control->setValue(framebuffer_height);
  238. framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
  239. framebuffer_picture_label->setPixmap(pixmap);
  240. }
  241. u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
  242. switch (format) {
  243. case Format::RGBA8:
  244. case Format::D24S8:
  245. return 4;
  246. case Format::RGB8:
  247. case Format::D24:
  248. return 3;
  249. case Format::RGB5A1:
  250. case Format::RGB565:
  251. case Format::RGBA4:
  252. case Format::D16:
  253. return 2;
  254. }
  255. }