How scrcpy Manages Window Rendering and OpenGL Display: SDL2 Architecture and Pipeline Deep Dive
scrcpy leverages SDL2 for cross-platform window management while automatically detecting and utilizing OpenGL renderers to enable trilinear filtering, mipmapping, and efficient YUV-to-RGB conversion through a layered architecture implemented in screen.c and display.c.
scrcpy (Screen Copy) is an open-source application that mirrors and controls Android devices from a desktop environment. Understanding how scrcpy handles window rendering and OpenGL display reveals a sophisticated architecture built on SDL2 that manages HiDPI scaling, dynamic orientation changes, and hardware-accelerated video processing.
SDL2 Window Management and HiDPI Handling (screen.c)
The window lifecycle begins in app/src/screen.c, where sc_screen_init() constructs the SDL window with flags for high-DPI support, resizing, and positioning.
Window Creation
At lines 397-410, the code calls SDL_CreateWindow with accumulated window flags:
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
HiDPI Scaling
To handle Retina and other high-density displays, sc_screen_init() calculates scaling factors by comparing drawable size against window size (lines 439-447):
*x = (int64_t)*x * dw / ww;
*y = (int64_t)*y * dh / wh;
Dynamic Resizing and Orientation
When content size changes, sc_screen_update_content_rect() (lines 165-199) recomputes the destination rectangle to preserve aspect ratio and remove black borders. Fullscreen toggling is handled by sc_screen_toggle_fullscreen() (lines 389-453) using SDL_SetWindowFullscreen.
OpenGL Display Pipeline (display.c)
The rendering abstraction lives in app/src/display.c within the sc_display structure, which encapsulates the SDL renderer, texture, and optional OpenGL context.
Renderer Detection and Context Creation
sc_display_init() (lines 34-40) creates an accelerated SDL renderer:
display->renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
OpenGL detection occurs at lines 52-55 by inspecting the renderer name:
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
When compiled with SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE, the code requests an explicit core-profile context (lines 56-68):
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
display->gl_context = SDL_GL_CreateContext(window);
OpenGL Symbol Loading
The app/src/opengl.c module handles symbol loading. sc_opengl_init() (lines 9-21) retrieves function pointers via SDL_GL_GetProcAddress, while sc_opengl_version_at_least() validates support for advanced features.
Mipmap Generation and Trilinear Filtering
If the user enables --mipmaps and the OpenGL version supports it (≥3.0 desktop or ≥2.0 ES), sc_display_init() enables mipmap generation (lines 71-88).
During texture setup (lines 40-52), the code configures trilinear filtering:
SDL_GL_BindTexture(texture, NULL, NULL);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
SDL_GL_UnbindTexture(texture);
Frame Rendering Pipeline
When a new frame arrives, sc_display_update_texture() (lines 66-71) uploads YUV data:
SDL_UpdateYUVTexture(display->texture, NULL,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]);
If mipmaps are enabled, sc_display_render() generates the mipmap chain (lines 76-80):
gl->GenerateMipmap(GL_TEXTURE_2D);
Orientation handling (lines 22-45) uses SDL_RenderCopyEx with rotation angles and horizontal flipping:
SDL_RenderCopyEx(display->renderer, display->texture, NULL, &rect,
rotation_angle, NULL, flip);
Finally, SDL_RenderPresent (lines 48-50) displays the frame.
Complete Rendering Flow Example
The following pseudo-code illustrates the typical initialization and rendering sequence:
// 1. Initialize screen (creates SDL window)
struct sc_screen screen;
struct sc_screen_params params = {/* … user options … */};
sc_screen_init(&screen, ¶ms);
// 2. Initialize display (creates renderer, optional GL context)
sc_display_init(&screen.display, screen.window, icon_surface, params.mipmaps);
// 3. First frame arrives (from the decoder)
AVFrame *frame = ...; // YUV420P
sc_display_set_texture_size(&screen.display,
(struct sc_size){frame->width, frame->height});
sc_display_update_texture(&screen.display, frame);
// 4. Render loop (called for every new frame)
sc_screen_render(&screen, true); // true → recompute content rect if needed
Summary
- SDL2 provides the cross-platform windowing foundation, handling HiDPI scaling, resizing, and event loops through
app/src/screen.c. - OpenGL integration is automatically detected in
app/src/display.c, enabling hardware-accelerated YUV-to-RGB conversion, trilinear filtering, and mipmap generation when the--mipmapsflag is used. - Texture management uses SDL's streaming textures with optional OpenGL binding for advanced filtering, while
app/src/opengl.chandles symbol loading and version detection. - Orientation support is implemented via
SDL_RenderCopyExrotations, ensuring the video displays correctly regardless of device orientation changes.
Frequently Asked Questions
How does scrcpy detect whether OpenGL is available?
scrcpy checks the renderer name returned by SDL_GetRendererInfo in app/src/display.c (lines 52-55). If the name begins with "opengl", the code sets up an OpenGL context and loads necessary function pointers via SDL_GL_GetProcAddress in app/src/opengl.c.
What is the purpose of the --mipmaps option in scrcpy?
The --mipmaps flag enables trilinear filtering (GL_LINEAR_MIPMAP_LINEAR) and automatic mipmap generation using glGenerateMipmap. This improves visual quality when the window is significantly smaller than the native video resolution by reducing aliasing artifacts. The feature requires OpenGL 3.0+ on desktop or OpenGL ES 2.0+.
How does scrcpy handle device rotation and orientation changes?
scrcpy uses SDL_RenderCopyEx in app/src/display.c (lines 22-45) to apply rotation angles (90°, 180°, 270°) and horizontal flipping based on the current device orientation. This occurs during the rendering phase after the YUV texture is updated but before SDL_RenderPresent displays the frame.
Why does scrcpy use SDL2 instead of raw OpenGL or platform-specific APIs?
SDL2 provides cross-platform window creation, event handling, and renderer abstraction that works consistently across Windows, macOS, and Linux. By using SDL2's high-level texture and renderer APIs while optionally binding to OpenGL for advanced features like mipmaps, scrcpy maintains portability without sacrificing performance or visual quality.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →