mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
The offscreen targets backing the scene texture and post-processing were allocated once from gr_screen at gr_init() and never revisited. Anything drawn while gr_screen was larger got clipped to their edge and stretched back over the viewport. The game hits this on the SDL window-resize path (osapi.cpp calls gr_screen_resize()); qtFRED hits it constantly, since its 3D viewport is a resizable dock widget. Add gf_resize_render_targets, called from gr_screen_resize(), and implement it for OpenGL: grow the targets to cover the new gr_screen, rebuilding only the resolution-dependent resources. The post-processing table, the compiled shaders and the SMAA lookup textures are resolution-independent and stay alive, which is what makes this cheap enough to run off a window drag. This mirrors what the Vulkan backend already does in VulkanPostProcessor::resize(); Vulkan leaves the hook unset because recreateSwapChain() owns it there. The allocation only ever grows: gr_screen_resize() is called every frame by qtFRED and repeatedly by the briefing map widget, so tracking the high-water mark avoids thrashing, and the shrunk state is already handled by Scene_texture_u_scale/v_scale. The size is also clamped to the hardware limit up front, so a viewport past that limit stops asking to be resized instead of rebuilding every frame. If the larger allocation fails outright -- most likely precisely when growing -- the resize stops before rebuilding the post-processing targets on top of scene textures that no longer exist. This replaces Gr_min_render_target_w/h, which sized the targets for the largest attached display up front -- on a 4K display at 2x scaling that was over a gigabyte of VRAM, allocated at launch, for a feature that is off by default and may never be switched on. Deletions now go through GL_state.Texture.Delete(): a freed texture name the state cache still holds would make a later Enable() of the recycled name a silent no-op. This mattered little when teardown only ran at shutdown. While there, the scene teardown now releases everything setup allocates -- it was leaking Scene_ldr/composite/luminance/Cockpit_depth and all six MSAA targets -- and post-processing shutdown releases the SMAA lookup textures. Separately, fix the sampling extents that the above makes reachable: - deferred-f.sdr turns gl_FragCoord into a G-buffer coordinate using invScreenWidth/Height, which described gr_screen rather than the G-buffer. Fixed in both backends; it is currently a no-op under Vulkan, where resize() keeps the extent equal to gr_screen, but states the requirement instead of relying on that. - the MSAA scene-colour copy, the MSAA resolve and the fog pass sampled the full [0,1] range of targets that are only filled to the u/v scale. - fxaa-v.sdr derived its texcoord from vertPosition, ignoring the sub-rectangle the draw call asked for. Those extents now come from opengl_draw_full_screen_scene_texture() rather than being open-coded, so a new pass cannot quietly reintroduce the bug. The volumetric nebula pass is deliberately left unscaled and commented: it uses fragTexCoord both to reconstruct a ray direction and to sample, which needs a shader change to separate. Co-Authored-By: Claude Opus 5 <[email protected]>
1450 lines
50 KiB
C++
1450 lines
50 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
#include "gropengldraw.h"
|
|
|
|
#include "globalincs/pstypes.h"
|
|
|
|
#include "ShaderProgram.h"
|
|
#include "freespace.h"
|
|
#include "gropengl.h"
|
|
#include "gropengldeferred.h"
|
|
#include "gropenglpostprocessing.h"
|
|
#include "gropenglshader.h"
|
|
#include "gropengltexture.h"
|
|
#include "gropengltnl.h"
|
|
|
|
#include "cmdline/cmdline.h"
|
|
#include "graphics/light.h"
|
|
#include "graphics/matrix.h"
|
|
#include "graphics/paths/PathRenderer.h"
|
|
#include "graphics/util/uniform_structs.h"
|
|
#include "lighting/lighting.h"
|
|
#include "render/3d.h"
|
|
#include "tracing/tracing.h"
|
|
#ifdef USE_OPENGL_ES
|
|
#include "es_compatibility.h"
|
|
#endif
|
|
|
|
GLuint Scene_framebuffer;
|
|
GLuint Scene_framebuffer_ms;
|
|
GLuint Scene_ldr_texture;
|
|
GLuint Scene_color_texture;
|
|
GLuint Scene_position_texture;
|
|
GLuint Scene_normal_texture;
|
|
GLuint Scene_specular_texture;
|
|
GLuint Scene_emissive_texture;
|
|
GLuint Scene_color_texture_ms;
|
|
GLuint Scene_position_texture_ms;
|
|
GLuint Scene_normal_texture_ms;
|
|
GLuint Scene_specular_texture_ms;
|
|
GLuint Scene_emissive_texture_ms;
|
|
GLuint Scene_composite_texture;
|
|
GLuint Scene_luminance_texture;
|
|
GLuint Scene_depth_texture;
|
|
GLuint Scene_depth_texture_ms;
|
|
GLuint Cockpit_depth_texture;
|
|
|
|
GLuint Back_framebuffer;
|
|
GLuint Back_texture;
|
|
GLuint Back_depth_texture;
|
|
|
|
GLuint GammaBlit_framebuffer;
|
|
GLuint GammaBlit_texture;
|
|
|
|
GLuint Distortion_framebuffer = 0;
|
|
GLuint Distortion_texture[2];
|
|
int Distortion_switch = 0;
|
|
|
|
int Scene_texture_initialized;
|
|
|
|
int Scene_texture_width;
|
|
int Scene_texture_height;
|
|
|
|
GLfloat Scene_texture_u_scale = 1.0f;
|
|
GLfloat Scene_texture_v_scale = 1.0f;
|
|
|
|
// Render targets are torn down and rebuilt mid-session by gr_opengl_resize_render_targets(), not
|
|
// just at shutdown, so deletion has to go through the state cache: the driver is free to hand a
|
|
// freed name straight back out, and a cache entry still holding that name would make a later
|
|
// Enable() of the recycled texture a no-op.
|
|
void opengl_delete_render_texture(GLuint& tex)
|
|
{
|
|
if ( !tex ) {
|
|
return;
|
|
}
|
|
|
|
GL_state.Texture.Delete(tex);
|
|
glDeleteTextures(1, &tex);
|
|
tex = 0;
|
|
}
|
|
|
|
// Callers must have bound something else first (the resize path binds 0); the framebuffer cache
|
|
// has no equivalent of Texture.Delete() to unbind through.
|
|
void opengl_delete_render_framebuffer(GLuint& fbo)
|
|
{
|
|
if ( !fbo ) {
|
|
return;
|
|
}
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
|
fbo = 0;
|
|
}
|
|
|
|
inline GLenum opengl_primitive_type(primitive_type prim_type)
|
|
{
|
|
switch ( prim_type ) {
|
|
case PRIM_TYPE_POINTS:
|
|
return GL_POINTS;
|
|
case PRIM_TYPE_TRIS:
|
|
return GL_TRIANGLES;
|
|
case PRIM_TYPE_TRISTRIP:
|
|
return GL_TRIANGLE_STRIP;
|
|
case PRIM_TYPE_TRIFAN:
|
|
return GL_TRIANGLE_FAN;
|
|
case PRIM_TYPE_LINES:
|
|
return GL_LINES;
|
|
case PRIM_TYPE_LINESTRIP:
|
|
return GL_LINE_STRIP;
|
|
default:
|
|
return GL_TRIANGLE_FAN;
|
|
}
|
|
}
|
|
|
|
void gr_opengl_sphere(material* material_def, float /*rad*/)
|
|
{
|
|
opengl_tnl_set_material(material_def, true);
|
|
|
|
opengl_draw_sphere();
|
|
}
|
|
|
|
extern int opengl_check_framebuffer();
|
|
void opengl_setup_scene_textures(int width, int height)
|
|
{
|
|
Scene_texture_initialized = 0;
|
|
|
|
if ( Cmdline_no_fbo ) {
|
|
Gr_post_processing_enabled = false;
|
|
Gr_enable_soft_particles = false;
|
|
Gr_framebuffer_effects = {};
|
|
|
|
Scene_ldr_texture = 0;
|
|
Scene_color_texture = 0;
|
|
Scene_depth_texture = 0;
|
|
return;
|
|
}
|
|
|
|
Scene_texture_width = width;
|
|
Scene_texture_height = height;
|
|
|
|
// clamp size, if needed
|
|
if ( Scene_texture_width > GL_max_renderbuffer_size ) {
|
|
Scene_texture_width = GL_max_renderbuffer_size;
|
|
}
|
|
|
|
if ( Scene_texture_height > GL_max_renderbuffer_size) {
|
|
Scene_texture_height = GL_max_renderbuffer_size;
|
|
}
|
|
|
|
mprintf((" Scene textures: %dx%d (screen %dx%d, max renderbuffer %d)\n",
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
gr_screen.max_w,
|
|
gr_screen.max_h,
|
|
GL_max_renderbuffer_size));
|
|
|
|
// create framebuffer
|
|
glGenFramebuffers(1, &Scene_framebuffer);
|
|
GL_state.BindFrameBuffer(Scene_framebuffer);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Scene_framebuffer, "Scene framebuffer");
|
|
|
|
// setup main render texture
|
|
|
|
// setup high dynamic range color texture
|
|
glGenTextures(1, &Scene_color_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_color_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_color_texture, 0);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_color_texture, "Scene color texture");
|
|
|
|
// setup low dynamic range color texture
|
|
glGenTextures(1, &Scene_ldr_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_ldr_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_ldr_texture, "Scene LDR texture");
|
|
|
|
// setup position render texture
|
|
glGenTextures(1, &Scene_position_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_position_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_position_texture, "Scene Position texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, Scene_position_texture, 0);
|
|
|
|
// setup normal render texture
|
|
glGenTextures(1, &Scene_normal_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_normal_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_normal_texture, "Scene Normal texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, Scene_normal_texture, 0);
|
|
|
|
// setup specular render texture
|
|
glGenTextures(1, &Scene_specular_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_specular_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_specular_texture, "Scene Specular texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, Scene_specular_texture, 0);
|
|
|
|
// setup emissive render texture
|
|
glGenTextures(1, &Scene_emissive_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_emissive_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_emissive_texture, "Scene Emissive texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, Scene_emissive_texture, 0);
|
|
|
|
// setup compositing render texture
|
|
glGenTextures(1, &Scene_composite_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_composite_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_composite_texture, "Scene Composite texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, GL_TEXTURE_2D, Scene_composite_texture, 0);
|
|
|
|
//Set up luminance texture (used as input for FXAA)
|
|
// also used as a light accumulation buffer during the deferred pass
|
|
glGenTextures(1, &Scene_luminance_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_luminance_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, Scene_texture_width, Scene_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_luminance_texture, "Scene Luminance texture");
|
|
|
|
// setup cockpit depth texture
|
|
glGenTextures(1, &Cockpit_depth_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Cockpit_depth_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
GL_DEPTH24_STENCIL8,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
0,
|
|
GL_DEPTH_COMPONENT,
|
|
GL_FLOAT,
|
|
nullptr);
|
|
opengl_set_object_label(GL_TEXTURE, Cockpit_depth_texture, "Cockpit depth texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Cockpit_depth_texture, 0);
|
|
gr_zbuffer_set(GR_ZBUFF_FULL);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
// setup main depth texture
|
|
glGenTextures(1, &Scene_depth_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_depth_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
GL_DEPTH24_STENCIL8,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
0,
|
|
GL_DEPTH_COMPONENT,
|
|
GL_FLOAT,
|
|
nullptr);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_depth_texture, "Scene depth texture");
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Scene_depth_texture, 0);
|
|
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
if ( opengl_check_framebuffer() ) {
|
|
GL_state.BindFrameBuffer(0);
|
|
opengl_delete_render_framebuffer(Scene_framebuffer);
|
|
|
|
opengl_delete_render_texture(Scene_color_texture);
|
|
opengl_delete_render_texture(Scene_position_texture);
|
|
opengl_delete_render_texture(Scene_normal_texture);
|
|
opengl_delete_render_texture(Scene_specular_texture);
|
|
opengl_delete_render_texture(Scene_emissive_texture);
|
|
opengl_delete_render_texture(Scene_depth_texture);
|
|
opengl_delete_render_texture(Scene_luminance_texture);
|
|
|
|
Gr_post_processing_enabled = false;
|
|
Gr_enable_soft_particles = false;
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_OPENGL_ES
|
|
if (Cmdline_msaa_enabled > 0) {
|
|
Cmdline_msaa_enabled = 0;
|
|
Warning(LOCATION, "MSAA is not currently supported under OpenGL ES. Disabling MSAA.");
|
|
}
|
|
#endif
|
|
|
|
if (Cmdline_msaa_enabled > 0) {
|
|
glEnable(GL_MULTISAMPLE);
|
|
|
|
// Make sure our MSAA setting are valid
|
|
int maxSamples;
|
|
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
|
|
|
//Disallow >16 samples due to manual sorting networks in shader
|
|
if (maxSamples > 16)
|
|
maxSamples = 16;
|
|
|
|
if (maxSamples < Cmdline_msaa_enabled) {
|
|
Warning(LOCATION, "Requested MSAA level of %d is not supported. Max MSAA level is %d.", Cmdline_msaa_enabled, maxSamples);
|
|
Cmdline_msaa_enabled = maxSamples;
|
|
}
|
|
else if (Cmdline_msaa_enabled & (Cmdline_msaa_enabled - 1)) {
|
|
int newLevel = 1 << (sizeof(int) * 8 - 2);
|
|
while ((newLevel & Cmdline_msaa_enabled) == 0)
|
|
newLevel = newLevel >> 1;
|
|
Warning(LOCATION, "Requested MSAA level of %d is not a power of 2. Setting to %d.", Cmdline_msaa_enabled, newLevel);
|
|
Cmdline_msaa_enabled = newLevel;
|
|
}
|
|
|
|
// create framebuffer
|
|
glGenFramebuffers(1, &Scene_framebuffer_ms);
|
|
GL_state.BindFrameBuffer(Scene_framebuffer_ms);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Scene_framebuffer_ms, "Scene framebuffer multisampling");
|
|
|
|
// setup high dynamic range color texture
|
|
glGenTextures(1, &Scene_color_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_color_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_RGBA16F,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT0,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_color_texture_ms,
|
|
0);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_color_texture_ms, "Scene color texture multisampling");
|
|
|
|
// setup position render texture
|
|
glGenTextures(1, &Scene_position_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_position_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_RGBA16F,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_position_texture_ms, "Scene Position texture multisampling");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT1,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_position_texture_ms,
|
|
0);
|
|
|
|
// setup normal render texture
|
|
glGenTextures(1, &Scene_normal_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_normal_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_RGBA16F,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_normal_texture_ms, "Scene Normal texture multisampling");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT2,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_normal_texture_ms,
|
|
0);
|
|
|
|
// setup specular render texture
|
|
glGenTextures(1, &Scene_specular_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_specular_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_RGBA8,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_specular_texture_ms, "Scene Specular texture multisample");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT3,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_specular_texture_ms,
|
|
0);
|
|
|
|
// setup emissive render texture
|
|
glGenTextures(1, &Scene_emissive_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_emissive_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_RGBA16F,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_emissive_texture_ms, "Scene Emissive texture multisample");
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT4,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_emissive_texture_ms,
|
|
0);
|
|
|
|
// setup main depth texture
|
|
glGenTextures(1, &Scene_depth_texture_ms);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
|
GL_state.Texture.Enable(Scene_depth_texture_ms);
|
|
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
|
|
Cmdline_msaa_enabled,
|
|
GL_DEPTH24_STENCIL8,
|
|
Scene_texture_width,
|
|
Scene_texture_height,
|
|
GL_TRUE);
|
|
opengl_set_object_label(GL_TEXTURE, Scene_depth_texture_ms, "Scene depth texture multisample");
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
|
GL_DEPTH_STENCIL_ATTACHMENT,
|
|
GL_TEXTURE_2D_MULTISAMPLE,
|
|
Scene_depth_texture_ms,
|
|
0);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
}
|
|
|
|
if (Cmdline_window_res) {
|
|
//Gen Framebuffer
|
|
glGenFramebuffers(1, &Back_framebuffer);
|
|
GL_state.BindFrameBuffer(Back_framebuffer);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Back_framebuffer, "Backbuffer");
|
|
|
|
// setup main render texture
|
|
|
|
// setup high dynamic range color texture
|
|
glGenTextures(1, &Back_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Back_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, gr_screen.max_w, gr_screen.max_h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Back_texture, 0);
|
|
opengl_set_object_label(GL_TEXTURE, Back_texture, "Backbuffer texture");
|
|
|
|
glGenTextures(1, &Back_depth_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Back_depth_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
GL_DEPTH24_STENCIL8,
|
|
gr_screen.max_w,
|
|
gr_screen.max_h,
|
|
0,
|
|
GL_DEPTH_STENCIL,
|
|
GL_UNSIGNED_INT_24_8,
|
|
nullptr);
|
|
opengl_set_object_label(GL_TEXTURE, Back_depth_texture, "Backbuffer depth texture");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Back_depth_texture, 0);
|
|
gr_zbuffer_set(GR_ZBUFF_FULL);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
// Intermediate target for the gamma-correction pass. The gamma pass samples Back_texture and writes
|
|
// here (an offscreen FBO->FBO draw, same as every other post-process pass); the result is then
|
|
// presented to the window via glBlitFramebuffer. Some drivers show flicker when a shader draw that
|
|
// samples a just-rendered texture targets the window-system default framebuffer directly, so the
|
|
// final presentation step always goes through a blit instead.
|
|
glGenFramebuffers(1, &GammaBlit_framebuffer);
|
|
GL_state.BindFrameBuffer(GammaBlit_framebuffer);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, GammaBlit_framebuffer, "Gamma blit framebuffer");
|
|
|
|
if (Scene_ldr_texture != 0 && Scene_texture_width == gr_screen.max_w && Scene_texture_height == gr_screen.max_h) {
|
|
// Scene_ldr_texture is dead scratch by flip time: its consumers (tonemap, AA, the final
|
|
// post pass) all write before reading within gr_opengl_post_process_end(), which completes
|
|
// before the flip. Reuse it as the gamma pass target instead of allocating another
|
|
// screen-sized texture. GammaBlit_texture stays 0 so shutdown won't delete it.
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_ldr_texture, 0);
|
|
} else {
|
|
// Scene textures are clamped to GL_max_renderbuffer_size and may not cover the screen;
|
|
// fall back to a dedicated texture in that case.
|
|
glGenTextures(1, &GammaBlit_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(GammaBlit_texture);
|
|
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gr_screen.max_w, gr_screen.max_h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, GammaBlit_texture, 0);
|
|
opengl_set_object_label(GL_TEXTURE, GammaBlit_texture, "Gamma blit texture");
|
|
}
|
|
|
|
GL_state.BindFrameBuffer(Back_framebuffer);
|
|
}
|
|
|
|
//Setup thruster distortion framebuffer
|
|
if (Gr_framebuffer_effects.any_set())
|
|
{
|
|
glGenFramebuffers(1, &Distortion_framebuffer);
|
|
GL_state.BindFrameBuffer(Distortion_framebuffer);
|
|
|
|
glGenTextures(2, Distortion_texture);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Distortion_texture[0]);
|
|
|
|
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_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
GL_state.Texture.Enable(Distortion_texture[1]);
|
|
|
|
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_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Distortion_texture[0], 0);
|
|
}
|
|
|
|
|
|
if ( opengl_check_framebuffer() ) {
|
|
GL_state.BindFrameBuffer(0);
|
|
glDeleteFramebuffers(1, &Distortion_framebuffer);
|
|
Distortion_framebuffer = 0;
|
|
|
|
glDeleteTextures(2, Distortion_texture);
|
|
Distortion_texture[0] = 0;
|
|
Distortion_texture[1] = 0;
|
|
return;
|
|
}
|
|
|
|
if ( opengl_check_for_errors("post_init_framebuffer()") ) {
|
|
Scene_color_texture = 0;
|
|
Scene_depth_texture = 0;
|
|
|
|
Gr_post_processing_enabled = false;
|
|
Gr_enable_soft_particles = false;
|
|
return;
|
|
}
|
|
|
|
GL_state.BindFrameBuffer(0);
|
|
|
|
Scene_texture_initialized = 1;
|
|
Scene_framebuffer_in_frame = false;
|
|
}
|
|
|
|
void opengl_scene_texture_shutdown()
|
|
{
|
|
if ( !Scene_texture_initialized ) {
|
|
return;
|
|
}
|
|
|
|
// Everything opengl_setup_scene_textures() generated, in the same order. Note that
|
|
// GammaBlit_texture is 0 when the gamma pass is aliasing Scene_ldr_texture, so the shared
|
|
// texture is only released once.
|
|
opengl_delete_render_texture(Scene_color_texture);
|
|
opengl_delete_render_texture(Scene_ldr_texture);
|
|
opengl_delete_render_texture(Scene_position_texture);
|
|
opengl_delete_render_texture(Scene_normal_texture);
|
|
opengl_delete_render_texture(Scene_specular_texture);
|
|
opengl_delete_render_texture(Scene_emissive_texture);
|
|
opengl_delete_render_texture(Scene_composite_texture);
|
|
opengl_delete_render_texture(Scene_luminance_texture);
|
|
opengl_delete_render_texture(Cockpit_depth_texture);
|
|
opengl_delete_render_texture(Scene_depth_texture);
|
|
opengl_delete_render_framebuffer(Scene_framebuffer);
|
|
|
|
opengl_delete_render_texture(Scene_color_texture_ms);
|
|
opengl_delete_render_texture(Scene_position_texture_ms);
|
|
opengl_delete_render_texture(Scene_normal_texture_ms);
|
|
opengl_delete_render_texture(Scene_specular_texture_ms);
|
|
opengl_delete_render_texture(Scene_emissive_texture_ms);
|
|
opengl_delete_render_texture(Scene_depth_texture_ms);
|
|
opengl_delete_render_framebuffer(Scene_framebuffer_ms);
|
|
|
|
opengl_delete_render_texture(Back_texture);
|
|
opengl_delete_render_texture(Back_depth_texture);
|
|
opengl_delete_render_framebuffer(Back_framebuffer);
|
|
|
|
opengl_delete_render_texture(GammaBlit_texture);
|
|
opengl_delete_render_framebuffer(GammaBlit_framebuffer);
|
|
|
|
opengl_delete_render_texture(Distortion_texture[0]);
|
|
opengl_delete_render_texture(Distortion_texture[1]);
|
|
opengl_delete_render_framebuffer(Distortion_framebuffer);
|
|
|
|
Scene_texture_initialized = 0;
|
|
Scene_framebuffer_in_frame = false;
|
|
}
|
|
|
|
void gr_opengl_resize_render_targets()
|
|
{
|
|
// Nothing allocated yet (still inside gr_init()), or FBOs are unavailable entirely.
|
|
if ( !Scene_texture_initialized ) {
|
|
return;
|
|
}
|
|
|
|
// Grow only. Shrinking back would mean reallocating every G-buffer again the moment the window
|
|
// grew back, and the shrunk state is already handled correctly: Scene_texture_u_scale and
|
|
// _v_scale confine rendering to the sub-rectangle actually in use. The hardware limit is
|
|
// applied here rather than left to opengl_setup_scene_textures(), so that a viewport larger
|
|
// than anything the GPU can allocate compares equal below and stops asking.
|
|
const int new_width = MIN(MAX(gr_screen.max_w, Scene_texture_width), GL_max_renderbuffer_size);
|
|
const int new_height = MIN(MAX(gr_screen.max_h, Scene_texture_height), GL_max_renderbuffer_size);
|
|
|
|
// The overwhelmingly common case: qtFred calls gr_screen_resize() every frame and the game
|
|
// calls it on every SDL resize event, almost always at a size the current targets already
|
|
// cover -- or, past the hardware limit, at one they never will.
|
|
if ( new_width == Scene_texture_width && new_height == Scene_texture_height ) {
|
|
return;
|
|
}
|
|
|
|
// Tearing down the framebuffer we are currently rendering into would corrupt the frame rather
|
|
// than fail cleanly, so refuse rather than trying to recover. Callers resize between frames.
|
|
// Scene_framebuffer_in_frame covers the post-processing passes too: they only ever run inside
|
|
// gr_scene_texture_begin()/end(), so it is set for the whole of Post_in_frame as well.
|
|
if ( Scene_framebuffer_in_frame ) {
|
|
Assertion(false, "Tried to resize the render targets to %dx%d while a scene was being "
|
|
"rendered into them! The resize has been skipped; the frame will be stretched.",
|
|
new_width, new_height);
|
|
return;
|
|
}
|
|
|
|
mprintf(("Growing render targets from %dx%d to %dx%d to cover the new %dx%d viewport.\n",
|
|
Scene_texture_width, Scene_texture_height, new_width, new_height,
|
|
gr_screen.max_w, gr_screen.max_h));
|
|
|
|
// Leave the framebuffer cache pointing at a name that cannot be deleted out from under it.
|
|
GL_state.BindFrameBufferBoth(0, 0);
|
|
|
|
// Only the size-dependent resources are touched. The post-processing table, the compiled
|
|
// shaders and the SMAA lookup textures are all resolution-independent and stay alive, which is
|
|
// what keeps this cheap enough to run off a window drag. The post-processing targets are
|
|
// rebuilt after the scene textures because they are sized to match them.
|
|
opengl_scene_texture_shutdown();
|
|
opengl_setup_scene_textures(new_width, new_height);
|
|
|
|
// Reallocating larger is exactly when running out of video memory is most likely, and
|
|
// opengl_setup_scene_textures() reports that by leaving the scene uninitialized (having
|
|
// already turned post-processing and soft particles off). Rebuilding the post-processing
|
|
// targets on top of scene textures that don't exist would only make it worse, so stop here;
|
|
// the renderer keeps drawing without the offscreen pipeline.
|
|
if ( !Scene_texture_initialized ) {
|
|
mprintf(("Failed to allocate %dx%d render targets! The offscreen rendering pipeline has "
|
|
"been disabled for the rest of this session.\n", new_width, new_height));
|
|
return;
|
|
}
|
|
|
|
opengl_post_resize_render_targets();
|
|
}
|
|
|
|
size_t opengl_get_scene_render_target_bytes()
|
|
{
|
|
// The bytes for each pixel come from the internal formats that opengl_setup_scene_textures()
|
|
// gives to these textures: GL_RGBA16F holds 8 bytes, GL_RGBA8 and GL_DEPTH24_STENCIL8 hold 4.
|
|
// Keep the values below equal to the formats at the creation sites in that function.
|
|
constexpr size_t RGBA16F_BYTES = 8;
|
|
constexpr size_t RGBA8_BYTES = 4;
|
|
constexpr size_t DEPTH24_STENCIL8_BYTES = 4;
|
|
|
|
size_t bytes = 0;
|
|
|
|
auto add_texture = [&bytes](GLuint texture_id, size_t width, size_t height, size_t bytes_per_pixel,
|
|
size_t samples = 1) {
|
|
if (texture_id == 0) {
|
|
return;
|
|
}
|
|
bytes += width * height * bytes_per_pixel * samples;
|
|
};
|
|
|
|
const auto scene_w = static_cast<size_t>(Scene_texture_width);
|
|
const auto scene_h = static_cast<size_t>(Scene_texture_height);
|
|
|
|
add_texture(Scene_color_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Scene_ldr_texture, scene_w, scene_h, RGBA8_BYTES);
|
|
add_texture(Scene_position_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Scene_normal_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Scene_specular_texture, scene_w, scene_h, RGBA8_BYTES);
|
|
add_texture(Scene_emissive_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Scene_composite_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Scene_luminance_texture, scene_w, scene_h, RGBA16F_BYTES);
|
|
add_texture(Cockpit_depth_texture, scene_w, scene_h, DEPTH24_STENCIL8_BYTES);
|
|
add_texture(Scene_depth_texture, scene_w, scene_h, DEPTH24_STENCIL8_BYTES);
|
|
|
|
// The multisample textures hold one value for each sample, thus their bytes go up with the
|
|
// MSAA level. Cmdline_msaa_enabled holds the number of samples and is 0 when MSAA is off.
|
|
const auto samples = static_cast<size_t>(std::max(1, Cmdline_msaa_enabled));
|
|
|
|
add_texture(Scene_color_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
|
|
add_texture(Scene_position_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
|
|
add_texture(Scene_normal_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
|
|
add_texture(Scene_specular_texture_ms, scene_w, scene_h, RGBA8_BYTES, samples);
|
|
add_texture(Scene_emissive_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
|
|
add_texture(Scene_depth_texture_ms, scene_w, scene_h, DEPTH24_STENCIL8_BYTES, samples);
|
|
|
|
// The back buffer and gamma blit textures use the size of the screen, not the (clamped) size
|
|
// of the scene textures.
|
|
const auto screen_w = static_cast<size_t>(gr_screen.max_w);
|
|
const auto screen_h = static_cast<size_t>(gr_screen.max_h);
|
|
|
|
add_texture(Back_texture, screen_w, screen_h, RGBA16F_BYTES);
|
|
add_texture(Back_depth_texture, screen_w, screen_h, DEPTH24_STENCIL8_BYTES);
|
|
add_texture(GammaBlit_texture, screen_w, screen_h, RGBA8_BYTES);
|
|
|
|
// The two distortion textures always have the size 32x32.
|
|
add_texture(Distortion_texture[0], 32, 32, RGBA8_BYTES);
|
|
add_texture(Distortion_texture[1], 32, 32, RGBA8_BYTES);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void gr_opengl_scene_texture_begin()
|
|
{
|
|
if ( !Scene_texture_initialized ) {
|
|
return;
|
|
}
|
|
|
|
if ( Scene_framebuffer_in_frame ) {
|
|
return;
|
|
}
|
|
|
|
GR_DEBUG_SCOPE("Begin scene texture");
|
|
TRACE_SCOPE(tracing::SceneTextureBegin);
|
|
|
|
GL_state.PushFramebufferState();
|
|
GL_state.BindFrameBuffer(Scene_framebuffer);
|
|
|
|
// The cockpit depth mask is only written when the cockpit is actually rendered (see
|
|
// gr_opengl_post_process_save_zbuffer), and the views that skip it -- external, chase,
|
|
// topdown, warp chase -- would otherwise leave the lightshaft pass masking against the
|
|
// silhouette left over from the last cockpit-view frame. Clear it every frame; when a
|
|
// cockpit does render, save_zbuffer clears it again anyway.
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Cockpit_depth_texture, 0);
|
|
GL_state.SetZbufferType(ZBUFFER_TYPE_FULL);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Scene_depth_texture, 0);
|
|
|
|
if (GL_rendering_to_texture)
|
|
{
|
|
Scene_texture_u_scale = i2fl(gr_screen.max_w) / i2fl(Scene_texture_width);
|
|
Scene_texture_v_scale = i2fl(gr_screen.max_h) / i2fl(Scene_texture_height);
|
|
|
|
CLAMP(Scene_texture_u_scale, 0.0f, 1.0f);
|
|
CLAMP(Scene_texture_v_scale, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
Scene_texture_u_scale = 1.0f;
|
|
Scene_texture_v_scale = 1.0f;
|
|
}
|
|
|
|
// A ratio above 1.0 means the viewport outgrew the allocation after the fact -- the scene
|
|
// texture can't be resized, so all we can do is render the part that fits and let the blit
|
|
// stretch it back out, which misaligns everything by a different amount on each axis. The
|
|
// floor above is meant to make this unreachable; if it does happen (a display hotplug or
|
|
// resolution change after gr_init(), or an allocation clamped by GL_max_renderbuffer_size on
|
|
// low-end hardware) say so once rather than every frame.
|
|
if (Scene_texture_u_scale > 1.0f || Scene_texture_v_scale > 1.0f) {
|
|
static bool reported_undersized_scene_texture = false;
|
|
|
|
if (!reported_undersized_scene_texture) {
|
|
reported_undersized_scene_texture = true;
|
|
nprintf(("OpenGL",
|
|
"Viewport (%dx%d) is larger than the scene texture backing it (%dx%d); "
|
|
"the post-processed image will be stretched to fit.\n",
|
|
gr_screen.max_w,
|
|
gr_screen.max_h,
|
|
Scene_texture_width,
|
|
Scene_texture_height));
|
|
}
|
|
}
|
|
|
|
CLAMP(Scene_texture_u_scale, 0.0f, 1.0f);
|
|
CLAMP(Scene_texture_v_scale, 0.0f, 1.0f);
|
|
|
|
if (!light_deferred_enabled()) {
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
} else {
|
|
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
|
|
glDrawBuffers(6, buffers);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
opengl_clear_deferred_buffers();
|
|
|
|
if (Cmdline_msaa_enabled > 0) {
|
|
GL_state.BindFrameBuffer(Scene_framebuffer_ms);
|
|
|
|
glDrawBuffers(6, buffers);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
opengl_clear_deferred_buffers();
|
|
|
|
GL_state.BindFrameBuffer(Scene_framebuffer);
|
|
}
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
Scene_framebuffer_in_frame = true;
|
|
|
|
if ( Gr_post_processing_enabled && !PostProcessing_override ) {
|
|
High_dynamic_range = true;
|
|
}
|
|
}
|
|
|
|
float time_buffer = 0.0f;
|
|
void gr_opengl_scene_texture_end()
|
|
{
|
|
|
|
if ( !Scene_framebuffer_in_frame ) {
|
|
return;
|
|
}
|
|
|
|
GR_DEBUG_SCOPE("End scene texture");
|
|
TRACE_SCOPE(tracing::SceneTextureEnd);
|
|
|
|
time_buffer+=flFrametime;
|
|
if(time_buffer>0.03f)
|
|
{
|
|
gr_opengl_update_distortion();
|
|
time_buffer = 0.0f;
|
|
}
|
|
|
|
if ( Gr_post_processing_enabled && !PostProcessing_override ) {
|
|
gr_opengl_post_process_end();
|
|
} else {
|
|
GR_DEBUG_SCOPE("Draw scene texture");
|
|
TRACE_SCOPE(tracing::DrawSceneTexture);
|
|
|
|
GLboolean depth = GL_state.DepthTest(GL_FALSE);
|
|
GLboolean depth_mask = GL_state.DepthMask(GL_FALSE);
|
|
GLboolean blend = GL_state.Blend(GL_FALSE);
|
|
GLboolean cull = GL_state.CullFace(GL_FALSE);
|
|
|
|
GL_state.PopFramebufferState();
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
opengl_shader_set_passthrough(true, High_dynamic_range);
|
|
|
|
GL_state.Array.BindArrayBuffer(0);
|
|
|
|
if (GL_rendering_to_texture)
|
|
{
|
|
opengl_draw_textured_quad(0.0f, 0.0f, 0.0f, 0.0f, (float)gr_screen.max_w, (float)gr_screen.max_h, Scene_texture_u_scale, Scene_texture_v_scale);
|
|
}
|
|
else
|
|
{
|
|
opengl_draw_textured_quad(0.0f, 0.0f, 0.0f, Scene_texture_v_scale, (float)gr_screen.max_w, (float)gr_screen.max_h, Scene_texture_u_scale, 0.0f);
|
|
}
|
|
|
|
// reset state
|
|
GL_state.DepthTest(depth);
|
|
GL_state.DepthMask(depth_mask);
|
|
GL_state.Blend(blend);
|
|
GL_state.CullFace(cull);
|
|
}
|
|
|
|
// Reset the UV scale values
|
|
|
|
Scene_texture_u_scale = 1.0f;
|
|
Scene_texture_v_scale = 1.0f;
|
|
|
|
Scene_framebuffer_in_frame = false;
|
|
High_dynamic_range = false;
|
|
}
|
|
|
|
void gr_opengl_copy_effect_texture()
|
|
{
|
|
GR_DEBUG_SCOPE("Copy effect texture");
|
|
|
|
if ( !Scene_framebuffer_in_frame ) {
|
|
return;
|
|
}
|
|
|
|
//Make sure we're reading from the up-to-date color texture
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT5);
|
|
glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
void gr_opengl_render_shield_impact(shield_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
gr_buffer_handle buffer_handle,
|
|
int n_verts)
|
|
{
|
|
matrix4 impact_transform;
|
|
matrix4 impact_projection;
|
|
vec3d min;
|
|
vec3d max;
|
|
|
|
opengl_tnl_set_material(material_info, false);
|
|
|
|
float radius = material_info->get_impact_radius();
|
|
min.xyz.x = min.xyz.y = min.xyz.z = -radius;
|
|
max.xyz.x = max.xyz.y = max.xyz.z = radius;
|
|
|
|
vm_matrix4_set_orthographic(&impact_projection, &max, &min);
|
|
|
|
matrix impact_orient = material_info->get_impact_orient();
|
|
vec3d impact_pos = material_info->get_impact_pos();
|
|
|
|
vm_matrix4_set_inverse_transform(&impact_transform, &impact_orient, &impact_pos);
|
|
|
|
uint32_t array_index = 0;
|
|
if (material_info->get_texture_map(TM_BASE_TYPE) >= 0) {
|
|
float u_scale, v_scale;
|
|
|
|
if (!gr_opengl_tcache_set(material_info->get_texture_map(TM_BASE_TYPE), material_info->get_texture_type(),
|
|
&u_scale, &v_scale, &array_index)) {
|
|
mprintf(("WARNING: Error setting bitmap texture (%i)!\n", material_info->get_texture_map(TM_BASE_TYPE)));
|
|
}
|
|
}
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::shield_impact_data>(
|
|
[&](graphics::generic_data::shield_impact_data* data) {
|
|
data->hitNormal = impact_orient.vec.fvec;
|
|
data->shieldProjMatrix = impact_projection;
|
|
data->shieldModelViewMatrix = impact_transform;
|
|
data->shieldMapIndex = array_index;
|
|
data->srgb = High_dynamic_range ? 1 : 0;
|
|
data->color = material_info->get_color();
|
|
});
|
|
|
|
gr_matrix_set_uniforms();
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, 0, 0);
|
|
}
|
|
|
|
void gr_opengl_update_distortion()
|
|
{
|
|
if (Distortion_framebuffer == 0) {
|
|
// distortion is disabled
|
|
return;
|
|
}
|
|
|
|
GR_DEBUG_SCOPE("Update distortion");
|
|
TRACE_SCOPE(tracing::UpdateDistortion);
|
|
|
|
GLboolean depth = GL_state.DepthTest(GL_FALSE);
|
|
GLboolean depth_mask = GL_state.DepthMask(GL_FALSE);
|
|
GLboolean blend = GL_state.Blend(GL_FALSE);
|
|
GLboolean cull = GL_state.CullFace(GL_FALSE);
|
|
|
|
opengl_shader_set_passthrough(true, High_dynamic_range);
|
|
|
|
GL_state.PushFramebufferState();
|
|
GL_state.BindFrameBuffer(Distortion_framebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Distortion_texture[!Distortion_switch], 0);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
glViewport(0,0,32,32);
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Distortion_texture[Distortion_switch]);
|
|
glClearColor(0.5f, 0.5f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
vertex vertices[4];
|
|
|
|
vertices[0].texture_position.u = 0.0f;
|
|
vertices[0].texture_position.v = 0.0f;
|
|
|
|
vertices[1].texture_position.u = 0.96875f;
|
|
vertices[1].texture_position.v = 0.0f;
|
|
|
|
vertices[2].texture_position.u = 0.0f;
|
|
vertices[2].texture_position.v = 1.0f;
|
|
|
|
vertices[3].texture_position.u = 0.96875f;
|
|
vertices[3].texture_position.v = 1.0f;
|
|
|
|
vertices[0].screen.xyw.x = 0.03f*(float)gr_screen.max_w;
|
|
vertices[0].screen.xyw.y = (float)gr_screen.max_h;
|
|
|
|
vertices[1].screen.xyw.x = (float)gr_screen.max_w;
|
|
vertices[1].screen.xyw.y = (float)gr_screen.max_h;
|
|
|
|
vertices[2].screen.xyw.x = 0.03f*(float)gr_screen.max_w;
|
|
vertices[2].screen.xyw.y = 0.0f;
|
|
|
|
vertices[3].screen.xyw.x = (float)gr_screen.max_w;
|
|
vertices[3].screen.xyw.y = 0.0f;
|
|
|
|
vertex_layout vert_def;
|
|
|
|
vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), offsetof(vertex, screen));
|
|
vert_def.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(vertex), offsetof(vertex, texture_position));
|
|
|
|
opengl_render_primitives_immediate(PRIM_TYPE_TRISTRIP, &vert_def, 4, vertices, sizeof(vertex) * 4);
|
|
|
|
opengl_shader_set_passthrough(false, High_dynamic_range);
|
|
|
|
vertex distortion_verts[33];
|
|
|
|
for(int i = 0; i < 33; i++)
|
|
{
|
|
distortion_verts[i].r = (ubyte)Random::next(256);
|
|
distortion_verts[i].g = (ubyte)Random::next(256);
|
|
distortion_verts[i].b = 255;
|
|
distortion_verts[i].a = 255;
|
|
|
|
distortion_verts[i].screen.xyw.x = 1.f;
|
|
distortion_verts[i].screen.xyw.y = (float)gr_screen.max_h*0.03125f*i;
|
|
}
|
|
|
|
vert_def = vertex_layout();
|
|
|
|
vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), offsetof(vertex, screen));
|
|
vert_def.add_vertex_component(vertex_format_data::COLOR4, sizeof(vertex), offsetof(vertex, r));
|
|
|
|
opengl_render_primitives_immediate(PRIM_TYPE_POINTS, &vert_def, 33, distortion_verts, 33 * sizeof(vertex));
|
|
|
|
Distortion_switch = !Distortion_switch;
|
|
|
|
// reset state
|
|
GL_state.PopFramebufferState();
|
|
|
|
glViewport(0,0,gr_screen.max_w,gr_screen.max_h);
|
|
|
|
GL_state.DepthTest(depth);
|
|
GL_state.DepthMask(depth_mask);
|
|
GL_state.Blend(blend);
|
|
GL_state.CullFace(cull);
|
|
}
|
|
|
|
void opengl_render_primitives(primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle,
|
|
size_t vert_offset,
|
|
size_t byte_offset)
|
|
{
|
|
GR_DEBUG_SCOPE("Render primitives");
|
|
|
|
Assertion(buffer_handle.isValid(),
|
|
"A valid buffer handle is required! Use the immediate buffer if data is not in GPU buffer yet.");
|
|
|
|
opengl_bind_vertex_layout(*layout, opengl_buffer_get_id(GL_ARRAY_BUFFER, buffer_handle), 0, byte_offset);
|
|
|
|
glDrawArrays(opengl_primitive_type(prim_type), (GLint)vert_offset, n_verts);
|
|
}
|
|
|
|
void opengl_render_primitives_immediate(primitive_type prim_type, vertex_layout* layout, int n_verts, void* data, int size)
|
|
{
|
|
auto offset = gr_add_to_immediate_buffer(size, data);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, gr_immediate_buffer_handle, 0, offset);
|
|
}
|
|
|
|
void gr_opengl_render_primitives(material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int offset,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle,
|
|
size_t buffer_offset)
|
|
{
|
|
GL_CHECK_FOR_ERRORS("start of gr_opengl_render_primitives()");
|
|
|
|
opengl_tnl_set_material(material_info, true);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, offset, buffer_offset);
|
|
|
|
GL_CHECK_FOR_ERRORS("end of gr_opengl_render_primitives()");
|
|
}
|
|
|
|
void gr_opengl_render_primitives_particle(particle_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int offset,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle)
|
|
{
|
|
GL_CHECK_FOR_ERRORS("start of gr_opengl_render_primitives_particle()");
|
|
|
|
opengl_tnl_set_material_particle(material_info);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, offset, 0);
|
|
|
|
GL_CHECK_FOR_ERRORS("end of gr_opengl_render_primitives_particle()");
|
|
}
|
|
|
|
void gr_opengl_render_primitives_distortion(distortion_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int offset,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle)
|
|
{
|
|
GL_CHECK_FOR_ERRORS("start of gr_opengl_render_primitives_distortion()");
|
|
|
|
opengl_tnl_set_material_distortion(material_info);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, offset, 0);
|
|
|
|
GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
|
|
glDrawBuffers(2, buffers);
|
|
|
|
GL_CHECK_FOR_ERRORS("start of gr_opengl_render_primitives_distortion()");
|
|
}
|
|
|
|
void gr_opengl_render_movie(movie_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int n_verts,
|
|
gr_buffer_handle buffer,
|
|
size_t buffer_offset)
|
|
{
|
|
GR_DEBUG_SCOPE("Render movie frame");
|
|
|
|
gr_set_2d_matrix();
|
|
|
|
opengl_tnl_set_material_movie(material_info);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer, 0, buffer_offset);
|
|
|
|
gr_end_2d_matrix();
|
|
}
|
|
|
|
void gr_opengl_render_nanovg(nanovg_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int offset,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle)
|
|
{
|
|
GR_DEBUG_SCOPE("Render NanoVG primitives");
|
|
|
|
opengl_tnl_set_material_nanovg(material_info);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, offset, 0);
|
|
}
|
|
|
|
void gr_opengl_render_primitives_batched(batched_bitmap_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int offset,
|
|
int n_verts,
|
|
gr_buffer_handle buffer_handle)
|
|
{
|
|
GR_DEBUG_SCOPE("Render batched primitives");
|
|
|
|
opengl_tnl_set_material_batched(material_info);
|
|
|
|
opengl_render_primitives(prim_type, layout, n_verts, buffer_handle, offset, 0);
|
|
}
|
|
void gr_opengl_render_rocket_primitives(interface_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int n_indices,
|
|
gr_buffer_handle vertex_buffer,
|
|
gr_buffer_handle index_buffer)
|
|
{
|
|
GR_DEBUG_SCOPE("Render rocket ui primitives");
|
|
|
|
gr_set_2d_matrix();
|
|
|
|
opengl_tnl_set_rocketui_material(material_info);
|
|
|
|
opengl_bind_vertex_layout(*layout,
|
|
opengl_buffer_get_id(GL_ARRAY_BUFFER, vertex_buffer),
|
|
opengl_buffer_get_id(GL_ELEMENT_ARRAY_BUFFER, index_buffer));
|
|
|
|
glDrawElements(opengl_primitive_type(prim_type), n_indices, GL_UNSIGNED_INT, nullptr);
|
|
|
|
gr_end_2d_matrix();
|
|
}
|
|
|
|
void opengl_draw_textured_quad(GLfloat x1,
|
|
GLfloat y1,
|
|
GLfloat u1,
|
|
GLfloat v1,
|
|
GLfloat x2,
|
|
GLfloat y2,
|
|
GLfloat u2,
|
|
GLfloat v2) {
|
|
GR_DEBUG_SCOPE("Draw textured quad");
|
|
|
|
GLfloat glVertices[4][4] = {
|
|
{ x1, y1, u1, v1 },
|
|
{ x1, y2, u1, v2 },
|
|
{ x2, y1, u2, v1 },
|
|
{ x2, y2, u2, v2 }
|
|
};
|
|
|
|
vertex_layout vert_def;
|
|
|
|
vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(GLfloat) * 4, 0);
|
|
vert_def.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(GLfloat) * 4, sizeof(GLfloat) * 2);
|
|
|
|
opengl_render_primitives_immediate(PRIM_TYPE_TRISTRIP, &vert_def, 4, glVertices, sizeof(glVertices));
|
|
}
|
|
|
|
void opengl_draw_full_screen_textured(GLfloat u1, GLfloat v1, GLfloat u2, GLfloat v2)
|
|
{
|
|
GR_DEBUG_SCOPE("Draw full screen triangle");
|
|
|
|
GLfloat glVertices[3][4] = {
|
|
{-1.f, -1.f, u1, v1},
|
|
{3.f, -1.f, u2 * 2.f, v1},
|
|
{-1.f, 3.f, u1, v2 * 2.f},
|
|
};
|
|
|
|
vertex_layout vert_def;
|
|
|
|
vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(GLfloat) * 4, 0);
|
|
vert_def.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(GLfloat) * 4, sizeof(GLfloat) * 2);
|
|
|
|
opengl_render_primitives_immediate(PRIM_TYPE_TRIS, &vert_def, 3, glVertices, sizeof(glVertices));
|
|
}
|
|
|
|
void opengl_draw_full_screen_scene_texture()
|
|
{
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_v_scale);
|
|
}
|
|
|
|
void gr_opengl_render_decals(decal_material* material_info,
|
|
primitive_type prim_type,
|
|
vertex_layout* layout,
|
|
int num_elements,
|
|
const indexed_vertex_source& binding,
|
|
const gr_buffer_handle& instance_buffer,
|
|
int num_instances) {
|
|
opengl_tnl_set_material_decal(material_info);
|
|
|
|
opengl_bind_vertex_layout_multiple(*layout,
|
|
SCP_vector<GLuint> { opengl_buffer_get_id(GL_ARRAY_BUFFER, binding.Vbuffer_handle), opengl_buffer_get_id(GL_ARRAY_BUFFER, instance_buffer) },
|
|
opengl_buffer_get_id(GL_ELEMENT_ARRAY_BUFFER, binding.Ibuffer_handle));
|
|
|
|
glDrawElementsInstanced(opengl_primitive_type(prim_type), num_elements, GL_UNSIGNED_INT, nullptr, num_instances);
|
|
}
|
|
|
|
void gr_opengl_start_decal_pass() {
|
|
// For now we only render into the diffuse channel of the framebuffer
|
|
#ifndef USE_OPENGL_ES
|
|
GLenum buffers[] = {
|
|
GL_COLOR_ATTACHMENT0,
|
|
GL_COLOR_ATTACHMENT2,
|
|
GL_COLOR_ATTACHMENT4,
|
|
};
|
|
glDrawBuffers(3, buffers);
|
|
#else
|
|
GLenum buffers[] = {
|
|
GL_COLOR_ATTACHMENT0,
|
|
GL_NONE,
|
|
GL_COLOR_ATTACHMENT2,
|
|
GL_NONE,
|
|
GL_COLOR_ATTACHMENT4,
|
|
};
|
|
glDrawBuffers(5, buffers);
|
|
#endif
|
|
}
|
|
void gr_opengl_stop_decal_pass() {
|
|
GLenum buffers2[] = {
|
|
GL_COLOR_ATTACHMENT0,
|
|
GL_COLOR_ATTACHMENT1,
|
|
GL_COLOR_ATTACHMENT2,
|
|
GL_COLOR_ATTACHMENT3,
|
|
GL_COLOR_ATTACHMENT4,
|
|
GL_COLOR_ATTACHMENT5,
|
|
};
|
|
glDrawBuffers(6, buffers2);
|
|
}
|
|
|
|
void gr_opengl_calculate_irrmap()
|
|
{
|
|
|
|
auto previous_target = gr_screen.rendering_to_texture;
|
|
int irr_shader = gr_opengl_maybe_create_shader(SDR_TYPE_IRRADIANCE_MAP_GEN, 0);
|
|
opengl_shader_set_current(irr_shader);
|
|
auto env_tex = bm_get_gr_info<tcache_slot_opengl>(ENVMAP);
|
|
glBindTexture(env_tex->texture_target, env_tex->texture_id);
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_CUBE_MAP, env_tex->texture_id);
|
|
Current_shader->program->Uniforms.setTextureUniform("envmap", 0);
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
bm_set_render_target(gr_screen.irrmap_render_target, i);
|
|
gr_clear();
|
|
opengl_set_generic_uniform_data<graphics::generic_data::irrmap_data>(
|
|
[&](graphics::generic_data::irrmap_data* data) { data->face = i; });
|
|
opengl_draw_full_screen_textured(-1.0f, -1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
bm_set_render_target(previous_target);
|
|
}
|