renderer_opengl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include "core/hw/gpu.h"
  5. #include "video_core/video_core.h"
  6. #include "video_core/renderer_opengl/renderer_opengl.h"
  7. #include "core/mem_map.h"
  8. /// RendererOpenGL constructor
  9. RendererOpenGL::RendererOpenGL() {
  10. memset(m_fbo, 0, sizeof(m_fbo));
  11. memset(m_fbo_rbo, 0, sizeof(m_fbo_rbo));
  12. memset(m_fbo_depth_buffers, 0, sizeof(m_fbo_depth_buffers));
  13. m_resolution_width = max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
  14. m_resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
  15. m_xfb_texture_top = 0;
  16. m_xfb_texture_bottom = 0;
  17. m_xfb_top = 0;
  18. m_xfb_bottom = 0;
  19. }
  20. /// RendererOpenGL destructor
  21. RendererOpenGL::~RendererOpenGL() {
  22. }
  23. /// Swap buffers (render frame)
  24. void RendererOpenGL::SwapBuffers() {
  25. m_render_window->MakeCurrent();
  26. // EFB->XFB copy
  27. // TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some
  28. // register write We're also treating both framebuffers as a single one in OpenGL.
  29. common::Rect framebuffer_size(0, 0, m_resolution_width, m_resolution_height);
  30. RenderXFB(framebuffer_size, framebuffer_size);
  31. // XFB->Window copy
  32. RenderFramebuffer();
  33. // Swap buffers
  34. m_render_window->PollEvents();
  35. m_render_window->SwapBuffers();
  36. // Switch back to EFB and clear
  37. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
  38. }
  39. /**
  40. * Helper function to flip framebuffer from left-to-right to top-to-bottom
  41. * @param in Pointer to input raw framebuffer in V/RAM
  42. * @param out Pointer to output buffer with flipped framebuffer
  43. * @todo Early on hack... I'd like to find a more efficient way of doing this /bunnei
  44. */
  45. void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) {
  46. int in_coord = 0;
  47. for (int x = 0; x < VideoCore::kScreenTopWidth; x++) {
  48. for (int y = VideoCore::kScreenTopHeight-1; y >= 0; y--) {
  49. // TODO: Properly support other framebuffer formats
  50. int out_coord = (x + y * VideoCore::kScreenTopWidth) * 3;
  51. out[out_coord] = in[in_coord]; // blue?
  52. out[out_coord + 1] = in[in_coord + 1]; // green?
  53. out[out_coord + 2] = in[in_coord + 2]; // red?
  54. in_coord+=3;
  55. }
  56. }
  57. }
  58. /**
  59. * Renders external framebuffer (XFB)
  60. * @param src_rect Source rectangle in XFB to copy
  61. * @param dst_rect Destination rectangle in output framebuffer to copy to
  62. */
  63. void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) {
  64. const auto& framebuffer_top = GPU::g_regs.Get<GPU::Regs::FramebufferTop>();
  65. const auto& framebuffer_sub = GPU::g_regs.Get<GPU::Regs::FramebufferBottom>();
  66. const u32 active_fb_top = (framebuffer_top.data.active_fb == 1)
  67. ? framebuffer_top.data.address_left2
  68. : framebuffer_top.data.address_left1;
  69. const u32 active_fb_sub = (framebuffer_sub.data.active_fb == 1)
  70. ? framebuffer_sub.data.address_left2
  71. : framebuffer_sub.data.address_left1;
  72. DEBUG_LOG(GPU, "RenderXFB: %x bytes from %x(%xx%x), fmt %x",
  73. framebuffer_top.data.stride * framebuffer_top.data.height,
  74. GPU::GetFramebufferAddr(active_fb_top), (int)framebuffer_top.data.width,
  75. (int)framebuffer_top.data.height, (int)framebuffer_top.data.format);
  76. // TODO: This should consider the GPU registers for framebuffer width, height and stride.
  77. FlipFramebuffer(GPU::GetFramebufferPointer(active_fb_top), m_xfb_top_flipped);
  78. FlipFramebuffer(GPU::GetFramebufferPointer(active_fb_sub), m_xfb_bottom_flipped);
  79. // Blit the top framebuffer
  80. // ------------------------
  81. // Update textures with contents of XFB in RAM - top
  82. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
  83. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  84. GL_BGR, GL_UNSIGNED_BYTE, m_xfb_top_flipped);
  85. glBindTexture(GL_TEXTURE_2D, 0);
  86. // Render target is destination framebuffer
  87. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  88. glViewport(0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight);
  89. // Render source is our EFB
  90. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_top);
  91. glReadBuffer(GL_COLOR_ATTACHMENT0);
  92. // Blit
  93. glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_,
  94. dst_rect.x0_, dst_rect.y1_, dst_rect.x1_, dst_rect.y0_,
  95. GL_COLOR_BUFFER_BIT, GL_LINEAR);
  96. glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
  97. // Blit the bottom framebuffer
  98. // ---------------------------
  99. // Update textures with contents of XFB in RAM - bottom
  100. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom);
  101. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  102. GL_BGR, GL_UNSIGNED_BYTE, m_xfb_bottom_flipped);
  103. glBindTexture(GL_TEXTURE_2D, 0);
  104. // Render target is destination framebuffer
  105. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  106. glViewport(0, 0,
  107. VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight);
  108. // Render source is our EFB
  109. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_bottom);
  110. glReadBuffer(GL_COLOR_ATTACHMENT0);
  111. // Blit
  112. int offset = (VideoCore::kScreenTopWidth - VideoCore::kScreenBottomWidth) / 2;
  113. glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight,
  114. offset, VideoCore::kScreenBottomHeight, VideoCore::kScreenBottomWidth + offset, 0,
  115. GL_COLOR_BUFFER_BIT, GL_LINEAR);
  116. glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
  117. }
  118. /// Initialize the FBO
  119. void RendererOpenGL::InitFramebuffer() {
  120. // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
  121. // separate framebuffers
  122. // Init the FBOs
  123. // -------------
  124. glGenFramebuffers(kMaxFramebuffers, m_fbo); // Generate primary framebuffer
  125. glGenRenderbuffers(kMaxFramebuffers, m_fbo_rbo); // Generate primary RBOs
  126. glGenRenderbuffers(kMaxFramebuffers, m_fbo_depth_buffers); // Generate primary depth buffer
  127. for (int i = 0; i < kMaxFramebuffers; i++) {
  128. // Generate color buffer storage
  129. glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]);
  130. glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth,
  131. VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
  132. // Generate depth buffer storage
  133. glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
  134. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth,
  135. VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
  136. // Attach the buffers
  137. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[i]);
  138. glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  139. GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
  140. glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  141. GL_RENDERBUFFER, m_fbo_rbo[i]);
  142. // Check for completeness
  143. if (GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)) {
  144. NOTICE_LOG(RENDER, "framebuffer(%d) initialized ok", i);
  145. } else {
  146. ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer");
  147. exit(1);
  148. }
  149. }
  150. glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s)
  151. // Initialize framebuffer textures
  152. // -------------------------------
  153. // Create XFB textures
  154. glGenTextures(1, &m_xfb_texture_top);
  155. glGenTextures(1, &m_xfb_texture_bottom);
  156. // Alocate video memorry for XFB textures
  157. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
  158. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  159. 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  160. glBindTexture(GL_TEXTURE_2D, 0);
  161. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom);
  162. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  163. 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  164. glBindTexture(GL_TEXTURE_2D, 0);
  165. // Create the FBO and attach color/depth textures
  166. glGenFramebuffers(1, &m_xfb_top); // Generate framebuffer
  167. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_top);
  168. glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
  169. m_xfb_texture_top, 0);
  170. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  171. glGenFramebuffers(1, &m_xfb_bottom); // Generate framebuffer
  172. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_bottom);
  173. glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
  174. m_xfb_texture_bottom, 0);
  175. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  176. }
  177. /// Blit the FBO to the OpenGL default framebuffer
  178. void RendererOpenGL::RenderFramebuffer() {
  179. // Render target is default framebuffer
  180. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  181. glViewport(0, 0, m_resolution_width, m_resolution_height);
  182. // Render source is our XFB
  183. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  184. glReadBuffer(GL_COLOR_ATTACHMENT0);
  185. // Blit
  186. glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width,
  187. m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
  188. // Update the FPS count
  189. UpdateFramerate();
  190. // Rebind EFB
  191. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
  192. m_current_frame++;
  193. }
  194. /// Updates the framerate
  195. void RendererOpenGL::UpdateFramerate() {
  196. }
  197. /**
  198. * Set the emulator window to use for renderer
  199. * @param window EmuWindow handle to emulator window to use for rendering
  200. */
  201. void RendererOpenGL::SetWindow(EmuWindow* window) {
  202. m_render_window = window;
  203. }
  204. /// Initialize the renderer
  205. void RendererOpenGL::Init() {
  206. m_render_window->MakeCurrent();
  207. glShadeModel(GL_SMOOTH);
  208. glStencilFunc(GL_ALWAYS, 0, 0);
  209. glBlendFunc(GL_ONE, GL_ONE);
  210. glViewport(0, 0, m_resolution_width, m_resolution_height);
  211. glClearDepth(1.0f);
  212. glEnable(GL_DEPTH_TEST);
  213. glDisable(GL_LIGHTING);
  214. glDepthFunc(GL_LEQUAL);
  215. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  216. glDisable(GL_STENCIL_TEST);
  217. glEnable(GL_SCISSOR_TEST);
  218. glScissor(0, 0, m_resolution_width, m_resolution_height);
  219. glClearDepth(1.0f);
  220. GLenum err = glewInit();
  221. if (GLEW_OK != err) {
  222. ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...",
  223. glewGetErrorString(err));
  224. exit(-1);
  225. }
  226. // Initialize everything else
  227. // --------------------------
  228. InitFramebuffer();
  229. NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION));
  230. }
  231. /// Shutdown the renderer
  232. void RendererOpenGL::ShutDown() {
  233. }