]> git.sesse.net Git - nageru/blobdiff - futatabi/video_stream.cpp
Change Futatabi frames to be cached as textures instead of in system memory.
[nageru] / futatabi / video_stream.cpp
index 5a36801b5e6ebf787cc5f84874650d63db2862fb..591ee7e219883e5f4eb9209cbb17e5100108ea83 100644 (file)
@@ -11,6 +11,7 @@ extern "C" {
 #include "flow.h"
 #include "jpeg_frame_view.h"
 #include "movit/util.h"
+#include "pbo_pool.h"
 #include "player.h"
 #include "shared/context.h"
 #include "shared/httpd.h"
@@ -37,6 +38,14 @@ Summary metric_interpolation_latency_seconds;
 Summary metric_fade_fence_wait_time_seconds;
 Summary metric_interpolation_fence_wait_time_seconds;
 
+void wait_for_upload(shared_ptr<Frame> &frame)
+{
+       if (frame->uploaded_interpolation != nullptr) {
+               glWaitSync(frame->uploaded_interpolation.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
+               frame->uploaded_interpolation.reset();
+       }
+}
+
 }  // namespace
 
 extern HTTPD *global_httpd;
@@ -152,6 +161,16 @@ string encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const uint8_t
        return move(dest.dest);
 }
 
