blob: ca04bf135e5997f64e1875ccaea1103d57b50b14 [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 <unistd.h>
#include <memory>
#include <vector>
#include "utils.h"
#include "vulkan_command_buffers.h"
#include "vulkan_command_pool.h"
#include "vulkan_framebuffer.h"
#include "vulkan_graphics_pipeline.h"
#include "vulkan_image_view.h"
#include "vulkan_instance.h"
#include "vulkan_layer.h"
#include "vulkan_logical_device.h"
#include "vulkan_physical_device.h"
#include "vulkan_render_pass.h"
#include "vulkan_surface.h"
#include "vulkan_swapchain.h"
#include "vulkan_sync.h"
#include <vulkan/vulkan.hpp>
#if USE_GLFW
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#endif
static bool DrawFrame(const VulkanLogicalDevice& logical_device, const VulkanSync& sync,
const VulkanSwapchain& swap_chain,
const VulkanCommandBuffers& command_buffers);
static bool DrawOffscreenFrame(const VulkanLogicalDevice& logical_device, const VulkanSync& sync,
const VulkanCommandBuffers& command_buffers);
static bool Readback(const VulkanLogicalDevice& logical_device, const VulkanImageView& image_view);
void glfwErrorCallback(int error, const char* description) {
fprintf(stderr, "glfwErrorCallback: %d : %s\n", error, description);
}
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;
auto instance = std::make_shared<VulkanInstance>();
#if USE_GLFW
glfwInit();
glfwSetErrorCallback(glfwErrorCallback);
if (!glfwVulkanSupported()) {
RTN_MSG(1, "glfwVulkanSupported has returned false.\n");
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(1024, 768, "VkPrimer", nullptr, nullptr);
if (!window) {
RTN_MSG(1, "glfwCreateWindow failed.\n");
}
if (!instance->Init(kEnableValidation, window)) {
RTN_MSG(1, "Instance Initialization Failed.\n");
}
#else
if (!instance->Init(kEnableValidation)) {
RTN_MSG(1, "Instance Initialization Failed.\n");
}
#endif
// LAYERS
VulkanLayer vulkan_layer(instance);
if (!vulkan_layer.Init()) {
RTN_MSG(1, "Layer Initialization Failed.\n");
}
// SURFACE
#if USE_GLFW
auto surface = std::make_shared<VulkanSurface>(instance, window);
#else
auto surface = std::make_shared<VulkanSurface>(instance);
#endif
if (!surface->Init()) {
RTN_MSG(1, "Surface Initialization Failed.\n");
}
// PHYSICAL DEVICE
auto physical_device = std::make_shared<VulkanPhysicalDevice>(instance, surface->surface());
if (!physical_device->Init()) {
RTN_MSG(1, "Phys Device Initialization Failed.\n");
}
// LOGICAL DEVICE
auto logical_device = std::make_shared<VulkanLogicalDevice>(
physical_device->phys_device(), surface->surface(), kEnableValidation);
if (!logical_device->Init()) {
RTN_MSG(1, "Logical Device Initialization Failed.\n");
}
vk::Format image_format;
vk::Extent2D extent;
std::shared_ptr<VulkanSwapchain> 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 signalling
// semaphores created subsequently.
std::vector<vk::ImageView> image_views;
std::shared_ptr<VulkanImageView> offscreen_image_view;
if (offscreen) {
// IMAGE VIEW
offscreen_image_view = std::make_shared<VulkanImageView>(logical_device, physical_device);
if (!offscreen_image_view->Init()) {
RTN_MSG(1, "Image View Initialization Failed.\n");
}
image_format = offscreen_image_view->format();
extent = offscreen_image_view->extent();
image_views.emplace_back(*(offscreen_image_view->view()));
} else {
// SWAP CHAIN
swap_chain =
std::make_shared<VulkanSwapchain>(physical_device->phys_device(), logical_device, surface);
if (!swap_chain->Init()) {
RTN_MSG(1, "Swap Chain Initialization Failed.\n");
}
image_format = swap_chain->image_format();
extent = swap_chain->extent();
const auto& swap_chain_image_views = swap_chain->image_views();
for (auto& view : swap_chain_image_views) {
image_views.emplace_back(*view);
}
}
// RENDER PASS
auto render_pass = std::make_shared<VulkanRenderPass>(logical_device, image_format, offscreen);
if (!render_pass->Init()) {
RTN_MSG(1, "Render Pass Initialization Failed.\n");
}
// GRAPHICS PIPELINE
auto graphics_pipeline =
std::make_unique<VulkanGraphicsPipeline>(logical_device, extent, render_pass);
if (!graphics_pipeline->Init()) {
RTN_MSG(1, "Graphics Pipeline Initialization Failed.\n");
}
// FRAMEBUFFER
auto framebuffer = std::make_unique<VulkanFramebuffer>(logical_device, extent,
*render_pass->render_pass(), image_views);
if (!framebuffer->Init()) {
RTN_MSG(1, "Framebuffer Initialization Failed.\n");
}
// COMMAND POOL
auto command_pool = std::make_shared<VulkanCommandPool>(
logical_device, physical_device->phys_device(), surface->surface());
if (!command_pool->Init()) {
RTN_MSG(1, "Command Pool Initialization Failed.\n");
}
// COMMAND BUFFER
auto command_buffers = std::make_unique<VulkanCommandBuffers>(
logical_device, command_pool, *framebuffer, extent, *render_pass->render_pass(),
*graphics_pipeline->graphics_pipeline());
if (!command_buffers->Init()) {
RTN_MSG(1, "Command Buffer Initialization Failed.\n");
}
// SYNC
auto sync = std::make_unique<VulkanSync>(logical_device, 3 /* max_frames_in_flight */);
if (!sync->Init()) {
RTN_MSG(1, "Sync Initialization Failed.\n");
}
#if USE_GLFW
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
if (offscreen) {
DrawOffscreenFrame(*logical_device, *sync, *command_buffers);
} else {
DrawFrame(*logical_device, *sync, *swap_chain, *command_buffers);
}
}
#else
if (offscreen) {
DrawOffscreenFrame(*logical_device, *sync, *command_buffers);
} else {
DrawFrame(*logical_device, *sync, *swap_chain, *command_buffers);
}
sleep(3);
#endif
logical_device->device()->waitIdle();
if (offscreen) {
Readback(*logical_device, *offscreen_image_view);
}
#if USE_GLFW
glfwDestroyWindow(window);
glfwTerminate();
#endif
return 0;
}
bool DrawFrame(const VulkanLogicalDevice& logical_device, const VulkanSync& sync,
const VulkanSwapchain& swap_chain, const VulkanCommandBuffers& command_buffers) {
static int current_frame = 0;
// Compact variables for readability derived from |current_frame|.
const vk::UniqueDevice& device = logical_device.device();
const vk::Fence& fence = *(sync.in_flight_fences()[current_frame]);
const vk::Semaphore& image_available_semaphore =
*(sync.image_available_semaphores()[current_frame]);
const vk::Semaphore& render_finished_semaphore =
*(sync.render_finished_semaphores()[current_frame]);
// Wait for any outstanding command buffers to be processed.
device->waitForFences({fence}, VK_TRUE, std::numeric_limits<uint64_t>::max());
device->resetFences({fence});
// Obtain next swap chain image in which to draw.
auto [result, image_index] =
device->acquireNextImageKHR(*swap_chain.swap_chain(), std::numeric_limits<uint64_t>::max(),
image_available_semaphore, nullptr);
if (vk::Result::eSuccess != result) {
RTN_MSG(false, "VK Error: 0x%x - Failed to acquire swap chain image.", result);
}
// Define stage that |image_available_semaphore| is waiting on.
const vk::PipelineStageFlags image_available_wait_stage =
vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::CommandBuffer command_buffer = *(command_buffers.command_buffers()[image_index]);
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;
if (logical_device.queue().submit(1, &submit_info, fence) != vk::Result::eSuccess) {
RTN_MSG(false, "Failed to submit draw command buffer.\n");
}
vk::PresentInfoKHR present_info;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &render_finished_semaphore;
present_info.swapchainCount = 1;
present_info.setPSwapchains(&(*swap_chain.swap_chain()));
present_info.pImageIndices = &image_index;
logical_device.queue().presentKHR(&present_info);
current_frame = (current_frame + 1) % sync.max_frames_in_flight();
return true;
}
bool DrawOffscreenFrame(const VulkanLogicalDevice& logical_device, const VulkanSync& sync,
const VulkanCommandBuffers& command_buffers) {
static int current_frame = 0;
const vk::UniqueDevice& device = logical_device.device();
const vk::Fence& fence = *(sync.in_flight_fences()[0]);
// Wait for any outstanding command buffers to be processed.
device->waitForFences({fence}, VK_TRUE, std::numeric_limits<uint64_t>::max());
device->resetFences({fence});
vk::CommandBuffer command_buffer = *(command_buffers.command_buffers()[0]);
vk::SubmitInfo submit_info;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
if (logical_device.queue().submit(1, &submit_info, fence) != vk::Result::eSuccess) {
RTN_MSG(false, "Failed to submit draw command buffer.\n");
}
current_frame = (current_frame + 1) % sync.max_frames_in_flight();
return true;
}
bool Readback(const VulkanLogicalDevice& logical_device, const VulkanImageView& image_view) {
const vk::UniqueDevice& device = logical_device.device();
vk::DeviceMemory device_memory = *(image_view.image_memory());
auto rv = device->mapMemory(device_memory, 0 /* offset */, VK_WHOLE_SIZE,
static_cast<vk::MemoryMapFlags>(0));
if (vk::Result::eSuccess != rv.result) {
RTN_MSG(false, "VK Error: 0x%x - Failed to map device memory for image.", rv.result);
}
uint8_t* image_buffer = static_cast<uint8_t*>(rv.value);
printf("Clear Color Read Back: (%02x,%02x,%02x,%02x)\n", *(image_buffer + 0), *(image_buffer + 1),
*(image_buffer + 2), *(image_buffer + 3));
device->unmapMemory(device_memory);
return true;
}