X-Git-Url: https://git.sesse.net/?p=movit;a=blobdiff_plain;f=effect_chain.h;h=ee387a954613edc8629b49762d720e339479d9ab;hp=5baf9a599cb8a791a3b622877ae50741303fb7d8;hb=c6ee050546b6940ae19a74f92bdcc8d2b1f56d22;hpb=4b08ab8b7c55672e8a44afea18f5a6b3a66bd502 diff --git a/effect_chain.h b/effect_chain.h index 5baf9a5..ee387a9 100644 --- a/effect_chain.h +++ b/effect_chain.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,19 @@ enum OutputOrigin { OUTPUT_ORIGIN_TOP_LEFT, }; +// Transformation to apply (if any) to pixel data in temporary buffers. +// See set_intermediate_format() below for more information. +enum FramebufferTransformation { + // The default; just store the value. This is what you usually want. + NO_FRAMEBUFFER_TRANSFORMATION, + + // If the values are in linear light, store sqrt(x) to the framebuffer + // instead of x itself, of course undoing it with x² on read. Useful as + // a rough approximation to the sRGB curve. (If the values are not in + // linear light, just store them as-is.) + SQUARE_ROOT_FRAMEBUFFER_TRANSFORMATION, +}; + // A node in the graph; basically an effect and some associated information. class Node { public: @@ -147,6 +161,11 @@ struct Phase { Node *output_node; GLuint glsl_program_num; // Owned by the resource_pool. + + // Position and texcoord attribute indexes, although it doesn't matter + // which is which, because they contain the same data. + std::set attribute_indexes; + bool input_needs_mipmaps; // Inputs are only inputs from other phases (ie., those that come from RTT); @@ -159,22 +178,36 @@ struct Phase { std::vector effects; // In order. unsigned output_width, output_height, virtual_output_width, virtual_output_height; + // Whether this phase is compiled as a compute shader, ie., the last effect is + // marked as one. + bool is_compute_shader; + + // If , which image unit the output buffer is bound to. + // This is used as source for a Uniform below. + int outbuf_image_unit; + + // These are used in transforming from unnormalized to normalized coordinates + // in compute shaders. + Point2D inv_output_size, output_texcoord_adjust; + // Identifier used to create unique variables in GLSL. // Unique per-phase to increase cacheability of compiled shaders. std::map effect_ids; // Uniforms for this phase; combined from all the effects. - std::vector > uniforms_sampler2d; - std::vector > uniforms_bool; - std::vector > uniforms_int; - std::vector > uniforms_float; - std::vector > uniforms_vec2; - std::vector > uniforms_vec3; - std::vector > uniforms_vec4; - std::vector > uniforms_mat3; + std::vector> uniforms_image2d; + std::vector> uniforms_sampler2d; + std::vector> uniforms_bool; + std::vector> uniforms_int; + std::vector> uniforms_float; + std::vector> uniforms_vec2; + std::vector> uniforms_vec3; + std::vector> uniforms_vec4; + std::vector> uniforms_mat3; // For measurement of GPU time used. - GLuint timer_query_object; + std::list timer_query_objects_running; + std::list timer_query_objects_free; uint64_t time_elapsed_ns; uint64_t num_measured_iterations; }; @@ -183,11 +216,11 @@ class EffectChain { public: // Aspect: e.g. 16.0f, 9.0f for 16:9. // resource_pool is a pointer to a ResourcePool with which to share shaders - // and other resources (see resource_pool.h). If NULL (the default), + // and other resources (see resource_pool.h). If nullptr (the default), // will create its own that is not shared with anything else. Does not take // ownership of the passed-in ResourcePool, but will naturally take ownership // of its own internal one if created. - EffectChain(float aspect_nom, float aspect_denom, ResourcePool *resource_pool = NULL); + EffectChain(float aspect_nom, float aspect_denom, ResourcePool *resource_pool = nullptr); ~EffectChain(); // User API: @@ -239,23 +272,46 @@ public: } Effect *add_effect(Effect *effect, const std::vector &inputs); - // Adds an RGBA output. Note that you can have at most one RGBA output and one - // Y'CbCr output (see below for details). + // Adds an RGBA output. Note that you can have at most one RGBA output and two + // Y'CbCr outputs (see below for details). void add_output(const ImageFormat &format, OutputAlphaFormat alpha_format); - // Adds an YCbCr output. Note that you can only have one output. - // Currently, only chunked packed output is supported, and only 4:4:4 - // (so chroma_subsampling_x and chroma_subsampling_y must both be 1). + // Adds an YCbCr output. Note that you can only have at most two Y'CbCr + // outputs, and they must have the same and . + // (This limitation may be lifted in the future, to allow e.g. simultaneous + // 8- and 10-bit output. Currently, multiple Y'CbCr outputs are only + // useful in some very limited circumstances, like if one texture goes + // to some place you cannot easily read from later.) + // + // Only 4:4:4 output is supported due to fragment shader limitations, + // so chroma_subsampling_x and chroma_subsampling_y must both be 1. + // should match the data type of the FBO you are rendering to, + // so that if you use 16-bit output (GL_UNSIGNED_SHORT), you will get + // 8-, 10- or 12-bit output correctly as determined by . + // Using e.g. ycbcr_format.num_levels == 1024 with GL_UNSIGNED_BYTE is + // nonsensical and invokes undefined behavior. // - // If you have both RGBA and Y'CbCr output, the RGBA output will come + // If you have both RGBA and Y'CbCr output(s), the RGBA output will come // in the last draw buffer. Also, and must be // identical between the two. void add_ycbcr_output(const ImageFormat &format, OutputAlphaFormat alpha_format, const YCbCrFormat &ycbcr_format, - YCbCrOutputSplitting output_splitting = YCBCR_OUTPUT_INTERLEAVED); + YCbCrOutputSplitting output_splitting = YCBCR_OUTPUT_INTERLEAVED, + GLenum output_type = GL_UNSIGNED_BYTE); + + // Change Y'CbCr output format. (This can be done also after finalize()). + // Note that you are not allowed to change subsampling parameters; + // however, you can change the color space parameters, ie., + // luma_coefficients, full_range and num_levels. + void change_ycbcr_output_format(const YCbCrFormat &ycbcr_format); // 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) { @@ -270,6 +326,46 @@ public: 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 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. @@ -281,7 +377,9 @@ public: void reset_phase_timing(); void print_phase_timing(); - //void render(unsigned char *src, unsigned char *dst); + // 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); @@ -291,9 +389,25 @@ public: // the current viewport. void render_to_fbo(GLuint fbo, unsigned width, unsigned height); + // Render the effect chain to the given set of textures. This is equivalent + // to render_to_fbo() with a freshly created FBO bound to the given textures, + // except that it is more efficient if the last phase contains a compute shader. + // Thus, prefer this to render_to_fbo() where possible. + // + // The format must currently be GL_RGBA16F, and only one destination + // texture is supported. Both of these restrictions will be lifted in the future. + // + // All destination textures must be exactly of size x . + // width and height can not be zero. + struct DestinationTexture { + GLuint texnum; + GLenum format; + }; + void render_to_texture(const std::vector &destinations, unsigned width, unsigned height); + Effect *last_added_effect() { if (nodes.empty()) { - return NULL; + return nullptr; } else { return nodes.back()->effect; } @@ -359,8 +473,20 @@ private: // as the last effect. Also pushes all phases in order onto . Phase *construct_phase(Node *output, std::map *completed_effects); + // Do the actual rendering of the chain. If is not (GLuint)-1, + // renders to that FBO. If is non-empty, render to that set + // of textures (last phase, save for the dummy phase, must be a compute shader), + // with x/y ignored. Having both set is an error. + void render(GLuint dest_fbo, const std::vector &destinations, + unsigned x, unsigned y, unsigned width, unsigned height); + // Execute one phase, ie. set up all inputs, effects and outputs, and render the quad. - void execute_phase(Phase *phase, bool last_phase, std::map *output_textures, std::set *generated_mipmaps); + // If dest_texture is 0, uses whatever output is current (and the phase must not be + // a compute shader). + void execute_phase(Phase *phase, + const std::map &output_textures, + GLuint dest_texture, + std::set *generated_mipmaps); // Set up uniforms for one phase. The program must already be bound. void setup_uniforms(Phase *phase); @@ -412,25 +538,38 @@ private: void fix_output_gamma(); void add_ycbcr_conversion_if_needed(); void add_dither_if_needed(); + void add_dummy_effect_if_needed(); float aspect_nom, aspect_denom; ImageFormat output_format; OutputAlphaFormat output_alpha_format; - bool output_color_rgba, output_color_ycbcr; - YCbCrFormat output_ycbcr_format; // If output_color_ycbcr is true. - YCbCrOutputSplitting output_ycbcr_splitting; // If output_color_ycbcr is true. + bool output_color_rgba; + int num_output_color_ycbcr; // Max 2. + YCbCrFormat output_ycbcr_format; // If num_output_color_ycbcr is > 0. + GLenum output_ycbcr_type; // If num_output_color_ycbcr is > 0. + YCbCrOutputSplitting output_ycbcr_splitting[2]; // If num_output_color_ycbcr is > N. std::vector nodes; std::map node_map; Effect *dither_effect; + Node *ycbcr_conversion_effect_node; std::vector inputs; // Also contained in nodes. std::vector phases; + GLenum intermediate_format; + FramebufferTransformation intermediate_transformation; unsigned num_dither_bits; OutputOrigin output_origin; bool finalized; + GLuint vbo; // Contains vertex and texture coordinate data. + + // Whether the last effect (which will then be in a phase all by itself) + // is a dummy effect that is only added because the last phase uses a compute + // shader, which cannot output directly to the backbuffer. This means that + // the phase can be skipped if we are _not_ rendering to the backbuffer. + bool has_dummy_effect = false; ResourcePool *resource_pool; bool owns_resource_pool;