X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fimage%2Fimage_kernel.cpp;h=e659c618b1678a5888aac89054ac98cdc81e35db;hb=134faacd0105bffe3bd051d708f175010dd1e9b8;hp=bf8d4939de7cb9fb2a9216ebc211fb1c6f5644c2;hpb=2a36a79b24795e2a69f32432e272cc0eb7324c76;p=casparcg diff --git a/core/mixer/image/image_kernel.cpp b/core/mixer/image/image_kernel.cpp index bf8d4939d..e659c618b 100644 --- a/core/mixer/image/image_kernel.cpp +++ b/core/mixer/image/image_kernel.cpp @@ -1,122 +1,48 @@ -#include "../../StdAfx.h" +/* +* copyright (c) 2010 Sveriges Television AB +* +* This file is part of CasparCG. +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +*/ +#include "../../stdafx.h" #include "image_kernel.h" -#include "image_mixer.h" +#include "image_shader.h" + +#include "blending_glsl.h" +#include "../gpu/shader.h" +#include "../gpu/device_buffer.h" +#include "../gpu/ogl_device.h" #include #include +#include + +#include +#include +#include -#include +#include #include #include namespace caspar { namespace core { - -class shader_program -{ - GLuint program_; -public: - - shader_program() : program_(0) {} - shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0) - { - GLint success; - - const char* vertex_source = vertex_source_str.c_str(); - - auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); - - GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL)); - GL(glCompileShaderARB(vertex_shader)); - - GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info)); - GL(glDeleteObjectARB(vertex_shader)); - std::stringstream str; - str << "Failed to compile vertex shader:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } - - const char* fragment_source = fragment_source_str.c_str(); - - auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); - - GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL)); - GL(glCompileShaderARB(fragmemt_shader)); - - GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info)); - GL(glDeleteObjectARB(fragmemt_shader)); - std::stringstream str; - str << "Failed to compile fragment shader:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } - - program_ = glCreateProgramObjectARB(); - - GL(glAttachObjectARB(program_, vertex_shader)); - GL(glAttachObjectARB(program_, fragmemt_shader)); - - GL(glLinkProgramARB(program_)); - - GL(glDeleteObjectARB(vertex_shader)); - GL(glDeleteObjectARB(fragmemt_shader)); - - GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(program_, sizeof(info), 0, info)); - GL(glDeleteObjectARB(program_)); - std::stringstream str; - str << "Failed to link shader program:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } - GL(glUseProgramObjectARB(program_)); - glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0); - glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1); - glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2); - glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3); - } - - GLint get_location(const char* name) - { - GLint loc = glGetUniformLocation(program_, name); - return loc; - } - - shader_program& operator=(shader_program&& other) - { - program_ = other.program_; - other.program_ = 0; - return *this; - } - - ~shader_program() - { - glDeleteProgram(program_); - } - - void use() - { - GL(glUseProgramObjectARB(program_)); - } -}; - -GLubyte progressive_pattern[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; GLubyte upper_pattern[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, @@ -130,121 +56,176 @@ GLubyte lower_pattern[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}; -struct image_kernel::implementation +struct image_kernel::implementation : boost::noncopyable { - std::unordered_map shaders_; - -public: - std::unordered_map& shaders() + std::shared_ptr shader_; + bool blend_modes_; + + void draw(ogl_device& ogl, draw_params&& params) { - GL(glEnable(GL_POLYGON_STIPPLE)); - GL(glEnable(GL_BLEND)); - GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + static const double epsilon = 0.001; + + CASPAR_ASSERT(params.pix_desc.planes.size() == params.textures.size()); - if(shaders_.empty()) + if(params.textures.empty() || !params.background) + return; + + if(params.transform.opacity < epsilon) + return; + + if(!std::all_of(params.textures.begin(), params.textures.end(), std::mem_fn(&device_buffer::ready))) { - std::string common_vertex = - "void main() " - "{ " - " gl_TexCoord[0] = gl_MultiTexCoord0; " - " gl_FrontColor = gl_Color; " - " gl_Position = ftransform(); " - "} "; - - std::string common_fragment = - "uniform sampler2D plane[4]; " - "uniform float color_gain; " - - // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf - // TODO: Support for more yuv formats might be needed. - "vec4 ycbcra_to_bgra(float y, float cb, float cr, float a) " - "{ " - " cb -= 0.5; " - " cr -= 0.5; " - " y = 1.164*(y-0.0625); " - " " - " vec4 color; " - " color.r = y + 1.596 * cr; " - " color.g = y - 0.813 * cr - 0.337633 * cb; " - " color.b = y + 2.017 * cb; " - " color.a = a; " - " " - " return color; " - "} " - " "; + CASPAR_LOG(warning) << L"[image_mixer] Performance warning. Host to device transfer not complete, GPU will be stalled"; + ogl.yield(); // Try to give it some more time. + } + + // Bind textures + + for(size_t n = 0; n < params.textures.size(); ++n) + params.textures[n]->bind(n); + + if(params.local_key) + params.local_key->bind(texture_id::local_key); + + if(params.layer_key) + params.layer_key->bind(texture_id::layer_key); - shaders_[pixel_format::abgr] = shader_program(common_vertex, common_fragment + + // Setup shader - "void main() " - "{ " - " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = abgr.argb * color_gain; " - "} "); + if(!shader_) + shader_ = get_image_shader(ogl, blend_modes_); + + ogl.use(*shader_); + + shader_->set("plane[0]", texture_id::plane0); + shader_->set("plane[1]", texture_id::plane1); + shader_->set("plane[2]", texture_id::plane2); + shader_->set("plane[3]", texture_id::plane3); + shader_->set("local_key", texture_id::local_key); + shader_->set("layer_key", texture_id::layer_key); + shader_->set("is_hd", params.pix_desc.planes.at(0).height > 700 ? 1 : 0); + shader_->set("has_local_key", params.local_key); + shader_->set("has_layer_key", params.layer_key); + shader_->set("pixel_format", params.pix_desc.pix_fmt); + shader_->set("opacity", params.transform.is_key ? 1.0 : params.transform.opacity); + + // Setup blend_func - shaders_[pixel_format::argb]= shader_program(common_vertex, common_fragment + + if(params.transform.is_key) + params.blend_mode = blend_mode::normal; + + if(blend_modes_) + { + params.background->bind(6); - "void main() " - "{ " - " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = argb.grab * gl_Color * color_gain; " - "} "); + shader_->set("background", texture_id::background); + shader_->set("blend_mode", params.blend_mode); + } + else + { + ogl.blend_func_separate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + // Setup image-adjustements - shaders_[pixel_format::bgra]= shader_program(common_vertex, common_fragment + + if(params.transform.levels.min_input > epsilon || + params.transform.levels.max_input < 1.0-epsilon || + params.transform.levels.min_output > epsilon || + params.transform.levels.max_output < 1.0-epsilon || + std::abs(params.transform.levels.gamma - 1.0) > epsilon) + { + shader_->set("levels", true); + shader_->set("min_input", params.transform.levels.min_input); + shader_->set("max_input", params.transform.levels.max_input); + shader_->set("min_output", params.transform.levels.min_output); + shader_->set("max_output", params.transform.levels.max_output); + shader_->set("gamma", params.transform.levels.gamma); + } + else + shader_->set("levels", false); - "void main() " - "{ " - " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = bgra.rgba * gl_Color * color_gain; " - "} "); + if(std::abs(params.transform.brightness - 1.0) > epsilon || + std::abs(params.transform.saturation - 1.0) > epsilon || + std::abs(params.transform.contrast - 1.0) > epsilon) + { + shader_->set("csb", true); + + shader_->set("brt", params.transform.brightness); + shader_->set("sat", params.transform.saturation); + shader_->set("con", params.transform.contrast); + } + else + shader_->set("csb", false); - shaders_[pixel_format::rgba] = shader_program(common_vertex, common_fragment + + // Setup interlacing - "void main() " - "{ " - " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = rgba.bgra * gl_Color * color_gain; " - "} "); + if(params.transform.field_mode == core::field_mode::progressive) + ogl.disable(GL_POLYGON_STIPPLE); + else + { + ogl.enable(GL_POLYGON_STIPPLE); + + if(params.transform.field_mode == core::field_mode::upper) + ogl.stipple_pattern(upper_pattern); + else if(params.transform.field_mode == core::field_mode::lower) + ogl.stipple_pattern(lower_pattern); + } + + // Setup drawing area - shaders_[pixel_format::ycbcr] = shader_program(common_vertex, common_fragment + - - "void main() " - "{ " - " float y = texture2D(plane[0], gl_TexCoord[0].st).r;" - " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;" - " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;" - " float a = 1.0; " - " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color * color_gain;" - "} "); + ogl.viewport(0, 0, params.background->width(), params.background->height()); + + auto m_p = params.transform.clip_translation; + auto m_s = params.transform.clip_scale; + + bool scissor = m_p[0] > std::numeric_limits::epsilon() || m_p[1] > std::numeric_limits::epsilon() || + m_s[0] < (1.0 - std::numeric_limits::epsilon()) || m_s[1] < (1.0 - std::numeric_limits::epsilon()); + + if(scissor) + { + double w = static_cast(params.background->width()); + double h = static_cast(params.background->height()); - shaders_[pixel_format::ycbcra] = shader_program(common_vertex, common_fragment + - - "void main() " - "{ " - " float y = texture2D(plane[0], gl_TexCoord[0].st).r;" - " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;" - " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;" - " float a = texture2D(plane[3], gl_TexCoord[0].st).r;" - " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color * color_gain;" - "} "); + ogl.enable(GL_SCISSOR_TEST); + ogl.scissor(static_cast(m_p[0]*w), static_cast(m_p[1]*h), static_cast(m_s[0]*w), static_cast(m_s[1]*h)); + } + + auto f_p = params.transform.fill_translation; + auto f_s = params.transform.fill_scale; + + // Set render target + + ogl.attach(*params.background); + + // Draw + + glBegin(GL_QUADS); + glMultiTexCoord2d(GL_TEXTURE0, 0.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , f_p[1] ); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 1.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), f_p[1] ); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 1.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), (f_p[1]+f_s[1])); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 0.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , (f_p[1]+f_s[1])); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glEnd(); + + // Cleanup + + ogl.disable(GL_SCISSOR_TEST); + + params.textures.clear(); + ogl.yield(); // Return resources to pool as early as possible. + + if(blend_modes_) + { + // http://www.opengl.org/registry/specs/NV/texture_barrier.txt + // This allows us to use framebuffer (background) both as source and target while blending. + glTextureBarrierNV(); } - return shaders_; } }; image_kernel::image_kernel() : impl_(new implementation()){} - -void image_kernel::apply(pixel_format::type pix_fmt, const image_transform& transform) +void image_kernel::draw(ogl_device& ogl, draw_params&& params) { - impl_->shaders()[pix_fmt].use(); - - GL(glUniform1f(impl_->shaders()[pix_fmt].get_location("color_gain"), static_cast(transform.gain))); - - if(transform.mode == video_mode::upper) - glPolygonStipple(upper_pattern); - else if(transform.mode == video_mode::lower) - glPolygonStipple(lower_pattern); - else - glPolygonStipple(progressive_pattern); + impl_->draw(ogl, std::move(params)); } }} \ No newline at end of file