How can I measure the frame time?

Hello.
I have the following render code:

void MainWindow::drawFrame()
{
	//Waiting for the fence to be ready(Waiting for the command buffer to finish executing for the frame)
	vkWaitForFences(logicalDevice, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);

	//Acquire image from swapchain
	uint32_t imageIndex;
	//UINT64_MAX disables timeout, timeout is infinite
	VkResult result = vkAcquireNextImageKHR(logicalDevice, swapchain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
	if (result == VK_ERROR_OUT_OF_DATE_KHR) {
		recreateSwapchain();
		return;
	}
	else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
		throw MakeErrorInfo("Failed to acquire swapchain image!");
	}

	// 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(logicalDevice, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
	}

	// Mark the image as now being in use by this frame
	imagesInFlight[imageIndex] = inFlightFences[currentFrame];

	glfwGetWindowPos(window, &windowXPos, &windowYPos);

	imguiUpdateCommandBuffers(imageIndex);

	updateUniformBuffer(imageIndex);

	//Submitting the command buffer
	//Each entry in the waitStages array corresponds to the semaphore with the same index in pWaitSemaphores.
	VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
	std::vector<VkPipelineStageFlags> waitStages = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
	VkSubmitInfo submitInfo{};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
	//The first three parameters specify which semaphores to wait on before execution begins
	//and in which stage(s) of the pipeline to wait.
	//We want to wait with writing colors to the image until it's available, so we're specifying the stage of the graphics pipeline that writes to the color attachment.
	submitInfo.waitSemaphoreCount = 1;
	submitInfo.pWaitSemaphores = waitSemaphores;
	submitInfo.pWaitDstStageMask = waitStages.data();
	std::vector<VkCommandBuffer> currentCommandBuffers = { commandBuffers[imageIndex], imguiCommandBuffers[imageIndex] };
	submitInfo.commandBufferCount = 2;
	submitInfo.pCommandBuffers = currentCommandBuffers.data();
	std::vector<VkSemaphore> signalSemaphores = { renderFinishedSemaphores[currentFrame], imguiRenderFinishedSemaphores[currentFrame] };
	submitInfo.signalSemaphoreCount = 2;
	submitInfo.pSignalSemaphores = signalSemaphores.data();

	//Restore the fence to the unsignaled state
	vkResetFences(logicalDevice, 1, &inFlightFences[currentFrame]);

	//Start executing the command buffer
	if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
		throw MakeErrorInfo("Failed to submit draw command buffer!");
	}

	VkPresentInfoKHR presentInfo{};
	presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
	presentInfo.waitSemaphoreCount = 2;
	presentInfo.pWaitSemaphores = signalSemaphores.data();

	VkSwapchainKHR swapchains[] = { swapchain };
	presentInfo.swapchainCount = 1;
	presentInfo.pSwapchains = swapchains;
	presentInfo.pImageIndices = &imageIndex;
	presentInfo.pResults = nullptr; // Optional

	result = vkQueuePresentKHR(presentQueue, &presentInfo);
	if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
		framebufferResized = false;
		recreateSwapchain();
		if (minimizedWindowNeedClose) {
			return;
		}
	}
	else if (result != VK_SUCCESS) {
		throw MakeErrorInfo("Failed to present swap chain image!");
	}

	currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}

Let’s say I want to measure the time elapsed from the moment vkAcquireNextImageKHR() is called, until the command buffer execution is completed(the moment when inFlightFences[currentFrame] is signaled).

I don’t want to use vkWaitForFences() after call vkQueueSubmit() because I want to do the next frame.

You cannot effectively measure the time around swapchain image acquisition, as the hardware around acquisition (the display engine) is not exposed to you as a queue. You can only measure the time around queue command execution.

If you’re trying to time how long a queue waits to acquire a swpachain image, the best way to do that is as follows. Query the queue time right before acquiring the swapchain (ie: at the end of the batch before acquiring it). After that, query the queue time at the start of the first batch that waits on the acquisition.

Note that if you do another queue operation afterwards, like presenting the image, then that will also be timed.

Okay, but what if I want to measure the render time, (the execution time of my command buffers in vkQueueSubmit()), how can I do this?

Use a timestamp query. Or more specifically, a pair of timestamp queries.

I’m going to do it.
But I need to make a timestamp query at the moment when the render is finished, but I don’t want to use vkWaitFences()(for wait while inFlightFences[currentFrame] signaled), for the reason described above.

Timestamps don’t work that way. Timestamps are queue commands; that’s why it starts with vkCmd. They execute as part of the queue. They do not care when you call them on the CPU, so vkWaitForFences is irrelevant to them. Timestamp queries measure GPU time, not CPU time.

A timestamp is recorded when all commands prior to the timestamp in the queue finish executing up to (at least) the given stage. Therefore, if you put a “bottom of the pipe” timestamp query after all of the rendering commands, the timestamp will be recorded at the time all rendering commands prior to the timestamp have completed.

1 Like

Oh, I expected that you are talking about other timestamps(using std:: chrono).
Thank you for the answer, I use the timestamp you are talking about.