2017-10-26 01:17:10 +02:00
|
|
|
#include <cstdint>
|
2017-08-18 15:08:40 +02:00
|
|
|
#include "base/logging.h"
|
|
|
|
|
2017-08-22 16:28:35 +02:00
|
|
|
#include "Common/Vulkan/VulkanContext.h"
|
|
|
|
#include "thin3d/VulkanRenderManager.h"
|
2017-08-22 17:18:54 +02:00
|
|
|
#include "thread/threadutil.h"
|
|
|
|
|
2017-10-28 18:41:54 +02:00
|
|
|
#if 0 // def _DEBUG
|
2017-10-25 17:19:00 +02:00
|
|
|
#define VLOG ILOG
|
|
|
|
#else
|
|
|
|
#define VLOG(...)
|
|
|
|
#endif
|
|
|
|
|
2017-08-28 17:10:10 +02:00
|
|
|
// TODO: Using a thread here is unfinished and does not work correctly.
|
2017-08-28 15:39:23 +02:00
|
|
|
const bool useThread = false;
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-10-26 13:00:27 +02:00
|
|
|
#ifndef UINT64_MAX
|
|
|
|
#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) {
|
2017-08-19 17:32:10 +02:00
|
|
|
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
|
|
|
ici.arrayLayers = 1;
|
|
|
|
ici.mipLevels = 1;
|
|
|
|
ici.extent.width = width;
|
|
|
|
ici.extent.height = height;
|
|
|
|
ici.extent.depth = 1;
|
|
|
|
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
ici.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
ici.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
ici.format = format;
|
2017-10-26 13:00:27 +02:00
|
|
|
// Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers.
|
2017-08-19 17:32:10 +02:00
|
|
|
ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
if (color) {
|
|
|
|
ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
|
|
} else {
|
|
|
|
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
|
|
}
|
2017-10-26 13:00:27 +02:00
|
|
|
|
2017-08-19 17:32:10 +02:00
|
|
|
vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image);
|
|
|
|
|
|
|
|
// TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers
|
|
|
|
|
|
|
|
VkMemoryRequirements memreq;
|
|
|
|
vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq);
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
|
|
|
alloc.allocationSize = memreq.size;
|
|
|
|
vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex);
|
|
|
|
VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
|
|
VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
|
|
|
|
|
|
|
|
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
|
|
|
ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
|
|
|
|
ivci.format = ici.format;
|
|
|
|
ivci.image = img.image;
|
|
|
|
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
ivci.subresourceRange.aspectMask = aspects;
|
|
|
|
ivci.subresourceRange.layerCount = 1;
|
|
|
|
ivci.subresourceRange.levelCount = 1;
|
|
|
|
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
2017-10-26 11:33:52 +02:00
|
|
|
VkPipelineStageFlags dstStage;
|
2017-08-19 17:32:10 +02:00
|
|
|
VkAccessFlagBits dstAccessMask;
|
|
|
|
switch (initialLayout) {
|
|
|
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
|
|
|
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
2017-10-26 11:33:52 +02:00
|
|
|
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
2017-08-19 17:32:10 +02:00
|
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
|
|
|
dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
|
|
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
|
|
|
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
2017-10-26 11:33:52 +02:00
|
|
|
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Crash();
|
2017-08-19 17:32:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TransitionImageLayout2(cmd, img.image, aspects,
|
|
|
|
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
|
|
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage,
|
|
|
|
0, dstAccessMask);
|
|
|
|
img.layout = initialLayout;
|
2017-10-28 18:41:54 +02:00
|
|
|
|
|
|
|
img.format = format;
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
2017-11-05 08:13:18 -08:00
|
|
|
bool VKRFramebuffer::Release() {
|
|
|
|
if (--refcount_ == 0) {
|
|
|
|
delete this;
|
|
|
|
return true;
|
|
|
|
} else if (refcount_ >= 10000 || refcount_ < 0) {
|
|
|
|
ELOG("Refcount (%d) invalid for object %p - corrupt?", refcount_.load(), this);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-27 22:10:36 +02:00
|
|
|
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(vulkan) {
|
2017-08-22 12:55:30 +02:00
|
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
|
|
|
semaphoreCreateInfo.flags = 0;
|
2017-10-09 12:43:56 +02:00
|
|
|
VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore_);
|
2017-08-22 12:55:30 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
2017-10-09 12:43:56 +02:00
|
|
|
res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_);
|
2017-08-22 12:55:30 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
2017-08-19 17:32:10 +02:00
|
|
|
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
|
|
|
|
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
|
|
|
cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex();
|
2017-08-22 12:55:30 +02:00
|
|
|
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
2017-08-22 17:18:54 +02:00
|
|
|
VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolInit);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolMain);
|
2017-08-19 17:32:10 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
2017-08-22 17:18:54 +02:00
|
|
|
cmd_alloc.commandPool = frameData_[i].cmdPoolInit;
|
2017-08-19 17:32:10 +02:00
|
|
|
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
2017-08-22 17:18:54 +02:00
|
|
|
cmd_alloc.commandBufferCount = 1;
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 17:18:54 +02:00
|
|
|
res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].initCmd);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
cmd_alloc.commandPool = frameData_[i].cmdPoolMain;
|
|
|
|
res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd);
|
2017-08-19 17:32:10 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on
|
|
|
|
}
|
2017-10-25 17:19:00 +02:00
|
|
|
|
2017-10-27 22:10:36 +02:00
|
|
|
queueRunner_.CreateDeviceObjects();
|
2017-08-22 12:55:30 +02:00
|
|
|
}
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::CreateBackbuffers() {
|
|
|
|
VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr);
|
2017-08-19 17:32:10 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
VkImage* swapchainImages = new VkImage[swapchainImageCount_];
|
|
|
|
res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages);
|
2017-08-19 17:32:10 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
VkCommandBuffer cmdInit = GetInitCmd();
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
|
2017-08-19 17:32:10 +02:00
|
|
|
SwapchainImageData sc_buffer;
|
2017-08-22 12:55:30 +02:00
|
|
|
sc_buffer.image = swapchainImages[i];
|
2017-08-19 17:32:10 +02:00
|
|
|
|
|
|
|
VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
|
|
|
color_image_view.format = vulkan_->GetSwapchainFormat();
|
|
|
|
color_image_view.components.r = VK_COMPONENT_SWIZZLE_R;
|
|
|
|
color_image_view.components.g = VK_COMPONENT_SWIZZLE_G;
|
|
|
|
color_image_view.components.b = VK_COMPONENT_SWIZZLE_B;
|
|
|
|
color_image_view.components.a = VK_COMPONENT_SWIZZLE_A;
|
|
|
|
color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
color_image_view.subresourceRange.baseMipLevel = 0;
|
|
|
|
color_image_view.subresourceRange.levelCount = 1;
|
|
|
|
color_image_view.subresourceRange.baseArrayLayer = 0;
|
|
|
|
color_image_view.subresourceRange.layerCount = 1;
|
|
|
|
color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
color_image_view.flags = 0;
|
2017-08-22 12:55:30 +02:00
|
|
|
color_image_view.image = sc_buffer.image;
|
2017-08-19 17:32:10 +02:00
|
|
|
|
|
|
|
// Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring
|
|
|
|
// in image to render to will be to transition them away from that.
|
|
|
|
TransitionImageLayout2(cmdInit, sc_buffer.image,
|
|
|
|
VK_IMAGE_ASPECT_COLOR_BIT,
|
|
|
|
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
|
|
|
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
|
|
|
|
2017-10-09 12:43:56 +02:00
|
|
|
res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, nullptr, &sc_buffer.view);
|
2017-08-19 17:32:10 +02:00
|
|
|
swapchainImages_.push_back(sc_buffer);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
|
|
|
delete[] swapchainImages;
|
2017-08-22 12:55:30 +02:00
|
|
|
|
|
|
|
InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass.
|
|
|
|
InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
|
|
|
curWidth_ = -1;
|
|
|
|
curHeight_ = -1;
|
2017-08-22 17:18:54 +02:00
|
|
|
|
|
|
|
// Start the thread.
|
|
|
|
if (useThread) {
|
2017-11-04 22:21:47 -07:00
|
|
|
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
|
|
|
|
// Reset all the frameData. Might be dirty from previous thread stop.
|
|
|
|
frameData_[i].readyForRun = false;
|
|
|
|
frameData_[i].readyForFence = true;
|
|
|
|
}
|
|
|
|
|
2017-08-22 17:18:54 +02:00
|
|
|
run_ = true;
|
2017-11-04 22:21:47 -07:00
|
|
|
// Won't necessarily be 0.
|
|
|
|
threadInitFrame_ = vulkan_->GetCurFrame();
|
2017-08-22 17:18:54 +02:00
|
|
|
thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this);
|
|
|
|
}
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::DestroyBackbuffers() {
|
2017-08-22 17:18:54 +02:00
|
|
|
if (useThread) {
|
|
|
|
run_ = false;
|
2017-08-28 17:10:10 +02:00
|
|
|
// Stop the thread.
|
|
|
|
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(frameData_[i].push_mutex);
|
|
|
|
frameData_[i].push_condVar.notify_all();
|
|
|
|
}
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(frameData_[i].pull_mutex);
|
|
|
|
frameData_[i].pull_condVar.notify_all();
|
|
|
|
}
|
|
|
|
}
|
2017-08-22 17:18:54 +02:00
|
|
|
thread_.join();
|
|
|
|
}
|
2017-11-05 08:39:02 -08:00
|
|
|
vulkan_->WaitUntilQueueIdle();
|
|
|
|
|
2017-08-19 17:32:10 +02:00
|
|
|
VkDevice device = vulkan_->GetDevice();
|
2017-08-22 12:55:30 +02:00
|
|
|
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
|
|
|
|
vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view);
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-08-22 12:55:30 +02:00
|
|
|
vulkan_->Delete().QueueDeleteImageView(depth_.view);
|
|
|
|
vulkan_->Delete().QueueDeleteImage(depth_.image);
|
|
|
|
vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem);
|
2017-10-25 17:19:00 +02:00
|
|
|
for (uint32_t i = 0; i < framebuffers_.size(); i++) {
|
|
|
|
assert(framebuffers_[i] != VK_NULL_HANDLE);
|
|
|
|
vulkan_->Delete().QueueDeleteFramebuffer(framebuffers_[i]);
|
|
|
|
}
|
|
|
|
framebuffers_.clear();
|
|
|
|
|
2017-08-19 17:32:10 +02:00
|
|
|
swapchainImages_.clear();
|
2017-08-22 12:55:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VulkanRenderManager::~VulkanRenderManager() {
|
2017-08-22 17:18:54 +02:00
|
|
|
run_ = false;
|
2017-08-22 12:55:30 +02:00
|
|
|
VkDevice device = vulkan_->GetDevice();
|
|
|
|
vulkan_->WaitUntilQueueIdle();
|
2017-08-19 17:32:10 +02:00
|
|
|
vkDestroySemaphore(device, acquireSemaphore_, nullptr);
|
2017-08-28 17:10:10 +02:00
|
|
|
vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr);
|
2017-08-19 17:32:10 +02:00
|
|
|
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
|
|
|
|
VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd };
|
2017-08-22 17:18:54 +02:00
|
|
|
vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd);
|
|
|
|
vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd);
|
2017-10-25 17:19:00 +02:00
|
|
|
vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr);
|
|
|
|
vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr);
|
2017-08-19 17:32:10 +02:00
|
|
|
vkDestroyFence(device, frameData_[i].fence, nullptr);
|
|
|
|
}
|
2017-10-27 22:10:36 +02:00
|
|
|
queueRunner_.DestroyDeviceObjects();
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
// TODO: Activate this code.
|
2017-08-19 17:32:10 +02:00
|
|
|
void VulkanRenderManager::ThreadFunc() {
|
2017-08-22 17:18:54 +02:00
|
|
|
setCurrentThreadName("RenderMan");
|
2017-11-04 22:21:47 -07:00
|
|
|
int threadFrame = threadInitFrame_;
|
|
|
|
bool nextFrame = false;
|
2017-08-22 17:18:54 +02:00
|
|
|
while (run_) {
|
2017-08-28 17:10:10 +02:00
|
|
|
{
|
2017-11-04 22:21:47 -07:00
|
|
|
if (nextFrame) {
|
|
|
|
threadFrame++;
|
|
|
|
if (threadFrame >= vulkan_->GetInflightFrames())
|
|
|
|
threadFrame = 0;
|
|
|
|
}
|
2017-08-28 17:10:10 +02:00
|
|
|
FrameData &frameData = frameData_[threadFrame];
|
|
|
|
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
|
|
|
|
while (!frameData.readyForRun && run_) {
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame);
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.pull_condVar.wait(lock);
|
|
|
|
}
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PULL: frame[%d].readyForRun = false", threadFrame);
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.readyForRun = false;
|
|
|
|
if (!run_) // quick exit if bailing.
|
|
|
|
return;
|
2017-11-04 22:21:47 -07:00
|
|
|
|
2017-11-04 22:23:01 -07:00
|
|
|
// Only increment next time if we're done.
|
|
|
|
nextFrame = frameData.type == VKRRunType::END;
|
|
|
|
assert(frameData.type == VKRRunType::END || frameData.type == VKRRunType::SYNC);
|
2017-08-22 17:18:54 +02:00
|
|
|
}
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PULL: Running frame %d", threadFrame);
|
2017-08-28 17:10:10 +02:00
|
|
|
Run(threadFrame);
|
2017-11-04 22:21:47 -07:00
|
|
|
VLOG("PULL: Finished frame %d", threadFrame);
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-11-04 22:21:47 -07:00
|
|
|
VLOG("PULL: Quitting");
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-08-16 23:03:30 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::BeginFrame() {
|
2017-08-19 17:32:10 +02:00
|
|
|
VkDevice device = vulkan_->GetDevice();
|
|
|
|
|
2017-08-22 17:44:28 +02:00
|
|
|
int curFrame = vulkan_->GetCurFrame();
|
|
|
|
FrameData &frameData = frameData_[curFrame];
|
2017-08-19 17:32:10 +02:00
|
|
|
|
|
|
|
// Make sure the very last command buffer from the frame before the previous has been fully executed.
|
2017-08-22 17:18:54 +02:00
|
|
|
if (useThread) {
|
2017-08-28 17:10:10 +02:00
|
|
|
std::unique_lock<std::mutex> lock(frameData.push_mutex);
|
2017-08-22 17:18:54 +02:00
|
|
|
while (!frameData.readyForFence) {
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame);
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.push_condVar.wait(lock);
|
2017-08-22 17:18:54 +02:00
|
|
|
}
|
|
|
|
frameData.readyForFence = false;
|
|
|
|
}
|
2017-08-28 17:10:10 +02:00
|
|
|
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PUSH: Fencing %d", curFrame);
|
2017-08-19 17:32:10 +02:00
|
|
|
vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX);
|
|
|
|
vkResetFences(device, 1, &frameData.fence);
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
// Must be after the fence - this performs deletes.
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PUSH: BeginFrame %d", curFrame);
|
2017-08-22 12:55:30 +02:00
|
|
|
vulkan_->BeginFrame();
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
insideFrame_ = true;
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VkCommandBuffer VulkanRenderManager::GetInitCmd() {
|
2017-08-22 12:55:30 +02:00
|
|
|
int curFrame = vulkan_->GetCurFrame();
|
|
|
|
FrameData &frameData = frameData_[curFrame];
|
|
|
|
if (!frameData.hasInitCommands) {
|
2017-10-28 18:03:27 +02:00
|
|
|
VkCommandBufferBeginInfo begin = {
|
|
|
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
|
|
nullptr,
|
|
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
|
|
|
};
|
2017-08-22 12:55:30 +02:00
|
|
|
VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
frameData.hasInitCommands = true;
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-08-22 17:44:28 +02:00
|
|
|
return frameData_[curFrame].initCmd;
|
2017-08-18 15:08:40 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) {
|
2017-10-31 21:15:19 +01:00
|
|
|
assert(insideFrame_);
|
2017-08-22 13:25:45 +02:00
|
|
|
// Eliminate dupes.
|
2017-10-31 12:02:10 +01:00
|
|
|
if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) {
|
2017-08-22 13:25:45 +02:00
|
|
|
if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) {
|
2017-10-31 12:02:10 +01:00
|
|
|
// We don't move to a new step, this bind was unnecessary and we can safely skip it.
|
2017-08-22 13:25:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-10-31 12:02:10 +01:00
|
|
|
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == VKRRenderPassAction::KEEP) {
|
|
|
|
// Can trivially kill the last empty render step.
|
|
|
|
assert(steps_.back() == curRenderStep_);
|
|
|
|
delete steps_.back();
|
|
|
|
steps_.pop_back();
|
|
|
|
curRenderStep_ = nullptr;
|
|
|
|
}
|
|
|
|
if (curRenderStep_ && curRenderStep_->commands.size() == 0) {
|
2017-11-04 11:41:44 +01:00
|
|
|
#ifdef _DEBUG
|
2017-10-31 12:02:10 +01:00
|
|
|
ILOG("Empty render step. Usually happens after uploading pixels..");
|
2017-11-04 11:41:44 +01:00
|
|
|
#endif
|
2017-10-31 12:02:10 +01:00
|
|
|
}
|
2017-08-22 13:25:45 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
VKRStep *step = new VKRStep{ VKRStepType::RENDER };
|
2017-08-19 17:32:10 +02:00
|
|
|
// This is what queues up new passes, and can end previous ones.
|
2017-08-22 12:55:30 +02:00
|
|
|
step->render.framebuffer = fb;
|
|
|
|
step->render.color = color;
|
|
|
|
step->render.depthStencil = depth;
|
|
|
|
step->render.clearColor = clearColor;
|
|
|
|
step->render.clearDepth = clearDepth;
|
|
|
|
step->render.clearStencil = clearStencil;
|
2017-08-22 13:25:45 +02:00
|
|
|
step->render.numDraws = 0;
|
|
|
|
step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
|
2017-08-22 12:55:30 +02:00
|
|
|
steps_.push_back(step);
|
2017-11-05 08:13:18 -08:00
|
|
|
if (fb) {
|
|
|
|
fb->AddRef();
|
|
|
|
}
|
2017-08-22 13:25:45 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
curRenderStep_ = step;
|
|
|
|
curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth();
|
|
|
|
curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight();
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 14:42:51 +01:00
|
|
|
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
|
2017-10-28 18:03:27 +02:00
|
|
|
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
|
|
|
|
step->readback.aspectMask = aspectBits;
|
|
|
|
step->readback.src = src;
|
|
|
|
step->readback.srcRect.offset = { x, y };
|
|
|
|
step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h };
|
|
|
|
steps_.push_back(step);
|
2017-11-05 08:13:18 -08:00
|
|
|
src->AddRef();
|
2017-10-28 18:03:27 +02:00
|
|
|
|
|
|
|
curRenderStep_ = nullptr;
|
|
|
|
|
|
|
|
FlushSync();
|
|
|
|
|
2017-10-28 18:41:54 +02:00
|
|
|
// Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM.
|
2017-10-29 10:56:02 +01:00
|
|
|
queueRunner_.CopyReadbackBuffer(w, h, destFormat, pixelStride, pixels);
|
2017-10-28 18:03:27 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
|
2017-08-19 17:32:10 +02:00
|
|
|
VkResult U_ASSERT_ONLY res;
|
2017-08-22 12:55:30 +02:00
|
|
|
// We share the same depth buffer but have multiple color buffers, see the loop below.
|
|
|
|
VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view };
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("InitFramebuffers: %dx%d", width, height);
|
2017-08-19 17:32:10 +02:00
|
|
|
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
2017-10-27 22:10:36 +02:00
|
|
|
fb_info.renderPass = queueRunner_.GetBackbufferRenderPass();
|
2017-08-22 12:55:30 +02:00
|
|
|
fb_info.attachmentCount = 2;
|
2017-08-19 17:32:10 +02:00
|
|
|
fb_info.pAttachments = attachments;
|
2017-08-22 12:55:30 +02:00
|
|
|
fb_info.width = width;
|
|
|
|
fb_info.height = height;
|
2017-08-19 17:32:10 +02:00
|
|
|
fb_info.layers = 1;
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
framebuffers_.resize(swapchainImageCount_);
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
|
2017-08-19 17:32:10 +02:00
|
|
|
attachments[0] = swapchainImages_[i].view;
|
|
|
|
res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) {
|
|
|
|
VkResult U_ASSERT_ONLY res;
|
|
|
|
bool U_ASSERT_ONLY pass;
|
|
|
|
|
|
|
|
const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
|
|
|
|
int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
|
|
|
image_info.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
image_info.format = depth_format;
|
2017-08-22 12:55:30 +02:00
|
|
|
image_info.extent.width = vulkan_->GetBackbufferWidth();
|
|
|
|
image_info.extent.height = vulkan_->GetBackbufferHeight();
|
2017-08-19 17:32:10 +02:00
|
|
|
image_info.extent.depth = 1;
|
|
|
|
image_info.mipLevels = 1;
|
|
|
|
image_info.arrayLayers = 1;
|
|
|
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
image_info.queueFamilyIndexCount = 0;
|
|
|
|
image_info.pQueueFamilyIndices = nullptr;
|
|
|
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
|
|
image_info.flags = 0;
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
|
|
|
mem_alloc.allocationSize = 0;
|
|
|
|
mem_alloc.memoryTypeIndex = 0;
|
|
|
|
|
|
|
|
VkMemoryRequirements mem_reqs;
|
|
|
|
|
|
|
|
depth_.format = depth_format;
|
|
|
|
|
|
|
|
VkDevice device = vulkan_->GetDevice();
|
|
|
|
res = vkCreateImage(device, &image_info, NULL, &depth_.image);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
|
|
vkGetImageMemoryRequirements(device, depth_.image, &mem_reqs);
|
|
|
|
|
|
|
|
mem_alloc.allocationSize = mem_reqs.size;
|
|
|
|
// Use the memory properties to determine the type of memory required
|
|
|
|
pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits,
|
|
|
|
0, /* No requirements */
|
|
|
|
&mem_alloc.memoryTypeIndex);
|
|
|
|
assert(pass);
|
|
|
|
|
|
|
|
res = vkAllocateMemory(device, &mem_alloc, NULL, &depth_.mem);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
|
|
res = vkBindImageMemory(device, depth_.image, depth_.mem, 0);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
|
|
TransitionImageLayout2(cmd, depth_.image,
|
|
|
|
aspectMask,
|
|
|
|
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
|
|
|
0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
|
|
|
|
|
|
|
|
VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
|
|
|
depth_view_info.image = depth_.image;
|
|
|
|
depth_view_info.format = depth_format;
|
|
|
|
depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R;
|
|
|
|
depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G;
|
|
|
|
depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B;
|
|
|
|
depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A;
|
|
|
|
depth_view_info.subresourceRange.aspectMask = aspectMask;
|
|
|
|
depth_view_info.subresourceRange.baseMipLevel = 0;
|
|
|
|
depth_view_info.subresourceRange.levelCount = 1;
|
|
|
|
depth_view_info.subresourceRange.baseArrayLayer = 0;
|
|
|
|
depth_view_info.subresourceRange.layerCount = 1;
|
|
|
|
depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
depth_view_info.flags = 0;
|
|
|
|
|
|
|
|
res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2017-08-16 23:03:30 +02:00
|
|
|
void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) {
|
2017-08-22 12:55:30 +02:00
|
|
|
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
2017-08-16 23:03:30 +02:00
|
|
|
// If this is the first drawing command, merge it into the pass.
|
2017-08-22 12:55:30 +02:00
|
|
|
if (curRenderStep_->render.numDraws == 0) {
|
|
|
|
curRenderStep_->render.clearColor = clearColor;
|
|
|
|
curRenderStep_->render.clearDepth = clearZ;
|
|
|
|
curRenderStep_->render.clearStencil = clearStencil;
|
|
|
|
curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
|
|
|
|
curRenderStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
|
2017-08-16 23:03:30 +02:00
|
|
|
} else {
|
2017-08-22 12:55:30 +02:00
|
|
|
VkRenderData data{ VKRRenderCommand::CLEAR };
|
2017-08-16 23:03:30 +02:00
|
|
|
data.clear.clearColor = clearColor;
|
|
|
|
data.clear.clearZ = clearZ;
|
|
|
|
data.clear.clearStencil = clearStencil;
|
|
|
|
data.clear.clearMask = clearMask;
|
2017-08-22 12:55:30 +02:00
|
|
|
curRenderStep_->commands.push_back(data);
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) {
|
2017-10-25 21:19:42 +02:00
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0");
|
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0");
|
2017-10-25 22:04:25 +02:00
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width");
|
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height");
|
2017-10-25 21:19:42 +02:00
|
|
|
_dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0");
|
|
|
|
_dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0");
|
2017-10-25 22:04:25 +02:00
|
|
|
_dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= (uint32_t)dst->width, "dstPos + extent x > width");
|
|
|
|
_dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= (uint32_t)dst->height, "dstPos + extent y > height");
|
2017-10-25 21:19:42 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
VKRStep *step = new VKRStep{ VKRStepType::COPY };
|
2017-10-25 21:19:42 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
step->copy.aspectMask = aspectMask;
|
|
|
|
step->copy.src = src;
|
|
|
|
step->copy.srcRect = srcRect;
|
|
|
|
step->copy.dst = dst;
|
|
|
|
step->copy.dstPos = dstPos;
|
2017-11-05 08:13:18 -08:00
|
|
|
src->AddRef();
|
|
|
|
dst->AddRef();
|
2017-10-25 20:28:12 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
|
|
steps_.push_back(step);
|
|
|
|
curRenderStep_ = nullptr;
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) {
|
2017-10-25 21:19:42 +02:00
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0");
|
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0");
|
2017-10-25 22:04:25 +02:00
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width");
|
|
|
|
_dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height");
|
2017-10-25 21:19:42 +02:00
|
|
|
|
|
|
|
_dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0");
|
|
|
|
_dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0");
|
2017-10-25 22:04:25 +02:00
|
|
|
_dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= (uint32_t)dst->width, "dstrect offset x + extent > width");
|
|
|
|
_dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= (uint32_t)dst->height, "dstrect offset y + extent > height");
|
2017-10-25 21:19:42 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
VKRStep *step = new VKRStep{ VKRStepType::BLIT };
|
2017-10-25 21:19:42 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
step->blit.aspectMask = aspectMask;
|
|
|
|
step->blit.src = src;
|
|
|
|
step->blit.srcRect = srcRect;
|
|
|
|
step->blit.dst = dst;
|
|
|
|
step->blit.dstRect = dstRect;
|
|
|
|
step->blit.filter = filter;
|
2017-11-05 08:13:18 -08:00
|
|
|
src->AddRef();
|
|
|
|
dst->AddRef();
|
2017-10-25 20:28:12 +02:00
|
|
|
|
2017-08-22 12:55:30 +02:00
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
|
|
steps_.push_back(step);
|
|
|
|
curRenderStep_ = nullptr;
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) {
|
|
|
|
// Should just mark the dependency and return the image.
|
2017-08-22 13:25:45 +02:00
|
|
|
for (int i = (int)steps_.size() - 1; i >= 0; i--) {
|
2017-08-22 12:55:30 +02:00
|
|
|
if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) {
|
2017-08-22 13:25:45 +02:00
|
|
|
// If this framebuffer was rendered to earlier in this frame, make sure to pre-transition it to the correct layout.
|
|
|
|
if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
2017-08-19 17:32:10 +02:00
|
|
|
steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
2017-08-22 13:25:45 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-10-30 16:30:19 +01:00
|
|
|
else if (steps_[i]->render.finalColorLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
|
|
|
Crash();
|
2017-08-22 13:25:45 +02:00
|
|
|
// May need to shadow the framebuffer if we re-order passes later.
|
|
|
|
}
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-08-16 23:03:30 +02:00
|
|
|
}
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-08-22 13:25:45 +02:00
|
|
|
curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL });
|
2017-11-05 08:13:18 -08:00
|
|
|
fb->AddRef();
|
2017-08-19 17:32:10 +02:00
|
|
|
return fb->color.imageView;
|
2017-08-16 23:03:30 +02:00
|
|
|
}
|
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
void VulkanRenderManager::Finish() {
|
2017-08-22 18:05:05 +02:00
|
|
|
curRenderStep_ = nullptr;
|
2017-08-22 17:44:28 +02:00
|
|
|
int curFrame = vulkan_->GetCurFrame();
|
2017-08-22 18:05:05 +02:00
|
|
|
FrameData &frameData = frameData_[curFrame];
|
2017-08-22 17:18:54 +02:00
|
|
|
if (!useThread) {
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.steps = std::move(steps_);
|
2017-11-04 22:23:01 -07:00
|
|
|
frameData.type = VKRRunType::END;
|
2017-08-22 17:44:28 +02:00
|
|
|
Run(curFrame);
|
2017-08-22 17:18:54 +02:00
|
|
|
} else {
|
2017-08-28 17:10:10 +02:00
|
|
|
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PUSH: Frame[%d].readyForRun = true", curFrame);
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.steps = std::move(steps_);
|
|
|
|
frameData.readyForRun = true;
|
2017-11-04 22:23:01 -07:00
|
|
|
frameData.type = VKRRunType::END;
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.pull_condVar.notify_all();
|
2017-08-22 17:18:54 +02:00
|
|
|
}
|
|
|
|
vulkan_->EndFrame();
|
2017-11-05 07:07:14 -08:00
|
|
|
|
|
|
|
insideFrame_ = false;
|
2017-08-22 17:18:54 +02:00
|
|
|
}
|
|
|
|
|
2017-11-01 21:42:19 +01:00
|
|
|
void VulkanRenderManager::Wipe() {
|
|
|
|
int curFrame = vulkan_->GetCurFrame();
|
|
|
|
for (auto iter : steps_) {
|
|
|
|
delete iter;
|
|
|
|
}
|
|
|
|
steps_.clear();
|
|
|
|
}
|
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
// Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way,
|
2017-11-01 08:57:31 +01:00
|
|
|
// or stop it in the middle for a synchronous readback, then start over again mostly normally but without repeating
|
|
|
|
// the backbuffer image acquisition.
|
2017-10-28 18:03:27 +02:00
|
|
|
void VulkanRenderManager::BeginSubmitFrame(int frame) {
|
2017-08-22 17:44:28 +02:00
|
|
|
FrameData &frameData = frameData_[frame];
|
2017-10-28 16:47:08 +02:00
|
|
|
if (!frameData.hasBegun) {
|
|
|
|
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
|
|
|
|
// Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine.
|
|
|
|
VkResult res = vkAcquireNextImageKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &frameData.curSwapchainImage);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
|
|
|
|
// return codes
|
2017-08-22 17:18:54 +02:00
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
|
|
|
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
res = vkBeginCommandBuffer(frameData.mainCmd, &begin);
|
2017-08-22 12:55:30 +02:00
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
2017-10-27 22:10:36 +02:00
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage]);
|
2017-08-19 17:32:10 +02:00
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
frameData.hasBegun = true;
|
|
|
|
}
|
|
|
|
}
|
2017-10-27 22:10:36 +02:00
|
|
|
|
2017-10-28 18:41:54 +02:00
|
|
|
void VulkanRenderManager::Submit(int frame, bool triggerFence) {
|
2017-10-28 16:47:08 +02:00
|
|
|
FrameData &frameData = frameData_[frame];
|
2017-10-28 18:03:27 +02:00
|
|
|
if (frameData.hasInitCommands) {
|
|
|
|
VkResult res = vkEndCommandBuffer(frameData.initCmd);
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
2017-08-28 17:10:10 +02:00
|
|
|
|
2017-10-28 16:47:08 +02:00
|
|
|
VkResult res = vkEndCommandBuffer(frameData.mainCmd);
|
2017-08-28 17:10:10 +02:00
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
2017-10-31 23:49:47 +01:00
|
|
|
VkCommandBuffer cmdBufs[2];
|
2017-08-28 17:10:10 +02:00
|
|
|
int numCmdBufs = 0;
|
|
|
|
if (frameData.hasInitCommands) {
|
2017-10-31 23:49:47 +01:00
|
|
|
cmdBufs[numCmdBufs++] = frameData.initCmd;
|
2017-08-28 17:10:10 +02:00
|
|
|
frameData.hasInitCommands = false;
|
|
|
|
}
|
2017-11-01 00:37:07 +01:00
|
|
|
if (false) {
|
|
|
|
// Send the init commands off separately. Used this once to confirm that the cause of a device loss was in the init cmdbuf.
|
|
|
|
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
|
|
|
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
|
|
|
|
submit_info.pCommandBuffers = cmdBufs;
|
|
|
|
res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, VK_NULL_HANDLE);
|
|
|
|
if (res == VK_ERROR_DEVICE_LOST) {
|
|
|
|
_assert_msg_(G3D, false, "Lost the Vulkan device!");
|
|
|
|
} else {
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
|
|
|
numCmdBufs = 0;
|
|
|
|
}
|
2017-10-31 23:49:47 +01:00
|
|
|
cmdBufs[numCmdBufs++] = frameData.mainCmd;
|
2017-08-28 17:10:10 +02:00
|
|
|
|
2017-10-31 23:49:13 +01:00
|
|
|
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
2017-10-28 18:41:54 +02:00
|
|
|
if (triggerFence) {
|
|
|
|
submit_info.waitSemaphoreCount = 1;
|
|
|
|
submit_info.pWaitSemaphores = &acquireSemaphore_;
|
2017-11-01 00:37:07 +01:00
|
|
|
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
|
|
|
submit_info.pWaitDstStageMask = waitStage;
|
2017-10-28 18:41:54 +02:00
|
|
|
}
|
2017-10-31 23:49:47 +01:00
|
|
|
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
|
|
|
|
submit_info.pCommandBuffers = cmdBufs;
|
2017-10-28 18:41:54 +02:00
|
|
|
if (triggerFence) {
|
|
|
|
submit_info.signalSemaphoreCount = 1;
|
|
|
|
submit_info.pSignalSemaphores = &renderingCompleteSemaphore_;
|
|
|
|
}
|
|
|
|
res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE);
|
2017-10-31 23:49:13 +01:00
|
|
|
if (res == VK_ERROR_DEVICE_LOST) {
|
2017-11-01 00:37:07 +01:00
|
|
|
_assert_msg_(G3D, false, "Lost the Vulkan device!");
|
2017-10-31 23:49:13 +01:00
|
|
|
} else {
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
2017-08-28 17:10:10 +02:00
|
|
|
|
2017-11-04 22:29:12 -07:00
|
|
|
// When !triggerFence, we notify after syncing with Vulkan.
|
2017-11-04 21:04:07 -07:00
|
|
|
if (useThread && triggerFence) {
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PULL: Frame %d.readyForFence = true", frame);
|
2017-08-28 17:10:10 +02:00
|
|
|
std::unique_lock<std::mutex> lock(frameData.push_mutex);
|
|
|
|
frameData.readyForFence = true;
|
|
|
|
frameData.push_condVar.notify_all();
|
|
|
|
}
|
2017-10-28 18:03:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanRenderManager::EndSubmitFrame(int frame) {
|
|
|
|
FrameData &frameData = frameData_[frame];
|
|
|
|
frameData.hasBegun = false;
|
|
|
|
|
2017-10-28 18:41:54 +02:00
|
|
|
Submit(frame, true);
|
2017-08-28 17:10:10 +02:00
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = vulkan_->GetSwapchain();
|
|
|
|
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
|
|
|
present.swapchainCount = 1;
|
|
|
|
present.pSwapchains = &swapchain;
|
2017-10-28 16:47:08 +02:00
|
|
|
present.pImageIndices = &frameData.curSwapchainImage;
|
2017-08-28 17:10:10 +02:00
|
|
|
present.pWaitSemaphores = &renderingCompleteSemaphore_;
|
|
|
|
present.waitSemaphoreCount = 1;
|
2017-10-28 18:03:27 +02:00
|
|
|
|
|
|
|
VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present);
|
2017-08-28 17:10:10 +02:00
|
|
|
// TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI
|
|
|
|
// return codes
|
2017-10-26 00:55:09 +02:00
|
|
|
if (res == VK_ERROR_OUT_OF_DATE_KHR) {
|
|
|
|
// ignore, it'll be fine. this happens sometimes during resizes, and we do make sure to recreate the swap chain.
|
|
|
|
} else {
|
|
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
2017-10-28 16:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanRenderManager::Run(int frame) {
|
2017-10-28 18:03:27 +02:00
|
|
|
BeginSubmitFrame(frame);
|
2017-10-28 16:47:08 +02:00
|
|
|
|
|
|
|
FrameData &frameData = frameData_[frame];
|
|
|
|
auto &stepsOnThread = frameData_[frame].steps;
|
|
|
|
VkCommandBuffer cmd = frameData.mainCmd;
|
2017-10-31 23:49:13 +01:00
|
|
|
// queueRunner_.LogSteps(stepsOnThread);
|
2017-10-28 16:47:08 +02:00
|
|
|
queueRunner_.RunSteps(cmd, stepsOnThread);
|
|
|
|
stepsOnThread.clear();
|
|
|
|
|
2017-11-04 22:23:01 -07:00
|
|
|
switch (frameData.type) {
|
|
|
|
case VKRRunType::END:
|
|
|
|
EndSubmitFrame(frame);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VKRRunType::SYNC:
|
|
|
|
EndSyncFrame(frame);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
2017-10-28 16:47:08 +02:00
|
|
|
|
2017-10-25 17:19:00 +02:00
|
|
|
VLOG("PULL: Finished running frame %d", frame);
|
2017-08-19 17:32:10 +02:00
|
|
|
}
|
2017-10-28 18:03:27 +02:00
|
|
|
|
2017-11-04 22:23:01 -07:00
|
|
|
void VulkanRenderManager::EndSyncFrame(int frame) {
|
2017-10-28 18:03:27 +02:00
|
|
|
FrameData &frameData = frameData_[frame];
|
2017-10-28 18:41:54 +02:00
|
|
|
Submit(frame, false);
|
2017-10-28 18:03:27 +02:00
|
|
|
|
2017-10-31 23:49:13 +01:00
|
|
|
// This is brutal! Should probably wait for a fence instead, not that it'll matter much since we'll
|
|
|
|
// still stall everything.
|
2017-10-28 18:03:27 +02:00
|
|
|
vkDeviceWaitIdle(vulkan_->GetDevice());
|
|
|
|
|
|
|
|
// At this point we can resume filling the command buffers for the current frame since
|
|
|
|
// we know the device is idle - and thus all previously enqueued command buffers have been processed.
|
|
|
|
// No need to switch to the next frame number.
|
2017-10-28 18:41:54 +02:00
|
|
|
VkCommandBufferBeginInfo begin{
|
2017-10-28 18:03:27 +02:00
|
|
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
|
|
nullptr,
|
|
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
|
|
|
};
|
2017-10-28 18:41:54 +02:00
|
|
|
VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin);
|
|
|
|
assert(res == VK_SUCCESS);
|
2017-11-04 22:23:01 -07:00
|
|
|
|
|
|
|
if (useThread) {
|
|
|
|
std::unique_lock<std::mutex> lock(frameData.push_mutex);
|
|
|
|
frameData.readyForFence = true;
|
|
|
|
frameData.push_condVar.notify_all();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanRenderManager::FlushSync() {
|
|
|
|
// TODO: Reset curRenderStep_?
|
|
|
|
int curFrame = vulkan_->GetCurFrame();
|
|
|
|
FrameData &frameData = frameData_[curFrame];
|
|
|
|
if (!useThread) {
|
|
|
|
frameData.steps = std::move(steps_);
|
|
|
|
frameData.type = VKRRunType::SYNC;
|
|
|
|
Run(curFrame);
|
|
|
|
} else {
|
|
|
|
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
|
|
|
|
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame);
|
|
|
|
frameData.steps = std::move(steps_);
|
|
|
|
frameData.readyForRun = true;
|
|
|
|
assert(frameData.readyForFence == false);
|
|
|
|
frameData.type = VKRRunType::SYNC;
|
|
|
|
frameData.pull_condVar.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useThread) {
|
|
|
|
std::unique_lock<std::mutex> lock(frameData.push_mutex);
|
|
|
|
// Wait for the flush to be hit, since we're syncing.
|
|
|
|
while (!frameData.readyForFence) {
|
|
|
|
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame);
|
|
|
|
frameData.push_condVar.wait(lock);
|
|
|
|
}
|
|
|
|
frameData.readyForFence = false;
|
|
|
|
}
|
2017-10-28 18:03:27 +02:00
|
|
|
}
|