From 20989aaee7ca26bb7aede452c55597fbcf26ebb8 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 4 Aug 2018 14:24:11 +0200 Subject: [PATCH] Split the equation texture in two, which speeds up SOR by ~30%. --- equations.frag | 33 ++++++++++++++++++++++++++------- equations.vert | 24 ++++++++++++++++++++---- flow.cpp | 43 ++++++++++++++++++++++++++++--------------- sor.frag | 18 +++++++++++++++--- sor.vert | 16 +++++++++++++++- 5 files changed, 104 insertions(+), 30 deletions(-) diff --git a/equations.frag b/equations.frag index ae32ed7..8bf0cbb 100644 --- a/equations.frag +++ b/equations.frag @@ -1,7 +1,9 @@ #version 450 core -in vec2 tc, tc_left, tc_down; -out uvec4 equation; +in vec2 tc0, tc_left0, tc_down0; +in vec2 tc1, tc_left1, tc_down1; +in float line_offset; +out uvec4 equation_red, equation_black; uniform sampler2D I_x_y_tex, I_t_tex; uniform sampler2D diff_flow_tex, base_flow_tex; @@ -65,7 +67,7 @@ float zero_if_outside_border(vec4 val) } } -void main() +uvec4 compute_equation(vec2 tc, vec2 tc_left, vec2 tc_down) { // Read the flow (on top of the u0/v0 flow). float du, dv; @@ -161,8 +163,25 @@ void main() b2 += laplacian.y; // Encode the equation down into four uint32s. - equation.x = floatBitsToUint(1.0 / A11); - equation.y = floatBitsToUint(A12); - equation.z = floatBitsToUint(1.0 / A22); - equation.w = pack_floats_shared(b1, b2); + uvec4 ret; + ret.x = floatBitsToUint(1.0 / A11); + ret.y = floatBitsToUint(A12); + ret.z = floatBitsToUint(1.0 / A22); + ret.w = pack_floats_shared(b1, b2); + return ret; +} + +void main() +{ + uvec4 eq0 = compute_equation(tc0, tc_left0, tc_down0); + uvec4 eq1 = compute_equation(tc1, tc_left1, tc_down1); + + if ((int(round(line_offset)) & 1) == 1) { + // Odd line, so the right value is red. + equation_red = eq1; + equation_black = eq0; + } else { + equation_red = eq0; + equation_black = eq1; + } } diff --git a/equations.vert b/equations.vert index 6ace577..eec3c17 100644 --- a/equations.vert +++ b/equations.vert @@ -1,7 +1,9 @@ #version 450 core layout(location=0) in vec2 position; -out vec2 tc, tc_left, tc_down; +out vec2 tc0, tc_left0, tc_down0; +out vec2 tc1, tc_left1, tc_down1; +out float line_offset; uniform sampler2D diffusivity_tex; @@ -14,7 +16,21 @@ void main() // 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 = position; - tc_left = vec2(tc.x - 0.5f / textureSize(diffusivity_tex, 0).x, tc.y); - tc_down = vec2(tc.x, tc.y - 0.5f / textureSize(diffusivity_tex, 0).y); + + const vec2 half_texel = 0.5f / textureSize(diffusivity_tex, 0); + + vec2 tc = position; + vec2 tc_left = vec2(tc.x - half_texel.x, tc.y); + vec2 tc_down = vec2(tc.x, tc.y - half_texel.y); + + // Adjust for different texel centers. + tc0 = vec2(tc.x - half_texel.x, tc.y); + tc_left0 = vec2(tc_left.x - half_texel.x, tc_left.y); + tc_down0 = vec2(tc_down.x - half_texel.x, tc_down.y); + + tc1 = vec2(tc.x + half_texel.x, tc.y); + tc_left1 = vec2(tc_left.x + half_texel.x, tc_left.y); + tc_down1 = vec2(tc_down.x + half_texel.x, tc_down.y); + + line_offset = position.y * textureSize(diffusivity_tex, 0).y - 0.5f; } diff --git a/flow.cpp b/flow.cpp index 620b511..1e48733 100644 --- a/flow.cpp +++ b/flow.cpp @@ -679,14 +679,23 @@ void ComputeDiffusivity::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint diff // All the values of the energy term (E_I, E_G, E_S), except the smoothness // terms that depend on other pixels, are calculated in one pass. // -// See variational_refinement.txt for more information. +// The equation set is split in two; one contains only the pixels needed for +// the red pass, and one only for the black pass (see sor.frag). This reduces +// the amount of data the SOR shader has to pull in, at the cost of some +// complexity when the equation texture ends up with half the size and we need +// to adjust texture coordinates. The contraction is done along the horizontal +// axis, so that on even rows (0, 2, 4, ...), the “red” texture will contain +// pixels 0, 2, 4, 6, etc., and on odd rows 1, 3, 5, etc.. +// +// See variational_refinement.txt for more information about the actual +// equations in use. class SetupEquations { public: SetupEquations(); - void exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint diffusivity_tex, GLuint equation_tex, int level_width, int level_height, bool zero_diff_flow); + void exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint diffusivity_tex, GLuint equation_red_tex, GLuint equation_black_tex, int level_width, int level_height, bool zero_diff_flow); private: - PersistentFBOSet<1> fbos; + PersistentFBOSet<2> fbos; GLuint equations_vs_obj; GLuint equations_fs_obj; @@ -716,7 +725,7 @@ SetupEquations::SetupEquations() uniform_zero_diff_flow = glGetUniformLocation(equations_program, "zero_diff_flow"); } -void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint base_flow_tex, GLuint beta_0_tex, GLuint diffusivity_tex, GLuint equation_tex, int level_width, int level_height, bool zero_diff_flow) +void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint base_flow_tex, GLuint beta_0_tex, GLuint diffusivity_tex, GLuint equation_red_tex, GLuint equation_black_tex, int level_width, int level_height, bool zero_diff_flow) { glUseProgram(equations_program); @@ -730,9 +739,9 @@ void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex glProgramUniform1f(equations_program, uniform_gamma, vr_gamma); glProgramUniform1i(equations_program, uniform_zero_diff_flow, zero_diff_flow); - glViewport(0, 0, level_width, level_height); + glViewport(0, 0, (level_width + 1) / 2, level_height); glDisable(GL_BLEND); - fbos.render_to(equation_tex); + fbos.render_to({equation_red_tex, equation_black_tex}); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } @@ -743,7 +752,7 @@ void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex class SOR { public: SOR(); - void exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint diffusivity_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer); + void exec(GLuint diff_flow_tex, GLuint equation_red_tex, GLuint equation_black_tex, GLuint diffusivity_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer); private: PersistentFBOSet<1> fbos; @@ -753,7 +762,7 @@ private: GLuint sor_program; GLuint uniform_diff_flow_tex; - GLuint uniform_equation_tex; + GLuint uniform_equation_red_tex, uniform_equation_black_tex; GLuint uniform_diffusivity_tex; GLuint uniform_phase, uniform_zero_diff_flow; }; @@ -765,19 +774,21 @@ SOR::SOR() sor_program = link_program(sor_vs_obj, sor_fs_obj); uniform_diff_flow_tex = glGetUniformLocation(sor_program, "diff_flow_tex"); - uniform_equation_tex = glGetUniformLocation(sor_program, "equation_tex"); + uniform_equation_red_tex = glGetUniformLocation(sor_program, "equation_red_tex"); + uniform_equation_black_tex = glGetUniformLocation(sor_program, "equation_black_tex"); uniform_diffusivity_tex = glGetUniformLocation(sor_program, "diffusivity_tex"); uniform_phase = glGetUniformLocation(sor_program, "phase"); uniform_zero_diff_flow = glGetUniformLocation(sor_program, "zero_diff_flow"); } -void SOR::exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint diffusivity_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer) +void SOR::exec(GLuint diff_flow_tex, GLuint equation_red_tex, GLuint equation_black_tex, GLuint diffusivity_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer) { glUseProgram(sor_program); bind_sampler(sor_program, uniform_diff_flow_tex, 0, diff_flow_tex, nearest_sampler); bind_sampler(sor_program, uniform_diffusivity_tex, 1, diffusivity_tex, zero_border_sampler); - bind_sampler(sor_program, uniform_equation_tex, 2, equation_tex, nearest_sampler); + bind_sampler(sor_program, uniform_equation_red_tex, 2, equation_red_tex, nearest_sampler); + bind_sampler(sor_program, uniform_equation_black_tex, 3, equation_black_tex, nearest_sampler); glProgramUniform1i(sor_program, uniform_zero_diff_flow, zero_diff_flow); @@ -1110,7 +1121,8 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra // And finally for the equation set. See SetupEquations for // the storage format. - GLuint equation_tex = pool.get_texture(GL_RGBA32UI, level_width, level_height); + GLuint equation_red_tex = pool.get_texture(GL_RGBA32UI, (level_width + 1) / 2, level_height); + GLuint equation_black_tex = pool.get_texture(GL_RGBA32UI, (level_width + 1) / 2, level_height); for (int outer_idx = 0; outer_idx < level + 1; ++outer_idx) { // Calculate the diffusivity term for each pixel. @@ -1122,14 +1134,14 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra // Set up the 2x2 equation system for each pixel. { ScopedTimer timer("Set up equations", &varref_timer); - setup_equations.exec(I_x_y_tex, I_t_tex, du_dv_tex, base_flow_tex, beta_0_tex, diffusivity_tex, equation_tex, level_width, level_height, outer_idx == 0); + setup_equations.exec(I_x_y_tex, I_t_tex, du_dv_tex, base_flow_tex, beta_0_tex, diffusivity_tex, equation_red_tex, equation_black_tex, level_width, level_height, outer_idx == 0); } // Run a few SOR (or quasi-SOR, since we're not really Jacobi) iterations. // Note that these are to/from the same texture. { ScopedTimer timer("SOR", &varref_timer); - sor.exec(du_dv_tex, equation_tex, diffusivity_tex, level_width, level_height, 5, outer_idx == 0, &timer); + sor.exec(du_dv_tex, equation_red_tex, equation_black_tex, diffusivity_tex, level_width, level_height, 5, outer_idx == 0, &timer); } } @@ -1137,7 +1149,8 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra pool.release_texture(I_x_y_tex); pool.release_texture(beta_0_tex); pool.release_texture(diffusivity_tex); - pool.release_texture(equation_tex); + pool.release_texture(equation_red_tex); + pool.release_texture(equation_black_tex); // Add the differential flow found by the variational refinement to the base flow, // giving the final flow estimate for this level. diff --git a/sor.frag b/sor.frag index 91bf831..047526c 100644 --- a/sor.frag +++ b/sor.frag @@ -1,11 +1,12 @@ #version 450 core in vec2 tc, tc_left, tc_down; -in float element_sum_idx; +in vec2 equation_tc_assuming_left, equation_tc_assuming_right; +in float element_x_idx, element_sum_idx; out vec2 diff_flow; uniform sampler2D diff_flow_tex, diffusivity_tex; -uniform usampler2D equation_tex; +uniform usampler2D equation_red_tex, equation_black_tex; uniform int phase; uniform bool zero_diff_flow; @@ -49,7 +50,18 @@ void main() int color = int(round(element_sum_idx)) & 1; if (color != phase) discard; - uvec4 equation = texture(equation_tex, tc); + uvec4 equation; + vec2 equation_tc; + if ((int(round(element_x_idx)) & 1) == 0) { + equation_tc = equation_tc_assuming_left; + } else { + equation_tc = equation_tc_assuming_right; + } + if (phase == 0) { + equation = texture(equation_red_tex, equation_tc); + } else { + equation = texture(equation_black_tex, equation_tc); + } float inv_A11 = uintBitsToFloat(equation.x); float A12 = uintBitsToFloat(equation.y); float inv_A22 = uintBitsToFloat(equation.z); diff --git a/sor.vert b/sor.vert index 55d1a90..32498de 100644 --- a/sor.vert +++ b/sor.vert @@ -2,9 +2,12 @@ layout(location=0) in vec2 position; out vec2 tc, tc_left, tc_down; +out vec2 equation_tc_assuming_left, equation_tc_assuming_right; +out float element_x_idx; out float element_sum_idx; uniform sampler2D diff_flow_tex, diffusivity_tex; +uniform usampler2D equation_red_tex; void main() { @@ -19,6 +22,17 @@ void main() tc_left = vec2(tc.x - 0.5f / textureSize(diffusivity_tex, 0).x, tc.y); tc_down = vec2(tc.x, tc.y - 0.5f / textureSize(diffusivity_tex, 0).y); - vec2 element_idx = position * textureSize(diff_flow_tex, 0) - 0.5; + // The equation textures have half the horizontal width, so we need to adjust the texel centers. + // It becomes extra tricky since the SOR texture might be of odd size, and then + // the equation texture is not exactly half the size. + vec2 element_idx = position * textureSize(diff_flow_tex, 0) - 0.5f; + float equation_texel_number_assuming_left = element_idx.x / 2.0f; + float equation_texel_number_assuming_right = (element_idx.x - 1.0f) / 2.0f; + equation_tc_assuming_left.x = (equation_texel_number_assuming_left + 0.5f) / textureSize(equation_red_tex, 0).x; + equation_tc_assuming_right.x = (equation_texel_number_assuming_right + 0.5f) / textureSize(equation_red_tex, 0).x; + equation_tc_assuming_left.y = tc.y; + equation_tc_assuming_right.y = tc.y; + + element_x_idx = element_idx.x; element_sum_idx = element_idx.x + element_idx.y; } -- 2.39.2