[feat] add OpenGL NV12 underlay rendering for Slint on Linux

This commit is contained in:
dijunkun
2026-08-25 10:26:05 +08:00
parent 3e63675787
commit 21eec76008
11 changed files with 315 additions and 188 deletions
+53 -42
View File
@@ -36,7 +36,6 @@
#include <GL/gl.h> #include <GL/gl.h>
#include "platform/tray/win_tray.h" #include "platform/tray/win_tray.h"
#include "platform/windows_opengl_video_renderer.h"
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include "platform/metal_video_renderer.h" #include "platform/metal_video_renderer.h"
#include "platform/tray/mac_tray.h" #include "platform/tray/mac_tray.h"
@@ -49,6 +48,9 @@
#include "platform/tray/linux_tray.h" #include "platform/tray/linux_tray.h"
#endif #endif
#if defined(_WIN32) || defined(__linux__)
#include "platform/opengl_video_renderer.h"
#endif
#include "rd_log.h" #include "rd_log.h"
#include "server_window_state.h" #include "server_window_state.h"
#include "ui/ui_localization.h" #include "ui/ui_localization.h"
@@ -62,6 +64,7 @@ using namespace std::chrono_literals;
#if !defined(__APPLE__) #if !defined(__APPLE__)
constexpr GLint kGlClampToEdge = 0x812F; constexpr GLint kGlClampToEdge = 0x812F;
constexpr float kStreamWindowCornerRadius = 12.0f;
#else #else
constexpr int kMetalAttachmentAttemptLimit = 30; constexpr int kMetalAttachmentAttemptLimit = 30;
#endif #endif
@@ -1114,9 +1117,8 @@ void GuiApplication::InitializeModules() {
if (modules_inited_) { if (modules_inited_) {
return; return;
} }
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
windows_opengl_video_renderer_ = opengl_video_renderer_ = std::make_unique<OpenGlVideoRenderer>();
std::make_unique<WindowsOpenGlVideoRenderer>();
#elif defined(__APPLE__) #elif defined(__APPLE__)
// Metal setup can log device, shader, or pipeline failures. Initialize it // Metal setup can log device, shader, or pipeline failures. Initialize it
// here, after Run() has selected the application's log directory, rather // here, after Run() has selected the application's log directory, rather
@@ -2351,9 +2353,9 @@ void GuiApplication::SyncStreamWindow() {
} }
if (!has_sessions) { if (!has_sessions) {
SetStreamKeyboardFocus(false); SetStreamKeyboardFocus(false);
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_) { if (opengl_video_renderer_) {
windows_opengl_video_renderer_->SetSelectedStream({}); opengl_video_renderer_->SetSelectedStream({});
} }
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
@@ -2643,10 +2645,10 @@ void GuiApplication::SyncStreamVideoFrame() {
} }
#endif #endif
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_ && if (opengl_video_renderer_ &&
windows_opengl_video_renderer_->IsReady()) { opengl_video_renderer_->IsReady()) {
windows_opengl_video_renderer_->SetSelectedStream(props->remote_id_); opengl_video_renderer_->SetSelectedStream(props->remote_id_);
SubmitCachedFrameToOpenGl(props); SubmitCachedFrameToOpenGl(props);
if (!(*ui_->stream)->get_native_video_enabled()) { if (!(*ui_->stream)->get_native_video_enabled()) {
(*ui_->stream)->set_native_video_enabled(true); (*ui_->stream)->set_native_video_enabled(true);
@@ -2854,10 +2856,10 @@ void GuiApplication::ConfigureStreamVideoRenderer() {
glBindTexture(GL_TEXTURE_2D, glBindTexture(GL_TEXTURE_2D,
static_cast<GLuint>(previous_texture)); static_cast<GLuint>(previous_texture));
ui_->video_gl_texture = texture; ui_->video_gl_texture = texture;
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_ && if (opengl_video_renderer_ &&
windows_opengl_video_renderer_->Setup()) { opengl_video_renderer_->Setup()) {
windows_opengl_video_renderer_->SetSelectedStream( opengl_video_renderer_->SetSelectedStream(
focused_remote_id_); focused_remote_id_);
video_frame_dirty_.store(true, std::memory_order_release); video_frame_dirty_.store(true, std::memory_order_release);
} }
@@ -2865,10 +2867,10 @@ void GuiApplication::ConfigureStreamVideoRenderer() {
return; return;
} }
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (state == slint::RenderingState::BeforeRendering && if (state == slint::RenderingState::BeforeRendering &&
windows_opengl_video_renderer_ && opengl_video_renderer_ &&
windows_opengl_video_renderer_->IsReady() && ui_->stream && opengl_video_renderer_->IsReady() && ui_->stream &&
(*ui_->stream)->get_native_video_enabled()) { (*ui_->stream)->get_native_video_enabled()) {
const auto window_size = (*ui_->stream)->window().size(); const auto window_size = (*ui_->stream)->window().size();
const float scale_factor = const float scale_factor =
@@ -2877,9 +2879,18 @@ void GuiApplication::ConfigureStreamVideoRenderer() {
!fullscreen_button_pressed_ && ui_->tab_ids.size() > 1 !fullscreen_button_pressed_ && ui_->tab_ids.size() > 1
? static_cast<int>(std::lround(30.0f * scale_factor)) ? static_cast<int>(std::lround(30.0f * scale_factor))
: 0; : 0;
windows_opengl_video_renderer_->RenderLatest( const bool rounded_window =
(*ui_->stream)->get_custom_titlebar() &&
!fullscreen_button_pressed_ &&
!(*ui_->stream)->window().is_maximized();
const int corner_radius =
rounded_window
? static_cast<int>(std::lround(
kStreamWindowCornerRadius * scale_factor))
: 0;
opengl_video_renderer_->RenderLatest(
focused_remote_id_, static_cast<int>(window_size.width), focused_remote_id_, static_cast<int>(window_size.width),
static_cast<int>(window_size.height), top_inset); static_cast<int>(window_size.height), top_inset, corner_radius);
return; return;
} }
#endif #endif
@@ -2914,9 +2925,9 @@ void GuiApplication::ConfigureStreamVideoRenderer() {
} }
if (state == slint::RenderingState::RenderingTeardown) { if (state == slint::RenderingState::RenderingTeardown) {
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_) { if (opengl_video_renderer_) {
windows_opengl_video_renderer_->Teardown(); opengl_video_renderer_->Teardown();
} }
#endif #endif
if (ui_->video_gl_texture != 0) { if (ui_->video_gl_texture != 0) {
@@ -2933,19 +2944,19 @@ void GuiApplication::ConfigureStreamVideoRenderer() {
}); });
if (error.has_value()) { if (error.has_value()) {
LOG_WARN("Slint OpenGL video renderer unavailable, using pixel buffers"); LOG_WARN("Slint OpenGL video renderer unavailable, using pixel buffers");
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
} else if (windows_opengl_video_renderer_) { } else if (opengl_video_renderer_) {
windows_opengl_video_renderer_->SetSelectedStream(focused_remote_id_); opengl_video_renderer_->SetSelectedStream(focused_remote_id_);
#endif #endif
} }
#endif #endif
} }
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
void GuiApplication::SubmitCachedFrameToOpenGl( void GuiApplication::SubmitCachedFrameToOpenGl(
const std::shared_ptr<RemoteSession>& session) { const std::shared_ptr<RemoteSession>& session) {
if (!session || !windows_opengl_video_renderer_ || if (!session || !opengl_video_renderer_ ||
!windows_opengl_video_renderer_->IsReady() || !opengl_video_renderer_->IsReady() ||
session->connection_status_.load() != ConnectionStatus::Connected) { session->connection_status_.load() != ConnectionStatus::Connected) {
return; return;
} }
@@ -2968,10 +2979,10 @@ void GuiApplication::SubmitCachedFrameToOpenGl(
} }
if (cached_frame && cached_width > 0 && cached_height > 0 && if (cached_frame && cached_width > 0 && cached_height > 0 &&
windows_opengl_video_renderer_->SubmitCachedNv12( opengl_video_renderer_->SubmitCachedNv12(
session->remote_id_, cached_frame->data(), cached_frame->size(), session->remote_id_, cached_frame->data(), cached_frame->size(),
cached_width, cached_height) == cached_width, cached_height) ==
WindowsOpenGlVideoRenderer::SubmitResult::submitted) { OpenGlVideoRenderer::SubmitResult::submitted) {
video_frame_dirty_.store(true, std::memory_order_release); video_frame_dirty_.store(true, std::memory_order_release);
} }
} }
@@ -3321,7 +3332,7 @@ void GuiApplication::SelectStreamTab(int index) {
return; return;
} }
const std::string selected_remote_id = ui_->tab_ids[index]; const std::string selected_remote_id = ui_->tab_ids[index];
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
const bool selection_changed = focused_remote_id_ != selected_remote_id; const bool selection_changed = focused_remote_id_ != selected_remote_id;
bool renderer_selection_changed = false; bool renderer_selection_changed = false;
#endif #endif
@@ -3331,10 +3342,10 @@ void GuiApplication::SelectStreamTab(int index) {
} }
focused_remote_id_ = selected_remote_id; focused_remote_id_ = selected_remote_id;
controlled_remote_id_ = selected_remote_id; controlled_remote_id_ = selected_remote_id;
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_) { if (opengl_video_renderer_) {
renderer_selection_changed = renderer_selection_changed =
windows_opengl_video_renderer_->SetSelectedStream(selected_remote_id); opengl_video_renderer_->SetSelectedStream(selected_remote_id);
} }
if (renderer_selection_changed) { if (renderer_selection_changed) {
video_frame_dirty_.store(true, std::memory_order_release); video_frame_dirty_.store(true, std::memory_order_release);
@@ -3371,7 +3382,7 @@ void GuiApplication::SelectStreamTab(int index) {
selected_session->connection_status_.load() == selected_session->connection_status_.load() ==
ConnectionStatus::Connected; ConnectionStatus::Connected;
} }
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (renderer_selection_changed) { if (renderer_selection_changed) {
SubmitCachedFrameToOpenGl(selected_session); SubmitCachedFrameToOpenGl(selected_session);
} }
@@ -3427,9 +3438,9 @@ void GuiApplication::CloseStreamTab(const std::string& remote_id) {
if (focused_remote_id_ == remote_id) { if (focused_remote_id_ == remote_id) {
focused_remote_id_.clear(); focused_remote_id_.clear();
controlled_remote_id_.clear(); controlled_remote_id_.clear();
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_ && if (opengl_video_renderer_ &&
windows_opengl_video_renderer_->SetSelectedStream({})) { opengl_video_renderer_->SetSelectedStream({})) {
video_frame_dirty_.store(true, std::memory_order_release); video_frame_dirty_.store(true, std::memory_order_release);
} }
#elif defined(__APPLE__) #elif defined(__APPLE__)
@@ -3631,9 +3642,9 @@ void GuiApplication::Cleanup() {
WaitForThumbnailSaveTasks(); WaitForThumbnailSaveTasks();
devices_.DestroyAudioOutput(); devices_.DestroyAudioOutput();
if (ui_->stream) { if (ui_->stream) {
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
if (windows_opengl_video_renderer_) { if (opengl_video_renderer_) {
windows_opengl_video_renderer_->SetSelectedStream({}); opengl_video_renderer_->SetSelectedStream({});
} }
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
+1 -1
View File
@@ -41,7 +41,7 @@ private:
void SyncStreamVideoFrame(); void SyncStreamVideoFrame();
void ScheduleNextVideoFrame(); void ScheduleNextVideoFrame();
void ConfigureStreamVideoRenderer(); void ConfigureStreamVideoRenderer();
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
void SubmitCachedFrameToOpenGl( void SubmitCachedFrameToOpenGl(
const std::shared_ptr<RemoteSession>& session); const std::shared_ptr<RemoteSession>& session);
#endif #endif
@@ -1,13 +1,20 @@
#include "platform/windows_opengl_video_renderer.h" #include "platform/opengl_video_renderer.h"
#if !defined(_WIN32) #if !defined(_WIN32) && !defined(__linux__)
#error "WindowsOpenGlVideoRenderer is only available on Windows" #error "OpenGlVideoRenderer is only available on Windows and Linux"
#endif #endif
#if defined(_WIN32)
#include <windows.h> #include <windows.h>
#elif defined(__linux__)
#include <dlfcn.h>
#endif
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glext.h> #include <GL/glext.h>
#if defined(__linux__)
#include <GL/glx.h>
#endif
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -49,6 +56,7 @@ struct SharedFrameState {
}; };
template <typename T> bool LoadOpenGlFunction(T *function, const char *name) { template <typename T> bool LoadOpenGlFunction(T *function, const char *name) {
#if defined(_WIN32)
PROC address = wglGetProcAddress(name); PROC address = wglGetProcAddress(name);
if (address == nullptr || address == reinterpret_cast<PROC>(1) || if (address == nullptr || address == reinterpret_cast<PROC>(1) ||
address == reinterpret_cast<PROC>(2) || address == reinterpret_cast<PROC>(2) ||
@@ -58,6 +66,14 @@ template <typename T> bool LoadOpenGlFunction(T *function, const char *name) {
address = module ? GetProcAddress(module, name) : nullptr; address = module ? GetProcAddress(module, name) : nullptr;
} }
*function = reinterpret_cast<T>(address); *function = reinterpret_cast<T>(address);
#elif defined(__linux__)
void *address = dlsym(RTLD_DEFAULT, name);
if (address == nullptr) {
address = reinterpret_cast<void *>(
glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(name)));
}
*function = reinterpret_cast<T>(address);
#endif
return *function != nullptr; return *function != nullptr;
} }
@@ -85,7 +101,10 @@ struct OpenGlFunctions {
PFNGLGETVERTEXATTRIBPOINTERVPROC get_vertex_attrib_pointer_v = nullptr; PFNGLGETVERTEXATTRIBPOINTERVPROC get_vertex_attrib_pointer_v = nullptr;
PFNGLLINKPROGRAMPROC link_program = nullptr; PFNGLLINKPROGRAMPROC link_program = nullptr;
PFNGLSHADERSOURCEPROC shader_source = nullptr; PFNGLSHADERSOURCEPROC shader_source = nullptr;
PFNGLUNIFORM1FPROC uniform_1f = nullptr;
PFNGLUNIFORM1IPROC uniform_1i = nullptr; PFNGLUNIFORM1IPROC uniform_1i = nullptr;
PFNGLUNIFORM2FPROC uniform_2f = nullptr;
PFNGLUNIFORM4FPROC uniform_4f = nullptr;
PFNGLUSEPROGRAMPROC use_program = nullptr; PFNGLUSEPROGRAMPROC use_program = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC vertex_attrib_pointer = nullptr; PFNGLVERTEXATTRIBPOINTERPROC vertex_attrib_pointer = nullptr;
@@ -116,7 +135,10 @@ struct OpenGlFunctions {
"glGetVertexAttribPointerv") && "glGetVertexAttribPointerv") &&
LoadOpenGlFunction(&link_program, "glLinkProgram") && LoadOpenGlFunction(&link_program, "glLinkProgram") &&
LoadOpenGlFunction(&shader_source, "glShaderSource") && LoadOpenGlFunction(&shader_source, "glShaderSource") &&
LoadOpenGlFunction(&uniform_1f, "glUniform1f") &&
LoadOpenGlFunction(&uniform_1i, "glUniform1i") && LoadOpenGlFunction(&uniform_1i, "glUniform1i") &&
LoadOpenGlFunction(&uniform_2f, "glUniform2f") &&
LoadOpenGlFunction(&uniform_4f, "glUniform4f") &&
LoadOpenGlFunction(&use_program, "glUseProgram") && LoadOpenGlFunction(&use_program, "glUseProgram") &&
LoadOpenGlFunction(&vertex_attrib_pointer, "glVertexAttribPointer"); LoadOpenGlFunction(&vertex_attrib_pointer, "glVertexAttribPointer");
} }
@@ -205,7 +227,7 @@ void RestoreVertexAttrib(const OpenGlFunctions &gl, GLuint index,
} // namespace } // namespace
struct WindowsOpenGlVideoRenderer::Impl { struct OpenGlVideoRenderer::Impl {
OpenGlFunctions gl; OpenGlFunctions gl;
std::shared_ptr<SharedFrameState> frames = std::shared_ptr<SharedFrameState> frames =
std::make_shared<SharedFrameState>(); std::make_shared<SharedFrameState>();
@@ -218,6 +240,10 @@ struct WindowsOpenGlVideoRenderer::Impl {
GLint texcoord_location = -1; GLint texcoord_location = -1;
GLint y_texture_location = -1; GLint y_texture_location = -1;
GLint uv_texture_location = -1; GLint uv_texture_location = -1;
GLint target_size_location = -1;
GLint video_rect_location = -1;
GLint corner_radius_location = -1;
GLint video_enabled_location = -1;
int texture_width = 0; int texture_width = 0;
int texture_height = 0; int texture_height = 0;
std::string uploaded_stream; std::string uploaded_stream;
@@ -232,6 +258,10 @@ struct WindowsOpenGlVideoRenderer::Impl {
texcoord_location = -1; texcoord_location = -1;
y_texture_location = -1; y_texture_location = -1;
uv_texture_location = -1; uv_texture_location = -1;
target_size_location = -1;
video_rect_location = -1;
corner_radius_location = -1;
video_enabled_location = -1;
texture_width = 0; texture_width = 0;
texture_height = 0; texture_height = 0;
uploaded_stream.clear(); uploaded_stream.clear();
@@ -239,12 +269,12 @@ struct WindowsOpenGlVideoRenderer::Impl {
} }
}; };
WindowsOpenGlVideoRenderer::WindowsOpenGlVideoRenderer() OpenGlVideoRenderer::OpenGlVideoRenderer()
: impl_(std::make_unique<Impl>()) {} : impl_(std::make_unique<Impl>()) {}
WindowsOpenGlVideoRenderer::~WindowsOpenGlVideoRenderer() = default; OpenGlVideoRenderer::~OpenGlVideoRenderer() = default;
bool WindowsOpenGlVideoRenderer::Setup() { bool OpenGlVideoRenderer::Setup() {
if (IsReady()) { if (IsReady()) {
return true; return true;
} }
@@ -274,15 +304,41 @@ precision mediump float;
#endif #endif
uniform sampler2D y_texture; uniform sampler2D y_texture;
uniform sampler2D uv_texture; uniform sampler2D uv_texture;
uniform vec2 target_size;
uniform vec4 video_rect;
uniform float corner_radius;
uniform float video_enabled;
varying vec2 video_texcoord; varying vec2 video_texcoord;
void main() { void main() {
vec2 surface_point = video_texcoord * target_size;
vec3 rgb = vec3(0.0);
if (video_enabled > 0.5 &&
surface_point.x >= video_rect.x &&
surface_point.x <= video_rect.x + video_rect.z &&
surface_point.y >= video_rect.y &&
surface_point.y <= video_rect.y + video_rect.w) {
vec2 frame_texcoord =
(surface_point - video_rect.xy) / video_rect.zw;
float y = 1.16438356 * float y = 1.16438356 *
(texture2D(y_texture, video_texcoord).r - 16.0 / 255.0); (texture2D(y_texture, frame_texcoord).r - 16.0 / 255.0);
vec2 uv = texture2D(uv_texture, video_texcoord).ra - vec2(0.5, 0.5); vec2 uv =
vec3 rgb = vec3(y + 1.59602678 * uv.y, texture2D(uv_texture, frame_texcoord).ra - vec2(0.5, 0.5);
rgb = clamp(vec3(y + 1.59602678 * uv.y,
y - 0.39176229 * uv.x - 0.81296764 * uv.y, y - 0.39176229 * uv.x - 0.81296764 * uv.y,
y + 2.01723214 * uv.x); y + 2.01723214 * uv.x),
gl_FragColor = vec4(clamp(rgb, 0.0, 1.0), 1.0); 0.0, 1.0);
}
float coverage = 1.0;
if (corner_radius > 0.0) {
vec2 half_size = target_size * 0.5;
vec2 corner = abs(surface_point - half_size) -
(half_size - vec2(corner_radius));
float distance_to_edge =
length(max(corner, vec2(0.0))) +
min(max(corner.x, corner.y), 0.0) - corner_radius;
coverage = clamp(0.5 - distance_to_edge, 0.0, 1.0);
}
gl_FragColor = vec4(rgb * coverage, coverage);
} }
)glsl"; )glsl";
static constexpr char kDesktopVertexShader[] = R"glsl( static constexpr char kDesktopVertexShader[] = R"glsl(
@@ -299,15 +355,41 @@ void main() {
#version 110 #version 110
uniform sampler2D y_texture; uniform sampler2D y_texture;
uniform sampler2D uv_texture; uniform sampler2D uv_texture;
uniform vec2 target_size;
uniform vec4 video_rect;
uniform float corner_radius;
uniform float video_enabled;
varying vec2 video_texcoord; varying vec2 video_texcoord;
void main() { void main() {
vec2 surface_point = video_texcoord * target_size;
vec3 rgb = vec3(0.0);
if (video_enabled > 0.5 &&
surface_point.x >= video_rect.x &&
surface_point.x <= video_rect.x + video_rect.z &&
surface_point.y >= video_rect.y &&
surface_point.y <= video_rect.y + video_rect.w) {
vec2 frame_texcoord =
(surface_point - video_rect.xy) / video_rect.zw;
float y = 1.16438356 * float y = 1.16438356 *
(texture2D(y_texture, video_texcoord).r - 16.0 / 255.0); (texture2D(y_texture, frame_texcoord).r - 16.0 / 255.0);
vec2 uv = texture2D(uv_texture, video_texcoord).ra - vec2(0.5, 0.5); vec2 uv =
vec3 rgb = vec3(y + 1.59602678 * uv.y, texture2D(uv_texture, frame_texcoord).ra - vec2(0.5, 0.5);
rgb = clamp(vec3(y + 1.59602678 * uv.y,
y - 0.39176229 * uv.x - 0.81296764 * uv.y, y - 0.39176229 * uv.x - 0.81296764 * uv.y,
y + 2.01723214 * uv.x); y + 2.01723214 * uv.x),
gl_FragColor = vec4(clamp(rgb, 0.0, 1.0), 1.0); 0.0, 1.0);
}
float coverage = 1.0;
if (corner_radius > 0.0) {
vec2 half_size = target_size * 0.5;
vec2 corner = abs(surface_point - half_size) -
(half_size - vec2(corner_radius));
float distance_to_edge =
length(max(corner, vec2(0.0))) +
min(max(corner.x, corner.y), 0.0) - corner_radius;
coverage = clamp(0.5 - distance_to_edge, 0.0, 1.0);
}
gl_FragColor = vec4(rgb * coverage, coverage);
} }
)glsl"; )glsl";
@@ -363,8 +445,19 @@ void main() {
impl_->gl.get_uniform_location(impl_->program, "y_texture"); impl_->gl.get_uniform_location(impl_->program, "y_texture");
impl_->uv_texture_location = impl_->uv_texture_location =
impl_->gl.get_uniform_location(impl_->program, "uv_texture"); impl_->gl.get_uniform_location(impl_->program, "uv_texture");
impl_->target_size_location =
impl_->gl.get_uniform_location(impl_->program, "target_size");
impl_->video_rect_location =
impl_->gl.get_uniform_location(impl_->program, "video_rect");
impl_->corner_radius_location =
impl_->gl.get_uniform_location(impl_->program, "corner_radius");
impl_->video_enabled_location =
impl_->gl.get_uniform_location(impl_->program, "video_enabled");
if (impl_->position_location < 0 || impl_->texcoord_location < 0 || if (impl_->position_location < 0 || impl_->texcoord_location < 0 ||
impl_->y_texture_location < 0 || impl_->uv_texture_location < 0) { impl_->y_texture_location < 0 || impl_->uv_texture_location < 0 ||
impl_->target_size_location < 0 || impl_->video_rect_location < 0 ||
impl_->corner_radius_location < 0 ||
impl_->video_enabled_location < 0) {
LOG_ERROR("OpenGL NV12 program is missing required shader bindings"); LOG_ERROR("OpenGL NV12 program is missing required shader bindings");
impl_->gl.delete_program(impl_->program); impl_->gl.delete_program(impl_->program);
impl_->ResetGlHandles(); impl_->ResetGlHandles();
@@ -429,7 +522,7 @@ void main() {
return true; return true;
} }
void WindowsOpenGlVideoRenderer::Teardown() { void OpenGlVideoRenderer::Teardown() {
impl_->ready.store(false, std::memory_order_release); impl_->ready.store(false, std::memory_order_release);
if (impl_->y_texture != 0) if (impl_->y_texture != 0)
glDeleteTextures(1, &impl_->y_texture); glDeleteTextures(1, &impl_->y_texture);
@@ -444,11 +537,11 @@ void WindowsOpenGlVideoRenderer::Teardown() {
impl_->ResetGlHandles(); impl_->ResetGlHandles();
} }
bool WindowsOpenGlVideoRenderer::IsReady() const { bool OpenGlVideoRenderer::IsReady() const {
return impl_->ready.load(std::memory_order_acquire); return impl_->ready.load(std::memory_order_acquire);
} }
bool WindowsOpenGlVideoRenderer::SetSelectedStream(std::string remote_id) { bool OpenGlVideoRenderer::SetSelectedStream(std::string remote_id) {
std::lock_guard lock(impl_->frames->mutex); std::lock_guard lock(impl_->frames->mutex);
if (impl_->frames->selected_stream == remote_id) { if (impl_->frames->selected_stream == remote_id) {
return false; return false;
@@ -463,7 +556,7 @@ bool WindowsOpenGlVideoRenderer::SetSelectedStream(std::string remote_id) {
return true; return true;
} }
void WindowsOpenGlVideoRenderer::DiscardStream(std::string_view remote_id) { void OpenGlVideoRenderer::DiscardStream(std::string_view remote_id) {
std::lock_guard lock(impl_->frames->mutex); std::lock_guard lock(impl_->frames->mutex);
for (auto &slot : impl_->frames->slots) { for (auto &slot : impl_->frames->slots) {
if (slot.remote_id == remote_id && slot.use != SlotUse::uploading) { if (slot.remote_id == remote_id && slot.use != SlotUse::uploading) {
@@ -477,22 +570,22 @@ void WindowsOpenGlVideoRenderer::DiscardStream(std::string_view remote_id) {
} }
} }
WindowsOpenGlVideoRenderer::SubmitResult OpenGlVideoRenderer::SubmitResult
WindowsOpenGlVideoRenderer::SubmitNv12(std::string_view remote_id, OpenGlVideoRenderer::SubmitNv12(std::string_view remote_id,
const uint8_t *data, size_t size, const uint8_t *data, size_t size,
int width, int height) { int width, int height) {
return SubmitNv12Internal(remote_id, data, size, width, height, true); return SubmitNv12Internal(remote_id, data, size, width, height, true);
} }
WindowsOpenGlVideoRenderer::SubmitResult OpenGlVideoRenderer::SubmitResult
WindowsOpenGlVideoRenderer::SubmitCachedNv12(std::string_view remote_id, OpenGlVideoRenderer::SubmitCachedNv12(std::string_view remote_id,
const uint8_t *data, size_t size, const uint8_t *data, size_t size,
int width, int height) { int width, int height) {
return SubmitNv12Internal(remote_id, data, size, width, height, false); return SubmitNv12Internal(remote_id, data, size, width, height, false);
} }
WindowsOpenGlVideoRenderer::SubmitResult OpenGlVideoRenderer::SubmitResult
WindowsOpenGlVideoRenderer::SubmitNv12Internal(std::string_view remote_id, OpenGlVideoRenderer::SubmitNv12Internal(std::string_view remote_id,
const uint8_t *data, size_t size, const uint8_t *data, size_t size,
int width, int height, int width, int height,
bool replace_pending) { bool replace_pending) {
@@ -562,10 +655,11 @@ WindowsOpenGlVideoRenderer::SubmitNv12Internal(std::string_view remote_id,
return SubmitResult::submitted; return SubmitResult::submitted;
} }
WindowsOpenGlVideoRenderer::RenderOutcome OpenGlVideoRenderer::RenderOutcome
WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id, OpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
int target_width, int target_height, int target_width, int target_height,
int top_inset_pixels) { int top_inset_pixels,
int corner_radius_pixels) {
if (!IsReady() || target_width <= 0 || target_height <= 0) { if (!IsReady() || target_width <= 0 || target_height <= 0) {
return {RenderResult::failed}; return {RenderResult::failed};
} }
@@ -643,7 +737,10 @@ WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glViewport(0, 0, target_width, target_height); glViewport(0, 0, target_width, target_height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // The Slint items above this underlay are transparent around the custom
// window corners. Keep those pixels transparent so the compositor can show
// the desktop instead of an opaque black framebuffer clear.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
bool upload_succeeded = true; bool upload_succeeded = true;
@@ -696,34 +793,37 @@ WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
} }
} }
RenderOutcome outcome{RenderResult::empty}; const int content_y = std::clamp(top_inset_pixels, 0, target_height);
if (upload_succeeded && impl_->uploaded_stream == remote_id && const int content_height = target_height - content_y;
impl_->texture_width > 0 && impl_->texture_height > 0) { const bool has_video =
upload_succeeded && impl_->uploaded_stream == remote_id &&
impl_->texture_width > 0 && impl_->texture_height > 0 &&
content_height > 0;
int video_x = 0;
int video_y = content_y;
int video_width = target_width;
int video_height = content_height;
if (has_video) {
source_width = impl_->texture_width; source_width = impl_->texture_width;
source_height = impl_->texture_height; source_height = impl_->texture_height;
sequence = impl_->uploaded_sequence; sequence = impl_->uploaded_sequence;
const int video_height = std::max(
0, target_height - std::clamp(top_inset_pixels, 0, target_height));
if (video_height > 0) {
const double source_aspect = const double source_aspect =
static_cast<double>(source_width) / source_height; static_cast<double>(source_width) / source_height;
const double target_aspect = const double target_aspect =
static_cast<double>(target_width) / video_height; static_cast<double>(target_width) / content_height;
int viewport_width = target_width;
int viewport_height = video_height;
int viewport_x = 0;
int viewport_y = 0;
if (source_aspect > target_aspect) { if (source_aspect > target_aspect) {
viewport_height = video_height =
std::max(1, static_cast<int>(target_width / source_aspect + 0.5)); std::max(1, static_cast<int>(target_width / source_aspect + 0.5));
viewport_y = (video_height - viewport_height) / 2; video_y = content_y + (content_height - video_height) / 2;
} else { } else {
viewport_width = video_width =
std::max(1, static_cast<int>(video_height * source_aspect + 0.5)); std::max(1, static_cast<int>(content_height * source_aspect + 0.5));
viewport_x = (target_width - viewport_width) / 2; video_x = (target_width - video_width) / 2;
}
} }
glViewport(viewport_x, viewport_y, viewport_width, viewport_height); while (glGetError() != GL_NO_ERROR) {
}
impl_->gl.use_program(impl_->program); impl_->gl.use_program(impl_->program);
impl_->gl.active_texture(GL_TEXTURE0); impl_->gl.active_texture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, impl_->y_texture); glBindTexture(GL_TEXTURE_2D, impl_->y_texture);
@@ -731,6 +831,18 @@ WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
impl_->gl.active_texture(GL_TEXTURE1); impl_->gl.active_texture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, impl_->uv_texture); glBindTexture(GL_TEXTURE_2D, impl_->uv_texture);
impl_->gl.uniform_1i(impl_->uv_texture_location, 1); impl_->gl.uniform_1i(impl_->uv_texture_location, 1);
impl_->gl.uniform_2f(impl_->target_size_location,
static_cast<GLfloat>(target_width),
static_cast<GLfloat>(target_height));
impl_->gl.uniform_4f(
impl_->video_rect_location, static_cast<GLfloat>(video_x),
static_cast<GLfloat>(video_y), static_cast<GLfloat>(video_width),
static_cast<GLfloat>(video_height));
impl_->gl.uniform_1f(
impl_->corner_radius_location,
static_cast<GLfloat>(std::clamp(
corner_radius_pixels, 0, std::min(target_width, target_height) / 2)));
impl_->gl.uniform_1f(impl_->video_enabled_location, has_video ? 1.0f : 0.0f);
impl_->gl.bind_buffer(GL_ARRAY_BUFFER, impl_->vertex_buffer); impl_->gl.bind_buffer(GL_ARRAY_BUFFER, impl_->vertex_buffer);
impl_->gl.vertex_attrib_pointer( impl_->gl.vertex_attrib_pointer(
static_cast<GLuint>(impl_->position_location), 2, GL_FLOAT, GL_FALSE, static_cast<GLuint>(impl_->position_location), 2, GL_FLOAT, GL_FALSE,
@@ -744,11 +856,13 @@ WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
impl_->gl.enable_vertex_attrib_array( impl_->gl.enable_vertex_attrib_array(
static_cast<GLuint>(impl_->texcoord_location)); static_cast<GLuint>(impl_->texcoord_location));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
outcome = {glGetError() == GL_NO_ERROR ? RenderResult::rendered const GLenum draw_error = glGetError();
: RenderResult::failed, RenderOutcome outcome{
source_width, source_height, sequence}; !upload_succeeded || draw_error != GL_NO_ERROR
} ? RenderResult::failed
} : has_video ? RenderResult::rendered : RenderResult::empty,
has_video ? source_width : 0, has_video ? source_height : 0,
has_video ? sequence : 0};
RestoreVertexAttrib(impl_->gl, static_cast<GLuint>(impl_->position_location), RestoreVertexAttrib(impl_->gl, static_cast<GLuint>(impl_->position_location),
position_state); position_state);
@@ -786,7 +900,7 @@ WindowsOpenGlVideoRenderer::RenderLatest(std::string_view remote_id,
return outcome; return outcome;
} }
bool WindowsOpenGlVideoRenderer::CopyLatestNv12( bool OpenGlVideoRenderer::CopyLatestNv12(
std::string_view remote_id, std::vector<unsigned char> *output, int *width, std::string_view remote_id, std::vector<unsigned char> *output, int *width,
int *height) const { int *height) const {
if (!output || !width || !height) { if (!output || !width || !height) {
@@ -1,5 +1,5 @@
#ifndef CROSSDESK_GUI_PLATFORM_WINDOWS_OPENGL_VIDEO_RENDERER_H_ #ifndef CROSSDESK_GUI_PLATFORM_OPENGL_VIDEO_RENDERER_H_
#define CROSSDESK_GUI_PLATFORM_WINDOWS_OPENGL_VIDEO_RENDERER_H_ #define CROSSDESK_GUI_PLATFORM_OPENGL_VIDEO_RENDERER_H_
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@@ -10,11 +10,12 @@
namespace crossdesk { namespace crossdesk {
// Windows NV12 underlay for Slint's FemtoVG OpenGL renderer. MiniRTC submits // NV12 underlay for Slint's FemtoVG OpenGL renderer on Windows and Linux.
// tightly packed CPU NV12 frames from its decode callback thread. The Slint UI // MiniRTC submits tightly packed CPU NV12 frames from its decode callback
// thread uploads the newest frame as Y and UV textures and performs color // thread. The Slint UI thread uploads the newest frame as Y and UV textures and
// conversion and scaling in a fragment shader before Slint draws its overlay. // performs color conversion and scaling in a fragment shader before Slint
class WindowsOpenGlVideoRenderer { // draws its overlay.
class OpenGlVideoRenderer {
public: public:
enum class SubmitResult { enum class SubmitResult {
submitted, submitted,
@@ -37,12 +38,11 @@ public:
uint64_t sequence = 0; uint64_t sequence = 0;
}; };
WindowsOpenGlVideoRenderer(); OpenGlVideoRenderer();
~WindowsOpenGlVideoRenderer(); ~OpenGlVideoRenderer();
WindowsOpenGlVideoRenderer(const WindowsOpenGlVideoRenderer &) = delete; OpenGlVideoRenderer(const OpenGlVideoRenderer &) = delete;
WindowsOpenGlVideoRenderer & OpenGlVideoRenderer &operator=(const OpenGlVideoRenderer &) = delete;
operator=(const WindowsOpenGlVideoRenderer &) = delete;
// These methods must run while Slint's OpenGL context is current, from the // These methods must run while Slint's OpenGL context is current, from the
// RenderingSetup and RenderingTeardown notifier states respectively. // RenderingSetup and RenderingTeardown notifier states respectively.
@@ -61,10 +61,12 @@ public:
SubmitResult SubmitCachedNv12(std::string_view remote_id, const uint8_t *data, SubmitResult SubmitCachedNv12(std::string_view remote_id, const uint8_t *data,
size_t size, int width, int height); size_t size, int width, int height);
// Draws below Slint while its OpenGL context is current. target dimensions // Draws below Slint while its OpenGL context is current. All dimensions are
// and top_inset_pixels are physical pixels in the window client area. // physical pixels in the window client area. corner_radius_pixels clips the
// underlay to Slint's custom rounded window surface.
RenderOutcome RenderLatest(std::string_view remote_id, int target_width, RenderOutcome RenderLatest(std::string_view remote_id, int target_width,
int target_height, int top_inset_pixels); int target_height, int top_inset_pixels,
int corner_radius_pixels);
// Used during disconnect cleanup so thumbnail generation does not require a // Used during disconnect cleanup so thumbnail generation does not require a
// second per-frame CPU copy during normal playback. // second per-frame CPU copy during normal playback.
@@ -83,4 +85,4 @@ private:
} // namespace crossdesk } // namespace crossdesk
#endif // CROSSDESK_GUI_PLATFORM_WINDOWS_OPENGL_VIDEO_RENDERER_H_ #endif // CROSSDESK_GUI_PLATFORM_OPENGL_VIDEO_RENDERER_H_
+9 -9
View File
@@ -11,8 +11,8 @@
#include "display_stream_id.h" #include "display_stream_id.h"
#include "localization.h" #include "localization.h"
#include "platform.h" #include "platform.h"
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
#include "platform/windows_opengl_video_renderer.h" #include "platform/opengl_video_renderer.h"
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include "platform/metal_video_renderer.h" #include "platform/metal_video_renderer.h"
@@ -229,7 +229,7 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
frame_snapshot = props->front_frame_; frame_snapshot = props->front_frame_;
video_width = props->video_width_; video_width = props->video_width_;
video_height = props->video_height_; video_height = props->video_height_;
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if ((!frame_snapshot || frame_snapshot->empty()) && if ((!frame_snapshot || frame_snapshot->empty()) &&
props->thumbnail_frame_ && !props->thumbnail_frame_->empty()) { props->thumbnail_frame_ && !props->thumbnail_frame_->empty()) {
frame_snapshot = props->thumbnail_frame_; frame_snapshot = props->thumbnail_frame_;
@@ -238,11 +238,11 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
} }
#endif #endif
} }
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#if defined(_WIN32) #if defined(__APPLE__)
auto* native_renderer = windows_opengl_video_renderer_.get();
#else
auto* native_renderer = mac_metal_video_renderer_.get(); auto* native_renderer = mac_metal_video_renderer_.get();
#else
auto* native_renderer = opengl_video_renderer_.get();
#endif #endif
if ((!frame_snapshot || frame_snapshot->empty()) && native_renderer) { if ((!frame_snapshot || frame_snapshot->empty()) && native_renderer) {
auto native_snapshot = std::make_shared<std::vector<unsigned char>>(); auto native_snapshot = std::make_shared<std::vector<unsigned char>>();
@@ -276,7 +276,7 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
} }
} }
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (native_renderer) { if (native_renderer) {
native_renderer->DiscardStream(props->remote_id_); native_renderer->DiscardStream(props->remote_id_);
video_frame_dirty_.store(true, std::memory_order_release); video_frame_dirty_.store(true, std::memory_order_release);
@@ -343,7 +343,7 @@ void GuiRuntime::ResetRemoteSessionResources(
std::lock_guard<std::mutex> lock(props->video_frame_mutex_); std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
props->front_frame_.reset(); props->front_frame_.reset();
props->back_frame_.reset(); props->back_frame_.reset();
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
props->thumbnail_frame_.reset(); props->thumbnail_frame_.reset();
props->thumbnail_width_ = 0; props->thumbnail_width_ = 0;
props->thumbnail_height_ = 0; props->thumbnail_height_ = 0;
+2 -2
View File
@@ -12,8 +12,8 @@
#include "display_stream_id.h" #include "display_stream_id.h"
#include "localization.h" #include "localization.h"
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
#include "platform/windows_opengl_video_renderer.h" #include "platform/opengl_video_renderer.h"
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include "platform/metal_video_renderer.h" #include "platform/metal_video_renderer.h"
+4 -4
View File
@@ -15,8 +15,8 @@
namespace crossdesk { namespace crossdesk {
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
class WindowsOpenGlVideoRenderer; class OpenGlVideoRenderer;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
class MacMetalVideoRenderer; class MacMetalVideoRenderer;
@@ -90,8 +90,8 @@ class GuiRuntime : protected gui_detail::GuiState {
KeyboardController keyboard_; KeyboardController keyboard_;
PeerEventHandler peer_events_; PeerEventHandler peer_events_;
std::atomic<bool> video_frame_dirty_{false}; std::atomic<bool> video_frame_dirty_{false};
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
std::unique_ptr<WindowsOpenGlVideoRenderer> windows_opengl_video_renderer_; std::unique_ptr<OpenGlVideoRenderer> opengl_video_renderer_;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
std::unique_ptr<MacMetalVideoRenderer> mac_metal_video_renderer_; std::unique_ptr<MacMetalVideoRenderer> mac_metal_video_renderer_;
+11 -12
View File
@@ -16,8 +16,8 @@
#include "file_transfer.h" #include "file_transfer.h"
#include "localization.h" #include "localization.h"
#include "platform.h" #include "platform.h"
#if defined(_WIN32) #if defined(_WIN32) || defined(__linux__)
#include "platform/windows_opengl_video_renderer.h" #include "platform/opengl_video_renderer.h"
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include "platform/metal_video_renderer.h" #include "platform/metal_video_renderer.h"
@@ -319,7 +319,7 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
props->enable_mouse_control_ = false; props->enable_mouse_control_ = false;
runtime->ResetRemoteServiceStatus(*props); runtime->ResetRemoteServiceStatus(*props);
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
std::shared_ptr<std::vector<unsigned char>> native_snapshot; std::shared_ptr<std::vector<unsigned char>> native_snapshot;
int native_snapshot_width = 0; int native_snapshot_width = 0;
int native_snapshot_height = 0; int native_snapshot_height = 0;
@@ -330,11 +330,10 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
props->thumbnail_frame_->empty(); props->thumbnail_frame_->empty();
} }
if (needs_native_snapshot) { if (needs_native_snapshot) {
#if defined(_WIN32) #if defined(__APPLE__)
auto* native_renderer =
runtime->windows_opengl_video_renderer_.get();
#else
auto* native_renderer = runtime->mac_metal_video_renderer_.get(); auto* native_renderer = runtime->mac_metal_video_renderer_.get();
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
#endif #endif
auto snapshot = std::make_shared<std::vector<unsigned char>>(); auto snapshot = std::make_shared<std::vector<unsigned char>>();
if (native_renderer && native_renderer->CopyLatestNv12( if (native_renderer && native_renderer->CopyLatestNv12(
@@ -349,7 +348,7 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
std::lock_guard<std::mutex> lock(props->video_frame_mutex_); std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
props->front_frame_.reset(); props->front_frame_.reset();
props->back_frame_.reset(); props->back_frame_.reset();
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (native_snapshot && if (native_snapshot &&
(!props->thumbnail_frame_ || props->thumbnail_frame_->empty())) { (!props->thumbnail_frame_ || props->thumbnail_frame_->empty())) {
props->thumbnail_frame_ = std::move(native_snapshot); props->thumbnail_frame_ = std::move(native_snapshot);
@@ -363,11 +362,11 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
props->render_rect_dirty_ = true; props->render_rect_dirty_ = true;
props->stream_cleanup_pending_ = true; props->stream_cleanup_pending_ = true;
} }
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#if defined(_WIN32) #if defined(__APPLE__)
auto* native_renderer = runtime->windows_opengl_video_renderer_.get();
#else
auto* native_renderer = runtime->mac_metal_video_renderer_.get(); auto* native_renderer = runtime->mac_metal_video_renderer_.get();
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
#endif #endif
if (native_renderer) { if (native_renderer) {
native_renderer->DiscardStream(remote_id); native_renderer->DiscardStream(remote_id);
+13 -13
View File
@@ -7,18 +7,18 @@
#include <vector> #include <vector>
#include "runtime/gui_runtime.h" #include "runtime/gui_runtime.h"
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#include <chrono> #include <chrono>
#if defined(_WIN32) #if defined(__APPLE__)
#include "platform/windows_opengl_video_renderer.h"
#else
#include "platform/metal_video_renderer.h" #include "platform/metal_video_renderer.h"
#else
#include "platform/opengl_video_renderer.h"
#endif #endif
#endif #endif
namespace crossdesk { namespace crossdesk {
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
namespace { namespace {
constexpr auto kBackgroundSnapshotInterval = std::chrono::seconds(1); constexpr auto kBackgroundSnapshotInterval = std::chrono::seconds(1);
@@ -45,14 +45,14 @@ void PeerEventHandler::OnReceiveVideoBuffer(
runtime->remote_sessions_.find(remote_id)->second.get(); runtime->remote_sessions_.find(remote_id)->second.get();
if (props->connection_established_) { if (props->connection_established_) {
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
bool background_snapshot_only = false; bool background_snapshot_only = false;
#if defined(_WIN32) #if defined(__APPLE__)
auto* native_renderer = runtime->windows_opengl_video_renderer_.get();
using NativeVideoRenderer = WindowsOpenGlVideoRenderer;
#else
auto* native_renderer = runtime->mac_metal_video_renderer_.get(); auto* native_renderer = runtime->mac_metal_video_renderer_.get();
using NativeVideoRenderer = MacMetalVideoRenderer; using NativeVideoRenderer = MacMetalVideoRenderer;
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
using NativeVideoRenderer = OpenGlVideoRenderer;
#endif #endif
if (native_renderer && native_renderer->IsReady() if (native_renderer && native_renderer->IsReady()
#if defined(__APPLE__) #if defined(__APPLE__)
@@ -101,7 +101,7 @@ void PeerEventHandler::OnReceiveVideoBuffer(
#endif #endif
{ {
std::lock_guard<std::mutex> lock(props->video_frame_mutex_); std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
if (background_snapshot_only && props->thumbnail_frame_ && if (background_snapshot_only && props->thumbnail_frame_ &&
!props->thumbnail_frame_->empty() && !props->thumbnail_frame_->empty() &&
@@ -137,7 +137,7 @@ void PeerEventHandler::OnReceiveVideoBuffer(
props->video_size_ = video_frame->size; props->video_size_ = video_frame->size;
props->front_frame_.swap(props->back_frame_); props->front_frame_.swap(props->back_frame_);
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
props->thumbnail_frame_ = props->front_frame_; props->thumbnail_frame_ = props->front_frame_;
props->thumbnail_width_ = video_frame->width; props->thumbnail_width_ = video_frame->width;
props->thumbnail_height_ = video_frame->height; props->thumbnail_height_ = video_frame->height;
@@ -149,7 +149,7 @@ void PeerEventHandler::OnReceiveVideoBuffer(
} }
props->streaming_ = true; props->streaming_ = true;
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (background_snapshot_only) { if (background_snapshot_only) {
return; return;
} }
+1 -1
View File
@@ -110,7 +110,7 @@ struct RemoteSession {
std::mutex video_frame_mutex_; std::mutex video_frame_mutex_;
std::shared_ptr<std::vector<unsigned char>> front_frame_; std::shared_ptr<std::vector<unsigned char>> front_frame_;
std::shared_ptr<std::vector<unsigned char>> back_frame_; std::shared_ptr<std::vector<unsigned char>> back_frame_;
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
// Retains the most recent background CPU frame across disconnect cleanup // Retains the most recent background CPU frame across disconnect cleanup
// so closing an unselected tab can still update its recent-item thumbnail. // so closing an unselected tab can still update its recent-item thumbnail.
std::shared_ptr<std::vector<unsigned char>> thumbnail_frame_; std::shared_ptr<std::vector<unsigned char>> thumbnail_frame_;
+3 -2
View File
@@ -279,7 +279,7 @@ function setup_targets()
add_cxxflags("/bigobj") add_cxxflags("/bigobj")
add_links("opengl32") add_links("opengl32")
add_files("src/gui/platform/tray/win_tray.cpp", add_files("src/gui/platform/tray/win_tray.cpp",
"src/gui/platform/windows_opengl_video_renderer.cpp") "src/gui/platform/opengl_video_renderer.cpp")
add_includedirs("src/service/windows", {public = true}) add_includedirs("src/service/windows", {public = true})
elseif is_os("macosx") then elseif is_os("macosx") then
add_files("src/gui/runtime/*.mm", "src/gui/platform/tray/*.mm", add_files("src/gui/runtime/*.mm", "src/gui/platform/tray/*.mm",
@@ -287,7 +287,8 @@ function setup_targets()
"src/gui/platform/metal_video_renderer.mm") "src/gui/platform/metal_video_renderer.mm")
elseif is_os("linux") then elseif is_os("linux") then
add_links("GL") add_links("GL")
add_files("src/gui/platform/tray/linux_tray.cpp") add_files("src/gui/platform/tray/linux_tray.cpp",
"src/gui/platform/opengl_video_renderer.cpp")
end end
if is_os("windows") then if is_os("windows") then