mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
It builds! With some shortcuts, of course.
This commit is contained in:
parent
417b96a1b0
commit
0a0494ef8e
19 changed files with 1462 additions and 1464 deletions
|
@ -167,120 +167,14 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) {
|
|||
VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::GetInitCommandBuffer() {
|
||||
void VulkanContext::BeginFrame() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
if (!frame->hasInitCommands) {
|
||||
VulkanBeginCommandBuffer(frame->cmdInit);
|
||||
frame->hasInitCommands = true;
|
||||
}
|
||||
return frame_[curFrame_].cmdInit;
|
||||
}
|
||||
|
||||
void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) {
|
||||
cmdQueue_.push_back(cmd);
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::BeginFrame() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
// 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(device_, swap_chain_, UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer);
|
||||
// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
|
||||
// return codes
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
// Make sure the very last command buffer from the frame before the previous has been fully executed.
|
||||
WaitAndResetFence(frame->fence);
|
||||
|
||||
// Process pending deletes.
|
||||
frame->deleteList.PerformDeletes(device_);
|
||||
|
||||
// Reset both command buffers in one fell swoop.
|
||||
// Note that on the first frame, there might already be commands so don't reset in that case.
|
||||
if (!frame->hasInitCommands) {
|
||||
vkResetCommandPool(device_, frame->cmdPool, 0);
|
||||
}
|
||||
|
||||
VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
begin.pInheritanceInfo = nullptr;
|
||||
res = vkBeginCommandBuffer(frame->cmdBuf, &begin);
|
||||
|
||||
TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
|
||||
return frame->cmdBuf;
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
||||
rp_begin.renderPass = surface_render_pass_;
|
||||
rp_begin.framebuffer = framebuffers_[current_buffer];
|
||||
rp_begin.renderArea.offset.x = 0;
|
||||
rp_begin.renderArea.offset.y = 0;
|
||||
rp_begin.renderArea.extent.width = width_;
|
||||
rp_begin.renderArea.extent.height = height_;
|
||||
rp_begin.clearValueCount = 2;
|
||||
rp_begin.pClearValues = clear_values;
|
||||
vkCmdBeginRenderPass(frame->cmdBuf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
return frame->cmdBuf;
|
||||
}
|
||||
|
||||
void VulkanContext::EndSurfaceRenderPass() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
// ILOG("VulkanContext::EndSurfaceRenderPass");
|
||||
vkCmdEndRenderPass(frame->cmdBuf);
|
||||
}
|
||||
|
||||
void VulkanContext::EndFrame() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
TransitionToPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
|
||||
|
||||
VkResult res = vkEndCommandBuffer(frame->cmdBuf);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
// So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf.
|
||||
// This way we bunch up all the initialization needed for the frame, we render to
|
||||
// other buffers before the back buffer, and then last we render to the backbuffer.
|
||||
|
||||
int numCmdBufs = 0;
|
||||
std::vector<VkCommandBuffer> cmdBufs;
|
||||
if (frame->hasInitCommands) {
|
||||
vkEndCommandBuffer(frame->cmdInit);
|
||||
cmdBufs.push_back(frame->cmdInit);
|
||||
frame->hasInitCommands = false;
|
||||
}
|
||||
for (auto cmd : cmdQueue_) {
|
||||
cmdBufs.push_back(cmd);
|
||||
}
|
||||
cmdQueue_.clear();
|
||||
cmdBufs.push_back(frame->cmdBuf);
|
||||
|
||||
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &acquireSemaphore_;
|
||||
VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT };
|
||||
submit_info.pWaitDstStageMask = waitStage;
|
||||
submit_info.commandBufferCount = (uint32_t)cmdBufs.size();
|
||||
submit_info.pCommandBuffers = cmdBufs.data();
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &renderingCompleteSemaphore_;
|
||||
res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||
present.swapchainCount = 1;
|
||||
present.pSwapchains = &swap_chain_;
|
||||
present.pImageIndices = ¤t_buffer;
|
||||
present.pWaitSemaphores = &renderingCompleteSemaphore_;
|
||||
present.waitSemaphoreCount = 1;
|
||||
present.pResults = NULL;
|
||||
|
||||
res = vkQueuePresentKHR(gfx_queue_, &present);
|
||||
// TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI
|
||||
// return codes
|
||||
assert(!res);
|
||||
|
||||
frame->deleteList.Take(globalDeleteList_);
|
||||
frame_[curFrame_].deleteList.Take(globalDeleteList_);
|
||||
curFrame_++;
|
||||
if (curFrame_ >= inflightFrames_) {
|
||||
curFrame_ = 0;
|
||||
|
@ -308,72 +202,19 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem
|
|||
return false;
|
||||
}
|
||||
|
||||
void VulkanBeginCommandBuffer(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkCommandBufferBeginInfo cmd_buf_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
cmd_buf_info.pInheritanceInfo = nullptr;
|
||||
cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
res = vkBeginCommandBuffer(cmd, &cmd_buf_info);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
bool VulkanContext::InitObjects(bool depthPresent) {
|
||||
int physical_device = 0; // TODO
|
||||
bool VulkanContext::InitObjects() {
|
||||
InitQueue();
|
||||
|
||||
// Create frame data
|
||||
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
cmd_pool_info.queueFamilyIndex = graphics_queue_family_index_;
|
||||
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
|
||||
res = vkCreateCommandPool(device_, &cmd_pool_info, NULL, &frame_[i].cmdPool);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
cmd_alloc.commandPool = frame_[i].cmdPool;
|
||||
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd_alloc.commandBufferCount = 2;
|
||||
|
||||
VkCommandBuffer cmdBuf[2];
|
||||
res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf);
|
||||
assert(res == VK_SUCCESS);
|
||||
frame_[i].cmdBuf = cmdBuf[0];
|
||||
frame_[i].cmdInit = cmdBuf[1];
|
||||
frame_[i].fence = CreateFence(true); // So it can be instantly waited on
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = GetInitCommandBuffer();
|
||||
if (!InitSwapchain(cmd)) {
|
||||
if (!InitSwapchain()) {
|
||||
return false;
|
||||
}
|
||||
InitDepthStencilBuffer(cmd);
|
||||
|
||||
InitSurfaceRenderPass(depthPresent, true);
|
||||
InitFramebuffers(depthPresent);
|
||||
|
||||
// The init command buffer will be executed as part of the first frame.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyObjects() {
|
||||
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
|
||||
VkCommandBuffer cmdBuf[2];
|
||||
cmdBuf[0] = frame_[i].cmdBuf;
|
||||
cmdBuf[1] = frame_[i].cmdInit;
|
||||
vkFreeCommandBuffers(device_, frame_[i].cmdPool, 2, cmdBuf);
|
||||
vkDestroyCommandPool(device_, frame_[i].cmdPool, nullptr);
|
||||
}
|
||||
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
|
||||
vkDestroyFence(device_, frame_[i].fence, nullptr);
|
||||
}
|
||||
|
||||
DestroyFramebuffers();
|
||||
DestroySurfaceRenderPass();
|
||||
DestroyDepthStencilBuffer();
|
||||
DestroySwapChain();
|
||||
if (swapchain_ != VK_NULL_HANDLE)
|
||||
vkDestroySwapchainKHR(device_, swapchain_, nullptr);
|
||||
swapchain_ = VK_NULL_HANDLE;
|
||||
|
||||
// If there happen to be any pending deletes, now is a good time.
|
||||
for (int i = 0; i < ARRAY_SIZE(frame_); i++) {
|
||||
|
@ -670,78 +511,6 @@ void VulkanContext::DestroyDebugMsgCallback() {
|
|||
}
|
||||
}
|
||||
|
||||
void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
bool U_ASSERT_ONLY pass;
|
||||
|
||||
const VkFormat depth_format = deviceInfo_.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;
|
||||
image_info.extent.width = width_;
|
||||
image_info.extent.height = height_;
|
||||
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 = NULL;
|
||||
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;
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void VulkanContext::InitSurfaceWin32(HINSTANCE conn, HWND wnd) {
|
||||
connection = conn;
|
||||
|
@ -855,39 +624,32 @@ void VulkanContext::InitQueue() {
|
|||
// supported format will be returned.
|
||||
if (formatCount == 0 || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) {
|
||||
ILOG("swapchain_format: Falling back to B8G8R8A8_UNORM");
|
||||
swapchain_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
} else {
|
||||
swapchain_format = VK_FORMAT_UNDEFINED;
|
||||
swapchainFormat_ = VK_FORMAT_UNDEFINED;
|
||||
for (uint32_t i = 0; i < formatCount; ++i) {
|
||||
if (surfFormats[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
|
||||
swapchain_format = surfFormats[i].format;
|
||||
swapchainFormat_ = surfFormats[i].format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (swapchain_format == VK_FORMAT_UNDEFINED) {
|
||||
if (swapchainFormat_ == VK_FORMAT_UNDEFINED) {
|
||||
// Okay, take the first one then.
|
||||
swapchain_format = surfFormats[0].format;
|
||||
swapchainFormat_ = surfFormats[0].format;
|
||||
}
|
||||
ILOG("swapchain_format: %d (/%d)", swapchain_format, formatCount);
|
||||
ILOG("swapchain_format: %d (/%d)", swapchainFormat_, formatCount);
|
||||
}
|
||||
delete[] surfFormats;
|
||||
|
||||
vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_);
|
||||
ILOG("gfx_queue_: %p", gfx_queue_);
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &acquireSemaphore_);
|
||||
assert(res == VK_SUCCESS);
|
||||
res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
||||
bool VulkanContext::InitSwapchain() {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkSurfaceCapabilitiesKHR surfCapabilities;
|
||||
|
||||
|
@ -967,7 +729,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
|||
VkSwapchainCreateInfoKHR swap_chain_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
|
||||
swap_chain_info.surface = surface_;
|
||||
swap_chain_info.minImageCount = desiredNumberOfSwapChainImages;
|
||||
swap_chain_info.imageFormat = swapchain_format;
|
||||
swap_chain_info.imageFormat = swapchainFormat_;
|
||||
swap_chain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
swap_chain_info.imageExtent.width = swapChainExtent.width;
|
||||
swap_chain_info.imageExtent.height = swapChainExtent.height;
|
||||
|
@ -988,141 +750,15 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
|||
swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||||
}
|
||||
|
||||
res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swap_chain_);
|
||||
res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swapchain_);
|
||||
assert(res == VK_SUCCESS);
|
||||
if (res != VK_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, nullptr);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
ILOG("Vulkan swapchain image count: %d", swapchainImageCount);
|
||||
|
||||
VkImage* swapchainImages = new VkImage[swapchainImageCount];
|
||||
assert(swapchainImages);
|
||||
res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, swapchainImages);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
for (uint32_t i = 0; i < swapchainImageCount; i++) {
|
||||
swap_chain_buffer sc_buffer;
|
||||
|
||||
VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
color_image_view.format = swapchain_format;
|
||||
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;
|
||||
|
||||
sc_buffer.image = swapchainImages[i];
|
||||
|
||||
// TODO: 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(cmd, 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);
|
||||
|
||||
color_image_view.image = sc_buffer.image;
|
||||
|
||||
res = vkCreateImageView(device_, &color_image_view, NULL, &sc_buffer.view);
|
||||
swapChainBuffers.push_back(sc_buffer);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
delete[] swapchainImages;
|
||||
current_buffer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanContext::InitSurfaceRenderPass(bool include_depth, bool clear) {
|
||||
// Need attachments for render target and depth buffer
|
||||
VkAttachmentDescription attachments[2]{};
|
||||
attachments[0].format = swapchain_format;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].flags = 0;
|
||||
|
||||
if (include_depth) {
|
||||
attachments[1].format = depth.format;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[1].stencilLoadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].flags = 0;
|
||||
}
|
||||
|
||||
VkAttachmentReference color_reference{};
|
||||
color_reference.attachment = 0;
|
||||
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depth_reference{};
|
||||
depth_reference.attachment = 1;
|
||||
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.flags = 0;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &color_reference;
|
||||
subpass.pResolveAttachments = nullptr;
|
||||
subpass.pDepthStencilAttachment = include_depth ? &depth_reference : nullptr;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp_info.attachmentCount = include_depth ? 2 : 1;
|
||||
rp_info.pAttachments = attachments;
|
||||
rp_info.subpassCount = 1;
|
||||
rp_info.pSubpasses = &subpass;
|
||||
rp_info.dependencyCount = 0;
|
||||
rp_info.pDependencies = nullptr;
|
||||
|
||||
VkResult U_ASSERT_ONLY res = vkCreateRenderPass(device_, &rp_info, nullptr, &surface_render_pass_);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanContext::InitFramebuffers(bool include_depth) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkImageView attachments[2];
|
||||
attachments[1] = depth.view;
|
||||
|
||||
ILOG("InitFramebuffers: %dx%d", width_, height_);
|
||||
VkFramebufferCreateInfo fb_info{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fb_info.renderPass = surface_render_pass_;
|
||||
fb_info.attachmentCount = include_depth ? 2 : 1;
|
||||
fb_info.pAttachments = attachments;
|
||||
fb_info.width = width_;
|
||||
fb_info.height = height_;
|
||||
fb_info.layers = 1;
|
||||
|
||||
framebuffers_.resize(swapchainImageCount);
|
||||
|
||||
for (uint32_t i = 0; i < swapchainImageCount; i++) {
|
||||
attachments[0] = swapChainBuffers[i].view;
|
||||
res = vkCreateFramebuffer(device_, &fb_info, nullptr, &framebuffers_[i]);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
VkFence VulkanContext::CreateFence(bool presignalled) {
|
||||
VkFence fence;
|
||||
VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
|
@ -1131,49 +767,6 @@ VkFence VulkanContext::CreateFence(bool presignalled) {
|
|||
return fence;
|
||||
}
|
||||
|
||||
void VulkanContext::WaitAndResetFence(VkFence fence) {
|
||||
vkWaitForFences(device_, 1, &fence, true, UINT64_MAX);
|
||||
vkResetFences(device_, 1, &fence);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyDepthStencilBuffer() {
|
||||
if (depth.view != VK_NULL_HANDLE)
|
||||
vkDestroyImageView(device_, depth.view, nullptr);
|
||||
if (depth.image != VK_NULL_HANDLE)
|
||||
vkDestroyImage(device_, depth.image, nullptr);
|
||||
if (depth.mem != VK_NULL_HANDLE)
|
||||
vkFreeMemory(device_, depth.mem, nullptr);
|
||||
|
||||
depth.view = VK_NULL_HANDLE;
|
||||
depth.image = VK_NULL_HANDLE;
|
||||
depth.mem = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VulkanContext::DestroySwapChain() {
|
||||
for (uint32_t i = 0; i < swapchainImageCount; i++) {
|
||||
vkDestroyImageView(device_, swapChainBuffers[i].view, nullptr);
|
||||
}
|
||||
if (swap_chain_ != VK_NULL_HANDLE)
|
||||
vkDestroySwapchainKHR(device_, swap_chain_, nullptr);
|
||||
swap_chain_ = VK_NULL_HANDLE;
|
||||
swapChainBuffers.clear();
|
||||
vkDestroySemaphore(device_, acquireSemaphore_, nullptr);
|
||||
vkDestroySemaphore(device_, renderingCompleteSemaphore_, nullptr);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyFramebuffers() {
|
||||
for (uint32_t i = 0; i < framebuffers_.size(); i++) {
|
||||
vkDestroyFramebuffer(device_, framebuffers_[i], nullptr);
|
||||
}
|
||||
framebuffers_.clear();
|
||||
}
|
||||
|
||||
void VulkanContext::DestroySurfaceRenderPass() {
|
||||
if (surface_render_pass_ != VK_NULL_HANDLE)
|
||||
vkDestroyRenderPass(device_, surface_render_pass_, nullptr);
|
||||
surface_render_pass_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyDevice() {
|
||||
vkDestroyDevice(device_, nullptr);
|
||||
device_ = nullptr;
|
||||
|
|
|
@ -195,8 +195,7 @@ private:
|
|||
std::vector<Callback> callbacks_;
|
||||
};
|
||||
|
||||
// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc.
|
||||
// Optionally, it can create a depth buffer for you as well.
|
||||
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
|
||||
class VulkanContext {
|
||||
public:
|
||||
VulkanContext();
|
||||
|
@ -227,19 +226,12 @@ public:
|
|||
void ReinitSurfaceAndroid(int width, int height);
|
||||
#endif
|
||||
void InitQueue();
|
||||
bool InitObjects(bool depthPresent);
|
||||
bool InitSwapchain(VkCommandBuffer cmd);
|
||||
void InitSurfaceRenderPass(bool include_depth, bool clear);
|
||||
void InitFramebuffers(bool include_depth);
|
||||
void InitDepthStencilBuffer(VkCommandBuffer cmd);
|
||||
bool InitObjects();
|
||||
bool InitSwapchain();
|
||||
|
||||
// Also destroys the surface.
|
||||
void DestroyObjects();
|
||||
|
||||
void DestroySurfaceRenderPass();
|
||||
void DestroyFramebuffers();
|
||||
void DestroySwapChain();
|
||||
void DestroyDepthStencilBuffer();
|
||||
void DestroyDevice();
|
||||
|
||||
void WaitUntilQueueIdle();
|
||||
|
@ -248,41 +240,17 @@ public:
|
|||
VkFence CreateFence(bool presignalled);
|
||||
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);
|
||||
|
||||
void WaitAndResetFence(VkFence fence);
|
||||
int GetBackbufferWidth() { return width_; }
|
||||
int GetBackbufferHeight() { return height_; }
|
||||
|
||||
int GetWidth() { return width_; }
|
||||
int GetHeight() { return height_; }
|
||||
|
||||
VkCommandBuffer GetInitCommandBuffer();
|
||||
|
||||
VkFramebuffer GetSurfaceFramebuffer() {
|
||||
return framebuffers_[current_buffer];
|
||||
}
|
||||
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
|
||||
VkCommandBuffer GetSurfaceCommandBuffer() {
|
||||
return frame_[curFrame_].cmdBuf;
|
||||
}
|
||||
|
||||
VkCommandBuffer BeginFrame();
|
||||
// The surface render pass is special because it has to acquire the backbuffer, and may thus "block".
|
||||
// Use the returned command buffer to enqueue commands that render to the backbuffer.
|
||||
// To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd).
|
||||
VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]);
|
||||
// May eventually need the ability to break and resume the backbuffer render pass in a few rare cases.
|
||||
void EndSurfaceRenderPass();
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
void QueueBeforeSurfaceRender(VkCommandBuffer cmd);
|
||||
|
||||
bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
|
||||
|
||||
VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr);
|
||||
void DestroyDebugMsgCallback();
|
||||
|
||||
VkRenderPass GetSurfaceRenderPass() const {
|
||||
return surface_render_pass_;
|
||||
}
|
||||
|
||||
VkPhysicalDevice GetPhysicalDevice(int n = 0) const {
|
||||
return physical_devices_[n];
|
||||
}
|
||||
|
@ -327,8 +295,19 @@ public:
|
|||
return inflightFrames_;
|
||||
}
|
||||
|
||||
int GetCurFrame() const {
|
||||
return curFrame_;
|
||||
}
|
||||
|
||||
VkSwapchainKHR GetSwapchain() const {
|
||||
return swapchain_;
|
||||
}
|
||||
VkFormat GetSwapchainFormat() const {
|
||||
return swapchainFormat_;
|
||||
}
|
||||
|
||||
enum {
|
||||
MAX_INFLIGHT_FRAMES = 2,
|
||||
MAX_INFLIGHT_FRAMES = 3,
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -340,9 +319,6 @@ private:
|
|||
|
||||
bool CheckLayers(const std::vector<LayerProperties> &layer_props, const std::vector<const char *> &layer_names) const;
|
||||
|
||||
VkSemaphore acquireSemaphore_;
|
||||
VkSemaphore renderingCompleteSemaphore_;
|
||||
|
||||
#ifdef _WIN32
|
||||
HINSTANCE connection = nullptr; // hInstance - Windows Instance
|
||||
HWND window = nullptr; // hWnd - window handle
|
||||
|
@ -380,36 +356,15 @@ private:
|
|||
// Custom collection of things that are good to know
|
||||
VulkanPhysicalDeviceInfo deviceInfo_;
|
||||
|
||||
struct swap_chain_buffer {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
};
|
||||
|
||||
// Swap chain
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int flags_ = 0;
|
||||
VkFormat swapchain_format = VK_FORMAT_UNDEFINED;
|
||||
std::vector<VkFramebuffer> framebuffers_;
|
||||
uint32_t swapchainImageCount = 0;
|
||||
VkSwapchainKHR swap_chain_ = VK_NULL_HANDLE;
|
||||
std::vector<swap_chain_buffer> swapChainBuffers;
|
||||
|
||||
int inflightFrames_ = MAX_INFLIGHT_FRAMES;
|
||||
|
||||
// Manages flipping command buffers for the backbuffer render pass.
|
||||
// It is recommended to do the same for other rendering passes.
|
||||
struct FrameData {
|
||||
FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {}
|
||||
|
||||
VkFence fence;
|
||||
bool hasInitCommands;
|
||||
|
||||
// TODO: Move to frame data
|
||||
VkCommandPool cmdPool;
|
||||
VkCommandBuffer cmdInit;
|
||||
VkCommandBuffer cmdBuf;
|
||||
|
||||
FrameData() {}
|
||||
VulkanDeleteList deleteList;
|
||||
};
|
||||
FrameData frame_[MAX_INFLIGHT_FRAMES];
|
||||
|
@ -421,15 +376,9 @@ private:
|
|||
|
||||
std::vector<VkDebugReportCallbackEXT> msg_callbacks;
|
||||
|
||||
struct {
|
||||
VkFormat format;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mem = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
} depth;
|
||||
VkSwapchainKHR swapchain_;
|
||||
VkFormat swapchainFormat_;
|
||||
|
||||
VkRenderPass surface_render_pass_ = VK_NULL_HANDLE;
|
||||
uint32_t current_buffer = 0;
|
||||
uint32_t queue_count = 0;
|
||||
|
||||
VkPhysicalDeviceFeatures featuresAvailable_;
|
||||
|
@ -438,15 +387,15 @@ private:
|
|||
std::vector<VkCommandBuffer> cmdQueue_;
|
||||
};
|
||||
|
||||
// Stand-alone utility functions
|
||||
void VulkanBeginCommandBuffer(VkCommandBuffer cmd);
|
||||
|
||||
// Detailed control.
|
||||
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask,
|
||||
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
|
||||
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
|
||||
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask);
|
||||
|
||||
void TransitionFromPresent(VkCommandBuffer cmd, VkImage image);
|
||||
void TransitionToPresent(VkCommandBuffer cmd, VkImage image);
|
||||
|
||||
// GLSL compiler
|
||||
void init_glslang();
|
||||
void finalize_glslang();
|
||||
|
|
|
@ -84,11 +84,9 @@ uint8_t *VulkanTexture::Lock(int level, int *rowPitch) {
|
|||
return (uint8_t *)data;
|
||||
}
|
||||
|
||||
void VulkanTexture::Unlock() {
|
||||
void VulkanTexture::Unlock(VkCommandBuffer cmd) {
|
||||
vkUnmapMemory(vulkan_->GetDevice(), mappableMemory);
|
||||
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
|
||||
// if we already have an image, queue it for destruction and forget it.
|
||||
Wipe();
|
||||
|
||||
|
@ -227,11 +225,9 @@ static bool IsDepthStencilFormat(VkFormat format) {
|
|||
}
|
||||
}
|
||||
|
||||
bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
|
||||
bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
|
||||
Wipe();
|
||||
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
|
||||
tex_width = w;
|
||||
tex_height = h;
|
||||
numMips_ = numMips;
|
||||
|
@ -336,7 +332,7 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
|
|||
return true;
|
||||
}
|
||||
|
||||
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
|
||||
void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
|
||||
VkBufferImageCopy copy_region = {};
|
||||
copy_region.bufferOffset = offset;
|
||||
copy_region.bufferRowLength = (uint32_t)rowLength;
|
||||
|
@ -349,12 +345,10 @@ void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buf
|
|||
copy_region.imageSubresource.baseArrayLayer = 0;
|
||||
copy_region.imageSubresource.layerCount = 1;
|
||||
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||||
}
|
||||
|
||||
void VulkanTexture::EndCreate() {
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
void VulkanTexture::EndCreate(VkCommandBuffer cmd) {
|
||||
TransitionImageLayout2(cmd, image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
|
@ -362,8 +356,7 @@ void VulkanTexture::EndCreate() {
|
|||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
void VulkanTexture::TransitionForUpload() {
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
void VulkanTexture::TransitionForUpload(VkCommandBuffer cmd) {
|
||||
TransitionImageLayout2(cmd, image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
|
|
|
@ -26,16 +26,16 @@ public:
|
|||
// been called.
|
||||
VkResult Create(int w, int h, VkFormat format);
|
||||
uint8_t *Lock(int level, int *rowPitch);
|
||||
void Unlock();
|
||||
void Unlock(VkCommandBuffer cmd);
|
||||
|
||||
// Fast uploads from buffer. Mipmaps supported.
|
||||
// Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip.
|
||||
// When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
|
||||
bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
|
||||
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
|
||||
void EndCreate();
|
||||
bool CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
|
||||
void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
|
||||
void EndCreate(VkCommandBuffer cmd);
|
||||
|
||||
void TransitionForUpload();
|
||||
void TransitionForUpload(VkCommandBuffer cmd);
|
||||
|
||||
int GetNumMips() const { return numMips_; }
|
||||
void Destroy();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "math/dataconv.h"
|
||||
|
@ -13,14 +15,14 @@
|
|||
|
||||
// The cool thing is that you can Flush on a different thread than you record the commands on!
|
||||
|
||||
enum VkRenderCmd : uint8_t {
|
||||
VKR_STENCIL,
|
||||
VKR_BLEND,
|
||||
VKR_VIEWPORT,
|
||||
VKR_SCISSOR,
|
||||
VKR_CLEAR,
|
||||
VKR_DRAW,
|
||||
VKR_DRAW_INDEXED,
|
||||
enum class VkRenderCmd : uint8_t {
|
||||
STENCIL,
|
||||
BLEND,
|
||||
VIEWPORT,
|
||||
SCISSOR,
|
||||
CLEAR,
|
||||
DRAW,
|
||||
DRAW_INDEXED,
|
||||
};
|
||||
|
||||
struct VkRenderData {
|
||||
|
@ -80,47 +82,160 @@ struct VkRenderData {
|
|||
};
|
||||
};
|
||||
|
||||
struct VKRRenderPass {
|
||||
VkFramebuffer framebuffer;
|
||||
uint32_t clearColor;
|
||||
float clearZ;
|
||||
int clearStencil;
|
||||
int clearMask = 0; // VK_IMAGE_ASPECT_COLOR_BIT etc
|
||||
int dontCareMask = 0;
|
||||
int numDraws;
|
||||
enum class VKStepType : uint8_t {
|
||||
RENDER,
|
||||
COPY,
|
||||
BLIT,
|
||||
READBACK,
|
||||
};
|
||||
|
||||
class VKRFramebuffer;
|
||||
|
||||
enum class RenderPassAction {
|
||||
DONT_CARE,
|
||||
CLEAR,
|
||||
KEEP,
|
||||
};
|
||||
|
||||
struct VKRStep {
|
||||
VKRStep(VKStepType _type) : stepType(_type) {}
|
||||
VKStepType stepType;
|
||||
std::vector<VkRenderData> commands;
|
||||
union {
|
||||
struct {
|
||||
VKRFramebuffer *framebuffer;
|
||||
RenderPassAction color;
|
||||
RenderPassAction depthStencil;
|
||||
uint32_t clearColor;
|
||||
float clearDepth;
|
||||
int clearStencil;
|
||||
int numDraws;
|
||||
VkImageLayout finalColorLayout;
|
||||
} render;
|
||||
struct {
|
||||
VKRFramebuffer *src;
|
||||
VKRFramebuffer *dst;
|
||||
VkRect2D srcRect;
|
||||
VkOffset2D dstPos;
|
||||
int aspectMask;
|
||||
} copy;
|
||||
struct {
|
||||
VKRFramebuffer *src;
|
||||
VKRFramebuffer *dst;
|
||||
VkRect2D srcRect;
|
||||
VkRect2D dstRect;
|
||||
int aspectMask;
|
||||
VkFilter filter;
|
||||
} blit;
|
||||
struct {
|
||||
VKRFramebuffer *src;
|
||||
void *destPtr;
|
||||
VkRect2D srcRect;
|
||||
} readback;
|
||||
};
|
||||
};
|
||||
|
||||
// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine
|
||||
// to let them have individual non-pooled allocations. Until it's not fine. We'll see.
|
||||
struct VKImage {
|
||||
VkImage image;
|
||||
VkImageView imageView;
|
||||
VkDeviceMemory memory;
|
||||
VkImageLayout layout;
|
||||
};
|
||||
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color);
|
||||
|
||||
class VKRFramebuffer {
|
||||
public:
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height) : vulkan_(vk) {
|
||||
width = _width;
|
||||
height = _height;
|
||||
|
||||
CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true);
|
||||
CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false);
|
||||
|
||||
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
VkImageView views[2]{};
|
||||
|
||||
fbci.renderPass = renderPass;
|
||||
fbci.attachmentCount = 2;
|
||||
fbci.pAttachments = views;
|
||||
views[0] = color.imageView;
|
||||
views[1] = depth.imageView;
|
||||
fbci.width = width;
|
||||
fbci.height = height;
|
||||
fbci.layers = 1;
|
||||
|
||||
vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf);
|
||||
}
|
||||
~VKRFramebuffer() {
|
||||
vulkan_->Delete().QueueDeleteImage(color.image);
|
||||
vulkan_->Delete().QueueDeleteImage(depth.image);
|
||||
vulkan_->Delete().QueueDeleteImageView(color.imageView);
|
||||
vulkan_->Delete().QueueDeleteImageView(depth.imageView);
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(color.memory);
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory);
|
||||
|
||||
vulkan_->Delete().QueueDeleteFramebuffer(framebuf);
|
||||
}
|
||||
|
||||
int numShadows = 1; // TODO: Support this.
|
||||
|
||||
VkFramebuffer framebuf = VK_NULL_HANDLE;
|
||||
VKImage color{};
|
||||
VKImage depth{};
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
};
|
||||
|
||||
class VulkanRenderManager {
|
||||
public:
|
||||
VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) {}
|
||||
VulkanRenderManager(VulkanContext *vulkan);
|
||||
~VulkanRenderManager();
|
||||
|
||||
void ThreadFunc();
|
||||
|
||||
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
|
||||
void BeginFrameWrites();
|
||||
void EndFrame();
|
||||
|
||||
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb);
|
||||
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment);
|
||||
|
||||
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint);
|
||||
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter);
|
||||
|
||||
void BeginRenderPass();
|
||||
|
||||
void SetViewport(const VkViewport &vp) {
|
||||
VkRenderData data{ VKR_VIEWPORT };
|
||||
_dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER);
|
||||
VkRenderData data{ VkRenderCmd::VIEWPORT };
|
||||
data.viewport.vp = vp;
|
||||
curRp_->commands.push_back(data);
|
||||
curStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetScissor(const VkRect2D &rc) {
|
||||
VkRenderData data{ VKR_SCISSOR };
|
||||
_dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER);
|
||||
VkRenderData data{ VkRenderCmd::SCISSOR };
|
||||
data.scissor.scissor = rc;
|
||||
curRp_->commands.push_back(data);
|
||||
curStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetBlendFactor(float color[4]) {
|
||||
VkRenderData data{ VKR_BLEND };
|
||||
_dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER);
|
||||
VkRenderData data{ VkRenderCmd::BLEND };
|
||||
CopyFloat4(data.blendColor.color, color);
|
||||
curRp_->commands.push_back(data);
|
||||
curStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);
|
||||
|
||||
void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) {
|
||||
VkRenderData data{ VKR_DRAW };
|
||||
_dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER);
|
||||
VkRenderData data{ VkRenderCmd::DRAW };
|
||||
data.draw.count = count;
|
||||
data.draw.pipeline = pipeline;
|
||||
data.draw.pipelineLayout = layout;
|
||||
|
@ -130,12 +245,13 @@ public:
|
|||
data.draw.numUboOffsets = numUboOffsets;
|
||||
for (int i = 0; i < numUboOffsets; i++)
|
||||
data.draw.uboOffsets[i] = uboOffsets[i];
|
||||
curRp_->commands.push_back(data);
|
||||
curRp_->numDraws++;
|
||||
curStep_->commands.push_back(data);
|
||||
curStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) {
|
||||
VkRenderData data{ VKR_DRAW };
|
||||
_dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER);
|
||||
VkRenderData data{ VkRenderCmd::DRAW_INDEXED };
|
||||
data.drawIndexed.count = count;
|
||||
data.drawIndexed.pipeline = pipeline;
|
||||
data.drawIndexed.pipelineLayout = layout;
|
||||
|
@ -148,21 +264,103 @@ public:
|
|||
for (int i = 0; i < numUboOffsets; i++)
|
||||
data.drawIndexed.uboOffsets[i] = uboOffsets[i];
|
||||
data.drawIndexed.indexType = indexType;
|
||||
curRp_->commands.push_back(data);
|
||||
curRp_->numDraws++;
|
||||
curStep_->commands.push_back(data);
|
||||
curStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
// Can run on a different thread! Just make sure to use BeginFrameWrites.
|
||||
void Flush(VkCommandBuffer cmd);
|
||||
void Flush();
|
||||
|
||||
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
|
||||
void Sync(VkCommandBuffer cmd);
|
||||
void Sync();
|
||||
|
||||
std::vector<VKRRenderPass *> renderPasses_;
|
||||
VKRRenderPass *curRp_;
|
||||
std::vector<VKRStep *> steps_;
|
||||
std::vector<VKRStep *> stepsOnThread_;
|
||||
std::mutex rpLock_;
|
||||
|
||||
VKRStep *curStep_;
|
||||
|
||||
VkCommandBuffer GetInitCmd();
|
||||
VkCommandBuffer GetSurfaceCommandBuffer() {
|
||||
return frameData_[vulkan_->GetCurFrame()].mainCmd;
|
||||
}
|
||||
VkRenderPass GetSurfaceRenderPass() const {
|
||||
return backbufferRenderPass_;
|
||||
}
|
||||
VkRenderPass GetRenderPass(int i) const {
|
||||
return renderPasses_[i];
|
||||
}
|
||||
|
||||
private:
|
||||
void InitFramebuffers();
|
||||
void InitSurfaceRenderPass();
|
||||
void InitRenderpasses();
|
||||
void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
|
||||
|
||||
// The surface render pass is special because it has to acquire the backbuffer, and may thus "block".
|
||||
// Use the returned command buffer to enqueue commands that render to the backbuffer.
|
||||
// To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd).
|
||||
void BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value);
|
||||
// May eventually need the ability to break and resume the backbuffer render pass in a few rare cases.
|
||||
void EndSurfaceRenderPass(VkCommandBuffer cmd);
|
||||
|
||||
void EndCurrentRenderpass(VkCommandBuffer cmd);
|
||||
|
||||
void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
|
||||
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
|
||||
inline int RPIndex(RenderPassAction color, RenderPassAction depth) {
|
||||
return (int)depth * 3 + (int)color;
|
||||
}
|
||||
|
||||
static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect);
|
||||
static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect);
|
||||
|
||||
VkSemaphore acquireSemaphore_;
|
||||
VkSemaphore renderingCompleteSemaphore;
|
||||
|
||||
// Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
|
||||
// TODO: Create these on demand.
|
||||
VkRenderPass renderPasses_[9];
|
||||
|
||||
struct FrameData {
|
||||
VkFence fence;
|
||||
VkCommandPool cmdPool;
|
||||
VkCommandBuffer initCmd;
|
||||
VkCommandBuffer mainCmd;
|
||||
bool hasInitCommands;
|
||||
};
|
||||
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
int curWidth_;
|
||||
int curHeight_;
|
||||
};
|
||||
|
||||
std::thread submissionThread;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condVar_;
|
||||
|
||||
struct SwapchainImageData {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
};
|
||||
std::vector<VkFramebuffer> framebuffers_;
|
||||
std::vector<SwapchainImageData> swapchainImages_;
|
||||
uint32_t swapchainImageCount;
|
||||
uint32_t current_buffer = 0;
|
||||
VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE;
|
||||
struct DepthBufferInfo {
|
||||
VkFormat format;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mem = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
};
|
||||
DepthBufferInfo depth_;
|
||||
// Interpreter state
|
||||
VkFramebuffer curFramebuffer_;
|
||||
VkRenderPass curRenderPass_;
|
||||
|
||||
};
|
||||
|
|
|
@ -94,7 +94,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra
|
|||
|
||||
InitDeviceObjects();
|
||||
|
||||
tessDataTransfer = new TessellationDataTransferVulkan(vulkan);
|
||||
tessDataTransfer = new TessellationDataTransferVulkan(vulkan, draw);
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::InitDeviceObjects() {
|
||||
|
@ -294,10 +294,11 @@ void DrawEngineVulkan::BeginFrame() {
|
|||
|
||||
// TODO : Find a better place to do this.
|
||||
if (!nullTexture_) {
|
||||
VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
nullTexture_ = new VulkanTexture(vulkan_);
|
||||
int w = 8;
|
||||
int h = 8;
|
||||
nullTexture_->CreateDirect(w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
uint32_t bindOffset;
|
||||
VkBuffer bindBuf;
|
||||
|
@ -308,8 +309,8 @@ void DrawEngineVulkan::BeginFrame() {
|
|||
data[y*w + x] = 0; // black
|
||||
}
|
||||
}
|
||||
nullTexture_->UploadMip(0, w, h, bindBuf, bindOffset, w);
|
||||
nullTexture_->EndCreate();
|
||||
nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w);
|
||||
nullTexture_->EndCreate(cmdInit);
|
||||
}
|
||||
|
||||
DirtyAllUBOs();
|
||||
|
@ -643,13 +644,15 @@ void DrawEngineVulkan::DoFlush() {
|
|||
// TODO: Should be enough to update this once per frame?
|
||||
gpuStats.numTrackedVertexArrays = (int)vai_.size();
|
||||
|
||||
/*
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
if (cmd != lastCmd_) {
|
||||
lastPipeline_ = nullptr;
|
||||
lastCmd_ = cmd;
|
||||
// Since we have a new cmdbuf, dirty our dynamic state so it gets re-set.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE);
|
||||
}
|
||||
}*/
|
||||
VkCommandBuffer cmd = VK_NULL_HANDLE;
|
||||
|
||||
VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS);
|
||||
if (!rp)
|
||||
|
@ -1087,11 +1090,12 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) {
|
|||
void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) {
|
||||
int rowPitch;
|
||||
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
// Position
|
||||
if (prevSize < size) {
|
||||
prevSize = size;
|
||||
|
||||
data_tex[0]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
data_tex[0]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
pos = (float *)data_tex[0]->Lock(0, &rowPitch);
|
||||
|
||||
|
@ -1100,7 +1104,7 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po
|
|||
if (prevSizeTex < size) {
|
||||
prevSizeTex = size;
|
||||
|
||||
data_tex[1]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
data_tex[1]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
tex = (float *)data_tex[1]->Lock(0, &rowPitch);
|
||||
}
|
||||
|
@ -1110,19 +1114,20 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po
|
|||
if (prevSizeCol < sizeColor) {
|
||||
prevSizeCol = sizeColor;
|
||||
|
||||
data_tex[2]->CreateDirect(sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
data_tex[2]->CreateDirect(cmd, sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
col = (float *)data_tex[2]->Lock(0, &rowPitch);
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) {
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
// Position
|
||||
data_tex[0]->Unlock();
|
||||
data_tex[0]->Unlock(cmd);
|
||||
|
||||
// Texcoords
|
||||
if (hasTexCoords)
|
||||
data_tex[1]->Unlock();
|
||||
data_tex[1]->Unlock(cmd);
|
||||
|
||||
// Color
|
||||
data_tex[2]->Unlock();
|
||||
data_tex[2]->Unlock(cmd);
|
||||
}
|
||||
|
|
|
@ -255,14 +255,15 @@ private:
|
|||
// Hardware tessellation
|
||||
class TessellationDataTransferVulkan : public TessellationDataTransfer {
|
||||
private:
|
||||
VulkanContext *vulkan;
|
||||
VulkanContext *vulkan_;
|
||||
Draw::DrawContext *draw_;
|
||||
VulkanTexture *data_tex[3];
|
||||
VkSampler sampler;
|
||||
public:
|
||||
TessellationDataTransferVulkan(VulkanContext *vulkan)
|
||||
: TessellationDataTransfer(), vulkan(vulkan), data_tex(), sampler() {
|
||||
TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw)
|
||||
: TessellationDataTransfer(), vulkan_(vulkan), draw_(draw), data_tex(), sampler() {
|
||||
for (int i = 0; i < 3; i++)
|
||||
data_tex[i] = new VulkanTexture(vulkan);
|
||||
data_tex[i] = new VulkanTexture(vulkan_);
|
||||
|
||||
CreateSampler();
|
||||
}
|
||||
|
@ -270,7 +271,7 @@ private:
|
|||
for (int i = 0; i < 3; i++)
|
||||
delete data_tex[i];
|
||||
|
||||
vulkan->Delete().QueueDeleteSampler(sampler);
|
||||
vulkan_->Delete().QueueDeleteSampler(sampler);
|
||||
}
|
||||
void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override;
|
||||
void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) override;
|
||||
|
@ -301,7 +302,7 @@ private:
|
|||
samp.minLod = 0.0f;
|
||||
samp.mipLodBias = 0.0f;
|
||||
|
||||
VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &sampler);
|
||||
VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &sampler);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "Common/Vulkan/VulkanMemory.h"
|
||||
#include "Common/Vulkan/VulkanImage.h"
|
||||
#include "Common/Vulkan/VulkanRenderManager.h"
|
||||
#include "Common/ColorConv.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
@ -131,7 +132,7 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
|
|||
assert(vsBasicTex_ != VK_NULL_HANDLE);
|
||||
|
||||
// Prime the 2D pipeline cache.
|
||||
vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_);
|
||||
vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_);
|
||||
vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_);
|
||||
|
||||
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
|
@ -234,13 +235,15 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
|||
drawPixelsTex_ = nullptr;
|
||||
}
|
||||
|
||||
VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
|
||||
if (!drawPixelsTex_) {
|
||||
drawPixelsTex_ = new VulkanTexture(vulkan_);
|
||||
drawPixelsTex_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
// Initialize backbuffer texture for DrawPixels
|
||||
drawPixelsTexFormat_ = srcPixelFormat;
|
||||
} else {
|
||||
drawPixelsTex_->TransitionForUpload();
|
||||
drawPixelsTex_->TransitionForUpload(initCmd);
|
||||
}
|
||||
|
||||
// TODO: We can just change the texture format and flip some bits around instead of this.
|
||||
|
@ -297,25 +300,22 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
|||
|
||||
VkBuffer buffer;
|
||||
size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer);
|
||||
drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width);
|
||||
drawPixelsTex_->EndCreate();
|
||||
drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width);
|
||||
drawPixelsTex_->EndCreate(initCmd);
|
||||
|
||||
overrideImageView_ = drawPixelsTex_->GetImageView();
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) {
|
||||
VkViewport vp;
|
||||
vp.minDepth = 0.0;
|
||||
vp.maxDepth = 1.0;
|
||||
vp.x = (float)x;
|
||||
vp.y = (float)y;
|
||||
vp.width = (float)w;
|
||||
vp.height = (float)h;
|
||||
|
||||
Draw::Viewport vp;
|
||||
vp.MinDepth = 0.0;
|
||||
vp.MaxDepth = 1.0;
|
||||
vp.TopLeftX = (float)x;
|
||||
vp.TopLeftY = (float)y;
|
||||
vp.Width = (float)w;
|
||||
vp.Height = (float)h;
|
||||
// Since we're about to override it.
|
||||
draw_->FlushState();
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||
draw_->SetViewports(1, &vp);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) {
|
||||
|
@ -359,18 +359,15 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
|
|||
// TODO: Should probably use draw_ directly and not go low level
|
||||
|
||||
VulkanPushBuffer *push = frameData_[curFrame_].push_;
|
||||
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
|
||||
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW);
|
||||
if ((flags & DRAWTEX_KEEP_TEX) == 0)
|
||||
overrideImageView_ = VK_NULL_HANDLE;
|
||||
vulkan2D_.BindDescriptorSet(cmd, view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_);
|
||||
VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
|
||||
VkBuffer vbuffer;
|
||||
VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset);
|
||||
vkCmdDraw(cmd, 4, 1, 0, 0);
|
||||
renderManager->Draw(cur2DPipeline_, vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Bind2DShader() {
|
||||
|
@ -645,18 +642,6 @@ void FramebufferManagerVulkan::BeginFrameVulkan() {
|
|||
|
||||
frame.push_->Reset();
|
||||
frame.push_->Begin(vulkan_);
|
||||
|
||||
if (!useBufferedRendering_) {
|
||||
// TODO: This hackery should not be necessary. Is it? Need to check.
|
||||
// We only use a single command buffer in this case.
|
||||
VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer();
|
||||
VkRect2D scissor;
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ };
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
} else {
|
||||
// Each render pass will set up scissor again.
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::EndFrame() {
|
||||
|
|
|
@ -466,6 +466,7 @@ VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) {
|
|||
void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceImages) {
|
||||
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
|
||||
|
||||
VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
// For the estimate, we assume cluts always point to 8888 for simplicity.
|
||||
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
|
||||
|
||||
|
@ -588,7 +589,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
break;
|
||||
}
|
||||
|
||||
bool allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
if (!allocSuccess && !lowMemoryMode_) {
|
||||
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
|
||||
lowMemoryMode_ = true;
|
||||
|
@ -607,7 +608,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
scaleFactor = 1;
|
||||
actualFmt = dstFmt;
|
||||
|
||||
allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
}
|
||||
|
||||
if (!allocSuccess) {
|
||||
|
@ -615,7 +616,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
entry->vkTex = nullptr;
|
||||
}
|
||||
} else {
|
||||
entry->vkTex->texture_->TransitionForUpload();
|
||||
entry->vkTex->texture_->TransitionForUpload(cmdInit);
|
||||
}
|
||||
lastBoundTexture = entry->vkTex;
|
||||
|
||||
|
@ -652,7 +653,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
} else {
|
||||
if (fakeMipmap) {
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt);
|
||||
entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
entry->vkTex->texture_->UploadMip(cmdInit, 0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
break;
|
||||
} else {
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt);
|
||||
|
@ -661,7 +662,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight);
|
||||
}
|
||||
}
|
||||
entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
entry->vkTex->texture_->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
}
|
||||
|
||||
if (maxLevel == 0) {
|
||||
|
@ -674,7 +675,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm
|
|||
}
|
||||
}
|
||||
|
||||
entry->vkTex->texture_->EndCreate();
|
||||
entry->vkTex->texture_->EndCreate(cmdInit);
|
||||
|
||||
gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL);
|
||||
gstate_c.SetTextureSimpleAlpha(entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN);
|
||||
|
|
|
@ -27,11 +27,11 @@ VulkanFBO::~VulkanFBO() {
|
|||
delete depthStencil_;
|
||||
}
|
||||
|
||||
void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) {
|
||||
void VulkanFBO::Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) {
|
||||
color_ = new VulkanTexture(vulkan);
|
||||
VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr);
|
||||
depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr);
|
||||
color_->CreateDirect(cmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr);
|
||||
depthStencil_->CreateDirect(cmd, width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr);
|
||||
|
||||
VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() };
|
||||
|
||||
|
@ -334,11 +334,6 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade
|
|||
}
|
||||
}
|
||||
|
||||
void Vulkan2D::BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1) {
|
||||
VkDescriptorSet descSet = GetDescriptorSet(tex1, sampler1, VK_NULL_HANDLE, VK_NULL_HANDLE);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 0, nullptr);
|
||||
}
|
||||
|
||||
VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) {
|
||||
std::vector<uint32_t> spirv;
|
||||
bool success = GLSLtoSPV(stage, code, spirv, error);
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
|
||||
// Depth-format is chosen automatically depending on hardware support.
|
||||
// Color format will be 32-bit RGBA.
|
||||
void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat);
|
||||
void Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat);
|
||||
|
||||
VulkanTexture *GetColor() { return color_; }
|
||||
VulkanTexture *GetDepthStencil() { return depthStencil_; }
|
||||
|
@ -84,15 +84,12 @@ public:
|
|||
void Shutdown();
|
||||
|
||||
VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs);
|
||||
|
||||
VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; }
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2);
|
||||
|
||||
// Simple way
|
||||
void BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1);
|
||||
|
||||
struct Vertex {
|
||||
float x, y, z;
|
||||
float u, v;
|
||||
|
|
|
@ -189,7 +189,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m
|
|||
g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions);
|
||||
}
|
||||
g_Vulkan->InitSurfaceWin32(hInst, hWnd);
|
||||
if (!g_Vulkan->InitObjects(true)) {
|
||||
if (!g_Vulkan->InitObjects()) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
@ -219,10 +219,12 @@ void WindowsVulkanContext::SwapBuffers() {
|
|||
|
||||
void WindowsVulkanContext::Resize() {
|
||||
g_Vulkan->WaitUntilQueueIdle();
|
||||
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
g_Vulkan->DestroyObjects();
|
||||
|
||||
g_Vulkan->ReinitSurfaceWin32();
|
||||
g_Vulkan->InitObjects(true);
|
||||
g_Vulkan->InitObjects();
|
||||
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
}
|
||||
|
||||
void WindowsVulkanContext::SwapInterval(int interval) {
|
||||
|
|
|
@ -105,6 +105,17 @@ inline void CopyFloat3(float dest[3], const float src[3]) {
|
|||
dest[2] = src[2];
|
||||
}
|
||||
|
||||
inline void CopyFloat4(float dest[3], const float src[3]) {
|
||||
#ifdef _M_SSE
|
||||
_mm_storeu_ps(dest, _mm_loadu_ps(src));
|
||||
#else
|
||||
dest[0] = src[0];
|
||||
dest[1] = src[1];
|
||||
dest[2] = src[2];
|
||||
dest[3] = src[3];
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void CopyFloat1To4(float dest[4], const float src) {
|
||||
#ifdef _M_SSE
|
||||
_mm_storeu_ps(dest, _mm_set_ss(src));
|
||||
|
|
|
@ -321,9 +321,10 @@ enum class NativeObject {
|
|||
BACKBUFFER_COLOR_TEX,
|
||||
BACKBUFFER_DEPTH_TEX,
|
||||
FEATURE_LEVEL,
|
||||
BACKBUFFER_RENDERPASS,
|
||||
COMPATIBLE_RENDERPASS,
|
||||
CURRENT_RENDERPASS,
|
||||
RENDERPASS_COMMANDBUFFER,
|
||||
INIT_COMMANDBUFFER,
|
||||
BOUND_TEXTURE_IMAGEVIEW,
|
||||
RENDER_MANAGER,
|
||||
};
|
||||
|
@ -678,7 +679,7 @@ public:
|
|||
}
|
||||
|
||||
virtual std::string GetInfoString(InfoField info) const = 0;
|
||||
virtual uintptr_t GetNativeObject(NativeObject obj) const = 0;
|
||||
virtual uintptr_t GetNativeObject(NativeObject obj) = 0;
|
||||
|
||||
virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0;
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
uintptr_t GetNativeObject(NativeObject obj) const override {
|
||||
uintptr_t GetNativeObject(NativeObject obj) override {
|
||||
switch (obj) {
|
||||
case NativeObject::DEVICE:
|
||||
return (uintptr_t)device_;
|
||||
|
|
|
@ -529,7 +529,7 @@ public:
|
|||
void DrawUP(const void *vdata, int vertexCount) override;
|
||||
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal);
|
||||
|
||||
uintptr_t GetNativeObject(NativeObject obj) const override {
|
||||
uintptr_t GetNativeObject(NativeObject obj) override {
|
||||
switch (obj) {
|
||||
case NativeObject::CONTEXT:
|
||||
return (uintptr_t)d3d_;
|
||||
|
|
|
@ -560,7 +560,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
uintptr_t GetNativeObject(NativeObject obj) const override {
|
||||
uintptr_t GetNativeObject(NativeObject obj) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue