Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

260 Neutral

About Dexario

  • Rank
  1. Hi. I'm currently building a small Vulkan engine, I find one of the most challenging things to do is abstracting, simplifying the Vulkan interface, especially the very long structs (ie Vk---CreateInfo). You quickly end up with hundreds of lines of just filling structs. To simplify that, I thought of wrapping the most important structs in classes with default values and helper functions, as shown in the following example: class GraphicsPipeline { public: GraphicsPipeline(); void create(const VkDevice* device); void dealloc(const VkDevice* device); VkPipeline getPipeline(); void resetToDefault(); ... Some helper functions ... VkGraphicsPipelineCreateInfo pipelineCreateInfo; private: VkPipeline m_pipeline; ... }; The resetToDefault() function is mainly where the abstraction takes place: it fills the pipelineCreateInfo attribute with default values. Since the pipelineCreateInfo attribute is public, it allows the programmer to change anything he wants within the structure (in theory) and expand upon the default values.   This all works great in theory. Unfortunately, there are a few problems with this: -The VkGraphicsPipelineCreateInfo struct contains pointers to constant structs, therefore forbidding any modification.         This means it is impossible to do that: GraphicsPipeline pipeline; pipeline.pipelineCreateInfo.pRasterizationState->polygonMode = VK_POLYGON_MODE_LINE          This is impossible since VkGraphicsPipelineCreateInfo::pRasterizationState is a pointer to a const: const VkPipelineRasterizationStateCreateInfo* pRasterizationState; -I may be wrong about that but I feel these Vk---CreateInfo structs are made for what I would call a "temporary instanciation". What makes me say that is the fact the structs contain so much pointers; at first it might not seem like a big deal but I realized it was a problem when creating my wrapper class: if the programmer wants to access any of the pipelineCreateInfo's pointers, they have to have been dynamically allocated before they get accessed. This means that in resetToDefault(), I would need to instanciate EVERY pointer and then recursively instanciate every pointer of the ones I just allocated and it quickly gets very complicated, even impossible. This is without thinking about deallocating everything after (though you could use a std::unique_ptr<>). This makes it very complicated to create a VkGraphicsPipelineCreateInfo attribute which can be filled in at different times.   This is why I come to you guys: How do you go about abstracting Vulkan's verbosity ? or Any ideas of how to simplify Vulkan's interface ? or How to implement Vulkan in an Engine ?    
  2. Oh yeah sorry, I forgot about that, I updated the link: https://www.dropbox.com/s/y7tjwufxz4s1g1w/Source.zip?dl=0   You have to put the res folder where your executable is (the folder contains the GLSL shaders)   To use it, create a main(), open a window with whatever API you want (I personnaly use SFML) and type: #include "Sprite.h" int main() { /* Open a window */ ... /* Load a sprite */ Sprite mySprite; mySprite.load("spritePath.png"); ... /* in update func / main loop */ mySprite.draw() } Hope I didn't forget anything else
  3. Of course: https://www.dropbox.com/s/bua25vug3nbjddl/Source.zip?dl=0
  4. I sped up considerably the drawing by only recalculating the mvp matrix when needed and by calculating it in a more "manual" way, to avoid having to multiply tons of matrices together, since the order in which the matrices are multiplied is always the same: if (m_updateNeeded) { /* Building the matrix */ float angle = m_rotation * 3.141592654f / 180.f; float cosine = std::cos(angle); float sine = std::sin(angle); float x1 = m_scale.x * cosine; float y1 = m_scale.y * sine; float z1 = 0.f; float w1 = 0.f; float x2 = m_scale.x * -sine; float y2 = m_scale.y * cosine; float z2 = 0.f; float w2 = 0.f; float x3 = 0.f; float y3 = 0.f; float z3 = 1.f; float w3 = 0.f; float x4 = -m_rotationOrigin.x * x1 - m_rotationOrigin.y * -y1 + m_position.x + m_rotationOrigin.x * m_scale.x; float y4 = -m_rotationOrigin.x * -x2 - m_rotationOrigin.y * y2 + m_position.y + m_rotationOrigin.y * m_scale.y; float z4 = 0.0f; float w4 = 1.0f; m_mvp = glm::ortho(0.0f, (float)screenWidth, (float)screenHeight, 0.0f) * glm::mat4(x1, y1, z1, w1, x2, y2, z2, w2, x3, y3, z3, w3, x4, y4, z4, w4); m_updateNeeded = false; } I was inspired by SFML: https://github.com/SFML/SFML/blob/master/src/SFML/Graphics/Transformable.cpp#L186-L199   I'll work on the other optimizations suggested by you guys, thank you all very much.
  5. But with SFML, the position is always changing and it runs at 240 FPS  . I'll investigate.
  6. I changed my code a bit and successfully obtained a staggering 260 FPS    What I did is I changed the map[][] vector from being a vector of Sprite pointers to just Sprites. I explain: A sprite contains all the transformations, and a texture contains all the buffers for rendering. The sprites all point to the same texture. This means that all the sprites have their own mvp matrix, which never changes and is never recalculated since they are static and never change position. However, this method is less memory efficient.   I see a problem though: this is just a test code, but in reality, the tiles will be moving behind the player, since the latter always stays at the center of the screen. It's the tiles which move behind him, creating an illusion of movement, so the mvp matrices will always have to be recalculated if the player is moving .   Maybe what I could do is have the sprites' mvps unchanged and have one matrix for the camera which will be calculated each frame and multiplied to all of the other mvps (I guess it's called a view matrix). I'll try this out.    
  7. @Alex Mekhed: My Transformable class is similar to SFML's, the main difference being that I'm using GLM. However, when I don't calculate any transformations (ie position, rotation..., except for glm::ortho()), the framerate goes up to 100FPS. I therefore did like SFML and added a bool updateNeeded variable, to only update the mvp matrix when something has changed. This should increase the framerate but not in my case since the position is always changing (remember that there is 1 sprite which is always being repositioned), so the mvp is always being recalculated, so it ultimately doesn't change much.   @Hexmind: This is just an example, I reduced the code to show a simplistic version of the problem, to avoid posting tons of code. In addition, the map[][] fills the screen exactly, so there's no need to reduce it.
  8. Using GL_TRIANGLE_STRIP doesn't change anything. 
  9. I understand that keeping track of what texture is currently loaded to avoid redundancy would increase performance, however, if I change my draw() function to: void Texture::draw() { glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); }  The VAO & the texture are already bounded once at the startup and are never unbounded/rebounded after. With that, I'm running at .... 39 FPS  , which represents an increase of 3 FPS.
  10. I am indeed drawing all the tiles separately, however, I also did that with SFML and it ran much faster. I looked at the code for rendering sprites in the SFML lib, it seems (to me) they are doing the same. I've broken down and analysed SFML's draw function (https://github.com/SFML/SFML/blob/master/src/SFML/Graphics/RenderTarget.cpp):   applyTransform(states.transform); -> applying matrices, transformations | applyBlendMode(states.blendMode); -> for RGBA | applyTexture(states.texture); -> binding the texture |- leads to: glCheck(glBindTexture(GL_TEXTURE_2D, texture->m_texture)); | applyShader(states.shader); -> using the correct shader |       const char* data = reinterpret_cast<const char*>(vertices);                              |       glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0));                  |-> binding the vertex data       glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8));  |       glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12));          |     | glCheck(glDrawArrays(mode, 0, vertexCount)); -> drawing | applyShader(NULL); -> unbinding the shader | applyTexture(NULL); -> unbinding the texture   I have to admit I am a bit confused since I can't see any signs of buffers (vao, vbo...), I guess they're using the old OpenGL. The draw() method just takes in an array of vertices and a struct called RenderStates, which contains all the info like shaders, transformations... and follows the steps detailed above.
  11. Hi,   I'm currently working on a 2D tile based game. I have to display ~3000 32x32 tiles every frame. However, I'm running into performance issues: the framerate drops to 36 FPS. My code works the following way: I have one sprite instance per tile, I then store all the tiles for the map in a two dimensional array of Sprite pointers, which point to the tile they represent: std::vector<std::vector<Sprite*>>. To display the map, I iterate through all the pointers, set the right position and draw. Here's a minimalist piece of code which can represent what is globally going on: /* Creating the map */ Sprite tile; tile.load("res/sprites/tiles/tile_test32.png"); std::vector<std::vector<Sprite*>> map; for (int x = 0; x < 64; x++) { map.push_back(std::vector<Sprite*>()); for (int y = 0; y < 48; y++) map[x].push_back(&tile); } .... /* Rendering */ for (int x = 0; x < map.size(); x++) { for (int y = 0; y < map[x].size(); y++) { map[x][y]->setPosition(x * 32, y * 32); map[x][y]->draw(); } } I tested this code but with SFML and its sf::Sprite class, it ran at about 240 FPS, instead of a miserable 36 FPS with my sprite class    Here is how my sprites are rendered: void Sprite::draw() { /* If the sprite is out of the screen, we don't render it */ if (m_position.x + m_size.x * m_scale.x < 0 || m_position.y + m_size.y * m_scale.y < 0 || m_position.x > SCREEN_WIDTH || m_position.y > SCREEN_HEIGHT) return; if (m_texture == nullptr) return; m_texture->getShader()->use(); /* Creating and sending the transformation matrices */ glm::mat4 mvp = Transformable::getTransformationMatrix(SCREEN_WIDTH, SCREEN_HEIGHT); glUniformMatrix4fv(glGetUniformLocation(m_texture->getShader()->getProgram(), "mvp"), 1, GL_FALSE, glm::value_ptr(mvp)); /* Sending the general color */ glUniform4f(glGetUniformLocation(m_texture->getShader()->getProgram(), "generalColor"), m_color.r, m_color.g, m_color.b, m_color.a); m_texture->draw(); } void Texture::draw() { m_shader->use(); /* Setting the texture */ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_texture); glUniform1i(glGetUniformLocation(m_shader->getProgram(), "tex"), 0); glEnable(GL_BLEND); /* For RGBA -> transparency */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* Binding the right vao and drawing */ glBindVertexArray(m_vao); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); /* Unbind texture & VAO */     glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } The shaders (vertex & fragment) are very minimalist. The vertex data carries a vec2 for position, a vec2 for texCoords and a vec4 for color, per vertex. The Texture class contains the VAO, the VBO (=vertices), the EBO and the OpenGL texture. The Sprite class is basically just there for the transformations, all the rendering is done in the Texture class.   I saw on the internet that binding textures is very costly, I therefore tried to boil down the Texture::draw() function to glBindVertexArray(), glDrawElements() and glBindVertexArray(). It didn't change anything to the performance, even though no texture was applied.   Thanks.
  12. I figured it out. My understanding of OpenGL is a bit limited since I'm learning OpenGL but it's working now, thanks. I basically had to change all the coordinates from being between -1 and 1 to 0 and SCREEN_WIDTH, same for the Y axis. I didn't understand that I also had to do that for the quad (it used to be, as illustrated above, between -1 and 1) and also for the translations.   Thanks to you guys.
  13. Position of the quad:     Basically, the square is at the top left of the screen in the OpenGL coordinate system.   Is that right or was I supposed to center the quad (put its center at 0;0 in the OpenGL coordinate system) ?
  14. Using projection = glm::ortho(0.0f, (float)SCREEN_WIDTH, 0.0f, (float)SCREEN_HEIGHT); Doesn't display anything, though using projection = glm::ortho(-(float)SCREEN_WIDTH/2.0f, (float)SCREEN_WIDTH/2.0f, -(float)SCREEN_HEIGHT/2.0f, (float)SCREEN_HEIGHT/2.0f); Displays the image at the right position (or it seems to be) yet it is absolutely miniscule (about 2x2 pixels)
  15.   I'd like to use a projection matrix, it's just that it doesn't seem to work: /* Projection matrix */ projection = glm::ortho(0.0f, (float)SCREEN_WIDTH, (float)SCREEN_HEIGHT, 0.0f); mvp = projection * model; glUniformMatrix4fv(glGetUniformLocation(m_texture->getShader()->getProgram(), "mvp"), 1, GL_FALSE, glm::value_ptr(mvp)); I've seen on the internet that this should do the trick, however, it just displays a white pixel at the top left of the window for me.
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!