diff --git a/game/graphics/opengl_renderer/BlitDisplays.cpp b/game/graphics/opengl_renderer/BlitDisplays.cpp index dbbfcdd875..9f4d72c4e5 100644 --- a/game/graphics/opengl_renderer/BlitDisplays.cpp +++ b/game/graphics/opengl_renderer/BlitDisplays.cpp @@ -16,12 +16,9 @@ void BlitDisplays::init_textures(TexturePool& texture_pool, GameVersion version) default: ASSERT_NOT_REACHED(); } + m_copier = std::make_unique(); TextureInput in; - glGenTextures(1, &in.gpu_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + in.gpu_texture = m_copier->texture(); in.w = 32; in.h = 32; in.debug_page_name = "PC-BLIT"; @@ -34,9 +31,6 @@ void BlitDisplays::init_textures(TexturePool& texture_pool, GameVersion version) void BlitDisplays::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { - auto back = render_state->back_fbo; - bool valid = back && back->valid; - // loop through all data while (dma.current_tag_offset() != render_state->next_bucket) { auto data = dma.read_and_advance(); @@ -46,28 +40,66 @@ void BlitDisplays::render(DmaFollower& dma, case 0x10: { // copy buffer->texture (tbp in vif1) u32 tbp = data.vifcode1().immediate; ASSERT_MSG(tbp == m_tbp, fmt::format("unexpected tbp {}", tbp)); - if (valid) { - // copy buffer texture -> custom texture - auto my_tex_id = m_gpu_tex->gpu_textures.at(0).gl; - int w = back->width, h = back->height; + // copy buffer texture -> custom texture + m_copier->copy_now(render_state->render_fb_w, render_state->render_fb_h, + render_state->render_fb); + m_gpu_tex->w = render_state->render_fb_w; + m_gpu_tex->h = render_state->render_fb_h; + render_state->texture_pool->move_existing_to_vram(m_gpu_tex, m_tbp); - glBindTexture(GL_TEXTURE_2D, my_tex_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } break; + case 0x11: { + // this case is "do nothing" on PS2 because the countdown is nonzero. + // On the PS2, there were two framebuffers. The first was where triangles were directly + // rendered to. Then, this was copied to the final buffer. These buffers were slightly + // different resolution. - glBindFramebuffer(GL_READ_FRAMEBUFFER, back->fbo_id); - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, w, h, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - m_gpu_tex->w = w; - m_gpu_tex->h = h; - render_state->texture_pool->move_existing_to_vram(m_gpu_tex, m_tbp); - } else { - lg::error("no valid back buffer to blit!"); - } + // On PC, we don't imitate this 2-buffer setup. So in cases where the original game + // skipped doing a copy, it would render to the first buffer but never copy to the second. + // On PC, we set this flag which means that after rendering is finished we copy the saved + // buffer back. + m_copy_back_pending = true; } break; } } } + + // after reading the framebuffer, clear it. + // The upcoming sky renderer bucket will clear it, but can't clear stuff in the letterbox regions, + // so we manually do the clear here. Note that we need to clear the window (framebuffer 0) in case + // the resolution/aspect/size changed, and there is a different letterbox than last frame. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(0.0); + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDisable(GL_BLEND); + + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(0.0); + glClearStencil(0); + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDisable(GL_BLEND); + render_state->stencil_dirty = false; } -void BlitDisplays::draw_debug_window() {} +void BlitDisplays::do_copy_back(SharedRenderState* render_state) { + if (m_copy_back_pending) { + if (render_state->render_fb_w == m_copier->width() && + render_state->render_fb_h == m_copier->height()) { + m_copier->copy_back_now(render_state->render_fb_w, render_state->render_fb_h, + render_state->render_fb); + } + m_copy_back_pending = false; + } +} + +void BlitDisplays::draw_debug_window() { + glBindTexture(GL_TEXTURE_2D, m_copier->texture()); + int w, h; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + ImGui::Image((void*)(u64)m_copier->texture(), ImVec2(w, h)); +} diff --git a/game/graphics/opengl_renderer/BlitDisplays.h b/game/graphics/opengl_renderer/BlitDisplays.h index 5905d5cccc..1dd2a92c8e 100644 --- a/game/graphics/opengl_renderer/BlitDisplays.h +++ b/game/graphics/opengl_renderer/BlitDisplays.h @@ -1,6 +1,7 @@ #pragma once #include "game/graphics/opengl_renderer/BucketRenderer.h" +#include "game/graphics/opengl_renderer/opengl_utils.h" #include "game/graphics/texture/TexturePool.h" /*! @@ -12,8 +13,11 @@ class BlitDisplays : public BucketRenderer { void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; void init_textures(TexturePool& texture_pool, GameVersion) override; void draw_debug_window() override; + void do_copy_back(SharedRenderState* render_state); private: + std::unique_ptr m_copier; GpuTexture* m_gpu_tex; u32 m_tbp; + bool m_copy_back_pending = false; }; diff --git a/game/graphics/opengl_renderer/BucketRenderer.h b/game/graphics/opengl_renderer/BucketRenderer.h index 773d764318..61877546d5 100644 --- a/game/graphics/opengl_renderer/BucketRenderer.h +++ b/game/graphics/opengl_renderer/BucketRenderer.h @@ -87,9 +87,6 @@ struct SharedRenderState { int draw_offset_x = 0; int draw_offset_y = 0; - // the FBO for blit buffer - const Fbo* back_fbo = nullptr; - int bucket_for_vis_copy = 0; int num_vis_to_copy = 0; GameVersion version; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index 97b6a368fe..7dd348380a 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -126,7 +126,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { // 0 init_bucket_renderer("vis", BucketCategory::OTHER, BucketId::BUCKET_2); - init_bucket_renderer("blit", BucketCategory::OTHER, BucketId::BUCKET_3); + m_blit_displays = + init_bucket_renderer("blit", BucketCategory::OTHER, BucketId::BUCKET_3); init_bucket_renderer("tex-lcom-sky-pre", BucketCategory::TEX, BucketId::TEX_LCOM_SKY_PRE, m_texture_animator); init_bucket_renderer("sky-draw", BucketCategory::OTHER, BucketId::SKY_DRAW, 1024); @@ -645,34 +646,9 @@ Fbo make_fbo(int w, int h, int msaa, bool make_zbuf_and_stencil) { } // namespace void OpenGLRenderer::blit_display() { - auto& back = m_fbo_state.resources.back_buffer; - if (!back.valid || !back.matches(*m_fbo_state.render_fbo)) { - back.clear(); - back = make_fbo(m_fbo_state.render_fbo->width, m_fbo_state.render_fbo->height, 1, false); + if (m_blit_displays) { + m_blit_displays->do_copy_back(&m_render_state); } - - Fbo* window_blit_src = nullptr; - if (m_fbo_state.resources.resolve_buffer.valid) { - // since this is called after do_pcrtc_effects, the resolve buffer is already made - window_blit_src = &m_fbo_state.resources.resolve_buffer; - } else { - window_blit_src = m_fbo_state.render_fbo; - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, window_blit_src->fbo_id); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, back.fbo_id); - glBlitFramebuffer(0, // srcX0 - 0, // srcY0 - window_blit_src->width, // srcX1 - window_blit_src->height, // srcY1 - 0, // dstX0 - 0, // dstY0 - back.width, // dstX1 - back.height, // dstY1 - GL_COLOR_BUFFER_BIT, // mask - GL_LINEAR // filter - ); - glBindFramebuffer(GL_FRAMEBUFFER, 0); } /*! @@ -714,6 +690,12 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) { } } + // blit framebuffer so that it can be used as a texture by the game later + { + auto prof = m_profiler.root()->make_scoped_child("blit-display"); + blit_display(); + } + // apply effects done with PCRTC registers { auto prof = m_profiler.root()->make_scoped_child("pcrtc"); @@ -723,12 +705,6 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) { } } - // blit framebuffer so that it can be used as a texture by the game later - { - auto prof = m_profiler.root()->make_scoped_child("blit-display"); - blit_display(); - } - m_last_pmode_alp = settings.pmode_alp_register; if (settings.save_screenshot) { @@ -910,24 +886,27 @@ void OpenGLRenderer::setup_frame(const RenderOptions& settings) { ASSERT_MSG(!m_fbo_state.render_fbo->is_window, "window fbo"); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, m_fbo_state.resources.window.width, m_fbo_state.resources.window.height); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClearDepth(0.0); - glDepthMask(GL_TRUE); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - glDisable(GL_BLEND); + if (m_version == GameVersion::Jak1) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, m_fbo_state.resources.window.width, m_fbo_state.resources.window.height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(0.0); + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDisable(GL_BLEND); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo_state.render_fbo->fbo_id); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClearDepth(0.0); - glClearStencil(0); - glDepthMask(GL_TRUE); - // Note: could rely on sky renderer to clear depth and color, but this causes problems with - // letterboxing - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - glDisable(GL_BLEND); - m_render_state.stencil_dirty = false; + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo_state.render_fbo->fbo_id); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(0.0); + glClearStencil(0); + glDepthMask(GL_TRUE); + // Note: could rely on sky renderer to clear depth and color, but this causes problems with + // letterboxing + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDisable(GL_BLEND); + m_render_state.stencil_dirty = false; + } + // jak 2 does the clear in BlitDisplays.cpp // setup the draw region to letterbox later m_render_state.draw_region_w = settings.draw_region_width; @@ -940,7 +919,6 @@ void OpenGLRenderer::setup_frame(const RenderOptions& settings) { (settings.window_framebuffer_height - m_render_state.draw_region_h) / 2; m_render_state.render_fb = m_fbo_state.render_fbo->fbo_id; - m_render_state.back_fbo = &m_fbo_state.resources.back_buffer; if (m_render_state.draw_region_w <= 0 || m_render_state.draw_region_h <= 0) { // trying to draw to 0 size region... opengl doesn't like this. diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.h b/game/graphics/opengl_renderer/OpenGLRenderer.h index a7475cf526..caabb961d4 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.h +++ b/game/graphics/opengl_renderer/OpenGLRenderer.h @@ -113,6 +113,7 @@ class OpenGLRenderer { std::shared_ptr m_texture_animator; std::vector> m_bucket_renderers; std::vector m_bucket_categories; + class BlitDisplays* m_blit_displays = nullptr; std::array m_category_times; FullScreenDraw m_blackout_renderer; @@ -126,7 +127,6 @@ class OpenGLRenderer { Fbo window; // provided by glfw Fbo render_buffer; // temporary buffer to render to Fbo resolve_buffer; // temporary buffer to resolve to - Fbo back_buffer; // the previous buffer we rendered } resources; Fbo* render_fbo = nullptr; // the selected fbo from the three above to use for rendering diff --git a/game/graphics/opengl_renderer/TextureAnimator.cpp b/game/graphics/opengl_renderer/TextureAnimator.cpp index 6b1201760d..1b31f9cd18 100644 --- a/game/graphics/opengl_renderer/TextureAnimator.cpp +++ b/game/graphics/opengl_renderer/TextureAnimator.cpp @@ -1806,6 +1806,14 @@ void TextureAnimator::run_fixed_animation_array(int idx, ASSERT(tbp < 0x40000); m_skip_tbps.push_back(tbp); // known to be an output texture. if (anim.pool_gpu_tex) { + // if the debug checkbox is checked, replace the texture with red. + if (m_output_debug_flags.at(anim.dest_slot).b) { + FramebufferTexturePairContext ctxt(*anim.fbt); + glColorMask(true, true, true, true); + glClearColor(1.0, 0.0, 0.0, 0.5); + glClear(GL_COLOR_BUFFER_BIT); + } + texture_pool->move_existing_to_vram(anim.pool_gpu_tex, tbp); ASSERT(texture_pool->lookup(tbp).value() == anim.fbt->texture()); } else { diff --git a/game/graphics/opengl_renderer/opengl_utils.cpp b/game/graphics/opengl_renderer/opengl_utils.cpp index 001485fa9b..3ae09d657d 100644 --- a/game/graphics/opengl_renderer/opengl_utils.cpp +++ b/game/graphics/opengl_renderer/opengl_utils.cpp @@ -208,5 +208,24 @@ void FramebufferCopier::copy_now(int render_fb_w, int render_fb_h, GLuint render GL_NEAREST // filter ); + glBindFramebuffer(GL_FRAMEBUFFER, render_fb); +} + +void FramebufferCopier::copy_back_now(int render_fb_w, int render_fb_h, GLuint render_fb) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, render_fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); + + glBlitFramebuffer(0, // srcX0 + 0, // srcY0 + m_fbo_width, // srcX1 + m_fbo_height, // srcY1 + 0, // dstX0 + 0, // dstY0 + render_fb_w, // dstX1 + render_fb_h, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + glBindFramebuffer(GL_FRAMEBUFFER, render_fb); } \ No newline at end of file diff --git a/game/graphics/opengl_renderer/opengl_utils.h b/game/graphics/opengl_renderer/opengl_utils.h index 79d971e5ab..337a13bbe4 100644 --- a/game/graphics/opengl_renderer/opengl_utils.h +++ b/game/graphics/opengl_renderer/opengl_utils.h @@ -86,7 +86,10 @@ class FramebufferCopier { FramebufferCopier(const FramebufferCopier&) = delete; FramebufferCopier& operator=(const FramebufferCopier&) = delete; void copy_now(int render_fb_w, int render_fb_h, GLuint render_fb); + void copy_back_now(int render_fb_w, int render_fb_h, GLuint render_fb); u64 texture() const { return m_fbo_texture; } + int width() const { return m_fbo_width; } + int height() const { return m_fbo_height; } private: GLuint m_fbo = 0, m_fbo_texture = 0; diff --git a/goal_src/jak2/engine/gfx/blit-displays.gc b/goal_src/jak2/engine/gfx/blit-displays.gc index 9f13609793..778219a363 100644 --- a/goal_src/jak2/engine/gfx/blit-displays.gc +++ b/goal_src/jak2/engine/gfx/blit-displays.gc @@ -175,9 +175,17 @@ (else ) ) - (if (nonzero? (-> gp-0 count-down)) - (+! (-> gp-0 count-down) -1) + (when (nonzero? (-> gp-0 count-down)) + (+! (-> gp-0 count-down) -1) + + (with-dma-buffer-add-bucket ((arg0 (-> *display* frames (-> *display* on-screen) global-buf)) + (bucket-id bucket-3) + ) + (dma-buffer-add-cnt-vif2 arg0 0 (new 'static 'vif-tag :cmd (vif-cmd pc-port) :imm #x11) ;; kind - do nothing + (new 'static 'vif-tag :cmd (vif-cmd pc-port))) ) + + ) ) (none) )