// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #include "ui/gl/gl_shader.h" #include "ui/gl/gl_image.h" #include "base/debug_log.h" #include namespace Ui::GL { [[nodiscard]] bool IsOpenGLES() { const auto current = QOpenGLContext::currentContext(); Assert(current != nullptr); return (current->format().renderableType() == QSurfaceFormat::OpenGLES); } QString VertexShader(const std::vector &parts) { const auto version = IsOpenGLES() ? QString("#version 100\nprecision highp float;\n") : QString("#version 120\n"); const auto accumulate = [&](auto proj) { return ranges::accumulate(parts, QString(), std::plus<>(), proj); }; return version + R"( attribute vec2 position; )" + accumulate(&ShaderPart::header) + R"( void main() { vec4 result = vec4(position, 0., 1.); )" + accumulate(&ShaderPart::body) + R"( gl_Position = result; } )"; } QString FragmentShader(const std::vector &parts) { const auto version = IsOpenGLES() ? QString("#version 100\nprecision highp float;\n") : QString("#version 120\n"); const auto accumulate = [&](auto proj) { return ranges::accumulate(parts, QString(), std::plus<>(), proj); }; return version + accumulate(&ShaderPart::header) + R"( void main() { vec4 result = vec4(0., 0., 0., 0.); )" + accumulate(&ShaderPart::body) + R"( gl_FragColor = result; } )"; } ShaderPart VertexPassTextureCoord(char prefix) { const auto name = prefix + QString("_texcoord"); return { .header = R"( attribute vec2 )" + name + R"(In; varying vec2 )" + name + ";\n", .body = R"( )" + name + " = " + name + "In;\n", }; } ShaderPart FragmentSampleARGB32Texture() { return { .header = R"( varying vec2 v_texcoord; uniform sampler2D s_texture; )", .body = R"( result = texture2D(s_texture, v_texcoord); )" + (kSwizzleRedBlue ? R"( result = vec4(result.b, result.g, result.r, result.a); )" : QString()), }; } QString FragmentYUV2RGB() { return R"( result = vec4( 1.164 * y + 1.596 * v, 1.164 * y - 0.392 * u - 0.813 * v, 1.164 * y + 2.17 * u, 1.); )"; } ShaderPart FragmentSampleYUV420Texture() { return { .header = R"( varying vec2 v_texcoord; uniform sampler2D y_texture; uniform sampler2D u_texture; uniform sampler2D v_texture; )", .body = R"( float y = texture2D(y_texture, v_texcoord).a - 0.0625; float u = texture2D(u_texture, v_texcoord).a - 0.5; float v = texture2D(v_texture, v_texcoord).a - 0.5; )" + FragmentYUV2RGB(), }; } ShaderPart FragmentSampleNV12Texture() { return { .header = R"( varying vec2 v_texcoord; uniform sampler2D y_texture; uniform sampler2D uv_texture; )", .body = R"( float y = texture2D(y_texture, v_texcoord).a - 0.0625; vec2 uv = texture2D(uv_texture, v_texcoord).rg - vec2(0.5, 0.5); float u = uv.x; float v = uv.y; )" + FragmentYUV2RGB(), }; } ShaderPart FragmentGlobalOpacity() { return { .header = R"( uniform float g_opacity; )", .body = R"( result *= g_opacity; )", }; } ShaderPart VertexViewportTransform() { return { .header = R"( uniform vec2 viewport; vec4 transform(vec4 position) { return vec4( vec2(-1, -1) + 2. * position.xy / viewport, position.z, position.w); } )", .body = R"( result = transform(result); )", }; } ShaderPart FragmentRoundCorners() { return { .header = R"( uniform vec4 roundRect; uniform vec2 radiusOutline; uniform vec4 roundBg; uniform vec4 outlineFg; vec2 roundedCorner() { vec2 rectHalf = roundRect.zw / 2.; vec2 rectCenter = roundRect.xy + rectHalf; vec2 fromRectCenter = abs(gl_FragCoord.xy - rectCenter); vec2 vectorRadius = radiusOutline.xx + vec2(0.5, 0.5); vec2 fromCenterWithRadius = fromRectCenter + vectorRadius; vec2 fromRoundingCenter = max(fromCenterWithRadius, rectHalf) - rectHalf; float rounded = length(fromRoundingCenter) - radiusOutline.x; float outline = rounded + radiusOutline.y; return vec2( 1. - smoothstep(0., 1., rounded), 1. - (smoothstep(0., 1., outline) * outlineFg.a)); } )", .body = R"( vec2 roundOutline = roundedCorner(); result = result * roundOutline.y + vec4(outlineFg.rgb, 1) * (1. - roundOutline.y); result = result * roundOutline.x + roundBg * (1. - roundOutline.x); )", }; } ShaderPart FragmentStaticColor() { return { .header = R"( uniform vec4 s_color; )", .body = R"( result = s_color; )", }; } not_null MakeShader( not_null program, QOpenGLShader::ShaderType type, const QString &source) { const auto result = new QOpenGLShader(type, program); if (!result->compileSourceCode(source)) { LOG(("Shader Compilation Failed: %1, error %2." ).arg(source ).arg(result->log())); } program->addShader(result); return result; } Program LinkProgram( not_null program, std::variant> vertex, std::variant> fragment) { const auto vertexAsSource = v::is(vertex); const auto v = vertexAsSource ? MakeShader( program, QOpenGLShader::Vertex, v::get(vertex)) : v::get>(vertex); if (!vertexAsSource) { program->addShader(v); } const auto fragmentAsSource = v::is(fragment); const auto f = fragmentAsSource ? MakeShader( program, QOpenGLShader::Fragment, v::get(fragment)) : v::get>(fragment); if (!fragmentAsSource) { program->addShader(f); } if (!program->link()) { LOG(("Shader Link Failed: %1.").arg(program->log())); } return { v, f }; } } // namespace Ui::GL