Do I need to set viewport and scissor for every framebuffer?

I have set up viewport and scissor as dynamic states for my pipelines and I am updating them if the window is resized (or first draw call):

void VulkanRenderer::setViewportAndScissor(VkCommandBuffer commandBuffer) {
  if (!sizeChanged)
    return;

  VkViewport viewport{};
  viewport.x = 0.0f;
  viewport.y = 0.0f;
  viewport.width = (float)swapchain.getExtent().width;
  viewport.height = (float)swapchain.getExtent().height;
  viewport.minDepth = 0.0f;
  viewport.maxDepth = 1.0f;

  VkRect2D scissor{};
  scissor.offset = {0, 0};
  scissor.extent = swapchain.getExtent();

  vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
  vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
  sizeChanged = true;
}

My renderer creates one command buffer per swapchain and when I acquire a swapchain image, I use the respective command buffer for all the draw related commands:


void VulkanRenderer::draw(Scene *scene) {
  uint32_t imageIdx = swapchain.acquireNextImage(imageAvailableSemaphore);

  vkWaitForFences(context.getDevice(), 1, &renderFence, true,
                  std::numeric_limits<uint32_t>::max());
  vkResetFences(context.getDevice(), 1, &renderFence);

  vkResetCommandBuffer(commandBuffers[imageIdx], 0);

  VkCommandBufferBeginInfo beginInfo{};
  beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  beginInfo.flags = 0;
  beginInfo.pInheritanceInfo = nullptr;

  if (vkBeginCommandBuffer(commandBuffers[imageIdx], &beginInfo) !=
      VK_SUCCESS) {
    throw VulkanError("Failed to begin recording command buffer for frame");
  }

  setViewportAndScissor(commandBuffers[imageIdx]);

  VkClearValue clearValue;
  clearValue.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
  VkClearValue depthClear;
  depthClear.depthStencil.depth = 1.0f;

  VkClearValue clearValues[] = {clearValue, depthClear};

  VkRenderPassBeginInfo renderPassInfo{};
  renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  renderPassInfo.renderPass = renderPass;
  renderPassInfo.framebuffer = framebuffers[imageIdx];
  renderPassInfo.renderArea.offset = {0, 0};
  renderPassInfo.renderArea.extent = swapchain.getExtent();
  renderPassInfo.clearValueCount = 2;
  renderPassInfo.pClearValues = clearValues;

  // rest of the call
}

Do I need to write the viewport and scissor for every outputted framebuffer?

Most state is not preserved across command buffers. So if you are relying on some state in a CB, then that state needs to be set by that CB.

You need to set it in each separate command buffer. And you need to set it after each new bound pipeline except when the previous one also had a dynamic viewport.

Anything else does not disturb the viewport dynamic state (e.g. vkCmdBeginRenderPass does not matter).