-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[1.18.X] Add support for user-defined render types in static geometry #8331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1.18.X] Add support for user-defined render types in static geometry #8331
Conversation
|
Currently working with Orion on some changes to try to clean up the patches. |
Additionally, provide an event through which to register render types, both static and not
5f98643 to
fcabe90
Compare
|
I have updated the PR description with all the changes Orion and I have made throughout the day. The patches have been reduced to a minimal footprint and several quality of life improvements have been made, including the addition of a new event for registering custom render types and hooks for uploading custom uniforms to a render type's shader on a per-chunk basis. |
Due to having two people working on this, the PR ended up having slightly varying code styles. We tried to minimize this earlier, but upon further inspection, some things still slipped through. This commit also removes the explicit profiling calls added during render dispatch, as vanilla seems to already do something similar. Other minor tweaks and cleanups, as well as slightly improved documentation are also included.
|
Noting here: @OrionDevelopment made substantial contributions to this PR as noted by the author in the PR description, and therefore cannot triage, review, and assign this PR on grounds of conflict of interest. |
|
My new mod which uses shaders/RenderTypes in static geometry could really use this PR 😛 https://www.curseforge.com/minecraft/mc-mods/literal-sky-block |
sciwhiz12
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I don't fully comprehend rendering, I've gone ahead and looked at this PR, mostly from formatting and documentation angles. I'll leave the bulk of the actual rendering review to @gigaherz. 👀
Aside from the formatting and whatnot, I do have one notable concern in that the test mod seems to only provide a test for a custom solid render type (as I note in one of my review comments). Ideally, it should provide tests for all three render type 'groups', to help look for possible regressions or oversights now and in the future.
In particular, custom translucent render types need testing because of their special interactions with regards to other render types.
Please update the license headers of the files added or modified by this PR to reflect the current year.
| /** | ||
| * Replaced by {@link LevelRenderPhaseManager#getAllKnownTypes()} | ||
| */ | ||
| @Deprecated(forRemoval = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add since = "1.18.1" to this deprecation annotation.
| builder.add(Pair.of(layer.getValue(), deserializationContext.deserialize(GsonHelper.getAsJsonObject(layersObject, layerName), BlockModel.class))); | ||
| } | ||
| var renderTypes = LevelRenderPhaseManager.getInstance().getAllKnownTypes(); | ||
| for (var key : layersObject.keySet()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting: braces on newlines:
| for (var key : layersObject.keySet()) { | |
| for (var key : layersObject.keySet()) | |
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is little off topic but why doesnt this repo have an .editorconfig file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because style is a weird and wacky place - patches and code have different styles, and no matter what we provide some people will choose to override it with their own preference anyway, which makes the whole thing moot.
| @@ -0,0 +1,54 @@ | |||
| package net.minecraftforge.client.renderer; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header.
| * Create using {@link ComplexRenderTypeBuilder}. | ||
| * | ||
| * @see ComplexRenderTypeBuilder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should likely include a @see tag for ComplexRenderType#builder().
| @@ -0,0 +1,37 @@ | |||
| package net.minecraftforge.client.renderer; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header.
| final double cameraY, | ||
| final double cameraZ, | ||
| final Matrix4f projectionMatrix | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting: braces on newlines:
| ) { | |
| ) | |
| { |
| final boolean sortOnUpload, | ||
| final CompositeState compositeState, | ||
| final IBeforeChunkRenderCallback beforeChunkRenderCallback | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting: braces on newlines:
| ) { | |
| ) | |
| { |
| * Adapter class used to dispatch draw calls for vanilla and user-provided static | ||
| * render types during level rendering. | ||
| */ | ||
| public final class LevelRendererAdapter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really an adapter class, but more of a hooks class (in the same vein of ForgeHooksClient).
| public net.minecraft.client.particle.ParticleEngine m_107381_(Lnet/minecraft/core/particles/ParticleType;Lnet/minecraft/client/particle/ParticleProvider;)V # register | ||
| public net.minecraft.client.particle.ParticleEngine$SpriteParticleRegistration | ||
| public net.minecraft.client.renderer.GameRenderer m_109128_(Lnet/minecraft/resources/ResourceLocation;)V # loadEffect | ||
| public net.minecraft.client.renderer.LevelRenderer m_172993_(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to have the mapped name of the method (renderChunkLayer) in a comment after the declaration.
As a thought, is it possible and viable to avoid this AT (and opening up renderChunkLayer to everyone) by passing in a method reference to it to the LevelRendererAdapter hooks?
| this.f_109413_.m_83945_(this.f_109461_.m_91385_()); | ||
| profilerfiller.m_6182_("translucent"); | ||
| + if (!net.minecraftforge.client.renderer.LevelRendererAdapter.getInstance().onRenderTranslucent(this, p_109600_, p_109604_, p_109607_)) { | ||
| this.m_172993_(RenderType.m_110466_(), p_109600_, d0, d1, d2, p_109607_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code in renderChunkLayer shows some hardcoded checks for RenderType.translucent(). Do these checks substantially affect render types which render alongside the vanilla translucent render type?
We may want to expand the test mod to include tests for these three render type 'groups', rather than only registering a custom solid render type.
|
|
||
| public static List<RenderType> m_110506_() { | ||
| - return ImmutableList.of(m_110451_(), m_110457_(), m_110463_(), m_110466_(), m_110503_()); | ||
| + return net.minecraftforge.client.renderer.LevelRenderPhaseManager.getInstance().getKnownTypes(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion this is missing an accompanying patch in RenderBuffers: The RenderBuffers#fixedBuffers map which is used to construct the RenderBuffers#bufferSource needs to contain all chunk layers because requesting a buffer from said buffer source for a certain RenderType after using a different RenderType that isn't in the map of fixed buffers will cause the current data in the buffer to be uploaded to the GPU because "non-fixed" RenderTypes use a shared buffer.
While this is not an issue for the static chunk rendering, which uses the ChunkBufferBuilderPack to get its buffers, I would still expect a RenderType, which has a pre-allocated buffer and can be used in static chunk rendering, to be useable in a dynamic context (i.e. EntityRenderer or BlockEntityRenderer) without causing an upload to the GPU every time I switch the RenderType in use to render the next thing in the dynamic renderer (which kills performance when a few instances of the related Entity or BlockEntity are present).
|
Hello! @amadornes, please leave a comment whether you wish to continue this PR and update it to the latest active version of 1.19.2 (or leave an explanation why this PR's changes are not applicable to 1.19.2). If we do not hear back from you after some time, this PR will be closed for inactivity. If anyone else wishes to continue the work of this PR, please create a PR with a link to this PR for reference. |
|
I'll be porting this 1.19, yep. Just haven't had the time for it 😅 I'll close this and open a new PR targetting 1.19 directly once it's ready. I could keep the same one, but it's going to be redone from scratch due to the all changes introduced in this area during the client cleanup/refactor, so I may as well keep them separate and just reference this PR in the other one. |
Minecraft's render engine supports the creation of custom render types, which allow the user to specify the vertex format and render state (face culling, depth sorting, shaders, etc) for certain parts of their geometry. However, natively these only work on dynamic geometry (entity/blockentity renderers), and not static elements like terrain.
This PR adds the necessary hooks to extend the default set of render types supported for static geometry rendering (solid, cutout_mipped, cutout, translucent, tripwire).
Motivation
The
RenderTypesystem is incredibly powerful, but it's currently limited to just dynamic geometry, which requires a considerable amount of CPU time and GPU bandwidth to gather and upload every single frame. This means that any effect involving static geometry that requires a custom render state or shader (which is not all that uncommon) is creating an unnecessary load on both the CPU and GPU.Implementation
As suggested by @OrionDevelopment, the code from Forge's existing
TierSortingRegistrywas generified and repurposed to provide a method through which developers can register their ownRenderTypeinstances and customize their ordering relative to both Minecraft's built in render types as well as other mods'. The tier sorting registry has been migrated to this generified registry as well.Due to the way Minecraft schedules its rendering, the render types have been split into three phases: solid, translucent and tripwire. Solid render types are rendered before entities, translucent render types are rendered after them, and tripwire render types are rendered after the dynamic buffers have been drawn. This is reflected in the events fired during client initialization to register custom render types:
StaticRenderTypeRegisterEvent.Solid,StaticRenderTypeRegisterEvent.TranslucentandStaticRenderTypeRegisterEvent.Tripwire. These events are fired on the mod bus as they are used to register mod-owned render types.Along with these events, a
RenderTypeRegisterEventhas been added for any mod that may need to register custom render types. This event provides a convenient and consistent place for this to take place, and ensures correct initialization of all required resources has taken place by the time it is fired. This event is also fired on the mod bus as it is used to register mod-owned render types.The render calls for each phase are delegated from
LevelRendererto theLevelRendererAdapterclass, which may decide (based on a Forge client config option) to run vanilla's default code path, or visit each of the vanilla + custom render types for that phase in the configured order.An additional set of interfaces and classes has been created to allow the creation of custom render types which receive a callback before rendering every chunk, allowing developers to compute and upload their own sets of uniforms to their shaders, or otherwise update the render state before the draw call is issued.
Forge's
MultiLayerModelhas been updated to support user-registered static render types, and the public field listing the old supported render types has been deprecated and marked for removal.Lastly, a test mod is provided (mod id
layer_render_type_test) which adds a block with a custom render type. This render type's shader makes the block's geometry shake, and its color changes depending on the camera's orientation relative to the faces.Demo
Here is a short demo gif of the block in the test mod as the camera moves around:
Performance and memory implications
The overhead of this system is minimal, with a baseline cost of 3 foreach loops iterating over a pre-computed empty list.
The cost of additional render layers is down to each layer's specific settings. The main factors will be:
RenderType#bufferSize()for every chunk (negligible under vanilla standards of <= 1MB)In conclusion, the actual cost if unused is negligible, but as with everything, it must be used in moderation (at least until the aforementioned changes to baked models are discussed and implemented).
Maintainability
This PR moves the maintenance tasks related to
MultiLayerModel(keeping an updated list of vanilla-supported static render types) to theLevelRenderPhaseManagerclass.MultiLayerModelnow relies on this, and as such it does not need to be kept updated anymore.The mipmap fixes in
LevelRendererhave been moved toForgeRenderTypes.MipmapFixTextureShardand are now applied by replacing vanilla's default mipmap texture render state shard, which is a more idiomatic approach and ensures that the fix is applied to any render types using the mipmapped blocks texture.Additional maintenance tasks may involve adding/re-ordering hooks in
LevelRendererin the case that vanilla adds new static render types. These changes are unlikely and should not result in any changes to the core API.Additional notes
During the testing of this pull request, I found that in a Forge dev environment with test mods enabled, reloading resources triggers an invalid GL state which changes the colors of everything in the game and prevents text from rendering. This has been tested with and without this PR's changes, and the effect is the same, so the problem is expected to be in one of the other tests. This has not been tested in an environment without tests because if it works in a mod dev environment (which it does), it should work in an empty Forge dev environment.
If you want, I can open an issue about this.
Special thanks to @OrionDevelopment for all the help, feedback and code provided throughout the development of this PR.
They provided a significant amount of code, but the git authorship has been lost in refactoring and cleanup.