Shöve 1.0.6 - Sometimes Less is More!

Cover Image
  • Wednesday, Apr 23, 2025
  • read

If you’ve been following our game development journey with LÖVE, you’ll know we’ve been working on Shöve, a resolution-handling and rendering library that makes scaling your game to different screen sizes a breeze. Today we’re chuffed to announce the release of Shöve 1.0.6, a performance-focused update that brings some significant improvements!

The Performance Paradox 🤔

You’d think adding clever optimisations would always improve performance, right? Well, this release taught us an important lesson about premature optimisation.

One of the most significant changes in this release was actually removing the custom layer batching system. After extensive benchmarking, we discovered that LÖVE’s built-in optimisations were already efficiently handling the batching of similar draw operations. Our clever custom implementation was actually adding computational overhead in Lua without providing any measurable benefits. Oops! 😅

This is a classic example of “less is more” - by removing unnecessary complexity and letting LÖVE’s native systems do their thing, we’ve achieved cleaner code and better performance. Win-win! 🎉

Despite removing the custom layer batching, our profiling identified several other critical areas where thoughtful optimisations could significantly improve Shöve’s performance 📈

What’s New in Shöve 1.0.6

Here’s a rundown of the key improvements in this release:

🎮 Smarter Canvas Management

One of the biggest performance improvements comes from implementing separate canvas pools for standard and stencil buffers. Previously, Shöve was creating canvases too eagerly and not releasing them effectively, leading to excessive memory use. Now, instead of creating new canvases for every operation, we reuse them from our pools, significantly reducing memory allocations.

The implementation properly supports LÖVE 11.5’s canvas API by using direct stencil parameter passing instead of options tables, making canvas recycling more efficient and reducing garbage collection pressure during rendering. This is particularly noticeable when working with many layers or when you’re making frequent layer transitions and our testing reveals about 4% memory reduction in these scenarios.

🖌️ Optimised Shader Effects Pipeline

We’ve completely revamped how multiple shader effects are applied, implementing a more efficient ping-pong buffer system that reduces memory allocations and state changes. This optimisation uses persistent dual canvases and streamlined coordinate handling to improve performance when applying multiple shader effects.

The change significantly reduces graphics state transitions and memory churn during effect rendering, which is particularly beneficial for complex scenes with multiple shader effects. If you’re into fancy visual effects in your games, you’ll definitely notice the difference!

📊 Profiler Improvements

The profiler has also seen significant optimisations:

  1. Optimised memory allocation - We’ve eliminated unnecessary table allocations in the diagnostic system by pre-initializing the complete table structure once. This reduces garbage collection pressure, resulting in more consistent frame times.

  2. Canvas-based caching for overlays - The profiler overlay is now rendered to a canvas only when its content actually changes, rather than every frame. This makes the profiler much less intrusive to gameplay performance, allowing you to keep it enabled during development with minimal impact.

  3. Better FPS overlay - We’ve refactored the average FPS calculation for better accuracy and optimised overlay updates to reduce unnecessary calculations when only the FPS counter is visible.

🎭 Stencil State Tracking System

We’ve added a stencil state tracking system. This introduces a setStencilTest() function that tracks and optimizes stencil state changes, properly initializing and resetting all state variables at frame boundaries to reduce pipeline reconfiguration overhead.

Canvas Interception - A Game Changer from 1.0.5 🎯

Before diving into the benchmarks, we want to highlight a fantastic feature that was introduced in Shöve 1.0.5.

Shöve now temporarily overrides the love.graphics.setCanvas() function during the beginDraw()/endDraw() cycle, which allows it to intelligently track when you’re switching to your own canvases and back.

This might sound technical, but the practical benefit is huge - you can now freely use your own canvases within Shöve’s rendering cycle without breaking anything!

Prior to this change, if you tried to create and use your own canvas within Shöve’s draw cycle, your carefully positioned content might end up in the wrong place, or worse, not appear at all! Now Shöve detects when you’re using your own canvas and automatically handles all the state management behind the scenes.

What’s really clever about this implementation is that it seamlessly maintains the correct transform states. When you switch to your canvas, Shöve automatically resets the transforms so you can draw with standard coordinates. Then when you switch back, Shöve restores its own transforms so everything aligns perfectly.

For game developers, this means you can use advanced rendering techniques like offscreen buffers, post-processing effects, or particle systems that require their own canvases, all while maintaining perfect integration with Shöve’s scaling and positioning. No more hacking around or fighting the library - it all “just works” ️™️

Show Me the Numbers! 📊

Now for everyone’s favourite part - benchmark results! We tested Shöve 1.0.5 against 1.0.6 on two different hardware configurations, and the improvements are quite impressive.

Desktop Workstation (AMD Ryzen 9 5950X + Radeon RX 7900 GRE)

On a development workstation with discrete graphics, we saw an average improvement of 30 FPS or 3.5% across all demo scenes. The “parallax” demo, which is the most complex, saw the biggest percentage improvement at 5%.

Laptop System (AMD Ryzen 5 PRO 4650U with integrated graphics)

Where things get really interesting is on lower-powered hardware. On a laptop with integrated graphics, the improvements were much more dramatic - an average of 84 FPS or 12.9% across all demos! The “parallax” demo saw a whopping 20.2% improvement.

This is brilliant news for game developers targeting the Steam Deck and similar handheld gaming devices, as the optimisations in Shöve 1.0.6 benefit systems with integrated graphics far more dramatically than high-end rigs – perfect for squeezing extra performance out of portable gaming hardware!

If you’re curious about how we gathered these benchmarks, they were run using LÖVE 11.5 with the demos running in fullscreen with VSync disabled and the full Shöve profile overlay enabled. The testing was done on NixOS 24.11 with Linux 6.12.23 and Mesa 24.2.8.

A Lesson in Optimisation 🎓

This release reinforces an important development principle: always benchmark your optimisations. What seems like a clever optimisation in theory might actually be adding unnecessary overhead in practice. Trust the data, not your intuition!

The improvements in Shöve 1.0.6 were achieved through a combination of:

  1. Adding efficient systems (canvas pooling, stencil tracking)
  2. Improving existing code (effect pipeline, profiler)
  3. Removing unnecessary complexity (custom batching)

It’s that third point that really drove home the lesson. Sometimes the best optimisation is the code you don’t write!

Try It Out! 🎮

Shöve is available now on GitHub.

If you’re developing a game with LÖVE, give it a try and let us know what you think!

 Discord 

The performance improvements are particularly noticeable on lower-end hardware, so it’s a great way to make your game more accessible to a wider audience.

Happy coding! 💻

Join the Community

 Steam   Itch.io   BlueSky   Mastodon 

📷 Photo by K8 on Unsplash.