From: Steinar H. Gunderson Date: Sat, 22 Mar 2014 14:53:11 +0000 (+0100) Subject: Merge branch 'master' into epoxy X-Git-Tag: 1.1~12^2~4 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=5ee3e6bb0bf100d57a06911b89c3a0a0dc49e2be;hp=9b0bd744d606d9d92bd955e0e575c73aa505073d;p=movit Merge branch 'master' into epoxy --- diff --git a/Makefile.in b/Makefile.in index 445f5b7..3b5f6c3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -17,15 +17,19 @@ datarootdir = @datarootdir@ datadir = @datadir@ top_builddir = @top_builddir@ with_demo_app = @with_demo_app@ +with_SDL2 = @with_SDL2@ with_coverage = @with_coverage@ CC=@CC@ CXX=@CXX@ -CXXFLAGS=-Wall @CXXFLAGS@ -I$(GTEST_DIR)/include @Eigen3_CFLAGS@ @GLEW_CFLAGS@ @FFTW3_CFLAGS@ +CXXFLAGS=-Wall @CXXFLAGS@ -I$(GTEST_DIR)/include @SDL2_CFLAGS@ @SDL_CFLAGS@ @Eigen3_CFLAGS@ @epoxy_CFLAGS@ @FFTW3_CFLAGS@ +ifeq ($(with_SDL2),yes) +CXXFLAGS += -DHAVE_SDL2 +endif LDFLAGS=@LDFLAGS@ -LDLIBS=@GLEW_LIBS@ @FFTW3_LIBS@ -lpthread -TEST_LDLIBS=@GLEW_LIBS@ @SDL_LIBS@ -lpthread -DEMO_LDLIBS=@SDL_image_LIBS@ -lrt -lpthread @libpng_LIBS@ @FFTW3_LIBS@ +LDLIBS=@epoxy_LIBS@ @FFTW3_LIBS@ -lpthread +TEST_LDLIBS=@epoxy_LIBS@ @SDL2_LIBS@ @SDL_LIBS@ -lpthread +DEMO_LDLIBS=@SDL2_image_LIBS@ @SDL_image_LIBS@ -lrt -lpthread @libpng_LIBS@ @FFTW3_LIBS@ SHELL=@SHELL@ LIBTOOL=@LIBTOOL@ --tag=CXX RANLIB=ranlib @@ -159,12 +163,14 @@ HDRS = effect_chain.h effect_util.h effect.h input.h image_format.h init.h util. HDRS += $(INPUTS:=.h) HDRS += $(EFFECTS:=.h) -SHADERS = vs.vert header.frag footer.frag +SHADERS = vs.vert vs.130.vert vs.300es.vert +SHADERS += header.frag header.130.frag header.300es.frag +SHADERS += footer.frag footer.130.frag footer.300es.frag +SHADERS += texture1d.frag texture1d.130.frag footer.300es.frag SHADERS += $(INPUTS:=.frag) SHADERS += $(EFFECTS:=.frag) SHADERS += highlight_cutoff_effect.frag SHADERS += overlay_matte_effect.frag -SHADERS += texture1d.frag # These purposefully do not exist. MISSING_SHADERS = diffusion_effect.frag glow_effect.frag unsharp_mask_effect.frag resize_effect.frag diff --git a/README b/README index f7017bd..58dac7a 100644 --- a/README +++ b/README @@ -21,12 +21,14 @@ OK, you need works fine on Linux and OS X, and Movit is not very POSIX-bound.) * GNU Make. * A GPU capable of running GLSL fragment shaders, - process floating-point textures, and a few other things. If your machine - is less than five years old _and you have the appropriate drivers_, - you're home free. + processing floating-point textures, and a few other things (all are + part of OpenGL 3.0 or newer, although most OpenGL 2.0 cards also + have what's needed through extensions). If your machine is less than five + years old _and you have the appropriate drivers_, you're home free. + GLES3 (for mobile devices) will also work. * The [Eigen 3], [FFTW3] and [Google Test] libraries. (The library itself does not depend on the latter, but you probably want to run the unit tests.) -* The [GLEW] library, for dealing with OpenGL extensions on various +* The [epoxy] library, for dealing with OpenGL extensions on various platforms. Movit has been tested with Intel GPUs with the Mesa drivers @@ -54,7 +56,8 @@ all research-grade problems, and Movit is currently not there.) TL;DR, but I am interested in a programming example instead =========================================================== -Assuming you have an OpenGL context already set up: +Assuming you have an OpenGL context already set up (either a classic OpenGL +context, a GL 3.x forward-compatible or core context, or a GLES3 context): using namespace movit; diff --git a/alpha_division_effect_test.cpp b/alpha_division_effect_test.cpp index faa8b17..549b569 100644 --- a/alpha_division_effect_test.cpp +++ b/alpha_division_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for AlphaDivisionEffect. -#include +#include #include "gtest/gtest.h" #include "image_format.h" #include "test_util.h" diff --git a/alpha_multiplication_effect_test.cpp b/alpha_multiplication_effect_test.cpp index c63c0a3..2aa9b6d 100644 --- a/alpha_multiplication_effect_test.cpp +++ b/alpha_multiplication_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for AlphaMultiplicationEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/blur_effect.cpp b/blur_effect.cpp index d43a7ca..903737a 100644 --- a/blur_effect.cpp +++ b/blur_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/blur_effect.h b/blur_effect.h index d97f5bd..ad269f5 100644 --- a/blur_effect.h +++ b/blur_effect.h @@ -9,7 +9,7 @@ // which is what the user is intended to use, instantiates two copies of // SingleBlurPassEffect behind the scenes). -#include +#include #include #include #include diff --git a/blur_effect_test.cpp b/blur_effect_test.cpp index 1ef479e..fb4e025 100644 --- a/blur_effect_test.cpp +++ b/blur_effect_test.cpp @@ -1,5 +1,5 @@ // Unit tests for BlurEffect. -#include +#include #include #include diff --git a/colorspace_conversion_effect_test.cpp b/colorspace_conversion_effect_test.cpp index cf8aad7..1395140 100644 --- a/colorspace_conversion_effect_test.cpp +++ b/colorspace_conversion_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for ColorspaceConversionEffect. -#include +#include #include "colorspace_conversion_effect.h" #include "gtest/gtest.h" diff --git a/complex_modulate_effect.cpp b/complex_modulate_effect.cpp index 6af589c..9483f6b 100644 --- a/complex_modulate_effect.cpp +++ b/complex_modulate_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include "complex_modulate_effect.h" #include "effect_chain.h" diff --git a/complex_modulate_effect.h b/complex_modulate_effect.h index 52de229..fd4eb8b 100644 --- a/complex_modulate_effect.h +++ b/complex_modulate_effect.h @@ -21,7 +21,7 @@ // don't care about the actual FFT result, just that the convolution property // holds.) -#include +#include #include #include "effect.h" diff --git a/complex_modulate_effect_test.cpp b/complex_modulate_effect_test.cpp index 8c49d8a..b505d86 100644 --- a/complex_modulate_effect_test.cpp +++ b/complex_modulate_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for ComplexModulateEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/configure.ac b/configure.ac index 93e168b..823740b 100644 --- a/configure.ac +++ b/configure.ac @@ -9,18 +9,27 @@ AC_CONFIG_AUX_DIR(.) AC_PROG_CC AC_PROG_CXX PKG_CHECK_MODULES([Eigen3], [eigen3]) -PKG_CHECK_MODULES([GLEW], [glew]) +PKG_CHECK_MODULES([epoxy], [epoxy]) PKG_CHECK_MODULES([FFTW3], [fftw3]) -# Needed for unit tests and the demo app. -PKG_CHECK_MODULES([SDL], [sdl]) +# Needed for unit tests and the demo app. We prefer SDL2 if possible, +# but can also use classic SDL. +with_SDL2=no +with_demo_app=yes +PKG_CHECK_MODULES([SDL2], [sdl2], [with_SDL2=yes], [ + PKG_CHECK_MODULES([SDL], [sdl]) +]) # These are only needed for the demo app. -with_demo_app=yes -PKG_CHECK_MODULES([SDL_image], [SDL_image], [], [with_demo_app=no; AC_MSG_WARN([SDL_image not found, demo program will not be built])]) +if test $with_SDL2 = "yes"; then + PKG_CHECK_MODULES([SDL2_image], [SDL2_image], [], [with_demo_app=no; AC_MSG_WARN([SDL2_image not found, demo program will not be built])]) +else + PKG_CHECK_MODULES([SDL_image], [SDL_image], [], [with_demo_app=no; AC_MSG_WARN([SDL_image not found, demo program will not be built])]) +fi PKG_CHECK_MODULES([libpng], [libpng12], [], [with_demo_app=no; AC_MSG_WARN([libpng12 not found, demo program will not be built])]) AC_SUBST([with_demo_app]) +AC_SUBST([with_SDL2]) with_coverage=no AC_ARG_ENABLE([coverage], [ --enable-coverage build with information needed to compute test coverage], [with_coverage=yes]) diff --git a/deconvolution_sharpen_effect.cpp b/deconvolution_sharpen_effect.cpp index f564b68..c4ad5cd 100644 --- a/deconvolution_sharpen_effect.cpp +++ b/deconvolution_sharpen_effect.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include diff --git a/deconvolution_sharpen_effect.h b/deconvolution_sharpen_effect.h index e4cc9a5..1f68071 100644 --- a/deconvolution_sharpen_effect.h +++ b/deconvolution_sharpen_effect.h @@ -19,7 +19,7 @@ // // Jain, Anil K.: “Fundamentals of Digital Image Processing”, Prentice Hall, 1988. -#include +#include #include #include diff --git a/deconvolution_sharpen_effect_test.cpp b/deconvolution_sharpen_effect_test.cpp index 8829b9d..ab1a5a4 100644 --- a/deconvolution_sharpen_effect_test.cpp +++ b/deconvolution_sharpen_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for DeconvolutionSharpenEffect. -#include +#include #include #include diff --git a/demo.cpp b/demo.cpp index 845a776..c9b46ea 100644 --- a/demo.cpp +++ b/demo.cpp @@ -4,7 +4,17 @@ #define WIDTH 1280 #define HEIGHT 720 -#include +#include + +#ifdef HAVE_SDL2 +#include +#include +#include +#include +#include +#include +#include +#else #include #include #include @@ -13,6 +23,8 @@ #include #include #include +#endif + #include #include #include @@ -121,9 +133,11 @@ unsigned char *load_image(const char *filename, unsigned *w, unsigned *h) rgba_fmt.Gshift = 8; rgba_fmt.Bshift = 0; rgba_fmt.Ashift = 24; - + +#ifndef HAVE_SDL2 rgba_fmt.colorkey = 0; rgba_fmt.alpha = 255; +#endif SDL_Surface *converted = SDL_ConvertSurface(img, &rgba_fmt, SDL_SWSURFACE); @@ -175,8 +189,19 @@ int main(int argc, char **argv) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + +#ifdef HAVE_SDL2 + SDL_Window *window = SDL_CreateWindow("OpenGL window", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + WIDTH, HEIGHT, + SDL_WINDOW_OPENGL); + SDL_GLContext context = SDL_GL_CreateContext(window); + assert(context != NULL); +#else SDL_SetVideoMode(WIDTH, HEIGHT, 0, SDL_OPENGL); SDL_WM_SetCaption("OpenGL window", NULL); +#endif CHECK(init_movit(".", MOVIT_DEBUG_ON)); printf("GPU texture subpixel precision: about %.1f bits\n", @@ -288,7 +313,11 @@ int main(int argc, char **argv) draw_saturation_bar(0.75f, blur_radius / 100.0f); draw_saturation_bar(0.80f, blurred_mix_amount); +#ifdef HAVE_SDL2 + SDL_GL_SwapWindow(window); +#else SDL_GL_SwapBuffers(); +#endif check_error(); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); diff --git a/diffusion_effect.h b/diffusion_effect.h index 2c31e4a..82eda93 100644 --- a/diffusion_effect.h +++ b/diffusion_effect.h @@ -12,7 +12,7 @@ // where we first blur the picture, and then overlay it on the original // using the original as a matte. -#include +#include #include #include diff --git a/diffusion_effect_test.cpp b/diffusion_effect_test.cpp index 7e50a04..e58c0de 100644 --- a/diffusion_effect_test.cpp +++ b/diffusion_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for DiffusionEffect. -#include +#include #include "diffusion_effect.h" #include "effect_chain.h" diff --git a/dither_effect.cpp b/dither_effect.cpp index 72a38de..17c577d 100644 --- a/dither_effect.cpp +++ b/dither_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/dither_effect.frag b/dither_effect.frag index 6994e84..3b6b892 100644 --- a/dither_effect.frag +++ b/dither_effect.frag @@ -9,7 +9,7 @@ vec4 FUNCNAME(vec2 tc) { // and if there's any inaccuracy earlier in the chain so that it becomes e.g. // 254.8, it's better to just get it rounded off than to dither and have it // possibly get down to 254. This is not the case for the color components. - result.rgb += texture2D(PREFIX(dither_tex), tc * PREFIX(tc_scale)).xxx; + result.rgb += tex2D(PREFIX(dither_tex), tc * PREFIX(tc_scale)).xxx; // NEED_EXPLICIT_ROUND will be #defined to 1 if the GPU has inaccurate // fp32 -> int8 framebuffer rounding, and 0 otherwise. diff --git a/dither_effect.h b/dither_effect.h index 20fb025..9818603 100644 --- a/dither_effect.h +++ b/dither_effect.h @@ -43,7 +43,7 @@ // like many LCD monitors do, but it starts to get very hairy, again, for limited gains.) // The dither is also deterministic across runs. -#include +#include #include #include "effect.h" diff --git a/dither_effect_test.cpp b/dither_effect_test.cpp index 18b286e..1b72ecb 100644 --- a/dither_effect_test.cpp +++ b/dither_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for DitherEffect. -#include +#include #include #include "effect_chain.h" diff --git a/effect.cpp b/effect.cpp index eded11a..fb66514 100644 --- a/effect.cpp +++ b/effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/effect.h b/effect.h index 14cbe30..9d95705 100644 --- a/effect.h +++ b/effect.h @@ -10,7 +10,7 @@ // effect instance; use the macro PREFIX() around your identifiers to // automatically prepend that prefix. -#include +#include #include #include #include diff --git a/effect_chain.cpp b/effect_chain.cpp index e4ab719..7bce60f 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -1,6 +1,6 @@ #define GL_GLEXT_PROTOTYPES 1 -#include +#include #include #include #include @@ -238,7 +238,7 @@ string replace_prefix(const string &text, const string &prefix) void EffectChain::compile_glsl_program(Phase *phase) { - string frag_shader = read_file("header.frag"); + string frag_shader = read_version_dependent_file("header", "frag"); // Create functions for all the texture inputs that we need. for (unsigned i = 0; i < phase->inputs.size(); ++i) { @@ -249,7 +249,7 @@ void EffectChain::compile_glsl_program(Phase *phase) frag_shader += string("uniform sampler2D tex_") + effect_id + ";\n"; frag_shader += string("vec4 ") + effect_id + "(vec2 tc) {\n"; - frag_shader += "\treturn texture2D(tex_" + string(effect_id) + ", tc);\n"; + frag_shader += "\treturn tex2D(tex_" + string(effect_id) + ", tc);\n"; frag_shader += "}\n"; frag_shader += "\n"; } @@ -288,9 +288,10 @@ void EffectChain::compile_glsl_program(Phase *phase) frag_shader += "\n"; } frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n"; - frag_shader.append(read_file("footer.frag")); + frag_shader.append(read_version_dependent_file("footer", "frag")); - phase->glsl_program_num = resource_pool->compile_glsl_program(read_file("vs.vert"), frag_shader); + string vert_shader = read_version_dependent_file("vs", "vert"); + phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader); // Prepare the geometry for the fullscreen quad used in this phase. // (We have separate VAOs per shader, since the bindings can in theory diff --git a/effect_chain.h b/effect_chain.h index 903018b..e7d99d5 100644 --- a/effect_chain.h +++ b/effect_chain.h @@ -17,7 +17,7 @@ // the EffectChain holds textures and other OpenGL objects that are tied to the // context. -#include +#include #include #include #include diff --git a/effect_chain_test.cpp b/effect_chain_test.cpp index 673e9cc..50d004d 100644 --- a/effect_chain_test.cpp +++ b/effect_chain_test.cpp @@ -2,7 +2,7 @@ // // Note that this also contains the tests for some of the simpler effects. -#include +#include #include #include "effect.h" @@ -152,19 +152,23 @@ TEST(EffectChainTest, RewritingWorksAndGammaConversionsAreInserted) { TEST(EffectChainTest, RewritingWorksAndTexturesAreAskedForsRGB) { unsigned char data[] = { - 0, 64, - 128, 255, + 0, 0, 0, 255, + 64, 64, 64, 255, + 128, 128, 128, 255, + 255, 255, 255, 255, }; - float expected_data[4] = { - 1.0f, 0.9771f, - 0.8983f, 0.0f, + float expected_data[] = { + 1.0000f, 1.0000f, 1.0000f, 1.0000f, + 0.9771f, 0.9771f, 0.9771f, 1.0000f, + 0.8983f, 0.8983f, 0.8983f, 1.0000f, + 0.0000f, 0.0000f, 0.0000f, 1.0000f }; - float out_data[4]; - EffectChainTester tester(NULL, 2, 2); - tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB); + float out_data[4 * 4]; + EffectChainTester tester(NULL, 1, 4); + tester.add_input(data, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB); RewritingEffect *effect = new RewritingEffect(); tester.get_chain()->add_effect(effect); - tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB); + tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB); Node *node = effect->replaced_node; ASSERT_EQ(1, node->incoming_links.size()); @@ -172,7 +176,7 @@ TEST(EffectChainTest, RewritingWorksAndTexturesAreAskedForsRGB) { EXPECT_EQ("FlatInput", node->incoming_links[0]->effect->effect_type_id()); EXPECT_EQ("GammaCompressionEffect", node->outgoing_links[0]->effect->effect_type_id()); - expect_equal(expected_data, out_data, 2, 2); + expect_equal(expected_data, out_data, 4, 4); } TEST(EffectChainTest, RewritingWorksAndColorspaceConversionsAreInserted) { diff --git a/effect_util.cpp b/effect_util.cpp index d485d46..dbeb48b 100644 --- a/effect_util.cpp +++ b/effect_util.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/effect_util.h b/effect_util.h index 5420ce5..a1588ef 100644 --- a/effect_util.h +++ b/effect_util.h @@ -4,7 +4,7 @@ // Utilities that are often useful for implementing Effect instances, // but don't need to be included from effect.h. -#include +#include #include #include #include diff --git a/fft_convolution_effect.cpp b/fft_convolution_effect.cpp index b3f77bd..1c48142 100644 --- a/fft_convolution_effect.cpp +++ b/fft_convolution_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "complex_modulate_effect.h" diff --git a/fft_convolution_effect.h b/fft_convolution_effect.h index a2b8062..e19a804 100644 --- a/fft_convolution_effect.h +++ b/fft_convolution_effect.h @@ -72,7 +72,7 @@ // time, which in turn means you cannot change image or kernel size on the fly. #include -#include +#include #include #include "effect.h" diff --git a/fft_convolution_effect_test.cpp b/fft_convolution_effect_test.cpp index f0f8527..a55fe31 100644 --- a/fft_convolution_effect_test.cpp +++ b/fft_convolution_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for FFTConvolutionEffect. -#include +#include #include #include "effect_chain.h" diff --git a/fft_input.cpp b/fft_input.cpp index ba539b6..c88a7c4 100644 --- a/fft_input.cpp +++ b/fft_input.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include "effect_util.h" @@ -104,7 +104,8 @@ void FFTInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsig string FFTInput::output_fragment_shader() { - return read_file("flat_input.frag"); + return string("#define FIXUP_SWAP_RB 0\n#define FIXUP_RED_TO_GRAYSCALE 0\n") + + read_file("flat_input.frag"); } void FFTInput::invalidate_pixel_data() diff --git a/fft_input.h b/fft_input.h index 681876b..b57723c 100644 --- a/fft_input.h +++ b/fft_input.h @@ -14,7 +14,7 @@ // // This class is tested as part of by FFTConvolutionEffectTest. -#include +#include #include #include diff --git a/fft_pass_effect.cpp b/fft_pass_effect.cpp index 4069be9..ee0b983 100644 --- a/fft_pass_effect.cpp +++ b/fft_pass_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "effect_chain.h" diff --git a/fft_pass_effect.frag b/fft_pass_effect.frag index edcf695..607f86a 100644 --- a/fft_pass_effect.frag +++ b/fft_pass_effect.frag @@ -6,11 +6,11 @@ uniform sampler2D PREFIX(support_tex); vec4 FUNCNAME(vec2 tc) { #if DIRECTION_VERTICAL - vec4 support = texture2D(PREFIX(support_tex), vec2(tc.y * PREFIX(num_repeats), 0.0)); + vec4 support = tex2D(PREFIX(support_tex), vec2(tc.y * PREFIX(num_repeats), 0.0)); vec4 c1 = INPUT(vec2(tc.x, tc.y + support.x)); vec4 c2 = INPUT(vec2(tc.x, tc.y + support.y)); #else - vec4 support = texture2D(PREFIX(support_tex), vec2(tc.x * PREFIX(num_repeats), 0.0)); + vec4 support = tex2D(PREFIX(support_tex), vec2(tc.x * PREFIX(num_repeats), 0.0)); vec4 c1 = INPUT(vec2(tc.x + support.x, tc.y)); vec4 c2 = INPUT(vec2(tc.x + support.y, tc.y)); #endif diff --git a/fft_pass_effect.h b/fft_pass_effect.h index 460a946..90e88bc 100644 --- a/fft_pass_effect.h +++ b/fft_pass_effect.h @@ -50,7 +50,7 @@ // scaling), and as fp16 has quite limited range at times, this can be relevant // on some GPUs for larger sizes. -#include +#include #include #include #include diff --git a/fft_pass_effect_test.cpp b/fft_pass_effect_test.cpp index 284aa5f..015847a 100644 --- a/fft_pass_effect_test.cpp +++ b/fft_pass_effect_test.cpp @@ -3,11 +3,11 @@ #include #include #include +#include +#include #include "effect_chain.h" #include "fft_pass_effect.h" -#include "glew.h" -#include "gtest/gtest.h" #include "image_format.h" #include "multiply_effect.h" #include "test_util.h" diff --git a/flat_input.cpp b/flat_input.cpp index a95b90e..f6ae827 100644 --- a/flat_input.cpp +++ b/flat_input.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "effect_util.h" #include "flat_input.h" @@ -11,9 +11,8 @@ using namespace std; namespace movit { -FlatInput::FlatInput(ImageFormat image_format, MovitPixelFormat pixel_format, GLenum type, unsigned width, unsigned height) +FlatInput::FlatInput(ImageFormat image_format, MovitPixelFormat pixel_format_in, GLenum type, unsigned width, unsigned height) : image_format(image_format), - pixel_format(pixel_format), type(type), pbo(0), texture_num(0), @@ -22,11 +21,37 @@ FlatInput::FlatInput(ImageFormat image_format, MovitPixelFormat pixel_format, GL width(width), height(height), pitch(width), - pixel_data(NULL) + pixel_data(NULL), + fixup_swap_rb(false), + fixup_red_to_grayscale(false) { assert(type == GL_FLOAT || type == GL_HALF_FLOAT || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_BYTE); register_int("output_linear_gamma", &output_linear_gamma); register_int("needs_mipmaps", &needs_mipmaps); + + // Some types are not supported in all GL versions (e.g. GLES), + // and will corrected into the right format in the shader. + switch (pixel_format_in) { + case FORMAT_BGRA_PREMULTIPLIED_ALPHA: + pixel_format = FORMAT_RGBA_PREMULTIPLIED_ALPHA; + fixup_swap_rb = true; + break; + case FORMAT_BGRA_POSTMULTIPLIED_ALPHA: + pixel_format = FORMAT_RGBA_POSTMULTIPLIED_ALPHA; + fixup_swap_rb = true; + break; + case FORMAT_BGR: + pixel_format = FORMAT_RGB; + fixup_swap_rb = true; + break; + case FORMAT_GRAYSCALE: + pixel_format = FORMAT_R; + fixup_red_to_grayscale = true; + break; + default: + pixel_format = pixel_format_in; + break; + } } FlatInput::~FlatInput() @@ -50,6 +75,8 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi internal_format = GL_R32F; } else if (pixel_format == FORMAT_RG) { internal_format = GL_RG32F; + } else if (pixel_format == FORMAT_RGB) { + internal_format = GL_RGB32F; } else { internal_format = GL_RGBA32F; } @@ -58,6 +85,8 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi internal_format = GL_R16F; } else if (pixel_format == FORMAT_RG) { internal_format = GL_RG16F; + } else if (pixel_format == FORMAT_RGB) { + internal_format = GL_RGB16F; } else { internal_format = GL_RGBA16F; } @@ -66,18 +95,28 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi internal_format = GL_R16; } else if (pixel_format == FORMAT_RG) { internal_format = GL_RG16; + } else if (pixel_format == FORMAT_RGB) { + internal_format = GL_RGB16; } else { internal_format = GL_RGBA16; } } else if (output_linear_gamma) { assert(type == GL_UNSIGNED_BYTE); - internal_format = GL_SRGB8_ALPHA8; + if (pixel_format == FORMAT_RGB) { + internal_format = GL_SRGB8; + } else if (pixel_format == FORMAT_RGBA_POSTMULTIPLIED_ALPHA) { + internal_format = GL_SRGB8_ALPHA8; + } else { + assert(false); + } } else { assert(type == GL_UNSIGNED_BYTE); if (pixel_format == FORMAT_R) { internal_format = GL_R8; } else if (pixel_format == FORMAT_RG) { internal_format = GL_RG8; + } else if (pixel_format == FORMAT_RGB) { + internal_format = GL_RGB8; } else { internal_format = GL_RGBA8; } @@ -87,15 +126,10 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi } else if (pixel_format == FORMAT_RGBA_PREMULTIPLIED_ALPHA || pixel_format == FORMAT_RGBA_POSTMULTIPLIED_ALPHA) { format = GL_RGBA; - } else if (pixel_format == FORMAT_BGR) { - format = GL_BGR; - } else if (pixel_format == FORMAT_BGRA_PREMULTIPLIED_ALPHA || - pixel_format == FORMAT_BGRA_POSTMULTIPLIED_ALPHA) { - format = GL_BGRA; - } else if (pixel_format == FORMAT_GRAYSCALE) { - format = GL_LUMINANCE; } else if (pixel_format == FORMAT_RG) { format = GL_RG; + } else if (pixel_format == FORMAT_R) { + format = GL_RED; } else { assert(false); } @@ -140,7 +174,10 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi string FlatInput::output_fragment_shader() { - return read_file("flat_input.frag"); + char buf[256]; + sprintf(buf, "#define FIXUP_SWAP_RB %d\n#define FIXUP_RED_TO_GRAYSCALE %d\n", + fixup_swap_rb, fixup_red_to_grayscale); + return buf + read_file("flat_input.frag"); } void FlatInput::invalidate_pixel_data() diff --git a/flat_input.frag b/flat_input.frag index d7c04cc..855d4fb 100644 --- a/flat_input.frag +++ b/flat_input.frag @@ -6,5 +6,17 @@ vec4 FUNCNAME(vec2 tc) { // we flip the y coordinate. tc.y = 1.0 - tc.y; - return texture2D(PREFIX(tex), tc); + vec4 pixel = tex2D(PREFIX(tex), tc); + + // These two are #defined to 0 or 1 in flat_input.cpp. +#if FIXUP_SWAP_RB + pixel.rb = pixel.br; +#endif +#if FIXUP_RED_TO_GRAYSCALE + pixel.gb = pixel.rr; +#endif + return pixel; } + +#undef FIXUP_SWAP_RB +#undef FIXUP_RED_TO_GRAYSCALE diff --git a/flat_input.h b/flat_input.h index 965e34d..25b9091 100644 --- a/flat_input.h +++ b/flat_input.h @@ -1,7 +1,7 @@ #ifndef _MOVIT_FLAT_INPUT_H #define _MOVIT_FLAT_INPUT_H 1 -#include +#include #include #include @@ -26,23 +26,26 @@ public: virtual std::string effect_type_id() const { return "FlatInput"; } virtual bool can_output_linear_gamma() const { + // On desktop OpenGL, there's also GL_SLUMINANCE8 which could give us + // support for single-channel sRGB decoding, but it's not supported + // on GLES, and we're already actively rewriting single-channel inputs + // to GL_RED (even on desktop), so we stick to 3- and 4-channel inputs. return (movit_srgb_textures_supported && type == GL_UNSIGNED_BYTE && + (pixel_format == FORMAT_RGB || + pixel_format == FORMAT_RGBA_POSTMULTIPLIED_ALPHA) && (image_format.gamma_curve == GAMMA_LINEAR || image_format.gamma_curve == GAMMA_sRGB)); } virtual AlphaHandling alpha_handling() const { switch (pixel_format) { case FORMAT_RGBA_PREMULTIPLIED_ALPHA: - case FORMAT_BGRA_PREMULTIPLIED_ALPHA: return INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA; case FORMAT_RGBA_POSTMULTIPLIED_ALPHA: - case FORMAT_BGRA_POSTMULTIPLIED_ALPHA: return OUTPUT_POSTMULTIPLIED_ALPHA; + case FORMAT_R: case FORMAT_RG: case FORMAT_RGB: - case FORMAT_BGR: - case FORMAT_GRAYSCALE: return OUTPUT_BLANK_ALPHA; default: assert(false); @@ -123,6 +126,7 @@ private: unsigned width, height, pitch; const void *pixel_data; ResourcePool *resource_pool; + bool fixup_swap_rb, fixup_red_to_grayscale; }; } // namespace movit diff --git a/flat_input_test.cpp b/flat_input_test.cpp index 58d9eb9..397cae3 100644 --- a/flat_input_test.cpp +++ b/flat_input_test.cpp @@ -1,6 +1,6 @@ // Unit tests for FlatInput. -#include +#include #include #include "effect_chain.h" diff --git a/footer.130.frag b/footer.130.frag new file mode 100644 index 0000000..83f615f --- /dev/null +++ b/footer.130.frag @@ -0,0 +1,6 @@ +out vec4 FragColor; + +void main() +{ + FragColor = INPUT(tc); +} diff --git a/footer.300es.frag b/footer.300es.frag new file mode 100644 index 0000000..83f615f --- /dev/null +++ b/footer.300es.frag @@ -0,0 +1,6 @@ +out vec4 FragColor; + +void main() +{ + FragColor = INPUT(tc); +} diff --git a/gamma_compression_effect.h b/gamma_compression_effect.h index 83208cf..ee3985f 100644 --- a/gamma_compression_effect.h +++ b/gamma_compression_effect.h @@ -9,7 +9,7 @@ // Note that Movit's internal formats generally do not have enough accuracy // for 12-bit input or output. -#include +#include #include #include "effect.h" diff --git a/gamma_compression_effect_test.cpp b/gamma_compression_effect_test.cpp index 41fe776..6efbdea 100644 --- a/gamma_compression_effect_test.cpp +++ b/gamma_compression_effect_test.cpp @@ -5,7 +5,7 @@ // However, the accuracy tests are somewhat simpler, since we // only need to care about absolute errors and not relative. -#include +#include #include #include "gtest/gtest.h" diff --git a/gamma_expansion_effect.h b/gamma_expansion_effect.h index b5381ad..81f42d1 100644 --- a/gamma_expansion_effect.h +++ b/gamma_expansion_effect.h @@ -9,7 +9,7 @@ // Note that Movit's internal formats generally do not have enough accuracy // for 12-bit input or output. -#include +#include #include #include "effect.h" diff --git a/gamma_expansion_effect_test.cpp b/gamma_expansion_effect_test.cpp index d1d6d2a..499a0d4 100644 --- a/gamma_expansion_effect_test.cpp +++ b/gamma_expansion_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for GammaExpansionEffect. -#include +#include #include #include "gamma_expansion_effect.h" diff --git a/glow_effect.h b/glow_effect.h index af83307..2a2af16 100644 --- a/glow_effect.h +++ b/glow_effect.h @@ -4,7 +4,7 @@ // Glow: Cut out the highlights of the image (everything above a certain threshold), // blur them, and overlay them onto the original image. -#include +#include #include #include diff --git a/glow_effect_test.cpp b/glow_effect_test.cpp index 11083a9..174937b 100644 --- a/glow_effect_test.cpp +++ b/glow_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for GlowEffect. -#include +#include #include #include "effect_chain.h" diff --git a/gtest_sdl_main.cpp b/gtest_sdl_main.cpp index 39027e6..cd68777 100644 --- a/gtest_sdl_main.cpp +++ b/gtest_sdl_main.cpp @@ -1,8 +1,14 @@ #define GTEST_HAS_EXCEPTIONS 0 +#ifdef HAVE_SDL2 +#include +#include +#include +#else #include #include #include +#endif #include #include @@ -17,8 +23,28 @@ int main(int argc, char **argv) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + +#ifdef HAVE_SDL2 + // You can uncomment this if you want to try a core context. + // For Mesa, you can get the same effect by doing + // + // export MESA_GL_VERSION_OVERRIDE=3.1FC + // + // before running tests. +// SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +// SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); +// SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_Window *window = SDL_CreateWindow("OpenGL window for unit test", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 32, 32, + SDL_WINDOW_OPENGL); + SDL_GLContext context = SDL_GL_CreateContext(window); + assert(context != NULL); +#else SDL_SetVideoMode(32, 32, 0, SDL_OPENGL); SDL_WM_SetCaption("OpenGL window for unit test", NULL); +#endif testing::InitGoogleTest(&argc, argv); int err = RUN_ALL_TESTS(); diff --git a/header.130.frag b/header.130.frag new file mode 100644 index 0000000..129f493 --- /dev/null +++ b/header.130.frag @@ -0,0 +1,8 @@ +#version 130 + +in vec2 tc; + +vec4 tex2D(sampler2D s, vec2 coord) +{ + return texture(s, coord); +} diff --git a/header.300es.frag b/header.300es.frag new file mode 100644 index 0000000..ea52263 --- /dev/null +++ b/header.300es.frag @@ -0,0 +1,10 @@ +#version 300 es + +precision highp float; + +in vec2 tc; + +vec4 tex2D(sampler2D s, vec2 coord) +{ + return texture(s, coord); +} diff --git a/header.frag b/header.frag index af211fe..44ce5b4 100644 --- a/header.frag +++ b/header.frag @@ -1,6 +1,15 @@ +#ifdef GL_ES +precision highp float; +#endif + #ifdef GL_EXT_gpu_shader4 // We sometimes want round(). #extension GL_EXT_gpu_shader4 : enable #endif varying vec2 tc; + +vec4 tex2D(sampler2D s, vec2 coord) +{ + return texture2D(s, coord); +} diff --git a/init.cpp b/init.cpp index 37e9d70..4802c68 100644 --- a/init.cpp +++ b/init.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -18,6 +18,7 @@ float movit_texel_subpixel_precision; bool movit_srgb_textures_supported; int movit_num_wrongly_rounded; bool movit_shader_rounding_supported; +MovitShaderModel movit_shader_model; // The rules for objects with nontrivial constructors in static scope // are somewhat convoluted, and easy to mess up. We simply have a @@ -78,7 +79,8 @@ void measure_texel_subpixel_precision() glViewport(0, 0, width, 1); GLuint glsl_program_num = resource_pool.compile_glsl_program( - read_file("vs.vert"), read_file("texture1d.frag")); + read_version_dependent_file("vs", "vert"), + read_version_dependent_file("texture1d", "frag")); glUseProgram(glsl_program_num); check_error(); glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0); // Bind the 2D sampler. @@ -122,14 +124,14 @@ void measure_texel_subpixel_precision() // Now read the data back and see what the card did. // (We only look at the red channel; the others will surely be the same.) // We assume a linear ramp; anything else will give sort of odd results here. - float out_data[width]; - glReadPixels(0, 0, width, 1, GL_RED, GL_FLOAT, out_data); + float out_data[width * 4]; + glReadPixels(0, 0, width, 1, GL_RGBA, GL_FLOAT, out_data); check_error(); float biggest_jump = 0.0f; for (unsigned i = 1; i < width; ++i) { - assert(out_data[i] >= out_data[i - 1]); - biggest_jump = max(biggest_jump, out_data[i] - out_data[i - 1]); + assert(out_data[i * 4] >= out_data[(i - 1) * 4]); + biggest_jump = max(biggest_jump, out_data[i * 4] - out_data[(i - 1) * 4]); } assert(biggest_jump > 0.0); @@ -209,7 +211,8 @@ void measure_roundoff_problems() glViewport(0, 0, 512, 1); GLuint glsl_program_num = resource_pool.compile_glsl_program( - read_file("vs.vert"), read_file("texture1d.frag")); + read_version_dependent_file("vs", "vert"), + read_version_dependent_file("texture1d", "frag")); glUseProgram(glsl_program_num); check_error(); glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0); // Bind the 2D sampler. @@ -242,16 +245,16 @@ void measure_roundoff_problems() // Now read the data back and see what the card did. (Ignore the last value.) // (We only look at the red channel; the others will surely be the same.) - unsigned char out_data[512]; - glReadPixels(0, 0, 512, 1, GL_RED, GL_UNSIGNED_BYTE, out_data); + unsigned char out_data[512 * 4]; + glReadPixels(0, 0, 512, 1, GL_RGBA, GL_UNSIGNED_BYTE, out_data); check_error(); int wrongly_rounded = 0; for (unsigned i = 0; i < 255; ++i) { - if (out_data[i * 2 + 0] != i) { + if (out_data[(i * 2 + 0) * 4] != i) { ++wrongly_rounded; } - if (out_data[i * 2 + 1] != i + 1) { + if (out_data[(i * 2 + 1) * 4] != i + 1) { ++wrongly_rounded; } } @@ -277,37 +280,88 @@ void measure_roundoff_problems() bool check_extensions() { + // GLES generally doesn't use extensions as actively as desktop OpenGL. + // For now, we say that for GLES, we require GLES 3, which has everything + // we need. + // + // Since we use implicit #version 100, we don't have round(). We will + // fix this at some later stage. + if (!epoxy_is_desktop_gl()) { + if (epoxy_gl_version() >= 30) { + movit_srgb_textures_supported = true; + movit_shader_rounding_supported = false; + } else { + return false; + } + } + // We fundamentally need FBOs and floating-point textures. - if (!glewIsSupported("GL_ARB_framebuffer_object")) return false; - if (!glewIsSupported("GL_ARB_texture_float")) return false; + // FBOs are covered by OpenGL 1.5, and are not an extension there. + // Floating-point textures are part of OpenGL 3.0 and newer. + if (epoxy_gl_version() < 15 && + !epoxy_has_gl_extension("GL_ARB_framebuffer_object")) return false; + if (epoxy_gl_version() < 30 && + !epoxy_has_gl_extension("GL_ARB_texture_float")) return false; // We assume that we can use non-power-of-two textures without restrictions. - if (!glewIsSupported("GL_ARB_texture_non_power_of_two")) return false; + if (epoxy_gl_version() < 20 && + !epoxy_has_gl_extension("GL_ARB_texture_non_power_of_two")) return false; // We also need GLSL fragment shaders. - if (!glewIsSupported("GL_ARB_fragment_shader")) return false; - if (!glewIsSupported("GL_ARB_shading_language_100")) return false; + if (epoxy_gl_version() < 20) { + if (!epoxy_has_gl_extension("GL_ARB_fragment_shader")) return false; + if (!epoxy_has_gl_extension("GL_ARB_shading_language_100")) return false; + } // FlatInput and YCbCrInput uses PBOs. (They could in theory do without, // but no modern card would really not provide it.) - if (!glewIsSupported("GL_ARB_pixel_buffer_object")) return false; + if (epoxy_gl_version() < 21 && + !epoxy_has_gl_extension("GL_ARB_pixel_buffer_object")) return false; // ResampleEffect uses RG textures to encode a two-component LUT. - if (!glewIsSupported("GL_ARB_texture_rg")) return false; + if (epoxy_gl_version() < 30 && + !epoxy_has_gl_extension("GL_ARB_texture_rg")) return false; // sRGB texture decode would be nice, but are not mandatory // (GammaExpansionEffect can do the same thing if needed). - movit_srgb_textures_supported = glewIsSupported("GL_EXT_texture_sRGB"); + movit_srgb_textures_supported = + (epoxy_gl_version() >= 21 || epoxy_has_gl_extension("GL_EXT_texture_sRGB")); // We may want to use round() at the end of the final shader, // if supported. We need either GLSL 1.30 or this extension to do that, // and 1.30 brings with it other things that we don't want to demand // for now. - movit_shader_rounding_supported = glewIsSupported("GL_EXT_gpu_shader4"); + movit_shader_rounding_supported = + (epoxy_gl_version() >= 30 || epoxy_has_gl_extension("GL_EXT_gpu_shader4")); return true; } +double get_glsl_version() +{ + char *glsl_version_str = strdup((const char *)glGetString(GL_SHADING_LANGUAGE_VERSION)); + + // Skip past the first period. + char *ptr = strchr(glsl_version_str, '.'); + assert(ptr != NULL); + ++ptr; + + // Now cut the string off at the next period or space, whatever comes first + // (unless the string ends first). + while (*ptr && *ptr != '.' && *ptr != ' ') { + ++ptr; + } + *ptr = '\0'; + + // Now we have something on the form X.YY. We convert it to a float, and hope + // that if it's inexact (e.g. 1.30), atof() will round the same way the + // compiler will. + float glsl_version = atof(glsl_version_str); + free(glsl_version_str); + + return glsl_version; +} + } // namespace bool init_movit(const string& data_directory, MovitDebugLevel debug_level) @@ -319,11 +373,6 @@ bool init_movit(const string& data_directory, MovitDebugLevel debug_level) movit_data_directory = new string(data_directory); movit_debug_level = debug_level; - GLenum err = glewInit(); - if (err != GLEW_OK) { - return false; - } - // geez glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -332,6 +381,18 @@ bool init_movit(const string& data_directory, MovitDebugLevel debug_level) if (!check_extensions()) { return false; } + + // Find out what shader model we should compile for. + if (epoxy_is_desktop_gl()) { + if (get_glsl_version() >= 1.30) { + movit_shader_model = MOVIT_GLSL_130; + } else { + movit_shader_model = MOVIT_GLSL_110; + } + } else { + movit_shader_model = MOVIT_ESSL_300; + } + measure_texel_subpixel_precision(); measure_roundoff_problems(); diff --git a/init.h b/init.h index d6f6dd4..628915d 100644 --- a/init.h +++ b/init.h @@ -67,6 +67,15 @@ extern bool movit_shader_rounding_supported; // Whether the GPU in use supports GL_EXT_texture_sRGB. extern bool movit_srgb_textures_supported; +// What shader model we are compiling for. This only affects the choice +// of a few files (like header.frag); most of the shaders are the same. +enum MovitShaderModel { + MOVIT_GLSL_110, + MOVIT_GLSL_130, + MOVIT_ESSL_300 +}; +extern MovitShaderModel movit_shader_model; + } // namespace movit #endif // !defined(_MOVIT_INIT_H) diff --git a/lift_gamma_gain_effect.cpp b/lift_gamma_gain_effect.cpp index e9832f4..60cd7df 100644 --- a/lift_gamma_gain_effect.cpp +++ b/lift_gamma_gain_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "effect_util.h" diff --git a/lift_gamma_gain_effect.h b/lift_gamma_gain_effect.h index b500772..c93724d 100644 --- a/lift_gamma_gain_effect.h +++ b/lift_gamma_gain_effect.h @@ -20,7 +20,7 @@ // Also, gamma is a case where we would not want premultiplied alpha. // Thus, we have to divide away alpha first, and then re-multiply it back later. -#include +#include #include #include "effect.h" diff --git a/lift_gamma_gain_effect_test.cpp b/lift_gamma_gain_effect_test.cpp index 612901f..1be2847 100644 --- a/lift_gamma_gain_effect_test.cpp +++ b/lift_gamma_gain_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for LiftGammaGainEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/luma_mix_effect_test.cpp b/luma_mix_effect_test.cpp index 618b733..4a506f5 100644 --- a/luma_mix_effect_test.cpp +++ b/luma_mix_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for LumaMixEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/mix_effect_test.cpp b/mix_effect_test.cpp index e3d66e7..b508284 100644 --- a/mix_effect_test.cpp +++ b/mix_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for MixEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/movit.pc.in b/movit.pc.in index b11ba15..20427c6 100644 --- a/movit.pc.in +++ b/movit.pc.in @@ -14,5 +14,5 @@ Version: git Requires: Conflicts: Libs: -lmovit -Libs.private: @GLEW_LIBS@ @FFTW3_LIBS@ -Cflags: -I${includedir}/movit @Eigen3_CFLAGS@ @GLEW_CFLAGS@ @FFTW3_CFLAGS@ +Libs.private: @epoxy_LIBS@ @FFTW3_LIBS@ +Cflags: -I${includedir}/movit @Eigen3_CFLAGS@ @epoxy_CFLAGS@ @FFTW3_CFLAGS@ diff --git a/multiply_effect.h b/multiply_effect.h index 50612fc..92e98e3 100644 --- a/multiply_effect.h +++ b/multiply_effect.h @@ -6,7 +6,7 @@ // sending it through OverlayEffect, e.g. with R=G=B=A=0.3 to get 30% alpha // (remember, alpha is premultiplied). -#include +#include #include #include "effect.h" diff --git a/overlay_effect_test.cpp b/overlay_effect_test.cpp index 597c59c..dfda93f 100644 --- a/overlay_effect_test.cpp +++ b/overlay_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for OverlayEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/padding_effect.cpp b/padding_effect.cpp index 15381a3..f576baa 100644 --- a/padding_effect.cpp +++ b/padding_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "effect_util.h" diff --git a/padding_effect.h b/padding_effect.h index 3bd4aac..901f892 100644 --- a/padding_effect.h +++ b/padding_effect.h @@ -12,7 +12,7 @@ // You may not change it after calling finalize(), since that could change the // graph (need_linear_light() etc. depend on the border color you choose). -#include +#include #include #include "effect.h" diff --git a/padding_effect_test.cpp b/padding_effect_test.cpp index 767f3e8..dd836ea 100644 --- a/padding_effect_test.cpp +++ b/padding_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for AlphaMultiplicationEffect. -#include +#include #include #include "effect_chain.h" diff --git a/resample_effect.cpp b/resample_effect.cpp index a677487..50b7c6b 100644 --- a/resample_effect.cpp +++ b/resample_effect.cpp @@ -1,7 +1,7 @@ // Three-lobed Lanczos, the most common choice. #define LANCZOS_RADIUS 3.0 -#include +#include #include #include #include diff --git a/resample_effect.frag b/resample_effect.frag index a31df17..d71b5f5 100644 --- a/resample_effect.frag +++ b/resample_effect.frag @@ -19,7 +19,7 @@ vec4 PREFIX(do_sample)(vec2 tc, int i) #else sample_tc.y = tc.x * PREFIX(num_loops); #endif - vec2 sample = texture2D(PREFIX(sample_tex), sample_tc).rg; + vec2 sample = tex2D(PREFIX(sample_tex), sample_tc).rg; #if DIRECTION_VERTICAL tc.y = sample.g + floor(sample_tc.y) * PREFIX(slice_height); diff --git a/resample_effect.h b/resample_effect.h index 44587bf..f0112b3 100644 --- a/resample_effect.h +++ b/resample_effect.h @@ -15,7 +15,7 @@ // which is what the user is intended to use, instantiates two copies of // SingleResamplePassEffect behind the scenes). -#include +#include #include #include #include diff --git a/resample_effect_test.cpp b/resample_effect_test.cpp index 211e6d1..95c2bcf 100644 --- a/resample_effect_test.cpp +++ b/resample_effect_test.cpp @@ -1,12 +1,11 @@ // Unit tests for ResampleEffect. -#include +#include +#include #include #include "effect_chain.h" #include "flat_input.h" -#include "glew.h" -#include "gtest/gtest.h" #include "image_format.h" #include "resample_effect.h" #include "test_util.h" diff --git a/resource_pool.cpp b/resource_pool.cpp index a83add2..10eeb9b 100644 --- a/resource_pool.cpp +++ b/resource_pool.cpp @@ -6,8 +6,8 @@ #include #include #include +#include -#include "glew.h" #include "init.h" #include "resource_pool.h" #include "util.h" @@ -200,10 +200,18 @@ GLuint ResourcePool::create_2d_texture(GLint internal_format, GLsizei width, GLs case GL_SRGB8_ALPHA8: format = GL_RGBA; break; + case GL_RGB32F: + case GL_RGB16F: + case GL_RGB8: + format = GL_RGB; + break; case GL_RG32F: case GL_RG16F: + case GL_RG8: format = GL_RG; break; + case GL_R32F: + case GL_R16F: case GL_R8: format = GL_RED; break; @@ -212,12 +220,38 @@ GLuint ResourcePool::create_2d_texture(GLint internal_format, GLsizei width, GLs assert(false); } + // Same with type; GLES is stricter than desktop OpenGL here. + GLenum type; + switch (internal_format) { + case GL_RGBA32F_ARB: + case GL_RGBA16F_ARB: + case GL_RGB32F: + case GL_RGB16F: + case GL_RG32F: + case GL_RG16F: + case GL_R32F: + case GL_R16F: + type = GL_FLOAT; + break; + case GL_SRGB8_ALPHA8: + case GL_RGBA8: + case GL_RGB8: + case GL_RG8: + case GL_R8: + type = GL_UNSIGNED_BYTE; + break; + default: + // TODO: Add more here as needed. + assert(false); + } + + GLuint texture_num; glGenTextures(1, &texture_num); check_error(); glBindTexture(GL_TEXTURE_2D, texture_num); check_error(); - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type, NULL); check_error(); glBindTexture(GL_TEXTURE_2D, 0); check_error(); @@ -340,16 +374,32 @@ size_t ResourcePool::estimate_texture_size(const Texture2D &texture_format) case GL_RGBA16F_ARB: bytes_per_pixel = 8; break; + case GL_RGB32F_ARB: + bytes_per_pixel = 12; + break; + case GL_RGB16F_ARB: + bytes_per_pixel = 6; + break; case GL_RGBA8: case GL_SRGB8_ALPHA8: bytes_per_pixel = 4; break; + case GL_RGB8: + case GL_SRGB8: + bytes_per_pixel = 3; + break; case GL_RG32F: bytes_per_pixel = 8; break; case GL_RG16F: bytes_per_pixel = 4; break; + case GL_R32F: + bytes_per_pixel = 4; + break; + case GL_R16F: + bytes_per_pixel = 2; + break; case GL_R8: bytes_per_pixel = 1; break; diff --git a/resource_pool.h b/resource_pool.h index 4ae4110..7029fbf 100644 --- a/resource_pool.h +++ b/resource_pool.h @@ -16,7 +16,7 @@ // safely called from multiple threads at the same time, provided they have // separate (but sharing) OpenGL contexts. -#include +#include #include #include #include diff --git a/sandbox_effect.cpp b/sandbox_effect.cpp index 76e1f5c..1ec093d 100644 --- a/sandbox_effect.cpp +++ b/sandbox_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include "sandbox_effect.h" #include "util.h" diff --git a/sandbox_effect.h b/sandbox_effect.h index f06b2ef..25c7992 100644 --- a/sandbox_effect.h +++ b/sandbox_effect.h @@ -8,7 +8,7 @@ // throwaway code. When you're happy, you can do a bit of search and replace // to give it a proper name and its own place in the build system. -#include +#include #include #include "effect.h" diff --git a/saturation_effect_test.cpp b/saturation_effect_test.cpp index 941df99..6a711a4 100644 --- a/saturation_effect_test.cpp +++ b/saturation_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for SaturationEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/slice_effect.cpp b/slice_effect.cpp index 1ec68f9..50504fe 100644 --- a/slice_effect.cpp +++ b/slice_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include "effect_chain.h" #include "slice_effect.h" diff --git a/slice_effect.h b/slice_effect.h index 0ce5d08..89aeb0e 100644 --- a/slice_effect.h +++ b/slice_effect.h @@ -10,7 +10,7 @@ // Note that vertical slices happen from the top, consistent with the rest of // Movit. -#include +#include #include #include "effect.h" diff --git a/slice_effect_test.cpp b/slice_effect_test.cpp index 3e56667..477c7a7 100644 --- a/slice_effect_test.cpp +++ b/slice_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for SliceEffect. -#include +#include #include "effect_chain.h" #include "flat_input.h" diff --git a/test_util.cpp b/test_util.cpp index a9f738d..a3ccb5c 100644 --- a/test_util.cpp +++ b/test_util.cpp @@ -2,11 +2,11 @@ #include #include #include +#include +#include +#include #include "flat_input.h" -#include "glew.h" -#include "gtest/gtest.h" -#include "gtest/gtest-message.h" #include "init.h" #include "resource_pool.h" #include "test_util.h" @@ -142,7 +142,30 @@ void EffectChainTester::run(float *out_data, GLenum format, Colorspace color_spa chain.render_to_fbo(fbo, width, height); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glReadPixels(0, 0, width, height, format, GL_FLOAT, out_data); + check_error(); + if (!epoxy_is_desktop_gl() && (format == GL_RED || format == GL_BLUE || format == GL_ALPHA)) { + // GLES will only read GL_RGBA. + float *temp = new float[width * height * 4]; + glReadPixels(0, 0, width, height, GL_RGBA, GL_FLOAT, temp); + check_error(); + if (format == GL_ALPHA) { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4 + 3]; + } + } else if (format == GL_BLUE) { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4 + 2]; + } + } else { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4]; + } + } + delete[] temp; + } else { + glReadPixels(0, 0, width, height, format, GL_FLOAT, out_data); + check_error(); + } if (format == GL_RGBA) { width *= 4; @@ -160,7 +183,30 @@ void EffectChainTester::run(unsigned char *out_data, GLenum format, Colorspace c chain.render_to_fbo(fbo, width, height); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, out_data); + check_error(); + if (!epoxy_is_desktop_gl() && (format == GL_RED || format == GL_BLUE || format == GL_ALPHA)) { + // GLES will only read GL_RGBA. + unsigned char *temp = new unsigned char[width * height * 4]; + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, temp); + check_error(); + if (format == GL_ALPHA) { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4 + 3]; + } + } else if (format == GL_BLUE) { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4 + 2]; + } + } else { + for (unsigned i = 0; i < width * height; ++i) { + out_data[i] = temp[i * 4]; + } + } + delete[] temp; + } else { + glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, out_data); + check_error(); + } if (format == GL_RGBA) { width *= 4; diff --git a/test_util.h b/test_util.h index ff568ba..e5e6551 100644 --- a/test_util.h +++ b/test_util.h @@ -1,7 +1,7 @@ #ifndef _MOVIT_TEST_UTIL_H #define _MOVIT_TEST_UTIL_H 1 -#include +#include #include "effect_chain.h" #include "image_format.h" diff --git a/texture1d.130.frag b/texture1d.130.frag new file mode 100644 index 0000000..a71f90e --- /dev/null +++ b/texture1d.130.frag @@ -0,0 +1,11 @@ +#version 130 + +uniform sampler2D tex; +in vec2 tc; + +out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, tc); // Second component is irrelevant. +} diff --git a/texture1d.300es.frag b/texture1d.300es.frag new file mode 100644 index 0000000..ddea589 --- /dev/null +++ b/texture1d.300es.frag @@ -0,0 +1,13 @@ +#version 300 es + +precision highp float; + +uniform sampler2D tex; +in vec2 tc; + +out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, tc); // Second component is irrelevant. +} diff --git a/texture1d.frag b/texture1d.frag index c3a8047..addf900 100644 --- a/texture1d.frag +++ b/texture1d.frag @@ -1,3 +1,7 @@ +#ifdef GL_ES +precision highp float; +#endif + uniform sampler2D tex; varying vec2 tc; diff --git a/unsharp_mask_effect.h b/unsharp_mask_effect.h index e6d3790..7a2052b 100644 --- a/unsharp_mask_effect.h +++ b/unsharp_mask_effect.h @@ -10,7 +10,7 @@ // See DeconvolutionSharpenEffect for a different, possibly better // sharpening algorithm. -#include +#include #include #include diff --git a/unsharp_mask_effect_test.cpp b/unsharp_mask_effect_test.cpp index 69ea5f7..e6750ce 100644 --- a/unsharp_mask_effect_test.cpp +++ b/unsharp_mask_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for UnsharpMaskEffect. -#include +#include #include #include "effect_chain.h" diff --git a/util.cpp b/util.cpp index 1e66c30..01787f0 100644 --- a/util.cpp +++ b/util.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -12,9 +12,9 @@ #if defined(__DARWIN__) #include #elif defined(WIN32) -#include +#include #else -#include +#include #endif using namespace std; @@ -96,6 +96,19 @@ string read_file(const string &filename) return string(buf, len); } +string read_version_dependent_file(const string &base, const string &extension) +{ + if (movit_shader_model == MOVIT_GLSL_110) { + return read_file(base + "." + extension); + } else if (movit_shader_model == MOVIT_GLSL_130) { + return read_file(base + ".130." + extension); + } else if (movit_shader_model == MOVIT_ESSL_300) { + return read_file(base + ".300es." + extension); + } else { + assert(false); + } +} + GLuint compile_shader(const string &shader_src, GLenum type) { GLuint obj = glCreateShader(type); diff --git a/util.h b/util.h index 1ecdc00..1fa4e78 100644 --- a/util.h +++ b/util.h @@ -3,7 +3,7 @@ // Various utilities. -#include +#include #include #include #include @@ -24,6 +24,10 @@ void hsv2rgb_normalized(float h, float s, float v, float *r, float *g, float *b) // Dies if the file does not exist. std::string read_file(const std::string &filename); +// Reads ., .130. or .300es. and +// returns its contents, depending on . +std::string read_version_dependent_file(const std::string &base, const std::string &extension); + // Compile the given GLSL shader (typically a vertex or fragment shader) // and return the object number. GLuint compile_shader(const std::string &shader_src, GLenum type); diff --git a/vignette_effect.cpp b/vignette_effect.cpp index 94383ff..d8b4f9b 100644 --- a/vignette_effect.cpp +++ b/vignette_effect.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/vignette_effect.h b/vignette_effect.h index 737a834..fdf1a11 100644 --- a/vignette_effect.h +++ b/vignette_effect.h @@ -4,7 +4,7 @@ // A circular vignette, falling off as cos² of the distance from the center // (the classic formula for approximating a real lens). -#include +#include #include #include "effect.h" diff --git a/vignette_effect_test.cpp b/vignette_effect_test.cpp index 9c28852..010fbc4 100644 --- a/vignette_effect_test.cpp +++ b/vignette_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for VignetteEffect. -#include +#include #include #include "effect_chain.h" diff --git a/vs.130.vert b/vs.130.vert new file mode 100644 index 0000000..7b4ebab --- /dev/null +++ b/vs.130.vert @@ -0,0 +1,17 @@ +#version 130 + +in vec2 position; +in vec2 texcoord; +out vec2 tc; + +void main() +{ + // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is: + // + // 2.000 0.000 0.000 -1.000 + // 0.000 2.000 0.000 -1.000 + // 0.000 0.000 -2.000 -1.000 + // 0.000 0.000 0.000 1.000 + gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); + tc = texcoord; +} diff --git a/vs.300es.vert b/vs.300es.vert new file mode 100644 index 0000000..542be89 --- /dev/null +++ b/vs.300es.vert @@ -0,0 +1,19 @@ +#version 300 es + +precision highp float; + +in vec2 position; +in vec2 texcoord; +out vec2 tc; + +void main() +{ + // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is: + // + // 2.000 0.000 0.000 -1.000 + // 0.000 2.000 0.000 -1.000 + // 0.000 0.000 -2.000 -1.000 + // 0.000 0.000 0.000 1.000 + gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); + tc = texcoord; +} diff --git a/white_balance_effect.cpp b/white_balance_effect.cpp index 0aa85da..69759ad 100644 --- a/white_balance_effect.cpp +++ b/white_balance_effect.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include "colorspace_conversion_effect.h" diff --git a/white_balance_effect.h b/white_balance_effect.h index 43bfccb..f438b91 100644 --- a/white_balance_effect.h +++ b/white_balance_effect.h @@ -3,7 +3,7 @@ // Color correction in LMS color space. -#include +#include #include #include "effect.h" diff --git a/white_balance_effect_test.cpp b/white_balance_effect_test.cpp index 5d51dd8..b064207 100644 --- a/white_balance_effect_test.cpp +++ b/white_balance_effect_test.cpp @@ -1,6 +1,6 @@ // Unit tests for WhiteBalanceEffect. -#include +#include #include "effect_chain.h" #include "gtest/gtest.h" diff --git a/widgets.cpp b/widgets.cpp index 3bf53c7..8726021 100644 --- a/widgets.cpp +++ b/widgets.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "widgets.h" diff --git a/ycbcr_input.cpp b/ycbcr_input.cpp index 4974715..dc5e4d9 100644 --- a/ycbcr_input.cpp +++ b/ycbcr_input.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/ycbcr_input.frag b/ycbcr_input.frag index 8da8256..fbf20d1 100644 --- a/ycbcr_input.frag +++ b/ycbcr_input.frag @@ -9,9 +9,9 @@ vec4 FUNCNAME(vec2 tc) { tc.y = 1.0 - tc.y; vec3 ycbcr; - ycbcr.x = texture2D(PREFIX(tex_y), tc).x; - ycbcr.y = texture2D(PREFIX(tex_cb), tc + PREFIX(cb_offset)).x; - ycbcr.z = texture2D(PREFIX(tex_cr), tc + PREFIX(cr_offset)).x; + ycbcr.x = tex2D(PREFIX(tex_y), tc).x; + ycbcr.y = tex2D(PREFIX(tex_cb), tc + PREFIX(cb_offset)).x; + ycbcr.z = tex2D(PREFIX(tex_cr), tc + PREFIX(cr_offset)).x; ycbcr -= PREFIX(offset); ycbcr *= PREFIX(scale); diff --git a/ycbcr_input.h b/ycbcr_input.h index 2466eed..bf6d800 100644 --- a/ycbcr_input.h +++ b/ycbcr_input.h @@ -5,7 +5,7 @@ // imprecisely, called “YUV”), which is typically what you get from a video decoder. // It upsamples planes as needed, using the default linear upsampling OpenGL gives you. -#include +#include #include #include diff --git a/ycbcr_input_test.cpp b/ycbcr_input_test.cpp index 352fe29..6895b38 100644 --- a/ycbcr_input_test.cpp +++ b/ycbcr_input_test.cpp @@ -1,7 +1,7 @@ // Unit tests for YCbCrInput. // FIXME: This class really ought to support mipmaps. -#include +#include #include #include "effect_chain.h"