+ // Set number of output bits, to scale the dither.
+ // 8 is the right value for most outputs.
+ //
+ // Special note for 10- and 12-bit Y'CbCr packed into GL_UNSIGNED_SHORT:
+ // This is relative to the actual output, not the logical one, so you should
+ // specify 16 here, not 10 or 12.
+ //
+ // The default, 0, is a special value that means no dither.
+ void set_dither_bits(unsigned num_bits)
+ {
+ this->num_dither_bits = num_bits;
+ }
+
+ // Set where (0,0) is taken to be in the output. The default is
+ // OUTPUT_ORIGIN_BOTTOM_LEFT, which is usually what you want
+ // (see OutputOrigin above for more details).
+ void set_output_origin(OutputOrigin output_origin)
+ {
+ this->output_origin = output_origin;
+ }
+
+ // Set intermediate format for framebuffers used when we need to bounce
+ // to a temporary texture. The default, GL_RGBA16F, is good for most uses;
+ // it is precise, has good range, and is relatively efficient. However,
+ // if you need even more speed and your chain can do with some loss of
+ // accuracy, you can change the format here (before calling finalize).
+ // Calculations between bounce buffers are still in 32-bit floating-point
+ // no matter what you specify.
+ //
+ // Of special interest is GL_SRGB8_ALPHA8, which stores sRGB-encoded RGB
+ // and linear alpha; this is half the memory bandwidth of GL_RGBA16F,
+ // while retaining reasonable precision for typical image data. It will,
+ // however, cause some gamut clipping if your colorspace is far from sRGB,
+ // as it cannot represent values outside [0,1]. NOTE: If you construct
+ // a chain where you end up bouncing pixels in non-linear light
+ // (gamma different from GAMMA_LINEAR), this will be the wrong thing.
+ // However, it's hard to see how this could happen in a non-contrived
+ // chain; few effects ever need texture bounce or resizing without also
+ // combining multiple pixels, which really needs linear light and thus
+ // triggers a conversion before the bounce.
+ //
+ // If you don't need alpha (or can do with very little of it), GL_RGB10_A2
+ // is even better, as it has two more bits for each color component. There
+ // is no GL_SRGB10, unfortunately, so on its own, it is somewhat worse than
+ // GL_SRGB8, but you can set <transformation> to SQUARE_ROOT_FRAMEBUFFER_TRANSFORMATION,
+ // and sqrt(x) will be stored instead of x. This is a rough approximation to
+ // the sRGB curve, and reduces maximum error (in sRGB distance) by almost an
+ // order of magnitude, well below what you can get from 8-bit true sRGB.
+ // (Note that this strategy avoids the problem with bounced non-linear data
+ // above, since the square root is turned off in that case.) However, texture
+ // filtering will happen on the transformed values, so if you have heavy
+ // downscaling or the likes (e.g. mipmaps), you could get subtly bad results.
+ // You'll need to see which of the two that works the best for you in practice.
+ void set_intermediate_format(
+ GLenum intermediate_format,
+ FramebufferTransformation transformation = NO_FRAMEBUFFER_TRANSFORMATION)
+ {
+ this->intermediate_format = intermediate_format;
+ this->intermediate_transformation = transformation;
+ }
+
+ void finalize();
+
+ // Measure the GPU time used for each actual phase during rendering.
+ // Note that this is only available if GL_ARB_timer_query
+ // (or, equivalently, OpenGL 3.3) is available. Also note that measurement
+ // will incur a performance cost, as we wait for the measurements to
+ // complete at the end of rendering.
+ void enable_phase_timing(bool enable);
+ void reset_phase_timing();
+ void print_phase_timing();
+
+ // Note: If you already know the width and height of the viewport,
+ // calling render_to_fbo() directly will be slightly more efficient,
+ // as it saves it from getting it from OpenGL.
+ void render_to_screen()
+ {
+ render_to_fbo(0, 0, 0);
+ }
+
+ // Render the effect chain to the given FBO. If width=height=0, keeps
+ // the current viewport.
+ void render_to_fbo(GLuint fbo, unsigned width, unsigned height);
+
+ Effect *last_added_effect() {
+ if (nodes.empty()) {
+ return NULL;
+ } else {
+ return nodes.back()->effect;
+ }
+ }
+
+ // API for manipulating the graph directly. Intended to be used from
+ // effects and by EffectChain itself.
+ //
+ // Note that for nodes with multiple inputs, the order of calls to
+ // connect_nodes() will matter.
+ Node *add_node(Effect *effect);
+ void connect_nodes(Node *sender, Node *receiver);
+ void replace_receiver(Node *old_receiver, Node *new_receiver);
+ void replace_sender(Node *new_sender, Node *receiver);
+ void insert_node_between(Node *sender, Node *middle, Node *receiver);
+ Node *find_node_for_effect(Effect *effect) { return node_map[effect]; }
+
+ // Get the OpenGL sampler (GL_TEXTURE0, GL_TEXTURE1, etc.) for the
+ // input of the given node, so that one can modify the sampler state
+ // directly. Only valid to call during set_gl_state().
+ //
+ // Also, for this to be allowed, <node>'s effect must have
+ // needs_texture_bounce() set, so that it samples directly from a
+ // single-sampler input, or from an RTT texture.
+ GLenum get_input_sampler(Node *node, unsigned input_num) const;
+
+ // Whether input <input_num> of <node> corresponds to a single sampler
+ // (see get_input_sampler()). Normally, you should not need to call this;
+ // however, if the input Effect has set override_texture_bounce(),
+ // this will return false, and you could be flexible and check it first
+ // if you want.
+ GLenum has_input_sampler(Node *node, unsigned input_num) const;
+
+ // Get the current resource pool assigned to this EffectChain.
+ // Primarily to let effects allocate textures as needed.
+ // Any resources you get from the pool must be returned to the pool
+ // no later than in the Effect's destructor.
+ ResourcePool *get_resource_pool() { return resource_pool; }