GLuint TexturePool::get_texture(GLenum format, GLuint width, GLuint height, GLuint num_layers)
{
- for (Texture &tex : textures) {
- if (!tex.in_use && !tex.is_renderbuffer && tex.format == format &&
- tex.width == width && tex.height == height && tex.num_layers == num_layers) {
- tex.in_use = true;
- return tex.tex_num;
+ {
+ lock_guard<mutex> lock(mu);
+ for (Texture &tex : textures) {
+ if (!tex.in_use && !tex.is_renderbuffer && tex.format == format &&
+ tex.width == width && tex.height == height && tex.num_layers == num_layers) {
+ tex.in_use = true;
+ return tex.tex_num;
+ }
}
}
tex.num_layers = num_layers;
tex.in_use = true;
tex.is_renderbuffer = false;
- textures.push_back(tex);
+ {
+ lock_guard<mutex> lock(mu);
+ textures.push_back(tex);
+ }
return tex.tex_num;
}
GLuint TexturePool::get_renderbuffer(GLenum format, GLuint width, GLuint height)
{
- for (Texture &tex : textures) {
- if (!tex.in_use && tex.is_renderbuffer && tex.format == format &&
- tex.width == width && tex.height == height) {
- tex.in_use = true;
- return tex.tex_num;
+ {
+ lock_guard<mutex> lock(mu);
+ for (Texture &tex : textures) {
+ if (!tex.in_use && tex.is_renderbuffer && tex.format == format &&
+ tex.width == width && tex.height == height) {
+ tex.in_use = true;
+ return tex.tex_num;
+ }
}
}
tex.height = height;
tex.in_use = true;
tex.is_renderbuffer = true;
- textures.push_back(tex);
+ {
+ lock_guard<mutex> lock(mu);
+ textures.push_back(tex);
+ }
return tex.tex_num;
}
void TexturePool::release_texture(GLuint tex_num)
{
+ lock_guard<mutex> lock(mu);
for (Texture &tex : textures) {
if (!tex.is_renderbuffer && tex.tex_num == tex_num) {
assert(tex.in_use);
void TexturePool::release_renderbuffer(GLuint tex_num)
{
+ lock_guard<mutex> lock(mu);
for (Texture &tex : textures) {
if (tex.is_renderbuffer && tex.tex_num == tex_num) {
assert(tex.in_use);
#include <epoxy/gl.h>
#include <array>
#include <map>
+#include <mutex>
#include <vector>
#include <utility>
GLuint uniform_scale_factor;
};
+// All operations, except construction and destruction, are thread-safe.
class TexturePool {
public:
GLuint get_texture(GLenum format, GLuint width, GLuint height, GLuint num_layers = 0);
bool in_use = false;
bool is_renderbuffer = false;
};
- std::vector<Texture> textures;
+ std::mutex mu;
+ std::vector<Texture> textures; // Under mu.
};
class DISComputeFlow {
check_error();
glGenerateTextureMipmap(resources.gray_tex);
check_error();
- GLuint flow_tex = compute_flow->exec(resources.gray_tex, DISComputeFlow::FORWARD_AND_BACKWARD, DISComputeFlow::DO_NOT_RESIZE_FLOW);
+ qf.flow_tex = compute_flow->exec(resources.gray_tex, DISComputeFlow::FORWARD_AND_BACKWARD, DISComputeFlow::DO_NOT_RESIZE_FLOW);
check_error();
- qf.output_tex = interpolate->exec(resources.input_tex, flow_tex, 1280, 720, alpha);
+ qf.output_tex = interpolate->exec(resources.input_tex, qf.flow_tex, 1280, 720, alpha);
check_error();
+ // We could have released qf.flow_tex here, but to make sure we don't cause a stall
+ // when trying to reuse it for the next frame, we can just as well hold on to it
+ // and release it only when the readback is done.
+
// Read it down (asynchronously) to the CPU.
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, resources.pbo);
glClientWaitSync(qf.fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
vector<uint8_t> jpeg = encode_jpeg((const uint8_t *)qf.resources.pbo_contents, 1280, 720);
+ compute_flow->release_texture(qf.flow_tex);
+ interpolate->release_texture(qf.output_tex);
AVPacket pkt;
av_init_packet(&pkt);
int64_t input_second_pts;
float alpha;
InterpolatedFrameResources resources;
- GLuint output_tex;
RefCountedGLsync fence; // Set when the interpolated image is read back to the CPU.
+ GLuint flow_tex, output_tex; // Released in the receiving thread; not really used for anything else.
};
std::deque<QueuedFrame> frame_queue; // Under <queue_lock>.
std::mutex queue_lock;