mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
gl_engine: rewrite the blending shader
Completely rewrite the GL backend shader to support advance blending and correct the blending behavior. Now the GL backend has the same blending result as SKIA and CanvasRenderingContext2D in browers did. The formula is referenced from: https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators issue: https://github.com/thorvg/thorvg/issues/2799
This commit is contained in:
parent
3fbb55440a
commit
3c884232fd
13 changed files with 256 additions and 150 deletions
|
@ -165,7 +165,7 @@ struct GlRadialGradientBlock
|
|||
struct GlCompositor : RenderCompositor
|
||||
{
|
||||
RenderRegion bbox = {};
|
||||
|
||||
BlendMethod blend = BlendMethod::Normal;
|
||||
GlCompositor(const RenderRegion& box) : bbox(box) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -101,13 +101,17 @@ int32_t GlProgram::getAttributeLocation(const char* name)
|
|||
|
||||
int32_t GlProgram::getUniformLocation(const char* name)
|
||||
{
|
||||
if (mUniformLocation.count(name)) return mUniformLocation[name];
|
||||
GL_CHECK(int32_t location = glGetUniformLocation(mProgramObj, name));
|
||||
mUniformLocation[name] = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
int32_t GlProgram::getUniformBlockIndex(const char* name)
|
||||
{
|
||||
if (mUniformBlock.count(name)) return mUniformBlock[name];
|
||||
GL_CHECK(int32_t index = glGetUniformBlockIndex(mProgramObj, name));
|
||||
mUniformBlock[name] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include "tvgGlShader.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
class GlProgram
|
||||
{
|
||||
public:
|
||||
|
@ -49,6 +52,8 @@ public:
|
|||
|
||||
private:
|
||||
uint32_t mProgramObj;
|
||||
std::unordered_map<std::string, int32_t> mUniformLocation;
|
||||
std::unordered_map<std::string, int32_t> mUniformBlock;
|
||||
static uint32_t mCurrentProgram;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
#include "tvgGlRenderPass.h"
|
||||
#include "tvgGlRenderTask.h"
|
||||
|
||||
GlRenderPass::GlRenderPass(GlRenderTarget* fbo): mFbo(fbo), mTasks(), mDrawDepth(0) {}
|
||||
GlRenderPass::GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport): mFbo(fbo), mViewport(viewport), mTasks(), mDrawDepth(0) {}
|
||||
|
||||
GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mTasks(), mDrawDepth(0)
|
||||
GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mViewport(other.mViewport), mTasks(), mDrawDepth(0)
|
||||
{
|
||||
mTasks.push(other.mTasks);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class GlProgram;
|
|||
class GlRenderPass
|
||||
{
|
||||
public:
|
||||
GlRenderPass(GlRenderTarget* fbo);
|
||||
GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport);
|
||||
GlRenderPass(GlRenderPass&& other);
|
||||
|
||||
~GlRenderPass();
|
||||
|
@ -45,7 +45,7 @@ public:
|
|||
|
||||
GLuint getTextureId() { return mFbo->getColorTexture(); }
|
||||
|
||||
const RenderRegion& getViewport() const { return mFbo->getViewport(); }
|
||||
const RenderRegion& getViewport() const { return mViewport; }
|
||||
|
||||
uint32_t getFboWidth() const { return mFbo->getWidth(); }
|
||||
|
||||
|
@ -63,7 +63,7 @@ public:
|
|||
|
||||
auto task = new T(program, targetFbo, mFbo, std::move(mTasks));
|
||||
|
||||
const auto& vp = mFbo->getViewport();
|
||||
const auto& vp = mViewport;
|
||||
|
||||
task->setRenderSize(static_cast<uint32_t>(vp.w), static_cast<uint32_t>(vp.h));
|
||||
|
||||
|
@ -77,6 +77,7 @@ public:
|
|||
GlRenderTarget* getFbo() { return mFbo; }
|
||||
private:
|
||||
GlRenderTarget* mFbo;
|
||||
RenderRegion mViewport;
|
||||
Array<GlRenderTask*> mTasks = {};
|
||||
int32_t mDrawDepth = 0;
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@ GlRenderTarget::~GlRenderTarget()
|
|||
if (mFbo == 0) return;
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
GL_CHECK(glDeleteFramebuffers(1, &mFbo));
|
||||
GL_CHECK(glDeleteFramebuffers(1, &mResolveFbo));
|
||||
|
||||
if (mColorTex != 0) {
|
||||
GL_CHECK(glDeleteTextures(1, &mColorTex));
|
||||
|
@ -36,6 +37,9 @@ GlRenderTarget::~GlRenderTarget()
|
|||
if (mDepthStencilBuffer != 0) {
|
||||
GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer));
|
||||
}
|
||||
if (mColorBuffer!= 0) {
|
||||
GL_CHECK(glDeleteRenderbuffers(1, &mColorBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
void GlRenderTarget::init(GLint resolveId)
|
||||
|
@ -119,15 +123,15 @@ GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLui
|
|||
for (uint32_t i = 0; i < mPool.count; i++) {
|
||||
auto rt = mPool[i];
|
||||
|
||||
if (rt->getWidth() == width && rt->getHeight() == height) {
|
||||
rt->setViewport(vp);
|
||||
if (rt->getWidth() == width && rt->getHeight() == height && !rt->isInUse()) {
|
||||
rt->setInUse(true);
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
auto rt = new GlRenderTarget(width, height);
|
||||
rt->init(resolveId);
|
||||
rt->setViewport(vp);
|
||||
rt->setInUse(true);
|
||||
mPool.push(rt);
|
||||
return rt;
|
||||
}
|
||||
|
|
|
@ -45,16 +45,19 @@ public:
|
|||
const RenderRegion& getViewport() const { return mViewport; }
|
||||
|
||||
bool invalid() const { return mFbo == GL_INVALID_VALUE; }
|
||||
bool isInUse() const { return mInUse; }
|
||||
void setInUse(bool inUse) { mInUse = inUse; }
|
||||
|
||||
private:
|
||||
uint32_t mWidth = 0;
|
||||
uint32_t mHeight = 0;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
RenderRegion mViewport{};
|
||||
GLuint mFbo = GL_INVALID_VALUE;
|
||||
GLuint mColorBuffer = 0;
|
||||
GLuint mDepthStencilBuffer = 0;
|
||||
GLuint mResolveFbo = 0;
|
||||
GLuint mColorTex = 0;
|
||||
bool mInUse = false;
|
||||
};
|
||||
|
||||
class GlRenderTargetPool {
|
||||
|
|
|
@ -332,12 +332,11 @@ void GlSimpleBlendTask::run()
|
|||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask)
|
||||
: GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask) {}
|
||||
GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask)
|
||||
: GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mComposeTask(composeTask) {}
|
||||
|
||||
GlComplexBlendTask::~GlComplexBlendTask()
|
||||
{
|
||||
delete mStencilTask;
|
||||
delete mComposeTask;
|
||||
}
|
||||
|
||||
|
@ -349,8 +348,8 @@ void GlComplexBlendTask::run()
|
|||
GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId()));
|
||||
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId()));
|
||||
|
||||
GL_CHECK(glViewport(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h));
|
||||
GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h));
|
||||
GL_CHECK(glViewport(0, 0, mViewport.w, mViewport.h));
|
||||
GL_CHECK(glScissor(0, 0, mViewport.w, mViewport.h));
|
||||
|
||||
const auto& vp = getViewport();
|
||||
|
||||
|
@ -358,21 +357,6 @@ void GlComplexBlendTask::run()
|
|||
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId()));
|
||||
|
||||
GL_CHECK(glEnable(GL_STENCIL_TEST));
|
||||
GL_CHECK(glColorMask(0, 0, 0, 0));
|
||||
GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x0, 0xFF));
|
||||
GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP));
|
||||
|
||||
GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x0, 0xFF));
|
||||
GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP));
|
||||
|
||||
|
||||
mStencilTask->run();
|
||||
|
||||
GL_CHECK(glColorMask(1, 1, 1, 1));
|
||||
GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF));
|
||||
GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE));
|
||||
|
||||
GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO));
|
||||
|
||||
GlRenderTask::run();
|
||||
|
@ -383,7 +367,6 @@ void GlComplexBlendTask::run()
|
|||
|
||||
void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth)
|
||||
{
|
||||
mStencilTask->normalizeDrawDepth(maxDepth);
|
||||
GlRenderTask::normalizeDrawDepth(maxDepth);
|
||||
}
|
||||
|
||||
|
|
|
@ -206,17 +206,21 @@ private:
|
|||
class GlComplexBlendTask: public GlRenderTask
|
||||
{
|
||||
public:
|
||||
GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask);
|
||||
GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask);
|
||||
~GlComplexBlendTask() override;
|
||||
|
||||
void run() override;
|
||||
|
||||
void normalizeDrawDepth(int32_t maxDepth) override;
|
||||
|
||||
void setViewport(const RenderRegion& viewport) { mViewport = viewport; }
|
||||
void setCopyViewport(const RenderRegion& viewport) { mCopyViewport = viewport; }
|
||||
private:
|
||||
GlRenderTarget* mDstFbo;
|
||||
GlRenderTarget* mDstCopyFbo;
|
||||
GlRenderTask* mStencilTask;
|
||||
GlComposeTask* mComposeTask;
|
||||
RenderRegion mViewport = {};
|
||||
RenderRegion mCopyViewport = {};
|
||||
};
|
||||
|
||||
class GlGaussianBlurTask: public GlRenderTask
|
||||
|
|
|
@ -156,6 +156,8 @@ void GlRenderer::initShaders()
|
|||
mPrograms.push(new GlProgram(MASK_VERT_SHADER, SOFT_LIGHT_BLEND_FRAG));
|
||||
mPrograms.push(new GlProgram(MASK_VERT_SHADER, DIFFERENCE_BLEND_FRAG));
|
||||
mPrograms.push(new GlProgram(MASK_VERT_SHADER, EXCLUSION_BLEND_FRAG));
|
||||
mPrograms.push(new GlProgram(MASK_VERT_SHADER, LIGHTEN_BLEND_FRAG));
|
||||
mPrograms.push(new GlProgram(MASK_VERT_SHADER, DARKEN_BLEND_FRAG));
|
||||
|
||||
// effects
|
||||
mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL));
|
||||
|
@ -174,7 +176,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
|
||||
bbox.intersect(vp);
|
||||
|
||||
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
|
||||
auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds());
|
||||
|
||||
if (complexBlend) {
|
||||
vp = currentPass()->getViewport();
|
||||
|
@ -254,11 +256,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
if (stencilTask) currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode));
|
||||
else currentPass()->addRenderTask(task);
|
||||
|
||||
if (complexBlend) {
|
||||
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
|
||||
sdata.geometry.draw(task, &mGpuBuffer, flag);
|
||||
endBlendingCompose(task, sdata.geometry.matrix);
|
||||
}
|
||||
if (complexBlend) endBlendingCompose(mBlendMethod);
|
||||
}
|
||||
|
||||
|
||||
|
@ -290,7 +288,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
return;
|
||||
}
|
||||
|
||||
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
|
||||
auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
|
@ -441,11 +439,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
currentPass()->addRenderTask(task);
|
||||
}
|
||||
|
||||
if (complexBlend) {
|
||||
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
|
||||
sdata.geometry.draw(task, &mGpuBuffer, flag);
|
||||
endBlendingCompose(task, sdata.geometry.matrix);
|
||||
}
|
||||
if (complexBlend) endBlendingCompose(mBlendMethod);
|
||||
}
|
||||
|
||||
|
||||
|
@ -539,7 +533,7 @@ GlRenderPass* GlRenderer::currentPass()
|
|||
return mRenderPassStack.last();
|
||||
}
|
||||
|
||||
bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds)
|
||||
bool GlRenderer::beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds)
|
||||
{
|
||||
if (vp.w == 0 || vp.h == 0) return false;
|
||||
|
||||
|
@ -547,18 +541,18 @@ bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bound
|
|||
|
||||
if (bounds.w == 0 || bounds.h == 0) return false;
|
||||
|
||||
if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false;
|
||||
if (blend == BlendMethod::Normal || blend == BlendMethod::Add) return false;
|
||||
|
||||
if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
|
||||
|
||||
auto blendFbo = mBlendPool[0]->getRenderTarget(bounds);
|
||||
|
||||
mRenderPassStack.push(new GlRenderPass(blendFbo));
|
||||
mRenderPassStack.push(new GlRenderPass(blendFbo, bounds));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix)
|
||||
void GlRenderer::endBlendingCompose(BlendMethod blend)
|
||||
{
|
||||
auto blendPass = mRenderPassStack.last();
|
||||
mRenderPassStack.pop();
|
||||
|
@ -571,34 +565,10 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
|
||||
auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp);
|
||||
|
||||
{
|
||||
const auto& passVp = currentPass()->getViewport();
|
||||
auto task = new GlComplexBlendTask(getBlendProgram(blend), currentPass()->getFbo(), dstCopyFbo, composeTask);
|
||||
|
||||
auto x = vp.x;
|
||||
auto y = vp.y;
|
||||
auto w = vp.w;
|
||||
auto h = vp.h;
|
||||
|
||||
stencilTask->setViewport({x, passVp.h - y - h, w, h});
|
||||
}
|
||||
|
||||
stencilTask->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
{
|
||||
// set view matrix
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, matrix);
|
||||
uint32_t viewOffset = mGpuBuffer.push(matrix44, 16 * sizeof(float), true);
|
||||
stencilTask->addBindResource(GlBindingResource{
|
||||
0,
|
||||
stencilTask->getProgram()->getUniformBlockIndex("Matrix"),
|
||||
mGpuBuffer.getBufferId(),
|
||||
viewOffset,
|
||||
16 * sizeof(float),
|
||||
});
|
||||
}
|
||||
|
||||
auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask);
|
||||
task->setViewport(currentPass()->getViewport());
|
||||
task->setCopyViewport(blendPass->getViewport());
|
||||
|
||||
prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight());
|
||||
|
||||
|
@ -613,9 +583,9 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
delete(blendPass);
|
||||
}
|
||||
|
||||
GlProgram* GlRenderer::getBlendProgram()
|
||||
GlProgram* GlRenderer::getBlendProgram(BlendMethod blend)
|
||||
{
|
||||
switch (mBlendMethod) {
|
||||
switch (blend) {
|
||||
case BlendMethod::Multiply: return mPrograms[RT_MultiplyBlend];
|
||||
case BlendMethod::Screen: return mPrograms[RT_ScreenBlend];
|
||||
case BlendMethod::Overlay: return mPrograms[RT_OverlayBlend];
|
||||
|
@ -625,6 +595,8 @@ GlProgram* GlRenderer::getBlendProgram()
|
|||
case BlendMethod::SoftLight: return mPrograms[RT_SoftLightBlend];
|
||||
case BlendMethod::Difference: return mPrograms[RT_DifferenceBlend];
|
||||
case BlendMethod::Exclusion: return mPrograms[RT_ExclusionBlend];
|
||||
case BlendMethod::Lighten: return mPrograms[RT_LightenBlend];
|
||||
case BlendMethod::Darken: return mPrograms[RT_DarkenBlend];
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -711,6 +683,41 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
|
|||
task->setViewport({x, static_cast<int32_t>((passVp.h - y - h)), w, h});
|
||||
}
|
||||
|
||||
void GlRenderer::prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp)
|
||||
{
|
||||
auto x = 0;
|
||||
auto y = 0;
|
||||
auto w = vp.w;
|
||||
auto h = vp.h;
|
||||
|
||||
Array<float> vertices(4 * 2);
|
||||
// left top
|
||||
vertices.push(x);
|
||||
vertices.push(y);
|
||||
// left bottom
|
||||
vertices.push(x);
|
||||
vertices.push(y + h);
|
||||
// right top
|
||||
vertices.push(x + w);
|
||||
vertices.push(y);
|
||||
// right bottom
|
||||
vertices.push(x + w);
|
||||
vertices.push(y + h);
|
||||
|
||||
Array<uint32_t> indices(6);
|
||||
indices.push(0);
|
||||
indices.push(1);
|
||||
indices.push(2);
|
||||
indices.push(2);
|
||||
indices.push(1);
|
||||
indices.push(3);
|
||||
|
||||
uint32_t vertexOffset = mGpuBuffer.push(vertices.data, vertices.count * sizeof(float));
|
||||
uint32_t indexOffset = mGpuBuffer.pushIndex(indices.data, indices.count * sizeof(uint32_t));
|
||||
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), vertexOffset});
|
||||
task->setDrawRange(indexOffset, indices.count);
|
||||
}
|
||||
|
||||
|
||||
void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
||||
{
|
||||
|
@ -723,6 +730,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
auto maskPass = mRenderPassStack.last();
|
||||
mRenderPassStack.pop();
|
||||
|
||||
bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox);
|
||||
|
||||
GlProgram* program = nullptr;
|
||||
switch(cmp->method) {
|
||||
case MaskMethod::Alpha: program = mPrograms[RT_MaskAlpha]; break;
|
||||
|
@ -758,6 +767,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
currentPass()->addRenderTask(compose_task);
|
||||
}
|
||||
|
||||
if (complexBlend) endBlendingCompose(glCmp->blend);
|
||||
|
||||
maskPass->getFbo()->setInUse(false);
|
||||
selfPass->getFbo()->setInUse(false);
|
||||
|
||||
delete(selfPass);
|
||||
delete(maskPass);
|
||||
} else {
|
||||
|
@ -766,6 +780,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
mRenderPassStack.pop();
|
||||
|
||||
if (!renderPass->isEmpty()) {
|
||||
bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox);
|
||||
|
||||
auto task = renderPass->endRenderPass<GlDrawBlitTask>(mPrograms[RT_Image], currentPass()->getFboId());
|
||||
task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight());
|
||||
|
@ -793,10 +809,22 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
4 * sizeof(uint32_t),
|
||||
});
|
||||
|
||||
task->addBindResource(GlBindingResource{
|
||||
1,
|
||||
task->getProgram()->getUniformBlockIndex("ColorInfo"),
|
||||
mGpuBuffer.getBufferId(),
|
||||
mGpuBuffer.push(info, 4 * sizeof(uint32_t), true),
|
||||
4 * sizeof(uint32_t),
|
||||
});
|
||||
|
||||
// texture id
|
||||
task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")});
|
||||
task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
|
||||
currentPass()->addRenderTask(std::move(task));
|
||||
|
||||
if (complexBlend) endBlendingCompose(glCmp->blend);
|
||||
renderPass->getFbo()->setInUse(false);
|
||||
|
||||
}
|
||||
delete(renderPass);
|
||||
}
|
||||
|
@ -902,7 +930,7 @@ bool GlRenderer::preRender()
|
|||
|
||||
currentContext();
|
||||
if (mPrograms.empty()) initShaders();
|
||||
mRenderPassStack.push(new GlRenderPass(&mRootTarget));
|
||||
mRenderPassStack.push(new GlRenderPass(&mRootTarget, RenderRegion{0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -940,13 +968,14 @@ bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
|
|||
}
|
||||
|
||||
auto glCmp = static_cast<GlCompositor*>(cmp);
|
||||
glCmp->blend = mBlendMethod;
|
||||
|
||||
if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) {
|
||||
auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox);
|
||||
mRenderPassStack.push(new GlRenderPass(renderTarget));
|
||||
mRenderPassStack.push(new GlRenderPass(renderTarget, glCmp->bbox));
|
||||
} else {
|
||||
// empty render pass
|
||||
mRenderPassStack.push(new GlRenderPass(nullptr));
|
||||
mRenderPassStack.push(new GlRenderPass(nullptr, glCmp->bbox));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1265,6 +1294,10 @@ bool GlRenderer::renderImage(void* data)
|
|||
|
||||
auto bbox = sdata->geometry.viewport;
|
||||
|
||||
bool complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata->geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
bbox.intersect(vp);
|
||||
|
||||
if (bbox.w <= 0 || bbox.h <= 0) return true;
|
||||
|
@ -1277,7 +1310,10 @@ bool GlRenderer::renderImage(void* data)
|
|||
|
||||
if (!sdata->clips.empty()) drawClip(sdata->clips);
|
||||
|
||||
auto task = new GlRenderTask(mPrograms[RT_Image]);
|
||||
GlRenderTask* task = nullptr;
|
||||
if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Image]);
|
||||
else task = new GlRenderTask(mPrograms[RT_Image]);
|
||||
|
||||
task->setDrawDepth(drawDepth);
|
||||
|
||||
if (!sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image)) {
|
||||
|
@ -1285,10 +1321,6 @@ bool GlRenderer::renderImage(void* data)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
// matrix buffer
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, sdata->geometry.matrix);
|
||||
|
@ -1319,11 +1351,7 @@ bool GlRenderer::renderImage(void* data)
|
|||
|
||||
currentPass()->addRenderTask(task);
|
||||
|
||||
if (complexBlend) {
|
||||
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
|
||||
sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image);
|
||||
endBlendingCompose(task, sdata->geometry.matrix);
|
||||
}
|
||||
if (complexBlend) endBlendingCompose(mBlendMethod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1566,4 +1594,4 @@ GlRenderer* GlRenderer::gen(TVG_UNUSED uint32_t threads)
|
|||
}
|
||||
|
||||
return new GlRenderer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
RT_SoftLightBlend,
|
||||
RT_DifferenceBlend,
|
||||
RT_ExclusionBlend,
|
||||
RT_LightenBlend,
|
||||
RT_DarkenBlend,
|
||||
|
||||
RT_GaussianVert,
|
||||
RT_GaussianHorz,
|
||||
|
@ -113,12 +115,13 @@ private:
|
|||
|
||||
GlRenderPass* currentPass();
|
||||
|
||||
bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds);
|
||||
void endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix);
|
||||
GlProgram* getBlendProgram();
|
||||
bool beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds);
|
||||
void endBlendingCompose(BlendMethod blend);
|
||||
GlProgram* getBlendProgram(BlendMethod blend);
|
||||
|
||||
void prepareBlitTask(GlBlitTask* task);
|
||||
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight);
|
||||
void prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp);
|
||||
void endRenderPass(RenderCompositor* cmp);
|
||||
|
||||
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform);
|
||||
|
|
|
@ -598,131 +598,199 @@ const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER(
|
|||
\
|
||||
in vec2 vUV; \
|
||||
out vec4 FragColor; \
|
||||
|
||||
// 1/1024.
|
||||
const float kEhCloseEnoughHalf = 0.0009765625;
|
||||
)"
|
||||
|
||||
const char* MULTIPLY_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
FragColor = srcColor * dstColor;
|
||||
vec4 src = texture(uSrcTexture, vUV);
|
||||
vec4 dst = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = vec4((1.0 - src.a)*dst.rgb + (1.0 - dst.a)*src.rgb + src.rgb*dst.rgb, src.a + (1.0 - src.a)*dst.a);
|
||||
}
|
||||
)";
|
||||
|
||||
#define SCREEN_BLEND_FUNC R"( \
|
||||
vec4 screenBlend(vec4 srcColor, vec4 dstColor) \
|
||||
{ \
|
||||
return dstColor + srcColor - (dstColor * srcColor); \
|
||||
} \
|
||||
)"
|
||||
|
||||
#define HARD_LIGHT_BLEND_FUNC R"( \
|
||||
vec4 hardLightBlend(vec4 srcColor, vec4 dstColor) \
|
||||
{ \
|
||||
return vec4(srcColor.r < 0.5 ? 2.0 * srcColor.r * dstColor.r : 1.0 - 2.0 * (1.0 - srcColor.r) * (1.0 - dstColor.r), \
|
||||
srcColor.g < 0.5 ? 2.0 * srcColor.g * dstColor.g : 1.0 - 2.0 * (1.0 - srcColor.g) * (1.0 - dstColor.g), \
|
||||
srcColor.b < 0.5 ? 2.0 * srcColor.b * dstColor.b : 1.0 - 2.0 * (1.0 - srcColor.b) * (1.0 - dstColor.b), \
|
||||
1.0); \
|
||||
} \
|
||||
)"
|
||||
|
||||
#define SOFT_LIGHT_BLEND_FUNC R"( \
|
||||
float softLightD(float v) \
|
||||
{ \
|
||||
if (v <= 0.25) return ((16.0 * v - 12.0) * v + 4.0) * v; \
|
||||
else return sqrt(v); \
|
||||
} \
|
||||
)"
|
||||
|
||||
const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER SCREEN_BLEND_FUNC R"(
|
||||
const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
FragColor = screenBlend(srcColor, dstColor);
|
||||
FragColor = srcColor + (1.0 - srcColor) * dstColor;
|
||||
}
|
||||
)";
|
||||
|
||||
const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"(
|
||||
#define BLEND_OVERLAY_FUNC R"(
|
||||
float blend_overlay_component(vec2 s, vec2 d)
|
||||
{
|
||||
return (2.0 * d.x <= d.y) ? 2.0 * s.x * d.x
|
||||
: s.y * d.y - 2.0 * (d.y - d.x) * (s.y - s.x);
|
||||
}
|
||||
|
||||
vec4 blend_overlay(vec4 src, vec4 dst)
|
||||
{
|
||||
vec4 result = vec4(
|
||||
blend_overlay_component(src.ra, dst.ra),
|
||||
blend_overlay_component(src.ga, dst.ga),
|
||||
blend_overlay_component(src.ba, dst.ba),
|
||||
src.a + (1.0 - src.a) * dst.a
|
||||
);
|
||||
|
||||
result.rgb += dst.rgb*(1 - src.a) + src.rgb*(1 - dst.a);
|
||||
|
||||
return result;
|
||||
}
|
||||
)"
|
||||
|
||||
const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
FragColor = hardLightBlend(dstColor, srcColor);
|
||||
|
||||
FragColor = blend_overlay(srcColor, dstColor);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* COLOR_DODGE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
float color_dodge_component(vec2 s, vec2 d)
|
||||
{
|
||||
float dxScale = d.x == 0.0 ? 0.0 : 1.0;
|
||||
float delta = dxScale * min(d.y, abs(s.y - s.x) >= kEhCloseEnoughHalf ? (d.x * s.y / (s.y - s.x)) : d.y);
|
||||
|
||||
return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = vec4(
|
||||
srcColor.r < 1.0 ? dstColor.r / (1.0 - srcColor.r) : (dstColor.r > 0.0 ? 1.0 : 0.0),
|
||||
srcColor.g < 1.0 ? dstColor.g / (1.0 - srcColor.g) : (dstColor.g > 0.0 ? 1.0 : 0.0),
|
||||
srcColor.b < 1.0 ? dstColor.b / (1.0 - srcColor.b) : (dstColor.b > 0.0 ? 1.0 : 0.0),
|
||||
1.0
|
||||
color_dodge_component(srcColor.ra, dstColor.ra),
|
||||
color_dodge_component(srcColor.ga, dstColor.ga),
|
||||
color_dodge_component(srcColor.ba, dstColor.ba),
|
||||
srcColor.a + (1.0 - srcColor.a) * dstColor.a
|
||||
);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* COLOR_BURN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
float color_burn_component(vec2 s, vec2 d)
|
||||
{
|
||||
float dyTerm = d.y == d.x ? d.y : 0.0;
|
||||
float delta = abs(s.x) >= kEhCloseEnoughHalf ? d.y - min(d.y, ((d.y - d.x) * s.y / s.x)) : dyTerm;
|
||||
|
||||
return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = vec4(
|
||||
srcColor.r > 0.0 ? (1.0 - (1.0 - dstColor.r) / srcColor.r) : (dstColor.r < 1.0 ? 0.0 : 1.0),
|
||||
srcColor.g > 0.0 ? (1.0 - (1.0 - dstColor.g) / srcColor.g) : (dstColor.g < 1.0 ? 0.0 : 1.0),
|
||||
srcColor.b > 0.0 ? (1.0 - (1.0 - dstColor.b) / srcColor.b) : (dstColor.b < 1.0 ? 0.0 : 1.0),
|
||||
1.0
|
||||
color_burn_component(srcColor.ra, dstColor.ra),
|
||||
color_burn_component(srcColor.ga, dstColor.ga),
|
||||
color_burn_component(srcColor.ba, dstColor.ba),
|
||||
srcColor.a + (1.0 - srcColor.a) * dstColor.a
|
||||
);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"(
|
||||
const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
FragColor = hardLightBlend(srcColor, dstColor);
|
||||
|
||||
FragColor = blend_overlay(dstColor, srcColor);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER SOFT_LIGHT_BLEND_FUNC R"(
|
||||
const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
float soft_light_component(vec2 s, vec2 d)
|
||||
{
|
||||
if (2.0 * s.x <= s.y) {
|
||||
return (d.x * d.x * (s.y - 2.0 * s.x) / d.y) + (1.0 - d.y) * s.x + d.x * (-s.y + 2.0 * s.x + 1.0);
|
||||
} else if(4.0 * d.x <= d.y) {
|
||||
float DSqd = d.x * d.x;
|
||||
float DCub = DSqd * d.x;
|
||||
float DaSqd = d.y * d.y;
|
||||
float DaCub = DaSqd * d.y;
|
||||
|
||||
return (DaSqd * (s.x - d.x * (3.0 * s.y - 6.0 * s.x - 1.0)) + 12.0 * d.y * DSqd * (s.y - 2.0 * s.x) - 16.0 * DCub * (s.y - 2.0 * s.x) - DaCub * s.x) / DaSqd;
|
||||
} else {
|
||||
return d.x * (s.y - 2.0 * s.x + 1.0) + s.x - sqrt(d.y * d.x) * (s.y - 2.0 * s.x) - d.y * s.x;
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = vec4(
|
||||
srcColor.r <= 0.5 ? dstColor.r - (1.0 - 2.0 * srcColor.r) * dstColor.r * (1.0 - dstColor.r) : dstColor.r + (2.0 * srcColor.r - 1.0) * (softLightD(dstColor.r) - dstColor.r),
|
||||
srcColor.g <= 0.5 ? dstColor.g - (1.0 - 2.0 * srcColor.g) * dstColor.g * (1.0 - dstColor.g) : dstColor.g + (2.0 * srcColor.g - 1.0) * (softLightD(dstColor.g) - dstColor.g),
|
||||
srcColor.b <= 0.5 ? dstColor.b - (1.0 - 2.0 * srcColor.b) * dstColor.b * (1.0 - dstColor.b) : dstColor.b + (2.0 * srcColor.b - 1.0) * (softLightD(dstColor.b) - dstColor.b),
|
||||
1.0
|
||||
);
|
||||
FragColor = dstColor.a == 0.0 ? srcColor : vec4(
|
||||
soft_light_component(srcColor.ra, dstColor.ra),
|
||||
soft_light_component(srcColor.ga, dstColor.ga),
|
||||
soft_light_component(srcColor.ba, dstColor.ba),
|
||||
srcColor.a + (1.0 - srcColor.a) * dstColor.a
|
||||
);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* DIFFERENCE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
vec4 src = texture(uSrcTexture, vUV);
|
||||
vec4 dst = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = abs(dstColor - srcColor);
|
||||
FragColor = vec4(src.rgb + dst.rgb - 2.0 * min(src.rgb * dst.a, dst.rgb * src.a), src.a + (1.0 - src.a) * dst.a);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* EXCLUSION_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
|
||||
void main()
|
||||
{
|
||||
vec4 src = texture(uSrcTexture, vUV);
|
||||
vec4 dst = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = vec4(dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb, src.a + (1.0 - src.a) * dst.a);
|
||||
}
|
||||
)";
|
||||
|
||||
#define LD_BLEND_FUNC R"(
|
||||
vec4 blendSrcOver(vec4 src, vec4 dst) { return src + (1 - src.a)*dst; }
|
||||
|
||||
vec4 blendLightDarken(float mode, vec4 src, vec4 dst)
|
||||
{
|
||||
vec4 a = blendSrcOver(src, dst);
|
||||
vec3 b = (1.0 - dst.a) * src.rgb + dst.rgb;
|
||||
a.rgb = mode * min(a.rgb * mode, b.rgb * mode);
|
||||
|
||||
return a;
|
||||
}
|
||||
)"
|
||||
|
||||
const char* LIGHTEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = dstColor + srcColor - (2.0 * dstColor * srcColor);
|
||||
FragColor = blendLightDarken(-1.0, srcColor, dstColor);
|
||||
}
|
||||
)";
|
||||
|
||||
const char* DARKEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"(
|
||||
void main()
|
||||
{
|
||||
vec4 srcColor = texture(uSrcTexture, vUV);
|
||||
vec4 dstColor = texture(uDstTexture, vUV);
|
||||
|
||||
FragColor = blendLightDarken(1.0, srcColor, dstColor);
|
||||
}
|
||||
)";
|
||||
|
||||
|
|
|
@ -58,12 +58,15 @@ extern const char* HARD_LIGHT_BLEND_FRAG;
|
|||
extern const char* SOFT_LIGHT_BLEND_FRAG;
|
||||
extern const char* DIFFERENCE_BLEND_FRAG;
|
||||
extern const char* EXCLUSION_BLEND_FRAG;
|
||||
extern const char* EFFECT_VERTEX;
|
||||
extern const char* LIGHTEN_BLEND_FRAG;
|
||||
extern const char* DARKEN_BLEND_FRAG;
|
||||
extern const char* GAUSSIAN_VERTICAL;
|
||||
extern const char* GAUSSIAN_HORIZONTAL;
|
||||
extern const char* EFFECT_VERTEX;
|
||||
extern const char* EFFECT_DROPSHADOW;
|
||||
extern const char* EFFECT_FILL;
|
||||
extern const char* EFFECT_TINT;
|
||||
extern const char* EFFECT_TRITONE;
|
||||
|
||||
|
||||
#endif /* _TVG_GL_SHADERSRC_H_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue