Skip to content

Commit 0aaa707

Browse files
author
N3xoX1
committed
Add watermark to the opengl renderer
1 parent bc5bcfb commit 0aaa707

5 files changed

Lines changed: 4827 additions & 3 deletions

File tree

src/yuzu_video_core/renderer_opengl/renderer_opengl.cpp

Lines changed: 261 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "yuzu_common/logging/log.h"
1313
#include "yuzu_common/microprofile.h"
1414
#include "yuzu_common/settings.h"
15+
#include "yuzu_common/stb.h"
1516
#include "core/core_timing.h"
1617
#include "frontend/emu_window.h"
1718
#include "yuzu_video_core/capture.h"
@@ -22,6 +23,7 @@
2223
#include "yuzu_video_core/renderer_opengl/gl_shader_util.h"
2324
#include "yuzu_video_core/renderer_opengl/renderer_opengl.h"
2425
#include "yuzu_video_core/textures/decoders.h"
26+
#include "yuzu_video_core/watermark.h"
2527

2628
namespace OpenGL {
2729
namespace {
@@ -94,7 +96,10 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window_,
9496
: RendererBase{emu_window_, std::move(context_)},
9597
emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_},
9698
state_tracker{}, program_manager{device},
97-
rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
99+
rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker),
100+
watermark_texture(0), watermark_vao(0), watermark_vbo(0), watermark_shader(0), watermark_width(0), watermark_height(0),
101+
watermark_timer_started(false), watermark_show(true)
102+
{
98103
if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
99104
glEnable(GL_DEBUG_OUTPUT);
100105
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
@@ -128,9 +133,14 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window_,
128133
glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle);
129134
glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth,
130135
VideoCore::Capture::LinearHeight);
136+
137+
InitializeWatermark();
131138
}
132139

133-
RendererOpenGL::~RendererOpenGL() = default;
140+
RendererOpenGL::~RendererOpenGL()
141+
{
142+
CleanupWatermark();
143+
}
134144

135145
void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
136146
if (framebuffers.empty()) {
@@ -142,13 +152,18 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
142152

143153
state_tracker.BindFramebuffer(0);
144154
blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);
155+
if (watermark_show)
156+
{
157+
RenderWatermark();
158+
}
145159

146160
++m_current_frame;
147161

148162
gpu.RendererFrameEndNotify();
149163
rasterizer.TickFrame();
150164

151165
context->SwapBuffers();
166+
152167
render_window.OnFrameDisplayed();
153168
}
154169

@@ -162,10 +177,253 @@ void RendererOpenGL::AddTelemetryFields() {
162177
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
163178
}
164179

