noodleBowl

Members
  • Content count

    372
  • Joined

  • Last visited

Community Reputation

713 Good

About noodleBowl

  • Rank
    Member

Personal Information

  • Interests
    Art
    Audio
    Programming
  1. Not sure what you mean here? Wouldn't this be issuing draw commands and then updating the buffers? //Draw a Cube UpdateBufferWithCubeData(); graphicsDevice->deviceContext->Draw(cube.vertexCount, 0); //Draw a Pyramid UpdateBufferWithPyramidData(); graphicsDevice->deviceContext->Draw(pyramid.vertexCount, 0); //Present everything graphicsDevice->swapChain->Present(0, 0); Not entirely sure what you mean here either. Do you mean that you have one const/tex buffer per set of APIs as in you have const buffer for the camera, a const buffer for setting colors on primitives, etc. Instead of having a singular const/tex buffer that could handle all of that? With the D3D11_MAP_WRITE_NO_OVERWRITE/D3D11_MAP_WRITE_DISCARD pattern or even in general is mesh/vertex data held in an intermediate place traditionally and then copied into the buffer? I've seen a lot of tutorials like this one Lesson 5: Drawing a Triangle where they just place the data into an array and copy it into the buffer. Wasn't sure if this is just because its a beginners tutorial and they are showing the basics or if there is a better way to do it
  2. C++ General C++ class questions

    I'm not super experienced, I could probably argue that I'm not experienced at all, when it comes to exceptions/error handling in game dev. So who does the operations that fail? I assume its some secondary method. Methods that do things like open file xyz, poll to see if a controller was disconnected, or etc, but how do they fail? Not in the sense that why did something go wrong, but in the sense that are they really throwing something like std::exception and its being caught. Maybe the code doesn't even attempt to catch it. Or its returning error codes that trigger an action/event (basically prevent a crash at all costs)
  3. When it comes to your structs like Layout1Stream0 and my VertexTypeA I think we are talking about the same thing here. EG: //The vertex types class VertexTypeA { Vector3 pos; //Holds 3 floats: x, y, z Color color; //Holds 4 floats: r, g, b, a }; class VertexTypeB { Vector3 pos; //Holds 3 floats: x, y, z }; class VertexTypeC { Color color; //Holds 4 floats: r, g, b, a }; //======== Create and use the first input layout D3D11_INPUT_ELEMENT_DESC layout1[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; d3dDevice->CreateInputLayout(layout1, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &inputLayout1); D3D11_BUFFER_DESC bufferDescription; ZeroMemory(&bufferDescription, sizeof(D3D11_BUFFER_DESC)); bufferDescription.Usage = D3D11_USAGE_DYNAMIC; bufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDescription.ByteWidth = sizeof(VertexTypeA) * 3; //Enough for a triangle graphicsDevice->device->CreateBuffer(&bufferDescription, NULL, &bufferVertexTypeA); //Use the buffer/layout for input layout 1 UINT stride = sizeof(Vertex); UINT offset = 0; graphicsDevice->deviceContext->IASetVertexBuffers(0, 1, &bufferVertexTypeA, &stride, &offset); //======== Create and use the second input layout D3D11_INPUT_ELEMENT_DESC layout2[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; d3dDevice->CreateInputLayout(layout2, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &inputLayout2); //Position buffer D3D11_BUFFER_DESC bufferDescription; ZeroMemory(&bufferDescription, sizeof(D3D11_BUFFER_DESC)); bufferDescription.Usage = D3D11_USAGE_DYNAMIC; bufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDescription.ByteWidth = sizeof(VertexTypeB) * 3; //Enough for a triangle graphicsDevice->device->CreateBuffer(&bufferDescription, NULL, &bufferVertexTypeB); //Color buffer D3D11_BUFFER_DESC bufferDescription; ZeroMemory(&bufferDescription, sizeof(D3D11_BUFFER_DESC)); bufferDescription.Usage = D3D11_USAGE_DYNAMIC; bufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDescription.ByteWidth = sizeof(VertexTypeC) * 3; //Enough for a triangle graphicsDevice->device->CreateBuffer(&bufferDescription, NULL, &bufferVertexTypeC); //Use the buffers/layout for input layout 2 UINT strides[2]; strides[0] = sizeof(VertexTypeB); strides[1] = sizeof(VertexTypeC); UINT offsets[2]; offsets[0] = 0; offsets[1] = 0; ID3D11Buffer* buffers[2]; buffers[0] = bufferVertexTypeB; buffers[1] = bufferVertexTypeC; deviceContext->IASetInputLayout(inputLayout2); deviceContext->IASetVertexBuffers(0, 2, buffers, strides, offsets); But yeah that makes sense that you could just have the one vertex shader to handle all of the different formats assuming they are more or less the same I feel like it might be better to just create a new layout, but could you omit a property from use? Eg: you setup a layout to have 3 properties (Position, Color, Normal), but one of the things you want to draw only uses the properties Position and Normal where potentially everything else uses all 3 properties
  4. Maybe I'm missing something here, but wouldn't a different input layout essentially mean a different vertex type? //Vertex Type 1 D3D11_INPUT_ELEMENT_DESC layout1[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, }; //Vertex Type 2 D3D11_INPUT_ELEMENT_DESC layout1[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORDS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, };
  5. So its not necessarily a renderer per thing (sprite, car, etc), but really a renderer per vertex type (VertexTypeA renderer, VertexTypeB renderer, or etc) and or task (lighting) Previously I also I had some psudeo code like: D3D11_MAPPED_SUBRESOURCE resource = Map(bigBuffer, D3D11_MAP_WRITE_NO_OVERWRITE) memcpy(resource.pData, (*renderable).data, (*renderable).dataSize); //(*renderable).data in this case is a vector or array of vertex data Unmap(); And the renderable's data was in a std::vector or array. But is it possible to have the vertex data already in a ID3D11Buffer and then copy this data directly into the big buffer owned by the renderer? Or would this be really bad because it could trigger a read from the GPU and that in turn would be super slow and cause stalling How should I be copying data over into the renderer's big buffer? Is it really just a simple memcpy from a std::vector (renderable's vertex data) into the ID3D11Buffer (renderers buffer) using map/unmap
  6. What is kind of tripping me up is how this big buffer exists. In the sense that I could have multiple vertex types like this class VertexTypeA { Vector3 pos; Color color; } class VertexTypeB { Vector3 pos; Color color; Vector2 texCoord } class VertexTypeC { //Some data to represent VertexTypeC } But surly these can't go all into the same buffer. I would need a VertexTypeA buffer, VertexTypeB buffer, VertexTypeC buffer, etc right? In the real world, are there renders that solely focus on one object to render? EG: You have a SpriteRenderer and its only job is to render sprites, then you have a CarRenderer and its only ever going to render cars, a CharacterRenderer that only renders characters so on and so forth. Or is there some single renderer entity that does everything (I really don't think this is the case)? Maybe a combo, where everything is fed into single renderer but then its really delegated out to the sub renderers (SpriteRenderer, CarRenderer, etc) to handle what you are actually wanting to draw?
  7. In the article you linked there is a part where they go over using D3D11_MAP_WRITE_NO_OVERWRITE and D3D11_MAP_WRITE_DISCARD when mapping. Where you use D3D11_MAP_WRITE_DISCARD starting off and then successive map/unmap calls should use D3D11_MAP_WRITE_NO_OVERWRITE until your buffer becomes full again and you switch back to the D3D11_MAP_WRITE_DISCARD flag. So now that has me wondering should I be placing everything in one large buffer? That way I don't have keep rebinding all these different buffers. Something like: //The big buffer was already bound at this point (the intialization point) for(std::vector<Renderable>::iterator renderable = renderables.begin(); renderable != renderables.end(); ++renderable) { if(bigBufferIsNotFull) { D3D11_MAPPED_SUBRESOURCE resource = Map(bigBuffer, D3D11_MAP_WRITE_NO_OVERWRITE) memcpy(resource.pData, (*renderable).data, (*renderable).dataSize); //(*renderable).data in this case is a vector or array of vertex data Unmap(); } else //Our big buffer is full { D3D11_MAPPED_SUBRESOURCE resource = Map(bigBuffer, D3D11_MAP_WRITE_DISCARD) memcpy(resource.pData, (*renderable).data, (*renderable).dataSize); //(*renderable).data in this case is a vector or array of vertex data Unmap(); } Draw((*renderable).vertexCount); } Opposed to: for(std::vector<Renderable>::iterator renderable = renderables.begin(); renderable != renderables.end(); ++renderable) { //(*renderable).buffer is a ID3D11Buffer and at some point (renderable creation) the buffer was mapped with the needed data BindVertexBuffer((*renderable).buffer); Draw((*renderable).vertexCount); }
  8. I got a quick question about buffers when it comes to DirectX 11. If I bind a buffer using a command like: IASetVertexBuffers IASetIndexBuffer VSSetConstantBuffers PSSetConstantBuffers and then later on I update that bound buffer's data using commands like Map/Unmap or any of the other update commands. Do I need to rebind the buffer again in order for my update to take effect? If I dont rebind is that really bad as in I get a performance hit? My thought process behind this is that if the buffer is already bound why do I need to rebind it? I'm using that same buffer it is just different data
  9. Intellisesne might be the wrong word here. Its whichever entity is responsible putting a red squiggly under things. The linter? I kind of lied here, completely forgot what I was trying to do. I actually do have a spot where I could use a vector of Buffers. I have this factory called BufferModule which has methods like createVertexBuffer, createIndexBuffer, etc. All of those buffer classes (VertexBuffer, IndexBuffer, etc) are derived from the base Buffer. So in order to hold all of these I need a member variable like std::vector<Buffer*> bufferObjects; Cause if I were to just a normal vector of objects, my child buffer class would be sliced when I would try to do something like this right? //Vector of the parent class Buffer std::vector<Buffer> bufferObjects; //Put the child class VertexBuffer into the vector VertexBuffer vertexBuffer; graphicsDevice->device->CreateBuffer(&bufferDescription, NULL, &vertexBuffer.buffer); buffers.push_back(vertexBuffer); // Not good... Just lost all of the info that was not specifically a part of the parent class So to fix this I would need to do //Vector of the parent class Buffer. Using a pointer to prevent the object slice std::vector<Buffer*> bufferObjects; VertexBuffer vertexBuffer = new VertexBuffer(); //Need to use new here otherwise when this goes out of scope all of our data goes bye-bye graphicsDevice->device->CreateBuffer(&bufferDescription, NULL, &vertexBuffer.buffer); buffers.push_back(&vertexBuffer); //Hurray! No object slicing happens Only problem I have here is that I really don't like the use of the new keyword
  10. I actually don't mean for the Buffer class to be concrete I just don't know how to make it not since there are no methods that need to be implemented for the child //Parent class class Buffer { public: Buffer(); Buffer(const Buffer& other); Buffer(Buffer&& other); virtual ~Buffer(); Buffer& operator=(const Buffer& other); Buffer& operator=(Buffer&& other); private: ID3D11Buffer *buffer; void release(); //Releases the buffer member variable and cleans it up }; //VertexBuffer child class, other versions would be ConstantBuffer or IndexBuffer class VertexBuffer : public Buffer { public: VertexBuffer(); VertexBuffer(const VertexBuffer& other); VertexBuffer(VertexBuffer&& other); ~VertexBuffer(); VertexBuffer& operator=(const VertexBuffer& other); VertexBuffer& operator=(VertexBuffer&& other); }; Do I need to set anything to the left hand side when I do this? or is it really just: //Vertex Buffer copy assignment opt VertexBuffer& VertexBuffer::operator=(const VertexBuffer& other) { if (&other != this) { Buffer::operator=(other); } return *this; } //Vertex Buffer move assignment opt VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other) { if (&other != this) { Buffer::operator=(other); } return *this; } I posted the implementation file contents before. Intellisense gets mad at me if use the virtual keyword there, but its in the header file Currently I really don't have a point where I need a container of Buffers. For now I will definitely know that I need a VertexBuffer here or I should use a IndexBuffer there. I guess I really just have a base class, because regardless of whether my instance object is a VertexBuffer, ConstantBuffer, or XXXXBuffer they are still all buffers at the core and all have that ID3D11Buffer *buffer member variable in common
  11. When it comes to the copy, move, and assignment operators of a child class how is it supposed to look? If this is the implementation of my parent class //Default constructor Buffer::Buffer() { buffer = nullptr; } //Copy constructor Buffer::Buffer(const Buffer& other) { buffer = other.buffer; buffer->AddRef(); } //Move constructor Buffer::Buffer(Buffer&& other) { buffer = other.buffer; buffer->AddRef(); other.release(); //Free the buffer resource of the other instance } //Destructor Buffer::~Buffer() { release(); } //Copy assignment Buffer& Buffer::operator=(const Buffer& other) { if (&other != this) { release(); //Free the buffer of this instance buffer = other.buffer; buffer->AddRef(); } return *this; } //Move assignment Buffer& Buffer::operator=(Buffer&& other) { if (&other != this) { release(); //Free the buffer of this instance buffer = other.buffer; buffer->AddRef(); other.release(); //Free the buffer of the other instance } return *this; } And this is the implementation of my child class. Is this correct? I'm really just wondering about the at the copy, move, and I'm not too sure about the assignment operators //Default constructor VertexBuffer::VertexBuffer() : Buffer() { } //Copt constructor VertexBuffer::VertexBuffer(const VertexBuffer& other) : Buffer(other) { } //Move constructor VertexBuffer::VertexBuffer(VertexBuffer&& other) : Buffer(other) { } //Destructor VertexBuffer::~VertexBuffer() { } //Not sure how to handle the copy/move operators. //Do I treat them as there own operators and copy the Buffer assignment operators code? VertexBuffer& VertexBuffer::operator=(const VertexBuffer& other) { } VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other) { }
  12. C++ General C++ class questions

    I feel like in some cases this unavoidable such as my example where I want to create a new mesh, but I can't load it's data from disk and well you can't have the mesh if you can't load in it's data. I guess I'm not really looking to catch and handle the exception. I'm really just looking to explode and say well we exploded cause of XYZ So I have a situation like the following: class GameWindow { public GameWindow(HINSTSNCE hInstance); ~GameWindow(); } In order to create a GameWindow you must pass in a HINSTANCE. As this variable is required to actually setup the application window. But the compiler gets mad about not having the default no arg constructor. Which makes sense cause I believe if you define a constructor then the compiler does not automatically try to add one for you. Now I thought I could just add one in and make it private (the default no arg constructor) and then this forces the use of the HINSTANCE version. But then you run into "cannot access" issue due to the constructor being private when you do something like: class Game { public: Game(HINSTANCE hInstance); ~Game(); private: GameWindow gameWindow; //compiler does not like this cause the default constructor is private } Which also makes sense I guess since GameWindow gameWindow is really just GameWindow gameWindow() right? It does not work like it would in Java or C# where GameWindow gameWindow is just a declaration and the value is null. In C++ it is literally an instance creation of the class So in this case I would absolutely require an init method right? I can't Because I can't just change the line to use the GameWindow(HINSTANCE hInstance) constructor because that hInstance value has to come from somewhere and be set (not null or a junk value), which I don't think would happen in time in this case
  13. C++ General C++ class questions

    For question 4, so I have several classes that have member variables that are COM-pointers. I also have some factory/manager type classes that create these objects and place them into a vector. That issue I was having is that when the local class (the one that is pushed back) goes out of scope the destructor is called, which it should be. But I also had code in there to clean up and free the COM-pointer. Unfortunately this caused the problem of resources being released when they should not I think I actually have the rule of 5 implace for one of my classes (VertexShader), but I don't believe I have done it correctly as the COM-pointers (shader and inputLayout) that are released on the local copy cause the copy placed into the vector to have a dangling pointers (address is still there, but the object being pointed to does not really exist) and ultimately causes an access violation eventually. This is my current implementation: //===== Header file for my VertexShader class class VertexShader { public: VertexShader(); ~VertexShader(); std::string id; VertexShader(std::string id); VertexShader(const VertexShader &other); VertexShader(VertexShader &&other); VertexShader &operator=(const VertexShader &other); //DirectX 11 COM-pointers that I want to free ID3D11VertexShader *shader; ID3D11InputLayout *inputLayout; void release(); }; //====== Implementation file for my VertexShader class //Normal constructor VertexShader::VertexShader() { shader = nullptr; inputLayout = nullptr; std::cout << "Vertex Shader constructor called!" << std::endl; } //Normal constructor with id arg VertexShader::VertexShader(std::string id) { this->id = id; std::cout << "Vertex Shader constructor called. Id: " << this->id.c_str() << std::endl; } //Destructor VertexShader::~VertexShader() { std::cout << "Vertex Shader destructor called..." << std::endl; release(); //Having release in the destructor currentl gives me in issue :( } //Copy constructor VertexShader::VertexShader(const VertexShader &other) { id = other.id + "-COPY-CON"; shader = other.shader; inputLayout = other.inputLayout; std::cout << "Vertex Shader copy constructor called. Id: " << id << " Other Id: " << other.id << std::endl; } //Move constructor VertexShader::VertexShader(VertexShader &&other) { id = other.id + "-MOVE_CON"; shader = other.shader; inputLayout = other.inputLayout; std::cout << "Vertex Shader move constructor called. Id: " << id << " Other Id: " << other.id << std::endl; } //Assignment operator VertexShader &VertexShader::operator=(const VertexShader &other) { if (&other != this) { id = other.id + "-ASSIGN-OPT"; shader = other.shader; inputLayout = other.inputLayout; } std::cout << "Vertex Shader assignment operator called . Id: " << id << " Other Id: " << other.id << std::endl; return *this; } //Release method that frees the COM-pointers void VertexShader::release() { if (inputLayout != nullptr) { inputLayout->Release(); inputLayout = nullptr; } if (shader != nullptr) { shader->Release(); shader = nullptr; } } //Factory / Manager class method used to place a VertexShader into a vector VertexShader *ShaderModule::createVertexShader(std::string id, LPCWSTR fileName, LPCSTR entryPoint, LPCSTR targetProfile, D3D11_INPUT_ELEMENT_DESC *inputElementsDescription, UINT inputElementDescriptionNumElements) { ID3DBlob *shaderCode = compileShaderFromFile(fileName, entryPoint, targetProfile); VertexShader vertexShader(id); graphicsDevice->device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader); graphicsDevice->device->CreateInputLayout(inputElementsDescription, inputElementDescriptionNumElements, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout); shaderCode->Release(); vertexShaders.push_back(vertexShader); return &vertexShaders.back(); } //vertexShader's destructor is called here, which in turn calls the release method. Freeing the COM-pointer object that later cause an access violation //Example usage of my factory/manager D3D11_INPUT_ELEMENT_DESC inputElementDescription[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; vShader = GraphicsModule::shaders.createVertexShader("VertexShader", L"default.shader", "VShader", "vs_4_0", inputElementDescription, 2);
  14. I just have some general questions when it comes to C++ mainly focused on classes 1. When it comes to instantiating a class is there something like a static initializer? Or would it be better to have a static variable(s) and a corresponding flag to say whether or not the variables are ready to use? 2. Is it "bad" to throw exceptions in the constructor or should I use a 2 step initialization process? Example: I have TruckModel class and when I create a new instance I load in the mesh that this truck model uses. I throw an exception if the file cannot be found because I need the truck mesh in order to continue. This all happens within the TruckModel constructor 3. I am currently throwing some exceptions when critical errors happen and I guess exceptions do not work in the way I would expect. I sort of expect the program to come to screeching hault and fully stop execution, but it continues on if you just say to "continue" in the debugger. Simplified example: #include <iostream> int main() { int count = 0; while (true) { //Exception is thrown and there is no attempt to catch it //Debugger says the exception occurs and complains, but I can carry on with my program like normal if I just ignore the complaints and keep stepping through until I come back to this point if (count == 10) throw std::exception("Something went wrong..."); std::cout << "Count:" << count << std::endl; ++count; } std::cout << "COMPLETE!" << std::endl; } 4. Is it possible to hide a destructor from being directly called? I have classes that have a Release method to clean up various pointer resources used by the class instance. The destructor of the class does not call my Release method as that would free the resources prematurely in some cases. Example: ResourceItem *ResourceCreator::createResourceItem(UINT size) { ResourceItem resourceItem; system->CreateResourceItem(size, &resourceItem.dataPointer); resourceItems.push_back(resourceItem); return &resourceItems.back(); } //resourceItem goes out of scope here. A call to the Release method in the destructor would free resources that should not be freed yet 5. When it comes to variables should I always be giving them a value in the constructor? I have noticed for pointer variables that they do not have NULL or nullptr as there value by default. So it has me wondering if all values are or could be garbage values at there start? Example: class MyClass { public: //Would expect this to be 0 by default. Not sure if I should set this to 0 in constructor because of potential default garbage value int count; //Would expect this to be NULL or nullptr, but have seen random garbage value as the default OtherObject *otherObj; }
  15. DX11 Models, model matrices, and rendering

    There is a whole lot I don't understand haha, but that is because my graphics experience / knowledge is very fragmented. Just need more practice Not sure if you are talking about interleaved vs non-interleave buffers? Or if you are talking about streaming out data back to the CPU from the GPU to do further processing? If you are talking about interleaved vs non-interleave buffers (I think this is what you mean or the option that make the most sense to me), why would I want to have non-interleave buffers? Just a general question about constant buffers/buffers, might be stupid, but in that tutorial they created an extra vertex buffer to hold position modifications, so then for certain situations should I just use/bind an extra (non-constant) buffer? For example the MVP matrix is not really constant and it can change every frame so would it be better suited in a buffer that does not use the D3D11_BIND_CONSTANT_BUFFER flag (even though you can set the usage to dynamic)? Where as something like a light's brightness, a value that wouldn't change, should go into a buffer that is created with the D3D11_BIND_CONSTANT_BUFFER flag? Or is that all nonsense? That there are some optimizations going on behind the scenes or that it is just better to have them split up (coming from the viewpoint that there are probably way less constant buffer binds then binds that involve other buffer types like vertex data which would need to be rebinded per mesh)