Changeset 20e4c2b in opengl-game for vulkan-game.cpp


Ignore:
Timestamp:
Mar 9, 2021, 2:59:40 AM (4 years ago)
Author:
Dmitry Portnoy <dportnoy@…>
Branches:
feature/imgui-sdl
Children:
187b0f5
Parents:
40eb092
Message:

In VulkanGame, use ImGui for the UI instead of using SDL to draw elements onto an overlay, and remove everything associated with the overlay pipeline

File:
1 edited

Legend:

Unmodified
Added
Removed
  • vulkan-game.cpp

    r40eb092 r20e4c2b  
    1212
    1313#include "utils.hpp"
    14 
    15 #include "gui/main-screen.hpp"
    16 #include "gui/game-screen.hpp"
    1714
    1815using namespace std;
     
    7168   laser_VP_mats = {};
    7269   explosion_UBO = {};
     70
     71   score = 0;
     72   fps = 0.0f;
    7373}
    7474
     
    7777
    7878void VulkanGame::run(int width, int height, unsigned char guiFlags) {
     79   // TODO: Maybe call the init code in the constructor instead of in run()
     80   // Research this
    7981   seedRandomNums();
    8082
     
    8385   cout << "Vulkan Game" << endl;
    8486
    85    this->score = 0;
    86 
     87   // TODO: Move IMGUI initialization in here
    8788   if (initUI(width, height, guiFlags) == RTWO_ERROR) {
    8889      return;
    8990   }
    9091
    91    // TODO: Maybe make a struct of properties to share with each screen instead of passing
    92    // in all of VulkanGame
    93    screens[SCREEN_MAIN] = new MainScreen(*renderer, *this);
    94    screens[SCREEN_GAME] = new GameScreen(*renderer, *this);
    95 
    96    currentScreen = screens[SCREEN_MAIN];
    97 
    9892   initVulkan();
    99    mainLoop();
     93
     94   ImGuiIO& io = ImGui::GetIO();
     95
     96   currentRenderScreenFn = &VulkanGame::renderMainScreen;
     97
     98   initGuiValueLists(valueLists);
     99
     100   valueLists["stats value list"].push_back(UIValue(UIVALUE_INT, "Score", &score));
     101   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "FPS", &fps));
     102   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "IMGUI FPS", &io.Framerate));
     103
     104   renderLoop();
    100105   cleanup();
    101106
    102107   close_log();
    103 }
    104 
    105 void VulkanGame::goToScreen(Screen* screen) {
    106    currentScreen = screen;
    107    currentScreen->init();
    108 
    109    // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
    110    // to make sure there'd be no issues
    111    recreateSwapChain();
    112 }
    113 
    114 void VulkanGame::quitGame() {
    115    done = true;
    116108}
    117109
     
    151143      cout << "UI library could not be initialized!" << endl;
    152144      cout << gui->getError() << endl;
     145      // TODO: Rename RTWO_ERROR to something else
    153146      return RTWO_ERROR;
    154147   }
     
    163156   cout << "Target window size: (" << width << ", " << height << ")" << endl;
    164157   cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
    165 
    166    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    167    if (renderer == nullptr) {
    168       cout << "Renderer could not be created!" << endl;
    169       cout << gui->getError() << endl;
    170       return RTWO_ERROR;
    171    }
    172 
    173    uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
    174       gui->getWindowWidth(), gui->getWindowHeight());
    175 
    176    if (uiOverlay == nullptr) {
    177       cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
    178       return RTWO_ERROR;
    179    }
    180    if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
    181       cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
    182       return RTWO_ERROR;
    183    }
    184 
    185    SDL_SetRenderTarget(renderer, uiOverlay);
    186 
    187    // TODO: Print the filename of the font in the error message
    188 
    189    lazyFont = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
    190    if (lazyFont == nullptr) {
    191       cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
    192       return RTWO_ERROR;
    193    }
    194 
    195    proggyFont = TTF_OpenFont("assets/fonts/ProggyClean.ttf", 16);
    196    if (proggyFont == nullptr) {
    197       cout << "Failed to load proggy font! SDL_ttf Error: " << TTF_GetError() << endl;
    198       return RTWO_ERROR;
    199    }
    200158
    201159   return RTWO_SUCCESS;
     
    320278
    321279   initGraphicsPipelines();
    322 
    323    overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
    324    overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
    325 
    326    overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
    327       VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
    328 
    329    addObject(overlayObjects, overlayPipeline,
    330       {
    331          {{-1.0f,  1.0f,  0.0f}, {0.0f, 1.0f}},
    332          {{ 1.0f,  1.0f,  0.0f}, {1.0f, 1.0f}},
    333          {{ 1.0f, -1.0f,  0.0f}, {1.0f, 0.0f}},
    334          {{-1.0f, -1.0f,  0.0f}, {0.0f, 0.0f}}
    335       }, {
    336          0, 1, 2, 2, 3, 0
    337       }, {}, false);
    338 
    339    overlayPipeline.createDescriptorSetLayout();
    340    overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
    341    overlayPipeline.createDescriptorPool(swapChainImages);
    342    overlayPipeline.createDescriptorSets(swapChainImages);
    343280
    344281   modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
     
    713650
    714651void VulkanGame::initGraphicsPipelines() {
    715    overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex, void*>(
    716       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
    717       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 4, 6, 0);
    718 
    719652   modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
    720653      VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
     
    772705}
    773706
    774 void VulkanGame::mainLoop() {
    775    this->startTime = high_resolution_clock::now();
    776    curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
    777 
    778    this->fpsStartTime = curTime;
    779    this->frameCount = 0;
     707void VulkanGame::renderLoop() {
     708   startTime = high_resolution_clock::now();
     709   curTime = duration<float, seconds::period>(high_resolution_clock::now() - startTime).count();
     710
     711   fpsStartTime = curTime;
     712   frameCount = 0;
    780713
    781714   lastSpawn_asteroid = curTime;
     
    921854         }
    922855
    923          currentScreen->handleEvent(e);
     856         // currentScreen->handleEvent(e);
    924857      }
    925858
     
    973906      }
    974907
    975       currentScreen->renderUI();
    976 
    977       // Copy the UI image to a vulkan texture
    978       // TODO: I'm pretty sure this severely slows down the pipeline since this functions waits for the copy to be
    979       // complete before continuing. See if I can find a more efficient method.
    980       VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, resourceCommandPool, uiOverlay, renderer,
    981          sdlOverlayImage, graphicsQueue);
    982 
    983908      updateScene();
    984909
     
    987912      ImGui::NewFrame();
    988913
    989       {
    990          ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
    991          ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
    992          ImGui::Begin("WndMenubar", NULL,
    993             ImGuiWindowFlags_NoTitleBar |
    994             ImGuiWindowFlags_NoResize |
    995             ImGuiWindowFlags_NoMove);
    996          ImGui::InvisibleButton("", ImVec2(155, 18));
    997          ImGui::SameLine();
    998          if (ImGui::Button("Main Menu")) {
    999             cout << "Clicked on the main button" << endl;
    1000             //events.push(Event::GO_TO_MAIN_MENU);
    1001          }
    1002          ImGui::End();
    1003       }
     914      (this->*currentRenderScreenFn)();
    1004915
    1005916      ImGui::Render();
    1006 
    1007       // There is already code in renderFrame to render screen-specific things
    1008       // The imgui code above should be moved to the renderUI function of the particular screen it's needed in
    1009       // and currentScreen->renderUI should be called in renderFrame.
    1010       // Since I am no longer drawing the UI to an sdl texture and then rendering that, I don't have to worry
    1011       // about calling populateVulkanImageFromSDLTexture at all.
    1012       // If I do ever want to do that again, I can still actually do it inside renderFrame
    1013917
    1014918      gui->refreshWindowSize();
     
    12271131   cleanupSwapChain();
    12281132
    1229    VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
    12301133   VulkanUtils::destroyVulkanImage(device, floorTextureImage);
    12311134   VulkanUtils::destroyVulkanImage(device, laserTextureImage);
     
    12341137
    12351138   modelPipeline.cleanupBuffers();
    1236    overlayPipeline.cleanupBuffers();
    12371139   shipPipeline.cleanupBuffers();
    12381140   asteroidPipeline.cleanupBuffers();
     
    12501152
    12511153   vkDestroyInstance(instance, nullptr);
    1252 
    1253    delete screens[SCREEN_MAIN];
    1254    delete screens[SCREEN_GAME];
    1255 
    1256    if (lazyFont != nullptr) {
    1257       TTF_CloseFont(lazyFont);
    1258       lazyFont = nullptr;
    1259    }
    1260 
    1261    if (proggyFont != nullptr) {
    1262       TTF_CloseFont(proggyFont);
    1263       proggyFont = nullptr;
    1264    }
    1265 
    1266    if (uiOverlay != nullptr) {
    1267       SDL_DestroyTexture(uiOverlay);
    1268       uiOverlay = nullptr;
    1269    }
    1270 
    1271    SDL_DestroyRenderer(renderer);
    1272    renderer = nullptr;
    12731154
    12741155   gui->destroyWindow();
     
    16681549   // TODO: Move all images/textures somewhere into the assets folder
    16691550
    1670    VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
    1671 
    1672    sdlOverlayImageDescriptor = {};
    1673    sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    1674    sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
    1675    sdlOverlayImageDescriptor.sampler = textureSampler;
    1676 
    16771551   VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/texture.jpg",
    16781552      floorTextureImage, graphicsQueue);
     
    18051679   vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
    18061680
    1807    currentScreen->createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1681   // TODO: Find a more elegant, per-screen solution for this
     1682   if (currentRenderScreenFn == &VulkanGame::renderGameScreen) {
     1683      modelPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1684      shipPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1685      asteroidPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1686      laserPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1687      explosionPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
     1688   }
    18081689
    18091690   ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
     
    22022083   modelPipeline.createDescriptorSets(swapChainImages);
    22032084
    2204    overlayPipeline.updateRenderPass(renderPass);
    2205    overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
    2206    overlayPipeline.createDescriptorPool(swapChainImages);
    2207    overlayPipeline.createDescriptorSets(swapChainImages);
    2208 
    22092085   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
    22102086      uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
     
    22582134   }
    22592135
    2260    overlayPipeline.cleanup();
    22612136   modelPipeline.cleanup();
    22622137   shipPipeline.cleanup();
     
    23042179   vkDestroySwapchainKHR(device, swapChain, nullptr);
    23052180}
     2181
     2182void VulkanGame::renderMainScreen() {
     2183   unsigned int windowWidth = 640;
     2184   unsigned int windowHeight = 480;
     2185
     2186   {
     2187      int padding = 4;
     2188      ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
     2189      ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
     2190      ImGui::Begin("WndMain", nullptr,
     2191         ImGuiWindowFlags_NoTitleBar |
     2192         ImGuiWindowFlags_NoResize |
     2193         ImGuiWindowFlags_NoMove);
     2194
     2195      ImGui::InvisibleButton("", ImVec2(10, 80));
     2196      ImGui::InvisibleButton("", ImVec2(285, 18));
     2197      ImGui::SameLine();
     2198      if (ImGui::Button("New Game")) {
     2199         goToScreen(&VulkanGame::renderGameScreen);
     2200      }
     2201
     2202      ImGui::InvisibleButton("", ImVec2(10, 15));
     2203      ImGui::InvisibleButton("", ImVec2(300, 18));
     2204      ImGui::SameLine();
     2205      if (ImGui::Button("Quit")) {
     2206         quitGame();
     2207      }
     2208
     2209      ImGui::End();
     2210   }
     2211}
     2212
     2213void VulkanGame::renderGameScreen() {
     2214   {
     2215      ImGui::SetNextWindowSize(ImVec2(130, 65), ImGuiCond_Once);
     2216      ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
     2217      ImGui::Begin("WndStats", nullptr,
     2218         ImGuiWindowFlags_NoTitleBar |
     2219         ImGuiWindowFlags_NoResize |
     2220         ImGuiWindowFlags_NoMove);
     2221
     2222      //ImGui::Text(ImGui::GetIO().Framerate);
     2223      renderGuiValueList(valueLists["stats value list"]);
     2224
     2225      ImGui::End();
     2226   }
     2227
     2228   {
     2229      ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
     2230      ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
     2231      ImGui::Begin("WndMenubar", nullptr,
     2232         ImGuiWindowFlags_NoTitleBar |
     2233         ImGuiWindowFlags_NoResize |
     2234         ImGuiWindowFlags_NoMove);
     2235      ImGui::InvisibleButton("", ImVec2(155, 18));
     2236      ImGui::SameLine();
     2237      if (ImGui::Button("Main Menu")) {
     2238         goToScreen(&VulkanGame::renderMainScreen);
     2239      }
     2240      ImGui::End();
     2241   }
     2242
     2243   {
     2244      ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
     2245      ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
     2246      ImGui::Begin("WndDebug", nullptr,
     2247         ImGuiWindowFlags_NoTitleBar |
     2248         ImGuiWindowFlags_NoResize |
     2249         ImGuiWindowFlags_NoMove);
     2250
     2251      renderGuiValueList(valueLists["debug value list"]);
     2252
     2253      ImGui::End();
     2254   }
     2255}
     2256
     2257void VulkanGame::initGuiValueLists(map<string, vector<UIValue>>& valueLists) {
     2258   valueLists["stats value list"] = vector<UIValue>();
     2259   valueLists["debug value list"] = vector<UIValue>();
     2260}
     2261
     2262void VulkanGame::renderGuiValueList(vector<UIValue>& values) {
     2263   float maxWidth = 0.0f;
     2264   float cursorStartPos = ImGui::GetCursorPosX();
     2265
     2266   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
     2267      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
     2268
     2269      if (maxWidth < textWidth)
     2270         maxWidth = textWidth;
     2271   }
     2272
     2273   stringstream ss;
     2274
     2275   // TODO: Possibly implement this based on gui/ui-value.hpp instead and use templates
     2276   // to keep track of the type. This should make it a bit easier to use and maintain
     2277   // Also, implement this in a way that's agnostic to the UI renderer.
     2278   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
     2279      ss.str("");
     2280      ss.clear();
     2281
     2282      switch (it->type) {
     2283      case UIVALUE_INT:
     2284         ss << it->label << ": " << *(unsigned int*)it->value;
     2285         break;
     2286      case UIVALUE_DOUBLE:
     2287         ss << it->label << ": " << *(double*)it->value;
     2288         break;
     2289      }
     2290
     2291      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
     2292
     2293      ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
     2294      //ImGui::Text("%s", ss.str().c_str());
     2295      ImGui::Text("%s: %.1f", it->label.c_str(), *(float*)it->value);
     2296   }
     2297}
     2298
     2299void VulkanGame::goToScreen(void (VulkanGame::* renderScreenFn)()) {
     2300   currentRenderScreenFn = renderScreenFn;
     2301
     2302   // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
     2303   // to make sure there'd be no issues
     2304   //recreateSwapChain();
     2305}
     2306
     2307void VulkanGame::quitGame() {
     2308   done = true;
     2309}
Note: See TracChangeset for help on using the changeset viewer.