]> git.sesse.net Git - nageru/commitdiff
Hook up display (via a separate RGBA texture) to the UI.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 4 Oct 2015 15:29:40 +0000 (17:29 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 4 Oct 2015 15:29:40 +0000 (17:29 +0200)
glwidget.cpp
glwidget.h
mixer.cpp
mixer.h

index b9021446abae3f79adf1ca0659a892a39e64662a..8ccba20f69de87226e422316bbbd37216963064a 100644 (file)
 #include <QThread>
 #include <math.h>
 #include <thread>
+#include <movit/resource_pool.h>
+#undef Success
+#include <movit/util.h>
 
 GLWidget::GLWidget(QWidget *parent)
-    : QOpenGLWidget(parent)
+    : QOpenGLWidget(parent),
+      resource_pool(new movit::ResourcePool)
 {
 }
 
@@ -28,15 +32,80 @@ void GLWidget::initializeGL()
        printf("egl context=%p\n", eglGetCurrentContext());
        //printf("threads: %p %p\n", QThread::currentThread(), qGuiApp->thread());
 
+       GLWidget *t = this;
+       set_frame_ready_fallback([t]{
+               QMetaObject::invokeMethod(t, "update", Qt::AutoConnection);
+       });
+
        QSurface *surface = create_surface(format());
        QSurface *surface2 = create_surface(format());
        QSurface *surface3 = create_surface(format());
        QSurface *surface4 = create_surface(format());
        start_mixer(surface, surface2, surface3, surface4);
+
+       // Prepare the shaders to actually get the texture shown (ick).
+       glDisable(GL_BLEND);
+       glDisable(GL_DEPTH_TEST);
+       glDepthMask(GL_FALSE);
+
+       std::string vert_shader =
+               "#version 130 \n"
+               "in vec2 position; \n"
+               "in vec2 texcoord; \n"
+               "out vec2 tc; \n"
+               " \n"
+               "void main() \n"
+               "{ \n"
+               "    gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); \n"
+               "    tc = texcoord; \n"
+               "    tc.y = 1.0 - tc.y; \n"
+               "} \n";
+       std::string frag_shader =
+               "#version 130 \n"
+               "in vec2 tc; \n"
+               "uniform sampler2D tex; \n"
+               "void main() { \n"
+               "    gl_FragColor = texture2D(tex, tc); \n"
+               "} \n";
+       program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
+
+       static const float vertices[] = {
+               0.0f, 2.0f,
+               0.0f, 0.0f,
+               2.0f, 0.0f
+       };
+       glGenVertexArrays(1, &vao);
+       glBindVertexArray(vao);
+       GLuint position_vbo = movit::fill_vertex_attribute(program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
+       GLuint texcoord_vbo = movit::fill_vertex_attribute(program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices);  // Same as vertices.
+
+#if 0
+       // Cleanup.
+       cleanup_vertex_attribute(phases[0]->glsl_program_num, "position", position_vbo);
+       cleanup_vertex_attribute(phases[0]->glsl_program_num, "texcoord", texcoord_vbo);
+
+       glDeleteVertexArrays(1, &vao);
+       nheck_error();
+#endif
 }
 
 void GLWidget::paintGL()
 {
-       glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
-       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       DisplayFrame frame;
+       if (!mixer_get_display_frame(&frame)) {
+               glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+               return;
+       }
+
+       glUseProgram(program_num);
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_2D, frame.texnum);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+       glBindVertexArray(vao);
+       glWaitSync(frame.ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
+       glDrawArrays(GL_TRIANGLES, 0, 3);
 }
index 7e37c4ce2ec9b24487a15f39b3a819e2afa20bc0..0d74480802ef37224707f53a1b2906591ee43209 100644 (file)
@@ -3,6 +3,10 @@
 
 #include <QOpenGLWidget>
 
+namespace movit {
+class ResourcePool;
+}
+
 class GLWidget : public QOpenGLWidget
 {
        Q_OBJECT
@@ -12,8 +16,13 @@ public:
        ~GLWidget();
 
 protected:
-       void initializeGL() Q_DECL_OVERRIDE;
-       void paintGL() Q_DECL_OVERRIDE;
+       void initializeGL() override;
+       void paintGL() override;
+
+private:
+       movit::ResourcePool *resource_pool;
+       GLuint vao, program_num;
+
 };
 
 #endif
index 05b32871d3f528cc1e767c842713467fe4b0e687..10350a509bb705b7c847e7384b9b81f8e737f67d 100644 (file)
--- a/mixer.cpp
+++ b/mixer.cpp
 #include "context.h"
 #include "bmusb.h"
 #include "pbo_frame_allocator.h"
+#include "mixer.h"
 #include "ref_counted_gl_sync.h"
 
 using namespace movit;
 using namespace std;
 using namespace std::placeholders;
 
+static float t = 0.0f;
 
-std::mutex bmusb_mutex;  // protects <cards>
+ResourcePool *resource_pool;
 
+std::mutex display_frame_mutex;
+DisplayFrame current_display_frame, ready_display_frame;  // protected by <frame_mutex>
+bool has_current_display_frame = false, has_ready_display_frame = false;  // protected by <frame_mutex>
+
+std::mutex bmusb_mutex;
 struct CaptureCard {
        BMUSBCapture *usb;
 
@@ -72,7 +79,10 @@ struct CaptureCard {
        GLsync new_data_ready_fence;  // Whether new_frame is ready for rendering.
        std::condition_variable new_data_ready_changed;  // Set whenever new_data_ready is changed.
 };
-CaptureCard cards[NUM_CARDS];
+CaptureCard cards[NUM_CARDS];  // protected by <bmusb_mutex>
+
+new_frame_ready_callback_t new_frame_ready_callback;
+bool has_new_frame_ready_callback = false;
 
 void bm_frame(int card_index, uint16_t timecode,
               FrameAllocator::Frame video_frame, size_t video_offset, uint16_t video_format,
@@ -248,6 +258,7 @@ void mixer_thread_func(QSurface *surface, QSurface *surface2, QSurface *surface3
 
        ycbcr_format.chroma_subsampling_x = 1;
 
+       chain.add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
        chain.add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
        chain.set_dither_bits(8);
        chain.set_output_origin(OUTPUT_ORIGIN_TOP_LEFT);
@@ -300,7 +311,7 @@ void mixer_thread_func(QSurface *surface, QSurface *surface2, QSurface *surface3
        //chain.enable_phase_timing(true);
 
        // Set up stuff for NV12 conversion.
-       ResourcePool *resource_pool = chain.get_resource_pool();
+       resource_pool = chain.get_resource_pool();
        GLuint chroma_tex = resource_pool->create_2d_texture(GL_RG8, WIDTH, HEIGHT);
 
        // Cb/Cr shader.
@@ -416,11 +427,10 @@ void mixer_thread_func(QSurface *surface, QSurface *surface2, QSurface *surface3
                assert(got_frame);
 
                // Render chain.
-               {
-                       GLuint ycbcr_fbo = resource_pool->create_fbo(y_tex, chroma_tex);
-                       chain.render_to_fbo(ycbcr_fbo, WIDTH, HEIGHT);
-                       resource_pool->release_fbo(ycbcr_fbo);
-               }
+               GLuint rgba_tex = resource_pool->create_2d_texture(GL_RGBA8, WIDTH, HEIGHT);
+               GLuint ycbcr_fbo = resource_pool->create_fbo(y_tex, chroma_tex, rgba_tex);
+               chain.render_to_fbo(ycbcr_fbo, WIDTH, HEIGHT);
+               resource_pool->release_fbo(ycbcr_fbo);
 
                // Set up for extraction.
                float vertices[] = {
@@ -461,19 +471,35 @@ void mixer_thread_func(QSurface *surface, QSurface *surface2, QSurface *surface3
                glDrawArrays(GL_TRIANGLES, 0, 3);
                check_error();
 
+               RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
+               check_error();
+
                cleanup_vertex_attribute(cbcr_program_num, "position", position_vbo);
                cleanup_vertex_attribute(cbcr_program_num, "texcoord", texcoord_vbo);
 
                glUseProgram(0);
                check_error();
 
-               RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
-               check_error();
-
                resource_pool->release_fbo(cbcr_fbo);
 
                h264_encoder.end_frame(fence, input_frames_to_release);
 
+               // Store this frame for display. Remove the ready frame if any
+               // (it was seemingly never used).
+               {
+                       std::unique_lock<std::mutex> lock(display_frame_mutex);
+                       if (has_ready_display_frame) {
+                               resource_pool->release_2d_texture(ready_display_frame.texnum);
+                               ready_display_frame.ready_fence.reset();
+                       }
+                       ready_display_frame.texnum = rgba_tex;
+                       ready_display_frame.ready_fence = fence;
+                       has_ready_display_frame = true;
+               }
+
+               if (has_new_frame_ready_callback) {
+                       new_frame_ready_callback();
+               }
 
 #if 1
 #if _POSIX_C_SOURCE >= 199309L
@@ -508,6 +534,37 @@ void mixer_thread_func(QSurface *surface, QSurface *surface2, QSurface *surface3
        BMUSBCapture::stop_bm_thread();
 }
 
+bool mixer_get_display_frame(DisplayFrame *frame)
+{
+       std::unique_lock<std::mutex> lock(display_frame_mutex);
+       if (!has_current_display_frame && !has_ready_display_frame) {
+               return false;
+       }
+
+       if (has_current_display_frame && has_ready_display_frame) {
+               // We have a new ready frame. Toss the current one.
+               resource_pool->release_2d_texture(current_display_frame.texnum);
+               current_display_frame.ready_fence.reset();
+               has_current_display_frame = false;
+       }
+       if (has_ready_display_frame) {
+               assert(!has_current_display_frame);
+               current_display_frame = ready_display_frame;
+               ready_display_frame.ready_fence.reset();  // Drop the refcount.
+               has_current_display_frame = true;
+               has_ready_display_frame = false;
+       }
+
+       *frame = current_display_frame;
+       return true;
+}
+
+void set_frame_ready_fallback(new_frame_ready_callback_t callback)
+{
+       new_frame_ready_callback = callback;
+       has_new_frame_ready_callback = true;
+}
+
 std::thread mixer_thread;
 
 void start_mixer(QSurface *surface, QSurface *surface2, QSurface *surface3, QSurface *surface4)
diff --git a/mixer.h b/mixer.h
index bf04dee396b37a77ce8e2b967b7ddc4c8d17b387..8bd023d68bf1e94ede59a5f0909f3e528daccf28 100644 (file)
--- a/mixer.h
+++ b/mixer.h
@@ -1,3 +1,19 @@
+#ifndef _MIXER_H
+#define _MIXER_H 1
+
+#include "ref_counted_gl_sync.h"
+
 class QSurface;
 void start_mixer(QSurface *surface, QSurface *surface2, QSurface *surface3, QSurface *surface4);
 void mixer_quit();
+
+struct DisplayFrame {
+       GLuint texnum;
+       RefCountedGLsync ready_fence;  // Asserted when the texture is done rendering.
+};
+bool mixer_get_display_frame(DisplayFrame *frame);  // Implicitly frees the previous one if there's a new frame available.
+
+typedef std::function<void()> new_frame_ready_callback_t;
+void set_frame_ready_fallback(new_frame_ready_callback_t callback);
+
+#endif  // !defined(_MIXER_H)