CFBO.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #include "CFBO.h"
  2. #include <algorithm>
  3. #include "WallpaperEngine/Logging/Log.h"
  4. using namespace WallpaperEngine::Render;
  5. namespace {
  6. // 3-channel formats get an alpha channel, RGB targets aren't guaranteed to be renderable
  7. GLint internalFormat (const TextureFormat format) {
  8. switch (format) {
  9. case TextureFormat_RG88:
  10. return GL_RG8;
  11. case TextureFormat_R8:
  12. return GL_R8;
  13. case TextureFormat_RG1616f:
  14. return GL_RG16F;
  15. case TextureFormat_R16f:
  16. return GL_R16F;
  17. case TextureFormat_RGBA16161616f:
  18. case TextureFormat_RGB161616f:
  19. return GL_RGBA16F;
  20. case TextureFormat_RGBa1010102:
  21. return GL_RGB10_A2;
  22. default:
  23. return GL_RGBA8;
  24. }
  25. }
  26. } // namespace
  27. CFBO::CFBO (
  28. std::string name, const TextureFormat format, const uint32_t flags, const float scale, uint32_t realWidth,
  29. uint32_t realHeight, uint32_t textureWidth, uint32_t textureHeight, const glm::vec4& borderColor,
  30. const uint32_t mipLevels
  31. ) :
  32. m_scale (scale), m_name (std::move (name)), m_format (format), m_flags (flags),
  33. m_mipLevels (std::max (mipLevels, 1u)) {
  34. constexpr GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
  35. glGenFramebuffers (1, &this->m_framebuffer);
  36. glBindFramebuffer (GL_FRAMEBUFFER, this->m_framebuffer);
  37. glGenTextures (1, &this->m_texture);
  38. glBindTexture (GL_TEXTURE_2D, this->m_texture);
  39. for (uint32_t level = 0; level < this->m_mipLevels; level++) {
  40. glTexImage2D (
  41. GL_TEXTURE_2D, level, internalFormat (format), std::max (textureWidth >> level, 1u),
  42. std::max (textureHeight >> level, 1u), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr
  43. );
  44. }
  45. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_mipLevels - 1);
  46. #if !NDEBUG
  47. glObjectLabel (GL_TEXTURE, this->m_texture, -1, this->m_name.c_str ());
  48. #endif /* DEBUG */
  49. if (flags & TextureFlags_ClampUVs) {
  50. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  51. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  52. } else if (flags & TextureFlags_ClampUVsBorder) {
  53. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
  54. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
  55. // without this the border defaults to transparent black, which GL_LINEAR filtering smears
  56. // into the last edge texel; this keeps out-of-bounds areas (letterboxing, zoomed-out scaling) solid
  57. glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &borderColor.x);
  58. } else {
  59. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  60. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  61. }
  62. if (flags & TextureFlags_NoInterpolation) {
  63. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  64. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  65. } else if (this->m_mipLevels > 1) {
  66. // WE samples its mipmapped frame buffer trilinear without anisotropy
  67. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  68. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  69. } else {
  70. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  71. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  72. }
  73. if (this->m_mipLevels == 1) {
  74. glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 8.0f);
  75. }
  76. glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->m_texture, 0);
  77. glDrawBuffers (1, drawBuffers);
  78. if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE && internalFormat (format) != GL_RGBA8) {
  79. sLog.error ("FBO ", this->m_name, " can't render to format ", format, ", falling back to RGBA8");
  80. for (uint32_t level = 0; level < this->m_mipLevels; level++) {
  81. glTexImage2D (
  82. GL_TEXTURE_2D, level, GL_RGBA8, std::max (textureWidth >> level, 1u),
  83. std::max (textureHeight >> level, 1u), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr
  84. );
  85. }
  86. this->m_format = TextureFormat_ARGB8888;
  87. }
  88. if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
  89. sLog.exception ("Framebuffers are not properly set");
  90. }
  91. // must start transparent: the scene clear color is often opaque and would make empty layer
  92. // areas render as solid rectangles
  93. GLfloat previousClearColor[4] = {};
  94. glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
  95. glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
  96. glClear (GL_COLOR_BUFFER_BIT);
  97. glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
  98. this->m_resolution = { textureWidth, textureHeight, realWidth, realHeight };
  99. const auto frame = std::make_shared<Frame> ();
  100. frame->frameNumber = 0;
  101. frame->frametime = 0;
  102. frame->height1 = textureHeight;
  103. frame->height2 = realHeight;
  104. frame->width1 = textureWidth;
  105. frame->width2 = realWidth;
  106. frame->x = 0;
  107. frame->y = 0;
  108. this->m_frames.push_back (frame);
  109. }
  110. CFBO::~CFBO () {
  111. if (this->m_depthbuffer != GL_NONE) {
  112. glDeleteRenderbuffers (1, &this->m_depthbuffer);
  113. }
  114. glDeleteTextures (1, &this->m_texture);
  115. glDeleteFramebuffers (1, &this->m_framebuffer);
  116. }
  117. void CFBO::attachDepthBuffer () {
  118. if (this->m_depthbuffer != GL_NONE) {
  119. return;
  120. }
  121. GLint previous = 0;
  122. glGetIntegerv (GL_FRAMEBUFFER_BINDING, &previous);
  123. glGenRenderbuffers (1, &this->m_depthbuffer);
  124. glBindRenderbuffer (GL_RENDERBUFFER, this->m_depthbuffer);
  125. glRenderbufferStorage (
  126. GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, static_cast<GLsizei> (this->m_resolution.x),
  127. static_cast<GLsizei> (this->m_resolution.y)
  128. );
  129. glBindRenderbuffer (GL_RENDERBUFFER, 0);
  130. glBindFramebuffer (GL_FRAMEBUFFER, this->m_framebuffer);
  131. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->m_depthbuffer);
  132. if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
  133. sLog.error ("FBO ", this->m_name, " can't take a depth buffer");
  134. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
  135. glDeleteRenderbuffers (1, &this->m_depthbuffer);
  136. this->m_depthbuffer = GL_NONE;
  137. }
  138. glBindFramebuffer (GL_FRAMEBUFFER, previous);
  139. }
  140. void CFBO::generateMipmaps () const {
  141. if (this->m_mipLevels < 2) {
  142. return;
  143. }
  144. glBindTexture (GL_TEXTURE_2D, this->m_texture);
  145. glGenerateMipmap (GL_TEXTURE_2D);
  146. }
  147. uint32_t CFBO::getMipLevels () const { return this->m_mipLevels; }
  148. const std::string& CFBO::getName () const { return this->m_name; }
  149. const float& CFBO::getScale () const { return this->m_scale; }
  150. TextureFormat CFBO::getFormat () const { return this->m_format; }
  151. uint32_t CFBO::getFlags () const { return this->m_flags; }
  152. GLuint CFBO::getFramebuffer () const { return this->m_framebuffer; }
  153. GLuint CFBO::getDepthbuffer () const { return this->m_depthbuffer; }
  154. GLuint CFBO::getTextureID (uint32_t imageIndex) const { return this->m_texture; }
  155. uint32_t CFBO::getTextureWidth (uint32_t imageIndex) const { return this->m_resolution.x; }
  156. uint32_t CFBO::getTextureHeight (uint32_t imageIndex) const { return this->m_resolution.y; }
  157. uint32_t CFBO::getRealWidth () const { return this->m_resolution.z; }
  158. uint32_t CFBO::getRealHeight () const { return this->m_resolution.w; }
  159. const std::vector<FrameSharedPtr>& CFBO::getFrames () const { return this->m_frames; }
  160. const glm::vec4* CFBO::getResolution () const { return &this->m_resolution; }
  161. bool CFBO::isAnimated () const { return false; }
  162. uint32_t CFBO::getSpritesheetCols () const {
  163. return 0; // FBOs don't have spritesheets
  164. }
  165. uint32_t CFBO::getSpritesheetRows () const {
  166. return 0; // FBOs don't have spritesheets
  167. }
  168. uint32_t CFBO::getSpritesheetFrames () const {
  169. return 0; // FBOs don't have spritesheets
  170. }
  171. float CFBO::getSpritesheetDuration () const {
  172. return 0.0f; // FBOs don't have spritesheets
  173. }
  174. void CFBO::incrementUsageCount () const { }
  175. void CFBO::decrementUsageCount () const { }
  176. void CFBO::update () const { }
  177. bool CFBO::isReady () const { return true; }
  178. std::optional<glm::vec4> CFBO::parseColor (const std::string& value) {
  179. std::string hex = value;
  180. if (!hex.empty () && hex[0] == '#') {
  181. hex = hex.substr (1);
  182. }
  183. if (hex.size () != 6 && hex.size () != 8) {
  184. return std::nullopt;
  185. }
  186. unsigned long parsed;
  187. try {
  188. std::size_t consumed = 0;
  189. parsed = std::stoul (hex, &consumed, 16);
  190. if (consumed != hex.size ()) {
  191. return std::nullopt;
  192. }
  193. } catch (const std::exception&) {
  194. return std::nullopt;
  195. }
  196. const bool hasAlpha = hex.size () == 8;
  197. const float r = static_cast<float> ((parsed >> (hasAlpha ? 24 : 16)) & 0xFF) / 255.0f;
  198. const float g = static_cast<float> ((parsed >> (hasAlpha ? 16 : 8)) & 0xFF) / 255.0f;
  199. const float b = static_cast<float> ((parsed >> (hasAlpha ? 8 : 0)) & 0xFF) / 255.0f;
  200. const float a = hasAlpha ? static_cast<float> (parsed & 0xFF) / 255.0f : 1.0f;
  201. return glm::vec4 { r, g, b, a };
  202. }
  203. void CFBO::setBorderColor (const glm::vec4& color) const {
  204. glBindTexture (GL_TEXTURE_2D, this->m_texture);
  205. glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &color.x);
  206. }