Lots to talk about this go around, so buckle up!


Focal Engine

We mentioned we would have a much more sizable updates on Focal’s development this month, and we meant it. Below, you’ll find fairly long, and technical updates from both of our full time shader devs, starting with our lead dev, dotModded, and ending with someone you guys don’t hear from all that often, Jakemichie97!

I’ll get out of the way and let dotModded take it from here though:

Naming Convention Changes

As some of you know, Focal Alpha is the code name we’re using for the OpenGL version of Focal Engine used to develop more advanced shaders that correspond to Vulkan design principles. Whereas Focal VK, or just Focal Engine being intended for the long running release. A standalone renderer meant to attach to another game engine; offering a configurable modern rendering pipeline to shader developers and artists. While hopefully breaking down barriers to entry and allowing an engaging learning environment.

Currently the latest versions of our shaders (Continuum EA and RT) are running on Focal Alpha, an extension of sorts to Optifines adaptation of the Shaders mod, EA is awaiting final polishes for beta/full release, and RT is awaiting VK extensions to properly ray trace the full environment. We started this endeavor in December of 2020 and after 3 full years of development, a language change, and dramatic shifts in the modding environment in mc, it’s a good time to take a look at where we are, and how much longer it’s going to take. Realistically I believe Focal won’t be released for at least another year. Rust has accelerated this prediction of mine significantly as the previous project was quickly becoming hard to debug. This is not exactly a fault of the language, rust just enforces better design.

This doesn’t mean we’re not nearing major milestones that we’re excited to share with everyone. With the Lua system coming together from Jake, the tracked value/dependency system being developed by all of us, and presentation/window management being handled by me we should close in on something capable of running shaders in the next few months! This doesn’t mean in Minecraft however, baby steps, this software needs to be robust, performant, and simple to use without imposing limitations. I can’t even give an approximated time to Minecraft yet as there’s simply too many unknowns still.

Focal Alpha’s Current Status

Focal Alphas delays and lack of updates have been rough. It’s been a very hard balance to nail down work distribution between Alpha and VK, especially since work on Alpha will be thrown away as soon as VK rolls around. As much as we want to get VK out, trust me OpenGL is unkind; out of necessity, we will be shifting to showing more attention to Alpha as the development of VK drags on.

On that note; Focal Alphas development environment is extremely complex, as I’ve described in previous posts. This complexity only compounds as our dependence on optifine continues, and as new Minecraft versions are released. We initially designed the environment to facilitate Focal VK’s compilation, where we have one core JNI layer, with individual version/api specifics in each platform, allowing for unilateral development of all versions at once. However this is not how the development of Focal Alpha began, Focal Alpha builds are only in forge, share no common code, and have their own custom platform per version with added complexity for compatibility with optifine. This was not the intended long term design goal of Focal Engine, and was merely a way to get going quickly.

We don’t have a firm plan at this time to build a second environment that better facilitates Focal Alphas development, but we know we need to come up with one, and are working on it. It’s likely we will be ditching patchwork, and compiling for both fabric and forge as we would with VK, along with updating to neoforge. This should put us in a far better position moving forward with maintaining alpha until VK’s release.

Focal VK Progress

We have finally finalized the design of the shared swapchain from GL to VK, along with compositing and presentation, while resizing…. We should have some pictures coming out soon of the renderer displaying both GL and VK from the same window. We wanted Focal Engine to allow multiple windows, perspectives, and UI’s to be configured by the render pack developer for spanning monitors in full screen, or having dedicated control windows with custom UI elements for cinematography. All windows in Focal Engine are backed by a VK context to support compositing with the various elements. Including the game engines default frame buffers, existing buffers in Vulkan defined by the pack developer, web UI elements, and more. Additional windows are planned to be highly customizable, supporting multiple color formats and infinite customization potential from the pack developer to build useful menus and control screens, or support multimonitor setups in native fullscreen. Beyond the windows the compositing step needed to be and still needs to be thought out more, as it needs special treatment in the render graph.

There can only be one compositing stage per window, and it needs to remain extremely performant to not desync from the OpenGL/WebUI’s swap chain. By default Focal Engine always composites and presents as soon as the VK Renderer completes, compositing the latest complete information known to the CPU after render graph submission from other sources. This allows variable frame rate technology to remain effective for the VK output, which should capture all vanilla in world elements, and most modded in world elements with some exceptions. The OpenGL swap chain exists to keep compatibility with existing mods that call OpenGL directly, doing custom rendering outside of Minecraft/modding api’s. These outputs from the games OpenGL output are provided only at the composition step to handle colorspace conversions and HDR tonemapping. OpenGL and VK interoperability aside the swap chain was rather complex to end up with such a simple design in the end. Memory allocation happens in the Vulkan allocation threads, and memory import for the frame happens at the start of the OpenGL render thread. OpenGL is optionally single, double, or triple buffered depending on memory limitations on the system and load from the pack. Memory cant be interacted with while it’s either being rendered too by OpenGL, or being presented by Vulkan. During a resize event n images are created in Vulkan to be imported by OpenGL, initially we planned to use an event bus for this cross thread communication, however in the end it was too bloated and cluttered to maintain and optimize, thus we ended up with a fast lockless system abusing Options in rust for detection of incoming textures, and correctly cleaning up old buffers after VK presentation if they’re even needed

