#Blaze3d Extensions (1.21.6+)
1 messages Β· Page 1 of 1 (latest)
Initial API prototyping is done in Cinnabar's b3dext API package, https://github.com/RogueLogix/Cinnabar/tree/master/mod/src/api/java/graphics/cinnabar/api/b3dext
Cinnabar also serves as a reference implementation for how the API is intended to work
I've currently prototyped a few different things:
- Adding
copyBufferToBufferto CommandEncoder for device-side buffer copies - A more complete version of the TextureView API currently part of B3D. GL 4.3 is required for full functionality.
- A more complete set of draw commands. GL 4.6, 4.3, 4.2, 4.0, or CPU side emulation needed for some features
- Allowing a pipeline to define multiple vertex input buffers, and per-instance input rate for a given buffer
When you say GL xyz is required, this would still work if the context is 3.2 with forward compatibility and you'd need to check for extensions, yes?
I am not even sure you need a brainstorming thread for these π
Yes, that's the version when it was made core. Draw parameters (draw id) in particular is usually available long before gl 4.6.
Some things can be faked though.
Ie: a multidraw indirect call can be faked on a Mac with gl 4.1, because it supports indirect. We may still be able to have the APIs added advertise a capability it doesn't fully have. gl_DrawID can also be faked with uniform calls when doing that. not fast, but will work if you want to rely on gl_DrawID.
well i dont mean fake in particular, I am just hung up on the terminology π
this can use an extension that is core in 4.3, I assume?
meaning: we don't have to increase the minimum context version
but what we do have to supply most likely is a feature check on the APIs that do require more than 3.2 core?
(Unless mojang bumps their requirement to 4.1 as well)
See ExtCapabilites object
Yeah I see, design-wise we have to see how that works out. I'd prefer those "supportsXYZ" checks next to the methods we provide but if a feature spans multiple methods that can get tedious
pointing to the method that has to be used to check support for a featur before using the method is fine too ig
With VK it's all available, GL would need to set based on what extensions it has.
Some things would need to throw errors if the capability isn't present.
yes that is fine if we provide proper check support for it, in my opinion
"proper". I mean easier to use than manually figuring out which GL caps are needed and checking LWJGL for it
That's basically the intention.
I think we have the same idea anyway, just slightly different locations where to put those methods, etc.
That is part of the brainstorming I want. I imagine API details are going to be the main thing discussed in this thread
to be specific on the requirements
ref: https://www.khronos.org/opengl/wiki/History_of_OpenGL
GL_ARB_texture_view was made part of GL 4.3. On lower versions without the extension we will need to put in place restrictions on how the API gets used, as it must be faked.
GL_ARB_draw_indirect was made part of 4.0. Same idea, version or extension needed for making a draw*indirect command.
GL_ARB_base_instance is part of 4.2, but can be faked.
4.3 added GL_ARB_mulit_draw_indirerct, can be faked with draw_indirect.
and 4.6 GL_ARB_shader_draw_parameters, though this can also be (somewhat) faked, its also available in most cases on earlier versions.
GL_ARB_instanced_arrays is also 3.3, not 3.2, and is required for per-instance input rates. In practice it is always available.
Compute shaders are a 4.2 thingy right?
No don't bother with that
Was just thinking
And they are like miles of for mac anyway
So forget that I asked
Can be like a second PR really
yup
technically this can be feature-by-feature with maybe some foundational stuff in the first one. or, I am not yet grasping the scope
Most of this is relatively small time stuff
Especially the stuff for which a 99% ARB exist
So it is basically put that all in
yeah i am not dead set on either
Oh, very much so
Then there are the features which require work arounds on older hardware / mac
just judge it based on line count
I would do it on a feature by feature basis
Those that are trivial can go in with the basic infra
And then you go on a feature by feature basis
i expect this thread to be long lived, and not targeting a single PR
initially, i want to get some of the more basic features (ie: buffer copy, draw commands) in, then later stuff like compute shaders can be looked at
@exotic pumice @trail hearth ima bring you two in here for input becaue Flywheel
I have not thought about how a port to 1.21.6 would look like besides leaving the GL calls the same. Only backends issue GL calls, so abstracting the draw calls themselves as well doesn't seem like it would have too much benefit for Flywheel. It's unclear whether existing backends will actually work with Vulkan even if they use abstracted graphics calls, and the plan for that was always a dedicated Vulkan backend, which would likely be able to achieve higher performance anyway.
i believe as it stands currently they wont, at a minimum because some free uniforms are used
that would be easy to change
I would make sure that each PR is digestible
Most importantly the PR description should make clear what the use case is, IMO. And it would be good to add some basic (!) tests in the process to help porting this stuff in the future, if possible (depending on the specific feature this may or may not be realistic)
do yall have client side rendering tests working in ci? 
spinning up another backend (or butchering instancing) to target blaze3d is not unrealistic so long as we can provide runtime-generated shader source
targeting B3D or "Neo3D" is probably the easier option for you to do, vs a jump straight to a VK backend
oh definitely
shader sources in B3D are provided through this
CompiledRenderPipeline precompilePipeline(RenderPipeline var1, @Nullable BiFunction<ResourceLocation, ShaderType, String> var2);
you can provide whatever BiFunction you want there for getting the source
and I have a feeling that a vk backend would need to target a specific vk mod (cough cinnabar) to truly be effective
not Fox3D?
only catch to be careful of is that it may not be called, the device is required to maintain an internal cache
Cinnabars short hand is far better IMO, C3D
B3D, C3D, ill see myself out now
but in actuality idk what would be a good way to differentiate between Vanilla B3D vs Neo B3D
and for that, i can provide an API that makes it not as much of a PITA for you, while still providing VK's benefits
just a βuse this source string pleaseβ would be amazing
ie: support for a pre-recorded commandencoder/renderpass, without having to deal with VK itself directly
ie: precompilePipeline(RenderPipeline pipeline, String vertexSource, String fragmentSource)?
but also, the way the cache(s) work,
sources is keyed off the resource location and type (in cinnabar), vanilla caches post-defines so the defines are used too
compiled is keyed off the pipeline by reference
so long as a given resource location and type always has the same source, the cache can be forgotten about
if not found, the bifunction is asked for the source
if no bifunction is provided, vanilla provides a default once resources get loaded
the cache is required because of startup
theoretically, it could be disabled for when a bifunction is povided
so, providing one is asking for compile, or recompile, and (re-)fetch this shader's source
https://github.com/RogueLogix/Cinnabar/blob/4fb984c01077cce05a9994fd1896802a72415f0b/mod/src/core/java/graphics/cinnabar/core/b3d/pipeline/CinnabarRenderPipeline.java#L82L83
https://github.com/RogueLogix/Cinnabar/blob/4fb984c01077cce05a9994fd1896802a72415f0b/mod/src/core/java/graphics/cinnabar/core/b3d/CinnabarDevice.java#L468L495
vanilla behaves basically identically
@fossil isle @late lotus @autumn zephyr
I have a question prompted by @fossil isle specifically mentioning my addition of R4G4_UNORM_PACK8 to TextureFormat given OpenGL can't support it at all (and also task/mesh shaders), should extensions to B3D in Neo be limited to what OpenGL can implement and/or what OpenGL's requirements are?
This can be broken down into a few different questions
q1: should things like R4G4_UNORM_PACK8 be included?
This specific instance doesn't really matter much (a shader can do the unpacking), but the idea of a definition for something OpenGL has no concept of. Obviously this means that Neo wouldn't support it, it would be expected that an alternate backend (ie: Cinnabar) could support it.
q2: should things that require not widely available extensions be defined?
Task/Mesh shaders are the best current example of this, where GL only has the GL_NV_mesh_shaders extension to support it. Similarly here, Neo would not need to support these, with the definition existing for alternate backends to implement.
q3: should the design of interfaces be restricted to what OpenGL requires?
The easiest example of this I have at the moment is glMemoryBarrier vs vkCmdMemoryBarrier. OpenGL only requires the second sync scope (see vk spec for what exactly that means) where VK requires both the first and second sync scope, as well as access. GL also only requires what is effectively VK's stage, while VK also requires the access be specified.
q4: should the design of interfaces allow/encourage usage that may have to or will always have to be decomposed for OpenGL?
glCopyBufferSubData vs vkCmdCopyBuffer2 comes to mind here. The former specifies a single range from a single source to a single destination. The latter allows for multiple ranges from multiple sources to multiple destinations. The latter can be faked by an OpenGL backend, but isn't necessarily the optimal API usage for GL.
Obviously, I am biased toward things that make it easier for VK, though I also realize its not reasonable to use a purely VK based design for a game written for GL
My opinions are
q1: Only if trivial. ie: TextureFormat enum entry sure, async operations no.
q2: Only if trivial. Shader type definition sure, pre-recorded commands (GL_NV_command_list) no.
q3: It depends on the different options, and would need to be considered on a case by case basis.
q4: Generally yes. In some cases doing so may help OpenGL drivers as well, though that isn't a guarantee.
wrt q1, can the value be gracefully downgraded to a known value if the backend doesn't support the value?
if it could, that would fall under Q2 or Q4, so, no.
using R4G4_UNORM_PACK8 specifically as an example, the only way to emulate this on OpenGL is using R8_UINT and then unpacking in the shader with uvec2((packed >> 4) & 0xF, packed & 0xF). Theoretically a backend could do this, but doing the proper replacement in arbitrary shader code is not a reasonable thing to do.
it being so easy to suggest an alternative for that is why the specific instance is meh either way. But if something did exist that cant be so easily (by the mod author) 1:1 replaced, it would matter more.
yeah, my gut feeling says something that doesn't exist in opengl doesn't belong in the default impl
but rather than emulating with a R8
my consideration would be more, R8G8_UNORM or whatever opengl happens to support
something that is not as ideal but still works
not the default impl, API definition only.
Uploading this would require the buffer/texture to be rerwrritten on the CPU (or using a custom upload compute shader on the GPU). Theoretically possible but a complex step as well.
oh this is pixel format? nevermind then
my brain was picturing vertex attributes / shader parameters
to be extra clear on this
Q1/Q2 effect API definition. impl at most needs to report unsupported and throw if attempted
Q3/Q4 effect the impl as well.
I think the impl definitely needs to throw a clear exception for Q1/Q2 if the answer is yes, so that it's easy to pinpoint usages of unsupported options
Yes, though that would depend on where. GlConst.toGl*(VertexFormat) probably shouldn't throw , but GlDevice.createTexture should throw.
same idea for not having GlConst.toGl(ShaderType) throw.
doing that is also necessary regardless, as some things aren't always supported, but absolutely should be included (compute shaders, stb, ssbo, indirect), or could be included and are supported directly (texture formats S8, R5G6B5, R5G5B5A1, R4G4B4A4)
also, for visibility here, current PR is https://github.com/neoforged/NeoForge/pull/2359
What I would like to see (and it would help answer these questions I think) is why some features/extensions are useful
Blaze3D doesn't have some stuff that wouldn't be useful in the context of vanilla. So the question is why is it useful in the context of mods
We can easily make incremental improvements to the API as more use cases are discovered, but I am quite hesitant with a blanket "add as much stuff as I could think about" PR
Ah you did reply with a justification on the PR, which I haven't read yet
Might read it tomorrow
TLDR of that: some are targeted at specific things (SSBO), and the less targeted ones are just adding constants that get passed through to keep people from trying to go around.
I'll have a deeper look tomorrow. Just adding the other constants from the opengl spec is fine. Some things I'll have to investigate a bit more deeply
TextureFormat: This is primarily targeted at data texture/UTB uses. Quartz has used textures of a particular format to store lighting data. Vanilla does to store cloud data. Flywheel does to store instance data. Each has their own specific needs and with that their own best texture format(s).
LogicOp/SourceFactor/DestFactor/PolygonMode/DepthTestFunction/AddressMode, these are all adding GL constants.
Storage Images (usage bit on textures) can be written to
for the GpuBuffer bits
Transform feedback: used by Quartz to support Iris shaders. Can also be utilized to fake compute on otherwise unsupported devices (Quartz also did this).
STBs can also be written to
SSBOs can be written to and also size limit of UBOs removed (and can be arrayed with undefined size). Cinnabar uses these behind the scenes for world rendering.
Indirect, shouldn't need much explanation. Sodium, Cinnabar, Quartz, Flywheel, and probably more all use this.
The last two bits are for performance. Cinnabar at least will use them, and its extremely useful to have "Vanilla" use them too.
FRAME_TRANSIENT is targeted at things like per-frame uniform buffers. Think projection matrices, or other per-frame state. Anything that gets updated every frame. For Cinnabar, this allows me to automatically use multiple buffers to back it, that are also extremely quickly allocated.
ONE_TIME_USE is the above, but for things that get used exactly once. This is targeted at things like rendertype immediate draw buffers. For Cinnabar, this allows me not copy to GPU local memory (having it read directly from the CPU memory instead).
OpenGL can also utilize particularly FRAME_TRANSIENT to similar benefit
The last one is ShaderType. What I said on github is basically it, but to be more specific.
Compute I want to add support for to Neo, multiple mods use this currently (again going to point at Flywheel and Quartz).
Task/Mesh shaders I plan to support in Cinnabar, but because OpenGL has them too putting them here also leaves open the option of some other GL based backend (including, extension/mixins) adding support via GL_NV_mesh_shader.
Tess and geometry are unlikely to get used, though theoretically very cool, and could probably be removed. They were added mostly for completeness.
incremental improvements
notable thing to consider, any additions to the API definition that aren't themselves (default) implemented within the API are a breaking change. while adding every feature isn't a good idea, parts of a feature should probably not be left out simply because there isn't a specific articulable today use for that subset of it.
ie: if a draw command is added, such as an instanced "drawArrays" command, i would argue that all reasonable draw command variants should be defined, even if not implemented immediately. If applicable, an implementation could be added to Neo later without being a breaking change.
OK, got started on the PR at least
DepthTestFunction, DestFactor, LogicOp, PolygonMode, SourceFactor, AddressMode extensions are certainly fine (especially for destfactor and sourcefactor I wonder why mojang didn't just go for all of them)
didn't have time to look at the rest yet - tomorrow? π
what is the minimum GL version that MC supports? 3.3?
3.2
PolygonMode seems odd to me as wireframe is already a rare thing to use (it's basically just used in debugging IIRC) but a single value doesn't hurt so whatever. For the rest I agree, though for LogicOp I'm curious what one would use that for in general.
What I'm concerned about is TextureFormat. Adding all those variants feels like insane bloat to me and unnecessarily confusing for less rendering-savy people.
I would agree for TextureFormat, but haven't had the chance to look at the details yet
in general I am also very wary of adding anything that requires checking for extensions
anything going beyond GL 3.2 should probably come with solid justification
Why are some vanilla enum fields reordered?
I suppose that rogue just copied all GL fields from the spec, and forgot to reorder some of them?
technically copied from VK spec, but GL has the same ordering iirc
LogicOp
text highlight is what vanilla uses it for
What I'm concerned about is TextureFormat. Adding all those variants feels like insane bloat to me and unnecessarily confusing for less rendering-savy people.
What ones would you keep then? The vanilla set alone is not enough to cover modded uses (I was running into exactly that problem).
The largest block of them (33 additions, 3 vanilla already had) is the combination of 8, 16, or 32 bits per element, 1, 2, or 4 elements, and then UNORM, SNORM, UINT, SINT, and SFLOAT data type
The packed ones could be done in shader with R16_UINT or R32_UINT, except 999e5, but are nice to have. They are reasonable to remove if a smaller set is desired.
Ones I would definitely remove are the compressed ones, ones GL does not support (this whole thing should still be GL-first since that's what Minecraft is natively built on), stencil-only ones and the packed ones. A smaller set is IMO definitely preferable to retain people's sanity.
I use LogicOp
I use it to draw an XOR inverted overlay over some UI
non-GL and stencil-only fair enough.
packed i would prefer to keep, as they can be a sanity saver for those using them. particularly for passing into an existing shader expecting a multi-channel texture.
compressed are a fairly use-case specific thing, its large textures where they are the most beneficial. I'd like to see them supported and used, but can also understand not wanting them there for a smaller format set instead.
with packed/compressed removed as well, there would be 41 total formats.
8bit R, RG, RGBA : UNORM, SNORM, UINT, SINT
16bit R, RG, RGBA : UNORM, SNORM, UINT, SINT, SFLOAT
32bit R, RG, RGBA : UINT, SINT, SFLOAT
D16_UNORM
D24_UNORM
D32_SFLOAT
D24_UNORM_S8_UINT
D32_SFLOAT_S8_UINT
@fossil isle what would you want the use comments to be exactly?
with USAGE_TRANSFORM_FEEDBACK as an example. That's saying "this buffer can be used as the capture destination for transform feedback operations".
To me, something explaining it to that level seems redundant, but explaining further such that its not redundant also seems out of scope of a comment on a usage bit.
the others would also be very similar.
even the VK spec basically just repeats what the enum name is, but gives some/all calls where it can be used
VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT specifies that the buffer is suitable for using for binding as a transform feedback buffer with vkCmdBindTransformFeedbackBuffersEXT.
The way the VK spec describes it seems decent to me. You need to keep in mind that a lot of people who are not nearly as well versed in all this as you are will be looking at this.
To be perfectly honest, I would prefer to thin it out even further. But I'm gonna wait for other people's opinions on this
I get that, but would it not be more helpful to instead (or additionally) link to something like this to actually explain what the feature is properly?
https://www.khronos.org/opengl/wiki/Transform_Feedback
Definitely
instead or additional?
Additionally
aight
following what VK does for wording, i think it would be good to list where it gets used, but in all cases where they get used is yet to be implemented, would it be fine to leave a {NOT_YET_IMPLEMENTED} placeholder?
ie:
USAGE_TRANSFORM_FEEDBACK specifies that the buffer is suitable for using for binding as a transform feedback buffer with {NOT_YET_IMPLEMENTED}.
See OpenGL wiki for details on what this feature is: https://www.khronos.org/opengl/wiki/Transform_Feedback
that would later get replaced with a link to something like RenderPass.bindTransforrmFeedbackBuffers, though that doesnt exist (yet)
Sure, that sounds reasonable to me
Should the reduced precision depth formats be removed? that would be D16, X8D24, and D24S8
the client config's reasoning for D24S8 being an option "slightly reduce vram", for which the compressed textures would make a far larger difference.
that would reduce it to
color variants (36 total), D32F, D32F_S8, for 38 options
i don't think the color variants should be reduced further.
If nobody else can come up with a good reason to keep them, then I would be in favor of this
as-is, color is already reduced to one option per bit width, component count, and component data type.
the only three that i find even remotely reasonable to remove are the 16_SFLOAT ones, as java does not have native support for fp16 conversion, though it can be done (or they can be used directly on GPU)
these three, specifically
R16_SFLOAT
R16G16_SFLOAT
R16G16B16A16_SFLOAT
additionally removing those would bring it down to 35 total formats
Hmm, 1.21.7-rc1 deprecates RenderPipeline.Builder#withColorLogic() and replaces the only use of LogicOp - color inversion for text selections - with a dedicated BlendFunction
shartte had a use case for logic op
But which specific one? If he just uses the existing LogicOp.OR_REVERSE, then he could almost certainly just switch the same way
XOR_INVERTED
isn't that just XNOR?
XOR_INVERTED actually isn't a thing, nor is XNOR, @autumn zephyr what logic op did you use exactly?
XOR IIRC
if they do blend function for inversion i can likely use that
I use
GL20.glLogicOp(GL20.GL_XOR);
GL20.glEnable(GL20.GL_COLOR_LOGIC_OP);
notable thing, this isn't a perfect 1:1 replacement, the OR_INVERSE did behave differently
the logic op definition is a bitwise s β¨ Β¬ d, or in java bitwise ops s | ~d
the new op is (1.0 - s) + (1.0 - d), and because unorm format 1.0 - x is equivalent to ~x, so they have effectively ~s + ~d (note: thats a normal add, w/ carry), may give a similar looking result but its not the same thing
actually, wait, im slightly wrong
they are using s * (1.0 - d) + d * (1.0 - s)
still a different operation, that gives a good enough result, but not the same result
oh, fun, so, potentially why they dropped support for it is because Metal (and by extension MVK) doesn't have LogicOp support
Apple uses undocumented metal features to implement OpenGL, so obviously the hardware can do it somehow
but, if its deprecated then intent does seem to be to remove the feature, and ^ that is probably why
rip
wait does that mean they are planning to make the backend Vulkan? 
or just expecting opengl to become crippled in the near future?
Gegy mentioned this is actually because of Qualcomm's OpenGL driver
Apparently it throws a fit if you try and use logic op via OpenGL (it's fine via VK)
I mean, they did have one, a prototype at least, so keeping that door as open as possible makes sense to me. But in reality it's because Qualcomm...
what the fuck
like. what
behold more oppengl cursed driver crap
No idea, ask gegy
i assume its windows? again?
I'll just accept it
Windows aarch64
becuase, i again, dont see mesa having this kind of shit
Yeah the new type of Windows ARM laptops, most likely
Well. "new"
You know what I mean
its always windows, unless its mac
I'll have you know there is a desktop one
I got so burned by broken AMD OpenGL drivers a decade ago I'll never forget it π
Assuming you ordered it at the right time, and didn't get your order canned before it shipped.
mine shipped first
I think I saw an ARM based Windows MiniPC, but could be hallucinating
The devkit is the only one I'm aware of
And afaik, it's the only shipped example of the X1E-00-1DE variant
well to be fair
most rendering bugs afaik have been macs lately
this is a rarer occurrence of a windows issue
becuase mac gl is mega fucking cursed
Ah yes, might have imagined that, tbh. The only news I can find is also the qualcomm devkit being scrapped
I am kinda a fan of Windows supporting new architectures, tbh
but, weak memory ordering
logicop being broken on a supported platform (Qualcomm) though, maybe it would be better to actively discourage its use, or note a warning about that and add a capability check to the GpuDevice for it.
but it's fucked via opengl on the same chip?
yup

for all intents and purpose, D3D12 and VK are the same thing
for a common feature (like logic op), id expect any bug to be present with both
and half the D3D12 driver is actually implemented by MS, not Qualcomm, so, almost certainly this is in the GL -> D3D12 layer
their buffer upload was also working incorrectly, so theres that too
hmm
that driver just has issues
so we have two choices, either include extended logic ops or not
say I have a qualcomm chip... is there any workaround?
w/ OpenGL and logic ops, afaik, no
ok then my verdict is no logic op for now
IMO, it should be either one way or the other entirely
option 1) feature is there, and we add the extensions for the entire feature
option 2) we dont add it, and if its left in by mojang make it very clear that it shouldn't be used (and why)
it's still there in 1.21.7-rc1
ermm yes
I think we can add a comment saying that it shouldn't be used
re DestFactor I would avoid adding anything that is not in GL 3.1
(that applies to the whole PR π )
I'm gonna leave some comments on the PR so we don't forget
thats an extremely limiting thing to do, and will be a problem for mods.
not just with this PR specifically (SSBOs), MDI is one of the major other things that requires a newer version.
also, 3.2*
ermm yes 3.2
I would rather not make it easy for mods to accidentally use features that are not supported on all platforms
because validation will be required regardless, we could validate use against 3.2 available things unless a specific i know what im doing, and that i need to check shit option is enabled. in IDE.
well I assume that the people who want to go beyond 3.2 features also want to do it in prod, not just in their IDE
right, in prod we'd default to hardware available or
that i know what im doing could actually be done on a per-feature basis (including at runtime), where the mod has to ask for something to be turned on/available, and if it can't be will get an error thrown at it by B3D.
VK does a similar thing when creating a device. you first must ask for its capabilities, then when creating it must tell it any optional features to be enabled.
and if you ask for something it doesnt have, it will fail to create the device
this couldn't be filtered on a per-mod basis, but would make sure that if a feature gets used in dev, they did ask for it, and therefore have somewhere specific that the error would get thrown if its not available (in prod)
okay that seems reasonable to me; let's ask @autumn zephyr and @fossil isle for their opinion as well.
TLDR: the discussion is about optional GL features. Rogue proposes that any feature beyond GL 3.2 require explicit enabling by mods, and if the GPU does not support it they will get a crash during initialization
which means that you can't accidentally use optional features, and that if you try to use an optional feature on hardware that doesn't support it you will get a predictable runtime crash during game loading
we could also do this for known problem features, like logic-op
and dev should default to only 3.2 available, even if your hardware has more, so you even then must explicitly enable a i know what im doing mode before it will even report that it has more than the bare minimum of features
I would probably try to make dev and prod behave similarly if possible
the dev mode thing is behave as if you have a 3.2-only card
I don't see a need for a difference - we could simply make both dev and prod require the feature to be turned on?
ehh, yea, i guess its probably fine to do that
i was thinking making the reporting side behave as if its 3.2 only by default in dev, even if you have a 4.6 card.
related thing that could be done is programmable simulated modes for specific for GL versions and/or extensions. so you could reasonably test what happens with the GL version/extensions that mac has, for example, without actually being on a mac.
yeah that would be quite nice I suppose
so that you can try many combinations without having to manually enable/disable stuff on your end
and i have most hardware, so i could actually build real feature set lists for most hardware. it wont be a perfect 1:1, but it would allow devs to catch most errors
glares angerly at shaders
I am of the opinion this is better done at a lower level
because what you're describing is only enforceable if (a) everyone uses B3D and as you mentioned (b) doesn't use extra features in shaders
Requiring explicit opt-in for features from newer GL versions sounds fine to me. We could potentially even re-use the main render target setup event for this.
As for dev, I wouldn't force the game into effectively 3.2 mode but having ways to simulate limited environments sounds like a good idea to me.
Ideally something like Mesa itself would provide the option but that will probably never happen
IMO, if a mod is going around b3d, that's its own problem, and is entirely into you know what you are doing territory, as it is today.
for shaders, we could look at the #version declaration and search for extensions too, its not impossible, and that would probably cover most things for usages
its driver bugs that you cant replicated
for example
HD5000 (TS2) has issues with image store working properly, on linux
HD4000 (TS1) is liable to just crash with some shaders, but theres no way to know why exactly
or mac not implementing separate shader objects properly
theres no good way to check for those, and thats true today as well
any addition of validation is strictly better than what is available today, where you just hope for the best and that you didnt fuck anything up
we can add anything as long as we support graceful fallback or feature testing
well yes, although what does enabling by mods mean
the device would throw regardless of feature-test?
something analogous to what VK does for device creation, not perfectly identical, but the general idea
i have no idea what VK does ofr device creation π
ill explain, gib sec
I am not sure where Tech is going entirely, but if he's trying to avoid Mods reaching for some GL constants that aren't in the base set, there are a couple options, yeah
one is the mod having to "unlock" the extension via a static method call in their mod constructor, otherwise the device throws if you try to use it
in a modpack you'd end up with cross-contamination, but in your own dev-env likely not
thats basically my idea with it
with VK, to use a feature, it must be explicitly asked for during device creation,
this is done via VkPhysicalDeviceFeatures* structs, where you set bunch of bools.
if a feature isn't enabled, it doesn't need to work, and validation errors with be thrown (if a validation layer is enabled).
you can (should) also ask the device for what it support beforehand, though technically you don't have to.
That reminds me of a fun bug with LWJGL where trying to create a Vulkan context on recent graphic cards would explode due to not enough stack space
as a very real example of this, logicOp is part of the base VK 1.0 VkPhysicalDeviceFeatures struct
a device may report false, in which case you must not enable it
if you ask for it, the device will throw an error
if you dont enable it, and try and use it anyway, a validation layer will catch that.
Thankfully that is solved nowadays
yea, i fix that by setting stack size to bigger before LWJGL inits the MemoryStack
VK purposely doesn't have validation done during normal runtime, because performance. this process where you ask for what it can do, ask it to enable things (it may throw), and then later if we want (yes [default] in dev, maybe? in prod) can validate that things are actually available when mods go to use them would probably be a good idea overall
re-use the main render target setup event
would it be possible to do it before GpuDevice/RenderSystem init?
very minor point but technically it would be better if it could be done at that stage.
i think thats before mod constructors fire though, so, idk
this also means all the "requires GL 4.X" comments could instead be targeted at the reported device capabilities, and then those can link to the GL/extension/VK/whatever requirements, and note issues with features, all in one central place
keeping that centralized should help with keeping it up to date as new driver issues are found too, because those will exist
Now we can
Since we moved the constructor calls for client mods to before Minecraft is initialized (Unless I am just hallucinating us doing that)
It might actually be easier to just expose static methods for this, but we're free to also add an event
cool, the minor point that lets me do is leave them disabled in Vulkan if not asked for
or many events, whatever suits us
well kinda, how does that interact with earlydisplay?
early display has its own VK instance and device
for GL the device will already be enabled, theoretically you might have to just do a validation-layer style thing in our side of the device
for GL, doesnt matter
in reality, id be implementing a "validation layer" device in Neo anyway, to support doing this
doing it in patches would suck, and that also will catch people trying to reach around and cast it to GlDevice or whatever
I'd like an event rather than a static method call
they'd be forced to use reflection (or turn validation off), which ofc is possible, but if they are going that far im not going to try and stop them
yeah event is fine, now that we can do it
we can technically track which mods request which features that way
the power of cleanup!
there can be a perf cost for enabled features that dont get used
then we could throw a mod-loading exception for all mods that request unavailable features too
so, you get an error of mod xyz requested graphics feature w that is not available
for all mods that did, and all the features they did too
do we know when the event is emitted which features are available? π€
well, technically you can emit it during the device creation I guess
yea, during device init is probably when this should get fired
but i can also poll VK before that ezpz
and GL, context exists the whole time, so, also just a matter of polling it whenever
That would actually give mods a chance to also efficiently feature test in the event and configure themselves accordingly
other fun thing this allows, almost any API addition could be done without it being an IMPL breaking change now.
to enable something, the impl must report it as available, and if you use it anyway that's bad on the using mod.
that makes a default to throw InvalidUse or similar actually correct, because that backend doesnt support the feature
new enum values or bitfields are also similarly a non-issue, because its invalid on the using mod side to enable/use them if the backend doesn't report support
well careful if you add anything to enums
you can run into switch expression issues
JVM will throw an error on that
no, a mod could just as well do switch on the same enum type
nothing forbids that
case for all existing issues, and receive a new enum value from another mod
you add that -> boom
adding enum values is likely still a break from our end
needs more discussion I suppose
since technically having runtime extensible enums lead to that problem too
add SSBO: older backend doesnt report support for SSBO (because it didnt exist), you cant enable SSBO, passing SSBO should explode regardless
you're thinking about this wrong
oh, does the JVM throw on classload?
i am talking about a mod doing:
switch (someOtherModsTexture.format) {
... list all cases of vanilla ...
}
if we add to textureformat enum, the mode consuming here did nothing wrong
but will still throw if another mod also didn't do anything wrong (by requesting use of the new textureformat enum value and using it)
oic, yea, yea that could be an issue
but, as I said, we kinda already declare enums as extensible
it still reduces what is a breaking change at least
which leads to this problem randomly at runtime by other mods adding enum values
it needs some discussion
since we likely dont want texture formats to be runtime extensible
can add indirect commands, default throw them, and then backends can implement when they feel like it
texture formats should probably be an "always enable" thing
but we might make it an explicit properties of those rendersystem enums that they're intended to be extensible going forward
but... no idea how to formalize that I think we have not had that case yet
but its stuff like the dual source blending where its behind GL 3.3
or compute shaders behind GL 4.3
switches in other mods exploding is the main issue, the backend exploding isn't an issue anymore though
and stuff like this is a non-issue to add whenever
if you tell mods that they have to expect values to be added to these enums (in whatever way we chose) also applies to backends, so they'd also be "safe" against added stuff
and yeah if everything added (that we know is not OGL 3.2 for example) is feature tested, that makes it relatively safe to add new stuff i think
*add new stuff later as a non breaking change
GL 3.2 available stuff could be added as non-breaking with it behind a feature test until the next BC window where its then unlocked always
so, adding new stuff could be made "always safe" with that
PR opened for device configuration, https://github.com/neoforged/NeoForge/pull/2399
with that, most, but not all, additions could be done as non-breaking. Some very heavy validation could also be done.
main open question i have is how to communicate to mods that the enums in B3D may get extended without being considered a breaking change, and that they need to accoutn for that
i have no qualms with that, but is it obvious enough?
I can't think of a way to communicate that, besides switching the enums to interfaces with opaque impls, but that destroys switches
This looks very nice
Yeah other then interface with static values
I see no other option
that destroys switches
that is kinda the point
Switch will always barf on runtime extensions
but, also, they are enums from vanilla, so, kinda a problem to change them from that
Unless you have a default, or you aren't a expression switch
"runtime"
the extensions would be done at compile time, but potentially after the mod is compiled
Just make them as IExtendableEnum or w/e
There's nothing more we can realistically do without destroying enum switch
For me that is basically runtime enum extension
Cause it has the exact same effect from a modders perspective
Yea, it is the same effect
Marking them as "runtime extendable" the same with others are is probably the right option then
I mean, it's not really invalid for a backend to extend them at runtime
It's invalid for most mods to do so
It does not fit the semantics of a runtime extensible enum
It's a bit of a shame that Java doesn't have a way to mark enums as non-exhaustive
glares at c++ where it's just an int, and you can cast it
time to rewrite MC in Rust?
Days since the last release of a Minecraft server software written in Rust.
I do like myself some unsafe behavior through, and rust doesn't like that
Yeah yeah mr Bjarne, it's the programmer's fault not the language's
The borrow checker would hate a lot of my concurrency code. It's heavily reliant on some pretty specific visibility ordering behavior to work right. bunch of manual memory/compiler barriers to get the compiler to do the right thing
unsafe {} is fine to use in targeted places
Just document it then, nothing else we can do
I think an annotation or interface is the right thing to do. Probably annotation
To make sure that even people who don't have sources attached see it clearly
Sure, INonExhaustiveEnum or something
where should that interface go?
IExtensibleEnum is in FML, but that doesn't seem like the right spot for this
I think an annotation makes more sense as it's metadata
same question on where it goes, and thats just a difference of @interface vs interface
and somewhere in an internal neo package
@NonExhaustiveEnum added, and validation for the currently known enum values
also added validation for known usage bits
LogicOp was specifically not marked as NonExhaustive due to its issues
@late lotus @autumn zephyr @fossil isle 2399 should be in a good enough state for review now, if you want to actually look it over
I want to have a look tonight
ok I had a look and would recommend to focus on solving one problem at the time
This is the "don't use features that aren't supported" and the extremely related "what are the usage limit" problems, almost entirely.
I could split it a bit, but a good chunk has to be done at once
I don't see why it would need to be done at the same time
checking if the LogicOp is supported seems completely independent from querying the max texture size
Strictly speaking, it doesn't have to be. If you'd like the PRs to be as granular as i can make them, I can do that. However, both of those things are also reliant on the boilerplate for the event. The validation layer is then also reliant on both of those, and would have to be created (or at minimum updated) after all three+ (base event, logicop enforcement, texture size query, etc) previous PRs. I can split it into many PRs like that if you would prefer.
@late lotus for the features interface, like this better?
public interface GpuDeviceFeatures {
/**
* LogicOp is problematic on Qualcomm GPUs via OpenGL
* LogicOp is unavailable on MacOS via Vulkan
*/
boolean logicOp();
void enableLogicOp();
/**
* Example for something that doesnt exist yet
*/
default boolean someNewFeatureThatsUnsupportedCurrently() {
return false;
}
default void enabledSomeNewFeatureThatsUnsupportedCurrently() {
throw new UnsupportedOperationException("This really cool feature is unavailable");
}
}
and an implementation of that interface
public class NeoGlDeviceFeatures implements GpuDeviceFeatures {
private final NeoGlDeviceFeatures availableFeatures;
private boolean locked = false;
private boolean logicOp = false;
public NeoGlDeviceFeatures(@Nullable NeoGlDeviceFeatures availableFeatures) {
if (availableFeatures == null) {
querySupport();
this.availableFeatures = this;
} else {
this.availableFeatures = availableFeatures;
}
}
private void querySupport() {
lock();
logicOp = !(Util.getPlatform() == Util.OS.WINDOWS && Util.isAarch64());
}
@ApiStatus.Internal
public void lock() {
locked = true;
}
void checkLocked() {
if (locked) {
throw new IllegalStateException("Cannot set features on locked feature set");
}
}
@Override
public boolean logicOp() {
return logicOp;
}
@Override
public void enableLogicOp() {
checkLocked();
if (!availableFeatures.logicOp()) {
throw new UnsupportedOperationException("LogicOp is unavailable");
}
logicOp = true;
}
}
what the event would look like
public class ConfigureGpuDeviceEvent extends Event implements IModBusEvent {
private final GpuDeviceProperties deviceProperties;
private final GpuDeviceFeatures availableFeatures;
private final GpuDeviceFeatures enabledFeatures;
public ConfigureGpuDeviceEvent(GpuDeviceProperties deviceProperties, GpuDeviceFeatures availableFeatures, GpuDeviceFeatures enabledFeatures) {
this.deviceProperties = deviceProperties;
this.availableFeatures = availableFeatures;
this.enabledFeatures = enabledFeatures;
}
public GpuDeviceProperties getDeviceProperties() {
return deviceProperties;
}
public GpuDeviceFeatures getAvailableFeatures() {
return availableFeatures;
}
public GpuDeviceFeatures getEnabledFeatures() {
return enabledFeatures;
}
}
firing the event
deviceProperties = GpuDeviceProperties.create(GpuDeviceProperties.class, propertiesMap);
final var availableFeatures = new NeoGlDeviceFeatures(null);
enabledFeatures = new NeoGlDeviceFeatures(availableFeatures); // memeber that can later be fetched from the GpuDevice
ModLoader.postEvent(new ConfigureGpuDeviceEvent(properties(), availableFeatures, enabledFeatures));
enabledFeatures.lock();
and then using it would be basically what you gave as an example for
if (avaialbleFeatures.logicOp()) {
enabledFeatures.enableLogicOp();
}
a base class for backends to extend could be made, but likely wouldn't be verry helpful because in my case id probably be directly getting/setting these from/in the VkPhysicalDeviceProperties and friends structs, so it wouldn't be in java bools in the same way as GL would have
I barely had a chance to look at it yet but that class definitively needs a class-comment
pointing out what it's for, and potentially specifically call out the procedure for backend implementors
oh and a secondary note which is less concrete: Why did you call it neo3d?
As an extension of blaze3d, I'd just have put it in a package named as such
separation between vanilla B3D and Neo B3D, though it could be renamed to whatever
you are already in a neoforge package...
Wildfire3d, after the blaze miniboss that lost a mobvote then was added to dungeons
i think any neo extensions to a vanilla system should be relatable to that vanilla system (so just name the subpackage in the neoforge structure blaze3d)
Yes, fair, just in my head it's "ahh, this is neo3d", so that's what the package got called
I'll probably leave renaming that for after other issues get solved though, because that's just a file move
that's fine
Want to nail down the what I'd be documenting first. So, in general expect that to similarly be a later addition.
yes but without those docs, reviewing is also harder
because I have to actually reverse engineer what you wanna do first
Fair
maybe one interface for reading and another interface extending it for selecting the versions, idk; we could just have the setters in the event
the reflection stuff with the propertiesMap should definitely go as well
How is this mechanically going to work for a consumer that is aware of an implementations extension?
what do you mean?
Yes please, no reflection magic
Well the class I saw seemed intended to be extended to sth like VkGpuProperties
With extra stuff on it
If that was not the intent I understand the design even less π
maybe we should do the properties in a separate PR, and just focus on doing something reasonable with the features
Hmhmhmhm, there are actually a lot of designs possible I think that we've not yet explored for this, this is not particularly performance sensitive code, I think, so one object per property would also work (which could encapsulate the metadata you wanted to encode such as its default, and bounds)
But for that I think I'd need to know more about what the design drive behind the properties is π
It's based on VkPhysicalDeviceProperties2 and friends
Some of those values need to be known by API users, and there's usually a glGetInteger equivalent.
It's also very needed for the validation layer to make sure people aren't doing invalid things
Ie: ubo size limit
Or alignment (Mojang screwed that up in a snapshot)
very much so yes, both GL and VK alternate backends could want to expose things that n3d doesn't
Out of curiosity, is there space in this PR/set of PRs for things that would be no-ops on OpenGL, but in other platforms like Vulkan would be super handy? The main thing I'd be interested in are things like image layouts: https://registry.khronos.org/vulkan/specs/latest/man/html/VkImageLayout.html
Why do you need that, considering
https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_unified_image_layouts.html
Huh. Interesting, I didn't know that existed
Looking at it though, that seems to be a fairly recent extension, and we can't assume that devices will actually support it for... well, yes amount of time
It's mostly just an informational extension, it's perf characteristics will predate it
sure, that can be implemented with a fetch-only interface instead.
what would the benefit of splitting the interface be?
the "backendName" property is largely for this. something aware of Cinnabar could look for that being Cinnabar before casting classes around and maybe hitting a CNF
To not expose the mutability in read-only contexts
ah @vale otter you didnt really make the PR to add render abstractions for earlydisplay yet, yeah?
we can do that later in a non-breaking way imho
as i said on github, dont have a super strong opinion, but i didnt immediately do that because its a downcast away from the mutable version (assuming its always a single impl)
not yet, no.
but, adding that shouldn't be breaking unless IWP is removed, which it doesn't need to be, so no need to block stable on that
downcasting is not a concern
the way xfact suggested on github however, is multi-impl
The mutable version can still be protected by a lock flag if you're worried
if its one impl, id want the mutable to be lockable
but 3 impl (two being API agnostic) is also an option, and honestly probably not that bad of one, very similar to what xfact suggested on github
mutable (can be its own class, no interface needed for the setters)
api-available (polls the underlying API for whats actually possible)
immutable (could be a record, created from either of the other two)
the API specific one wouldn't even need to have the fields itself
note that the immutable one being a record here works because it constructs itself from another interface instance, which i didn't think about doing previously
i imagine that works for the both of you?
properties could also be done in a similar way, where its a record constructed from an interface of basically itself
What do you mean by "record constructed from an interface of basically itself"?
interface GpuFeatures {
boolean logicOp();
}
record EnabledGpuFeatures(boolean logicOp) implements GpuFeatures {
EnabledGpuFeatures(GpuFeatures features) {
this(features.logicOp());
}
}
then add a mutable interface impl (doesn't need to be lockable), and api specific polling impl like your github comment has
Ah, ok, sounds reasonable then
updated PR with basically that, for both features and properties
@keen pine did you have an old revision pulled up in github? i pushed this last night (my time), which swapped it to R/O interface
https://github.com/neoforged/NeoForge/pull/2399/commits/220a0b96a7c0b3fd03a114f26c2173856639ef75
I'm honestly still not a fan of making GlDevice abstract and extending it instead of either patching the custom stuff in or putting it in an extension interface that's interface-injected onto GlDevice. The specific provided example with glDrawElementsInstancedBaseVertexBaseInstanceARB() doesn't convince me either as I don't think the difference between ARBBaseInstance.glDrawElementsInstancedBaseVertexBaseInstanceARB() and org.lwjgl.opengl.ARBBaseInstance.glDrawElementsInstancedBaseVertexBaseInstanceARB() is relevant (I hate static imports with a passion, they just make it unnecessarily hard to find where a method or field came from [queue rant about C/C++ headers doing the same thing], which is why I won't even consider the option of omitting the class name)
i have the exact opposite opinion, having the class name (and especially fully qualified) there adds unnecessary clutter that makes it harder to read, particularly when basically all documentation assumes a single namespace (because C)
if i need to find exactly where its from, my IDE can do that just fine
The single namespace thing is one of the details I despise about C++ in particular. The same namespace (or even none at all) is used across a stupid amount of headers and tells me exactly nothing about were a function comes from (granted, in C++ looking at the impl is often futile anyway, especially when it's about something in the stdlib).
The "my IDE can find it just fine" justification falls apart for the same reason why we forbid wildcard imports: I should be able to read and understand the code and know where shit comes from even when I'm reading it on GitHub.
using only GL32C or GL46C would fall into the same issue IMO.
looking at the existing GlConst patch, the prepend of org.lwjgl.opengl.GL32 adds nothing to what it is that the GL_ or gl prefix (and if applicable ARB, EXT, or KHR suffix) doesn't.
also, small note, do make sure to use the C variant, GL32 is compat profile, GL32C is core profile
with GL calls specifically, doing ^ helps nothing and only makes reading it worse.
this is marginally helpful, but even still only marginally, because it actually tells you what version of GL its from
case DEPTH32 -> org.lwjgl.opengl.GL11C.GL_FLOAT;
case D32_SFLOAT_S8_UINT -> org.lwjgl.opengl.GL30C.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
Another benefit of patching in the validation directly in the source code is that it can be done later in a non-breaking way. And potentially less invasive way if we only perform the few checks that actually matter in practice
adding the validation is non-breaking regardless
its just an interface layer thats disabled in prod anyway
this also isn't about the validation, but rather NeoGlDevice's existence
I'm not talking about the validation, I'd actually be in favor of keeping the validation layer separate. What I'm talking about are the custom extensions and whether they should be patched directly into the vanilla GlDevice or kept in a custom class extending the vanilla GlDevice, the latter of which requires making the vanilla class abstract (with too little justification in my opinion)
personally, id be more in favor of a NeoGpuDevice interface for any extension, and then NeoGlDevice implements that (and extends GlDevice) leaving the base b3d interfaces/objects almost or entirely untouched
which is taking what i did a step further, rather than moving more stuff to patches
I personally think that knowing where it actually comes from is preferable, especially when it's not just about the GL version but about a specific extension.
hierarchy for that would be
NeoGpuDevice extends GpuDevice
GlDevice implements GpuDevice
NeoGlDevice extends GlDevice implements NeoGpuDevice
What is a NeoGpuDevice and why is this not an injected interface?
something that doesnt currently exist
but how i would personally handle where any added function to the interface gets added, instead of patching it into GpuDevice directly
Why do things differently than our standard pattern?
I was very confused by this class, especially compared to NeoGlDevice
Putting the abstract extension methods into a GpuDeviceExtensions interface instead of patching them into GpuDevice is perfectly fine and in line with the way we do it everywhere else. My argument is literally just about their implementation in the backend-specific GlDevice vs. an extension thereof which requires making a vanilla class abstract
Ah yeah I'd just patch them in GlDevice directly
is the main problem making GlDevice abstract?
Understanding the hierarchy of what you propose took me a while, it's much cleaner to patch directly the methods into vanilla
the way i suggested would fix that, because GlDevice wouldn't implement the GpuDeviceExtensions/NeoGpuDevice interface
As a side note using Neo(Forge) in the name of class should be avoided, it's non-descriptive and generally means that the design can be improved
(except for use cases like datagen of course)
It would avoid making the vanilla class abstract but would at the same time hide the extensions from the caller unless they specifically cast to GpuDeviceExtension which is awful for useability and discoverability
could patch RenderSystem#getDevice to return the extension interface instead
It would because of the interface injection of the extensions into GpuDevice, no?
but thats not ideal either
Well no, Rogue's suggested approach is to only implement the extension interface on the custom GlDevice subclass
the main thing i want to avoid is implementing everything in patches
That is really not a problem though
I don't see a particularly good reason to avoid that. In the end it's a single patch hunk (or two if you also need to add fields), it's not like we'd be spraying lots of tiny patch hunks everywhere across the whole class
Well one thing I see as majorly annoying here
Is that with it being a patch it requires him to use FQDNs everywhere
Which tanks readability of patches
Especially if this starts growing
For a method or two that is fine
^ like 99% of my problem
For anything more then that, I would highly suggest moving the implementation out of the patch either into the default methods of the interface
patches also have worse visibility into the code's history
Or into a completely different implementation
for anything that isn't trivial, good code history is a useful thing to have
Also documentation in patches is awefull
As I pointed out above, the difference between just a class name and a FGN of any of the LWJGL/GL/GLFW classes is basically irrelevant. Static imports are awful and you will not convince me otherwise
I highly disagree on the the name irrelevance part. I agree on the static import part for methods. If it is for fields I do them (See OpCodes shit when working with ASM)
note: i do use classnames for Java things, its with GL/VK where the spec required prefix is also there
IMO, GL46C.GL_ tells me nothing more than just GL_ does
the FQDN same thing, but longer
however, my own code has GpuTexture.USAGE_CUBEMAP_COMPATIBLE, because there is no other prefix on that
GL46C.gl* same idea vs just a gl* function call
In the case of GL core methods/fields it at least tells me which GL version it comes from. For extensions, it tells me which extension this comes from. This is valuable information for anyone who isn't so deep in the matter that they've memorized this stuff.
i mean, even i dont have it memorized, mostly, lots of googling and checking against spec
i also have less issue with GL46C vs the entire FQDN for it
@late lotus am i good to resolve the convos about the reflection use in GpuDeviceProperties/Features, given that it no longer exists? also the one about use an interface instead. (from first review)
https://github.com/neoforged/NeoForge/pull/2399#discussion_r2180997930
https://github.com/neoforged/NeoForge/pull/2399#discussion_r2180998955
https://github.com/neoforged/NeoForge/pull/2399#discussion_r2181003314
yes
similar on this too, not fields anymore but the items are still in the interface
https://github.com/neoforged/NeoForge/pull/2399#discussion_r2180997582
oop, wrong link on that one fixed link
final var is not a problem just ugly
i mean, java doesn't have val
just var is a lot nicer IMO
i have a preference toward using final when possible, it can help for performance but usually for me its making it clear that the reference/value wont change
yes sure here it doesnt really matter, but i do this basically everywhere
i dont really care either way (C++'s const doesnt work the same way, so i often cant at work), just a preference (hence doing it in the first place)
final is useless on function parameters and local variables; I'd even argue that it's harmful for readability of the code. I can't force you to change it so feel free to mark it as resolved
its not useless though, it does still enforce that you cant re-assign it, and i have seen the JIT be able to pull of fairly large performance gains because of it
citation needed on that.
not on local variables or parameters for sure, wrt. JIT
granted, said perf gains were some hella math intensive code, but still
this code, particularly runIrradiationRequest
https://github.com/BiggerSeries/BiggerReactors/blob/master/src/main/java/net/roguelogix/biggerreactors/multiblocks/reactor/simulation/cpu/FullPassReactorSimulation.java
though i might be wrong or otherwise misremembering on that, so i can re-run the test
unless I am mistaken, final modifier on local variables doesn't even make it into the bytecode
idk, i just remember seeing a perf gain from slapping final on everything i could
well that is precisely what we are pointing out; that this does not seem true
what can make a difference is using a local variable instead of an instance field
50/50 that wasn't from the compiler but rather from me changing something that let the compiler make some new assumption it could, ill re-run and see on that, im curious on if this matters or not
i was already doing that afaik, ima just re-test and see
also this one for reply or resolve, https://github.com/neoforged/NeoForge/pull/2399#discussion_r2181013877
it is a lot of boilerplate, but i think keeping it as its own layer is the better idea
eventually, some more invasive validation may also be done, where keeping that in its own layer would be better anyway
idk, this is a lot of code that does not need to be done in this PR
I have noticed that you tend to get carried away in your PRs it seems; this is not good for the poor reviewers like me who have to understand why the change is needed
thats probably a habit i picked up from work, where its the exact opposite problem
single CRs are preferred, so its reviewed at once as a whole
ie: this is a related thing, instead of a second CR to bug everyone about, ill just shove it in this one
don't you have the concept of reviews?
because big PRs are extremely annoying to review imo π
CR: Code Review (or Change Request)
ah right
i can change it and/or break this PR up, but explaining my thought process
properties/features exists for enforcement, so the validation layer is there to use those (and enforce them)
features has logicop as the only one, and is given as an expected-usage example
properties has depthZeroToOne as a usage example of something where other parts of the game may need to react to a GpuDevice property
other properties are covering extending B3D (validation layer checking that), or existing B3D features that it doesn't check itself already, and serve as examples for how other properties may be used
so, my reasoning is that we have a very clear scope for the PR, namely providing the ability to extend B3D in a non-breaking way in the future
adding additional properties such as depthZeroToOne should be doable later, and needs to be reviewed properly based on proposed use cases
I would expect validation exclusively for the new B3D features, at least in the first step; this is simply to ensure that we can extend B3D later in a sane way
i can pull those things then
also, this has at least 3 conflicting ways people would like to go, https://github.com/neoforged/NeoForge/pull/2399#discussion_r2190675964
it is mostly boilerplate in the current PR but, preferences are
xfact: definition in GpuDevice and implementaiton in GlDevice
tech: definition in new GpuDeviceExtension interface and implementation in GlDevice
me: definition in new GpuDeviceExtension interface and implementation in new NeoGlDevice class
and then what the PR currently has: definition in GpuDevice and implementation in NeoGlDevice
I never said the abstract methods should be in GpuDevice, I was specifically arguing about putting the implementations in GlDevice. I'm in the same boat as Tech that the abstract methods should be in an extension interface
^ i thought you implied that by stating that the opposite is bad for usability/discoverability
That was referring to the approach of having NeoGlDevice implement the extension interface instead of GpuDevice extending said interface which would have lead to the extension methods being "invisible" unless you already know to cast to either the extension interface or NeoGlDevice first
i mean, if GlDevice implements the extension interface, largely the same problem
and also, the validation device's existence will prevent people from casting to a *GlDevice anyway, at least in its current form
Not nearly as bad, because auto-complete will suggest the extension methods on a GpuDevice variable/field/whatever
oh, wait, you mean having GpuDevice extend the extension interface, rather than the other way aroudn?
Yes, as we usually do
so, GpuDevice extends GpuDeviceExtensions that GlDevice then implements?
Yes
ok, yea, that i can see
i was thinking the other way around, i see no reason to not do that actually
i think theres currently some functions in CommandEncoder and/or RenderPass, those should probably be pulled to a similar extension interface, but that can be done later
so, then its just where the implementation goes
Yeah, the stencil stuff. IIRC, it was initially patched in because it was two little (one and two methods respectively) to justify an extension interface
yea, this stuff probably is by itself too, but ive got a long enough list of things to add that it wont stay that way
which is where my argument for i dont want to implement this all in patches comes from, that list isn't all that short, and i know vaguely what implementing it will look like, and doing that in patches will suck
As a middle group we can add the extension interface but do the actual implementation in a subclass of GlDevice
thats basically what i was wanting to do
it being a super interface to GpuDevice changes very little to me
What I dislike about that is that it requires either providing throwing default impls for the extension methods or making GlDevice abstract
We can always move it out in another BC window
i was meaning specifically the interface(s)
definition in GpuDevice vs GpuDeviceExtensions (with GpuDevice extending GpuDeviceExtensions) makes basically no difference for the implementation
for the implementation, thats in GlDevice or a sub-class of GlDevice
Users are the mods who want to call the new methods
yea, and it makes little difference for them too, in either case if they have a GpuDevice, they can call it, i dont think we actually disagree on anything with the interfaces that mods will actually use
Btw the convention is Extension without s π
noted, will make sure to use GpuDeviceExtension when making the interface
i just dont want to put the impl in GlDevice itself via patches
I think we can start with a patch and potentially move stuff later if it gets unwieldy
Well let's see. Yagni etc. It won't matter until we port again in which case we'll be able to move the methods
i mean, ive implemented them before, and a whole "ComputePipeline" is going to be fun to try and impl in patches
yes i know the "ComputePipeline" object wont be in patches, but the GL impl part would be.
particularly because compute can be dispatched from inside or outside a renderpass (GL doesn't care, but the existing renderpass execution restrictions are pretty directly tied to what VK does)
Final is not persisted on local variables, its source only. On parameters it only gets persisted if you enable parameter reflection in javac (disabled by default)
Now, final on methods is super useful, the jit does use that as a hint for inlining
@wraith osprey I sorta think that requesting extensions is out of scope for the event in Neo. The only case where you need to enable an extension specifically is with VK, and with that only if you are directly using VK as well. Given that the use case is asking Cinnabar to enable something, I think it would be better to have Cinnabar expose this instead.
It's designed mainly for mods which need, e.g. compute shaders
It has been requested before from what I understand
(and also from what I remember, I don't remember where/when/how though)
I plan to add compute shaders to B3D directly, so no need to ask for the GL extension
ofc, but there are other extensions which would be nice to have too
GL also doesn't require the extension to be enabled, so similarly no need to ask
but they may not be present on e.g. macos
I think giving, encouraging, or generally making it viable to do direct GL/VK calls is just asking for trouble, mods should do everything through a b3d exposed api
^
I dont want mods suddenly exploding when running on VK, or vice versa
or, oops sorry we only support dx12
I would normally agree, but there's no way we can be fully prescient what people will need access to, so we should design a safe escape hatch which lets people break out if they absolutely need to
I think if people want to do that, they can do it unsafely with mixins
Its an incredibly hard line to draw in the sand, i'm sure some other maintainers share your viewpoint, usually i'd be on the other side here
the existing interface says if you are on GL or not
For GL, there's no need to ask
For VK, Cinnabar can let you ask for extensions
there is a need to ask on OpenGL because not all extensions are always present
like I said on macOS as an example
You should be very aware of the backend being run on when doing something
Throw a mod loading exception yourself if the extension isn't present then
Same as you'd need to currently
i thought with mac you got ether 2.1 legacy profile, or 4.1 core
there was no mixed
2.1 compat, 3.3 core, or 4.1 core
i didnt thnk there were extensons to enable
Depending on hardware
mac gl is cursed af, your probably right there
GL doesn't have the idea of enabling extension, they always are enabled, they just might not be present
it doesn't have stuff like mesh shaders though, plus the combined depth/stencil attachment is TECHNICALLY an extension which we check for
it has the concept of profiles, which can present different extensions
VK has the idea of instance extensions, device extensions, and needing to explicitly enable them too
usually they are forward available, on older profiles
Not really, you have compat and core, and then GL version
Everything 3.3 and up are just making extensions part of the core spec (asterisk on DSA, there's one function thats weird)
I'm pretty sure mac gl was particularly cursed here, if you requested 3.2 you got ONLY 3.2 and nothing newer
unlike windows/linux
No, that's Nvidia that does that
Mac has two request modes, 3.2 or 4.0, for core.
3.2 will give you 3.3, 4.0 gives you 4.1
I can ref the GLFW code that does the context creation
Or the CGL docs on it
I don't really want to argue it, whatever
@keen pine because you are awake, can i get your opinion on ^ https://github.com/neoforged/NeoForge/pull/2399#discussion_r2196369630
Yeah sure
oh, i meant specifically monica's ask
As said seems like a reasonable ask to have
There are for sure scenarios where you can use a fallback rendering route
When an extension is not available
I still dont think there should be any defined api for mods to directly use GL/VK, thats asking for trouble
Yeah
I'm mostly against it because it encourages reaching around, and that if thats being done it should be done with a more direct hand to the backend being reached around.
Okey
Well I am on the fence
It is a reasonable ask
But if that circumvents the original concepts
Then it is a no
Reasonable ask, but i think its better to put it as a ask the backend direrctly thing, rather than the generic API
the generic API should stay generic
Directly asking Cinnabar to enable some extensions is fair, as to do so you must know exactly what backend is being asked to do this
Okey agreed
rather than any VK backend (though theres unlikely to be a second) responding to an extension request, when it may not behave the same and may break when reached around in that specific way
reaching around GL is also where state machine issues are liable to poke their ugly head up, and even i have been at fault for those.
making sure everything is always cleaned up properly is a PITA at best, and human error is human error.
you can always instanceof-check the device and do backend-specific stuff if you must, yeah
and btw, the extension strings are backend specific, correct?
Yeah
Yeah then I'd not put that on the generic interface or event
Fair enough
I felt the concept was general enough that it seemed reasonable to put it on the generic interface, and a backend can fairly trivially detect a "bad" extension.
yes but the consumer should pre-check if they're working with a backend they can reasonably expect to have that extension first
but if they do that, then they can just call a backend specific method, I think that will hopefull lead to more graceful degradation than outright crashes
Seems good enough to me
One thing that came to my mind last night with respect to the debate about patch vs. subclass for implementing the extensions: since GlCommandEncoder and GlDevice are (mostly) stateless, the extension methods could also be implemented in an extension interface that's implemented on GlDevice:
interface GpuDevice extends GpuDeviceExtension { ... }
interface GlDeviceExtension extends GpuDeviceExtension { ... }
class GlDevice implements GlDeviceExtension { ... }
If an extension method does need to retain some custom state or touch any vanilla state, then getters and/or setters for those fields could be declared in the GlDeviceExtension interface and implemented in GlDevice via patches.
what is the benefit to doing it this way as opposed to making GlDevice abstract and extending it?
Not having to make GlDevice abstract
what is the drawback of doing that?
I think we generally try and keep bincompat with vanilla, but that line is fuzzy these days i think
i guess its mostly that im considering GlDevice an internal API
Is the class public?
it has to be because RenderSystem makes an instance of it
there are no "internal" APIs in Minecraft
Then its public mc api surface
but thats the only place anything in that package is referenced by Mojang iirc
We still try to keep bincompat as far as reasonably possible. There are only very few cases where we break bincompat and all of them are methods, IIRC most, if not all, of them are private (not that that stops people)
Historical precedent for this is things like liteloader, and i imagine it makes fabric on forge easier at times
I honestly got a bit lost in the scope of all the PRs being opened.... Can you give a summary of the goals we're trying to reach?
The initial one on my radar was adding missing GL state to Blaze3d
2399 is get the boiler plate in place so that adding stuff to B3D isn't a breaking change
this comes along with the validation layer to check usage, and mechanisms for reporting and enabling things that may or may not be supported by the backend being used
when you say "adding stuff to B3D", what kind of additions are we talking about, and who'd be doing the adding?
so, what this allows is for something like indirect drawing, and any required usage bits, enums, functions, etc, to be added while Neo is stable, and if the backend doesnt support it, it reports it doesnt support it, and calling any of those functions or using those bits/enums is considered invalid (and the validation layer in dev will catch you doing this)
note that "reports it doesnt support it" is more "doesnt report it does support it" because doesnt support is the default state
this is a very similar style to how VK handles extensions, with GL being vaguely similar too (just no need for explicit enabling)
the other benefit of the validation layer is that in the future profiles for it that match different known hardware could be made
that would allow for testing in an emulated environment more restrictive than the hardware used for dev, without actually owning that hardware
Ok, so the goal is to establish an API contract now for backends (which is still breaking Vanilla compat) that establishes they need to handle extensions of certain enums gracefully
Not so sure about the validation layer and how that plays into that
both the backends need to, and mods need to
old cinnabar doesn't know about USAGE_INDIRECT
new mod does, and uses it
the validation will check that you enabled the indirect feature when using the USAGE_INDIECT bit, in dev
at runtime, if you try and enable that feature, and Cinnabar doesn't know about it, there will be an exception thrown (which the validation layer enforced that you asked for)
the validation is entirely an enforcement mechanism to make sure mods (and backends) aren't breaking the contract
I don't know if the validation layer isn't complete overkill, tbh.
if theres no enforcement, then mods are liable to crash later, rather than at startup
something like Quartz could never make an indirect draw call and everything is fine, but the moment it does everything explodes because Cinnabar doesn't support that, and there was no check nor enforcement of that check
VK in particular also has this fun thing where unless a feature is enabled ahead of time, its not available. So this means that even if the version of Cinnabar does support it, if the featue wasn't enabled it may not be available, and now you get a native level crash instead
GL doesnt suffer from that one, if GL advertises it, its enabled and available
@fossil isle 2399 poke
Could a check for using the right license header be added to the immaculate checks?
I know there already is a license check, but i didn't even notice that there were two different headers
at some point i copied one, obviously got the wrong one, and then just copy-pastaed it again and still had the wrong one
no, it's intentional so we keep the forge ones where they already exist
only for new files*
immaculate doesn't know what is a "new" file is the problem
i was going to suggest however tech did it for interfaces, but just listing all the files that are allowed to have the old one probably isn't reasonable to do...
@fossil isle i think i got them all
also, for vis here, these two PRs aren't strictly dependent on 2399, but should be merged (or rejected) before stable
https://github.com/neoforged/NeoForge/pull/2445
https://github.com/neoforged/NeoForge/pull/2448
There's a very simple solution for that: just don't add the header yourself, instead run gradlew applyAllFormatting and it'll add the header for you as necessary
thats exists? cool, ill use that next time
i have a feeling ill be adding more files at some point
2399 looks fine to me now except for one point: we should provide a way to disable the validation layer in dev (could be done with a system property), for example to remove the overhead when doing performance testing.
ie: if System.getProperty("neoforge.disableB3DValidation") turn it off?
Yes
i thought about this when adding the prod check, and then promptly forgot to actually add some mechanism to force it off in dev
so, off if (FMLLoader.isProduction() || System.getProperty("neoforge.disableB3DValidation") != null) {
I would do if (FMLLoader.isProduction() || Boolean.getBoolean("neoforge.disableB3DValidation")) {
does that work where just -Dneoforge.disableB3DValidation turns it off?
the != null does, because that returns an empty string
No, it would require a boolean value for the property
i mildly prefer being able to just define it, but only mildly
so, Boolean.getBoolean it is.
pushed up
also, do you have any particular thoughts on 2445?
Not really, no, in part because I lack the knowledge to understand what that bit would actually do under the hood
ahh, it tells me that i can use a temporary buffer to back it, and dont need to track that frame to frame
for entity rendering, that means i can use a bunch of them, upload all of them from the CPU at the start of the frame, and then use a single continuing renderpass for all entities later in the frame
rather than stall, upload, stall, draw, stall, upload, stall, repeat
without that bit, sync requirements make it a lot harder to try and do things like that
GL can also utilize it by having a much larger persistently mapped buffer(s) backing it, and using a new section of it on each upload, to similar sync benefit
its a very neat trick thats helpful for how MC does entity rendering in particular, or any staging buffer
notable that this is exactly what Apple's GL driver is doing, for the same reason as Cinnabar defaulting to it
Hmm, interesting
@late lotus @keen pine poke for 2399 because requested changes
cc: @autumn zephyr too if you want to actually look it over
in general, i dont expect mods to use it, but they also probably don't need to
Nvidia and probably AMD will allocate multiple buffers behind the scenes, and do magic with them to avoid most of the cost. I tried something similar, it didn't work out well. Could probably try and make it better, but id rather not try and replicate the Nvidia driver.
This does make me wonder again what the Qualcomm windows driver is fucking up to require that staging buffer workaround
likely a sync thing
using a single buffer for upload is likely making it stall a lot, which solves the sync issue
π€
i say that because Cinnabar had neither issue on Qualcomm hardware
but Cinnabar has to do its own syncing
logicop also works fine, because reasons
also, isn't it 3am for you?
Yes π
do you just, not sleep?
My sleep schedule is completely fucked
understandable, have a nice insomnia
@vale otter ok, here is the code I was referencing
uses some util functions from this file as well
and it uses this shader with this include
the TLDR is that there is an in-game editor for the dragon players in the mod, and the end user can select between hundreds of different parts, each with their own hue/saturation/brightness; what this does is take all of the user's skin data, and then dynamically render the individual textures into a "master" texture on the GPU for each player, which is then cached
just wanted to know if there were any major red flags for the new B3D API
embeds
embeds yeeted
let me also pull up a quick screenshot of what this editor even looks like for some extra context
as it is right now, it looks like you are going through the old RenderSystem GL manager and old B3D, you are likely already covered by existing B3D
some things will need to be updated, but feature wise you should be covered
oh ok great
it should be a relatively straightforward port, its mostly just defining stuff ahead of time in a RenderPipeline instead of directly setting stuff in RenderSystem
so the new stuff is stateless (like vulkan)?
since I had issues with this setup in the past where I completely messed up the state of stuff (regardless of my attempts to try to restore the GL state) so I just forced this to all happen at the start of the frame rather than on-demand (which was how it originally was setup)
pretty much, yes
awesome, my life is becoming a lot simpler then 
its notable that a VK impl was alluded to, that already exists (and I'm the one that made it), which ofc is completely stateless
it is a very vulkan-like API, not perfectly, but enough to make that ^ possible
one thing i will say, dont yeet shit every frame
glowTarget.destroyBuffers();
normalTarget.destroyBuffers();
dont do that
you should re-use stuff like that for many frames
well in this case the size of that target might change
depending on the textureSize of the DragonBody
so i wasn't really sure what to do
make a bigger than necessary one, and set the viewport/scissor
then blit the subset you used
the problem is that when it is time to render the dragon it expects the texture to be that size
as in, not have some of the data be a subset of a texture
i guess i could do it at the time of the copy
anyways i'm aware there are ways to fix this, i'll figure it out if i have the will and time at some point in the future
the thing is, we're not really yeeting this every frame in reality
since this generation code is only called when a new player joins with skin data, or an existing player changes their skin data
which happens very infrequently, other than on the editor screen
it happens every frame in 1 case, if the user is sliding the color bar in the editor
so it smoothly updates the texture as the user is changing hue/saturation/brightness
maybe cache whatever the last used texture was
in that case, it should always be the same size, so the same one can be re-used
i suppose
creating/deleting textures is expensive to do
buffers too, but i dont see you making buffers, just textures/framebuffers
in any case, this is miles better than the previous implementation that was done, where it was done as a blocking CPU-side copy, the whole game just stalled to 10 FPS when you moved the color bar before 
(i rewrote a lot of the old code for this mod to get rid of crazy stuff like this)
yea i'll keep this in mind, maybe i'll try to fix this block of code up to not do this when i have to refactor it for the rendering updates anyways
your refactoring should be pretty simple luckily
mostly just making a render pipeline instead of a shader instance, and then shoving all your uniforms into a UBO
@fossil isle i forgot about doing this earlier, do you want me to open a PR to move the stencil state to the RenderPipeline, or do you want to do that, or someone else?
if its not moved id need to check hardware compat for VK_EXT_extended_dynamic_state because VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE and VK_DYNAMIC_STATE_STENCIL_OP aren't part of VK 1.2, which might be a problem
so far, ive just had the functions throw, but ofc if its not being used thats never going to be hit so i forgot about it
got a prototype of moving the loading overlay to using a B3D backed APIRenderer working. was pretty easy to do, but also uncoverred a bug in B3D, go figure
ill get PRs for that bug and for moving the stencil state open tomorrow
also need to add viewport to the renderpass, didn't realize that wasn't supported, but thats easy enough to do
^ the loading overlay uses viewport, hence needing that
Feel free to make a PR to move it
@vale otter one more thing regarding 2399: please adjust the PR description to match the adjusted scope of the PR, primarily with respect to the removal of the depthZeroToOne property
what is the stencil test supposted to look like when its working correctly?
this doesnt seem correct based on the description
ok, thats the exact same as what happens w/o my changes, @fossil isle, is the test broken or is that expected?
It's quite possibly broken in 1.21.7
I don't remember what we did to try and fix it
yea, i checked in renderdoc, the grass doesnt even get rendered
How did we even try to fix it? PIP?
it kinda has to be a PIP
since that's the only way to interleave raw render-calls, but funnily enough that also means only the off-screen render target will have stencil use
i mean, w/ pipeline modifiers, if it can hold the modified pipeline for the render later, that would work
Hmmmm, I vaguely recall talking about having to apply pipeline modifiers as render-state is being recorded for the GUI but I don't remember if we followed up
(which requires GuiGraphics to have its own modifier stack IIRC)
yea, probably would need to mirror the scissorStack
yeah
Although we wouldn't need to capture it in the actual state
we could simply apply the transform as the pipeline is stored in the state
There's nothing stopping someone from using stenciling in-world in a RenderLevelStageEvent handler (BER or ER would technically also work, but you'd have to explicitly end the batch of your render type within the renderer which is discouraged) and in such a case the main render target could have a stencil applied
Yes, it's more likely to be used in-world
I really meant more in this particular test case
IIRC the only user of stencil was Immersive Engineering wire-rendering?
Or am I imagining things
As for stenciling in UIs: I'm not sure it's worth supporting them on elements directly since you'd need to render two elements, one that prepares the stencil and one that uses it for masking, and relying on these elements staying in order (especially when there's more than one use of stenciling on screen at once) is a fools errand in my opinion
Probably, yeah, the pipeline modifier is just a more general thing
Especially for item rendering in the UI
but I'd also expect stuff like this to be PIP
The pipeline modifier system would still be relevant for this within a PiP. Only downside is that you need to flush the buffer yourself within renderToTexture() instead of leaving that to vanilla
should fixing it be included as part of PR to move it to the pipeline, or because the test is currently broken its fine to leave it updated to use the modifier system but still broken?
Would be nice to fix it since you need to modify it anyway
Which PR? I hope not 2399
no, not 2399
new one, doesnt exist yet
moving stencil to the render pipeline, rather than dynamic state on the renderpass
ill push this up to a neo repo branch so it can be fixed there then. basically as soon as the B3D API layer is crossed i know a lot less about whats going on, especially with how exactly the recent changes to gui rendering work
Not gonna comment on final var but please don't use it in future PRs π«
i mean we don't have a style rule on it but i'd also prefer not to use it
a rule on it should probably be added then
or, i guess more generally than final var is that its final on a local, because final GlDevice is the same thing there
Why are Properties and Features still different concepts?
There's really no difference on the user side IMO
Without looking at the code: property == value range, feature = on/off?
such as: how many texture units does the GPU have, blah blah
properties are read-only I'd assume since they're just information about the device the user has?
where the spec leaves wiggle-room such as "minimum of 4, up to X" of some thing
At least that's how it was for GL
yup, though there can be two different sets of properties, theoretically available (during device config), and then actually available (varies by feature enablement)
you mean additional properties or the value of properties can change?
I'd find that surprising
ie: if extendedDepthTestFunctions is disabled, the extra depth test enums aren't reported (note: doesnt exist currently) after configuration
so properties after config tells you what you can actually use, where during config is what you could use
now that's confusing
Are you sure? It sounds more like you need to enable a feature to potentially get more values in a property
but as far as I understand it, some features will add a minimum set of new values, but depending on device support may add more, hence the potential need to inspect the properties for support (you should confirm that, RogueLogix)
and maybe we should make that a bit more concrete
But I find the use of properties before feature requests a bit odd
We discussed that anything beyond standard OpenGL core 3.2 needs explicit opt in
for reference of why its separate in the first place, and where the ideas come from
properties is following the idea of VkPhysicalDeviceProperties which you fetch from the physical device before logical device creation
features is following the idea of VkPhysicalDeviceFeatures which you fetch from the physical device before creation and then pass a subset of to logical device creation to actually enable things
I think the separation is quite artificial
And I don't think that doing things because that's how it is in vulkan, makes sense for what we're doing here
So from a modder perspective
Okay no, before I continue, I'd want to see what are examples for properties. I'll have to look at the PR I guess
Which one is this in?
2399?
2399,
Examples might be LogicOp or some GL 3.3 dest factor
Ok
or the number of texture units available, that would be a property
without a corresponding feature
Yeah but that's not really in scope of what we're doing here IMO
I'd expect logic op to be present only if there's a HW support difference
If the only difference of supported logic ops is whether feature X is requested or not, but the supported ops are either fully supported as requested or not
then that doesnt need to have a property
currently, but the whole idea of properties is to report things like that, especially if there isn't a feature difference (ie: how many textures can you use)
Which is the case at the moment in the PR, logic op only has a feature but no property
vice versa is also going to be common, and also many properties for one feature
well there can be overlap between feature and property
just as a thought experiment
Well, we might want to make using additional texture units require optin (else validation would fail). Again just an idea
the GL 3.2 spec says: LogicOps A, B and C are required by 3.2, but devices may support D, E, F
and then there might be an extension that adds G, H, and I
that'd be a case where you have a property + a feature
The device either has the texture units or it don't
you don't get to ask for more
That sucks from a modding pov though
Which one?
The point is that the modder acknowledges that the feature is only available on some hardware and will have a workaround in place if it's not
should it though? GL and VK both just tell you the number you can use
sure the number you actually use can be validated, and we can have a profile for the most limiting, but idk if you should need to ask to be allowed to use more
Yes, which is why I think properties you don't need to know before config (?)
Is there a use case for knowing the properties before config, i.e. to ask for a potential extension if the device doesn't support another optional feature?
I can see such cases
some properties/features may be an all or nothing thing, so you can leave many disabled if one isn't good enough
i.e. to implement alternate render paths if the device doesn't have a native feature
Texture units are a bad example because RenderSystem only supports 12 anyway, so you'd have to bypass B3D to use more
12 is good enough for anyone!
ie: i need compute and 24 textures, else i care about neither
RenderPass lets you use however many you want
My point is we want modders to opt into non standard "things"
yeah yeah ok ok, but we should limit properties to the ones that actually change with HW suport
the rest is superflous IMHO
Splitting these things across two interfaces is not good
optional HW feature are standard things
There's no difference from my pov
Okay so?
you only use these properties as a decision point to use an alternate rendering implementation
if the device simply doesn't have what you need
Well same as extensions
as I said, read-only vs. non-read-only
no, since you can request an extension (write access essentially)
If the device doesn't have the extension you have the exact same problem
while you cannot request more properties
bad wording
you cannot request more support from a property
I am operating under the assumption that we don't want to add extensions to B3D that might not work for some hardware unless the modder opts in
?? B3D does not though?
b3d takes in GLSL, it does
We discussed this a while ago
So you might also be making the wrong assumption π
No, I am thinking a lot more generally
features are all booleans
on or off, if theres mutitple levels, thats separate features
I don't care if it's a bool, an int range or a set of flags, it's the same concept that the hardware might or might not support it
Keep in mind that there are two layers of support involved here, one being the actual hardware support and the other being what the backend implementation supports. The point of this abstraction is not just to indicate what the hardware can support, it's also to make the addition of new B3D features (i.e. new enum values, new usage bits, new draw modes, entirely new systems like compute shaders, etc.) non-breaking for custom backends
Okay let's take maximum texture size
If we add a GL3.3 feature to B3D do we have any guardrail to make sure modders don't accidentally lock out X% of the players from using their mod?
I don't see at a glance that B3D validates this for created GpuTextures
0% in that case but idea applies
GL 3.2 guarantees 1024x1024 as the supported minimum size, but devices may support more
And since B3D doesn't seem to clamp it
You already have the situation that mods can use the HW capability
And max texture size is precisely something I'd expect to have a property for
Well yeah this would be impractical to enforce. But it's also a different topic, and arguably out of scope for this one PR
Is it?
You need the property in a device-independent manner
erm
backend-independent, not device
Yes, the PR is already large enough. I don't want more stuff in it
So that is true
We'll do it in a second PR if we need to
GpuDevice#getMaxTextureSize() would like to have a word with you
oh ffs
Same thing for uniform offset alignment for example
Is that on mojang?
amazing
okay I'd go with Tech here
on the following basis:
we can easily add properties later
Those should be purely informational
Properties should be known during config IMO, specifically to inform decisions on what features to enable
And/or fail mod loading immediately during device config, rather than at some later point
Yeah but mojang didn't make it so, so changing that is another discussion
Right now the goal of the PR is to allow opting in to B3D extensions, without adding extensions yet, and doing so in a way that future extensions can be added without being a BC for B3D implementations
The difference is that the two properties vanilla exposes are extraordinarily unlikely to decide over the viability of a feature