mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Change from render to texture to render to renderbuffer
This commit is contained in:
		
							parent
							
								
									52d7676831
								
							
						
					
					
						commit
						9c32c0b98b
					
				
					 6 changed files with 83 additions and 31 deletions
				
			
		|  | @ -45,14 +45,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Presentation thread calls this to get the latest frame available to present. If there is no |      * Presentation thread calls this to get the latest frame available to present. If there is no | ||||||
|      * frame available after timeout, returns nullptr |      * frame available after timeout, returns the previous frame. If there is no previous frame it | ||||||
|  |      * returns nullptr | ||||||
|      */ |      */ | ||||||
|     virtual Frontend::Frame* TryGetPresentFrame(int timeout_ms) = 0; |     virtual Frontend::Frame* TryGetPresentFrame(int timeout_ms) = 0; | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Presentation thread calls this after swap to release the frame and add it back to the queue |  | ||||||
|      */ |  | ||||||
|     virtual void ReleasePresentFrame(Frame* frame) = 0; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,23 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | void OGLRenderbuffer::Create() { | ||||||
|  |     if (handle != 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||||||
|  |     glGenRenderbuffers(1, &handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void OGLRenderbuffer::Release() { | ||||||
|  |     if (handle == 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); | ||||||
|  |     glDeleteRenderbuffers(1, &handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void OGLTexture::Create() { | void OGLTexture::Create() { | ||||||
|     if (handle != 0) |     if (handle != 0) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  | @ -12,6 +12,31 @@ | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | class OGLRenderbuffer : private NonCopyable { | ||||||
|  | public: | ||||||
|  |     OGLRenderbuffer() = default; | ||||||
|  | 
 | ||||||
|  |     OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | ||||||
|  | 
 | ||||||
|  |     ~OGLRenderbuffer() { | ||||||
|  |         Release(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept { | ||||||
|  |         Release(); | ||||||
|  |         handle = std::exchange(o.handle, 0); | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle
 | ||||||
|  |     void Create(); | ||||||
|  | 
 | ||||||
|  |     /// Deletes the internal OpenGL resource
 | ||||||
|  |     void Release(); | ||||||
|  | 
 | ||||||
|  |     GLuint handle = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class OGLTexture : private NonCopyable { | class OGLTexture : private NonCopyable { | ||||||
| public: | public: | ||||||
|     OGLTexture() = default; |     OGLTexture() = default; | ||||||
|  |  | ||||||
|  | @ -89,6 +89,8 @@ OpenGLState::OpenGLState() { | ||||||
|     viewport.height = 0; |     viewport.height = 0; | ||||||
| 
 | 
 | ||||||
|     clip_distance = {}; |     clip_distance = {}; | ||||||
|  | 
 | ||||||
|  |     renderbuffer = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OpenGLState::Apply() const { | void OpenGLState::Apply() const { | ||||||
|  | @ -337,6 +339,10 @@ void OpenGLState::Apply() const { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (renderbuffer != cur_state.renderbuffer) { | ||||||
|  |         glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     cur_state = *this; |     cur_state = *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -144,6 +144,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
 |     std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
 | ||||||
| 
 | 
 | ||||||
|  |     GLuint renderbuffer; | ||||||
|  | 
 | ||||||
|     OpenGLState(); |     OpenGLState(); | ||||||
| 
 | 
 | ||||||
|     /// Get the currently active OpenGL state
 |     /// Get the currently active OpenGL state
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ struct Frame { | ||||||
|     u32 width{};                      /// Width of the frame (to detect resize)
 |     u32 width{};                      /// Width of the frame (to detect resize)
 | ||||||
|     u32 height{};                     /// Height of the frame
 |     u32 height{};                     /// Height of the frame
 | ||||||
|     bool color_reloaded = false;      /// Texture attachment was recreated (ie: resized)
 |     bool color_reloaded = false;      /// Texture attachment was recreated (ie: resized)
 | ||||||
|     OpenGL::OGLTexture color{};       /// Texture shared between the render/present FBO
 |     OpenGL::OGLRenderbuffer color{};  /// Buffer shared between the render/present FBO
 | ||||||
|     OpenGL::OGLFramebuffer render{};  /// FBO created on the render thread
 |     OpenGL::OGLFramebuffer render{};  /// FBO created on the render thread
 | ||||||
|     OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
 |     OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
 | ||||||
|     GLsync render_fence{};            /// Fence created on the render thread
 |     GLsync render_fence{};            /// Fence created on the render thread
 | ||||||
|  | @ -48,7 +48,10 @@ struct Frame { | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| constexpr std::size_t SWAP_CHAIN_SIZE = 5; | // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
 | ||||||
|  | // to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
 | ||||||
|  | // number but 8 seems to be a good trade off for now
 | ||||||
|  | constexpr std::size_t SWAP_CHAIN_SIZE = 8; | ||||||
| 
 | 
 | ||||||
| class OGLTextureMailbox : public Frontend::TextureMailbox { | class OGLTextureMailbox : public Frontend::TextureMailbox { | ||||||
| public: | public: | ||||||
|  | @ -58,6 +61,7 @@ public: | ||||||
|     std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{}; |     std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{}; | ||||||
|     std::deque<Frontend::Frame*> free_queue{}; |     std::deque<Frontend::Frame*> free_queue{}; | ||||||
|     std::deque<Frontend::Frame*> present_queue{}; |     std::deque<Frontend::Frame*> present_queue{}; | ||||||
|  |     Frontend::Frame* previous_frame = nullptr; | ||||||
| 
 | 
 | ||||||
|     OGLTextureMailbox() { |     OGLTextureMailbox() { | ||||||
|         for (auto& frame : swap_chain) { |         for (auto& frame : swap_chain) { | ||||||
|  | @ -65,7 +69,10 @@ public: | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ~OGLTextureMailbox() override = default; |     ~OGLTextureMailbox() override { | ||||||
|  |         // lock the mutex and clear out the present and free_queues and notify any people who are
 | ||||||
|  |         // blocked to prevent deadlock on shutdown
 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override { |     void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override { | ||||||
|         frame->present.Release(); |         frame->present.Release(); | ||||||
|  | @ -73,12 +80,12 @@ public: | ||||||
|         GLint previous_draw_fbo{}; |         GLint previous_draw_fbo{}; | ||||||
|         glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); |         glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); | ||||||
|         glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); |         glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); | ||||||
|         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||||||
|                                frame->color.handle, 0); |                                   frame->color.handle); | ||||||
|         if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { |         if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { | ||||||
|             LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); |             LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); | ||||||
|         } |         } | ||||||
|         glBindFramebuffer(GL_FRAMEBUFFER, previous_draw_fbo); |         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); | ||||||
|         frame->color_reloaded = false; |         frame->color_reloaded = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -89,14 +96,9 @@ public: | ||||||
|         // Recreate the color texture attachment
 |         // Recreate the color texture attachment
 | ||||||
|         frame->color.Release(); |         frame->color.Release(); | ||||||
|         frame->color.Create(); |         frame->color.Create(); | ||||||
|         state.texture_units[0].texture_2d = frame->color.handle; |         state.renderbuffer = frame->color.handle; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |  | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |  | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |  | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |  | ||||||
|         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); |  | ||||||
| 
 | 
 | ||||||
|         // Recreate the FBO for the render target
 |         // Recreate the FBO for the render target
 | ||||||
|         frame->render.Release(); |         frame->render.Release(); | ||||||
|  | @ -104,8 +106,8 @@ public: | ||||||
|         state.draw.read_framebuffer = frame->render.handle; |         state.draw.read_framebuffer = frame->render.handle; | ||||||
|         state.draw.draw_framebuffer = frame->render.handle; |         state.draw.draw_framebuffer = frame->render.handle; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
|         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||||||
|                                frame->color.handle, 0); |                                   frame->color.handle); | ||||||
|         if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { |         if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { | ||||||
|             LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); |             LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); | ||||||
|         } |         } | ||||||
|  | @ -138,8 +140,15 @@ public: | ||||||
|                             [&] { return !present_queue.empty(); }); |                             [&] { return !present_queue.empty(); }); | ||||||
|         if (present_queue.empty()) { |         if (present_queue.empty()) { | ||||||
|             // timed out waiting for a frame to draw so return nullptr
 |             // timed out waiting for a frame to draw so return nullptr
 | ||||||
|             return nullptr; |             return previous_frame; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // free the previous frame and add it back to the free queue
 | ||||||
|  |         if (previous_frame) { | ||||||
|  |             free_queue.push_back(previous_frame); | ||||||
|  |             free_cv.notify_one(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // the newest entries are pushed to the front of the queue
 |         // the newest entries are pushed to the front of the queue
 | ||||||
|         Frontend::Frame* frame = present_queue.front(); |         Frontend::Frame* frame = present_queue.front(); | ||||||
|         present_queue.pop_front(); |         present_queue.pop_front(); | ||||||
|  | @ -149,14 +158,9 @@ public: | ||||||
|             free_cv.notify_one(); |             free_cv.notify_one(); | ||||||
|         } |         } | ||||||
|         present_queue.clear(); |         present_queue.clear(); | ||||||
|  |         previous_frame = frame; | ||||||
|         return frame; |         return frame; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     void ReleasePresentFrame(Frontend::Frame* frame) override { |  | ||||||
|         std::unique_lock<std::mutex> lock(swap_chain_lock); |  | ||||||
|         free_queue.push_back(frame); |  | ||||||
|         free_cv.notify_one(); |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const char vertex_shader[] = R"( | static const char vertex_shader[] = R"( | ||||||
|  | @ -838,12 +842,15 @@ void RendererOpenGL::TryPresent(int timeout_ms) { | ||||||
|     const auto& layout = render_window.GetFramebufferLayout(); |     const auto& layout = render_window.GetFramebufferLayout(); | ||||||
|     auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms); |     auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms); | ||||||
|     if (!frame) { |     if (!frame) { | ||||||
|         LOG_CRITICAL(Render_OpenGL, "Try returned no frame to present"); |         LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     glClear(GL_COLOR_BUFFER_BIT); | ||||||
|  | 
 | ||||||
|     // Recreate the presentation FBO if the color attachment was changed
 |     // Recreate the presentation FBO if the color attachment was changed
 | ||||||
|     if (frame->color_reloaded) { |     if (frame->color_reloaded) { | ||||||
|         LOG_CRITICAL(Render_OpenGL, "Reloading present frame"); |         LOG_DEBUG(Render_OpenGL, "Reloading present frame"); | ||||||
|         render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height); |         render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height); | ||||||
|     } |     } | ||||||
|     glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); |     glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); | ||||||
|  | @ -860,7 +867,6 @@ void RendererOpenGL::TryPresent(int timeout_ms) { | ||||||
|     /* insert fence for the main thread to block on */ |     /* insert fence for the main thread to block on */ | ||||||
|     frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |     frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||||||
|     glFlush(); |     glFlush(); | ||||||
|     render_window.mailbox->ReleasePresentFrame(frame); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::PresentComplete() { | void RendererOpenGL::PresentComplete() { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue