X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fimage%2Fimage_kernel.cpp;h=3209bd4e9a0ff83cbd9f41d3b23a7ab4af02bf1d;hb=9852656342ffa51e8022fe13ec7518f89ecc0381;hp=c509f1d5989c4647f48b937ff23f06120fe4c93a;hpb=9e37e91c3195f5baaa0c63b5894e9c7b70797b2a;p=casparcg diff --git a/core/mixer/image/image_kernel.cpp b/core/mixer/image/image_kernel.cpp index c509f1d59..3209bd4e9 100644 --- a/core/mixer/image/image_kernel.cpp +++ b/core/mixer/image/image_kernel.cpp @@ -21,124 +21,28 @@ #include "image_kernel.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 #include -namespace caspar { namespace mixer { - -class shader_program : boost::noncopyable -{ - GLuint program_; -public: - - shader_program() : program_(0) {} - shader_program(shader_program&& other) : program_(other.program_){other.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}; +namespace caspar { namespace core { 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, @@ -154,147 +58,187 @@ GLubyte lower_pattern[] = { 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, + render_item&& item, + device_buffer& background, + const std::shared_ptr& local_key, + const std::shared_ptr& layer_key) { - GL(glEnable(GL_POLYGON_STIPPLE)); - GL(glEnable(GL_BLEND)); - GL(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)); + static const double epsilon = 0.001; + + CASPAR_ASSERT(item.pix_desc.planes.size() == item.textures.size()); - if(shaders_.empty()) + if(item.textures.empty()) + return; + + if(item.transform.opacity < epsilon) + return; + + if(!std::all_of(item.textures.begin(), item.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 gain; " - "uniform bool HD; " - "uniform bool has_separate_key; " - - // 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_sd(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.391 * cb; " - " color.b = y + 2.018 * cb; " - " color.a = a; " - " " - " return color; " - "} " - " " - - "vec4 ycbcra_to_bgra_hd(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.793 * cr; " - " color.g = y - 0.534 * cr - 0.213 * cb; " - " color.b = y + 2.115 * 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 < item.textures.size(); ++n) + item.textures[n]->bind(n); + + if(local_key) + local_key->bind(texture_id::local_key); + + if(layer_key) + layer_key->bind(texture_id::layer_key); - shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment + + // Setup shader + + 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", item.pix_desc.planes.at(0).height > 700 ? 1 : 0); + shader_->set("has_local_key", local_key); + shader_->set("has_layer_key", layer_key); + shader_->set("pixel_format", item.pix_desc.pix_fmt); + shader_->set("opacity", item.transform.is_key ? 1.0 : item.transform.opacity); + + // Setup blend_func + + if(item.transform.is_key) + item.blend_mode = blend_mode::normal; + + if(blend_modes_) + { + background.bind(6); + + shader_->set("background", texture_id::background); + shader_->set("blend_mode", item.blend_mode); + } + else + { + ogl.blend_func_separate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + // Setup image-adjustements + + if(item.transform.levels.min_input > epsilon || + item.transform.levels.max_input < 1.0-epsilon || + item.transform.levels.min_output > epsilon || + item.transform.levels.max_output < 1.0-epsilon || + std::abs(item.transform.levels.gamma - 1.0) > epsilon) + { + shader_->set("levels", true); + shader_->set("min_input", item.transform.levels.min_input); + shader_->set("max_input", item.transform.levels.max_input); + shader_->set("min_output", item.transform.levels.min_output); + shader_->set("max_output", item.transform.levels.max_output); + shader_->set("gamma", item.transform.levels.gamma); + } + else + shader_->set("levels", false); - "void main() " - "{ " - " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = abgr.argb * gain; " - "} "); + if(std::abs(item.transform.brightness - 1.0) > epsilon || + std::abs(item.transform.saturation - 1.0) > epsilon || + std::abs(item.transform.contrast - 1.0) > epsilon) + { + shader_->set("csb", true); + + shader_->set("brt", item.transform.brightness); + shader_->set("sat", item.transform.saturation); + shader_->set("con", item.transform.contrast); + } + else + shader_->set("csb", false); - shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment + + // Setup interlacing + + if(item.transform.field_mode == core::field_mode::progressive) + ogl.disable(GL_POLYGON_STIPPLE); + else + { + ogl.enable(GL_POLYGON_STIPPLE); + + if(item.transform.field_mode == core::field_mode::upper) + ogl.stipple_pattern(upper_pattern); + else if(item.transform.field_mode == core::field_mode::lower) + ogl.stipple_pattern(lower_pattern); + } - "void main() " - "{ " - " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = argb.grab * gl_Color * gain; " - "} "); + // Setup drawing area - shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment + + ogl.viewport(0, 0, background.width(), background.height()); + + auto m_p = item.transform.clip_translation; + auto m_s = item.transform.clip_scale; - "void main() " - "{ " - " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = bgra.rgba * gl_Color * gain; " - "} "); + 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(background.width()); + double h = static_cast(background.height()); - shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment + + 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)); + } - "void main() " - "{ " - " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = rgba.bgra * gl_Color * gain; " - "} "); + auto f_p = item.transform.fill_translation; + auto f_s = item.transform.fill_scale; + + // Set render target - shaders_[core::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; " - " if(has_separate_key) " - " a = texture2D(plane[3], gl_TexCoord[0].st).r+0.2; " - " if(HD) " - " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;" - " else " - " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;" - "} "); + ogl.attach(background); - shaders_[core::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; " - " if(HD) " - " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;" - " else " - " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;" - "} "); + // 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); + + item.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::draw(ogl_device& ogl, + render_item&& item, + device_buffer& background, + const std::shared_ptr& local_key, + const std::shared_ptr& layer_key) +{ + impl_->draw(ogl, std::move(item), background, local_key, layer_key); +} -void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool has_separate_key) +bool operator==(const render_item& lhs, const render_item& rhs) { - impl_->shaders()[pix_desc.pix_fmt].use(); - - GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast(transform.get_gain()))); - GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0)); - GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("has_separate_key"), has_separate_key ? 1 : 0)); - - if(transform.get_mode() == core::video_mode::upper) - glPolygonStipple(upper_pattern); - else if(transform.get_mode() == core::video_mode::lower) - glPolygonStipple(lower_pattern); - else - glPolygonStipple(progressive_pattern); + return lhs.textures == rhs.textures && lhs.transform == rhs.transform; } }} \ No newline at end of file