From 20ab48796dccf70ccd5c4cc8e473415a1f427725 Mon Sep 17 00:00:00 2001 From: ManDude <7569514+ManDude@users.noreply.github.com> Date: Fri, 15 Jul 2022 02:37:03 +0100 Subject: [PATCH] split game framebuffers to allow custom resolutions/shaders (#1641) * split rendering framebuffer * fix blackout * unhardcode c++ stuff * optimization * implement goal side of all this * fix msaa * sprite distort fixes * fix resolution menu options * save & load msaa * linear filtering on the game screen * fix bad texture units * fun! * unused * make screenshot button capture the framebuffer and not the window buffer * panik * screenshot settings * fix black screen on first frame of new framebuffer * default fullscreen resolution to the screen size * hide funny resolutions in windowed since it makes no sense --- game/graphics/gfx.cpp | 9 + game/graphics/gfx.h | 9 + .../graphics/opengl_renderer/BucketRenderer.h | 37 ++++ .../opengl_renderer/OpenGLRenderer.cpp | 160 +++++++++++++++--- .../graphics/opengl_renderer/OpenGLRenderer.h | 8 +- game/graphics/opengl_renderer/Shader.cpp | 1 + game/graphics/opengl_renderer/Shader.h | 1 + game/graphics/opengl_renderer/Sprite3.cpp | 44 ++--- game/graphics/opengl_renderer/Sprite3.h | 4 +- game/graphics/opengl_renderer/debug_gui.cpp | 3 + game/graphics/opengl_renderer/debug_gui.h | 4 + .../graphics/opengl_renderer/opengl_utils.cpp | 5 +- .../shaders/post_processing.frag | 13 ++ .../shaders/post_processing.vert | 10 ++ game/graphics/pipelines/opengl.cpp | 42 +++-- game/kernel/jak1/kmachine.cpp | 10 ++ goal_src/jak1/engine/debug/default-menu.gc | 2 + goal_src/jak1/kernel-defs.gc | 2 + goal_src/jak1/pc/pckernel-h.gc | 7 +- goal_src/jak1/pc/pckernel.gc | 27 ++- goal_src/jak1/pc/progress-pc.gc | 104 +++++++----- 21 files changed, 386 insertions(+), 116 deletions(-) create mode 100644 game/graphics/opengl_renderer/shaders/post_processing.frag create mode 100644 game/graphics/opengl_renderer/shaders/post_processing.vert diff --git a/game/graphics/gfx.cpp b/game/graphics/gfx.cpp index f9427e2fe3..294ec9e959 100644 --- a/game/graphics/gfx.cpp +++ b/game/graphics/gfx.cpp @@ -274,6 +274,15 @@ void set_window_lock(bool lock) { } } +void set_game_resolution(int w, int h) { + g_global_settings.game_res_w = w; + g_global_settings.game_res_h = h; +} + +void set_msaa(int samples) { + g_global_settings.msaa_samples = samples; +} + void input_mode_set(u32 enable) { if (enable == s7.offset + jak1_symbols::FIX_SYM_TRUE) { // #t Pad::g_input_mode_mapping = g_settings.pad_mapping_info; diff --git a/game/graphics/gfx.h b/game/graphics/gfx.h index d7ee6e5c53..d9a9710b04 100644 --- a/game/graphics/gfx.h +++ b/game/graphics/gfx.h @@ -76,6 +76,13 @@ struct GfxGlobalSettings { int lbox_w; int lbox_h; + // actual game resolution + int game_res_w = 640; + int game_res_h = 480; + + // multi-sampled anti-aliasing sample count. 1 = disabled. + int msaa_samples = 4; + // current renderer const GfxRendererModule* renderer; @@ -137,6 +144,8 @@ void set_vsync(bool vsync); void set_letterbox(int w, int h); void set_fullscreen(GfxDisplayMode mode, int screen); void set_window_lock(bool lock); +void set_game_resolution(int w, int h); +void set_msaa(int samples); void input_mode_set(u32 enable); void input_mode_save(); s64 get_mapped_button(s64 pad, s64 button); diff --git a/game/graphics/opengl_renderer/BucketRenderer.h b/game/graphics/opengl_renderer/BucketRenderer.h index 5300a4b36b..8c11760c86 100644 --- a/game/graphics/opengl_renderer/BucketRenderer.h +++ b/game/graphics/opengl_renderer/BucketRenderer.h @@ -16,6 +16,41 @@ struct LevelVis { u8 data[2048]; }; +struct FboState { + GLuint fbo = -1; + GLuint tex = -1; + GLuint fbo2 = -1; + GLuint tex2 = -1; + GLuint zbuf = -1; + GLenum render_targets[1] = {GL_COLOR_ATTACHMENT0}; + int width = 640; + int height = 480; + int msaa = 4; + + void delete_objects() { + if (fbo != -1) { + glDeleteFramebuffers(1, &fbo); + fbo = -1; + } + if (tex != -1) { + glDeleteTextures(1, &tex); + tex = -1; + } + if (fbo2 != -1) { + glDeleteFramebuffers(1, &fbo2); + fbo2 = -1; + } + if (tex2 != -1) { + glDeleteTextures(1, &tex2); + tex2 = -1; + } + if (zbuf != -1) { + glDeleteRenderbuffers(1, &zbuf); + zbuf = -1; + } + } +}; + class EyeRenderer; /*! * The main renderer will contain a single SharedRenderState that's passed to all bucket renderers. @@ -61,6 +96,8 @@ struct SharedRenderState { int window_height_px; int window_offset_x_px; int window_offset_y_px; + + FboState fbo_state; }; /*! diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index 4f7935f167..a46f9064c7 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -274,8 +274,7 @@ void OpenGLRenderer::init_bucket_renderers() { init_bucket_renderer("debug", BucketCategory::OTHER, BucketId::DEBUG, 0x20000); init_bucket_renderer("debug-no-zbuf", BucketCategory::OTHER, BucketId::DEBUG_NO_ZBUF, 0x8000); - init_bucket_renderer("subtitle", BucketCategory::OTHER, BucketId::SUBTITLE, - 0x2000); + init_bucket_renderer("subtitle", BucketCategory::OTHER, BucketId::SUBTITLE, 2000); // for now, for any unset renderers, just set them to an EmptyBucketRenderer. for (size_t i = 0; i < m_bucket_renderers.size(); i++) { @@ -303,8 +302,7 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) { { auto prof = m_profiler.root()->make_scoped_child("frame-setup"); - setup_frame(settings.window_width_px, settings.window_height_px, settings.lbox_width_px, - settings.lbox_height_px); + setup_frame(settings); } { @@ -364,8 +362,8 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) { } if (settings.save_screenshot) { - finish_screenshot(settings.screenshot_path, settings.window_width_px, settings.window_height_px, - settings.lbox_width_px, settings.lbox_height_px); + finish_screenshot(settings.screenshot_path, m_render_state.fbo_state.width, + m_render_state.fbo_state.height, 0, 0, m_render_state.fbo_state.fbo2); } } @@ -404,21 +402,104 @@ void OpenGLRenderer::draw_renderer_selection_window() { /*! * Pre-render frame setup. */ -void OpenGLRenderer::setup_frame(int window_width_px, - int window_height_px, - int offset_x, - int offset_y) { - glViewport(offset_x, offset_y, window_width_px, window_height_px); +void OpenGLRenderer::setup_frame(const RenderOptions& settings) { + auto& fbo_state = m_render_state.fbo_state; + + // restart the buffers if settings changed. + if (settings.game_res_w != fbo_state.width || settings.game_res_h != fbo_state.height || + settings.msaa_samples != fbo_state.msaa) { + fbo_state.delete_objects(); + } + + if (fbo_state.fbo == -1 || fbo_state.fbo2 == -1 || fbo_state.tex == -1 || fbo_state.tex2 == -1 || + fbo_state.zbuf == -1) { + fbo_state.width = settings.game_res_w; + fbo_state.height = settings.game_res_h; + fbo_state.msaa = settings.msaa_samples; + + bool bad = false; + + // make framebuffer object + if (fbo_state.fbo == -1) { + glGenFramebuffers(1, &fbo_state.fbo); + } + glBindFramebuffer(GL_FRAMEBUFFER, fbo_state.fbo); + + // make texture that will hold the colors of the framebuffer + if (fbo_state.tex == -1) { + glGenTextures(1, &fbo_state.tex); + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, fbo_state.tex); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, fbo_state.msaa, GL_RGBA8, fbo_state.width, + fbo_state.height, GL_TRUE); + + // make depth and stencil buffers that will hold the... depth and stencil buffers + if (fbo_state.zbuf == -1) { + glGenRenderbuffers(1, &fbo_state.zbuf); + } + glBindRenderbuffer(GL_RENDERBUFFER, fbo_state.zbuf); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, fbo_state.msaa, GL_DEPTH24_STENCIL8, + fbo_state.width, fbo_state.height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + fbo_state.zbuf); + + // attach texture to framebuffer as target for colors + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, + fbo_state.tex, 0); + + glDrawBuffers(1, fbo_state.render_targets); + + bad |= glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE; + + // make framebuffer object + if (fbo_state.fbo2 == -1) { + glGenFramebuffers(1, &fbo_state.fbo2); + } + glBindFramebuffer(GL_FRAMEBUFFER, fbo_state.fbo2); + + // make texture that will hold the msaa resolved color buffer + if (fbo_state.tex2 == -1) { + glGenTextures(1, &fbo_state.tex2); + } + glActiveTexture(GL_TEXTURE30); + glBindTexture(GL_TEXTURE_2D, fbo_state.tex2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fbo_state.width, fbo_state.height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // attach texture to framebuffer as target for colors + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_state.tex2, 0); + + glDrawBuffers(1, fbo_state.render_targets); + + bad |= glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE; + + if (!bad) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo_state.fbo); + } else { + lg::error("bad framebuffer setup. fbo: {}, tex: {}, zbuf: {}, fbo2: {}, tex2: {}", + fbo_state.fbo, fbo_state.tex, fbo_state.zbuf, fbo_state.fbo2, fbo_state.tex2); + fbo_state.delete_objects(); + } + } else { + // we have the objects. bind framebuffer. + glBindFramebuffer(GL_FRAMEBUFFER, fbo_state.fbo); + } + glViewport(0, 0, settings.game_res_w, settings.game_res_h); 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); - m_render_state.window_width_px = window_width_px; - m_render_state.window_height_px = window_height_px; - m_render_state.window_offset_x_px = offset_x; - m_render_state.window_offset_y_px = offset_y; + m_render_state.window_width_px = settings.window_width_px; + m_render_state.window_height_px = settings.window_height_px; + m_render_state.window_offset_x_px = settings.lbox_width_px; + m_render_state.window_offset_y_px = settings.lbox_height_px; } /*! @@ -488,10 +569,14 @@ void OpenGLRenderer::finish_screenshot(const std::string& output_name, int width, int height, int x, - int y) { + int y, + GLuint fbo) { std::vector buffer(width * height); glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadBuffer(GL_BACK); + GLint oldbuf; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldbuf); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0); glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data()); // flip upside down in place for (int h = 0; h < height / 2; h++) { @@ -505,19 +590,44 @@ void OpenGLRenderer::finish_screenshot(const std::string& output_name, px |= 0xff000000; } file_util::write_rgba_png(output_name, buffer.data(), width, height); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldbuf); } void OpenGLRenderer::do_pcrtc_effects(float alp, SharedRenderState* render_state, ScopedProfilerNode& prof) { - if (alp < 1) { - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - glBlendEquation(GL_FUNC_ADD); + int w = render_state->fbo_state.width; + int h = render_state->fbo_state.height; + glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->fbo_state.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, render_state->fbo_state.fbo2); + glBlitFramebuffer(0, // srcX0 + 0, // srcY0 + w, // srcX1 + h, // srcY1 + 0, // dstX0 + 0, // dstY0 + w, // dstX1 + h, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_LINEAR // filter + ); - m_blackout_renderer.draw(Vector4f(0, 0, 0, 1.f - alp), render_state, prof); + // Render to the screen directly now + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(render_state->window_offset_x_px, render_state->window_offset_y_px, + render_state->window_width_px, render_state->window_height_px); - glEnable(GL_DEPTH_TEST); - } + 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); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + glBlendEquation(GL_FUNC_ADD); + + m_blackout_renderer.draw(Vector4f(0, 0, 0, alp), render_state, prof); + + glEnable(GL_DEPTH_TEST); } diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.h b/game/graphics/opengl_renderer/OpenGLRenderer.h index 3e9794409b..975af0ed32 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.h +++ b/game/graphics/opengl_renderer/OpenGLRenderer.h @@ -22,6 +22,10 @@ struct RenderOptions { bool draw_small_profiler_window = false; bool draw_subtitle_editor_window = false; + int msaa_samples = 4; + int game_res_w = 640; + int game_res_h = 480; + bool save_screenshot = false; std::string screenshot_path; @@ -44,12 +48,12 @@ class OpenGLRenderer { void render(DmaFollower dma, const RenderOptions& settings); private: - void setup_frame(int window_width_px, int window_height_px, int offset_x, int offset_y); + void setup_frame(const RenderOptions& settings); void dispatch_buckets(DmaFollower dma, ScopedProfilerNode& prof); void do_pcrtc_effects(float alp, SharedRenderState* render_state, ScopedProfilerNode& prof); void init_bucket_renderers(); void draw_renderer_selection_window(); - void finish_screenshot(const std::string& output_name, int px, int py, int x, int y); + void finish_screenshot(const std::string& output_name, int px, int py, int x, int y, GLuint fbo); template T* init_bucket_renderer(const std::string& name, BucketCategory cat, diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index 00931cc366..a129abcb0b 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -89,6 +89,7 @@ ShaderLibrary::ShaderLibrary() { at(ShaderId::MERC2) = {"merc2"}; at(ShaderId::SPRITE_DISTORT) = {"sprite_distort"}; at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced"}; + at(ShaderId::POST_PROCESSING) = {"post_processing"}; for (auto& shader : m_shaders) { ASSERT_MSG(shader.okay(), "Shader compiled"); diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index 6b3692d7da..e05eb45345 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -44,6 +44,7 @@ enum class ShaderId { MERC2 = 19, SPRITE_DISTORT = 20, SPRITE_DISTORT_INSTANCED = 21, + POST_PROCESSING = 22, MAX_SHADERS }; diff --git a/game/graphics/opengl_renderer/Sprite3.cpp b/game/graphics/opengl_renderer/Sprite3.cpp index 425d2ad370..61eeb842f0 100644 --- a/game/graphics/opengl_renderer/Sprite3.cpp +++ b/game/graphics/opengl_renderer/Sprite3.cpp @@ -44,9 +44,9 @@ bool looks_like_distort_frame_data(const DmaFollower& dma) { } } // namespace -constexpr int SPRITE_RENDERER_MAX_SPRITES = 8000; +constexpr int SPRITE_RENDERER_MAX_SPRITES = 1920 * 10; constexpr int SPRITE_RENDERER_MAX_DISTORT_SPRITES = - 256 * 8; // size of sprite-aux-list in GOAL code * SPRITE_MAX_AMOUNT_MULT + 256 * 10; // size of sprite-aux-list in GOAL code * SPRITE_MAX_AMOUNT_MULT Sprite3::Sprite3(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id), m_direct(name, my_id, 1024) { @@ -519,10 +519,10 @@ void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) { */ void Sprite3::distort_setup_instanced(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { - if (m_distort_instanced_ogl.last_window_width != render_state->window_width_px || - m_distort_instanced_ogl.last_window_height != render_state->window_height_px) { - m_distort_instanced_ogl.last_window_width = render_state->window_width_px; - m_distort_instanced_ogl.last_window_height = render_state->window_height_px; + if (m_distort_instanced_ogl.last_width != render_state->fbo_state.width || + m_distort_instanced_ogl.last_height != render_state->fbo_state.height) { + m_distort_instanced_ogl.last_width = render_state->fbo_state.width; + m_distort_instanced_ogl.last_height = render_state->fbo_state.height; // Window dimensions changed, which means the aspect ratio may have changed, which means we have // a new sine table @@ -702,22 +702,22 @@ void Sprite3::distort_draw_instanced(SharedRenderState* render_state, ScopedProf void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { // The distort effect needs to read the current framebuffer, so copy what's been rendered so far // to a texture that we can then pass to the shader - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->fbo_state.fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo); - glBlitFramebuffer(render_state->window_offset_x_px, // srcX0 - render_state->window_offset_y_px, // srcY0 - render_state->window_width_px + render_state->window_offset_x_px, // srcX1 - render_state->window_height_px + render_state->window_offset_y_px, // srcY1 - 0, // dstX0 - 0, // dstY0 - m_distort_ogl.fbo_width, // dstX1 - m_distort_ogl.fbo_height, // dstY1 - GL_COLOR_BUFFER_BIT, // mask - GL_NEAREST // filter + glBlitFramebuffer(0, // srcX0 + 0, // srcY0 + render_state->fbo_state.width, // srcX1 + render_state->fbo_state.height, // srcY1 + 0, // dstX0 + 0, // dstY0 + m_distort_ogl.fbo_width, // dstX1 + m_distort_ogl.fbo_height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter ); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, render_state->fbo_state.fbo); // Set up OpenGL state m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf @@ -732,10 +732,10 @@ void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfile void Sprite3::distort_setup_framebuffer_dims(SharedRenderState* render_state) { // Distort framebuffer must be the same dimensions as the default window framebuffer - if (m_distort_ogl.fbo_width != render_state->window_width_px || - m_distort_ogl.fbo_height != render_state->window_height_px) { - m_distort_ogl.fbo_width = render_state->window_width_px; - m_distort_ogl.fbo_height = render_state->window_height_px; + if (m_distort_ogl.fbo_width != render_state->fbo_state.width || + m_distort_ogl.fbo_height != render_state->fbo_state.height) { + m_distort_ogl.fbo_width = render_state->fbo_state.width; + m_distort_ogl.fbo_height = render_state->fbo_state.height; glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); diff --git a/game/graphics/opengl_renderer/Sprite3.h b/game/graphics/opengl_renderer/Sprite3.h index fd622e1321..72423a9a24 100644 --- a/game/graphics/opengl_renderer/Sprite3.h +++ b/game/graphics/opengl_renderer/Sprite3.h @@ -117,8 +117,8 @@ class Sprite3 : public BucketRenderer { GLuint vao; GLuint vertex_buffer; // contains vertex data for each possible sprite resolution (3-11) GLuint instance_buffer; // contains all instance specific data for each sprite per frame - int last_window_width = -1; - int last_window_height = -1; + int last_width = -1; + int last_height = -1; bool vertex_data_changed = false; } m_distort_instanced_ogl; diff --git a/game/graphics/opengl_renderer/debug_gui.cpp b/game/graphics/opengl_renderer/debug_gui.cpp index 2fb9fcffba..8cf4714448 100644 --- a/game/graphics/opengl_renderer/debug_gui.cpp +++ b/game/graphics/opengl_renderer/debug_gui.cpp @@ -110,6 +110,9 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) { if (ImGui::BeginMenu("Screenshot")) { ImGui::MenuItem("Screenshot Next Frame!", nullptr, &m_want_screenshot); ImGui::InputText("File", m_screenshot_save_name, 50); + ImGui::InputInt("Width", &screenshot_width); + ImGui::InputInt("Height", &screenshot_height); + ImGui::InputInt("MSAA", &screenshot_samples); ImGui::EndMenu(); } diff --git a/game/graphics/opengl_renderer/debug_gui.h b/game/graphics/opengl_renderer/debug_gui.h index bdefa0d093..4e6282737d 100644 --- a/game/graphics/opengl_renderer/debug_gui.h +++ b/game/graphics/opengl_renderer/debug_gui.h @@ -63,6 +63,10 @@ class OpenGlDebugGui { bool dump_events = false; bool want_reboot_in_debug = false; + int screenshot_width = 1920; + int screenshot_height = 1080; + int screenshot_samples = 16; + private: FrameTimeRecorder m_frame_timer; bool m_draw_frame_time = false; diff --git a/game/graphics/opengl_renderer/opengl_utils.cpp b/game/graphics/opengl_renderer/opengl_utils.cpp index c17abcfae6..0d181f8244 100644 --- a/game/graphics/opengl_renderer/opengl_utils.cpp +++ b/game/graphics/opengl_renderer/opengl_utils.cpp @@ -140,11 +140,12 @@ void FullScreenDraw::draw(const math::Vector4f& color, ScopedProfilerNode& prof) { glBindVertexArray(m_vao); glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); - auto& shader = render_state->shaders[ShaderId::SOLID_COLOR]; + auto& shader = render_state->shaders[ShaderId::POST_PROCESSING]; shader.activate(); glUniform4f(glGetUniformLocation(shader.id(), "fragment_color"), color[0], color[1], color[2], color[3]); + prof.add_tri(2); prof.add_draw_call(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} \ No newline at end of file +} diff --git a/game/graphics/opengl_renderer/shaders/post_processing.frag b/game/graphics/opengl_renderer/shaders/post_processing.frag new file mode 100644 index 0000000000..244753aa00 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/post_processing.frag @@ -0,0 +1,13 @@ +#version 430 core + +in vec2 screen_pos; + +out vec4 color; + +uniform vec4 fragment_color; + +layout (binding = 30) uniform sampler2D screen_tex; + +void main() { + color = vec4(texture(screen_tex, screen_pos).rgb * fragment_color.a, 1.0); +} diff --git a/game/graphics/opengl_renderer/shaders/post_processing.vert b/game/graphics/opengl_renderer/shaders/post_processing.vert new file mode 100644 index 0000000000..bc1e305596 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/post_processing.vert @@ -0,0 +1,10 @@ +#version 430 core + +layout (location = 0) in vec2 position_in; + +out vec2 screen_pos; + +void main() { + gl_Position = vec4(position_in, 0, 1.0); + screen_pos = (position_in + 1) / 2; +} diff --git a/game/graphics/pipelines/opengl.cpp b/game/graphics/pipelines/opengl.cpp index be0badd265..93299517db 100644 --- a/game/graphics/pipelines/opengl.cpp +++ b/game/graphics/pipelines/opengl.cpp @@ -140,7 +140,6 @@ static int gl_init(GfxSettings& settings) { glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_FALSE); } glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); - glfwWindowHint(GLFW_SAMPLES, 1); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); return 0; @@ -263,7 +262,13 @@ static bool endsWith(std::string_view str, std::string_view suffix) { 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); } -void render_game_frame(int width, int height, int lbox_width, int lbox_height) { +void render_game_frame(int game_width, + int game_height, + int window_width, + int window_height, + int lbox_width, + int lbox_height, + int msaa_samples) { // wait for a copied chain. bool got_chain = false; { @@ -278,16 +283,32 @@ void render_game_frame(int width, int height, int lbox_width, int lbox_height) { if (got_chain) { g_gfx_data->frame_idx_of_input_data = g_gfx_data->frame_idx; RenderOptions options; - options.window_height_px = height; - options.window_width_px = width; + options.game_res_w = game_width; + options.game_res_h = game_height; + options.window_height_px = window_height; + options.window_width_px = window_width; options.lbox_height_px = lbox_height; options.lbox_width_px = lbox_width; + options.msaa_samples = msaa_samples; options.draw_render_debug_window = g_gfx_data->debug_gui.should_draw_render_debug(); options.draw_profiler_window = g_gfx_data->debug_gui.should_draw_profiler(); options.draw_subtitle_editor_window = g_gfx_data->debug_gui.should_draw_subtitle_editor(); - options.save_screenshot = g_gfx_data->debug_gui.get_screenshot_flag(); + options.save_screenshot = false; + if (g_gfx_data->debug_gui.get_screenshot_flag()) { + options.save_screenshot = true; + options.game_res_w = g_gfx_data->debug_gui.screenshot_width; + options.game_res_h = g_gfx_data->debug_gui.screenshot_height; + options.msaa_samples = g_gfx_data->debug_gui.screenshot_samples; + } options.draw_small_profiler_window = g_gfx_data->debug_gui.small_profiler; options.pmode_alp_register = g_gfx_data->pmode_alp; + + GLint msaa_max; + glGetIntegerv(GL_MAX_SAMPLES, &msaa_max); + if (options.msaa_samples > msaa_max) { + options.msaa_samples = msaa_max; + } + if (options.save_screenshot) { // ensure the screenshot has an extension std::string temp_path = g_gfx_data->debug_gui.screenshot_name(); @@ -471,8 +492,8 @@ void GLDisplay::render() { } // window size - int width = Gfx::g_global_settings.lbox_w; - int height = Gfx::g_global_settings.lbox_h; + int win_w = Gfx::g_global_settings.lbox_w; + int win_h = Gfx::g_global_settings.lbox_h; int fbuf_w, fbuf_h; glfwGetFramebufferSize(m_window, &fbuf_w, &fbuf_h); #ifdef _WIN32 @@ -482,9 +503,9 @@ void GLDisplay::render() { } #endif // horizontal letterbox size - int lbox_w = (fbuf_w - width) / 2; + int lbox_w = (fbuf_w - win_w) / 2; // vertical letterbox size - int lbox_h = (fbuf_h - height) / 2; + int lbox_h = (fbuf_h - win_h) / 2; #ifdef _WIN32 if (last_fullscreen_mode() == GfxDisplayMode::Borderless) { // add one pixel of vertical letterbox on borderless to make up for extra line @@ -495,7 +516,8 @@ void GLDisplay::render() { // render game! if (g_gfx_data->debug_gui.should_advance_frame()) { auto p = scoped_prof("game-render"); - render_game_frame(width, height, lbox_w, lbox_h); + render_game_frame(Gfx::g_global_settings.game_res_w, Gfx::g_global_settings.game_res_h, win_w, + win_h, lbox_w, lbox_h, Gfx::g_global_settings.msaa_samples); } if (g_gfx_data->debug_gui.should_gl_finish()) { diff --git a/game/kernel/jak1/kmachine.cpp b/game/kernel/jak1/kmachine.cpp index 2bf0d8abd0..eb58a3872b 100644 --- a/game/kernel/jak1/kmachine.cpp +++ b/game/kernel/jak1/kmachine.cpp @@ -586,6 +586,14 @@ void set_fullscreen(u32 symptr, s64 screen) { } } +void set_game_resolution(s64 w, s64 h) { + Gfx::set_game_resolution(w, h); +} + +void set_msaa(s64 samples) { + Gfx::set_msaa(samples); +} + void InitMachine_PCPort() { // PC Port added functions @@ -619,6 +627,8 @@ void InitMachine_PCPort() { make_function_symbol_from_c("pc-set-frame-rate", (void*)set_frame_rate); make_function_symbol_from_c("pc-set-vsync", (void*)set_vsync); make_function_symbol_from_c("pc-set-window-lock", (void*)set_window_lock); + make_function_symbol_from_c("pc-set-game-resolution", (void*)set_game_resolution); + make_function_symbol_from_c("pc-set-msaa", (void*)set_msaa); // graphics things make_function_symbol_from_c("pc-set-letterbox", (void*)Gfx::set_letterbox); diff --git a/goal_src/jak1/engine/debug/default-menu.gc b/goal_src/jak1/engine/debug/default-menu.gc index 2b4385e335..ed10a187b4 100644 --- a/goal_src/jak1/engine/debug/default-menu.gc +++ b/goal_src/jak1/engine/debug/default-menu.gc @@ -4540,6 +4540,8 @@ (function "2560 x 1440" #f (lambda () (set-size! *pc-settings* 2560 1440))) (function "2880 x 2160" #f (lambda () (set-size! *pc-settings* 2880 2160))) (function "3840 x 2160" #f (lambda () (set-size! *pc-settings* 3840 2160))) + (function "512 x 224" #f (lambda () (set-size! *pc-settings* 512 224))) + (function "512 x 256" #f (lambda () (set-size! *pc-settings* 512 256))) (function "512 x 448" #f (lambda () (set-size! *pc-settings* 512 448))) (function "512 x 512" #f (lambda () (set-size! *pc-settings* 512 512))) ) diff --git a/goal_src/jak1/kernel-defs.gc b/goal_src/jak1/kernel-defs.gc index 4c62e4d1c1..92b5f5eaf0 100644 --- a/goal_src/jak1/kernel-defs.gc +++ b/goal_src/jak1/kernel-defs.gc @@ -358,6 +358,8 @@ (define-extern pc-sound-set-flava-hack (function int none)) (define-extern pc-sound-set-fade-hack (function int none)) (define-extern pc-set-window-lock (function symbol none)) +(define-extern pc-set-game-resolution (function int int none)) +(define-extern pc-set-msaa (function int none)) (defenum pc-prof-event (begin 0) diff --git a/goal_src/jak1/pc/pckernel-h.gc b/goal_src/jak1/pc/pckernel-h.gc index 811c6cf090..b8e9ff546a 100644 --- a/goal_src/jak1/pc/pckernel-h.gc +++ b/goal_src/jak1/pc/pckernel-h.gc @@ -29,7 +29,7 @@ (defglobalconstant PC_KERNEL_VERSION_BUILD #x0001) -(defglobalconstant PC_KERNEL_VERSION_REVISION #x0007) +(defglobalconstant PC_KERNEL_VERSION_REVISION #x0008) (defglobalconstant PC_KERNEL_VERSION_MINOR #x0006) (defglobalconstant PC_KERNEL_VERSION_MAJOR #x0001) @@ -256,7 +256,7 @@ (gfx-renderer pc-gfx-renderer) ;; the renderer to use (gfx-resolution float) ;; for supersampling (gfx-anisotropy float) ;; for anisotropy - (gfx-msaa int) ;; for MSAA + (gfx-msaa int32) ;; for MSAA ;; ps2 settings (ps2-read-speed? symbol) ;; emulate DVD loads @@ -417,8 +417,7 @@ "Set the default graphics settings" (set! (-> obj target-fps) 60) - (set! (-> obj width) PC_BASE_WIDTH) - (set! (-> obj height) PC_BASE_HEIGHT) + (pc-get-screen-size -1 (&-> obj width) (&-> obj height)) (set! (-> obj use-vis?) #f) (set! (-> obj aspect-ratio-auto?) #f) (set! (-> obj vsync?) #t) diff --git a/goal_src/jak1/pc/pckernel.gc b/goal_src/jak1/pc/pckernel.gc index e5b60ed0a7..a92e38b43c 100644 --- a/goal_src/jak1/pc/pckernel.gc +++ b/goal_src/jak1/pc/pckernel.gc @@ -66,8 +66,8 @@ (set! (-> obj win-height) height) ) (else - (format 0 "Nope! No changing fullscreen resolution for now!~%") - (format #t "Nope! No changing fullscreen resolution for now!~%") + (set! (-> obj width) width) + (set! (-> obj height) height) ) ) (none)) @@ -98,7 +98,7 @@ (defmethod set-frame-rate! pc-settings ((obj pc-settings) (rate int)) "set the aspect ratio used for rendering." - + (pc-set-frame-rate rate) (if (and (!= 'fullscreen (-> obj display-mode)) (!= (pc-get-screen-rate -1) rate)) @@ -120,7 +120,7 @@ (set-video-mode '150fps) ) ) - + rate) (defmethod commit-to-file pc-settings ((obj pc-settings)) @@ -187,7 +187,9 @@ ) ) + ;; set fullscreen to what we want (pc-set-fullscreen (-> obj display-mode) 0) + ;; set window size and fps automatically if the window lock is enabled. (when (-> obj window-lock?) (pc-set-vsync (-> obj vsync?)) @@ -201,6 +203,21 @@ (!= (-> obj win-height) (-> obj real-height)))) (pc-set-window-size (max PC_MIN_WIDTH (-> obj win-width)) (max PC_MIN_HEIGHT (-> obj win-height)))) ) + ;; do game resolution + (if (= (-> obj display-mode) 'windowed) + (pc-set-game-resolution (-> obj win-width) (-> obj win-height)) + (pc-set-game-resolution (-> obj width) (-> obj height))) + + ;; set msaa sample rate. if invalid, just reset to 4. + (let ((valid? #f)) + (dotimes (i 31) + (if (= (-> obj gfx-msaa) (ash 1 i)) + (true! valid?)) + ) + + (if (not valid?) (set! (-> obj gfx-msaa) 4)) + (pc-set-msaa (-> obj gfx-msaa)) + ) (pc-discord-rpc-set (if (-> obj discord-rpc?) 1 0)) @@ -753,6 +770,7 @@ (set! (-> obj width) (file-stream-read-int file)) (set! (-> obj height) (file-stream-read-int file)) ) + (("msaa") (set! (-> obj gfx-msaa) (file-stream-read-int file))) (("aspect") (set-aspect! obj (file-stream-read-int file) (file-stream-read-int file))) (("aspect-test") (set! (-> obj aspect-custom-x) (file-stream-read-int file)) @@ -872,6 +890,7 @@ (format file " (fps ~D)~%" (-> obj target-fps)) (format file " (game-size ~D ~D)~%" (-> obj width) (-> obj height)) + (format file " (msaa ~D)~%" (-> obj gfx-msaa)) (format file " (window-size ~D ~D)~%" (-> obj win-width) (-> obj win-height)) (format file " (aspect ~D ~D)~%" (-> obj width) (-> obj height)) (format file " (aspect-test ~D ~D)~%" (-> obj aspect-custom-x) (-> obj aspect-custom-y)) diff --git a/goal_src/jak1/pc/progress-pc.gc b/goal_src/jak1/pc/progress-pc.gc index 3da013cbea..487dcea44d 100644 --- a/goal_src/jak1/pc/progress-pc.gc +++ b/goal_src/jak1/pc/progress-pc.gc @@ -62,7 +62,7 @@ (subtitle-backup pc-subtitle-lang) (aspect-native-choice symbol) (current-carousell (array game-text-id)) - + (selection int) (direction symbol) (transition symbol) @@ -127,7 +127,7 @@ (new 'static 'game-option :option-type (game-option-type display-mode) :name (game-text-id display-mode) :scale #t) (new 'static 'game-option :option-type (game-option-type on-off) :name (game-text-id vsync) :scale #t) (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id aspect-ratio) :scale #t :param3 (game-option-menu aspect-ratio)) - ;(new 'static 'game-option :option-type (game-option-type msaa) :name (game-text-id msaa) :scale #t) + (new 'static 'game-option :option-type (game-option-type msaa) :name (game-text-id msaa) :scale #t) (new 'static 'game-option :option-type (game-option-type frame-rate) :name (game-text-id frame-rate) :scale #t) (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id ps2-options) :scale #t :param3 (game-option-menu gfx-ps2-options)) (new 'static 'game-option :option-type (game-option-type button) :name (game-text-id back) :scale #t) @@ -139,7 +139,7 @@ (new 'static 'game-option :option-type (game-option-type display-mode) :name (game-text-id display-mode) :scale #t) (new 'static 'game-option :option-type (game-option-type on-off) :name (game-text-id vsync) :scale #t) (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id aspect-ratio) :scale #t :param3 (game-option-menu aspect-ratio)) - ;(new 'static 'game-option :option-type (game-option-type msaa) :name (game-text-id msaa) :scale #t) + (new 'static 'game-option :option-type (game-option-type msaa) :name (game-text-id msaa) :scale #t) (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id ps2-options) :scale #t :param3 (game-option-menu gfx-ps2-options)) (new 'static 'game-option :option-type (game-option-type button) :name (game-text-id back) :scale #t) ) @@ -321,14 +321,14 @@ (defun find-highest-resolution () - + (let ((sx 0) (sy 0) (found #f) (mult GAME_MIN_RES_MULT) (lastr 0) (hir 0) (lor 0)) (pc-get-screen-size -1 (the (pointer int32) (& sx)) (the (pointer int32) (& sy))) (if (> sy sx) (set! sy sx)) - + (until found - + (dotimes (i (-> *resolutions* length)) (let ((thisr (the int (* mult (the float (-> *resolutions* i)))))) (cond @@ -342,15 +342,15 @@ (set! lastr thisr)) ) )) - + (if (not found) (*! mult 2)) - + ) (set! hir lastr) mult ) - + ) (defmacro add-resolution-option (x y) @@ -361,7 +361,7 @@ (set! (-> option param1) (the float ,x)) (set! (-> option param2) (the float ,y)) (set! (-> option scale) #t) - + (1+! (-> *temp-options* length)) ) ) @@ -373,7 +373,7 @@ (set! (-> option name) ,text-id) (set! (-> option param1) (the float ,flava)) (set! (-> option scale) #t) - + (1+! (-> *temp-options* length)) ) ) @@ -384,35 +384,34 @@ (set! (-> option option-type) (game-option-type button)) (set! (-> option name) (game-text-id back)) (set! (-> option scale) #t) - + (1+! (-> *temp-options* length)) ) ) (defun build-resolution-options ((skip int) (amount int)) - + (set! (-> *temp-options* length) 0) - - (let ((done? #f) (mult (find-highest-resolution)) (sx 0) (sy 0) (flip? #f) (aspect 0.0) + + (let* ((done? #f) (mult (find-highest-resolution)) (sx 0) (sy 0) + (display-mode (pc-get-fullscreen)) ;; hack - do not use screen resolution in windowed mode, taskbar etc. messes it up and makes it useless! - (skip? (= 'windowed (pc-get-fullscreen))) + (skip? (= 'windowed display-mode)) ;; shortcut (max-options (1- (-> *temp-options* allocated-length)))) - + ;; get screen size (for capping) (pc-get-screen-size -1 (the (pointer int32) (& sx)) (the (pointer int32) (& sy))) - ;; portrait mode. unused - (set! flip? (> sy sx)) - - (case (pc-get-fullscreen) - (('fullscreen) + + (case display-mode + #|(('fullscreen) ;; insert psyched out trollface here (set! sx 0) (set! sy 0) (countdown (i (pc-get-screen-vmode-count)) (let ((thisx 0) (thisy 0)) (pc-get-screen-size i (the (pointer int32) (& thisx)) (the (pointer int32) (& thisy))) - + (when (not (and (= thisx sx) (= thisy sy))) (add-resolution-option thisx thisy) ) @@ -420,24 +419,32 @@ (set! sy thisy) ) ) - ) - + )|# + (else ;; extra button when fullscreen (when (not skip?) (add-resolution-option sx sy) (set! (-> *temp-options* (1- (length *temp-options*)) name) (game-text-id fit-to-screen)) ) + + ;; supersampling in fullscreen and stuff. this results in way too many resolution options lol. + (when (!= display-mode 'windowed) + (*! sx 4) + (*! sy 4) + (*! mult 4) + ) + ;; game aspect ratio - (set! aspect (-> *pc-settings* aspect-ratio)) - + (let ((aspect (-> *pc-settings* aspect-ratio))) + (until (or done? (= (length *temp-options*) max-options)) (countdown (i (-> *resolutions* length)) (let* ((thisr (the int (* mult (the float (-> *resolutions* i))))) (thisx (the int (* aspect (the float thisr)))) (thisy thisr) ) - + (when (and (not skip?) (< (length *temp-options*) max-options) (<= thisy sy) (<= thisx sx) (= aspect (/ (the float thisx) (the float thisy))) @@ -446,31 +453,38 @@ )) (false! skip?) ) - + (if (> mult GAME_MIN_RES_MULT) - (*! mult 0.5) + (/! mult 2) (true! done?)) - ) + )) ) ) + ;; extra funny options + (when (and (!= display-mode 'windowed) + (< (length *temp-options*) (- (-> *temp-options* allocated-length) 3))) + (add-resolution-option 512 224) + (add-resolution-option 512 448) + ) + (add-back-option) ) - - + + *temp-options* ) (defun build-flava-player-options ((mus-idx int)) - + (set! (-> *temp-options* length) 0) - + (dotimes (i (-> *music-flava-name-list* mus-idx length)) (if (nonzero? (-> *music-flava-name-list* mus-idx i)) (add-flava-player-option (-> *music-flava-name-list* mus-idx i) i) ) ) (add-back-option) - + *temp-options* ) @@ -511,7 +525,7 @@ (defun progress-draw-carousell-from-string-list ((options (array game-text-id)) (font font-context) (y-off int) (new-val int)) "yep." - + (let ((old-lang (-> *progress-carousell* selection)) (new-lang new-val) (max-lang (length options)) @@ -647,12 +661,12 @@ (set! (-> *options-remap* (progress-screen flava-player)) *temp-options*) (set! (-> *options-remap* (progress-screen memcard-disable-auto-save)) *yes-no-options*) (set! (-> *options-remap* (progress-screen memcard-auto-save-disabled)) *ok-options*) - + ;; set default params (set! (-> *progress-state* aspect-ratio-choice) (get-aspect-ratio)) (set! (-> *progress-state* video-mode-choice) (get-video-mode)) (set! (-> *progress-state* yes-no-choice) #f) - + ;; set variable pointers (set! (-> *game-options* 0 value-to-modify) (&-> *setting-control* default vibration)) (set! (-> *game-options* 1 value-to-modify) (&-> *setting-control* default play-hints)) @@ -677,11 +691,11 @@ (set! (-> *game-options-pc* 7 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-pc* 1 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-pc* 2 value-to-modify) (&-> *pc-settings* vsync?)) - ;(set! (-> *graphic-options-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) + (set! (-> *graphic-options-pc* 5 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-no-frame-rate-pc* 1 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-no-frame-rate-pc* 2 value-to-modify) (&-> *pc-settings* vsync?)) - ;(set! (-> *graphic-options-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) + (set! (-> *graphic-options-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *misc-options* 0 value-to-modify) (&-> *pc-settings* discord-rpc?)) (set! (-> *misc-options* 1 value-to-modify) (&-> *pc-settings* money-starburst?)) (set! (-> *camera-options* 0 value-to-modify) (&-> *pc-settings* first-camera-h-inverted?)) @@ -697,7 +711,7 @@ (set! (-> *sound-options-pc* 2 value-to-modify) (&-> *setting-control* default dialog-volume)) ;(set! (-> *sound-options-pc* 3 value-to-modify) (&-> *pc-settings* music-fadein?)) (set! (-> *progress-carousell* aspect-native-choice) (-> *pc-settings* use-vis?)) - + ;; scroll stuff! (progress-scroll-reset) (none) @@ -1339,7 +1353,7 @@ (defmethod respond-memcard progress ((obj progress)) "logic for handling input in memcard screens." - + (let ((s5-0 (-> obj card-info))) (when (and s5-0 (not (-> obj in-transition))) (when (or (cpad-pressed? 0 x) (cpad-pressed? 0 circle)) @@ -1993,7 +2007,7 @@ ;; override the post handler for progress-normal -(set! (-> progress-normal post) +(set! (-> progress-normal post) (lambda :behavior progress () (when (progress-scrolling?) (seek! (-> *progress-scroll* transition) 1.0 (* (/ 1.75 512) (-> self transition-speed) (-> *display* time-adjust-ratio))) @@ -2156,7 +2170,7 @@ )) ;; override the enter handler for progress-going-out -(set! (-> progress-going-out enter) +(set! (-> progress-going-out enter) (lambda :behavior progress () (sound-play "menu-close") (hide-progress-icons)