diff --git a/code/def_files/data/effects/gamma-correct-f.sdr b/code/def_files/data/effects/gamma-correct-f.sdr new file mode 100644 index 00000000000..8bf66ae8fa8 --- /dev/null +++ b/code/def_files/data/effects/gamma-correct-f.sdr @@ -0,0 +1,18 @@ +in vec4 fragTexCoord; +out vec4 fragOut0; + +uniform sampler2D tex; + +layout (std140) uniform genericData { + float gamma; +}; + +void main() +{ + vec4 color = texture(tex, fragTexCoord.xy); + + float g = max(gamma, 0.001); + color.rgb = pow(color.rgb, vec3(1.0 / g)); + + fragOut0 = color; +} diff --git a/code/graphics/2d.cpp b/code/graphics/2d.cpp index 189227e8912..1d597436413 100644 --- a/code/graphics/2d.cpp +++ b/code/graphics/2d.cpp @@ -313,7 +313,7 @@ bool Save_custom_screen_size; bool Deferred_lighting = false; bool High_dynamic_range = false; -static ushort* Gr_original_gamma_ramp = nullptr; + static int videodisplay_deserializer(const json_t* value) { @@ -755,7 +755,6 @@ void removeVSyncOption() static std::unique_ptr UniformBufferManager; // Forward definitions -static void uniform_buffer_managers_init(); static void uniform_buffer_managers_deinit(); static void uniform_buffer_managers_retire_buffers(); @@ -1305,15 +1304,6 @@ void gr_close() if(Cmdline_enable_vr) openxr_close(); - if (Gr_original_gamma_ramp != nullptr && os::getSDLMainWindow() != nullptr) { - SDL_SetWindowGammaRamp(os::getSDLMainWindow(), Gr_original_gamma_ramp, (Gr_original_gamma_ramp + 256), - (Gr_original_gamma_ramp + 512)); - } - - // This is valid even if Gr_original_gamma_ramp is nullptr - vm_free(Gr_original_gamma_ramp); - Gr_original_gamma_ramp = nullptr; - gpu_heap_deinit(); // Cleanup uniform buffer managers @@ -1870,8 +1860,13 @@ bool gr_init(std::unique_ptr&& graphicsOps, int d_mode, // (be that a window, a render overlay from nsight, or an FSO-internal buffer) instead of directly rendering to the OS-provided direct screen backbuffer. // As the cost of -window_res is one single blit of a fullscreen buffer, it's probably an acceptable compromise to get rid of render artifacts. // As such, forcibly enable -window_res at the screen resolution here, if we're in fullscreen. - + // // Additionally, SDL3+ doesn't work when reading from the GL_FRONT buffers, so we need our own intermediate buffers. + // + // Furthermore, gamma handling now also requires a blit of the final scene (not just the 3D scene, since it needs to work on menus as well). + // + // As such, window_res is effectively no longer optional, part of the core render path and must always be enabled. + // The only reason it is not yet refactored into an always-on thing is due to issues with FRED integration. Cmdline_window_res.emplace(static_cast(width), static_cast(height)); } @@ -1984,9 +1979,6 @@ bool gr_init(std::unique_ptr&& graphicsOps, int d_mode, gr_light_init(); - // Initialize uniform buffer managers - uniform_buffer_managers_init(); - gpu_heap_init(); mprintf(("Checking graphics capabilities:\n")); @@ -2995,7 +2987,7 @@ void gr_print_timestamp(int x, int y, fix timestamp, int resize_mode) gr_string(x, y, time.c_str(), resize_mode); } -static void uniform_buffer_managers_init() +void gr_uniform_buffer_managers_init() { if (gr_screen.mode == GR_STUB) { return; @@ -3197,62 +3189,6 @@ void gr_heap_deallocate(GpuHeap heap_type, size_t data_offset) gpuHeap->freeGpuData(data_offset); } -// I feel dirty... -static void make_gamma_ramp(float gamma, ushort* ramp) -{ - ushort x, y; - ushort base_ramp[256]; - - Assert(ramp != nullptr); - - // generate the base ramp values first off - - // if no gamma set then just do this quickly - if (gamma <= 0.0f) { - memset(ramp, 0, 3 * 256 * sizeof(ushort)); - return; - } - // identity gamma, avoid all of the math - else if (gamma == 1.0f || Gr_original_gamma_ramp == nullptr) { - if (Gr_original_gamma_ramp != nullptr) { - memcpy(ramp, Gr_original_gamma_ramp, 3 * 256 * sizeof(ushort)); - } - // set identity if no original ramp - else { - for (x = 0; x < 256; x++) { - ramp[x] = (x << 8) | x; - ramp[x + 256] = (x << 8) | x; - ramp[x + 512] = (x << 8) | x; - } - } - - return; - } - // for everything else we need to actually figure it up - else { - double g = 1.0 / (double)gamma; - double val; - - Assert(Gr_original_gamma_ramp != nullptr); - - for (x = 0; x < 256; x++) { - val = (pow(x / 255.0, g) * 65535.0 + 0.5); - CLAMP(val, 0., 65535.); - - base_ramp[x] = (ushort)val; - } - - for (y = 0; y < 3; y++) { - for (x = 0; x < 256; x++) { - val = (base_ramp[x] * 2) - Gr_original_gamma_ramp[x + y * 256]; - CLAMP(val, 0., 65535.); - - ramp[x + y * 256] = (ushort)val; - } - } - } -} - void gr_set_gamma(float gamma) { if (gr_screen.mode == GR_STUB) { @@ -3260,38 +3196,6 @@ void gr_set_gamma(float gamma) } Gr_gamma = gamma; - - // new way - but not while running FRED - if (!Fred_running && !Cmdline_no_set_gamma && os::getSDLMainWindow() != nullptr) { - if (Gr_original_gamma_ramp == nullptr) { - // First time we are here so get the current (original) gamma ramp here so we can reset it later - Gr_original_gamma_ramp = (ushort*)vm_malloc(3 * 256 * sizeof(ushort), memory::quiet_alloc); - - if (Gr_original_gamma_ramp == nullptr) { - mprintf((" Unable to allocate memory for gamma ramp! Disabling...\n")); - Cmdline_no_set_gamma = 1; - } else { - SDL_GetWindowGammaRamp(os::getSDLMainWindow(), Gr_original_gamma_ramp, (Gr_original_gamma_ramp + 256), - (Gr_original_gamma_ramp + 512)); - } - } - - auto gamma_ramp = (ushort*)vm_malloc(3 * 256 * sizeof(ushort), memory::quiet_alloc); - - if (gamma_ramp == nullptr) { - Int3(); - return; - } - - memset(gamma_ramp, 0, 3 * 256 * sizeof(ushort)); - - // Create the Gamma lookup table - make_gamma_ramp(gamma, gamma_ramp); - - SDL_SetWindowGammaRamp(os::getSDLMainWindow(), gamma_ramp, (gamma_ramp + 256), (gamma_ramp + 512)); - - vm_free(gamma_ramp); - } } void gr_get_post_process_effect_names(SCP_vector& names) diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 53dbc1980e2..66159bd9f7e 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -214,6 +214,8 @@ enum shader_type { SDR_TYPE_IRRADIANCE_MAP_GEN, + SDR_TYPE_GAMMA_BLIT, + NUM_SHADER_TYPES }; @@ -1487,6 +1489,8 @@ void gr_get_post_process_effect_names(SCP_vector &names); bool gr_is_viewport_window(); +void gr_uniform_buffer_managers_init(); + // Include this last to make the 2D rendering function available everywhere #include "graphics/render.h" diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 79252e7eec3..c408d17d288 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -119,15 +119,24 @@ void gr_opengl_flip() return; if (Cmdline_window_res) { - GL_state.BindFrameBuffer(0, GL_DRAW_FRAMEBUFFER); - GL_state.BindFrameBuffer(Back_framebuffer, GL_READ_FRAMEBUFFER); + GL_state.PopFramebufferState(); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glDrawBuffer(GL_BACK); - glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, Cmdline_window_res->first, Cmdline_window_res->second, GL_COLOR_BUFFER_BIT, GL_LINEAR); - glDrawBuffer(GL_NONE); + glViewport(0, 0, Cmdline_window_res->first, Cmdline_window_res->second); - GL_state.PopFramebufferState(); + opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_GAMMA_BLIT, 0)); + + GL_state.Texture.Enable(0, GL_TEXTURE_2D, Back_texture); + Current_shader->program->Uniforms.setTextureUniform("tex", 0); + + GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE); + GL_state.SetZbufferType(ZBUFFER_TYPE_NONE); + + opengl_set_generic_uniform_data( + [](graphics::generic_data::gamma_blit_data* data) { + data->gamma = Cmdline_no_set_gamma ? 1.f : Gr_gamma; + }); + + opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f); } if (Cmdline_gl_finish) @@ -1485,6 +1494,8 @@ bool gr_opengl_init(std::unique_ptr&& graphicsOps) Gr_current_green = &Gr_green; Gr_current_alpha = &Gr_alpha; + // Initialize uniform buffer managers + gr_uniform_buffer_managers_init(); gr_setup_frame(); gr_opengl_reset_clip(); diff --git a/code/graphics/opengl/gropenglshader.cpp b/code/graphics/opengl/gropenglshader.cpp index 6baeb10bb6d..27d6bdf0d57 100644 --- a/code/graphics/opengl/gropenglshader.cpp +++ b/code/graphics/opengl/gropenglshader.cpp @@ -178,6 +178,9 @@ static opengl_shader_type_t GL_shader_types[] = { { SDR_TYPE_IRRADIANCE_MAP_GEN, "post-v.sdr", "irrmap-f.sdr", nullptr, { opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Irradiance Map Generation", false }, + + { SDR_TYPE_GAMMA_BLIT, "post-v.sdr", "gamma-correct-f.sdr", nullptr, + { opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Gamma correct blit", false }, }; // clang-format on diff --git a/code/graphics/util/uniform_structs.h b/code/graphics/util/uniform_structs.h index 454db36d1a0..1f2dcaeec32 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -408,6 +408,11 @@ struct irrmap_data { int face; }; +struct gamma_blit_data { + float gamma; + float pad[3]; +}; + } // namespace generic_data } // namespace graphics diff --git a/code/source_groups.cmake b/code/source_groups.cmake index aa5f98ad520..b620220f785 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -247,6 +247,7 @@ add_file_folder("Default files\\\\data\\\\effects" def_files/data/effects/fxaa-v.sdr def_files/data/effects/fxaapre-f.sdr def_files/data/effects/gamma.sdr + def_files/data/effects/gamma-correct-f.sdr def_files/data/effects/irrmap-f.sdr def_files/data/effects/lighting.sdr def_files/data/effects/ls-f.sdr