Next up, the promised update from Jakemichie97!

Render Graph

Almost the entire render graph has been redesigned from the ground up. To provide a bit of context that will help with understanding the rest of this, I’ll quickly explain what a render graph is and why it’s necessary: Put simply, a render graph is a system used in modern game engines that is able to analyze a high level overview of the different passes within a frame (object draws, compute shader dispatches, raytracing pipeline dispatches, raytracing acceleration structure updates, data transfer operations, etc.), determine what passes depends on other passes (ie object draw A reads from an image that compute shader B writes to, so A needs to run after B) and efficiently schedule all the passes with dependencies in mind while also managing resources such as images, buffers, etc. Without a render graph a developer would have to do all of this themselves which requires keeping track of a lot of information, especially as you scale up to multiple GPU queues and CPU threads. With a render graph a developer just has to provide basic information about what resources a particular pass uses and how it uses them, and the render graph figures out the rest. Render graphs are a necessary part of modern game engines and Focal Engine is no different. With that out of the way, lets move on.

So, we’ve been wanting to use a render graph in Focal Engine since the beginning, and as such we had come up with a design that we thought would have worked quite well. Passes, called stages in Focal Engine terms, would be grouped together within sequences and the render graph would work with sequences of stages, rather than the stages themselves; we thought this would have been a good idea as it separates scheduling and resource management into two separate layers, which somewhat mirrors Vulkan’s separation of in-command-buffer, between-command-buffer-on-same-queue and between-command-buffers-on-different-queues scheduling and resource management. Additionally, everything would have been statically provided upfront, with structures that describe dynamic elements such as loops, branches/if statements, etc.; this is similar to how Focal Engine Alpha’s render pass system works so it was familiar to us, and we knew that we could extend it now that we were moving from JSON to Lua and can have actual code be part of this. Well, both choices turned out to be wrong as they led to a few major issues when we tried to further implement the render graph: having everything be static with structures describing dynamic elements would have led to bloated and clunky code, and having sequences of stages be the unit of execution for the render graph made the Lua side quite verbose, worsened the problem with bloated and clunky code, and would have led to less efficient scheduling and resource management as the render graph has an added layer of indirection to work with. As such we’ve decided to ditch this design, though we did try to find a way to make it work and trust me, we couldn’t.

With the old design out of the way, lets talk about the new design. The new design solves basically all of the problems of the old design, and even offers some improvements that should help with reusability on both ends; ours and render pack developers. Firstly, the render graph is now completely dynamic and code driven. When you’re writing your Lua script to register stages and resources, you will be asked to provide what we’re currently calling the rendering function to Focal Engine. This is a Lua function that will be called potentially every frame (more on that later), that gives you a representation of the render graph timeline (name TBD) to place stages onto. Secondly, stages have been stripped back to be closer to Vulkan’s pipelines. Where previously you would provide information such as the used resources or the draw/dispatch parameters within the stage itself, you now provide that in the rendering function when you add the stage to the render graph. This allows you to reuse the same stage multiple times, to the point where you could have a single generic shadow map stage that you reuse multiple times with different cameras and output images. Thirdly, control flow has been removed from the render graph itself and instead we’re now relying on Lua’s own native control flow. Since you’re adding stages to the render graph in Lua code there’s nothing stopping you from just using a regular Lua if statement or for loop if you want some control flow. You’ll also be able to pipe data from Lua directly into a stage via push constants if you want to, say, let the shader know what Lua for loop iteration it originated from. Fourth, you will be given a representation of the tracked value system which will let you import values into the rendering function from the tracked value system (but only into the rendering function, if you decide to store a tracked value outside and reuse in another frame then we won’t stop you but that will be considered undefined behavior since we can’t account for that). Our current idea is to have you ask for the tracked values that you wish to import as this will let us cache the render graph when the rendering function doesn’t need to be changed. And lastly, sequences have been removed since they serve no purpose in the new render graph design. Well, they currently don’t, we have ideas for how we could reintroduce them.

We don’t have concrete code examples yet since much of this is still just on paper, though I have been prototyping outside of Focal Engine and I do think this new design will work. For a rough overview of what this may look like, here’s an example:

For a bit of a look into the future regarding the render graph, we still need to figure out how we’re going to deal with multiple GPU queues and we would also like to have some way for developers to insert custom dependencies into the render graph. The current idea is to add an “anchor” dependency which will force all stages added after the anchor to be executed after the anchor, and a “syncpoint” dependency which works somewhat like an anchor but will be opt-in for each individual stage. Names to be determined, but that’s the current plan. We also need to figure out a way to group together multiple stages under the same label to make debugging and profiling easier, current plan is to add “label spans” to the render graph that essentially act as prefixes to the names of added stages.

Tracked Value System

Some decent progress has also been made on the tracked value system. The developer who was originally working on the tracked value system has been unavailable for a little while now, so we were essentially left alone to implement a system which easily works within C++, but is a pain in Rust. We were planning on just working on Focal Engine until that developer was available again, but we’ve now reached a point where we desperately need the tracked value system, especially now that we’re working on the render graph which, as described above, will have a somewhat tight coupling to the tracked value system. As such we’re having to work on it ourselves, with a bit of help from the developer who was originally working on it. We don’t have it fully working quite yet, but we’re confident that we’ve nailed down a design that will work and be flexible.

First, just a quick overview of what the tracked value system is for context: The tracked value system is essentially a global variable tracking system that lets us have variables that can be globally referenced in different parts of Focal Engine and the render pack. A variable can be registered within the system and will have some memory allocated to store the underlying data. That same variable can then be referenced by name elsewhere, with the reference pointing to that same memory allocation. Variables can be constant or changing and can even be computed from other tracked values. The idea is to have the tracked value system hold different pieces of data such as the current window size, frame index, camera transforms, dimension ID, etc. This data can be registered by Focal Engine itself or the render pack, can contain settings coming from the render pack, and will be usable in many places. The goal is to essentially let you tie a tracked value to any parameter within the render pack, whether it be the size of an image, the format of an image or even some state within a render graph stage. That way settings can manipulate anything, with little to no restrictions.

Focal Engine Alpha uses a more primitive form of the tracked value system. Several of the things we have planned are not part of Focal Engine Alpha’s tracked value system, including tracked values computed in Lua from other values, registration from the render pack or the ability to be tied to any parameter. Focal Engine Alpha’s tracked value system works very well, which is why we’re continuing to use it with Focal Engine, with some changes of course. So far we have the design pretty much nailed down, we have the dependency graph working to determine the update order of tracked values and we’re starting to work on Lua integration and memory allocation. There’s a surprising amount of coupling between the tracked value system and Lua, to the point where we’ve actually had to rethink how Lua will work in Focal Engine since there was a sort of race condition that we discovered with our original design for Lua. Like the render graph, we are prototyping the tracked value system outside of Focal Engine, but once we’ve got Lua integration and memory allocation done then we just need to get tracked value references in, then we’re ready to actually implement it in Focal Engine

That concludes the updates directly from our developers. These updates are a good deal more time consuming to put together, particularly on our developers side, so we hope you find them enlightening and interesting.

Support Focal Engine with a Continuum RT Early Access Subscription

Continuum 2.1

Despite the focus on Focal, Jake managed to deliver a few notable improvements in Continuum 2.1 Beta Build 23 this month, including;

  • Fixing a rare issue where extreme noise would appear on PBR resourcepack textures due to the sun spot reflection being reflected again
  • Added new settings menu for Water in the Material Override menu: Coefficient Settings. These allow you to make anything from slight tweaks to water color, to sweeping changes. If you want blood red water for a Halloween themed screenshot, you can now achieve it quickly and easily!
  • Numerous settings title/description tweaks

With these fixes and enhancements, 2.1 moves ever closer to Release Candidate status, however, we have still only laid the preliminary groundwork for settings presets, and until we find the time to sit down, build those out and dial them in, 2.1 will remain in Beta. This does mean more time for smaller, easier to implement features the community suggests (like the water Coefficient Settings) to be added, since those are generally much easier to work in around our dev teams workload with Focal Engine, but work on settings presets themselves are going to be a good bit more time intensive, especially to get to a Release Candidate ready state.

See images below for a screenshot of the new settings menu and a few examples of what can be done with it!


Stratum

Mythical decided to focus on some of the creepier remaining textures for this months update, specifically; Sculk. Most of the variants of the main Sculk blocks were added, as well as one of the last Deepslate blocks (Chiseled), and some notable improvements were made to a few previously added textures!

See the changelog below for detailed update notes.

Stratum Build 41 Changelog

Added

  • Sculk Block
  • Sculk Vein
  • Sculk Catalyst
  • Chiseled Deepslate

Tweaked

  • Updated Cherry Leaves with branches to increase realism and fix floating Blossoms
  • Tweaked Sunflower head distance from stem

Click an image to enlarge and enable gallery view


Miscellaneous Updates

Before we close out this progress update, we have one last order of business to address. Our partnership with Studio Archetype has officially ended. We came to realize that our goals have diverged, and thus the partnership no longer made sense. We harbor no ill will towards them, and wish them luck in their future endeavors!

Also, with the Fall season now upon us, we may have another screenshot contest soon, so if you’re reading this and would like to participate in such an event, keep an eye on our social media / Discord server.

As always, thank you for your continued support, and kudos to all that have made it all the way through what is no doubt our longest blog ever!