I'm using OpenGL ES 2 and am trying to load textures in the background using st::async
however because I'm loading the image into a GL texture in a thread it doesnt work how its supposed to, I read on google that OpenGL isnt thread-safe to I think that's my issue, I mostly come here for advise on how I should implement my code so that most of the texture loading happens in the background without this issue, below is my current code
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
/// png format
GLuint load_texture(
const GLsizei width, const GLsizei height,
const GLenum type, const GLvoid *pixels)
{
// create new OpenGL texture
GLuint texture_object_id;
glGenTextures(1, &texture_object_id);
glBindTexture(GL_TEXTURE_2D, texture_object_id);
// set texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// generate texture from bitmap data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// don't create MipMaps, we must assert npot2!
// glGenerateMipmap(GL_TEXTURE_2D);
// release and return texture
glBindTexture(GL_TEXTURE_2D, 0);
// log_info("width: %d, height: %d, type: %d, pixels: %p", width, height, type, pixels);
return texture_object_id;
}
/* externed */
vec2 tex_size; // last loaded png size as (w, h)
/// textures
std::mutex textures_mtx;
GLuint load_png_asset_into_texture(const char* relative_path)
{
std::lock_guard<std::mutex> lock(textures_mtx);
int w = 0, h = 0, comp= 0;
unsigned char* image = stbi_load(relative_path, &w, &h, &comp, STBI_rgb_alpha);
if(image == nullptr){
log_debug( "%s(%s) FAILED!", __FUNCTION__, relative_path);
return 0;
}
GLuint tex = load_texture(w, h, comp, image);
log_info("loaded %s(%s) as %d", __FUNCTION__, relative_path, tex);
stbi_image_free(image);
return tex;
}
void check_tex_for_reload(int idx)
{
std::string cb_path;
std::lock_guard<std::mutex> lock(disc_lock);
int w, h;
cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].info.id);
if (!if_exists(cb_path.c_str()) || !is_png_vaild(cb_path.c_str(), &w, &h))
{
log_info("%s doesnt exist", cb_path.c_str());
all_apps[idx].icon.cover_exists = false;
}
else
all_apps[idx].icon.cover_exists = true;
}
void check_n_load_textures(int idx)
{
std::lock_guard<std::mutex> lock(disc_lock);
int w, h;
if (all_apps[idx].icon.texture.load() == GL_NULL)
{
std::string cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].info.id);
if (all_apps[idx].icon.cover_exists)
all_apps[idx].icon.texture = load_png_asset_into_texture(cb_path.c_str());
else // load icon0 if cover doesnt exist
all_apps[idx].icon.texture = load_png_asset_into_texture(all_apps[idx].info.picpath.c_str());
}
}
static void check_n_draw_textures(int idx, int SH_type, vec4 col)
{
// std::lock_guard<std::mutex> lock(disc_lock);
cover_t.render_tex(cb_tex, SH_type, col);
if (!all_apps[idx].icon.cover_exists && all_apps[idx].icon.texture == GL_NULL)
cover_i.render_tex(fallback_t, SH_type, col);
else
cover_i.render_tex(all_apps[idx].icon.texture.load(), SH_type, col);
// log_info("tex %d", all_apps[idx].icon.texture.load() );
}
static void LoadIconsAsync(int i){
vec4 colo = (1.);
uint64_t fmem = 0;
check_tex_for_reload(i);
check_n_load_textures(i);
}
std::vector<std::future<void>> m_future;
int i = 0;
for (item_t &item : all_apps)
{
m_future.push_back(std::async(std::launch::async, LoadIconsAsync, i));
i++;
}
called in the main loop
check_n_draw_textures(tex_idx, +1, colo);
I tried to make it more thread-safe by using mutexs but later I read something about sharing GL contexts