Files
jak-project/game/graphics/opengl_renderer/BlitDisplays.cpp
T
Tyler Wilding d3cc739e43 jakx: Commit existing work from other PRs (#4112)
This attempts to get into master whatever work was done in this PR /
it's earlier PR https://github.com/open-goal/jak-project/pull/3965

I don't want this work to be lost / floating around in massive PRs.

However the changes are:
- switch to ntsc_v1 instead of PAL as the development target, as we have
done for all other games
- remove most of the copied-from-jak2/3 changes as they need to be
confirmed during the decompilation process not just assumed
- avoids committing any changes to `game/kernel/common` as it was not
clear to me if these were changes made in jak x's kernel that were not
properly broken out into it's own functions. We don't want to
accidentally introduce bugs into jak1-3's kernel code.
- in other words, if the change in the kernel only happens in jak x...it
should likely be specific to jak x's kernel, not common.

---------

Co-authored-by: VodBox <dillon@vodbox.io>
Co-authored-by: yodah <greenboyyodah@gmail.com>
2025-12-31 21:08:44 -05:00

225 lines
8.7 KiB
C++

#include "BlitDisplays.h"
#include "common/log/log.h"
#include "game/graphics/opengl_renderer/Fbo.h"
BlitDisplays::BlitDisplays(const std::string& name, int my_id) : BucketRenderer(name, my_id) {}
void BlitDisplays::init_textures(TexturePool& texture_pool, GameVersion version) {
// set up target texture
u32 tbp = 0;
switch (version) {
case GameVersion::Jak2:
tbp = 0x3300;
break;
case GameVersion::Jak3:
case GameVersion::JakX:
tbp = 0x3300; // assuming this for now...
break;
default:
ASSERT_NOT_REACHED();
}
m_copier = std::make_unique<FramebufferCopier>();
TextureInput in;
in.gpu_texture = m_copier->texture();
in.w = 32;
in.h = 32;
in.debug_page_name = "PC-BLIT";
in.debug_name = fmt::format("blit-display");
in.id = texture_pool.allocate_pc_port_texture(version);
m_gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp);
m_tbp = tbp;
}
void BlitDisplays::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// loop through all data
while (dma.current_tag_offset() != render_state->next_bucket) {
auto data = dma.read_and_advance();
if (data.vifcode0().kind == VifCode::Kind::PC_PORT) {
switch (data.vifcode0().immediate) {
case 0x10: { // copy buffer->texture (tbp in vif1)
u32 tbp = data.vifcode1().immediate;
ASSERT_MSG(tbp == m_tbp, fmt::format("unexpected tbp {}", tbp));
// 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);
} 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.
// 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;
case 0x13: {
// copy from render buffer
m_blur_old_copier.copy_now(render_state->render_fb_w, render_state->render_fb_h,
render_state->render_fb);
memcpy(&m_zoom_blur, data.data, sizeof(PcZoomBlur));
m_zoom_blur_pending = true;
} break;
case 0x14: {
m_color_filter_pending = true;
memcpy(m_color_filter.data(), data.data, sizeof(math::Vector4f));
} break;
case 0x15: { // slow-time
m_blur_old_copier.copy_now(render_state->render_fb_w, render_state->render_fb_h,
render_state->render_fb);
m_slow_time_pending = true;
memcpy(&m_slow_time_amount, data.data, sizeof(float));
} 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::do_copy_back(SharedRenderState* render_state, ScopedProfilerNode& prof) {
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;
} else if (m_zoom_blur_pending) {
do_zoom_blur(render_state, prof);
m_zoom_blur_pending = false;
} else if (m_slow_time_pending) {
do_slow_time(render_state, prof);
m_slow_time_pending = false;
}
}
void BlitDisplays::do_zoom_blur(SharedRenderState* render_state, ScopedProfilerNode& prof) {
const float texels = m_zoom_blur.texels;
// this is computing texture coordinates that zoom by texels, and offset by pos
// the reason to limit only in non-2d is not understood.
float xmin, xmax, ymin, ymax;
if (m_zoom_blur.is_2d) {
const float f2_0 = (texels / 512.f) * m_zoom_blur.pos.x();
const float f0_6 = 512.f - (texels - f2_0);
const float f3_1 = (texels / 416.f) * m_zoom_blur.pos.y();
const float f1_4 = 416.f - (texels - f3_1);
xmin = f2_0 / 512.f;
xmax = (f0_6 - 1.f) / 512.f;
ymin = f3_1 / 416.f;
ymax = (f1_4 - 1.f) / 416.f;
} else {
const float f1_10 = (texels / 512.f) * m_zoom_blur.pos.x();
const float f2_8 = std::max(0.f, std::min(f1_10, texels));
const float f0_22 = 512.f - (texels - f2_8);
const float f3_4 = (texels / 416.f) * m_zoom_blur.pos.y();
const float f3_6 = std::max(0.f, std::min(f3_4, texels));
const float f1_16 = 416.f - (texels - f3_6);
xmin = f2_8 / 512.f;
xmax = (f0_22 - 1.f) / 512.f;
ymin = f3_6 / 416.f;
ymax = (f1_16 - 1.f) / 416.f;
}
m_blur_new_copier.copy_now(render_state->render_fb_w, render_state->render_fb_h,
render_state->render_fb);
// clear screen
glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
// GL Setup
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, m_blur_old_copier.texture());
// draw old image
m_fullscreen_tex_draw.draw(m_zoom_blur.color.cast<float>() / 128.f, math::Vector2f{xmin, ymin},
math::Vector2f{xmax, ymax}, render_state, prof);
// draw new image on top of it
glBindTexture(GL_TEXTURE_2D, m_blur_new_copier.texture());
m_fullscreen_tex_draw.draw(math::Vector4f{1.f, 1.f, 1.f, m_zoom_blur.alpha_current},
math::Vector2f{0, 0}, math::Vector2f{1, 1}, render_state, prof);
}
void BlitDisplays::do_slow_time(SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_blur_new_copier.copy_now(render_state->render_fb_w, render_state->render_fb_h,
render_state->render_fb);
// clear screen
glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
// GL Setup
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, m_blur_old_copier.texture());
// draw old image
m_fullscreen_tex_draw.draw({1.f, 1.f, 1.f, 1.f}, math::Vector2f{0, 0}, math::Vector2f{1, 1},
render_state, prof);
glBindTexture(GL_TEXTURE_2D, m_blur_new_copier.texture());
m_slow_time_effect.draw(m_slow_time_amount, render_state, prof);
}
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((ImTextureID)(intptr_t)m_copier->texture(), ImVec2(w, h));
}
void BlitDisplays::apply_color_filter(SharedRenderState* render_state, ScopedProfilerNode& prof) {
if (m_color_filter_pending) {
// if the multiplier is >1, the only way I see to do this is copy and redraw the whole screen.
// doing the fullscreen draw also seems to cause alpha-related issues - the debug menu
// background stops showing up.
m_blur_new_copier.copy_now(render_state->render_fb_w, render_state->render_fb_h,
render_state->render_fb);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, m_blur_new_copier.texture());
m_fullscreen_tex_draw.draw(m_color_filter, math::Vector2f{0, 0}, math::Vector2f{1, 1},
render_state, prof);
m_color_filter_pending = false;
}
}