+string encode_jpeg_from_pbo(void *contents, unsigned width, unsigned height, const string exif_data)
+{
+       unsigned chroma_width = width / 2;
+
+       const uint8_t *y = (const uint8_t *)contents;
+       const uint8_t *cb = (const uint8_t *)contents + width * height;
+       const uint8_t *cr = (const uint8_t *)contents + width * height + chroma_width * height;
+       return encode_jpeg(y, cb, cr, width, height, move(exif_data));
+}
+
 VideoStream::VideoStream(AVFormatContext *file_avctx)
        : avctx(file_avctx), output_fast_forward(file_avctx != nullptr)
 {
@@ -430,6 +449,8 @@ void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64
 
        shared_ptr<Frame> frame1 = decode_jpeg_with_cache(frame1_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
        shared_ptr<Frame> frame2 = decode_jpeg_with_cache(frame2_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
+       wait_for_upload(frame1);
+       wait_for_upload(frame2);
 
        ycbcr_semiplanar_converter->prepare_chain_for_fade(frame1, frame2, fade_alpha)->render_to_fbo(resources->fade_fbo, global_flags.width, global_flags.height);
 
@@ -517,6 +538,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts
                FrameOnDisk frame_spec = frame_no == 1 ? frame2 : frame1;
                bool did_decode;
                shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
+               wait_for_upload(frame);
                ycbcr_converter->prepare_chain_for_conversion(frame)->render_to_fbo(resources->input_fbos[frame_no], global_flags.width, global_flags.height);
                if (frame_no == 1) {
                        qf.exif_data = frame->exif_data;  // Use the white point from the last frame.
@@ -557,6 +579,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts
                // Now decode the image we are fading against.
                bool did_decode;
                shared_ptr<Frame> frame2 = decode_jpeg_with_cache(secondary_frame, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
+               wait_for_upload(frame2);
 
                // Then fade against it, putting it into the fade Y' and CbCr textures.
                RGBTriplet neutral_color = get_neutral_color(qf.exif_data);
@@ -645,31 +668,27 @@ void VideoStream::schedule_silence(steady_clock::time_point local_pts, int64_t o
 
 namespace {
 
-shared_ptr<Frame> frame_from_pbo(void *contents, size_t width, size_t height)
+RefCountedTexture clone_r8_texture(GLuint src_tex, unsigned width, unsigned height)
 {
-       size_t chroma_width = width / 2;
-
-       const uint8_t *y = (const uint8_t *)contents;
-       const uint8_t *cb = (const uint8_t *)contents + width * height;
-       const uint8_t *cr = (const uint8_t *)contents + width * height + chroma_width * height;
+       GLuint tex;
+       glCreateTextures(GL_TEXTURE_2D, 1, &tex);
+       check_error();
+       glTextureStorage2D(tex, 1, GL_R8, width, height);
+       check_error();
+       glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, 0, 0, 0,
+                          tex, GL_TEXTURE_2D, 0, 0, 0, 0,
+                          width, height, 1);
+       check_error();
+       glTextureParameteri(tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       check_error();
+       glTextureParameteri(tex, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       check_error();
+       glTextureParameteri(tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       check_error();
+       glTextureParameteri(tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       check_error();
 
-       shared_ptr<Frame> frame(new Frame);
-       frame->y.reset(new uint8_t[width * height]);
-       frame->cb.reset(new uint8_t[chroma_width * height]);
-       frame->cr.reset(new uint8_t[chroma_width * height]);
-       for (unsigned yy = 0; yy < height; ++yy) {
-               memcpy(frame->y.get() + width * yy, y + width * yy, width);
-               memcpy(frame->cb.get() + chroma_width * yy, cb + chroma_width * yy, chroma_width);
-               memcpy(frame->cr.get() + chroma_width * yy, cr + chroma_width * yy, chroma_width);
-       }
-       frame->is_semiplanar = false;
-       frame->width = width;
-       frame->height = height;
-       frame->chroma_subsampling_x = 2;
-       frame->chroma_subsampling_y = 1;
-       frame->pitch_y = width;
-       frame->pitch_chroma = chroma_width;
-       return frame;
+       return RefCountedTexture(new GLuint(tex), TextureDeleter());
 }
 
 }  // namespace
@@ -685,6 +704,8 @@ void VideoStream::encode_thread_func()
                abort();
        }
 
+       init_pbo_pool();
+
        while (!should_quit) {
                QueuedFrame qf;
                {
@@ -751,11 +772,8 @@ void VideoStream::encode_thread_func()
                        metric_fade_fence_wait_time_seconds.count_event(duration<double>(stop - start).count());
                        metric_fade_latency_seconds.count_event(duration<double>(stop - qf.fence_created).count());
 
-                       shared_ptr<Frame> frame = frame_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height);
-                       assert(frame->exif_data.empty());
-
                        // Now JPEG encode it, and send it on to the stream.
-                       string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, /*exif_data=*/"");
+                       string jpeg = encode_jpeg_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height, /*exif_data=*/"");
 
                        AVPacket pkt;
                        av_init_packet(&pkt);
@@ -775,13 +793,25 @@ void VideoStream::encode_thread_func()
                        metric_interpolation_latency_seconds.count_event(duration<double>(stop - qf.fence_created).count());
 
                        // Send it on to display.
-                       shared_ptr<Frame> frame = frame_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height);
                        if (qf.display_decoded_func != nullptr) {
-                               qf.display_decoded_func(frame);
+                               shared_ptr<Frame> frame(new Frame);
+                               if (qf.type == QueuedFrame::FADED_INTERPOLATED) {
+                                       frame->y = clone_r8_texture(qf.resources->fade_y_output_tex, global_flags.width, global_flags.height);
+                               } else {
+                                       frame->y = clone_r8_texture(qf.output_tex, global_flags.width, global_flags.height);
+                               }
+                               frame->cb = clone_r8_texture(qf.resources->cb_tex, global_flags.width / 2, global_flags.height);
+                               frame->cr = clone_r8_texture(qf.resources->cr_tex, global_flags.width / 2, global_flags.height);
+                               frame->width = global_flags.width;
+                               frame->height = global_flags.height;
+                               frame->chroma_subsampling_x = 2;
+                               frame->chroma_subsampling_y = 1;
+                               frame->uploaded_ui_thread = RefCountedGLsync(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
+                               qf.display_decoded_func(move(frame));
                        }
 
                        // Now JPEG encode it, and send it on to the stream.
-                       string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, move(qf.exif_data));
+                       string jpeg = encode_jpeg_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height, move(qf.exif_data));
                        if (qf.flow_tex != 0) {
                                compute_flow->release_texture(qf.flow_tex);
                        }