title: What is causing VK_ERROR_DEVICE_LOST when calling vkQueueSubmit?
body: <p>I'm working on a voxel engine in C++ using Vulkan. Most of the boilerplate code is heavily based on <a href="https://vulkan-tutorial.com/" rel="nofollow noreferrer">vulkan-tutorial.com</a>. I have a drawFrame function that looks like this...</p>
  <pre><code>void drawFrame(float dt) {
      vkWaitForFences(device, 1, &amp;inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
  
      uint32_t imageIndex;
      VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &amp;imageIndex);
  
      updateUniformBuffer(imageIndex, dt);
  
      if (result == VK_ERROR_OUT_OF_DATE_KHR) {
          recreateSwapChain();
          return;
      } else if (result != VK_SUCCESS &amp;&amp; result != VK_SUBOPTIMAL_KHR) {
          throw std::runtime_error(&quot;failed to acquire swap chain image!&quot;);
      }
  
      // Check if a previous frame is using this image (i.e.there is its fence to wait on)
      if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
          vkWaitForFences(device, 1, &amp;imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
      }
      // Mark the image as now being in use by this frame
      imagesInFlight[imageIndex] = inFlightFences[currentFrame];
  
      VkSubmitInfo submitInfo{};
      submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  
      VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
      VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
      submitInfo.waitSemaphoreCount = 1;
      submitInfo.pWaitSemaphores = waitSemaphores;
      submitInfo.pWaitDstStageMask = waitStages;
      submitInfo.commandBufferCount = 1;
      submitInfo.pCommandBuffers = &amp;commandBuffers[imageIndex];
  
      VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
      submitInfo.signalSemaphoreCount = 1;
      submitInfo.pSignalSemaphores = signalSemaphores;
  
      vkResetFences(device, 1, &amp;inFlightFences[currentFrame]);
  
      result = vkQueueSubmit(graphicsQueue, 1, &amp;submitInfo, inFlightFences[currentFrame]);
      if (result != VK_SUCCESS) {
          throw std::runtime_error(&quot;failed to submit draw command buffer!&quot;);
      }
  
      VkPresentInfoKHR presentInfo{};
      presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
      presentInfo.waitSemaphoreCount = 1;
      presentInfo.pWaitSemaphores = signalSemaphores;
  
      VkSwapchainKHR swapChains[] = { swapChain };
      presentInfo.swapchainCount = 1;
      presentInfo.pSwapchains = swapChains;
      presentInfo.pImageIndices = &amp;imageIndex;
      presentInfo.pResults = nullptr; // Optional
  
      result = vkQueuePresentKHR(presentQueue, &amp;presentInfo);
  
      if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
          framebufferResized = false;
          recreateSwapChain();
      } else if (result != VK_SUCCESS) {
          throw std::runtime_error(&quot;failed to present swap chain image!&quot;);
      }
  
      // Increment the frame. By using the modulo(%) operator, we ensure that the frame index loops around after every MAX_FRAMES_IN_FLIGHT enqueued frames.
      currentFrame = (currentFrame + 1) % config-&gt;maxFramesInFlight;
  }
  </code></pre>
  <p>I'm passing in vertices like this...</p>
  <pre><code>void createVertexAndIndexBuffer() {
      for (size_t x = 0; x &lt; 100; x++) {
          for (size_t y = 0; y &lt; 4; y++) {
              for (size_t z = 0; z &lt; 100; z++) {
                  // for each block in the world vector
                  auto blockId = world.getBlock(x, y, z);
                  if (blockId == BlockId::Air) {
                      continue;
                  }
                  Vec3 blockPosition = { x, y, z };
  
                  // get its data
                  auto verts = blockdb.blockDataFor(blockId).getVertices();
                  auto inds = blockdb.blockDataFor(blockId).getIndices();
  
                  // account for the block position and store the new verts for later
                  for (int i = 0; i &lt; verts.size(); i++) {
                      Vertex v(verts[i]);
                      v.pos += blockPosition;
                      vertices.push_back(v);
                  }
  
                  // store the indices for later accounting for the offset into the verts vector
                  for (int i = 0; i &lt; inds.size(); i++) {
                      int ind(inds[i] + vertices.size());
                      indices.push_back(ind);
                  }
              }
          }
      }
  
      // time to start creating the actual buffer 
      VkDeviceSize vertexBufferSize = sizeof(vertices[0]) * vertices.size();
  
      VkBuffer vertexStagingBuffer;
      VkDeviceMemory vertexStagingBufferMemory;
      
      createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexStagingBuffer, vertexStagingBufferMemory);
  
      void* vertexData;
      vkMapMemory(device, vertexStagingBufferMemory, 0, vertexBufferSize, 0, &amp;vertexData);
      memcpy(vertexData, vertices.data(), (size_t)vertexBufferSize); 
      vkUnmapMemory(device, vertexStagingBufferMemory);
  
      createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
  
      // use copyBuffer() to move the vertex data to the device local buffer
      copyBuffer(vertexStagingBuffer, vertexBuffer, vertexBufferSize);
  
      // After copying the data from the staging buffer to the device buffer, we should clean up the staging buffer since it is no longer needed.
      vkDestroyBuffer(device, vertexStagingBuffer, nullptr);
      vkFreeMemory(device, vertexStagingBufferMemory, nullptr);
  
  
      // and do the same for the index buffer
      VkDeviceSize indexBufferSize = sizeof(indices[0]) * indices.size();
  
      VkBuffer indexStagingBuffer;
      VkDeviceMemory indexStagingBufferMemory;
      createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, indexStagingBuffer, indexStagingBufferMemory);
  
      void* indexData;
      vkMapMemory(device, indexStagingBufferMemory, 0, indexBufferSize, 0, &amp;indexData);
      memcpy(indexData, indices.data(), (size_t)indexBufferSize);
      vkUnmapMemory(device, indexStagingBufferMemory);
  
      createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
  
      copyBuffer(indexStagingBuffer, indexBuffer, indexBufferSize);
  
      vkDestroyBuffer(device, indexStagingBuffer, nullptr);
      vkFreeMemory(device, indexStagingBufferMemory, nullptr);
  }
  </code></pre>
  <p>Everything works fine like that but I need be able to render by chunk instead of by block in order to implement chunk geometry optimizations. This is my chunk.h and chunk.cpp...</p>
  <pre><code>#pragma once
  #include &quot;Layer.h&quot;
  
  class Chunk {
  public:
      Chunk() = default;
      Chunk(World* _world, Vec2XZ pos);
      ~Chunk() {}
  
      BlockId getBlock(int x, int y, int z);
      bool setBlock(BlockId id, int x, int y, int z);
      bool isBlockOutOfBounds(int x, int y, int z);
      void generateVerticesAndIndices();
      void load();
  
      std::array&lt;Layer, CHUNK_HEIGHT&gt; layers;
      const Vec2XZ position;
      const World* world;
      bool isLoaded = false;
      std::vector&lt;Vertex&gt; vertices;
      std::vector&lt;uint32_t&gt; indices;
  private:
  };
  </code></pre>
  <pre><code>#pragma once
  #include &quot;Chunk.h&quot;
  
  Chunk::Chunk(World* _world, Vec2XZ pos) :
      position(pos),
      world(_world) {
  }
  
  BlockId Chunk::getBlock(int x, int y, int z) {
      if (isBlockOutOfBounds(x, y, z)) {
          return BlockId::Air;
      }
  
      return layers[y].getBlock(x, z);
  }
  
  bool Chunk::setBlock(BlockId id, int x, int y, int z) {
      if (!isBlockOutOfBounds(x, y, z)) {
          if (layers[y].setBlock(id, x, z)) {
              return true;
          }
      }
  
      return false;
  }
  
  bool Chunk::isBlockOutOfBounds(int x, int y, int z) {
      if (x &gt;= CHUNK_WIDTH)
          return true;
      if (z &gt;= CHUNK_WIDTH)
          return true;
  
      if (x &lt; 0)
          return true;
      if (y &lt; 0)
          return true;
      if (z &lt; 0)
          return true;
  
      if (y &gt;= CHUNK_HEIGHT) {
          return true;
      }
  
      return false;
  }
  
  void Chunk::generateVerticesAndIndices() {
      vertices.clear();
      indices.clear();
      for (int y = 0; y &lt; CHUNK_HEIGHT; y++) {
          for (int x = 0; x &lt; CHUNK_WIDTH; x++) {
              for (int z = 0; z &lt; CHUNK_WIDTH; z++) {
                  // for each block in this chunk
                  auto blockId = getBlock(x, y, z);
  
                  if (blockId == BlockId::Air) {
                      continue; // dont render air
                  }
                  
                  // infer the block position using its coordinates
                  Vec3 blockPosition = { x, y, z };
  
                  // get its data
                  auto verts = world-&gt;blockdb-&gt;blockDataFor(blockId).getVertices();
                  auto inds = world-&gt;blockdb-&gt;blockDataFor(blockId).getIndices();
  
                  // account for the block position and store the new verts
                  for (int i = 0; i &lt; verts.size(); i++) {
                      Vertex v(verts[i]);
                      v.pos += blockPosition;
                      vertices.push_back(v);
                  }
  
                  // store the indices for later accounting for the offset into the verts vector
                  for (int i = 0; i &lt; inds.size(); i++) {
                      int ind(inds[i] + vertices.size());
                      indices.push_back(ind);
                  }
              }
          }
      }
  }
  
  void Chunk::load() {
      if (isLoaded) {
          return;
      }
  
      // todo: actual terrain generation
      for (int y = 0; y &lt; 4; y++) {
          for (int x = 0; x &lt; CHUNK_WIDTH; x++) {
              for (int z = 0; z &lt; CHUNK_WIDTH; z++) {
                  setBlock(BlockId::Grass, x, y, z);
              }
          }
      }
  
      isLoaded = true;
  }
  
  
  </code></pre>
  <p>So I've basically migrated the top part of createVertexAndIndexBuffer() over to the chunk class. Then within createVertexAndIndexBuffer(), I iterate through the chunks around the player within render distance like this...</p>
  <pre><code> void createVertexAndIndexBuffer() {
      // set bounds of how far out to render based on what chunk the player is in
      Vec2XZ playerChunkCoords = { floor(player.position.x) / CHUNK_WIDTH, floor(player.position.z) / CHUNK_WIDTH };
  
      Vec2XZ lowChunkXZ = { playerChunkCoords.x - renderDistance, playerChunkCoords.z - renderDistance };
      Vec2XZ highChunkXZ = { playerChunkCoords.x + renderDistance, playerChunkCoords.z + renderDistance };
  
      // for each chunk around the player within render distance
      for (int x = lowChunkXZ.x; x &lt; highChunkXZ.x; x++) {
          for (int z = lowChunkXZ.z; z &lt; highChunkXZ.z; z++) {
              // get the chunk
              Chunk* chunk = &amp;world.getChunk(x, z);
  
              // load it if it isnt already
              if (!chunk-&gt;isLoaded) {
                  chunk-&gt;load();
              }
  
              // generate its geometry if it doesnt already exist
              if (chunk-&gt;vertices.size() == 0 || chunk-&gt;indices.size() == 0) {
                  chunk-&gt;generateVerticesAndIndices();
              }
  
              auto verts = chunk-&gt;vertices;
              auto inds = chunk-&gt;indices;
  
              // account for the chunk position and store the new verts for later
              for (int i = 0; i &lt; verts.size(); i++) {
                  Vertex v(verts[i]);
                  v.pos.x += x * CHUNK_WIDTH;
                  v.pos.z += z * CHUNK_WIDTH;
                  vertices.push_back(v);
              }
  
              // store the indices for later accounting for the offset into the verts vector
              for (int i = 0; i &lt; inds.size(); i++) {
                  int ind(inds[i] + vertices.size());
                  indices.push_back(ind);
              }
          }
      }
  
      // time to start creating the actual buffer 
      VkDeviceSize vertexBufferSize = sizeof(vertices[0]) * vertices.size();
  
      VkBuffer vertexStagingBuffer;
      VkDeviceMemory vertexStagingBufferMemory;
      
      createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexStagingBuffer, vertexStagingBufferMemory);
  
      void* vertexData;
      vkMapMemory(device, vertexStagingBufferMemory, 0, vertexBufferSize, 0, &amp;vertexData);
      memcpy(vertexData, vertices.data(), (size_t)vertexBufferSize); 
      vkUnmapMemory(device, vertexStagingBufferMemory);
  
      createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
  
      // use copyBuffer() to move the vertex data to the device local buffer
      copyBuffer(vertexStagingBuffer, vertexBuffer, vertexBufferSize);
  
      // After copying the data from the staging buffer to the device buffer, we should clean up the staging buffer since it is no longer needed.
      vkDestroyBuffer(device, vertexStagingBuffer, nullptr);
      vkFreeMemory(device, vertexStagingBufferMemory, nullptr);
  
  
      // and do the same for the index buffer
      VkDeviceSize indexBufferSize = sizeof(indices[0]) * indices.size();
  
      VkBuffer indexStagingBuffer;
      VkDeviceMemory indexStagingBufferMemory;
      createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, indexStagingBuffer, indexStagingBufferMemory);
  
      void* indexData;
      vkMapMemory(device, indexStagingBufferMemory, 0, indexBufferSize, 0, &amp;indexData);
      memcpy(indexData, indices.data(), (size_t)indexBufferSize);
      vkUnmapMemory(device, indexStagingBufferMemory);
  
      createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
  
      copyBuffer(indexStagingBuffer, indexBuffer, indexBufferSize);
  
      vkDestroyBuffer(device, indexStagingBuffer, nullptr);
      vkFreeMemory(device, indexStagingBufferMemory, nullptr);
  }
  </code></pre>
  <p>With this code, the engine starts up fine but the screen stays white and then after a few calls to vkQueueSubmit() within drawFrame(), vkQueueSubmit() returns VK_ERROR_DEVICE_LOST instead of VK_SUCCESS and then the app throws the corresponding runtime error, prints out the corresponding debug information &quot;failed to submit draw command buffer!&quot;, waits for me to press a key, and then finally terminates with EXIT_FAILURE.</p>
  <p>Why does pushing vertices from blocks directly work fine, but pushing them from chunks does not? I have checked the Vulkan specification and did a lot of googling but I just couldn't find much on what causes this error to be thrown. I want to know how to fix it and in turn, fix my engine.</p>