diff --git a/code/graphics/2d.h b/code/graphics/2d.h index b701bdeefc6..d6eae7694c6 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -787,6 +787,7 @@ typedef struct screen { std::function gf_bm_make_render_target; std::function gf_bm_set_render_target; + std::function gf_bm_is_valid_render_target; std::function gf_set_texture_addressing; @@ -1129,6 +1130,8 @@ inline int gr_bm_set_render_target(int n, int face = -1) return gr_screen.gf_bm_set_render_target(n, face); } +#define gr_bm_is_valid_render_target GR_CALL(gr_screen.gf_bm_is_valid_render_target) + #define gr_set_texture_addressing GR_CALL(gr_screen.gf_set_texture_addressing) inline gr_buffer_handle gr_create_buffer(BufferType type, BufferUsageHint usage) diff --git a/code/graphics/grstub.cpp b/code/graphics/grstub.cpp index d3155560e2b..9a737b40054 100644 --- a/code/graphics/grstub.cpp +++ b/code/graphics/grstub.cpp @@ -274,6 +274,11 @@ int gr_stub_bm_set_render_target(int /*n*/, int /*face*/) return 0; } +bool gr_stub_bm_is_valid_render_target(int /*bitmap_handle*/) +{ + return false; +} + void gr_stub_bm_create(bitmap_slot* /*slot*/) { } @@ -542,6 +547,7 @@ void gr_stub_init_function_pointers() { gr_screen.gf_bm_data = gr_stub_bm_data; gr_screen.gf_bm_make_render_target = gr_stub_bm_make_render_target; gr_screen.gf_bm_set_render_target = gr_stub_bm_set_render_target; + gr_screen.gf_bm_is_valid_render_target = gr_stub_bm_is_valid_render_target; gr_screen.gf_set_cull = gr_stub_set_cull; gr_screen.gf_set_color_buffer = gr_stub_set_color_buffer; diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index d13a48bb4ce..1331c9fbdf5 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -1002,6 +1002,7 @@ void gr_opengl_init_function_pointers() gr_screen.gf_bm_data = gr_opengl_bm_data; gr_screen.gf_bm_make_render_target = gr_opengl_bm_make_render_target; gr_screen.gf_bm_set_render_target = gr_opengl_bm_set_render_target; + gr_screen.gf_bm_is_valid_render_target = gr_opengl_bm_is_valid_render_target; gr_screen.gf_set_cull = gr_opengl_set_cull; gr_screen.gf_set_color_buffer = gr_opengl_set_color_buffer; diff --git a/code/graphics/opengl/gropenglbmpman.cpp b/code/graphics/opengl/gropenglbmpman.cpp index fa49b068a6e..f022cd2cff6 100644 --- a/code/graphics/opengl/gropenglbmpman.cpp +++ b/code/graphics/opengl/gropenglbmpman.cpp @@ -141,6 +141,21 @@ int gr_opengl_bm_set_render_target(int n, int face) return 0; } +bool gr_opengl_bm_is_valid_render_target(int bitmap_handle) +{ + if (bitmap_handle < 0) { + return false; + } + + if ( !bm_is_render_target(bitmap_handle) ) { + return false; + } + + auto ts = bm_get_gr_info(bitmap_handle); + + return (ts->texture_id && (ts->fbo_id >= 0)); +} + bool gr_opengl_bm_data(int /*n*/, bitmap* /*bm*/) { // Do nothing here diff --git a/code/graphics/opengl/gropenglbmpman.h b/code/graphics/opengl/gropenglbmpman.h index c4eb6300b74..c348b12706b 100644 --- a/code/graphics/opengl/gropenglbmpman.h +++ b/code/graphics/opengl/gropenglbmpman.h @@ -34,5 +34,6 @@ bool gr_opengl_bm_data(int handle, bitmap* bm); void gr_opengl_bm_save_render_target(int slot); int gr_opengl_bm_make_render_target(int n, int *width, int *height, int *bpp, int *mm_lvl, int flags); int gr_opengl_bm_set_render_target(int n, int face); +bool gr_opengl_bm_is_valid_render_target(int bitmap_handle); #endif // _OGL_BMPMAN_H diff --git a/code/graphics/opengl/gropengltexture.cpp b/code/graphics/opengl/gropengltexture.cpp index 6407df36a47..6f945b8f1db 100644 --- a/code/graphics/opengl/gropengltexture.cpp +++ b/code/graphics/opengl/gropengltexture.cpp @@ -673,8 +673,10 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap auto mipmap_w = tex_w; auto mipmap_h = tex_h; - // should never have mipmap levels if we also have to manually resize - if ((mipmap_levels > 1) && resize) { + // if we are doing mipmap resizing we need to account for adjusted tex size + // (we can end up with only one mipmap level if base_level is high enough so don't check it) + if (base_level > 0) { + Assertion(resize == false, "ERROR: Cannot use manual and mipmap resizing at the same time!"); Assert(texmem == nullptr); // If we have mipmaps then tex_w/h are already adjusted for the base level but that will cause problems with @@ -1113,11 +1115,6 @@ int gr_opengl_tcache_set_internal(int bitmap_handle, int bitmap_type, float *u_s GR_DEBUG_SCOPE("Activate texture"); - if (GL_last_detail != Detail.hardware_textures) { - GL_last_detail = Detail.hardware_textures; - opengl_tcache_flush(); - } - auto t = bm_get_gr_info(bitmap_handle, true); if (!bm_is_render_target(bitmap_handle) && t->bitmap_handle < 0) @@ -1166,6 +1163,11 @@ int gr_opengl_tcache_set(int bitmap_handle, int bitmap_type, float *u_scale, flo int rc = 0; + // set output defaults in case of error + *u_scale = 1.0f; + *v_scale = 1.0f; + *array_index = 0; + if (bitmap_handle < 0) { return 0; } @@ -1193,7 +1195,14 @@ void opengl_preload_init() if (gr_screen.mode != GR_OPENGL) return; -// opengl_tcache_flush (); + // If texture detail level has changed since last mission load then flush the + // cache to allow for texture resizing. We should only get here very early + // during mission load (and restart) which should allow for render targets + // to be (re)created normally. + if (GL_last_detail != Detail.hardware_textures) { + GL_last_detail = Detail.hardware_textures; + opengl_tcache_flush(); + } } int gr_opengl_preload(int bitmap_num, int is_aabitmap) diff --git a/code/render/batching.cpp b/code/render/batching.cpp index 2c5555804f0..801934c22a0 100644 --- a/code/render/batching.cpp +++ b/code/render/batching.cpp @@ -811,9 +811,13 @@ void batching_add_line(vec3d *start, vec3d *end, float widthStart, float widthEn return; } - if (lineTexture < 0) + if ( !gr_bm_is_valid_render_target(lineTexture) ) { - //We only need a single pixel sized texture to render as many lines as we want. + if (lineTexture >= 0) { + bm_release(lineTexture, 1); + } + + //We only need a single pixel sized texture to render as many lines as we want. //If it doesn't exist yet, then we make one! auto previous_target = gr_screen.rendering_to_texture; diff --git a/code/scripting/api/objs/texture.cpp b/code/scripting/api/objs/texture.cpp index 17b0b6f6711..bc3c3ec117b 100644 --- a/code/scripting/api/objs/texture.cpp +++ b/code/scripting/api/objs/texture.cpp @@ -28,7 +28,7 @@ texture_h::~texture_h() //the textures using load_count. Anything that creates a texture object must also increase load count, unless it is //created in a way that already increases load_count (like bm_load). That way, a texture going out of scope needs to be //released and is safed against memleaks. -Lafiel - bm_release(handle); + bm_release(handle, 1); } bool texture_h::isValid() const { return bm_is_valid(handle) != 0; } texture_h::texture_h(texture_h&& other) noexcept { @@ -114,7 +114,7 @@ ADE_FUNC(unload, l_Texture, NULL, "Unloads a texture from memory", NULL, NULL) if (!th->isValid()) return ADE_RETURN_NIL; - bm_release(th->handle); + bm_release(th->handle, 1); //WMC - invalidate this handle th->handle = -1; diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index addb3f0355e..a74ebb19890 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8452,7 +8452,7 @@ void ship_close_cockpit_displays(ship* shipp) } if ( Player_displays[i].target >= 0 ) { - bm_release(Player_displays[i].target); + bm_release(Player_displays[i].target, 1); } } diff --git a/code/starfield/starfield.cpp b/code/starfield/starfield.cpp index 0f1a7168639..4d5d5c10dbd 100644 --- a/code/starfield/starfield.cpp +++ b/code/starfield/starfield.cpp @@ -1073,12 +1073,19 @@ void stars_level_close() { } } + if (gr_screen.irrmap_render_target >= 0) { + if ( bm_release(gr_screen.irrmap_render_target, 1) ) { + gr_screen.irrmap_render_target = -1; + } + } + if (Mission_env_map >= 0) { bm_release(Mission_env_map); Mission_env_map = -1; } ENVMAP = Default_env_map; + IRRMAP = -1; }