From 7e9b580546f3cc81259c20fb3d8f8a06632e1e9d Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 14 Jun 2024 15:43:34 +0200 Subject: [PATCH 1/3] Fix #119360: Precision issue with cycle modifier The issue was a floating point precision issue between the number of the cycle and the time within the cycle (`cycle` and `cyct`). Due to that the `cycle` would advance to the next whole number while the `cycle time` is still slightly under that. This is fixed by calculating the `cycle` with double precision. This has a measurable performance impact (for `fcm_cycles_time`) | Before | After | | - | - | | 39ns | 44ns | For fcurves with a cycle modifier, this code is run at every evaluate call. The only time when that would be an issue is in the graph editor, where there is an evaluate call for roughly every pixel for curves that have a modifier. However even in that scenario the performance is the same within run to run variance (for `graph_draw_curves`) | Before | After | | - | - | | 91565ns | 91430ns | Pull Request: https://projects.blender.org/blender/blender/pulls/123222 --- source/blender/blenkernel/intern/fmodifier.cc | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/intern/fmodifier.cc b/source/blender/blenkernel/intern/fmodifier.cc index 06c416261b0..5afac449d53 100644 --- a/source/blender/blenkernel/intern/fmodifier.cc +++ b/source/blender/blenkernel/intern/fmodifier.cc @@ -624,7 +624,7 @@ static float fcm_cycles_time( { const FMod_Cycles *data = (FMod_Cycles *)fcm->data; tFCMED_Cycles *storage = static_cast(storage_); - float prevkey[2], lastkey[2], cycyofs = 0.0f; + float firstkey[2], lastkey[2], cycyofs = 0.0f; short side = 0, mode = 0; int cycles = 0; float ofs = 0; @@ -642,11 +642,11 @@ static float fcm_cycles_time( /* calculate new evaltime due to cyclic interpolation */ if (fcu->bezt) { - const BezTriple *prevbezt = fcu->bezt; - const BezTriple *lastbezt = prevbezt + fcu->totvert - 1; + const BezTriple *firstbezt = &fcu->bezt[0]; + const BezTriple *lastbezt = &fcu->bezt[fcu->totvert - 1]; - prevkey[0] = prevbezt->vec[1][0]; - prevkey[1] = prevbezt->vec[1][1]; + firstkey[0] = firstbezt->vec[1][0]; + firstkey[1] = firstbezt->vec[1][1]; lastkey[0] = lastbezt->vec[1][0]; lastkey[1] = lastbezt->vec[1][1]; @@ -656,8 +656,8 @@ static float fcm_cycles_time( const FPoint *prevfpt = fcu->fpt; const FPoint *lastfpt = prevfpt + fcu->totvert - 1; - prevkey[0] = prevfpt->vec[0]; - prevkey[1] = prevfpt->vec[1]; + firstkey[0] = prevfpt->vec[0]; + firstkey[1] = prevfpt->vec[1]; lastkey[0] = lastfpt->vec[0]; lastkey[1] = lastfpt->vec[1]; @@ -667,12 +667,12 @@ static float fcm_cycles_time( * 1) if in data range, definitely don't do anything * 2) if before first frame or after last frame, make sure some cycling is in use */ - if (evaltime < prevkey[0]) { + if (evaltime < firstkey[0]) { if (data->before_mode) { side = -1; mode = data->before_mode; cycles = data->before_cycles; - ofs = prevkey[0]; + ofs = firstkey[0]; } } else if (evaltime > lastkey[0]) { @@ -690,17 +690,18 @@ static float fcm_cycles_time( /* find relative place within a cycle */ { /* calculate period and amplitude (total height) of a cycle */ - const float cycdx = lastkey[0] - prevkey[0]; - const float cycdy = lastkey[1] - prevkey[1]; + const float cycdx = lastkey[0] - firstkey[0]; + const float cycdy = lastkey[1] - firstkey[1]; /* check if cycle is infinitely small, to be point of being impossible to use */ if (cycdx == 0) { return evaltime; } - /* calculate the 'number' of the cycle */ - const float cycle = (float(side) * (evaltime - ofs) / cycdx); - + /* Calculate the 'number' of the cycle. Needs to be a double to combat precision issues like + * #119360. With floats it can happen that the `cycle` jumps to the next full number, while + * `cyct` below is still behind. */ + const double cycle = side * (double(evaltime) - double(ofs)) / double(cycdx); /* calculate the time inside the cycle */ const float cyct = fmod(evaltime - ofs, cycdx); @@ -730,10 +731,10 @@ static float fcm_cycles_time( /* special case for cycle start/end */ if (cyct == 0.0f) { - evaltime = (side == 1 ? lastkey[0] : prevkey[0]); + evaltime = (side == 1 ? lastkey[0] : firstkey[0]); if ((mode == FCM_EXTRAPOLATE_MIRROR) && (int(cycle) % 2)) { - evaltime = (side == 1 ? prevkey[0] : lastkey[0]); + evaltime = (side == 1 ? firstkey[0] : lastkey[0]); } } /* calculate where in the cycle we are (overwrite evaltime to reflect this) */ @@ -744,7 +745,7 @@ static float fcm_cycles_time( * (result of fmod will be negative, and with different phase). */ if (side < 0) { - evaltime = prevkey[0] - cyct; + evaltime = firstkey[0] - cyct; } else { evaltime = lastkey[0] - cyct; @@ -752,9 +753,9 @@ static float fcm_cycles_time( } else { /* the cycle is played normally... */ - evaltime = prevkey[0] + cyct; + evaltime = firstkey[0] + cyct; } - if (evaltime < prevkey[0]) { + if (evaltime < firstkey[0]) { evaltime += cycdx; } } From 1fec22e350b03bb839fddffc6b5a840993820c14 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 14 Jun 2024 15:20:13 +0200 Subject: [PATCH 2/3] Fix #104061: RenderEngine Python example does not work in background mode And add example of how to use bl_use_gpu_context. --- ...rEngine.py => bpy.types.RenderEngine.1.py} | 9 +++-- .../examples/bpy.types.RenderEngine.2.py | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) rename doc/python_api/examples/{bpy.types.RenderEngine.py => bpy.types.RenderEngine.1.py} (96%) create mode 100644 doc/python_api/examples/bpy.types.RenderEngine.2.py diff --git a/doc/python_api/examples/bpy.types.RenderEngine.py b/doc/python_api/examples/bpy.types.RenderEngine.1.py similarity index 96% rename from doc/python_api/examples/bpy.types.RenderEngine.py rename to doc/python_api/examples/bpy.types.RenderEngine.1.py index 0b8795340ad..6c2558f85cf 100644 --- a/doc/python_api/examples/bpy.types.RenderEngine.py +++ b/doc/python_api/examples/bpy.types.RenderEngine.1.py @@ -5,8 +5,6 @@ Simple Render Engine import bpy import array -import gpu -from gpu_extras.presets import draw_texture_2d class CustomRenderEngine(bpy.types.RenderEngine): @@ -95,6 +93,10 @@ class CustomRenderEngine(bpy.types.RenderEngine): # Blender will draw overlays for selection and editing on top of the # rendered image automatically. def view_draw(self, context, depsgraph): + # Lazily import GPU module, so that the render engine works in + # background mode where the GPU module can't be imported by default. + import gpu + region = context.region scene = depsgraph.scene @@ -116,6 +118,8 @@ class CustomRenderEngine(bpy.types.RenderEngine): class CustomDrawData: def __init__(self, dimensions): + import gpu + # Generate dummy float image buffer self.dimensions = dimensions width, height = dimensions @@ -134,6 +138,7 @@ class CustomDrawData: del self.texture def draw(self): + from gpu_extras.presets import draw_texture_2d draw_texture_2d(self.texture, (0, 0), self.texture.width, self.texture.height) diff --git a/doc/python_api/examples/bpy.types.RenderEngine.2.py b/doc/python_api/examples/bpy.types.RenderEngine.2.py new file mode 100644 index 00000000000..e2bf261b261 --- /dev/null +++ b/doc/python_api/examples/bpy.types.RenderEngine.2.py @@ -0,0 +1,36 @@ +""" +GPU Render Engine ++++++++++++++++++ +""" + +import bpy + + +class CustomGPURenderEngine(bpy.types.RenderEngine): + bl_idname = "CUSTOM_GPU" + bl_label = "Custom GPU" + + # Request a GPU context to be created and activated for the render method. + # This may be used either to perform the rendering itself, or to allocate + # and fill a texture for more efficient drawing. + bl_use_gpu_context = True + + def render(self, depsgraph): + # Lazily import GPU module, since GPU context is only created on demand + # for rendering and does not exist on register. + import gpu + + # Perform rendering task. + pass + + +def register(): + bpy.utils.register_class(CustomGPURenderEngine) + + +def unregister(): + bpy.utils.unregister_class(CustomGPURenderEngine) + + +if __name__ == "__main__": + register() From bda53799d545af124ce3243df22e5c5ed74d6063 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 14 Jun 2024 15:22:24 +0200 Subject: [PATCH 3/3] Cleanup: make format --- source/blender/gpu/opengl/gl_compilation_subprocess.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/opengl/gl_compilation_subprocess.cc b/source/blender/gpu/opengl/gl_compilation_subprocess.cc index 937dd8b6953..d7a2383d1e9 100644 --- a/source/blender/gpu/opengl/gl_compilation_subprocess.cc +++ b/source/blender/gpu/opengl/gl_compilation_subprocess.cc @@ -6,10 +6,10 @@ #if BLI_SUBPROCESS_SUPPORT -# include "BLI_tempfile.h" # include "BLI_fileops.hh" # include "BLI_hash.hh" # include "BLI_path_util.h" +# include "BLI_tempfile.h" # include "CLG_log.h" # include "GHOST_C-api.h" # include "GPU_context.hh"