Clone the sources: git clone --recurse-submodules github.com/emeiri/ogldev.git If you want to get the same version that was used in the video you can checkout the tag 'TUT_50_DSA'. Build on Windows: Open the Visual Studio solution: ogldev\Windows\ogldev_vs_2022\ogldev_vs_2022.sln Build the project 'OpenGL Tutorials\Tutorial50_DSA'
Thanks! I think that the efficiency is in the way you structure your game, rather than on a function by function basis. So don't expect something like "glCreateBuffers takes 5% less cycles than glGenBuffers+glBindBuffer". It allows you to rethink the way you architect your code and reduce the number of state changes to help the driver become more efficient.
@@OGLDEV I don't know where the TextureUnit variable in your code is coming from though, like where in the code do you assign it? I tried messing with the enum and couldn't get it to work. I've been experimenting though and it apparently the 1st argument of glBindTextureUnit (GLuint unit) and the 3rd arg of glProgramUniform1i (GLint v0) have to be the same number, which has to be an unsigned int that is less than some sort of max texture number.
In this tutorial I'm using ogldev\Common\ogldev_phong_renderer.cpp as a simple renderer class to take care of setting up the state for each draw call. In PhongRenderer::InitPhongRenderer() we set the different texture unit indices into the technique class and this goes into the shader. These values are zero based indices and this is what the shader expects in its sampler uniforms. These indices are defined in ogldev\Include\ogldev_engine_common.h and each one has a corresponding OpenGL enum which is used by the BasicMesh class when it binds the textures before the draw call. If you are on a non-DSA system this enum goes to glActiveTexture but in the DSA case we actually need the same zero based index so we subtract GL_TEXTURE0.
I have not experimented with DSA that much before... is it possible to intermix object handles made from direct and non-direct calls? Can you intermix direct and indirect access calls in general? As DSA uses _Create_ and non-direct uses _Gen._ For example can you _create_ a handle and bind it for state change? And vice versa, can you _generate_ a handle and then directly change it's associated state with no binding? I just imagine if they can work together, this can be quite useful for being very particular with what is happening to state, leaving room for more optimization. In general I just sort of question how DSA works internally because the way the API is designed implies (to me) that it is an independent system which frankly confuses me. If they are indeed independent then yeah DSA does feel sort of pointless... though it does technically save on some gl calls but ultimately with an decently designed system my intuition tells me gl's state machine will still take the lead. This of course ignores the fact that maybe ADS includes internal optimization that isn't possible otherwise. This is being very nit-picky however, regardless if you want to work with state or not, the choice is nice, being black and white. I'd look into docs and specs I guess but prefer a clear answer. I have to give thanks to the great presentation and clear examples! It makes it easier drill into my head, the overlapping names has always made it harder to search about, awesome!
Thanks! Regarding intermixing DSA and non-DSA calls/objects - I haven't tried it myself so I'll just tell you how I assume it works. When using non-DSA objects you generate a handle and it remains stateless until you bind it to the state. So if you create an object through a DSA call and then bind it to the state it will probably work. The system will simply not initialize it since it has already been initiaized in the glCreate* call. It's not the optimized way of working and some drivers may warn you about it if you enable debug output but it may work. The other way around may also work but only if you completed the initialization of the object by calling glGen* followed by glBind*. For example, the man page for glTextureParameter says that "GL_INVALID_OPERATION is generated by glTextureParameter if texture is not the name of an existing texture object.". But if you did the above two calls then the object is indeed an existing texture object so I expect it to work. If you skip glBind* then it will not work because the handle has not been initialized. Therefore, I don't think this is really two different systems. It's just a set of APIs that activate the same components in the driver in a different order, if that makes sense. Again, it's easy to test this but I just don't have time right now. Also remember that sometimes different drivers behave differently when it comes to stuff which is considered undefined behavior (though I can't say that this is indeed undefined).
If you take advantage of this correctly this can bring many benefits to your code. With DSA you have less "save and restore" state changes to do. It also enables more parallelism because you can create and update resources in multiple threads concurrently.
would you consider making a vid on how to set up environment on linux ? like the SDL, some extra cpp niceties so that it touched upon it all in 1 video. (just a suggestion).
I have a video on setting up OpenGL on Linux: ruclips.net/video/h2cP6sQYdf0/видео.html. I'm afraid SDL right now is a bit out of scope for me because I'm currently not using it myself. Perhaps in the future I'll play with SDL/SFML and do something about it.
I recently watched a clip of Jonathan Blow saying OpenGL is the worst option for a graphics API. I don't know what he is on about... I think the state machine design is the simplest way this graphics pipeline could be done... so it seems to me the best way to do it.
Unfortunately, there are not a lot of options to choose from. I think DX11 is a bit more complex and DX12/Vulkan are way more complex. I heard JB say on one of his live streams that the code he was working on currently uses OpenGL as a backend but he plans to replace it later on. I don't know what happened later...
If frame rate is not of essence, then OGL is the most effective way to render a 3d scene (without HW ray tracing that is). That's why I'm using it for my hobby project.
It's just like... his opinion man... OpenGL is alright, I like DSA better than older OpenGL I also think learning Vulkan is useful to get in depth on how GPU works.
@@OGLDEVopengl is one of the first historical graphical api that had changed the world.i love the api and glsl.I think it's good for the hobby and custom applications since we don't have to compete to anyone.If someone want to make game they could use a dedicated well made game engine with more modern api.But we are hobbyists so we have limited resource and energy to dive into more complex api or low level systems like drivers.
@user-je4dh9js1dno the api will be upgraded.If u can't master opengl how can you master lower level APIs?i doubt that most of people would know basic things about vector maths and mappings or geometry to be good enough for mastering opengl.
Thank you for the OpenGL videos - handful of them helped me to setup my basic renderer. I might be ignorant but I see zero improvement in this DSA API 😂 As I see it is just duplication of API with bind-less option. IMHO, In the bigger picture: DSA just made full OpenGL API more complicated and error-prone - now you can call wrong function by mistake (glTextureParameteri instead of glTexParameteri).
You have a point. But, it does improve things. For one, it improve performance. And it is far less likely to mess up the state. For me, I'm still trying to get used to all the different names and how it's used.
It's true that the naming issue is a bit confusing but I agree with @TheJGAdams. Updating objects without touching the state should lead to less bugs, hopefully...
What's the real performance difference? I hear DSA is supposed to be faster, but I've never seen any real data. In what situation do you see a big difference? I have a large codebase that I'm considering rewriting, but it's not clear that this is worth the effort. @@TheJGAdams
@@FrankGennari If you have large database. Try to use function to supply both method. Use macro to let you support old and new OpenGL. Also, it does not require a complete rewrite. You can just gradually insert them in. As for performance. It only improve on the CPU side by eliminating some CPU-GPU-CPU (or is it GPU-CPU-GPU?) So if CPU isn't the bottleneck you can't notice any difference. But, you will for a larger simulation. It is likely to be worth adopting before your codebase get any bigger.
@FrankGennari afaik it can only be faster if it helps you reduce state changes. You need to analyze your code and identify redundant state changes that can be removed. Another "side effect" is that it makes it easier to write multithreaded OpenGL code which has always been a problem because you can encapsulate OpenGL code in threads without affecting the state. This was one of the main motivations behind Vulkan.
True, but most of it is relevant only to driver developers who need to handle all kinds of correctness stuff. As a developer you can get by with just the basics.
A state machine never should have been used. Should have been like this from the start. State machines make for ambiguity and hidden errors. Wish I knew about this sooner.
I guess you're right but it will be interesting to hear the reflections of the original OpenGL architects. This architecture probably made sense to them at the time. It is often difficult to reach a good design on the first step, even when you have the best people as designers. Look at Microsoft and DirectX. They had to go through 9 revisions before they reached something which was well accepted by the developer community and became very popular.
Clone the sources:
git clone --recurse-submodules github.com/emeiri/ogldev.git
If you want to get the same version that was used in the video you can checkout the tag 'TUT_50_DSA'.
Build on Windows:
Open the Visual Studio solution: ogldev\Windows\ogldev_vs_2022\ogldev_vs_2022.sln
Build the project 'OpenGL Tutorials\Tutorial50_DSA'
@OGLDEV, great intro. How much more efficient is it doing it this way?
Thanks! I think that the efficiency is in the way you structure your game, rather than on a function by function basis. So don't expect something like "glCreateBuffers takes 5% less cycles than glGenBuffers+glBindBuffer". It allows you to rethink the way you architect your code and reduce the number of state changes to help the driver become more efficient.
@@OGLDEV So I suppose with anything, it's best to benchmark each one and see what happens, got it! 👍👍
Exactly - assume nothing.
It's amazing how nobody explains what binding is and you did it perfectly in 10 seconds, ty
Thanks!
This is perfect,i may consider this to my application upgrade
Thanks!
I'm confused about where you get the texture unit that you feed to the functions and subtract GL_TEXTURE0 from @ 8:50
I keep the texture units enums and indices in ogldev\Include\ogldev_engine_common.h
@@OGLDEV I don't know where the TextureUnit variable in your code is coming from though, like where in the code do you assign it? I tried messing with the enum and couldn't get it to work.
I've been experimenting though and it apparently the 1st argument of glBindTextureUnit (GLuint unit) and the 3rd arg of glProgramUniform1i (GLint v0) have to be the same number, which has to be an unsigned int that is less than some sort of max texture number.
In this tutorial I'm using ogldev\Common\ogldev_phong_renderer.cpp as a simple renderer class to take care of setting up the state for each draw call. In PhongRenderer::InitPhongRenderer() we set the different texture unit indices into the technique class and this goes into the shader. These values are zero based indices and this is what the shader expects in its sampler uniforms. These indices are defined in ogldev\Include\ogldev_engine_common.h and each one has a corresponding OpenGL enum which is used by the BasicMesh class when it binds the textures before the draw call. If you are on a non-DSA system this enum goes to glActiveTexture but in the DSA case we actually need the same zero based index so we subtract GL_TEXTURE0.
This is the way.
Exactly.
@@OGLDEV I suggest create a video about OpenGL 4.6 with Approaching Zero Driver Overhead (AZDO) where DSA is used with others techniques.
It's on the roadmap. Hope it will make it into 2024...
I have not experimented with DSA that much before... is it possible to intermix object handles made from direct and non-direct calls?
Can you intermix direct and indirect access calls in general?
As DSA uses _Create_ and non-direct uses _Gen._ For example can you _create_ a handle and bind it for state change? And vice versa, can you _generate_ a handle and then directly change it's associated state with no binding?
I just imagine if they can work together, this can be quite useful for being very particular with what is happening to state, leaving room for more optimization.
In general I just sort of question how DSA works internally because the way the API is designed implies (to me) that it is an independent system which frankly confuses me. If they are indeed independent then yeah DSA does feel sort of pointless... though it does technically save on some gl calls but ultimately with an decently designed system my intuition tells me gl's state machine will still take the lead. This of course ignores the fact that maybe ADS includes internal optimization that isn't possible otherwise. This is being very nit-picky however, regardless if you want to work with state or not, the choice is nice, being black and white.
I'd look into docs and specs I guess but prefer a clear answer.
I have to give thanks to the great presentation and clear examples! It makes it easier drill into my head, the overlapping names has always made it harder to search about, awesome!
Thanks!
Regarding intermixing DSA and non-DSA calls/objects - I haven't tried it myself so I'll just tell you how I assume it works. When using non-DSA objects you generate a handle and it remains stateless until you bind it to the state. So if you create an object through a DSA call and then bind it to the state it will probably work. The system will simply not initialize it since it has already been initiaized in the glCreate* call. It's not the optimized way of working and some drivers may warn you about it if you enable debug output but it may work. The other way around may also work but only if you completed the initialization of the object by calling glGen* followed by glBind*. For example, the man page for glTextureParameter says that "GL_INVALID_OPERATION is generated by glTextureParameter if texture is not the name of an existing texture object.". But if you did the above two calls then the object is indeed an existing texture object so I expect it to work. If you skip glBind* then it will not work because the handle has not been initialized. Therefore, I don't think this is really two different systems. It's just a set of APIs that activate the same components in the driver in a different order, if that makes sense. Again, it's easy to test this but I just don't have time right now. Also remember that sometimes different drivers behave differently when it comes to stuff which is considered undefined behavior (though I can't say that this is indeed undefined).
Hello. I don't understand: is it just fancy syntactic sugar? or did they change the whole philosophy?
If you take advantage of this correctly this can bring many benefits to your code. With DSA you have less "save and restore" state changes to do. It also enables more parallelism because you can create and update resources in multiple threads concurrently.
would you consider making a vid on how to set up environment on linux ? like the SDL, some extra cpp niceties so that it touched upon it all in 1 video. (just a suggestion).
I have a video on setting up OpenGL on Linux: ruclips.net/video/h2cP6sQYdf0/видео.html.
I'm afraid SDL right now is a bit out of scope for me because I'm currently not using it myself. Perhaps in the future I'll play with SDL/SFML and do something about it.
@@OGLDEV nice, thank you for your reply !
I recently watched a clip of Jonathan Blow saying OpenGL is the worst option for a graphics API. I don't know what he is on about... I think the state machine design is the simplest way this graphics pipeline could be done... so it seems to me the best way to do it.
Unfortunately, there are not a lot of options to choose from. I think DX11 is a bit more complex and DX12/Vulkan are way more complex. I heard JB say on one of his live streams that the code he was working on currently uses OpenGL as a backend but he plans to replace it later on. I don't know what happened later...
If frame rate is not of essence, then OGL is the most effective way to render a 3d scene (without HW ray tracing that is). That's why I'm using it for my hobby project.
It's just like... his opinion man... OpenGL is alright, I like DSA better than older OpenGL I also think learning Vulkan is useful to get in depth on how GPU works.
@@OGLDEVopengl is one of the first historical graphical api that had changed the world.i love the api and glsl.I think it's good for the hobby and custom applications since we don't have to compete to anyone.If someone want to make game they could use a dedicated well made game engine with more modern api.But we are hobbyists so we have limited resource and energy to dive into more complex api or low level systems like drivers.
@user-je4dh9js1dno the api will be upgraded.If u can't master opengl how can you master lower level APIs?i doubt that most of people would know basic things about vector maths and mappings or geometry to be good enough for mastering opengl.
Thank you for the OpenGL videos - handful of them helped me to setup my basic renderer.
I might be ignorant but I see zero improvement in this DSA API 😂 As I see it is just duplication of API with bind-less option. IMHO, In the bigger picture: DSA just made full OpenGL API more complicated and error-prone - now you can call wrong function by mistake (glTextureParameteri instead of glTexParameteri).
You have a point. But, it does improve things. For one, it improve performance. And it is far less likely to mess up the state.
For me, I'm still trying to get used to all the different names and how it's used.
It's true that the naming issue is a bit confusing but I agree with @TheJGAdams. Updating objects without touching the state should lead to less bugs, hopefully...
What's the real performance difference? I hear DSA is supposed to be faster, but I've never seen any real data. In what situation do you see a big difference? I have a large codebase that I'm considering rewriting, but it's not clear that this is worth the effort. @@TheJGAdams
@@FrankGennari If you have large database. Try to use function to supply both method. Use macro to let you support old and new OpenGL.
Also, it does not require a complete rewrite. You can just gradually insert them in.
As for performance. It only improve on the CPU side by eliminating some CPU-GPU-CPU (or is it GPU-CPU-GPU?)
So if CPU isn't the bottleneck you can't notice any difference. But, you will for a larger simulation.
It is likely to be worth adopting before your codebase get any bigger.
@FrankGennari afaik it can only be faster if it helps you reduce state changes. You need to analyze your code and identify redundant state changes that can be removed. Another "side effect" is that it makes it easier to write multithreaded OpenGL code which has always been a problem because you can encapsulate OpenGL code in threads without affecting the state. This was one of the main motivations behind Vulkan.
It's intimidating that the specification is 800 pages 😭
True, but most of it is relevant only to driver developers who need to handle all kinds of correctness stuff. As a developer you can get by with just the basics.
A state machine never should have been used. Should have been like this from the start. State machines make for ambiguity and hidden errors. Wish I knew about this sooner.
I guess you're right but it will be interesting to hear the reflections of the original OpenGL architects. This architecture probably made sense to them at the time. It is often difficult to reach a good design on the first step, even when you have the best people as designers. Look at Microsoft and DirectX. They had to go through 9 revisions before they reached something which was well accepted by the developer community and became very popular.