From 047989abcf121fc0c512ef5c3389c78dc2c14f18 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 22 May 2021 19:48:33 +0400 Subject: [PATCH] Fill solid background in OpenGL renderer. --- .../calls/group/calls_group_large_video.cpp | 338 +++++++++++------- Telegram/lib_ui | 2 +- 2 files changed, 209 insertions(+), 131 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp index 728bbda8f9..71346def21 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp @@ -23,12 +23,95 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include +#include +#include +#include namespace Calls::Group { namespace { constexpr auto kShadowMaxAlpha = 80; +const char *FrameVertexShader() { + return R"( +#version 130 +in vec2 position; +in vec2 texcoord; +uniform vec2 viewport; +out vec2 v_texcoord; +vec4 transform(vec2 pos) { + return vec4(vec2(-1, -1) + 2 * pos / viewport, 0., 1.); +} +void main() { + gl_Position = transform(position); + v_texcoord = texcoord; +} +)"; +} + +const char *FrameFragmentShader() { + return R"( +#version 130 +in vec2 v_texcoord; +uniform sampler2D s_texture; +out vec4 fragColor; +void main() { + vec4 color = texture(s_texture, v_texcoord); + fragColor = vec4(color.b, color.g, color.r, color.a); +} +)"; +} + +const char *FillVertexShader() { + return R"( +#version 130 +in vec2 position; +uniform vec2 viewport; +vec4 transform(vec2 pos) { + return vec4(vec2(-1, -1) + 2 * pos / viewport, 0., 1.); +} +void main() { + gl_Position = transform(position); +} +)"; +} + +const char *FillFragmentShader() { + return R"( +#version 130 +uniform vec4 s_color; +out vec4 fragColor; +void main() { + fragColor = s_color; +} +)"; +} + +not_null MakeShader( + not_null program, + QOpenGLShader::ShaderType type, + const char *source) { + const auto result = new QOpenGLShader(type, program); + if (!result->compileSourceCode(source)) { + LOG(("Shader Compilation Failed: %1, error %2." + ).arg(source + ).arg(result->log())); + } + program->addShader(result); + return result; +} + +void LinkProgram( + not_null program, + const char *vertexSource, + const char *fragmentSource) { + MakeShader(program, QOpenGLShader::Vertex, vertexSource); + MakeShader(program, QOpenGLShader::Fragment, fragmentSource); + if (!program->link()) { + LOG(("Shader Link Failed: %1.").arg(program->log())); + } +} + } // namespace struct LargeVideo::PinButton { @@ -55,6 +138,10 @@ public: not_null widget, not_null f) override; + void deinit( + not_null widget, + not_null f) override; + void resize( not_null widget, not_null f, @@ -66,17 +153,14 @@ public: not_null f) override; private: - void deinit(not_null context); - const not_null _owner; std::array _textures = {}; - GLuint _vertexBuffer = 0; - GLuint _vertexShader = 0; - GLuint _fragmentShader = 0; - GLuint _shaderProgram = 0; + std::optional _frameBuffer; + std::optional _fillBuffer; + std::optional _frameProgram; + std::optional _fillProgram; qint64 _key = 0; - QMetaObject::Connection _connection; }; @@ -98,15 +182,6 @@ LargeVideo::RendererGL::~RendererGL() { void LargeVideo::RendererGL::init( not_null widget, not_null f) { - if (_connection) { - QObject::disconnect(_connection); - } - const auto context = widget->context(); - _connection = QObject::connect( - context, - &QOpenGLContext::aboutToBeDestroyed, - [=] { deinit(context); }); - f->glGenTextures(3, _textures.data()); for (const auto texture : _textures) { f->glBindTexture(GL_TEXTURE_2D, texture); @@ -116,79 +191,32 @@ void LargeVideo::RendererGL::init( f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - f->glGenBuffers(1, &_vertexBuffer); + _frameBuffer.emplace(); + _frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); + _frameBuffer->create(); - const char *vertexShaderSource = R"( -#version 130 -in vec2 position; -in vec2 texcoord; -out vec2 v_texcoord; -void main() { - gl_Position = vec4(position.x, position.y, 0.0, 1.0); - v_texcoord = texcoord; -} -)"; - _vertexShader = f->glCreateShader(GL_VERTEX_SHADER); - f->glShaderSource(_vertexShader, 1, &vertexShaderSource, NULL); - f->glCompileShader(_vertexShader); + _fillBuffer.emplace(); + _fillBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); + _fillBuffer->create(); - { - int success; - char infoLog[512]; - f->glGetShaderiv(_vertexShader, GL_COMPILE_STATUS, &success); - if (!success) { - f->glGetShaderInfoLog(_vertexShader, 512, NULL, infoLog); - } - int a = 0; - } + _frameProgram.emplace(); + LinkProgram(&*_frameProgram, FrameVertexShader(), FrameFragmentShader()); - const char *fragmentShaderSource = R"( -#version 130 -in vec2 v_texcoord; -uniform sampler2D s_texture; -out vec4 fragColor; -void main() { - vec4 color = texture(s_texture, v_texcoord); - fragColor = vec4(color.b, color.g, color.r, color.a); -} -)"; - // ; - _fragmentShader = f->glCreateShader(GL_FRAGMENT_SHADER); - f->glShaderSource(_fragmentShader, 1, &fragmentShaderSource, NULL); - f->glCompileShader(_fragmentShader); - - { - int success; - char infoLog[512]; - f->glGetShaderiv(_fragmentShader, GL_COMPILE_STATUS, &success); - if (!success) { - f->glGetShaderInfoLog(_fragmentShader, 512, NULL, infoLog); - } - int a = 0; - } - - _shaderProgram = f->glCreateProgram(); - f->glAttachShader(_shaderProgram, _vertexShader); - f->glAttachShader(_shaderProgram, _fragmentShader); - f->glLinkProgram(_shaderProgram); - - { - int success; - char infoLog[512]; - f->glGetProgramiv(_shaderProgram, GL_LINK_STATUS, &success); - if (!success) { - f->glGetProgramInfoLog(_shaderProgram, 512, NULL, infoLog); - } - int a = 0; - } + _fillProgram.emplace(); + LinkProgram(&*_fillProgram, FillVertexShader(), FillFragmentShader()); } -void LargeVideo::RendererGL::deinit(not_null context) { - context->functions()->glDeleteTextures(_textures.size(), _textures.data()); - context->functions()->glDeleteBuffers(1, &_vertexBuffer); - context->functions()->glDeleteProgram(_shaderProgram); - context->functions()->glDeleteShader(_vertexShader); - context->functions()->glDeleteShader(_fragmentShader); +void LargeVideo::RendererGL::deinit( + not_null widget, + not_null f) { + if (_textures.front()) { + f->glDeleteTextures(_textures.size(), _textures.data()); + ranges::fill(_textures, 0); + } + _frameBuffer = std::nullopt; + _fillBuffer = std::nullopt; + _frameProgram = std::nullopt; + _fillProgram = std::nullopt; } void LargeVideo::RendererGL::resize( @@ -202,23 +230,58 @@ void LargeVideo::RendererGL::resize( void LargeVideo::RendererGL::paint( not_null widget, not_null f) { - const auto bg = st::groupCallMembersFg->c; - const auto fill = [&](QRect rect) { - //p.fillRect(rect, st::groupCallMembersBg); - }; + const auto size = _owner->widget()->size(); + if (size.isEmpty()) { + return; + } + + const auto bg = st::groupCallMembersBg->c; + const auto bgvector = QVector4D( + bg.redF(), + bg.greenF(), + bg.blueF(), + bg.alphaF()); + const auto [image, rotation] = _owner->_track ? _owner->_track.track->frameOriginalWithRotation() : std::pair(); if (image.isNull()) { - f->glClearColor(bg.redF(), bg.greenF(), bg.blueF(), 1.); + f->glClearColor(bgvector[0], bgvector[1], bgvector[2], bgvector[3]); f->glClear(GL_COLOR_BUFFER_BIT); return; } - f->glUseProgram(_shaderProgram); + + const auto scaled = Media::View::FlipSizeByRotation( + image.size(), + rotation + ).scaled(size, Qt::KeepAspectRatio); + const auto left = (size.width() - scaled.width()) / 2; + const auto top = (size.height() - scaled.height()) / 2; + const auto right = left + scaled.width(); + const auto bottom = top + scaled.height(); + + auto texcoords = std::array, 4> { { + { {0, 1}}, + { {1, 1} }, + { {1, 0} }, + { {0, 0} }, + } }; + if (rotation > 0) { + std::rotate( + texcoords.begin(), + texcoords.begin() + (rotation / 90), + texcoords.end()); + } + const GLfloat vertices[] = { + float(left), float(top), texcoords[0][0], texcoords[0][1], + float(right), float(top), texcoords[1][0], texcoords[1][1], + float(right), float(bottom), texcoords[2][0], texcoords[2][1], + float(left), float(bottom), texcoords[3][0], texcoords[3][1], + }; + + f->glUseProgram(_frameProgram->programId()); f->glActiveTexture(GL_TEXTURE0); f->glBindTexture(GL_TEXTURE_2D, _textures[0]); - - // #TODO calls check stride, upload with stride or from copy of an image. const auto key = image.cacheKey(); if (_key != key) { _key = key; @@ -237,51 +300,66 @@ void LargeVideo::RendererGL::paint( } _owner->_track.track->markFrameShown(); - f->glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); - std::array, 4> UVCoords = { { - {{0, 1}}, // Lower left. - {{1, 1}}, // Lower right. - {{1, 0}}, // Upper right. - {{0, 0}}, // Upper left. - } }; + _frameBuffer->bind(); + _frameBuffer->allocate(vertices, sizeof(vertices)); - const auto rotation_offset = (rotation / 90); - std::rotate( - UVCoords.begin(), - UVCoords.begin() + rotation_offset, - UVCoords.end()); - const GLfloat vertices[] = { - -1, -1, UVCoords[0][0], UVCoords[0][1], - 1, -1, UVCoords[1][0], UVCoords[1][1], - 1, 1, UVCoords[2][0], UVCoords[2][1], - -1, 1, UVCoords[3][0], UVCoords[3][1], - }; - f->glBufferData( - GL_ARRAY_BUFFER, - sizeof(vertices), - vertices, - GL_DYNAMIC_DRAW); + _frameProgram->setUniformValue("viewport", QSizeF(size)); + _frameProgram->setUniformValue("s_texture", GLint(0)); - GLint sampler = f->glGetUniformLocation(_shaderProgram, "s_texture"); - GLint position = f->glGetAttribLocation(_shaderProgram, "position"); - GLint texcoord = f->glGetAttribLocation(_shaderProgram, "texcoord"); - if (position < 0 || texcoord < 0) { - return; - } - - f->glUniform1i(sampler, 0); - - // Read position attribute with size of 2 and stride of 4 beginning at the start of the array. The - // last argument indicates offset of data within the vertex buffer. - f->glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)0); + GLint position = _frameProgram->attributeLocation("position"); + f->glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0); f->glEnableVertexAttribArray(position); - // Read texcoord attribute with size of 2 and stride of 4 beginning at the first texcoord in the - // array. The last argument indicates offset of data within the vertex buffer. - f->glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat))); + GLint texcoord = _frameProgram->attributeLocation("texcoord"); + f->glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); f->glEnableVertexAttribArray(texcoord); f->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + f->glDisableVertexAttribArray(position); + f->glDisableVertexAttribArray(texcoord); + + constexpr auto kMaxTriangles = 8; + auto coordinates = std::array{ 0 }; + auto triangles = 0; + const auto fill = [&](QRect rect) { + auto i = triangles * 6; + coordinates[i + 0] = coordinates[i + 10] = rect.x(); + coordinates[i + 1] = coordinates[i + 11] = rect.y(); + coordinates[i + 2] = rect.x() + rect.width(); + coordinates[i + 3] = rect.y(); + coordinates[i + 4] = coordinates[i + 6] = rect.x() + rect.width(); + coordinates[i + 5] = coordinates[i + 7] = rect.y() + rect.height(); + coordinates[i + 8] = rect.x(); + coordinates[i + 9] = rect.y() + rect.height(); + triangles += 2; + }; + if (left > 0) { + fill({ 0, 0, left, size.height() }); + } + if (right < size.width()) { + fill({ right, 0, size.width() - right, size.height() }); + } + if (top > 0) { + fill({ 0, 0, size.width(), top }); + } + if (bottom < size.height()) { + fill({ 0, bottom, size.width(), size.height() - bottom }); + } + if (triangles > 0) { + _fillBuffer->bind(); + _fillBuffer->allocate(coordinates.data(), triangles * 6 * sizeof(GLfloat)); + + f->glUseProgram(_fillProgram->programId()); + _fillProgram->setUniformValue("viewport", QSizeF(size)); + _fillProgram->setUniformValue("s_color", bgvector); + + GLint position = _fillProgram->attributeLocation("position"); + f->glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (void*)0); + f->glEnableVertexAttribArray(position); + + f->glDrawArrays(GL_TRIANGLES, 0, triangles * 3); + } } LargeVideo::LargeVideo( diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 5e38964fbf..8d7ced5c74 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 5e38964fbfed38efdcf5c1628ca65d7e3469764a +Subproject commit 8d7ced5c74328db316e2d80883ec0b12ab8c062b