I will do this so it will choose the type based on what is in the shader, and you can use either. It also stores a shared pointer to the texture so it can't get deleted as long as it is bound to the shader:
void RenderShader::SetUniform(const int location, std::shared_ptr<RenderTexture> tex)
{
if (not Finalize()) return;
if (location < 0 or location >= uniforms.size()) return;
auto& u = uniforms[location];
if (u.defined and u.resource == tex) return;
if (u.size != 1) return;
GLuint64 handle = 0;
switch (u.type)
{
case GL_UNSIGNED_INT64_ARB:
if (tex) handle = tex->GetHandle();
glProgramUniformHandleui64ARB(program, u.location, handle);
break;
case GL_UNSIGNED_INT_VEC2:
if (tex) handle = tex->GetHandle();
glProgramUniform2uiv(program, u.location, 1, (GLuint*)handle);
break;
default:
return;
break;
}
glCheckError();
u.defined = true;
u.resource = nullptr;
if (handle) u.resource = tex;
}