180+
void RendererOpenGL::InitializeWatermark()
181+
{
182+
while (glGetError() != GL_NO_ERROR) {}
183+
184+
int channels;
185+
unsigned char * image_data = stbi_load_from_memory(watermark_png, sizeof(watermark_png), &watermark_width, &watermark_height, &channels, 4);
186+
if (!image_data)
187+
{
188+
LOG_ERROR(Render_OpenGL, "Failed to load watermark image: {}", stbi_failure_reason());
189+
return;
190+
}
191+
192+
glGenTextures(1, &watermark_texture);
193+
glBindTexture(GL_TEXTURE_2D, watermark_texture);
194+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
195+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
196+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
197+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
198+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, watermark_width, watermark_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
199+
stbi_image_free(image_data);
200+
201+
glGenVertexArrays(1, &watermark_vao);
202+
glGenBuffers(1, &watermark_vbo);
203+
204+
glBindVertexArray(watermark_vao);
205+
glBindBuffer(GL_ARRAY_BUFFER, watermark_vbo);
206+
207+
const size_t vertex_data_size = 6 * 4 * sizeof(float);
208+
glBufferData(GL_ARRAY_BUFFER, vertex_data_size, nullptr, GL_DYNAMIC_DRAW);
209+
210+
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
211+
glEnableVertexAttribArray(0);
212+
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
213+
glEnableVertexAttribArray(1);
214+
215+
const std::string vertex_shader_source = R"(
216+
#version 330 core
217+
layout (location = 0) in vec2 aPos;
218+
layout (location = 1) in vec2 aTexCoord;
219+
220+
out vec2 TexCoord;
221+
222+
void main() {
223+
gl_Position = vec4(aPos, 0.0, 1.0);
224+
TexCoord = aTexCoord;
225+
}
226+
)";
227+
228+
const std::string fragment_shader_source = R"(
229+
#version 330 core
230+
in vec2 TexCoord;
231+
out vec4 FragColor;
232+
233+
uniform sampler2D watermarkTexture;
234+
uniform float alpha;
235+
236+
void main() {
237+
vec4 texColor = texture(watermarkTexture, TexCoord);
238+
FragColor = vec4(texColor.rgb, texColor.a * alpha);
239+
}
240+
)";
241+
242+
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
243+
const char* vertex_src = vertex_shader_source.c_str();
244+
glShaderSource(vertex_shader, 1, &vertex_src, nullptr);
245+
glCompileShader(vertex_shader);
246+
247+
GLint success;
248+
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
249+
if (!success)
250+
{
251+
char info_log[512];
252+
glGetShaderInfoLog(vertex_shader, 512, nullptr, info_log);
253+
LOG_ERROR(Render_OpenGL, "Vertex shader compilation failed: {}", info_log);
254+
return;
255+
}
256+
257+
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
258+
const char* fragment_src = fragment_shader_source.c_str();
259+
glShaderSource(fragment_shader, 1, &fragment_src, nullptr);
260+
glCompileShader(fragment_shader);
261+
262+
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
263+
if (!success) {
264+
char info_log[512];
265+
glGetShaderInfoLog(fragment_shader, 512, nullptr, info_log);
266+
LOG_ERROR(Render_OpenGL, "Fragment shader compilation failed: {}", info_log);
267+
glDeleteShader(vertex_shader);
268+
return;
269+
}
270+
271+
watermark_shader = glCreateProgram();
272+
glAttachShader(watermark_shader, vertex_shader);
273+
glAttachShader(watermark_shader, fragment_shader);
274+
glLinkProgram(watermark_shader);
275+
276+
glGetProgramiv(watermark_shader, GL_LINK_STATUS, &success);
277+
if (!success)
278+
{
279+
char info_log[512];
280+
glGetProgramInfoLog(watermark_shader, 512, nullptr, info_log);
281+
LOG_ERROR(Render_OpenGL, "Shader program linking failed: {}", info_log);
282+
watermark_shader = 0;
283+
}
284+
glDeleteShader(vertex_shader);
285+
glDeleteShader(fragment_shader);
286+
287+
glBindVertexArray(0);
288+
glBindBuffer(GL_ARRAY_BUFFER, 0);
289+
glBindTexture(GL_TEXTURE_2D, 0);
290+
}
291+
292+
void RendererOpenGL::RenderWatermark()
293+
{
294+
if (!watermark_texture || !watermark_shader || !watermark_vao || !watermark_vbo)
295+
{
296+
return;
297+
}
298+
299+
if (!watermark_timer_started)
300+
{
301+
watermark_start_time = std::chrono::steady_clock::now();
302+
watermark_timer_started = true;
303+
}
304+
305+
std::chrono::steady_clock::time_point current_time = std::chrono::steady_clock::now();
306+
long long elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(current_time - watermark_start_time).count();
307+
308+
float fade_alpha;
309+
const float fade_duration = 300.0f;
310+
if (elapsed_seconds >= fade_duration)
311+
{
312+
fade_alpha = 0.0f;
313+
watermark_show = false;
314+
}
315+
else
316+
{
317+
float progress = elapsed_seconds / fade_duration;
318+
float eased = 0.5f * (1.0f + cosf(progress * 3.14159f));
319+
fade_alpha = 0.8f * eased;
320+
}
321+
322+
GLint current_program, current_vao, current_buffer, current_texture, current_active_texture;
323+
glGetIntegerv(GL_CURRENT_PROGRAM, &current_program);
324+
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &current_vao);
325+
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_buffer);
326+
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
327+
glGetIntegerv(GL_ACTIVE_TEXTURE, &current_active_texture);
328+
329+
glUseProgram(0);
330+
glBindVertexArray(0);
331+
glBindBuffer(GL_ARRAY_BUFFER, 0);
332+
glActiveTexture(GL_TEXTURE0);
333+
glBindTexture(GL_TEXTURE_2D, 0);
334+
335+
glDisable(GL_DEPTH_TEST);
336+
glDisable(GL_CULL_FACE);
337+
glDisable(GL_STENCIL_TEST);
338+
glDisable(GL_SCISSOR_TEST);
339+
glEnable(GL_BLEND);
340+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
341+
342+
const Layout::FramebufferLayout & layout = emu_window.GetFramebufferLayout();
343+
float watermark_scale = 0.14f;
344+
float aspect_ratio = (float)watermark_height / (float)watermark_width;
345+
float watermark_screen_width = layout.width * watermark_scale;
346+
float watermark_screen_height = watermark_screen_width * aspect_ratio;
347+
348+
float padding = 18.0f;
349+
float x = layout.width - watermark_screen_width - padding;
350+
float y = padding;
351+
352+
float ndc_left = (x / layout.width) * 2.0f - 1.0f;
353+
float ndc_right = ((x + watermark_screen_width) / layout.width) * 2.0f - 1.0f;
354+
float ndc_bottom = (y / layout.height) * 2.0f - 1.0f;
355+
float ndc_top = ((y + watermark_screen_height) / layout.height) * 2.0f - 1.0f;
356+
357+
float vertices[] = {
358+
ndc_left, ndc_bottom, 0.0f, 1.0f, // Bottom-left
359+
ndc_right, ndc_bottom, 1.0f, 1.0f, // Bottom-right
360+
ndc_left, ndc_top, 0.0f, 0.0f, // Top-left
361+
362+
ndc_right, ndc_bottom, 1.0f, 1.0f, // Bottom-right
363+
ndc_right, ndc_top, 1.0f, 0.0f, // Top-right
364+
ndc_left, ndc_top, 0.0f, 0.0f // Top-left
365+
};
366+
367+
glUseProgram(watermark_shader);
368+
glBindVertexArray(watermark_vao);
369+
glBindBuffer(GL_ARRAY_BUFFER, watermark_vbo);
370+
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
371+
glActiveTexture(GL_TEXTURE0);
372+
glBindTexture(GL_TEXTURE_2D, watermark_texture);
373+
374+
GLint tex_uniform = glGetUniformLocation(watermark_shader, "watermarkTexture");
375+
GLint alpha_uniform = glGetUniformLocation(watermark_shader, "alpha");
376+
if (tex_uniform != -1)
377+
{
378+
glUniform1i(tex_uniform, 0);
379+
}
380+
381+
if (alpha_uniform != -1)
382+
{
383+
glUniform1f(alpha_uniform, fade_alpha);
384+
}
385+
386+
while (glGetError() != GL_NO_ERROR) {}
387+
388+
glDrawArrays(GL_TRIANGLES, 0, 6);
389+
glUseProgram(current_program);
390+
glBindVertexArray(current_vao);
391+
glBindBuffer(GL_ARRAY_BUFFER, current_buffer);
392+
glActiveTexture(current_active_texture);
393+
glBindTexture(GL_TEXTURE_2D, current_texture);
394+
}
395+
396+
void RendererOpenGL::CleanupWatermark()
397+
{
398+
if (watermark_texture)
399+
{
400+
glDeleteTextures(1, &watermark_texture);
401+
watermark_texture = 0;
402+
}
403+
404+
if (watermark_shader)
405+
{
406+
glDeleteProgram(watermark_shader);
407+
watermark_shader = 0;
408+
}
409+
410+
if (watermark_vao)
411+
{
412+
glDeleteVertexArrays(1, &watermark_vao);
413+
watermark_vao = 0;
414+
}
415+
416+
if (watermark_vbo)
417+
{
418+
glDeleteBuffers(1, &watermark_vbo);
419+
watermark_vbo = 0;
420+
}
421+
}
422+
165423
void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
166424
const Layout::FramebufferLayout& layout, void* dst) {
167425
GLint old_read_fb;
168-
GLint old_draw_fb;
426+
GLint old_draw_fb;
169427
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
170428
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
171429

src/yuzu_video_core/renderer_opengl/renderer_opengl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class RendererOpenGL final : public VideoCore::RendererBase {
5353
private:
5454
void AddTelemetryFields();
5555

56+
void InitializeWatermark();
57+
void RenderWatermark();
58+
void CleanupWatermark();
5659
void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
5760
const Layout::FramebufferLayout& layout, void* dst);
5861
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
@@ -69,6 +72,15 @@ class RendererOpenGL final : public VideoCore::RendererBase {
6972
OGLFramebuffer screenshot_framebuffer;
7073
OGLFramebuffer capture_framebuffer;
7174
OGLRenderbuffer capture_renderbuffer;
75+
GLuint watermark_texture;
76+
GLuint watermark_vao;
77+
GLuint watermark_vbo;
78+
GLuint watermark_shader;
79+
int watermark_width;
80+
int watermark_height;
81+
std::chrono::steady_clock::time_point watermark_start_time;
82+
bool watermark_timer_started;
83+
bool watermark_show;
7284

7385
std::unique_ptr<BlitScreen> blit_screen;
7486
std::unique_ptr<BlitScreen> blit_applet;

0 commit comments

Comments
 (0)