From a5204594f8ed09c1bd91c24481dc96e5a3000520 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 2 Aug 2018 17:59:58 +0200 Subject: [PATCH] Fix an issue where we would lose >1 ms for computing flow on NVIDIA, due to lack of fast clears. --- equations.frag | 13 ++++++--- flow.cpp | 57 +++++++++++++++++++++++---------------- smoothness.frag | 72 +++++++++++++++++++++++++++++++++++-------------- sor.frag | 32 +++++++++++++--------- 4 files changed, 115 insertions(+), 59 deletions(-) diff --git a/equations.frag b/equations.frag index 51e1394..6c4fa5d 100644 --- a/equations.frag +++ b/equations.frag @@ -14,6 +14,8 @@ uniform float delta; // Relative weighting of gradient term. uniform float gamma; +uniform bool zero_diff_flow; + // Similar to packHalf2x16, but the two values share exponent, and are stored // as 12-bit fixed point numbers multiplied by that exponent (the leading one // can't be implicit in this kind of format). This allows us to store a much @@ -56,9 +58,14 @@ uint pack_floats_shared(float a, float b) void main() { // Read the flow (on top of the u0/v0 flow). - vec2 diff_flow = texture(diff_flow_tex, tc).xy; - float du = diff_flow.x; - float dv = diff_flow.y; + float du, dv; + if (zero_diff_flow) { + du = dv = 0.0f; + } else { + vec2 diff_flow = texture(diff_flow_tex, tc).xy; + du = diff_flow.x; + dv = diff_flow.y; + } // Read the first derivatives. vec2 I_x_y = texture(I_x_y_tex, tc).xy; diff --git a/flow.cpp b/flow.cpp index cb747c9..d914d68 100644 --- a/flow.cpp +++ b/flow.cpp @@ -586,6 +586,8 @@ void Densify::exec(GLuint tex0_view, GLuint tex1_view, GLuint flow_tex, GLuint d glBlendFunc(GL_ONE, GL_ONE); glBindVertexArray(densify_vao); fbos.render_to(dense_flow_tex); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, width_patches * height_patches); } @@ -713,7 +715,7 @@ void Derivatives::exec(GLuint input_tex, GLuint I_x_y_tex, GLuint beta_0_tex, in class ComputeSmoothness { public: ComputeSmoothness(); - void exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height); + void exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, bool zero_diff_flow); private: PersistentFBOSet<2> fbos; @@ -724,7 +726,7 @@ private: GLuint smoothness_vao; GLuint uniform_flow_tex, uniform_diff_flow_tex; - GLuint uniform_alpha; + GLuint uniform_alpha, uniform_zero_diff_flow; }; ComputeSmoothness::ComputeSmoothness() @@ -745,15 +747,17 @@ ComputeSmoothness::ComputeSmoothness() uniform_flow_tex = glGetUniformLocation(smoothness_program, "flow_tex"); uniform_diff_flow_tex = glGetUniformLocation(smoothness_program, "diff_flow_tex"); uniform_alpha = glGetUniformLocation(smoothness_program, "alpha"); + uniform_zero_diff_flow = glGetUniformLocation(smoothness_program, "zero_diff_flow"); } -void ComputeSmoothness::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height) +void ComputeSmoothness::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, bool zero_diff_flow) { glUseProgram(smoothness_program); bind_sampler(smoothness_program, uniform_flow_tex, 0, flow_tex, nearest_sampler); bind_sampler(smoothness_program, uniform_diff_flow_tex, 1, diff_flow_tex, nearest_sampler); glProgramUniform1f(smoothness_program, uniform_alpha, vr_alpha); + glProgramUniform1i(smoothness_program, uniform_zero_diff_flow, zero_diff_flow); glViewport(0, 0, level_width, level_height); @@ -783,7 +787,7 @@ void ComputeSmoothness::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoot 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 smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height); + void exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height, bool zero_diff_flow); private: PersistentFBOSet<1> fbos; @@ -797,7 +801,7 @@ private: GLuint uniform_diff_flow_tex, uniform_base_flow_tex; GLuint uniform_beta_0_tex; GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex; - GLuint uniform_gamma, uniform_delta; + GLuint uniform_gamma, uniform_delta, uniform_zero_diff_flow; }; SetupEquations::SetupEquations() @@ -824,9 +828,10 @@ SetupEquations::SetupEquations() uniform_smoothness_y_tex = glGetUniformLocation(equations_program, "smoothness_y_tex"); uniform_gamma = glGetUniformLocation(equations_program, "gamma"); uniform_delta = glGetUniformLocation(equations_program, "delta"); + 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 smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height) +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 smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height, bool zero_diff_flow) { glUseProgram(equations_program); @@ -839,6 +844,7 @@ void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex bind_sampler(equations_program, uniform_smoothness_y_tex, 6, smoothness_y_tex, zero_border_sampler); glProgramUniform1f(equations_program, uniform_delta, vr_delta); glProgramUniform1f(equations_program, uniform_gamma, vr_gamma); + glProgramUniform1i(equations_program, uniform_zero_diff_flow, zero_diff_flow); glViewport(0, 0, level_width, level_height); glDisable(GL_BLEND); @@ -854,7 +860,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 smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations, ScopedTimer *sor_timer); + void exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer); private: PersistentFBOSet<1> fbos; @@ -867,7 +873,7 @@ private: GLuint uniform_diff_flow_tex; GLuint uniform_equation_tex; GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex; - GLuint uniform_phase; + GLuint uniform_phase, uniform_zero_diff_flow; }; SOR::SOR() @@ -890,9 +896,10 @@ SOR::SOR() uniform_smoothness_x_tex = glGetUniformLocation(sor_program, "smoothness_x_tex"); uniform_smoothness_y_tex = glGetUniformLocation(sor_program, "smoothness_y_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 smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations, ScopedTimer *sor_timer) +void SOR::exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations, bool zero_diff_flow, ScopedTimer *sor_timer) { glUseProgram(sor_program); @@ -901,6 +908,8 @@ void SOR::exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_te bind_sampler(sor_program, uniform_smoothness_y_tex, 2, smoothness_y_tex, zero_border_sampler); bind_sampler(sor_program, uniform_equation_tex, 3, equation_tex, nearest_sampler); + glProgramUniform1i(sor_program, uniform_zero_diff_flow, zero_diff_flow); + // NOTE: We bind to the texture we are rendering from, but we never write any value // that we read in the same shader pass (we call discard for red values when we compute // black, and vice versa), and we have barriers between the passes, so we're fine @@ -919,6 +928,10 @@ void SOR::exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_te } { ScopedTimer timer("Black pass", sor_timer); + if (zero_diff_flow && i == 0) { + // Not zero anymore. + glProgramUniform1i(sor_program, uniform_zero_diff_flow, 0); + } glProgramUniform1i(sor_program, uniform_phase, 1); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (i != num_iterations - 1) { @@ -1174,9 +1187,8 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra // Densification. - // Set up an output texture (initially zero). + // Set up an output texture (cleared in Densify). GLuint dense_flow_tex = pool.get_texture(GL_RGB16F, level_width, level_height); - glClearTexImage(dense_flow_tex, 0, GL_RGB, GL_FLOAT, nullptr); // And draw. { @@ -1220,9 +1232,9 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra pool.release_texture(I_tex); // We need somewhere to store du and dv (the flow increment, relative - // to the non-refined base flow u0 and v0). It starts at zero. + // to the non-refined base flow u0 and v0). It's initially garbage, + // but not read until we've written something sane to it. GLuint du_dv_tex = pool.get_texture(GL_RG16F, level_width, level_height); - glClearTexImage(du_dv_tex, 0, GL_RG, GL_FLOAT, nullptr); // And for smoothness. GLuint smoothness_x_tex = pool.get_texture(GL_R16F, level_width, level_height); @@ -1237,20 +1249,20 @@ GLuint DISComputeFlow::exec(GLuint tex0, GLuint tex1, ResizeStrategy resize_stra // both in x and y direction. { ScopedTimer timer("Compute smoothness", &varref_timer); - compute_smoothness.exec(base_flow_tex, du_dv_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height); + compute_smoothness.exec(base_flow_tex, du_dv_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height, outer_idx == 0); } // 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, smoothness_x_tex, smoothness_y_tex, equation_tex, level_width, level_height); + setup_equations.exec(I_x_y_tex, I_t_tex, du_dv_tex, base_flow_tex, beta_0_tex, smoothness_x_tex, smoothness_y_tex, equation_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, smoothness_x_tex, smoothness_y_tex, level_width, level_height, 5, &timer); + sor.exec(du_dv_tex, equation_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height, 5, outer_idx == 0, &timer); } } @@ -1365,6 +1377,12 @@ void Splat::exec(GLuint tex0, GLuint tex1, GLuint forward_flow_tex, GLuint backw fbos.render_to(depth_tex, flow_tex); + // Evidently NVIDIA doesn't use fast clears for glClearTexImage, so clear now that + // we've got it bound. + glClearColor(1000.0f, 1000.0f, 0.0f, 1.0f); // Invalid flow. + glClearDepth(1.0f); // Effectively infinity. + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Do forward splatting. bind_sampler(splat_program, uniform_flow_tex, 2, forward_flow_tex, nearest_sampler); glProgramUniform1i(splat_program, uniform_invert_flow, 0); @@ -1651,13 +1669,6 @@ GLuint Interpolate::exec(GLuint tex0, GLuint tex1, GLuint forward_flow_tex, GLui GLuint flow_tex = pool.get_texture(GL_RG16F, flow_width, flow_height); GLuint depth_tex = pool.get_texture(GL_DEPTH_COMPONENT32F, flow_width, flow_height); // Used for ranking flows. - { - ScopedTimer timer("Clear", &total_timer); - float invalid_flow[] = { 1000.0f, 1000.0f }; - glClearTexImage(flow_tex, 0, GL_RG, GL_FLOAT, invalid_flow); - float infinity = 1.0f; - glClearTexImage(depth_tex, 0, GL_DEPTH_COMPONENT, GL_FLOAT, &infinity); - } { ScopedTimer timer("Splat", &total_timer); diff --git a/smoothness.frag b/smoothness.frag index adc3dcf..218d6df 100644 --- a/smoothness.frag +++ b/smoothness.frag @@ -9,11 +9,16 @@ uniform sampler2D flow_tex, diff_flow_tex; // Relative weighting of smoothness term. uniform float alpha; +uniform bool zero_diff_flow; + // This must be a macro, since the offset needs to be a constant expression. #define get_flow(x_offs, y_offs) \ (textureOffset(flow_tex, tc, ivec2((x_offs), (y_offs))).xy + \ textureOffset(diff_flow_tex, tc, ivec2((x_offs), (y_offs))).xy) +#define get_flow_no_diff(x_offs, y_offs) \ + textureOffset(flow_tex, tc, ivec2((x_offs), (y_offs))).xy + float diffusivity(float u_x, float u_y, float v_x, float v_y) { return alpha * inversesqrt(u_x * u_x + u_y * u_y + v_x * v_x + v_y * v_y + eps_sq); @@ -23,29 +28,56 @@ void main() { float g, g_right, g_up; - // These are shared between some of the diffusivities. - vec2 flow_0_0 = get_flow(0, 0); - vec2 flow_1_1 = get_flow(1, 1); + if (zero_diff_flow) { + // These are shared between some of the diffusivities. + vec2 flow_0_0 = get_flow_no_diff(0, 0); + vec2 flow_1_1 = get_flow_no_diff(1, 1); - // Find diffusivity (g) for this pixel, using central differences. - { - vec2 uv_x = get_flow(1, 0) - get_flow(-1, 0); - vec2 uv_y = get_flow(0, 1) - get_flow( 0, -1); - g = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); - } + // Find diffusivity (g) for this pixel, using central differences. + { + vec2 uv_x = get_flow_no_diff(1, 0) - get_flow_no_diff(-1, 0); + vec2 uv_y = get_flow_no_diff(0, 1) - get_flow_no_diff( 0, -1); + g = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } - // Now find diffusivity for the pixel to the right. - { - vec2 uv_x = get_flow(2, 0) - flow_0_0; - vec2 uv_y = flow_1_1 - get_flow( 1, -1); - g_right = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); - } + // Now find diffusivity for the pixel to the right. + { + vec2 uv_x = get_flow_no_diff(2, 0) - flow_0_0; + vec2 uv_y = flow_1_1 - get_flow_no_diff( 1, -1); + g_right = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } + + // And up. + { + vec2 uv_x = flow_1_1 - get_flow_no_diff(-1, 1); + vec2 uv_y = get_flow_no_diff(0, 2) - flow_0_0; + g_up = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } + } else { + // These are shared between some of the diffusivities. + vec2 flow_0_0 = get_flow(0, 0); + vec2 flow_1_1 = get_flow(1, 1); + + // Find diffusivity (g) for this pixel, using central differences. + { + vec2 uv_x = get_flow(1, 0) - get_flow(-1, 0); + vec2 uv_y = get_flow(0, 1) - get_flow( 0, -1); + g = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } + + // Now find diffusivity for the pixel to the right. + { + vec2 uv_x = get_flow(2, 0) - flow_0_0; + vec2 uv_y = flow_1_1 - get_flow( 1, -1); + g_right = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } - // And up. - { - vec2 uv_x = flow_1_1 - get_flow(-1, 1); - vec2 uv_y = get_flow(0, 2) - flow_0_0; - g_up = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + // And up. + { + vec2 uv_x = flow_1_1 - get_flow(-1, 1); + vec2 uv_y = get_flow(0, 2) - flow_0_0; + g_up = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y); + } } smoothness_x = 0.5 * (g + g_right); diff --git a/sor.frag b/sor.frag index ac5b83c..2b68597 100644 --- a/sor.frag +++ b/sor.frag @@ -8,6 +8,8 @@ uniform sampler2D diff_flow_tex, smoothness_x_tex, smoothness_y_tex; uniform usampler2D equation_tex; uniform int phase; +uniform bool zero_diff_flow; + // See pack_floats_shared() in equations.frag. vec2 unpack_floats_shared(uint c) { @@ -43,21 +45,25 @@ void main() float inv_A22 = uintBitsToFloat(equation.z); vec2 b = unpack_floats_shared(equation.w); - // Subtract the missing terms from the right-hand side - // (it couldn't be done earlier, because we didn't know - // the values of the neighboring pixels; they change for - // each SOR iteration). - float smooth_l = textureOffset(smoothness_x_tex, tc, ivec2(-1, 0)).x; - float smooth_r = texture(smoothness_x_tex, tc).x; - float smooth_d = textureOffset(smoothness_y_tex, tc, ivec2( 0, -1)).x; - float smooth_u = texture(smoothness_y_tex, tc).x; - b += smooth_l * textureOffset(diff_flow_tex, tc, ivec2(-1, 0)).xy; - b += smooth_r * textureOffset(diff_flow_tex, tc, ivec2( 1, 0)).xy; - b += smooth_d * textureOffset(diff_flow_tex, tc, ivec2( 0, -1)).xy; - b += smooth_u * textureOffset(diff_flow_tex, tc, ivec2( 0, 1)).xy; + if (zero_diff_flow) { + diff_flow = vec2(0.0f); + } else { + // Subtract the missing terms from the right-hand side + // (it couldn't be done earlier, because we didn't know + // the values of the neighboring pixels; they change for + // each SOR iteration). + float smooth_l = textureOffset(smoothness_x_tex, tc, ivec2(-1, 0)).x; + float smooth_r = texture(smoothness_x_tex, tc).x; + float smooth_d = textureOffset(smoothness_y_tex, tc, ivec2( 0, -1)).x; + float smooth_u = texture(smoothness_y_tex, tc).x; + b += smooth_l * textureOffset(diff_flow_tex, tc, ivec2(-1, 0)).xy; + b += smooth_r * textureOffset(diff_flow_tex, tc, ivec2( 1, 0)).xy; + b += smooth_d * textureOffset(diff_flow_tex, tc, ivec2( 0, -1)).xy; + b += smooth_u * textureOffset(diff_flow_tex, tc, ivec2( 0, 1)).xy; + diff_flow = texture(diff_flow_tex, tc).xy; + } const float omega = 1.8; // Marginally better than 1.6, it seems. - diff_flow = texture(diff_flow_tex, tc).xy; // From https://en.wikipedia.org/wiki/Successive_over-relaxation. float sigma_u = A12 * diff_flow.y; -- 2.39.2