From 66ba732829110684a6f233c629f15df00fa98118 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 8 Oct 2026 19:33:43 +0300 Subject: [PATCH] rsx/gl: Implement support for framebuffer-less rendering --- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 5 ++++ rpcs3/Emu/RSX/GL/GLProcTable.h | 2 ++ rpcs3/Emu/RSX/GL/GLRenderTargets.cpp | 12 +++++++++ rpcs3/Emu/RSX/GL/glutils/capabilities.cpp | 2 ++ rpcs3/Emu/RSX/GL/glutils/capabilities.h | 1 + rpcs3/Emu/RSX/GL/glutils/fbo.cpp | 12 +++++++++ rpcs3/Emu/RSX/GL/glutils/fbo.h | 3 +++ rpcs3/Emu/RSX/RSXThread.cpp | 18 ++++++++++--- rpcs3/Emu/RSX/RSXThread.h | 31 ++++++++++++----------- 9 files changed, 67 insertions(+), 19 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index abc80450fa..f774e1e135 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -192,6 +192,11 @@ void GLGSRender::on_init_thread() backend_config.supports_hw_instanced_rendering = false; } + if (gl_caps.ARB_framebuffer_no_attachments_supported) + { + backend_config.supports_framebufferless_rendering = true; + } + if (!gl_caps.ARB_bindless_texture_supported) { switch (shadermode) diff --git a/rpcs3/Emu/RSX/GL/GLProcTable.h b/rpcs3/Emu/RSX/GL/GLProcTable.h index 65bfbb798f..2aaf6991c1 100644 --- a/rpcs3/Emu/RSX/GL/GLProcTable.h +++ b/rpcs3/Emu/RSX/GL/GLProcTable.h @@ -158,6 +158,8 @@ OPENGL_PROC(PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC, NamedFramebufferDrawBuffers); OPENGL_PROC(PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC, FramebufferDrawBuffersEXT); OPENGL_PROC(PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC, NamedFramebufferReadBuffer); OPENGL_PROC(PFNGLFRAMEBUFFERREADBUFFEREXTPROC, FramebufferReadBufferEXT); +OPENGL_PROC(PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC, NamedFramebufferParameteri); +OPENGL_PROC(PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC, NamedFramebufferParameteriEXT); OPENGL_PROC(PFNGLENABLEIPROC, Enablei); OPENGL_PROC(PFNGLDISABLEIPROC, Disablei); diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index 1cdfa3fb43..74fd523a42 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -268,6 +268,18 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /* ensure(m_draw_fbo); + if (!depth_stencil_target && + std::all_of(color_targets.begin(), color_targets.end(), FN(x == GL_NONE))) + { + // Framebufferless rendering. Raster dimensions must be supplied explicitly and match what real surfaces would have used. + const auto [raster_width, raster_height] = rsx::apply_resolution_scale( + resolution_scaling_config, m_framebuffer_layout.width, m_framebuffer_layout.height); + + const GLint raster_samples = (g_cfg.video.antialiasing_level == msaa_level::_auto) ? samples : 1; + m_draw_fbo->set_default_extents({ raster_width, raster_height }); + m_draw_fbo->set_default_samples(raster_samples); + } + switch (rsx::method_registers.surface_color_target()) { case rsx::surface_target::none: break; diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index 7d99630948..baffeec12d 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -96,6 +96,8 @@ namespace gl CHECK_EXTENSION_SUPPORT(ARB_shader_storage_buffer_object); + CHECK_EXTENSION_SUPPORT(ARB_framebuffer_no_attachments); + #undef CHECK_EXTENSION_SUPPORT // Set GLSL version diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index b68cd56923..c1ae1a4975 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -44,6 +44,7 @@ namespace gl bool ARB_shader_texture_image_samples_supported = false; bool EXT_texture_compression_s3tc_supported = false; bool ARB_shader_storage_buffer_object_supported = false; + bool ARB_framebuffer_no_attachments_supported = false; bool vendor_INTEL = false; // has broken GLSL compiler bool vendor_AMD = false; // has broken ARB_multidraw diff --git a/rpcs3/Emu/RSX/GL/glutils/fbo.cpp b/rpcs3/Emu/RSX/GL/glutils/fbo.cpp index b1f6300c01..d5b759bb4e 100644 --- a/rpcs3/Emu/RSX/GL/glutils/fbo.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/fbo.cpp @@ -255,6 +255,18 @@ namespace gl return m_size; } + void fbo::set_default_extents(const size2i& extents) const + { + DSA_CALL2(NamedFramebufferParameteri, m_id, GL_FRAMEBUFFER_DEFAULT_WIDTH, extents.width); + DSA_CALL2(NamedFramebufferParameteri, m_id, GL_FRAMEBUFFER_DEFAULT_HEIGHT, extents.height); + } + + void fbo::set_default_samples(GLint samples) const + { + DSA_CALL2(NamedFramebufferParameteri, m_id, GL_FRAMEBUFFER_DEFAULT_SAMPLES, samples > 1 ? samples : 0); + DSA_CALL2(NamedFramebufferParameteri, m_id, GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS, GL_TRUE); + } + bool fbo::matches(const std::array& color_targets, GLuint depth_stencil_target) const { for (u32 index = 0; index < 4; ++index) diff --git a/rpcs3/Emu/RSX/GL/glutils/fbo.h b/rpcs3/Emu/RSX/GL/glutils/fbo.h index e17e8a62e6..c7bfa27464 100644 --- a/rpcs3/Emu/RSX/GL/glutils/fbo.h +++ b/rpcs3/Emu/RSX/GL/glutils/fbo.h @@ -257,6 +257,9 @@ namespace gl void set_extents(const size2i& extents); size2i get_extents() const; + void set_default_extents(const size2i& extents) const; + void set_default_samples(GLint samples) const; + bool matches(const std::array& color_targets, GLuint depth_stencil_target) const; bool references_any(const std::vector& resources) const; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index d1a45dfc40..302a95bc83 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1773,10 +1773,19 @@ namespace rsx m_graphics_state.set(rsx::rtt_config_valid); } - if (!m_graphics_state.test(rsx::rtt_config_valid) && !layout.zeta_address) + const bool framebufferless = !m_graphics_state.test(rsx::rtt_config_valid) && !layout.zeta_address; + if (framebufferless) { - rsx_log.warning("Framebuffer setup failed. Draw calls may have been lost"); - return; + // No attachments. The hardware still rasterizes in this case and side effects such as ZPASS counters are still updated. + if (context != rsx::framebuffer_creation_context::context_draw || + !backend_config.supports_framebufferless_rendering) + { + rsx_log.warning("Framebuffer setup failed. Draw calls may have been lost"); + return; + } + + // Context must be draw now... + // TODO: Also disable rendering if ZPASS stat counting is not enabled (side-effect) } // At least one attachment exists @@ -1852,7 +1861,8 @@ namespace rsx } } - if (!really_changed) + // Framebufferless setups have no surface info to compare against. Always rebuild. + if (!really_changed && !framebufferless) { if (layout.zeta_address == m_depth_surface_info.address && layout.depth_format == m_depth_surface_info.depth_format && diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index d09a1bc802..7c6042abf5 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -79,21 +79,22 @@ namespace rsx struct backend_configuration { - bool supports_multidraw; // Draw call batching - bool supports_hw_a2c; // Alpha to coverage - bool supports_hw_a2c_1spp; // Alpha to coverage at 1 sample per pixel - bool supports_hw_renormalization; // Should be true on NV hardware which matches PS3 texture renormalization behaviour - bool supports_hw_msaa; // MSAA support - bool supports_hw_a2one; // Alpha to one - bool supports_hw_conditional_render; // Conditional render - bool supports_hw_instanced_rendering; // Instanced draws - bool supports_passthrough_dma; // DMA passthrough - bool supports_asynchronous_compute; // Async compute - bool supports_host_gpu_labels; // Advanced host synchronization - bool supports_normalized_barycentrics; // Basically all GPUs except NVIDIA have properly normalized barycentrics - bool supports_last_provoking_vertex; // Flat shading using RSX's last-vertex convention - bool supports_extended_depth_range; // Depth values above 1 can be stored and tested. Float depth targets are emulated otherwise. - bool supports_programmable_blending; // Can handle programmable blending requests + bool supports_multidraw; // Draw call batching + bool supports_hw_a2c; // Alpha to coverage + bool supports_hw_a2c_1spp; // Alpha to coverage at 1 sample per pixel + bool supports_hw_renormalization; // Should be true on NV hardware which matches PS3 texture renormalization behaviour + bool supports_hw_msaa; // MSAA support + bool supports_hw_a2one; // Alpha to one + bool supports_hw_conditional_render; // Conditional render + bool supports_hw_instanced_rendering; // Instanced draws + bool supports_passthrough_dma; // DMA passthrough + bool supports_asynchronous_compute; // Async compute + bool supports_host_gpu_labels; // Advanced host synchronization + bool supports_normalized_barycentrics; // Basically all GPUs except NVIDIA have properly normalized barycentrics + bool supports_last_provoking_vertex; // Flat shading using RSX's last-vertex convention + bool supports_programmable_blending; // Can handle programmable blending requests + bool supports_extended_depth_range; // Depth values above 1 can be stored and tested. Float depth targets are emulated otherwise. + bool supports_framebufferless_rendering; // Can rasterize without any attachments bound (e.g for occlusion queries) }; struct desync_fifo_cmd_info