renderer_opengl.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include "core/hw/lcd.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. BasicRect 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. int out_coord = (x + y * VideoCore::kScreenTopWidth) * 3;
  50. out[out_coord] = in[in_coord];
  51. out[out_coord + 1] = in[in_coord + 1];
  52. out[out_coord + 2] = in[in_coord + 2];
  53. in_coord+=3;
  54. }
  55. }
  56. }
  57. /**
  58. * Renders external framebuffer (XFB)
  59. * @param src_rect Source rectangle in XFB to copy
  60. * @param dst_rect Destination rectangle in output framebuffer to copy to
  61. */
  62. void RendererOpenGL::RenderXFB(const BasicRect& src_rect, const BasicRect& dst_rect) {
  63. FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_top_left_1), m_xfb_top_flipped);
  64. FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped);
  65. // Blit the top framebuffer
  66. // ------------------------
  67. // Update textures with contents of XFB in RAM - top
  68. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
  69. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  70. GL_BGR, GL_UNSIGNED_BYTE, m_xfb_top_flipped);
  71. glBindTexture(GL_TEXTURE_2D, 0);
  72. // Render target is destination framebuffer
  73. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  74. glViewport(0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight);
  75. // Render source is our EFB
  76. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_top);
  77. glReadBuffer(GL_COLOR_ATTACHMENT0);
  78. // Blit
  79. glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_,
  80. dst_rect.x0_, dst_rect.y1_, dst_rect.x1_, dst_rect.y0_,
  81. GL_COLOR_BUFFER_BIT, GL_LINEAR);
  82. glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
  83. // Blit the bottom framebuffer
  84. // ---------------------------
  85. // Update textures with contents of XFB in RAM - bottom
  86. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom);
  87. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  88. GL_RGB, GL_UNSIGNED_BYTE, m_xfb_bottom_flipped);
  89. glBindTexture(GL_TEXTURE_2D, 0);
  90. // Render target is destination framebuffer
  91. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  92. glViewport(0, 0,
  93. VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight);
  94. // Render source is our EFB
  95. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_bottom);
  96. glReadBuffer(GL_COLOR_ATTACHMENT0);
  97. // Blit
  98. int offset = (VideoCore::kScreenTopWidth - VideoCore::kScreenBottomWidth) / 2;
  99. glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight,
  100. offset, VideoCore::kScreenBottomHeight, VideoCore::kScreenBottomWidth + offset, 0,
  101. GL_COLOR_BUFFER_BIT, GL_LINEAR);
  102. glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
  103. }
  104. /// Initialize the FBO
  105. void RendererOpenGL::InitFramebuffer() {
  106. // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
  107. // separate framebuffers
  108. // Init the FBOs
  109. // -------------
  110. glGenFramebuffers(kMaxFramebuffers, m_fbo); // Generate primary framebuffer
  111. glGenRenderbuffers(kMaxFramebuffers, m_fbo_rbo); // Generate primary RBOs
  112. glGenRenderbuffers(kMaxFramebuffers, m_fbo_depth_buffers); // Generate primary depth buffer
  113. for (int i = 0; i < kMaxFramebuffers; i++) {
  114. // Generate color buffer storage
  115. glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]);
  116. glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth,
  117. VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
  118. // Generate depth buffer storage
  119. glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
  120. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth,
  121. VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
  122. // Attach the buffers
  123. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[i]);
  124. glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  125. GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
  126. glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  127. GL_RENDERBUFFER, m_fbo_rbo[i]);
  128. // Check for completeness
  129. if (GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)) {
  130. NOTICE_LOG(RENDER, "framebuffer(%d) initialized ok", i);
  131. } else {
  132. ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer");
  133. exit(1);
  134. }
  135. }
  136. glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s)
  137. // Initialize framebuffer textures
  138. // -------------------------------
  139. // Create XFB textures
  140. glGenTextures(1, &m_xfb_texture_top);
  141. glGenTextures(1, &m_xfb_texture_bottom);
  142. // Alocate video memorry for XFB textures
  143. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
  144. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  145. 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  146. glBindTexture(GL_TEXTURE_2D, 0);
  147. glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom);
  148. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
  149. 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  150. glBindTexture(GL_TEXTURE_2D, 0);
  151. // Create the FBO and attach color/depth textures
  152. glGenFramebuffers(1, &m_xfb_top); // Generate framebuffer
  153. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_top);
  154. glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
  155. m_xfb_texture_top, 0);
  156. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  157. glGenFramebuffers(1, &m_xfb_bottom); // Generate framebuffer
  158. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_bottom);
  159. glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
  160. m_xfb_texture_bottom, 0);
  161. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  162. }
  163. /// Blit the FBO to the OpenGL default framebuffer
  164. void RendererOpenGL::RenderFramebuffer() {
  165. // Render target is default framebuffer
  166. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  167. glViewport(0, 0, m_resolution_width, m_resolution_height);
  168. // Render source is our XFB
  169. glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
  170. glReadBuffer(GL_COLOR_ATTACHMENT0);
  171. // Blit
  172. glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width,
  173. m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
  174. // Update the FPS count
  175. UpdateFramerate();
  176. // Rebind EFB
  177. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
  178. m_current_frame++;
  179. }
  180. /// Updates the framerate
  181. void RendererOpenGL::UpdateFramerate() {
  182. }
  183. /**
  184. * Set the emulator window to use for renderer
  185. * @param window EmuWindow handle to emulator window to use for rendering
  186. */
  187. void RendererOpenGL::SetWindow(EmuWindow* window) {
  188. m_render_window = window;
  189. }
  190. /// Initialize the renderer
  191. void RendererOpenGL::Init() {
  192. m_render_window->MakeCurrent();
  193. glShadeModel(GL_SMOOTH);
  194. glStencilFunc(GL_ALWAYS, 0, 0);
  195. glBlendFunc(GL_ONE, GL_ONE);
  196. glViewport(0, 0, m_resolution_width, m_resolution_height);
  197. glClearDepth(1.0f);
  198. glEnable(GL_DEPTH_TEST);
  199. glDisable(GL_LIGHTING);
  200. glDepthFunc(GL_LEQUAL);
  201. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  202. glDisable(GL_STENCIL_TEST);
  203. glEnable(GL_SCISSOR_TEST);
  204. glScissor(0, 0, m_resolution_width, m_resolution_height);
  205. glClearDepth(1.0f);
  206. GLenum err = glewInit();
  207. if (GLEW_OK != err) {
  208. ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...",
  209. glewGetErrorString(err));
  210. exit(-1);
  211. }
  212. // Initialize everything else
  213. // --------------------------
  214. InitFramebuffer();
  215. NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION));
  216. }
  217. /// Shutdown the renderer
  218. void RendererOpenGL::ShutDown() {
  219. }