/* * Copyright (c) 2023 - 2025 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tvgGlRenderTarget.h" GlRenderTarget::GlRenderTarget() {} GlRenderTarget::~GlRenderTarget() { reset(); } void GlRenderTarget::init(uint32_t width, uint32_t height, GLint resolveId) { if (mFbo != GL_INVALID_VALUE || width == 0 || height == 0) return; mWidth = width; mHeight = height; //TODO: fbo is used. maybe we can consider the direct rendering with resolveId as well. GL_CHECK(glGenFramebuffers(1, &mFbo)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mFbo)); GL_CHECK(glGenRenderbuffers(1, &mColorBuffer)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer)); GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mWidth, mHeight)); GL_CHECK(glGenRenderbuffers(1, &mDepthStencilBuffer)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer)); GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, mWidth, mHeight)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColorBuffer)); GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilBuffer)); // resolve target GL_CHECK(glGenTextures(1, &mColorTex)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, mColorTex)); GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0)); GL_CHECK(glGenFramebuffers(1, &mResolveFbo)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mResolveFbo)); GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mColorTex, 0)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolveId)); } void GlRenderTarget::reset() { if (mFbo == 0) return; GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); GL_CHECK(glDeleteFramebuffers(1, &mFbo)); GL_CHECK(glDeleteRenderbuffers(1, &mColorBuffer)); GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer)); GL_CHECK(glDeleteFramebuffers(1, &mResolveFbo)); GL_CHECK(glDeleteTextures(1, &mColorTex)); mFbo = GL_INVALID_VALUE; } GlRenderTargetPool::GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight): mMaxWidth(maxWidth), mMaxHeight(maxHeight), mPool() {} GlRenderTargetPool::~GlRenderTargetPool() { for (uint32_t i = 0; i < mPool.count; i++) { delete mPool[i]; } } uint32_t alignPow2(uint32_t value) { uint32_t ret = 1; while (ret < value) { ret <<= 1; } return ret; } GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId) { auto width = vp.w(); auto height = vp.h(); // pow2 align width and height if (width >= mMaxWidth) width = mMaxWidth; else width = alignPow2(width); if (width >= mMaxWidth) width = mMaxWidth; if (height >= mMaxHeight) height = mMaxHeight; else height = alignPow2(height); if (height >= mMaxHeight) height = mMaxHeight; for (uint32_t i = 0; i < mPool.count; i++) { auto rt = mPool[i]; if (rt->getWidth() == width && rt->getHeight() == height) { rt->setViewport(vp); return rt; } } auto rt = new GlRenderTarget(); rt->init(width, height, resolveId); rt->setViewport(vp); mPool.push(rt); return rt; }