gl_shader.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/gl/gl_shader.h"
  8. #include "ui/gl/gl_image.h"
  9. #include "base/debug_log.h"
  10. #include <QtGui/QOpenGLContext>
  11. namespace Ui::GL {
  12. [[nodiscard]] bool IsOpenGLES() {
  13. const auto current = QOpenGLContext::currentContext();
  14. Assert(current != nullptr);
  15. return (current->format().renderableType() == QSurfaceFormat::OpenGLES);
  16. }
  17. QString VertexShader(const std::vector<ShaderPart> &parts) {
  18. const auto version = IsOpenGLES()
  19. ? QString("#version 100\nprecision highp float;\n")
  20. : QString("#version 120\n");
  21. const auto accumulate = [&](auto proj) {
  22. return ranges::accumulate(parts, QString(), std::plus<>(), proj);
  23. };
  24. return version + R"(
  25. attribute vec2 position;
  26. )" + accumulate(&ShaderPart::header) + R"(
  27. void main() {
  28. vec4 result = vec4(position, 0., 1.);
  29. )" + accumulate(&ShaderPart::body) + R"(
  30. gl_Position = result;
  31. }
  32. )";
  33. }
  34. QString FragmentShader(const std::vector<ShaderPart> &parts) {
  35. const auto version = IsOpenGLES()
  36. ? QString("#version 100\nprecision highp float;\n")
  37. : QString("#version 120\n");
  38. const auto accumulate = [&](auto proj) {
  39. return ranges::accumulate(parts, QString(), std::plus<>(), proj);
  40. };
  41. return version + accumulate(&ShaderPart::header) + R"(
  42. void main() {
  43. vec4 result = vec4(0., 0., 0., 0.);
  44. )" + accumulate(&ShaderPart::body) + R"(
  45. gl_FragColor = result;
  46. }
  47. )";
  48. }
  49. ShaderPart VertexPassTextureCoord(char prefix) {
  50. const auto name = prefix + QString("_texcoord");
  51. return {
  52. .header = R"(
  53. attribute vec2 )" + name + R"(In;
  54. varying vec2 )" + name + ";\n",
  55. .body = R"(
  56. )" + name + " = " + name + "In;\n",
  57. };
  58. }
  59. ShaderPart FragmentSampleARGB32Texture() {
  60. return {
  61. .header = R"(
  62. varying vec2 v_texcoord;
  63. uniform sampler2D s_texture;
  64. )",
  65. .body = R"(
  66. result = texture2D(s_texture, v_texcoord);
  67. )" + (kSwizzleRedBlue
  68. ? R"(
  69. result = vec4(result.b, result.g, result.r, result.a);
  70. )" : QString()),
  71. };
  72. }
  73. QString FragmentYUV2RGB() {
  74. return R"(
  75. result = vec4(
  76. 1.164 * y + 1.596 * v,
  77. 1.164 * y - 0.392 * u - 0.813 * v,
  78. 1.164 * y + 2.17 * u,
  79. 1.);
  80. )";
  81. }
  82. ShaderPart FragmentSampleYUV420Texture() {
  83. return {
  84. .header = R"(
  85. varying vec2 v_texcoord;
  86. uniform sampler2D y_texture;
  87. uniform sampler2D u_texture;
  88. uniform sampler2D v_texture;
  89. )",
  90. .body = R"(
  91. float y = texture2D(y_texture, v_texcoord).a - 0.0625;
  92. float u = texture2D(u_texture, v_texcoord).a - 0.5;
  93. float v = texture2D(v_texture, v_texcoord).a - 0.5;
  94. )" + FragmentYUV2RGB(),
  95. };
  96. }
  97. ShaderPart FragmentSampleNV12Texture() {
  98. return {
  99. .header = R"(
  100. varying vec2 v_texcoord;
  101. uniform sampler2D y_texture;
  102. uniform sampler2D uv_texture;
  103. )",
  104. .body = R"(
  105. float y = texture2D(y_texture, v_texcoord).a - 0.0625;
  106. vec2 uv = texture2D(uv_texture, v_texcoord).rg - vec2(0.5, 0.5);
  107. float u = uv.x;
  108. float v = uv.y;
  109. )" + FragmentYUV2RGB(),
  110. };
  111. }
  112. ShaderPart FragmentGlobalOpacity() {
  113. return {
  114. .header = R"(
  115. uniform float g_opacity;
  116. )",
  117. .body = R"(
  118. result *= g_opacity;
  119. )",
  120. };
  121. }
  122. ShaderPart VertexViewportTransform() {
  123. return {
  124. .header = R"(
  125. uniform vec2 viewport;
  126. vec4 transform(vec4 position) {
  127. return vec4(
  128. vec2(-1, -1) + 2. * position.xy / viewport,
  129. position.z,
  130. position.w);
  131. }
  132. )",
  133. .body = R"(
  134. result = transform(result);
  135. )",
  136. };
  137. }
  138. ShaderPart FragmentRoundCorners() {
  139. return {
  140. .header = R"(
  141. uniform vec4 roundRect;
  142. uniform vec2 radiusOutline;
  143. uniform vec4 roundBg;
  144. uniform vec4 outlineFg;
  145. vec2 roundedCorner() {
  146. vec2 rectHalf = roundRect.zw / 2.;
  147. vec2 rectCenter = roundRect.xy + rectHalf;
  148. vec2 fromRectCenter = abs(gl_FragCoord.xy - rectCenter);
  149. vec2 vectorRadius = radiusOutline.xx + vec2(0.5, 0.5);
  150. vec2 fromCenterWithRadius = fromRectCenter + vectorRadius;
  151. vec2 fromRoundingCenter = max(fromCenterWithRadius, rectHalf)
  152. - rectHalf;
  153. float rounded = length(fromRoundingCenter) - radiusOutline.x;
  154. float outline = rounded + radiusOutline.y;
  155. return vec2(
  156. 1. - smoothstep(0., 1., rounded),
  157. 1. - (smoothstep(0., 1., outline) * outlineFg.a));
  158. }
  159. )",
  160. .body = R"(
  161. vec2 roundOutline = roundedCorner();
  162. result = result * roundOutline.y
  163. + vec4(outlineFg.rgb, 1) * (1. - roundOutline.y);
  164. result = result * roundOutline.x + roundBg * (1. - roundOutline.x);
  165. )",
  166. };
  167. }
  168. ShaderPart FragmentStaticColor() {
  169. return {
  170. .header = R"(
  171. uniform vec4 s_color;
  172. )",
  173. .body = R"(
  174. result = s_color;
  175. )",
  176. };
  177. }
  178. not_null<QOpenGLShader*> MakeShader(
  179. not_null<QOpenGLShaderProgram*> program,
  180. QOpenGLShader::ShaderType type,
  181. const QString &source) {
  182. const auto result = new QOpenGLShader(type, program);
  183. if (!result->compileSourceCode(source)) {
  184. LOG(("Shader Compilation Failed: %1, error %2.").arg(
  185. source,
  186. result->log()));
  187. }
  188. program->addShader(result);
  189. return result;
  190. }
  191. Program LinkProgram(
  192. not_null<QOpenGLShaderProgram*> program,
  193. std::variant<QString, not_null<QOpenGLShader*>> vertex,
  194. std::variant<QString, not_null<QOpenGLShader*>> fragment) {
  195. const auto vertexAsSource = v::is<QString>(vertex);
  196. const auto v = vertexAsSource
  197. ? MakeShader(
  198. program,
  199. QOpenGLShader::Vertex,
  200. v::get<QString>(vertex))
  201. : v::get<not_null<QOpenGLShader*>>(vertex);
  202. if (!vertexAsSource) {
  203. program->addShader(v);
  204. }
  205. const auto fragmentAsSource = v::is<QString>(fragment);
  206. const auto f = fragmentAsSource
  207. ? MakeShader(
  208. program,
  209. QOpenGLShader::Fragment,
  210. v::get<QString>(fragment))
  211. : v::get<not_null<QOpenGLShader*>>(fragment);
  212. if (!fragmentAsSource) {
  213. program->addShader(f);
  214. }
  215. if (!program->link()) {
  216. LOG(("Shader Link Failed: %1.").arg(program->log()));
  217. }
  218. return { v, f };
  219. }
  220. } // namespace Ui::GL