renderer_opengl.cpp 10 KB

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