blob: aee1391373316b007aa1879c1636581a9a8f1d14 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <endian.h>
#include <unistd.h>
#include <cassert>
#include <limits>
#include <memory>
#include <vector>
#include "src/graphics/examples/vkproto/common/command_buffers.h"
#include "src/graphics/examples/vkproto/common/debug_utils_messenger.h"
#include "src/graphics/examples/vkproto/common/graphics_pipeline.h"
#include "src/graphics/examples/vkproto/common/image_view.h"
#include "src/graphics/examples/vkproto/common/instance.h"
#include "src/graphics/examples/vkproto/common/physical_device.h"
#include "src/graphics/examples/vkproto/common/readback.h"
#include "src/graphics/examples/vkproto/common/render_pass.h"
#ifdef __Fuchsia__
#include "src/graphics/examples/vkproto/fuchsia/fuchsia_surface.h"
#else
#include "src/graphics/examples/vkproto/glfw/glfw_surface.h"
#endif
#include "src/graphics/examples/vkproto/common/swapchain.h"
#include "src/graphics/examples/vkproto/common/utils.h"
#include <vulkan/vulkan.hpp>
#if USE_GLFW
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#endif
static bool DrawFrame(const vkp::Device& vkp_device, const vkp::Swapchain& swap_chain,
const vkp::CommandBuffers& command_buffers,
const std::vector<vk::UniqueFence>& fences);
static bool DrawOffscreenFrame(const vkp::Device& vkp_device,
const vkp::CommandBuffers& command_buffers, const vk::Fence& fence);
#if USE_GLFW
void glfwErrorCallback(int error, const char* description) {
fprintf(stderr, "glfwErrorCallback: %d : %s\n", error, description);
}
static void glfwFramebufferResizeCallback(GLFWwindow* window, int width, int height) {
// TODO(rosasco): Add ability to recreate swapchain here. Define new struct with all
// required dependencies to rebuild the swapchain called SwapchainDeps.
// Call glfwSetWindowUserPointer(window, swapchain_deps) in main below, then:
// auto swapchain_deps =
// reinterpret_cast<SwapchainDeps*>(glfwGetWindowUserPointer(window));
fprintf(stderr, "ERROR: Window resize not implemented.\n");
exit(1);
}
#endif
int main(int argc, char* argv[]) {
const bool offscreen = (argc == 2 && !strcmp(argv[1], "-offscreen"));
printf("Is Offscreen: %s\n", offscreen ? "yes" : "no");
// INSTANCE
const bool kEnableValidation = true;
vkp::Instance vkp_instance(kEnableValidation);
RTN_IF_MSG(1, !vkp_instance.Init(), "Instance Initialization Failed.\n");
std::shared_ptr<vk::Instance> instance = vkp_instance.shared();
// DEBUG UTILS MESSENGER
vkp::DebugUtilsMessenger vkp_debug_messenger(instance);
RTN_IF_MSG(1, !vkp_debug_messenger.Init(), "Debug messenger initialization failed");
#if USE_GLFW
GLFWwindow* window = nullptr;
if (!offscreen) {
glfwInit();
glfwSetErrorCallback(glfwErrorCallback);
RTN_IF_MSG(1, !glfwVulkanSupported(), "glfwVulkanSupported has returned false.\n");
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
window = glfwCreateWindow(1024, 768, "VkProto", nullptr, nullptr);
RTN_IF_MSG(1, !window, "glfwCreateWindow failed.\n");
glfwSetFramebufferSizeCallback(window, glfwFramebufferResizeCallback);
}
#endif
// SURFACE
std::shared_ptr<vkp::Surface> vkp_surface;
if (!offscreen) {
#if USE_GLFW
vkp_surface = std::make_shared<vkp::GlfwSurface>(instance, window);
#else
vkp_surface = std::make_shared<vkp::FuchsiaSurface>(instance);
#endif
RTN_IF_MSG(1, !vkp_surface->Init(), "Surface initialization failed\n");
}
VkSurfaceKHR surface = vkp_surface ? vkp_surface->get() : nullptr;
// PHYSICAL DEVICE
vkp::PhysicalDevice vkp_physical_device(instance, surface);
RTN_IF_MSG(1, !vkp_physical_device.Init(), "Physical device initialization failed\n");
const vk::PhysicalDevice& physical_device = vkp_physical_device.get();
// LOGICAL DEVICE
auto vkp_device = vkp::Device(physical_device, surface);
RTN_IF_MSG(1, !vkp_device.Init(), "Logical device initialization failed\n");
std::shared_ptr<vk::Device> device = vkp_device.shared();
vk::Format image_format;
vk::Extent2D extent;
std::shared_ptr<vkp::Swapchain> vkp_swap_chain;
// The number of image views added in either the offscreen or onscreen logic
// blocks below controls the number of framebuffers, command buffers, fences
// and signaling semaphores created subsequently.
std::vector<vk::ImageView> image_views;
std::shared_ptr<vkp::ImageView> vkp_offscreen_image_view;
if (offscreen) {
// IMAGE VIEW
vkp_offscreen_image_view = std::make_shared<vkp::ImageView>(device, physical_device);
RTN_IF_MSG(1, !vkp_offscreen_image_view->Init(), "Image View initialization failed\n");
image_format = vkp_offscreen_image_view->format();
extent = vkp_offscreen_image_view->extent();
image_views.emplace_back(vkp_offscreen_image_view->get());
} else {
// SWAP CHAIN
vkp_swap_chain = std::make_shared<vkp::Swapchain>(physical_device, device, surface);
RTN_IF_MSG(1, !vkp_swap_chain->Init(), "Swap chain initialization failed\n");
image_format = vkp_swap_chain->image_format();
extent = vkp_swap_chain->extent();
const auto& swap_chain_image_views = vkp_swap_chain->image_views();
for (auto& view : swap_chain_image_views) {
image_views.emplace_back(*view);
}
}
// RENDER PASS
auto vkp_render_pass = std::make_shared<vkp::RenderPass>(device, image_format, offscreen);
RTN_IF_MSG(1, !vkp_render_pass->Init(), "Render pass initialization failed\n");
// GRAPHICS PIPELINE
auto vkp_pipeline = std::make_unique<vkp::GraphicsPipeline>(device, extent, vkp_render_pass);
RTN_IF_MSG(1, !vkp_pipeline->Init(), "Graphics pipeline initialization failed\n");
// FRAMEBUFFER
auto vkp_framebuffers =
std::make_unique<vkp::Framebuffers>(device, extent, vkp_render_pass->get(), image_views);
RTN_IF_MSG(1, !vkp_framebuffers->Init(), "Framebuffer Initialization Failed.\n");
// COMMAND POOL
auto vkp_command_pool =
std::make_shared<vkp::CommandPool>(device, vkp_device.queue_family_index());
RTN_IF_MSG(1, !vkp_command_pool->Init(), "Command Pool Initialization Failed.\n");
// COMMAND BUFFER
auto vkp_command_buffers = std::make_unique<vkp::CommandBuffers>(
vkp_device.shared(), vkp_command_pool, vkp_framebuffers->framebuffers(), vkp_pipeline->get(),
vkp_render_pass->get(), extent);
RTN_IF_MSG(1, !vkp_command_buffers->Init(), "Command buffer initialization.\n");
// Offscreen drawing submission fence.
const vk::FenceCreateInfo fence_info(vk::FenceCreateFlagBits::eSignaled);
auto [r_offscren_fence, offscreen_fence] = device->createFenceUnique(fence_info);
RTN_IF_VKH_ERR(1, r_offscren_fence, "Offscreen submission fence.\n");
// Onscreen drawing submission fences.
// There is a 1/1/1 mapping between swapchain image view / command buffer /
// fence.
std::vector<vk::UniqueFence> fences;
for (size_t i = 0; i < image_views.size(); i++) {
auto [r_fence, fence] = device->createFenceUnique(fence_info);
RTN_IF_VKH_ERR(1, r_fence, "Onscreen submission fence.\n");
fences.emplace_back(std::move(fence));
}
#if USE_GLFW
if (offscreen) {
DrawOffscreenFrame(vkp_device, *vkp_command_buffers, offscreen_fence.get());
} else {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
DrawFrame(vkp_device, *vkp_swap_chain, *vkp_command_buffers, fences);
}
}
#else
if (offscreen) {
DrawOffscreenFrame(vkp_device, *vkp_command_buffers, offscreen_fence.get());
} else {
DrawFrame(vkp_device, *vkp_swap_chain, *vkp_command_buffers, fences);
}
sleep(3);
#endif
RTN_IF_VKH_ERR(1, device->waitIdle(), "waitIdle\n");
if (offscreen) {
// READBACK
std::vector<uint32_t> output_pixels(1);
vkp::ReadPixels(physical_device, *device, *(vkp_offscreen_image_view->image()), extent,
vkp_command_pool->get(), vkp_device.queue(), vk::Extent2D{1, 1}, vk::Offset2D{},
&output_pixels);
uint32_t output_pixel = htole32(output_pixels[0]);
printf("Clear Color Read: %02x,%02x,%02x,%02x\n", (uint8_t)(output_pixel >> 0) & 0xFF,
(uint8_t)(output_pixel >> 8) & 0xFF, (uint8_t)(output_pixel >> 16) & 0xFF,
(uint8_t)(output_pixel >> 24) & 0xFF);
}
#if USE_GLFW
if (!offscreen) {
glfwDestroyWindow(window);
glfwTerminate();
}
#endif
return 0;
}
bool DrawFrame(const vkp::Device& vkp_device, const vkp::Swapchain& vkp_swap_chain,
const vkp::CommandBuffers& vkp_command_buffers,
const std::vector<vk::UniqueFence>& fences) {
// Compact variables for readability derived from |current_frame|.
const vk::Device& device = vkp_device.get();
auto [r_image_available_semaphore, image_available_semaphore] =
device.createSemaphore(vk::SemaphoreCreateInfo{});
RTN_IF_VKH_ERR(false, r_image_available_semaphore, "Image available semaphore.\n");
auto [r_render_finished_semaphore, render_finished_semaphore] =
device.createSemaphore(vk::SemaphoreCreateInfo{});
RTN_IF_VKH_ERR(false, r_render_finished_semaphore, "Render finished semaphore.\n");
// Obtain next swap chain image in which to draw.
// The timeout makes this a blocking call if no swapchain images, and
// therefore command buffers, are available so there is no need to wait for a
// submission fence before calling acquireNextImageKHR().
auto [r_acquire, swapchain_image_index] =
device.acquireNextImageKHR(vkp_swap_chain.get(), std::numeric_limits<uint64_t>::max(),
image_available_semaphore, nullptr);
RTN_IF_VKH_ERR(false, r_acquire, "Acquire swapchain image.\n");
// Define stage that |image_available_semaphore| is waiting on.
const vk::PipelineStageFlags image_available_wait_stage =
vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::CommandBuffer command_buffer =
vkp_command_buffers.command_buffers()[swapchain_image_index].get();
vk::SubmitInfo submit_info;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &image_available_semaphore;
submit_info.pWaitDstStageMask = &image_available_wait_stage;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &render_finished_semaphore;
// No guarantees that we're done with the acquired swap chain image and
// therefore the command buffer we're about to use so wait on the command
// buffer's fence.
const vk::Fence& fence = fences[swapchain_image_index].get();
RTN_IF_VKH_ERR(false,
device.waitForFences(1, &fence, VK_TRUE, std::numeric_limits<uint64_t>::max()),
"waitForFences\n");
RTN_IF_VKH_ERR(false, device.resetFences(1, &fence), "resetFences failed\n");
RTN_IF_VKH_ERR(false, vkp_device.queue().submit(1, &submit_info, fence),
"Failed to onscreen submit command buffer.\n");
vk::PresentInfoKHR present_info;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &render_finished_semaphore;
present_info.swapchainCount = 1;
present_info.setPSwapchains(&(vkp_swap_chain.get()));
present_info.pImageIndices = &swapchain_image_index;
RTN_IF_VKH_ERR(false, vkp_device.queue().presentKHR(&present_info), "presentKHR failed\n");
RTN_IF_VKH_ERR(false, vkp_device.queue().waitIdle(), "queue waitIdle failed\n");
device.destroySemaphore(render_finished_semaphore);
device.destroySemaphore(image_available_semaphore);
return true;
}
bool DrawOffscreenFrame(const vkp::Device& vkp_device,
const vkp::CommandBuffers& vkp_command_buffers, const vk::Fence& fence) {
vk::CommandBuffer command_buffer = vkp_command_buffers.command_buffers()[0].get();
vk::SubmitInfo submit_info;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
// Wait for any outstanding command buffers to be processed.
const vk::Device& device = vkp_device.get();
RTN_IF_VKH_ERR(false,
device.waitForFences(1, &fence, VK_TRUE, std::numeric_limits<uint64_t>::max()),
"waitForFences failed\n");
RTN_IF_VKH_ERR(false, device.resetFences(1, &fence), "resetFences failed\n");
RTN_IF_VKH_ERR(false, vkp_device.queue().submit(1, &submit_info, fence),
"Failed to offscreen submit command buffer.\n");
return true;
}