Jump to content

klepto2

Developers
  • Posts

    854
  • Joined

  • Last visited

Everything posted by klepto2

  1. This shows the current state: It is really impressive what you can do. There are tons of widgets available (the colored Texteditor is a 3rd party plugin which works nativly with the integration). Also Dear ImGui is meant to be used for tooling/prototyping and debugging, not for ingame usage. Therefore, there are other tools.
  2. Maybe Maybe a flag to disable the Culling step for particular models/meshes might do the trick, in Orthographic projection (especially GUIs) it might be useful to always assume the mesh is visible as long as the model itself is not hidden. I was able to optimize the rendering to be flicker free and still performant. The previous flickering was due to a misconception on my side earlier. This is how Imgui provides the render data: you notify ImGui that you will render a new frame you define the GUI last you ask ImGui for the Renderdata The RenderData consists of Displaylists (for each window, foreground or background operation) The Displaylists itself contains the VertexBuffer and the indexbuffer and a commandlist the commandlists specify which texture to use and which indices (by providing an Offset and an ElementCount) The Render data is ordered front to back representing the Z-Index. In my first implementations i assumed that I need a separate Model for each command in the commandlists, and i reused these models and reordered them every frame. The Problem: if in the previous frame a big window with lots of content was rendered focused, but the next frame a small and simple window was in front. The Model of the big window was resized to the smaller window and vice versa. In the current implementation i only use one model per displaylist (which contains the _ownername) and add meshes according to the commandbuffer. So know i can identify which model belongs to a specific window (displaylist) and I only reuse this model for that particular window, when a window is closed I just hide the model and reuse it once the window is back. If a window has fewer commands than before i clear it and rebuild als meshes needed. Otherwise i use the resize approach. Surprisingly, this eliminates all the flickering but still maintains a good performance. This is what i do when the cmds are lower than the previously created meshes, but this only works by removing all meshes from the model and adding them again afterwards. There currently is no way to remove a specific mesh from a model.
  3. I have found a very hackish workaround and this should not be used, unless you know exactly what you do: class ModifiableMesh : public Mesh { public: void ResizeVertexBuffer(shared_ptr<Mesh> base, int newSize) { static_cast<ModifiableMesh*>(base.get())->Mesh::m_vertices.resize(newSize); }; void ResizeIndexBuffer(shared_ptr<Mesh> base, int newSize) { static_cast<ModifiableMesh*>(base.get())->Mesh::m_indices.resize(newSize); }; int VertexSize(shared_ptr<Mesh> base) { return static_cast<ModifiableMesh*>(base.get())->Mesh::m_vertices.size(); }; int IndexSize(shared_ptr<Mesh> base) { return static_cast<ModifiableMesh*>(base.get())->Mesh::m_indices.size(); }; }; const shared_ptr<ModifiableMesh> ModMesh = std::make_shared<ModifiableMesh>(); with this you can now resize the Vectors even when they are protected: int v_size = ModMesh->VertexSize(mesh); int i_size = ModMesh->IndexSize(mesh); int v_diff = v_size - newVertices.size() - INDEX_FLOAT_RANGE; int i_diff = i_size - newIndices.size() - INDEX_FLOAT_RANGE; if (abs(v_diff) > INDEX_FLOAT_RANGE) { Print("Resizing (Vertex-Buffer): " + String(v_diff) + " | " + String(v_size) + "-" + String(newVertices.size())); ModMesh->ResizeVertexBuffer(mesh,newVertices.size() + INDEX_FLOAT_RANGE); } if (abs(i_diff) > INDEX_FLOAT_RANGE)//newIndices.size() > mesh->m_indices.size()) { Print("Resizing (Index-Buffer): " + String(i_diff) + " | " + String(i_size) + "-" + String(newIndices.size())); ModMesh->ResizeIndexBuffer(mesh,newIndices.size() + INDEX_FLOAT_RANGE); } mesh->Modify(newVertices); mesh->Modify(newIndices); Not really a good way, but it will do it as long as we have no ability to access the vectors directly.
  4. Maybe I should clarify this a bit. The Architecture of 3rd party libraries (including Dear ImGui, or most nvidia libs) normalliy provide a shared VertexStructure and then just an Indexbuffer for each "surface". To replicate this behaviour in UltraEngine, you currently have to create multiple meshs and recalculate the Vertexbuffer (I have done so for ImGui) or you need to duplicate the Vertexbuffer for each mesh. The above thing is a nice to have but not essential. What is most important for me is the abillity to resize the Vertices and Indices at will. While I understand that Ultraengine is layedout for high performance I believe that some parts should also be a bit flexible. (It doesn't cost to much if you just need to resize every now and then and just upload small bits to the gpu instead of a big buffer where just a small bit is used anyway).
  5. While the Mesh API is very fast and also flexible, I think it could be more flexible. Background: I am working on this: and I have encountered a small issue with the mesh API. While you're able to modify vertices and indices on the fly it is not possible to change and update the size of these Vectors. I found a post where Josh describes this by design and i agree that from a performance point this is useful. On the other side it limits the usage of the class. Lets take the "Dear ImGui' as a sample: As this is an intermediate Gui system the layout is not calculated once, but instead each frame, This means that a window with small content has a small vertex and index count, but when you modify (eg: expand trees or add text) the mesh is regenerated and the vertex size and index buffer changes in size. Currently there are 2 ways to solve this: Recreate meshes on the fly (delete the old mesh, and create a new one with the new size) Surprisingly fast, but due to sync with the graphics card it leads to high flickering Create the meshes with hight vertex and inddex counts (lets say 10000 / 30000) and use the Mesh::Modify methods Works without flickering, but is extremly slow, not because of the modifcation itself, but the extreme size and unneeded uploads to the gpu. I have tested something which i assumed will not work, but surprisingly this worked flawless with nearly no impact: I modfied mesh.h and changed the protected modifier to public This gave me access to the actual Vectors for vertices and indices With access to these Vectors i can now resize them at will. I give them just a small bigger size (around 300 items) than needed to not perform resizing every frame After that i just use the Mesh::Modify methods Results: with the recreation method i get around 150 to 180 fps, but flickering with the preserved Size method i get 20 to 50 fps (depending on debug or release mode) with the resizing method i get 180 to 240 fps So maybe the resizing should be added optionaly to the Modify methods or maybe another DynamicMesh class. Another thing which might be useful for some scenarios: Shared VertexBuffers per Model or MeshGroup: Most Vulkan or modern DirectX/OpenGL use one VertexBuffer for big models, and just separate Indexbuffers per material. In Ultraengine this could be used to further increase the GPU-Bandwidth: I could Imagine that you define a Main-Mesh and this mesh can be assigned to other Meshs as parent. Then these Child meshes only need to provide indices and the vertexbuffer is the same as the the parent mesh. Use Cases: Lod, Voxel terrains etc.
  6. Made a lot of progress today I have finished the basic ImGuiManager for UltraEngine (not yet commited) with some additional features: you can now register your own UltraEngine Textures to be used by ImGui ImGuiManager::RegisterTexture(texture) will return an ImTextureID which can be used then by eg: ImGui::Image(...) Multiple-Fonts are now supported + reload and recreation of the texture-atlas This is a small sample which i am using to test some features: #include "UltraEngine.h" #include "Components/Motion/Mover.hpp" #include "imgui-integration/ImGuiManager.h" using namespace UltraEngine; using namespace UltraEngine::ImGuiIntegration; using namespace std; #define MAX_FRAMES 100 struct StatCounter { float data[MAX_FRAMES]; float getMax() { return *max_element(data, data + MAX_FRAMES); } float getAvarge() { float average = 0.0f; int removeCounts = 0; for (int n = 0; n < IM_ARRAYSIZE(data); n++) { auto v = data[n]; if (v < 0.0) { v = 0.0; removeCounts++; } average += v; } average /= (float)(MAX_FRAMES - removeCounts); return average; } }; class ImDebugRenderer : public Object { shared_ptr<World> _world; StatCounter _fpsCounter; StatCounter _cullCounter; StatCounter _renderCounter; int _currentFrame = 0; bool _isOpen = true; public: ImDebugRenderer(shared_ptr<World> world) : _world(world) { _world->RecordStats(true); } void CollectStatistics() { _fpsCounter.data[_currentFrame] = _world->renderstats.framerate; _cullCounter.data[_currentFrame] = _world->renderstats.cullingtime; _renderCounter.data[_currentFrame] = _world->renderstats.rendertime; } void Render() { CollectStatistics(); if (_isOpen) { ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_Appearing); ImGui::SetNextWindowBgAlpha(0.5); //ImGui::SetNextWindowSize(ImVec2(350, 420), ImGuiCond_Appearing); ImGui::Begin("Debug", &_isOpen, ImGuiWindowFlags_AlwaysAutoResize); ImGui::PlotLines(("Framerate (" + String(_world->renderstats.framerate) + " fps )").c_str(), _fpsCounter.data, IM_ARRAYSIZE(_fpsCounter.data), _currentFrame, ("average : " + String(_fpsCounter.getAvarge())).c_str(), 0.0, _fpsCounter.getMax(), ImVec2(0, 80.0f)); ImGui::PlotLines(("Culltime (" + String(_world->renderstats.cullingtime) + " ms)").c_str(), _cullCounter.data, IM_ARRAYSIZE(_cullCounter.data), _currentFrame, ("average : " + String(_cullCounter.getAvarge())).c_str(), 0.0, _cullCounter.getMax(), ImVec2(0, 80.0f)); ImGui::PlotLines(("Rendertime (" + String(_world->renderstats.rendertime) + " ms)").c_str(), _renderCounter.data, IM_ARRAYSIZE(_renderCounter.data), _currentFrame, ("average : " + String(_renderCounter.getAvarge())).c_str(), 0.0, _renderCounter.getMax(), ImVec2(0, 80.0f)); ImGui::Text("Cameras : %d", _world->renderstats.cameras); ImGui::Text("Meshbatches : %d", _world->renderstats.meshbatches); ImGui::Text("Pipelines : %d", _world->renderstats.pipelines); ImGui::Text("Polygons : %d", _world->renderstats.polygons); ImGui::Text("Vertices : %d", _world->renderstats.vertices); ImGui::Text("Instances : %d", _world->renderstats.instances); ImGui::Text("Shadows : %d", _world->renderstats.shadows); ImGui::Text("Shadowpolygons: %d", _world->renderstats.shadowpolygons); ImGui::Text("VRam : %d", _world->renderstats.vram); ImGui::End(); } _currentFrame = (_currentFrame + 1) % MAX_FRAMES; } void Show() { _isOpen = true; }; void Toggle() { _isOpen = !_isOpen; }; }; int main(int argc, const char* argv[]) { auto plg = LoadPlugin("Plugins/FITextureLoader"); auto plg2 = LoadPlugin("Plugins/KTX2TextureLoader"); //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a world auto world = CreateWorld(); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a camera auto camera = CreateCamera(world); camera->SetPosition(0, 0, -1); camera->SetFov(70); camera->SetClearColor(0.125); camera->SetTessellation(4); //Create a light auto light = CreateBoxLight(world); light->SetRange(-10, 10); light->SetRotation(35, 35, 0); light->SetColor(4); //Display material auto model = CreateCubeSphere(world, 0.5, 8, MESH_QUADS); auto mtl = LoadMaterial("Materials/rocks_ground_02.json"); mtl->SetTessellation(true); mtl->SetDisplacement(0.075f); model->SetMaterial(mtl); //Entity component system auto component = model->AddComponent<Mover>(); component->rotationspeed.y = 45; // Init the Manager auto imGuiManager = CreateImGuiManager(window, world); auto debugRenderer = make_shared<ImDebugRenderer>(world); // A custom class to render debug information for the world auto sampleTexture = mtl->GetTexture(0); // Get the diffuse Texture auto imGuiTexture = imGuiManager->RegisterTexture(sampleTexture); // Register the texture for usage with ImGui //Define some settings to control the camera and material settings bool camera_wireFrame = false; float tesselation_level = 4.0; Vec2 mtl_displacement = mtl->GetDisplacement(); //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { //Begin the ui-definition imGuiManager->BeginFrame(); debugRenderer->Render(); //render the debug panel (only when open) //Start rendering of the Settings ImGui::Begin("Settings", NULL, ImGuiWindowFlags_AlwaysAutoResize); ImGui::Checkbox("Wireframe", &camera_wireFrame); ImGui::SliderFloat("Tesselation-Level", &tesselation_level,0.0,32); ImGui::SliderFloat2("Displacement", &mtl_displacement[0], -1.0, 1.0); if (ImGui::Button("Toggle Debug-Window")) { debugRenderer->Toggle(); } ImGui::End(); //Render a window containing the UltraEngine texture ImGui::Begin("Texture", NULL, ImGuiWindowFlags_AlwaysAutoResize); ImGui::Image(imGuiTexture,ImVec2(128,128)); ImGui::End(); //Stop UI-Recording and update/create the models for UltraEngine imGuiManager->Sync(); //Update the Settings modified by the UI camera->SetWireframe(camera_wireFrame); camera->SetTessellation(tesselation_level); mtl->SetDisplacement(mtl_displacement.x, mtl_displacement.y); world->Update(); world->Render(framebuffer); } return 0; }
  7. small update: clipping errors and z-order problems are fixed. Now working on the simplyfied (ultralike) access and setting up a github repo for this to give access to this gem to everyone.
  8. I already posted a small screenshot in the Gallery showing an early proof of concept for integrating ImGui into UltraEngine. https://www.ultraengine.com/community/gallery/image/2676-imgui-integration-in-ultraengine/ Now I have made some progress and finished a lot of the integration stuff. Ordered Rendering (not completely working, sometimes the order seems a bit off) Keyboard and MouseInput Shader and Material stuff General-Rendering is working What is still to do: Resolve some clipping errors Make the whole integration more Ultra-like not the c approach ImGui is normally using for initialisation Add multiple texture and font support (for images or framebuffer display) This small gif shows a small sample with the provided demo window and a custom debug window showing the renderstats: Here is a small code snippet showing the debug window code: #define MAX_FRAMES 100 struct StatCounter { float data[MAX_FRAMES]; float getMax() { return *max_element(data, data + MAX_FRAMES); } float getAvarge() { float average = 0.0f; int removeCounts = 0; for (int n = 0; n < IM_ARRAYSIZE(data); n++) { auto v = data[n]; if (v < 0.0) { v = 0.0; removeCounts++; } average += v; } average /= (float)(MAX_FRAMES - removeCounts); return average; } }; class ImDebugRenderer : public Object { shared_ptr<World> _world; StatCounter _fpsCounter; StatCounter _cullCounter; StatCounter _renderCounter; int _currentFrame = 0; bool _isOpen = true; public: ImDebugRenderer(shared_ptr<World> world) : _world(world) { _world->RecordStats(true); } void CollectStatistics() { _fpsCounter.data[_currentFrame] = _world->renderstats.framerate; _cullCounter.data[_currentFrame] = _world->renderstats.cullingtime; _renderCounter.data[_currentFrame] = _world->renderstats.rendertime; } void Render() { CollectStatistics(); if (_isOpen) { ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_Appearing); ImGui::SetNextWindowBgAlpha(0.5); //ImGui::SetNextWindowSize(ImVec2(350, 420), ImGuiCond_Appearing); ImGui::Begin("Debug", &_isOpen, ImGuiWindowFlags_AlwaysAutoResize); ImGui::PlotLines(("Framerate (" + String(_world->renderstats.framerate) + " fps )").c_str(), _fpsCounter.data, IM_ARRAYSIZE(_fpsCounter.data), _currentFrame, ("average : " + String(_fpsCounter.getAvarge())).c_str(), 0.0, _fpsCounter.getMax(), ImVec2(0, 80.0f)); ImGui::PlotLines(("Culltime (" + String(_world->renderstats.cullingtime) + " ms)").c_str(), _cullCounter.data, IM_ARRAYSIZE(_cullCounter.data), _currentFrame, ("average : " + String(_cullCounter.getAvarge())).c_str(), 0.0, _cullCounter.getMax(), ImVec2(0, 80.0f)); ImGui::PlotLines(("Rendertime (" + String(_world->renderstats.rendertime) + " ms)").c_str(), _renderCounter.data, IM_ARRAYSIZE(_renderCounter.data), _currentFrame, ("average : " + String(_renderCounter.getAvarge())).c_str(), 0.0, _renderCounter.getMax(), ImVec2(0, 80.0f)); ImGui::Text("Cameras : %d", _world->renderstats.cameras); ImGui::Text("Meshbatches : %d", _world->renderstats.meshbatches); ImGui::Text("Pipelines : %d", _world->renderstats.pipelines); ImGui::Text("Polygons : %d", _world->renderstats.polygons); ImGui::Text("Vertices : %d", _world->renderstats.vertices); ImGui::Text("Instances : %d", _world->renderstats.instances); ImGui::Text("Shadows : %d", _world->renderstats.shadows); ImGui::Text("Shadowpolygons: %d", _world->renderstats.shadowpolygons); ImGui::Text("VRam : %d", _world->renderstats.vram); ImGui::End(); } _currentFrame = (_currentFrame + 1) % MAX_FRAMES; } void Show() { _isOpen = true; }; }; The setup is more or less the same as for the default GUI, you need an orthographic camera and a separate renderlayer. But then you can simply call: // This is how i imagine a more ultra-way of rendering the ImGui auto ui_manager = ImGuiUltraManager::Create(window, world, renderlayer); //Framebuffer will work as well void main() { ui_manager->BeginFrame(); // Begins the frame-recording debugWindow->Render(); // Add the commands to draw the debug window (code from above) ui_manager->Render(); // Stops the frame-recording and prepares the models for rendering world->Update(); world->Render(framebuffer,true); }
  9. While the integrated GUI-System is powerful and great, I decided to give ImGui a go. ImGui is an Intermediate GUI, which means you describe the layout in every frame and get what needs to be rendered, This is useful if you want to add some simple debug values to screen. If you look onto the webpage of ImGui you can see what can be done with it. This is a screenshot showing an early stage of the prototype integration, to see if it is even possible.
  10. For the reprojection i noticed, that i don't need it, but it will come in handy to render masks etc. for godrays eg. the same time you render the more complicated stuff.
  11. klepto2

    small_optimization

    I got a first version with reprojection working. it is not fine tuned, but in general the algo is very basic. This gif shows the debugging of the reprojection. Red shows the pixels which went offscreen and needs to be rerendered as well. In the non red area only every 16th pixel is rendered per frame, the others are reprojected from the previous frame.
  12. I have the need to write to 2 colorattachments and i have set it up like this: "textures": [ { "size": [0.5, 0.5], "format": 97 }, { "size": [0.5, 0.5], "format": 97 } ], "subpasses": [ { "samplers": ["DEPTH", "NORMAL", "PREVPASS"], "colorAttachments": [0,1], "shader": { "float32": { "fragment": "Shaders/Environment/environment_clouds.frag.spv" } } }, in the shader file i have something like this (very simplfied): layout(location = 0) out vec4 outColor[2]; void main() { outColor[0] = vec4(1.0,0.0,0.0,1.0); outColor[1] = vec4(0.0,1.0,0.0,1.0); } only the red value is stored to the texture, the second one is still unmodified. Ate multiple attachemnts currently not supported?
  13. klepto2

    small_optimization

    ok, doubled the raysteps and reduced the raycasts per frame to 1/16 as a first step to reprojection: and this is the result (with the old raystep range (32-64) i even get around 700-800fps):
  14. klepto2

    small_optimization

    @Josh, that would be interessting. I have seen some parameters for the screen velocity, but is there already a posteffect texture i can use for that, like albeado or depth?
  15. klepto2

    small_optimization

    in theory yes, I still need to figure out the bet weathermap format to support all of them. some systems use just the rgb and some 4 channels with the alpha map.
  16. klepto2

    small_optimization

    Not directly, the clouds system is based on a weather texture, where red is used for the overall coverage of clouds, green is used for the cloud type and blue is used for the cloud wetness. So if you modify one of the channels it will reflect on the clud rendering. In this case i just pasted "UltraEngine" Logo directly in the red channel. Also you may notice, that currently a plain texture is used which leads to repition, i later will also support cubemaps which can then be used to create individual weather for whole planets.
  17. klepto2

    small_optimization

    and just to show something how the cloudshape can be modified by an artist or so:
  18. klepto2

    small_optimization

    Implemented the downscaling: Maybe some additional AA needs to be added, but all in all it is a big performance increase.
  19. or (TextureFormat)VK_FORMAT_R16_UINT if you want to save the shorts. the previous solution will store normlaized floats.
  20. Try it with this: auto map = CreatePixmap(size, size, (TextureFormat)VK_FORMAT_R16_UNORM, data);
  21. klepto2

    small_optimization

    With some finetuning of the parameter i could already double the fps and lower the rednering time. Next step: render to 0.5 scale and upscale the result. (this should make a huge difference, as pixel based operations scale with the resolution) after that i will investigate temporal reprojection (another way to optimize the rendering time).
  22. klepto2

    sneak_peak_clouds

    Yes, this is first basic implementation without nearly no optimizations. E.g the clouds are raymarched at full resolution. Also it uses 128 to 256 steps per ray multiplied by 6 lightcone raymarches per step. What disturbed me more is the high culling time and the useless shadow render and high instance count. But this is something I will investigate further and file a bug report on that later.
  23. klepto2

    sneak_peak_clouds

    a small sneak-peak at the current status of cloud rendering :)
  24. Also Terrain::GetElevation doesn't take the Terrain-Position into account. Which is understandable as the terrain matrix is fully modifiable (scale, position and rotation), but it should be mentioned in the Docs.
×
×
  • Create New...