mirror of
https://github.com/open-goal/jak-project
synced 2026-07-09 14:55:51 -04:00
4076b1477e
When `do_zoom_blur` sets up its draw, it doesn't update the blend settings, so it ends up inheriting the previous values. In retail mode, the previous draw is from a texture animation, which sets up the blend in a specific way that causes it to draw a white texture over the screen. In debug mode, right before the zoom blur is drawn, the debug text is drawn, which modifies the blend settings to something that makes the blur look as it should. Closes #4206
227 lines
8.8 KiB
C++
227 lines
8.8 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);
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
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;
|
|
}
|
|
}
|