Skip to content

Commit b0d261c

Browse files
authored
blend osd in shader (#131)
1 parent d13621a commit b0d261c

6 files changed

Lines changed: 204 additions & 108 deletions

File tree

src/frame_colorcorrect.cpp

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,25 @@ void main() {
2525
}
2626
)GLSL";
2727

28-
// Forward colortrans: y = clamp((x + offset) * gain, 0, 1).
29-
// samplerExternalOES lets the driver convert NV12 → RGB transparently.
28+
// Color-correct + OSD composite.
29+
// samplerExternalOES converts NV12 → RGB implicitly during sampling.
30+
// osd_tex is a plain RGBA texture (DRM_FORMAT_ARGB8888 imported as EGLImage).
31+
// has_osd: 1.0 = composite OSD, 0.0 = pass through (transparent OSD fallback).
3032
static const char* kFrag = R"GLSL(
3133
#extension GL_OES_EGL_image_external : require
3234
precision mediump float;
3335
varying vec2 v_uv;
3436
uniform samplerExternalOES tex;
37+
uniform sampler2D osd_tex;
3538
uniform float gain;
3639
uniform float offset;
40+
uniform float has_osd;
3741
void main() {
3842
vec3 c = texture2D(tex, v_uv).rgb;
39-
gl_FragColor = vec4(clamp((c + offset) * gain, 0.0, 1.0), 1.0);
43+
vec4 video = vec4(clamp((c + offset) * gain, 0.0, 1.0), 1.0);
44+
vec4 osd = texture2D(osd_tex, v_uv);
45+
float alpha = has_osd * osd.a;
46+
gl_FragColor = vec4(alpha * osd.rgb + (1.0 - alpha) * video.rgb, 1.0);
4047
}
4148
)GLSL";
4249

@@ -166,13 +173,24 @@ bool FrameColorCorrect::build_shader() {
166173
glDeleteShader(vs); glDeleteShader(fs);
167174
if (!prog_) return false;
168175

169-
loc_tex_ = glGetUniformLocation(prog_, "tex");
170-
loc_gain_ = glGetUniformLocation(prog_, "gain");
171-
loc_offset_ = glGetUniformLocation(prog_, "offset");
176+
loc_tex_ = glGetUniformLocation(prog_, "tex");
177+
loc_gain_ = glGetUniformLocation(prog_, "gain");
178+
loc_offset_ = glGetUniformLocation(prog_, "offset");
179+
loc_osd_tex_ = glGetUniformLocation(prog_, "osd_tex");
180+
loc_has_osd_ = glGetUniformLocation(prog_, "has_osd");
172181
return true;
173182
}
174183

