C&C++   发布时间:2022-04-13  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Flutter Windows 渲染外部纹理大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

Flutter 支持通过在 native 侧注册一个本地纹理来将 RGBA8 格式的外部图像绘制到 TextureWidget 内。因此这个功能特别适合同离屏渲染技术结合来嵌入原本 native 侧才能渲染的内容,比如视频图像、游戏画面。

理论上这种方法会耗费大量的资源,因为经过了从 GPU(OpenGL) -> CPU(PixelBuffer) -> GPU(flutter) 的过程,在高分辨率、高帧率的情况下性能一定是不理想的。但是在本文写作的时间目前,Flutter Windows 暂时不支持共享 OpenGL context 以及 PlatformView,因此这是目前唯一的选择。

首先由插件在初始化时获取一个flutter::TextureRegistrar对象,并在新的帧到来后调用该对象上的@H_40_6@markTextureFrameAvailable方法,触发 flutter 重绘。Flutter 引擎在状态改变,或者由于前面的回调触发进行重绘时,会调用由 native 侧事先注册的回调函数以获取一个 RGBA8 格式的 pixel buffer. 但需要注意的是应当避免在回调中进行耗时的渲染操作,而是在后台线程准备好缓冲区内容后,在回调中回传缓冲区指针即可。

下面以渲染 mpv 播放器的视频帧到 flutter 控件内为例。首先实现一个单独的渲染线程,并在该线程中初始化好 opengl 环境。在离屏渲染中,我们需要创建一个隐藏的窗体,并准备好一个 Framebuffer Object(FBO)。在 mpv 绘制帧数据到 FBO 后,通过 glReadPixels获得对应的 RGBA8 缓冲。

注意在离屏渲染的时候,仍然要创建一个隐藏的窗口以获得 OpenGL Context,但是最好是使用 GLFW 而不是老旧的 freeglut,因为 freeglut 对窗口进行隐藏需要调用glutHideWindow,而这个 API 的调用需要等待进入 glut 事件循环才能生效,并且只有绑定了 render callBACk 才能进入事件循环。因此离屏渲染最好还是使用 GLFW 进行初始化,指定 hints 即可。

#pragma once
#pragma warning(disable : 4505)

#include <atomic>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>

#include "common/GL/glew.h"
#include "common/GL/glfw3.h"
#include "common/mpv_controller.h"
#include "common/semaphore.h"
#include "common/buffer.h"

static void* get_proc_address(void* ctx, @R_772_2600@ Name) {
  void* p = (void*)wglGetProcAddress(Name);
  if (p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
      (p == (void*)-1)) {
    HMODULE module = LoadLibraryA("opengl32.dll");
    p = (void*)GetProcAddress(module, Name);
  }

  return p;
}

static void glfw_error_callBACk(int error, @R_772_2600@ desc) {
  LOG(INFO) << desc;
}

using RenderCb = std::function<void(void)>;

class RenderThread {
  std::shared_ptr<Semaphore> render_trigger;
  std::atomic_bool quit{falsE};
  std::thread loop;

  // opengl entries
  GLFWwindow* osr_window = nullptr;
  GLuint fbo = 1;
  GLuint texture;
  GLuint depth_render_buffer;
  GLuint color_render_buffer;

  // callBACks
  RenderCb render_callBACk;

  /// Called by mpv to invoke a new call to render
  static void mpv_frame_callBACk(void* ctX) {
    if (!ctX) {
      return;
    }
    auto* render_thread = static_cast<RenderThread*>(ctX);
    render_thread->render_trigger->signal();
  }

 public:
  RenderThread();
  ~RenderThread();

  std::atomic_bool started = false;

  /// Start render loop
  void start_render(std::shared_ptr<BufferController> buffer_controller,
                    RenderCb _render_callBACk) {
    if (!_render_callBACk || !buffer_controller) return;

    this->render_callBACk = _render_callBACk;
    quit = false;
    loop = std::thread([=]() {
      LOG(INFO) << "Render thread started";

      if (!glfwInit()) {
        LOG(FATAL) << "Init glfw failed";
        return;
      }
      glfwSetErrorCallBACk(glfw_error_callBACk);
      glfwWindowHint(GLFW_VISIBLE, GLFW_falSE);
      osr_window = glfwCreateWindow(1, 1, "", nullptr, nullptr);
      if (!osr_window) {
        LOG(FATAL) << "Init glfw window failed";
        return;
      }
      glfwMakeContextCurrent(osr_window);
      LOG(INFO) << "Finish init glfw";

      // glew
      glewExperimental = TRUE;
      GLenum err = glewInit();
      if (err != GLEW_OK) {
        LOG(FATAL) << "GLEW init failed";
        return;
      }
      if (GLEW_EXT_framebuffer_object != GL_TRUE) {
        LOG(FATAL) << "FBO unavaliable";
        return;
      }

      // init mpv & gl
      auto* mpv = MpvController::instance()->mpv;
      LOG(INFO) << "Init mpv gl";
      mpv_opengl_init_params gl_init_params{get_proc_address, nullptr, nullptr};
      mpv_render_param params[]{
          {MPV_RENDER_PARAM_API_TYPE,
           const_cast<char*>(MPV_RENDER_API_TYPE_OPENGL)},
          {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
          {MPV_RENDER_PARAM_INVALID, nullptr}};

      mpv_render_context* mpv_ctx;
      if (mpv_render_context_create(&mpv_ctx, mpv, params) < 0) {
        LOG(FATAL) << "Create mpv ractx failed";
        throw std::runtime_error("failed to initialize mpv GL context");
      }
      LOG(INFO) << "Init mpv gl finished";

      mpv_render_context_set_update_callBACk(
          mpv_ctx, &RenderThread::mpv_frame_callBACk, static_cast<void*>(this));
      MpvController::instance()->mpv_ctx = mpv_ctx;

      // init framebuffer
      glGenFramebuffers(1, &fbo);
      glBindFramebuffer(GL_FRAMEBUFFER, fbo);

      GLuint texture;
      glGentextures(1, &texturE);
      glBindTexture(GL_TEXTURE_2D, texturE);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 800, 400, 0, GL_RGBA,
                   GL_UNSIGNED_BYTE, nullptr);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                             GL_TEXTURE_2D, texture, 0);

      err = glcheckFramebufferStatus(GL_FRAMEBUFFER);
      if (err != GL_FRAMEBUFFER_COMPLETE) {
        LOG(FATAL) << "FBO imcomplete";
        return;
      }

      // render loop
      started = true;
      LOG(INFO) << "Render loop started";
      while (true) {
        render_trigger->wait();
        if (quit) {
          break;
        }

        // perform render
        mpv_opengl_fbo mpfbo{static_cast<int>(fbo), 800, 400, 0};
        int flip_y = 0;

        mpv_render_param render_params[] = {
            {MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},
            {MPV_RENDER_PARAM_FLIP_Y, &flip_y},
            {MPV_RENDER_PARAM_INVALID, nullptr}};
        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT);
        mpv_render_context_render(MpvController::instance()->mpv_ctx,
                                  render_params);

        auto render_buffer = buffer_controller->get_render();
        render_buffer->reconfig(800, 400);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_READ_BUFFER, fbo);
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        glReadPixels(0, 0, 800, 400, GL_RGBA, GL_UNSIGNED_BYTE,
                     render_buffer->buffer);

        {
          GLenum glerr;
          while ((glerr = glGetError()) != GL_NO_ERROR) {
            LOG(DEBUG) << "GL error:" << glerr;
          }
        }
        buffer_controller->release_render(render_buffer);
        LOG(INFO) << "New mpv frame rendered";
        render_callBACk();
      }
      LOG(INFO) << "Render loop end";
      started = false;
      quit = false;
      glfwTerminate();
    });
  }
};

RenderThread::renderThread() {
  render_trigger = std::make_shared<Semaphore>(0);
}

RenderThread::~RenderThread() {
  // quit render thread
  quit = true;
  render_trigger->signal();
  loop.join();
}

为了解决 mpv 渲染(生产者)和 flutter 渲染(消费者)两个线程的异步问题,我们需要随手实现一个多缓冲 buffer。两条额外的蓝色线分别对应生产者 overflow 和 underflow 的情况。

Flutter Windows 渲染外部纹理

#include "buffer.h"
#include "easylogging++.h"

BufferController::BufferController(int buffer_count) {
  auto count = buffer_count < 3 ? 3 : buffer_count;
  for (int i = 0; i < count; ++i) {
    dirty_queue.emplace_BACk(
        std::make_shared<MpvRenderBuffer>());  // not a valid buffer currently
  }
}

BufferController::~BufferController() {}

SharedBuffer BufferController::get_render() {
  std::lock_guard lock(mu);
  auto& targeT_Buffer = (!dirty_queue.empty()) ? dirty_queue : ready_queue;
  if (targeT_Buffer.empty()) {
    return nullptr;
  }

  auto render_target = targeT_Buffer.front();
  targeT_Buffer.pop_front();
  return render_target;
}

void BufferController::release_render(SharedBuffer& buffer) {
  std::lock_guard lock(mu);
  ready_queue.push_BACk(buffer);
}

SharedBuffer BufferController::get_use() {
  std::lock_guard lock(mu);
  if (!ready_queue.empty()) {
    auto use_target = ready_queue.front();
    ready_queue.pop_front();
    return use_target;
  } else if (!dirty_queue.empty()) {
    // reused last buffer
    auto use_target = dirty_queue.BACk();
    dirty_queue.pop_BACk();
    return use_target;
  }
  return nullptr;
}

void BufferController::release_use(SharedBuffer& buffer) {
  std::lock_guard lock(mu);
  dirty_queue.push_BACk(buffer);
}

大佬总结

以上是大佬教程为你收集整理的Flutter Windows 渲染外部纹理全部内容,希望文章能够帮你解决Flutter Windows 渲染外部纹理所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: