CWallpaper.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. #include <vector>
  2. #include "CWallpaper.h"
  3. #include "WallpaperEngine/Logging/Log.h"
  4. #include "WallpaperEngine/Render/Wallpapers/CScene.h"
  5. #include "WallpaperEngine/Render/Wallpapers/CVideo.h"
  6. #include "WallpaperEngine/Render/Wallpapers/CWeb.h"
  7. #include "WallpaperEngine/Data/Model/Project.h"
  8. #include "WallpaperEngine/Data/Model/Wallpaper.h"
  9. using namespace WallpaperEngine::Render;
  10. CWallpaper::CWallpaper (
  11. const Wallpaper& wallpaperData, RenderContext& context, AudioContext& audioContext,
  12. const WallpaperState::TextureUVsScaling& scalingMode, const uint32_t& clampMode
  13. ) :
  14. ContextAware (context), FBOProvider (nullptr), m_wallpaperData (wallpaperData), m_audioContext (audioContext),
  15. m_state (scalingMode, clampMode) {
  16. glGenVertexArrays (1, &this->m_vaoBuffer);
  17. glBindVertexArray (this->m_vaoBuffer);
  18. this->setupShaders ();
  19. constexpr GLfloat texCoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
  20. // inverted positions so the final texture is rendered properly
  21. constexpr GLfloat position[] = { -1.0f, 1.0f, 0.0f, 1.0, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f,
  22. -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f };
  23. // GL_DYNAMIC_DRAW: render() rewrites this buffer's contents (via glBufferSubData) whenever
  24. // the wallpaper's UVs change. The attrib pointer/enable state below is captured by the VAO
  25. // once here rather than redone every render() call - only the buffer's contents change later.
  26. glGenBuffers (1, &this->m_texCoordBuffer);
  27. glBindBuffer (GL_ARRAY_BUFFER, this->m_texCoordBuffer);
  28. glBufferData (GL_ARRAY_BUFFER, sizeof (texCoords), texCoords, GL_DYNAMIC_DRAW);
  29. glEnableVertexAttribArray (this->a_TexCoord);
  30. glVertexAttribPointer (this->a_TexCoord, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  31. // Static for the object's lifetime, so its attrib setup also only needs to happen once here.
  32. glGenBuffers (1, &this->m_positionBuffer);
  33. glBindBuffer (GL_ARRAY_BUFFER, this->m_positionBuffer);
  34. glBufferData (GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW);
  35. glEnableVertexAttribArray (this->a_Position);
  36. glVertexAttribPointer (this->a_Position, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
  37. }
  38. CWallpaper::~CWallpaper () {
  39. // programs here only ever have 2 shaders attached (vertex + fragment)
  40. GLuint attachedShaders[2];
  41. GLsizei attachedCount = 0;
  42. glGetAttachedShaders (this->m_shader, 2, &attachedCount, attachedShaders);
  43. for (auto i = 0; i < attachedCount; i++) {
  44. glDeleteShader (attachedShaders[i]);
  45. }
  46. glDeleteProgram (this->m_shader);
  47. glDeleteBuffers (1, &this->m_texCoordBuffer);
  48. glDeleteBuffers (1, &this->m_positionBuffer);
  49. glDeleteVertexArrays (1, &this->m_vaoBuffer);
  50. }
  51. const AssetLocator& CWallpaper::getAssetLocator () const { return *this->m_wallpaperData.project.assetLocator; }
  52. const Wallpaper& CWallpaper::getWallpaperData () const { return this->m_wallpaperData; }
  53. GLuint CWallpaper::getWallpaperFramebuffer () const { return this->m_sceneFBO->getFramebuffer (); }
  54. GLuint CWallpaper::getWallpaperTexture () const { return this->m_sceneFBO->getTextureID (0); }
  55. void CWallpaper::setupShaders () {
  56. const GLuint vertexShaderID = glCreateShader (GL_VERTEX_SHADER);
  57. const char* sourcePointer = "#version 330\n"
  58. "precision highp float;\n"
  59. "in vec3 a_Position;\n"
  60. "in vec2 a_TexCoord;\n"
  61. "out vec2 v_TexCoord;\n"
  62. "void main () {\n"
  63. "gl_Position = vec4 (a_Position, 1.0);\n"
  64. "v_TexCoord = a_TexCoord;\n"
  65. "}";
  66. glShaderSource (vertexShaderID, 1, &sourcePointer, nullptr);
  67. glCompileShader (vertexShaderID);
  68. GLint result = GL_FALSE;
  69. int infoLogLength = 0;
  70. glGetShaderiv (vertexShaderID, GL_COMPILE_STATUS, &result);
  71. glGetShaderiv (vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
  72. if (infoLogLength > 0) {
  73. const auto logBuffer = new char[infoLogLength + 1];
  74. memset (logBuffer, 0, infoLogLength + 1);
  75. glGetShaderInfoLog (vertexShaderID, infoLogLength, nullptr, logBuffer);
  76. const std::string message = logBuffer;
  77. delete[] logBuffer;
  78. sLog.exception (message);
  79. }
  80. const GLuint fragmentShaderID = glCreateShader (GL_FRAGMENT_SHADER);
  81. sourcePointer = "#version 330\n"
  82. "precision highp float;\n"
  83. "uniform sampler2D g_Texture0;\n"
  84. "in vec2 v_TexCoord;\n"
  85. "out vec4 out_FragColor;\n"
  86. "void main () {\n"
  87. "out_FragColor = texture (g_Texture0, v_TexCoord);\n"
  88. "}";
  89. glShaderSource (fragmentShaderID, 1, &sourcePointer, nullptr);
  90. glCompileShader (fragmentShaderID);
  91. result = GL_FALSE;
  92. infoLogLength = 0;
  93. glGetShaderiv (fragmentShaderID, GL_COMPILE_STATUS, &result);
  94. glGetShaderiv (fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
  95. if (infoLogLength > 0) {
  96. const auto logBuffer = new char[infoLogLength + 1];
  97. memset (logBuffer, 0, infoLogLength + 1);
  98. glGetShaderInfoLog (fragmentShaderID, infoLogLength, nullptr, logBuffer);
  99. const std::string message = logBuffer;
  100. delete[] logBuffer;
  101. sLog.exception (message);
  102. }
  103. this->m_shader = glCreateProgram ();
  104. glAttachShader (this->m_shader, vertexShaderID);
  105. glAttachShader (this->m_shader, fragmentShaderID);
  106. glLinkProgram (this->m_shader);
  107. result = GL_FALSE;
  108. infoLogLength = 0;
  109. glGetProgramiv (this->m_shader, GL_LINK_STATUS, &result);
  110. glGetProgramiv (this->m_shader, GL_INFO_LOG_LENGTH, &infoLogLength);
  111. if (infoLogLength > 0) {
  112. const auto logBuffer = new char[infoLogLength + 1];
  113. memset (logBuffer, 0, infoLogLength + 1);
  114. glGetProgramInfoLog (this->m_shader, infoLogLength, nullptr, logBuffer);
  115. const std::string message = logBuffer;
  116. delete[] logBuffer;
  117. sLog.exception (message);
  118. }
  119. // shaders can be detached and deleted once linked into the program
  120. glDetachShader (this->m_shader, vertexShaderID);
  121. glDetachShader (this->m_shader, fragmentShaderID);
  122. glDeleteShader (vertexShaderID);
  123. glDeleteShader (fragmentShaderID);
  124. this->g_Texture0 = glGetUniformLocation (this->m_shader, "g_Texture0");
  125. this->a_Position = glGetAttribLocation (this->m_shader, "a_Position");
  126. this->a_TexCoord = glGetAttribLocation (this->m_shader, "a_TexCoord");
  127. }
  128. void CWallpaper::setDestinationFramebuffer (GLuint framebuffer) { this->m_destFramebuffer = framebuffer; }
  129. void CWallpaper::setSpanInfo (const SpanInfo& spanInfo) { this->m_spanInfo = spanInfo; }
  130. const CWallpaper::SpanInfo* CWallpaper::getSpanInfo () const {
  131. return this->m_spanInfo.has_value () ? &this->m_spanInfo.value () : nullptr;
  132. }
  133. void CWallpaper::updateUVs (const glm::ivec4& viewport, const bool vflip) {
  134. if (this->m_state.hasChanged (viewport, vflip, this->getWidth (), this->getHeight ())) {
  135. this->m_state.updateState (viewport, vflip, this->getWidth (), this->getHeight ());
  136. }
  137. }
  138. void CWallpaper::render (
  139. const glm::ivec4& viewport, const bool vflip, const glm::ivec2& globalPosition, const glm::ivec2& logicalSize
  140. ) {
  141. const uint32_t currentFrame = this->getContext ().getDriver ().getFrameCounter ();
  142. const bool needsSceneRender = (currentFrame != this->m_lastRenderedFrame);
  143. const glm::ivec4 sceneViewport = this->m_spanInfo.has_value ()
  144. ? glm::ivec4 { 0, 0, this->m_spanInfo->totalBounds.z, this->m_spanInfo->totalBounds.w }
  145. : viewport;
  146. #if !NDEBUG
  147. glPushDebugGroup (GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Rendering scene");
  148. #endif /* !NDEBUG */
  149. if (needsSceneRender) {
  150. this->renderFrame (sceneViewport);
  151. this->m_lastRenderedFrame = currentFrame;
  152. }
  153. #if !NDEBUG
  154. glPopDebugGroup ();
  155. glPushDebugGroup (GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Rendering scene to output");
  156. #endif /* !NDEBUG */
  157. float ustart, uend, vstart, vend;
  158. if (this->m_spanInfo.has_value ()) {
  159. // span mode: scale the wallpaper to the bounding box using the normal scaling rules
  160. // (fill/fit/stretch/default), then slice per monitor
  161. const auto& span = this->m_spanInfo.value ();
  162. const float spanW = static_cast<float> (span.totalBounds.z);
  163. const float spanH = static_cast<float> (span.totalBounds.w);
  164. const float spanX = static_cast<float> (span.totalBounds.x);
  165. const float spanY = static_cast<float> (span.totalBounds.y);
  166. this->updateUVs (span.totalBounds, vflip);
  167. auto [baseUstart, baseUend, baseVstart, baseVend] = this->m_state.getTextureUVs ();
  168. // this viewport's relative position within the bounding box [0..1]; logicalSize is in the
  169. // same coordinate space as globalPosition and totalBounds
  170. const float relLeft = (static_cast<float> (globalPosition.x) - spanX) / spanW;
  171. const float relRight = (static_cast<float> (globalPosition.x + logicalSize.x) - spanX) / spanW;
  172. const float relTop = (static_cast<float> (globalPosition.y) - spanY) / spanH;
  173. const float relBottom = (static_cast<float> (globalPosition.y + logicalSize.y) - spanY) / spanH;
  174. // interpolate within the base UVs to get this viewport's slice
  175. const float baseURange = baseUend - baseUstart;
  176. const float baseVRange = baseVend - baseVstart;
  177. ustart = baseUstart + relLeft * baseURange;
  178. uend = baseUstart + relRight * baseURange;
  179. vstart = baseVstart + relTop * baseVRange;
  180. vend = baseVstart + relBottom * baseVRange;
  181. if (this->m_lastRenderedFrame < 5) {
  182. sLog.debug (
  183. "SPAN DEBUG: viewport=", viewport.z, "x", viewport.w, " globalPos=(", globalPosition.x, ",",
  184. globalPosition.y, ")", " span=(", span.totalBounds.x, ",", span.totalBounds.y, ",", span.totalBounds.z,
  185. ",", span.totalBounds.w, ")", " rel=[", relLeft, ",", relRight, "]x[", relTop, ",", relBottom, "]",
  186. " baseUV=[", baseUstart, ",", baseUend, "]x[", baseVstart, ",", baseVend, "]", " finalUV=[", ustart,
  187. ",", uend, "]x[", vstart, ",", vend, "]"
  188. );
  189. }
  190. } else {
  191. updateUVs (viewport, vflip);
  192. auto uvs = this->m_state.getTextureUVs ();
  193. ustart = uvs.ustart;
  194. uend = uvs.uend;
  195. vstart = uvs.vstart;
  196. vend = uvs.vend;
  197. }
  198. const GLfloat texCoords[] = {
  199. ustart, vstart, uend, vstart, ustart, vend, ustart, vend, uend, vstart, uend, vend,
  200. };
  201. glViewport (viewport.x, viewport.y, viewport.z, viewport.w);
  202. glBindFramebuffer (GL_FRAMEBUFFER, this->m_destFramebuffer);
  203. glBindVertexArray (this->m_vaoBuffer);
  204. glDisable (GL_BLEND);
  205. glDisable (GL_DEPTH_TEST);
  206. glDisable (GL_CULL_FACE);
  207. glUseProgram (this->m_shader);
  208. glActiveTexture (GL_TEXTURE0);
  209. glBindTexture (GL_TEXTURE_2D, this->getWallpaperTexture ());
  210. glUniform1i (this->g_Texture0, 0);
  211. if (ustart != this->m_uploadedUstart || uend != this->m_uploadedUend || vstart != this->m_uploadedVstart
  212. || vend != this->m_uploadedVend) {
  213. glBindBuffer (GL_ARRAY_BUFFER, this->m_texCoordBuffer);
  214. glBufferSubData (GL_ARRAY_BUFFER, 0, sizeof (texCoords), texCoords);
  215. this->m_uploadedUstart = ustart;
  216. this->m_uploadedUend = uend;
  217. this->m_uploadedVstart = vstart;
  218. this->m_uploadedVend = vend;
  219. }
  220. glDrawArrays (GL_TRIANGLES, 0, 6);
  221. if (this->getContext ().getApp ().getContext ().settings.render.debug.brightnessLog) {
  222. static uint32_t brightnessFrameCounter = 0;
  223. if ((brightnessFrameCounter++ % 30) == 0) {
  224. std::vector<unsigned char> px (viewport.z * viewport.w * 4);
  225. glReadPixels (
  226. viewport.x, viewport.y, viewport.z, viewport.w, GL_RGBA, GL_UNSIGNED_BYTE, px.data ()
  227. );
  228. uint64_t sum = 0;
  229. for (size_t i = 0; i < px.size (); i += 4) {
  230. sum += px[i] + px[i + 1] + px[i + 2];
  231. }
  232. const double mean = static_cast<double> (sum) / (px.size () / 4 * 3);
  233. sLog.out ("[BRIGHTNESS] frame=", brightnessFrameCounter, " mean=", mean);
  234. }
  235. }
  236. #if !NDEBUG
  237. glPopDebugGroup ();
  238. #endif /* !NDEBUG */
  239. }
  240. void CWallpaper::setPause (bool newState) { }
  241. void CWallpaper::setAudioPolicy (bool muted, std::optional<int> ambientVolume) { }
  242. void CWallpaper::setupFramebuffers () {
  243. const uint32_t width = this->getWidth ();
  244. const uint32_t height = this->getHeight ();
  245. const uint32_t clamp = this->m_state.getClampingMode ();
  246. this->m_sceneFBO = this->create (
  247. "_rt_FullFrameBuffer", TextureFormat_ARGB8888, clamp, 1.0, { width, height }, { width, height },
  248. this->m_cornerColor
  249. );
  250. this->alias ("_rt_MipMappedFrameBuffer", "_rt_FullFrameBuffer");
  251. }
  252. AudioContext& CWallpaper::getAudioContext () const { return this->m_audioContext; }
  253. const WallpaperState& CWallpaper::getState () const { return this->m_state; }
  254. void CWallpaper::setScalingMode (WallpaperState::TextureUVsScaling mode) { this->m_state.setTextureUVsStrategy (mode); }
  255. void CWallpaper::setZoom (float zoom) { this->m_state.setZoom (zoom); }
  256. void CWallpaper::setCornerColor (const glm::vec4& color) {
  257. this->m_cornerColor = color;
  258. if (this->m_sceneFBO != nullptr) {
  259. this->m_sceneFBO->setBorderColor (color);
  260. }
  261. }
  262. std::shared_ptr<const CFBO> CWallpaper::findFBO (const std::string& name) const {
  263. const auto fbo = this->find (name);
  264. if (fbo == nullptr) {
  265. sLog.exception ("Cannot find FBO ", name);
  266. }
  267. return fbo;
  268. }
  269. std::shared_ptr<const CFBO> CWallpaper::getFBO () const { return this->m_sceneFBO; }
  270. std::unique_ptr<CWallpaper> CWallpaper::fromWallpaper (
  271. const Wallpaper& wallpaper, RenderContext& context, AudioContext& audioContext,
  272. const std::filesystem::path& resolvedBackgroundPath, const WallpaperState::TextureUVsScaling& scalingMode,
  273. const uint32_t& clampMode, const glm::ivec2& maxRenderSize
  274. ) {
  275. if (wallpaper.is<Scene> ()) {
  276. return std::make_unique<WallpaperEngine::Render::Wallpapers::CScene> (
  277. wallpaper, context, audioContext, scalingMode, clampMode
  278. );
  279. }
  280. if (wallpaper.is<Video> ()) {
  281. return std::make_unique<WallpaperEngine::Render::Wallpapers::CVideo> (
  282. wallpaper, context, audioContext, scalingMode, clampMode
  283. );
  284. }
  285. if (wallpaper.is<Web> ()) {
  286. return std::make_unique<WallpaperEngine::Render::Wallpapers::CWeb> (
  287. wallpaper, context, audioContext, resolvedBackgroundPath, scalingMode, clampMode, maxRenderSize
  288. );
  289. }
  290. sLog.exception ("Unsupported wallpaper type");
  291. }