175184
bool FrameColorCorrect::create_targets() {
185+
// 1×1 fully-transparent texture used when no OSD is active.
186+
glGenTextures(1, &osd_tex_gl_);
187+
glBindTexture(GL_TEXTURE_2D, osd_tex_gl_);
188+
const uint8_t transparent[4] = {0, 0, 0, 0};
189+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
190+
GL_RGBA, GL_UNSIGNED_BYTE, transparent);
191+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
192+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
193+
176194
for (int i = 0; i < kTargets; i++) {
177195
Target& t = targets_[i];
178196

@@ -232,8 +250,7 @@ bool FrameColorCorrect::create_targets() {
232250

233251
bool FrameColorCorrect::process(int src_fd, uint32_t src_w, uint32_t src_h,
234252
uint32_t src_hs, uint32_t src_vs,
235-
int dst_fd, uint32_t dst_w, uint32_t dst_h,
236-
uint32_t dst_hs, uint32_t dst_vs)
253+
int dst_fd, uint32_t dst_hs, uint32_t dst_vs)
237254
{
238255
// --- Import NV12 source as EGLImage (two-plane) -------------------------
239256
EGLint uv_offset = (EGLint)((size_t)src_hs * src_vs);
@@ -274,10 +291,17 @@ bool FrameColorCorrect::process(int src_fd, uint32_t src_w, uint32_t src_h,
274291
glBindFramebuffer(GL_FRAMEBUFFER, tgt.fbo);
275292
glViewport(0, 0, (GLsizei)width_, (GLsizei)height_);
276293

294+
// Bind OSD on texture unit 1.
295+
glActiveTexture(GL_TEXTURE1);
296+
glBindTexture(GL_TEXTURE_2D, osd_tex_gl_);
297+
298+
glActiveTexture(GL_TEXTURE0);
277299
glUseProgram(prog_);
278-
glUniform1i(loc_tex_, 0);
279-
glUniform1f(loc_gain_, gain_);
280-
glUniform1f(loc_offset_, offset_);
300+
glUniform1i(loc_tex_, 0);
301+
glUniform1i(loc_osd_tex_, 1);
302+
glUniform1f(loc_gain_, gain_);
303+
glUniform1f(loc_offset_, offset_);
304+
glUniform1f(loc_has_osd_, osd_prime_fd_ >= 0 ? 1.0f : 0.0f);
281305

282306
// Flip V so that top-origin NV12 memory maps to the top of the GL FB.
283307
const GLfloat verts[] = {
@@ -302,17 +326,18 @@ bool FrameColorCorrect::process(int src_fd, uint32_t src_w, uint32_t src_h,
302326
glDeleteTextures(1, &src_tex);
303327
eglDestroyImageKHR_(dpy_, src_img);
304328

305-
// --- RGA: convert RGBA target → NV12 destination -----------------------
306-
// DRM_FORMAT_ARGB8888 is BGRA in memory on little-endian → RK_FORMAT_BGRA_8888
307-
// When dst dimensions differ from src, RGA performs resize + CSC in one pass.
329+
// --- RGA: CSC RGBA target → NV12 destination ---------------------------
330+
// GBM BO is at output size (width_ × height_), dst is the same size,
331+
// so this is pure colour-space conversion with no resize.
332+
// DRM_FORMAT_ARGB8888 is BGRA in memory on little-endian → RK_FORMAT_BGRA_8888.
308333
rga_buffer_t src_rga = wrapbuffer_fd_t(
309334
tgt.prime_fd,
310335
(int)width_, (int)height_,
311336
(int)tgt.stride_px, (int)height_,
312337
RK_FORMAT_BGRA_8888);
313338
rga_buffer_t dst_rga = wrapbuffer_fd_t(
314339
dst_fd,
315-
(int)dst_w, (int)dst_h,
340+
(int)width_, (int)height_,
316341
(int)dst_hs, (int)dst_vs,
317342
RK_FORMAT_YCbCr_420_SP);
318343

@@ -324,7 +349,70 @@ bool FrameColorCorrect::process(int src_fd, uint32_t src_w, uint32_t src_h,
324349
return true;
325350
}
326351

352+
void FrameColorCorrect::set_osd(int prime_fd, uint32_t w, uint32_t h, uint32_t stride_px)
353+
{
354+
if (prime_fd == osd_prime_fd_ && w == osd_w_ && h == osd_h_)
355+
return; // same buffer — EGLImage still valid
356+
357+
// Release old EGLImage if any.
358+
if (osd_img_ != EGL_NO_IMAGE_KHR) {
359+
eglDestroyImageKHR_(dpy_, osd_img_);
360+
osd_img_ = EGL_NO_IMAGE_KHR;
361+
}
362+
363+
osd_prime_fd_ = prime_fd;
364+
osd_w_ = w;
365+
osd_h_ = h;
366+
osd_stride_px_ = stride_px;
367+
368+
const EGLint osd_attrs[] = {
369+
EGL_WIDTH, (EGLint)w,
370+
EGL_HEIGHT, (EGLint)h,
371+
EGL_LINUX_DRM_FOURCC_EXT, (EGLint)DRM_FORMAT_ARGB8888,
372+
EGL_DMA_BUF_PLANE0_FD_EXT, prime_fd,
373+
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
374+
EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)(stride_px * 4),
375+
EGL_NONE
376+
};
377+
osd_img_ = eglCreateImageKHR_(dpy_, EGL_NO_CONTEXT,
378+
EGL_LINUX_DMA_BUF_EXT, nullptr, osd_attrs);
379+
if (osd_img_ == EGL_NO_IMAGE_KHR) {
380+
spdlog::warn("FrameCC: eglCreateImageKHR for OSD failed (0x{:x})", eglGetError());
381+
osd_prime_fd_ = -1;
382+
return;
383+
}
384+
385+
// Re-bind the osd texture object to the new EGLImage.
386+
glBindTexture(GL_TEXTURE_2D, osd_tex_gl_);
387+
glEGLImageTargetTexture2DOES_(GL_TEXTURE_2D, osd_img_);
388+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
389+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
390+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
391+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
392+
}
393+
394+
void FrameColorCorrect::clear_osd()
395+
{
396+
if (osd_prime_fd_ < 0) return;
397+
if (osd_img_ != EGL_NO_IMAGE_KHR) {
398+
eglDestroyImageKHR_(dpy_, osd_img_);
399+
osd_img_ = EGL_NO_IMAGE_KHR;
400+
}
401+
osd_prime_fd_ = -1;
402+
// Restore the 1×1 transparent fallback.
403+
const uint8_t transparent[4] = {0, 0, 0, 0};
404+
glBindTexture(GL_TEXTURE_2D, osd_tex_gl_);
405+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
406+
GL_RGBA, GL_UNSIGNED_BYTE, transparent);
407+
}
408+
327409
void FrameColorCorrect::destroy_targets() {
410+
if (osd_img_ != EGL_NO_IMAGE_KHR) {
411+
eglDestroyImageKHR_(dpy_, osd_img_); osd_img_ = EGL_NO_IMAGE_KHR;
412+
}
413+
osd_prime_fd_ = -1;
414+
if (osd_tex_gl_) { glDeleteTextures(1, &osd_tex_gl_); osd_tex_gl_ = 0; }
415+
328416
for (int i = 0; i < kTargets; i++) {
329417
Target& t = targets_[i];
330418
if (t.fbo) { glDeleteFramebuffers(1, &t.fbo); t.fbo = 0; }

src/frame_colorcorrect.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,29 @@ class FrameColorCorrect {
3131
FrameColorCorrect() = default;
3232
~FrameColorCorrect();
3333

34-
// Init EGL context, compile shader, create RGBA render targets.
34+
// Init EGL context, compile shader, create RGBA render targets at OUTPUT size.
3535
// Must be called from the thread that will call process().
36-
bool init(int drm_fd, uint32_t width, uint32_t height,
36+
// gain/offset apply the DRM gamma formula y = clamp((x+offset)*gain, 0,1);
37+
// pass gain=1 offset=0 when only OSD blend is needed.
38+
bool init(int drm_fd, uint32_t out_w, uint32_t out_h,
3739
float gain, float offset);
3840

3941
void deinit();
4042
bool ready() const { return ready_; }
4143

42-
// Apply color correction: read src NV12 DMA-buf, write corrected NV12
43-
// into dst DMA-buf. Both fds come from mpp_buffer_get_fd().
44-
// dst dimensions may differ from src — RGA performs resize + CSC in one pass.
44+
// Register the current OSD DMA-buf for compositing. Called whenever the
45+
// OSD double-buffer switches. The EGLImage is cached and only re-imported
46+
// when prime_fd changes. Must be called from the processor thread.
47+
void set_osd(int prime_fd, uint32_t w, uint32_t h, uint32_t stride_px);
48+
void clear_osd(); // disable OSD compositing
49+
50+
// Apply color-correction + OSD blend: read src NV12 DMA-buf, render into
51+
// the RGBA GBM BO at output size (resizing via texture sampling if needed),
52+
// then RGA-CSC to the NV12 dst DMA-buf (no resize — sizes always match).
4553
// Returns false on failure — caller should fall back to plain copy.
4654
bool process(int src_fd, uint32_t src_w, uint32_t src_h,
4755
uint32_t src_hs, uint32_t src_vs,
48-
int dst_fd, uint32_t dst_w, uint32_t dst_h,
49-
uint32_t dst_hs, uint32_t dst_vs);
56+
int dst_fd, uint32_t dst_hs, uint32_t dst_vs);
5057

5158
private:
5259
bool build_shader();
@@ -69,6 +76,14 @@ class FrameColorCorrect {
6976
GLint loc_tex_{-1};
7077
GLint loc_gain_{-1};
7178
GLint loc_offset_{-1};
79+
GLint loc_osd_tex_{-1};
80+
GLint loc_has_osd_{-1};
81+
82+
// OSD compositing state (processor-thread only)
83+
GLuint osd_tex_gl_{0}; // 1×1 transparent fallback or live OSD
84+
int osd_prime_fd_{-1}; // DMA-buf fd of the current OSD buffer
85+
EGLImageKHR osd_img_{EGL_NO_IMAGE_KHR};
86+
uint32_t osd_w_{0}, osd_h_{0}, osd_stride_px_{0};
7287

7388
// Double-buffered RGBA render targets
7489
static constexpr int kTargets = 2;

0 commit comments

Comments
 (0)