From a15af9b550386b7c5bbeb747b0c0b1a1b7402ff9 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Sun, 29 Jun 2025 13:51:29 +0200 Subject: [PATCH] video_core: Fix LCD color fill (#1198) --- .../renderer_opengl/renderer_opengl.cpp | 67 ++++++++++--------- .../renderer_opengl/renderer_opengl.h | 6 +- .../renderer_software/renderer_software.cpp | 13 ++-- .../renderer_software/renderer_software.h | 2 +- .../renderer_vulkan/renderer_vulkan.cpp | 1 - 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 24e946740..bb9df55f0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -165,15 +165,16 @@ void RendererOpenGL::PrepareRendertarget() { const auto color_fill = fb_id == 0 ? regs_lcd.color_fill_top : regs_lcd.color_fill_bottom; if (color_fill.is_enabled) { - FillScreen(color_fill.AsVector(), texture); - continue; + // Resize the texture to let it be reconfigured + texture.width = 1; + texture.height = 1; } if (texture.width != framebuffer.width || texture.height != framebuffer.height || texture.format != framebuffer.color_format) { - ConfigureFramebufferTexture(texture, framebuffer); + ConfigureFramebufferTexture(texture, framebuffer, color_fill); } - LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1); + LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1, color_fill); } } @@ -231,7 +232,8 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout, * Loads framebuffer from emulated memory into the active OpenGL texture. */ void RendererOpenGL::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, - ScreenInfo& screen_info, bool right_eye) { + ScreenInfo& screen_info, bool right_eye, + const Pica::ColorFill& color_fill) { if (framebuffer.address_right1 == 0 || framebuffer.address_right2 == 0) right_eye = false; @@ -255,15 +257,27 @@ void RendererOpenGL::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuff // only allows rows to have a memory alignement of 4. ASSERT(pixel_stride % 4 == 0); - if (!rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, static_cast(pixel_stride), + if (color_fill.is_enabled || + !rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, static_cast(pixel_stride), screen_info)) { + u32 width = framebuffer.width; + u32 height = framebuffer.height; + u8 fill_pixel[3]; // Reset the screen info's display texture to its own permanent texture screen_info.display_texture = screen_info.texture.resource.handle; screen_info.display_texcoords = Common::Rectangle(0.f, 0.f, 1.f, 1.f); rasterizer.FlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); - const u8* framebuffer_data = system.Memory().GetPhysicalPointer(framebuffer_addr); + u8* framebuffer_data = system.Memory().GetPhysicalPointer(framebuffer_addr); + + if (color_fill.is_enabled) { + memcpy(fill_pixel, color_fill.AsVector().AsArray(), sizeof(fill_pixel)); + framebuffer_data = fill_pixel; + width = 1; + height = 1; + pixel_stride = 0; + } state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.Apply(); @@ -276,9 +290,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuff // they differ from the LCD resolution. // TODO: Applications could theoretically crash Citra here by specifying too large // framebuffer sizes. We should make sure that this cannot happen. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, - screen_info.texture.gl_format, screen_info.texture.gl_type, - framebuffer_data); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, screen_info.texture.gl_format, + screen_info.texture.gl_type, framebuffer_data); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -287,23 +300,6 @@ void RendererOpenGL::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuff } } -void RendererOpenGL::FillScreen(Common::Vec3 color, TextureInfo& texture) { - state.texture_units[0].texture_2d = texture.resource.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - - // Update existing texture - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, color.AsArray()); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - - // Resize the texture in case the framebuffer size has changed - texture.width = 1; - texture.height = 1; -} - /** * Initializes the OpenGL state and creates persistent objects. */ @@ -429,13 +425,20 @@ void RendererOpenGL::ReloadShader() { } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, - const Pica::FramebufferConfig& framebuffer) { + const Pica::FramebufferConfig& framebuffer, + const Pica::ColorFill& color_fill) { Pica::PixelFormat format = framebuffer.color_format; GLint internal_format{}; + u32 width, height; texture.format = format; - texture.width = framebuffer.width; - texture.height = framebuffer.height; + width = texture.width = framebuffer.width; + height = texture.height = framebuffer.height; + if (color_fill.is_enabled) { + width = 1; + height = 1; + format = Pica::PixelFormat::RGB8; + } switch (format) { case Pica::PixelFormat::RGBA8: @@ -482,8 +485,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, state.Apply(); glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, - texture.gl_format, texture.gl_type, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, texture.gl_format, + texture.gl_type, nullptr); state.texture_units[0].texture_2d = 0; state.Apply(); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index ebbebf606..7fa1af976 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -62,7 +62,8 @@ private: void RenderToMailbox(const Layout::FramebufferLayout& layout, std::unique_ptr& mailbox, bool flipped); void ConfigureFramebufferTexture(TextureInfo& texture, - const Pica::FramebufferConfig& framebuffer); + const Pica::FramebufferConfig& framebuffer, + const Pica::ColorFill& color_fill); void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); void ApplySecondLayerOpacity(bool isPortrait = false); void ResetSecondLayerOpacity(bool isPortrait = false); @@ -78,8 +79,7 @@ private: // Loads framebuffer from emulated memory into the display information structure void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info, - bool right_eye); - void FillScreen(Common::Vec3 color, TextureInfo& texture); + bool right_eye, const Pica::ColorFill& color_fill); private: Pica::PicaCore& pica; diff --git a/src/video_core/renderer_software/renderer_software.cpp b/src/video_core/renderer_software/renderer_software.cpp index ce8c6ca86..87bd47311 100644 --- a/src/video_core/renderer_software/renderer_software.cpp +++ b/src/video_core/renderer_software/renderer_software.cpp @@ -30,13 +30,11 @@ void RendererSoftware::PrepareRenderTarget() { const u32 fb_id = i == 2 ? 1 : 0; const auto color_fill = fb_id == 0 ? regs_lcd.color_fill_top : regs_lcd.color_fill_bottom; - if (!color_fill.is_enabled) { - LoadFBToScreenInfo(i); - } + LoadFBToScreenInfo(i, color_fill); } } -void RendererSoftware::LoadFBToScreenInfo(int i) { +void RendererSoftware::LoadFBToScreenInfo(int i, const Pica::ColorFill& color_fill) { const u32 fb_id = i == 2 ? 1 : 0; const auto& framebuffer = pica.regs.framebuffer_config[fb_id]; auto& info = screen_infos[i]; @@ -54,7 +52,12 @@ void RendererSoftware::LoadFBToScreenInfo(int i) { for (u32 y = 0; y < info.height; y++) { for (u32 x = 0; x < info.width; x++) { const u8* pixel = framebuffer_data + (y * pixel_stride + pixel_stride - x) * bpp; - const Common::Vec4 color = [&] { + Common::Vec4 color = [&] { + if (color_fill.is_enabled) { + return Common::Vec4(color_fill.color_r, color_fill.color_g, + color_fill.color_b, 255); + } + switch (framebuffer.color_format) { case Pica::PixelFormat::RGBA8: return Common::Color::DecodeRGBA8(pixel); diff --git a/src/video_core/renderer_software/renderer_software.h b/src/video_core/renderer_software/renderer_software.h index 2c1b61fb6..e5efe5257 100644 --- a/src/video_core/renderer_software/renderer_software.h +++ b/src/video_core/renderer_software/renderer_software.h @@ -38,7 +38,7 @@ public: private: void PrepareRenderTarget(); - void LoadFBToScreenInfo(int i); + void LoadFBToScreenInfo(int i, const Pica::ColorFill& color_fill); private: Memory::MemorySystem& memory; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 5e9f0e94f..4f1fc0d25 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -455,7 +455,6 @@ void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, } void RendererVulkan::FillScreen(Common::Vec3 color, const TextureInfo& texture) { - return; const vk::ClearColorValue clear_color = { .float32 = std::array{