| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- #include "CFBO.h"
- #include <algorithm>
- #include "WallpaperEngine/Logging/Log.h"
- using namespace WallpaperEngine::Render;
- namespace {
- // 3-channel formats get an alpha channel, RGB targets aren't guaranteed to be renderable
- GLint internalFormat (const TextureFormat format) {
- switch (format) {
- case TextureFormat_RG88:
- return GL_RG8;
- case TextureFormat_R8:
- return GL_R8;
- case TextureFormat_RG1616f:
- return GL_RG16F;
- case TextureFormat_R16f:
- return GL_R16F;
- case TextureFormat_RGBA16161616f:
- case TextureFormat_RGB161616f:
- return GL_RGBA16F;
- case TextureFormat_RGBa1010102:
- return GL_RGB10_A2;
- default:
- return GL_RGBA8;
- }
- }
- } // namespace
- CFBO::CFBO (
- std::string name, const TextureFormat format, const uint32_t flags, const float scale, uint32_t realWidth,
- uint32_t realHeight, uint32_t textureWidth, uint32_t textureHeight, const glm::vec4& borderColor,
- const uint32_t mipLevels
- ) :
- m_scale (scale), m_name (std::move (name)), m_format (format), m_flags (flags),
- m_mipLevels (std::max (mipLevels, 1u)) {
- constexpr GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
- glGenFramebuffers (1, &this->m_framebuffer);
- glBindFramebuffer (GL_FRAMEBUFFER, this->m_framebuffer);
- glGenTextures (1, &this->m_texture);
- glBindTexture (GL_TEXTURE_2D, this->m_texture);
- for (uint32_t level = 0; level < this->m_mipLevels; level++) {
- glTexImage2D (
- GL_TEXTURE_2D, level, internalFormat (format), std::max (textureWidth >> level, 1u),
- std::max (textureHeight >> level, 1u), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr
- );
- }
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_mipLevels - 1);
- #if !NDEBUG
- glObjectLabel (GL_TEXTURE, this->m_texture, -1, this->m_name.c_str ());
- #endif /* DEBUG */
- if (flags & TextureFlags_ClampUVs) {
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- } else if (flags & TextureFlags_ClampUVsBorder) {
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- // without this the border defaults to transparent black, which GL_LINEAR filtering smears
- // into the last edge texel; this keeps out-of-bounds areas (letterboxing, zoomed-out scaling) solid
- glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &borderColor.x);
- } else {
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- }
- if (flags & TextureFlags_NoInterpolation) {
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- } else if (this->m_mipLevels > 1) {
- // WE samples its mipmapped frame buffer trilinear without anisotropy
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- } else {
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- }
- if (this->m_mipLevels == 1) {
- glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 8.0f);
- }
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->m_texture, 0);
- glDrawBuffers (1, drawBuffers);
- if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE && internalFormat (format) != GL_RGBA8) {
- sLog.error ("FBO ", this->m_name, " can't render to format ", format, ", falling back to RGBA8");
- for (uint32_t level = 0; level < this->m_mipLevels; level++) {
- glTexImage2D (
- GL_TEXTURE_2D, level, GL_RGBA8, std::max (textureWidth >> level, 1u),
- std::max (textureHeight >> level, 1u), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr
- );
- }
- this->m_format = TextureFormat_ARGB8888;
- }
- if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- sLog.exception ("Framebuffers are not properly set");
- }
- // must start transparent: the scene clear color is often opaque and would make empty layer
- // areas render as solid rectangles
- GLfloat previousClearColor[4] = {};
- glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
- glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
- glClear (GL_COLOR_BUFFER_BIT);
- glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
- this->m_resolution = { textureWidth, textureHeight, realWidth, realHeight };
- const auto frame = std::make_shared<Frame> ();
- frame->frameNumber = 0;
- frame->frametime = 0;
- frame->height1 = textureHeight;
- frame->height2 = realHeight;
- frame->width1 = textureWidth;
- frame->width2 = realWidth;
- frame->x = 0;
- frame->y = 0;
- this->m_frames.push_back (frame);
- }
- CFBO::~CFBO () {
- if (this->m_depthbuffer != GL_NONE) {
- glDeleteRenderbuffers (1, &this->m_depthbuffer);
- }
- glDeleteTextures (1, &this->m_texture);
- glDeleteFramebuffers (1, &this->m_framebuffer);
- }
- void CFBO::attachDepthBuffer () {
- if (this->m_depthbuffer != GL_NONE) {
- return;
- }
- GLint previous = 0;
- glGetIntegerv (GL_FRAMEBUFFER_BINDING, &previous);
- glGenRenderbuffers (1, &this->m_depthbuffer);
- glBindRenderbuffer (GL_RENDERBUFFER, this->m_depthbuffer);
- glRenderbufferStorage (
- GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, static_cast<GLsizei> (this->m_resolution.x),
- static_cast<GLsizei> (this->m_resolution.y)
- );
- glBindRenderbuffer (GL_RENDERBUFFER, 0);
- glBindFramebuffer (GL_FRAMEBUFFER, this->m_framebuffer);
- glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->m_depthbuffer);
- if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- sLog.error ("FBO ", this->m_name, " can't take a depth buffer");
- glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
- glDeleteRenderbuffers (1, &this->m_depthbuffer);
- this->m_depthbuffer = GL_NONE;
- }
- glBindFramebuffer (GL_FRAMEBUFFER, previous);
- }
- void CFBO::generateMipmaps () const {
- if (this->m_mipLevels < 2) {
- return;
- }
- glBindTexture (GL_TEXTURE_2D, this->m_texture);
- glGenerateMipmap (GL_TEXTURE_2D);
- }
- uint32_t CFBO::getMipLevels () const { return this->m_mipLevels; }
- const std::string& CFBO::getName () const { return this->m_name; }
- const float& CFBO::getScale () const { return this->m_scale; }
- TextureFormat CFBO::getFormat () const { return this->m_format; }
- uint32_t CFBO::getFlags () const { return this->m_flags; }
- GLuint CFBO::getFramebuffer () const { return this->m_framebuffer; }
- GLuint CFBO::getDepthbuffer () const { return this->m_depthbuffer; }
- GLuint CFBO::getTextureID (uint32_t imageIndex) const { return this->m_texture; }
- uint32_t CFBO::getTextureWidth (uint32_t imageIndex) const { return this->m_resolution.x; }
- uint32_t CFBO::getTextureHeight (uint32_t imageIndex) const { return this->m_resolution.y; }
- uint32_t CFBO::getRealWidth () const { return this->m_resolution.z; }
- uint32_t CFBO::getRealHeight () const { return this->m_resolution.w; }
- const std::vector<FrameSharedPtr>& CFBO::getFrames () const { return this->m_frames; }
- const glm::vec4* CFBO::getResolution () const { return &this->m_resolution; }
- bool CFBO::isAnimated () const { return false; }
- uint32_t CFBO::getSpritesheetCols () const {
- return 0; // FBOs don't have spritesheets
- }
- uint32_t CFBO::getSpritesheetRows () const {
- return 0; // FBOs don't have spritesheets
- }
- uint32_t CFBO::getSpritesheetFrames () const {
- return 0; // FBOs don't have spritesheets
- }
- float CFBO::getSpritesheetDuration () const {
- return 0.0f; // FBOs don't have spritesheets
- }
- void CFBO::incrementUsageCount () const { }
- void CFBO::decrementUsageCount () const { }
- void CFBO::update () const { }
- bool CFBO::isReady () const { return true; }
- std::optional<glm::vec4> CFBO::parseColor (const std::string& value) {
- std::string hex = value;
- if (!hex.empty () && hex[0] == '#') {
- hex = hex.substr (1);
- }
- if (hex.size () != 6 && hex.size () != 8) {
- return std::nullopt;
- }
- unsigned long parsed;
- try {
- std::size_t consumed = 0;
- parsed = std::stoul (hex, &consumed, 16);
- if (consumed != hex.size ()) {
- return std::nullopt;
- }
- } catch (const std::exception&) {
- return std::nullopt;
- }
- const bool hasAlpha = hex.size () == 8;
- const float r = static_cast<float> ((parsed >> (hasAlpha ? 24 : 16)) & 0xFF) / 255.0f;
- const float g = static_cast<float> ((parsed >> (hasAlpha ? 16 : 8)) & 0xFF) / 255.0f;
- const float b = static_cast<float> ((parsed >> (hasAlpha ? 8 : 0)) & 0xFF) / 255.0f;
- const float a = hasAlpha ? static_cast<float> (parsed & 0xFF) / 255.0f : 1.0f;
- return glm::vec4 { r, g, b, a };
- }
- void CFBO::setBorderColor (const glm::vec4& color) const {
- glBindTexture (GL_TEXTURE_2D, this->m_texture);
- glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &color.x);
- }
|