mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Update VulkanContext
This commit is contained in:
parent
cfbecf5071
commit
e89a7f0c67
8 changed files with 174 additions and 269 deletions
|
@ -24,9 +24,6 @@
|
|||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
enum TextureFiltering {
|
||||
TEX_FILTER_AUTO = 1,
|
||||
TEX_FILTER_NEAREST = 2,
|
||||
|
|
|
@ -338,6 +338,7 @@
|
|||
<ClCompile Include="Vulkan\PipelineManagerVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\StateMappingVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\VertexShaderGeneratorVulkan.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -403,5 +403,6 @@
|
|||
<ClCompile Include="Vulkan\FramebufferVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
9
GPU/Vulkan/TextureCacheVulkan.cpp
Normal file
9
GPU/Vulkan/TextureCacheVulkan.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "GPU/Vulkan/TextureCacheVulkan.h"
|
||||
|
||||
bool TextureCacheVulkan::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextureCacheVulkan::DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) {
|
||||
|
||||
}
|
|
@ -29,12 +29,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) override {
|
||||
return false;
|
||||
}
|
||||
void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) override {
|
||||
|
||||
}
|
||||
bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) override;
|
||||
void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) override;
|
||||
|
||||
void DownloadFramebufferForClut(u32 clutAddr, u32 bytes) override {
|
||||
|
||||
|
|
|
@ -55,8 +55,6 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags)
|
|||
swapchainImageCount(0),
|
||||
swap_chain_(nullptr),
|
||||
cmd_pool_(nullptr),
|
||||
cmd_(nullptr),
|
||||
cmdInitActive_(false),
|
||||
dbgCreateMsgCallback(nullptr),
|
||||
dbgDestroyMsgCallback(nullptr),
|
||||
queue_count(0),
|
||||
|
@ -176,12 +174,24 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) {
|
|||
0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::GetInitCommandBuffer() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
if (!frame->hasInitCommands) {
|
||||
VulkanBeginCommandBuffer(frame->cmdInit);
|
||||
frame->hasInitCommands = true;
|
||||
}
|
||||
return frame_[curFrame_].cmdInit;
|
||||
}
|
||||
|
||||
VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
|
||||
// Make sure the command buffer from the frame before the previous has been fully executed.
|
||||
WaitAndResetFence(frame->fence);
|
||||
|
||||
// Process pending deletes.
|
||||
frame->deleteList.PerformDeletes(device_);
|
||||
|
||||
// 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?
|
||||
VkResult res = fpAcquireNextImageKHR(device_, swap_chain_,
|
||||
|
@ -198,7 +208,7 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
|
|||
begin.pNext = NULL;
|
||||
begin.flags = 0;
|
||||
begin.pInheritanceInfo = nullptr;
|
||||
res = vkBeginCommandBuffer(cmd_, &begin);
|
||||
res = vkBeginCommandBuffer(frame->cmdBuf, &begin);
|
||||
|
||||
TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
|
||||
|
||||
|
@ -219,6 +229,7 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
|
|||
}
|
||||
|
||||
void VulkanContext::WaitUntilQueueIdle() {
|
||||
// Should almost never be used
|
||||
vkQueueWaitIdle(gfx_queue_);
|
||||
}
|
||||
|
||||
|
@ -238,38 +249,6 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem
|
|||
return false;
|
||||
}
|
||||
|
||||
// Terrible for performance!
|
||||
void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue, VkCommandBuffer cmd) {
|
||||
const VkCommandBuffer cmd_bufs[] = { cmd };
|
||||
|
||||
VkFenceCreateInfo fenceInfo;
|
||||
VkFence drawFence;
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.pNext = NULL;
|
||||
fenceInfo.flags = 0;
|
||||
vkCreateFence(device, &fenceInfo, NULL, &drawFence);
|
||||
|
||||
VkSubmitInfo submit_info[1] = {};
|
||||
submit_info[0].pNext = NULL;
|
||||
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info[0].waitSemaphoreCount = waitForSemaphore ? 1 : 0;
|
||||
submit_info[0].pWaitSemaphores = waitForSemaphore ? &waitForSemaphore : NULL;
|
||||
submit_info[0].commandBufferCount = 1;
|
||||
submit_info[0].pCommandBuffers = cmd_bufs;
|
||||
submit_info[0].signalSemaphoreCount = 0;
|
||||
submit_info[0].pSignalSemaphores = NULL;
|
||||
|
||||
/* Queue the command buffer for execution */
|
||||
VkResult res = vkQueueSubmit(queue, 1, submit_info, drawFence);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
do {
|
||||
res = vkWaitForFences(device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT);
|
||||
} while (res != VK_SUCCESS && res != VK_TIMEOUT);
|
||||
|
||||
vkDestroyFence(device, drawFence, NULL);
|
||||
}
|
||||
|
||||
void VulkanContext::EndSurfaceRenderPass() {
|
||||
FrameData *frame = &frame_[curFrame_];
|
||||
vkCmdEndRenderPass(frame->cmdBuf);
|
||||
|
@ -279,19 +258,27 @@ void VulkanContext::EndSurfaceRenderPass() {
|
|||
VkResult res = vkEndCommandBuffer(frame->cmdBuf);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkSubmitInfo submit_info = {};
|
||||
submit_info.pNext = NULL;
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &acquireSemaphore;
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &frame->cmdBuf;
|
||||
submit_info.signalSemaphoreCount = 0;
|
||||
submit_info.pSignalSemaphores = NULL;
|
||||
res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence);
|
||||
assert(res == VK_SUCCESS);
|
||||
int numCmdBufs = 0;
|
||||
VkCommandBuffer cmdBufs[2];
|
||||
|
||||
// At this point we are certain that acquireSemaphore is of no further use and can be destroyed.
|
||||
if (frame->hasInitCommands) {
|
||||
vkEndCommandBuffer(frame->cmdInit);
|
||||
cmdBufs[numCmdBufs++] = frame->cmdInit;
|
||||
frame->hasInitCommands = false;
|
||||
}
|
||||
cmdBufs[numCmdBufs++] = frame->cmdBuf;
|
||||
|
||||
VkSubmitInfo submit_info[1] = {};
|
||||
submit_info[0].pNext = NULL;
|
||||
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info[0].waitSemaphoreCount = 1;
|
||||
submit_info[0].pWaitSemaphores = &acquireSemaphore;
|
||||
submit_info[0].commandBufferCount = numCmdBufs;
|
||||
submit_info[0].pCommandBuffers = cmdBufs;
|
||||
submit_info[0].signalSemaphoreCount = 0;
|
||||
submit_info[0].pSignalSemaphores = NULL;
|
||||
res = vkQueueSubmit(gfx_queue_, 1, submit_info, frame->fence);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkPresentInfoKHR present;
|
||||
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
|
@ -308,59 +295,59 @@ void VulkanContext::EndSurfaceRenderPass() {
|
|||
// return codes
|
||||
assert(!res);
|
||||
|
||||
frame->deleteList.Ingest(globalDeleteList_);
|
||||
curFrame_ ^= 1;
|
||||
}
|
||||
|
||||
void VulkanContext::BeginInitCommandBuffer() {
|
||||
assert(!cmdInitActive_);
|
||||
VulkanBeginCommandBuffer(cmd_);
|
||||
cmdInitActive_ = true;
|
||||
}
|
||||
|
||||
void VulkanContext::SubmitInitCommandBufferSync() {
|
||||
if (cmdInitActive_) {
|
||||
vkEndCommandBuffer(cmd_);
|
||||
vk_submit_sync(device_, 0, gfx_queue_, cmd_);
|
||||
cmdInitActive_ = false;
|
||||
}
|
||||
void VulkanBeginCommandBuffer(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkCommandBufferBeginInfo cmd_buf_info = {};
|
||||
cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
cmd_buf_info.pNext = NULL;
|
||||
cmd_buf_info.pInheritanceInfo = nullptr;
|
||||
cmd_buf_info.flags = 0;
|
||||
res = vkBeginCommandBuffer(cmd, &cmd_buf_info);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanContext::InitObjects(HINSTANCE hInstance, HWND hWnd, bool depthPresent) {
|
||||
InitSurfaceAndQueue(hInstance, hWnd);
|
||||
InitCommandPool();
|
||||
|
||||
InitCommandBuffer();
|
||||
BeginInitCommandBuffer();
|
||||
InitSwapchain();
|
||||
InitDepthStencilBuffer();
|
||||
|
||||
InitSurfaceRenderPass(depthPresent, true);
|
||||
InitFramebuffers(depthPresent);
|
||||
SubmitInitCommandBufferSync();
|
||||
|
||||
// Create frame data
|
||||
|
||||
VkCommandBufferAllocateInfo cmd = {};
|
||||
cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cmd.pNext = NULL;
|
||||
cmd.commandPool = cmd_pool_;
|
||||
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd.commandBufferCount = 2;
|
||||
VkCommandBufferAllocateInfo cmd_alloc = {};
|
||||
cmd_alloc.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cmd_alloc.pNext = NULL;
|
||||
cmd_alloc.commandPool = cmd_pool_;
|
||||
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd_alloc.commandBufferCount = 4;
|
||||
|
||||
VkCommandBuffer cmdBuf[2];
|
||||
VkResult res = vkAllocateCommandBuffers(device_, &cmd, cmdBuf);
|
||||
VkCommandBuffer cmdBuf[4];
|
||||
VkResult res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
frame_[0].cmdBuf = cmdBuf[0];
|
||||
frame_[0].cmdInit = cmdBuf[1];
|
||||
frame_[0].fence = CreateFence(true); // So it can be instantly waited on
|
||||
frame_[1].cmdBuf = cmdBuf[1];
|
||||
frame_[1].cmdBuf = cmdBuf[2];
|
||||
frame_[1].cmdInit = cmdBuf[3];
|
||||
frame_[1].fence = CreateFence(true);
|
||||
|
||||
VkCommandBuffer cmd = GetInitCommandBuffer();
|
||||
InitSwapchain(cmd);
|
||||
InitDepthStencilBuffer(cmd);
|
||||
|
||||
InitSurfaceRenderPass(depthPresent, true);
|
||||
InitFramebuffers(depthPresent);
|
||||
|
||||
// The init command buffer will be executed as part of the first frame.
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyObjects() {
|
||||
VkCommandBuffer cmdBuf[2] = { frame_[0].cmdBuf, frame_[1].cmdBuf };
|
||||
VkCommandBuffer cmdBuf[4] = { frame_[0].cmdBuf, frame_[0].cmdInit, frame_[1].cmdBuf, frame_[1].cmdInit};
|
||||
|
||||
vkFreeCommandBuffers(device_, cmd_pool_, 2, cmdBuf);
|
||||
vkFreeCommandBuffers(device_, cmd_pool_, sizeof(cmdBuf)/sizeof(cmdBuf[0]), cmdBuf);
|
||||
vkDestroyFence(device_, frame_[0].fence, nullptr);
|
||||
vkDestroyFence(device_, frame_[1].fence, nullptr);
|
||||
|
||||
|
@ -368,7 +355,6 @@ void VulkanContext::DestroyObjects() {
|
|||
DestroySurfaceRenderPass();
|
||||
DestroyDepthStencilBuffer();
|
||||
DestroySwapChain();
|
||||
DestroyCommandBuffer();
|
||||
DestroyCommandPool();
|
||||
}
|
||||
|
||||
|
@ -498,6 +484,9 @@ VkResult VulkanContext::InitDeviceExtensionProperties(layer_properties &layer_pr
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: function description here
|
||||
*/
|
||||
VkResult VulkanContext::InitDeviceLayerProperties() {
|
||||
uint32_t device_layer_count;
|
||||
VkLayerProperties *vk_props = NULL;
|
||||
|
@ -670,7 +659,7 @@ void VulkanContext::DestroyDebugMsgCallback() {
|
|||
}
|
||||
}
|
||||
|
||||
void VulkanContext::InitDepthStencilBuffer() {
|
||||
void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
bool U_ASSERT_ONLY pass;
|
||||
VkImageCreateInfo image_info = {};
|
||||
|
@ -752,7 +741,7 @@ void VulkanContext::InitDepthStencilBuffer() {
|
|||
assert(res == VK_SUCCESS);
|
||||
|
||||
/* Set the image layout to depth stencil optimal */
|
||||
TransitionImageLayout(cmd_, depth.image,
|
||||
TransitionImageLayout(cmd, depth.image,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
|
@ -867,7 +856,7 @@ void VulkanContext::InitSurfaceAndQueue(HINSTANCE conn, HWND wnd) {
|
|||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanContext::InitSwapchain() {
|
||||
void VulkanContext::InitSwapchain(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkSurfaceCapabilitiesKHR surfCapabilities;
|
||||
|
||||
|
@ -991,7 +980,7 @@ void VulkanContext::InitSwapchain() {
|
|||
|
||||
// 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.
|
||||
TransitionImageLayout(cmd_, sc_buffer.image,
|
||||
TransitionImageLayout(cmd, sc_buffer.image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
@ -1105,31 +1094,6 @@ void VulkanContext::InitCommandPool() {
|
|||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanContext::InitCommandBuffer() {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
|
||||
VkCommandBufferAllocateInfo cmd = {};
|
||||
cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cmd.pNext = NULL;
|
||||
cmd.commandPool = cmd_pool_;
|
||||
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd.commandBufferCount = 1;
|
||||
|
||||
res = vkAllocateCommandBuffers(device_, &cmd, &this->cmd_);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanBeginCommandBuffer(VkCommandBuffer cmd) {
|
||||
VkResult U_ASSERT_ONLY res;
|
||||
VkCommandBufferBeginInfo cmd_buf_info = {};
|
||||
cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
cmd_buf_info.pNext = NULL;
|
||||
cmd_buf_info.flags = 0;
|
||||
cmd_buf_info.pInheritanceInfo = nullptr;
|
||||
res = vkBeginCommandBuffer(cmd, &cmd_buf_info);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) {
|
||||
tex_width = w;
|
||||
tex_height = h;
|
||||
|
@ -1139,7 +1103,7 @@ void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) {
|
|||
VkFormatProperties formatProps;
|
||||
vkGetPhysicalDeviceFormatProperties(vulkan->GetPhysicalDevice(), VK_FORMAT_R8G8B8A8_UNORM, &formatProps);
|
||||
|
||||
/* See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data */
|
||||
// See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data
|
||||
needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false;
|
||||
|
||||
VkImageCreateInfo image_create_info = {};
|
||||
|
@ -1154,8 +1118,7 @@ void VulkanTexture::Create(VulkanContext *vulkan, int w, int h) {
|
|||
image_create_info.arrayLayers = 1;
|
||||
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT :
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
image_create_info.queueFamilyIndexCount = 0;
|
||||
image_create_info.pQueueFamilyIndices = NULL;
|
||||
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
@ -1198,9 +1161,9 @@ uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int *rowPitch) {
|
|||
void *data;
|
||||
|
||||
/* Get the subresource layout so we know what the row pitch is */
|
||||
vkGetImageSubresourceLayout(vulkan->device_, mappableImage, &subres, &layout);
|
||||
vkGetImageSubresourceLayout(vulkan->GetDevice(), mappableImage, &subres, &layout);
|
||||
|
||||
VkResult res = vkMapMemory(vulkan->device_, mappableMemory, 0, mem_reqs.size, 0, &data);
|
||||
VkResult res = vkMapMemory(vulkan->GetDevice(), mappableMemory, 0, mem_reqs.size, 0, &data);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
*rowPitch = (int)layout.rowPitch;
|
||||
|
@ -1208,17 +1171,16 @@ uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int *rowPitch) {
|
|||
}
|
||||
|
||||
void VulkanTexture::Unlock(VulkanContext *vulkan) {
|
||||
vkUnmapMemory(vulkan->device_, mappableMemory);
|
||||
vkUnmapMemory(vulkan->GetDevice(), mappableMemory);
|
||||
|
||||
VkCommandBuffer cmd = vulkan->GetInitCommandBuffer();
|
||||
|
||||
if (!needStaging) {
|
||||
/* If we can use the linear tiled image as a texture, just do it */
|
||||
image = mappableImage;
|
||||
mem = mappableMemory;
|
||||
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
TransitionImageLayout(vulkan->GetInitCommandBuffer(), image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
imageLayout);
|
||||
TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);
|
||||
} else {
|
||||
VkImageCreateInfo image_create_info = {};
|
||||
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
|
@ -1235,14 +1197,15 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
|
|||
image_create_info.pQueueFamilyIndices = NULL;
|
||||
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_create_info.flags = 0;
|
||||
/* The mappable image cannot be our texture, so create an optimally tiled image and blit to it */
|
||||
// The mappable image cannot be our texture, so create an optimally tiled image and blit to it
|
||||
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkResult res = vkCreateImage(vulkan->device_, &image_create_info, NULL, &image);
|
||||
VkResult res = vkCreateImage(vulkan->GetDevice(), &image_create_info, NULL, &image);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
vkGetImageMemoryRequirements(vulkan->device_, image, &mem_reqs);
|
||||
vkGetImageMemoryRequirements(vulkan->GetDevice(), image, &mem_reqs);
|
||||
|
||||
VkMemoryAllocateInfo mem_alloc = {};
|
||||
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
|
@ -1256,22 +1219,22 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
|
|||
assert(pass);
|
||||
|
||||
/* allocate memory */
|
||||
res = vkAllocateMemory(vulkan->device_, &mem_alloc, NULL, &mem);
|
||||
res = vkAllocateMemory(vulkan->GetDevice(), &mem_alloc, NULL, &mem);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
/* bind memory */
|
||||
res = vkBindImageMemory(vulkan->device_, image, mem, 0);
|
||||
res = vkBindImageMemory(vulkan->GetDevice(), image, mem, 0);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
/* Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL */
|
||||
/* Side effect is that this will create info.cmd */
|
||||
TransitionImageLayout(vulkan->GetInitCommandBuffer(), mappableImage,
|
||||
TransitionImageLayout(cmd, mappableImage,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
/* Since we're going to blit to the texture image, set its layout to DESTINATION_OPTIMAL */
|
||||
TransitionImageLayout(vulkan->GetInitCommandBuffer(), image,
|
||||
TransitionImageLayout(cmd, image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
@ -1295,59 +1258,23 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
|
|||
copy_region.extent.height = tex_height;
|
||||
copy_region.extent.depth = 1;
|
||||
|
||||
VkCommandBufferBeginInfo cmd_buf_info = {};
|
||||
cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
cmd_buf_info.pNext = NULL;
|
||||
cmd_buf_info.flags = 0;
|
||||
cmd_buf_info.pInheritanceInfo = nullptr;
|
||||
|
||||
res = vkBeginCommandBuffer(vulkan->cmd_, &cmd_buf_info);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
/* Put the copy command into the command buffer */
|
||||
vkCmdCopyImage(vulkan->cmd_,
|
||||
// Put the copy command into the command buffer
|
||||
vkCmdCopyImage(cmd,
|
||||
mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ©_region);
|
||||
|
||||
res = vkEndCommandBuffer(vulkan->cmd_);
|
||||
assert(res == VK_SUCCESS);
|
||||
const VkCommandBuffer cmd_bufs[] = { vulkan->cmd_ };
|
||||
VkFence nullFence = VK_NULL_HANDLE;
|
||||
|
||||
VkSubmitInfo submit_info[1] = {};
|
||||
submit_info[0].pNext = NULL;
|
||||
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info[0].waitSemaphoreCount = 0;
|
||||
submit_info[0].pWaitSemaphores = NULL;
|
||||
submit_info[0].commandBufferCount = 1;
|
||||
submit_info[0].pCommandBuffers = cmd_bufs;
|
||||
submit_info[0].signalSemaphoreCount = 0;
|
||||
submit_info[0].pSignalSemaphores = NULL;
|
||||
|
||||
// Queue the command buffer for execution. We can't legally delete the staging source
|
||||
// until this has completed, so let's throw in a fence. Note that this is a huge stall
|
||||
// that could be avoided with smarter code - for example we can check the fence the next
|
||||
// time the texture is used and discard the staging image then.
|
||||
|
||||
VkFence fence = vulkan->CreateFence(false);
|
||||
|
||||
res = vkQueueSubmit(vulkan->gfx_queue_, 1, submit_info, fence);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
/* Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY */
|
||||
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
TransitionImageLayout(vulkan->GetInitCommandBuffer(), image,
|
||||
TransitionImageLayout(cmd, image,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
imageLayout);
|
||||
|
||||
vulkan->WaitAndResetFence(fence);
|
||||
|
||||
/* Release the resources for the staging image */
|
||||
vkFreeMemory(vulkan->device_, mappableMemory, NULL);
|
||||
vkDestroyImage(vulkan->device_, mappableImage, NULL);
|
||||
vkDestroyFence(vulkan->device_, fence, NULL);
|
||||
vulkan->QueueDelete(mappableMemory);
|
||||
vulkan->QueueDelete(mappableImage);
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo view_info = {};
|
||||
|
@ -1368,14 +1295,14 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
|
|||
|
||||
/* create image view */
|
||||
view_info.image = image;
|
||||
VkResult res = vkCreateImageView(vulkan->device_, &view_info, NULL, &view);
|
||||
VkResult res = vkCreateImageView(vulkan->GetDevice(), &view_info, NULL, &view);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanTexture::Destroy(VulkanContext *vulkan) {
|
||||
vkDestroyImageView(vulkan->device_, view, NULL);
|
||||
vkDestroyImage(vulkan->device_, image, NULL);
|
||||
vkFreeMemory(vulkan->device_, mem, NULL);
|
||||
vkDestroyImageView(vulkan->GetDevice(), view, NULL);
|
||||
vkDestroyImage(vulkan->GetDevice(), image, NULL);
|
||||
vkFreeMemory(vulkan->GetDevice(), mem, NULL);
|
||||
view = NULL;
|
||||
image = NULL;
|
||||
mem = NULL;
|
||||
|
@ -1392,16 +1319,8 @@ VkFence VulkanContext::CreateFence(bool presignalled) {
|
|||
}
|
||||
|
||||
void VulkanContext::WaitAndResetFence(VkFence fence) {
|
||||
VkResult res = vkWaitForFences(device_, 1, &fence, true, UINT64_MAX);
|
||||
assert(!res);
|
||||
res = vkResetFences(device_, 1, &fence);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyCommandBuffer() {
|
||||
VkCommandBuffer cmd_bufs[1] = { cmd_ };
|
||||
vkFreeCommandBuffers(device_, cmd_pool_, 1, cmd_bufs);
|
||||
cmd_ = nullptr;
|
||||
vkWaitForFences(device_, 1, &fence, true, UINT64_MAX);
|
||||
vkResetFences(device_, 1, &fence);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyCommandPool() {
|
||||
|
@ -1473,9 +1392,13 @@ void TransitionImageLayout(
|
|||
image_memory_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
|
||||
image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
||||
/* Make sure anything that was copying from this image has completed */
|
||||
image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT|VK_ACCESS_MEMORY_READ_BIT;
|
||||
}
|
||||
|
||||
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
||||
|
|
|
@ -64,6 +64,34 @@ struct layer_properties {
|
|||
std::vector<VkExtensionProperties> extensions;
|
||||
};
|
||||
|
||||
class VulkanDeleteList {
|
||||
public:
|
||||
void QueueDelete(VkImage image) { images_.push_back(image); }
|
||||
void QueueDelete(VkDeviceMemory deviceMemory) { memory_.push_back(deviceMemory); }
|
||||
|
||||
void Ingest(VulkanDeleteList &del) {
|
||||
images_ = del.images_;
|
||||
memory_ = del.memory_;
|
||||
del.images_.clear();
|
||||
del.memory_.clear();
|
||||
}
|
||||
|
||||
void PerformDeletes(VkDevice device) {
|
||||
for (auto &mem : memory_) {
|
||||
vkFreeMemory(device, mem, nullptr);
|
||||
}
|
||||
memory_.clear();
|
||||
for (auto &image : images_) {
|
||||
vkDestroyImage(device, image, nullptr);
|
||||
}
|
||||
images_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<VkImage> images_;
|
||||
std::vector<VkDeviceMemory> memory_;
|
||||
};
|
||||
|
||||
// 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.
|
||||
class VulkanContext {
|
||||
|
@ -77,29 +105,28 @@ public:
|
|||
return device_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void QueueDelete(T mem) {
|
||||
globalDeleteList_.QueueDelete(mem);
|
||||
}
|
||||
|
||||
void InitSurfaceAndQueue(HINSTANCE conn, HWND wnd);
|
||||
void InitSwapchain();
|
||||
void InitSwapchain(VkCommandBuffer cmd);
|
||||
void InitSurfaceRenderPass(bool include_depth, bool clear);
|
||||
void InitFramebuffers(bool include_depth);
|
||||
void InitDepthStencilBuffer();
|
||||
void InitDepthStencilBuffer(VkCommandBuffer cmd);
|
||||
void InitCommandPool();
|
||||
void InitCommandBuffer();
|
||||
|
||||
void InitObjects(HINSTANCE hInstance, HWND hWnd, bool depthPresent);
|
||||
void DestroyObjects();
|
||||
|
||||
void BeginInitCommandBuffer();
|
||||
void SubmitInitCommandBufferSync();
|
||||
VkCommandBuffer GetInitCommandBuffer() {
|
||||
return cmd_;
|
||||
}
|
||||
VkCommandBuffer GetInitCommandBuffer();
|
||||
|
||||
void DestroySurfaceRenderPass();
|
||||
void DestroyFramebuffers();
|
||||
void DestroySwapChain();
|
||||
void DestroyDepthStencilBuffer();
|
||||
void DestroyCommandPool();
|
||||
void DestroyCommandBuffer();
|
||||
void DestroyDevice();
|
||||
|
||||
void WaitUntilQueueIdle();
|
||||
|
@ -147,7 +174,7 @@ public:
|
|||
VkResult InitDeviceExtensionProperties(layer_properties &layer_props);
|
||||
VkResult InitDeviceLayerProperties();
|
||||
|
||||
|
||||
private:
|
||||
VkSemaphore acquireSemaphore;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -162,9 +189,8 @@ public:
|
|||
xcb_intern_atom_reply_t *atom_wm_delete_window;
|
||||
#endif // _WIN32
|
||||
|
||||
// TODO: Move to frame data
|
||||
VkCommandPool cmd_pool_;
|
||||
VkCommandBuffer cmd_; // Buffer for initialization commands
|
||||
bool cmdInitActive_;
|
||||
|
||||
VkInstance instance_;
|
||||
VkDevice device_;
|
||||
|
@ -190,7 +216,6 @@ public:
|
|||
std::vector<VkQueueFamilyProperties> queue_props;
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
|
||||
private:
|
||||
struct swap_chain_buffer {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
|
@ -208,12 +233,23 @@ private:
|
|||
// Manages flipping command buffers for the backbuffer render pass.
|
||||
// It is recommended to do the same for other rendering passes.
|
||||
struct FrameData {
|
||||
FrameData() : fence(nullptr), hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {}
|
||||
|
||||
VkFence fence;
|
||||
bool hasInitCommands;
|
||||
VkCommandBuffer cmdInit;
|
||||
VkCommandBuffer cmdBuf;
|
||||
|
||||
VulkanDeleteList deleteList;
|
||||
};
|
||||
|
||||
FrameData frame_[2];
|
||||
int curFrame_;
|
||||
|
||||
// At the end of the frame, this is copied into the frame's delete list, so it can be processed
|
||||
// the next time the frame comes around again.
|
||||
VulkanDeleteList globalDeleteList_;
|
||||
|
||||
// Simple loader for the WSI extension.
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
|
@ -272,8 +308,6 @@ VkBool32 CheckLayers(const std::vector<layer_properties> &layer_props, const std
|
|||
|
||||
void VulkanBeginCommandBuffer(VkCommandBuffer cmd);
|
||||
|
||||
void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue, VkCommandBuffer cmd);
|
||||
|
||||
void init_glslang();
|
||||
void finalize_glslang();
|
||||
|
||||
|
|
|
@ -351,6 +351,7 @@ public:
|
|||
info->pVertexAttributeDescriptions = attrDescs;
|
||||
info->vertexBindingDescriptionCount = 1;
|
||||
info->pVertexBindingDescriptions = bindDescs;
|
||||
info->flags = 0;
|
||||
}
|
||||
|
||||
bool RequiresBuffer() {
|
||||
|
@ -512,8 +513,6 @@ private:
|
|||
void ApplyDynamicState();
|
||||
void DirtyDynamicState();
|
||||
|
||||
void BeginInitCommands();
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
|
||||
// These are used to compose the pipeline cache key.
|
||||
|
@ -554,15 +553,7 @@ private:
|
|||
Thin3DVKTexture *boundTextures_[MAX_BOUND_TEXTURES];
|
||||
Thin3DVKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES];
|
||||
|
||||
// Ephemeral command buffer used for initializing textures, etc. As there can only be one in flight, can cause annoying stalls until I do something better.
|
||||
VkCommandBuffer initCmd_;
|
||||
bool hasInitCommands_;
|
||||
VkFence initFence_;
|
||||
|
||||
// TODO: Transpose this into a struct FrameObject[2].
|
||||
|
||||
VkCommandBuffer cmd_; // The current one
|
||||
|
||||
VulkanPushBuffer *pushBuffer_[2];
|
||||
int frameNum_;
|
||||
VulkanPushBuffer *push_;
|
||||
|
@ -753,22 +744,6 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
|
|||
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
|
||||
assert(VK_SUCCESS == res);
|
||||
|
||||
VkCommandBufferAllocateInfo cb;
|
||||
cb.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cb.pNext = nullptr;
|
||||
cb.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cb.commandPool = cmdPool_;
|
||||
cb.commandBufferCount = 1;
|
||||
res = vkAllocateCommandBuffers(device_, &cb, &initCmd_);
|
||||
assert(VK_SUCCESS == res);
|
||||
hasInitCommands_ = false;
|
||||
|
||||
VkFenceCreateInfo f;
|
||||
f.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
f.pNext = nullptr;
|
||||
f.flags = 0;
|
||||
vkCreateFence(device_, &f, nullptr, &initFence_);
|
||||
|
||||
VkPipelineCacheCreateInfo pc;
|
||||
pc.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
pc.pNext = nullptr;
|
||||
|
@ -811,42 +786,9 @@ void Thin3DVKContext::Begin(bool clear, uint32_t colorval, float depthVal, int s
|
|||
viewportDirty_ = true;
|
||||
}
|
||||
|
||||
void Thin3DVKContext::BeginInitCommands() {
|
||||
assert(!hasInitCommands_);
|
||||
|
||||
VkCommandBufferBeginInfo begin;
|
||||
begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
begin.pNext = nullptr;
|
||||
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
begin.pInheritanceInfo = nullptr;
|
||||
VkResult res = vkBeginCommandBuffer(initCmd_, &begin);
|
||||
assert(VK_SUCCESS == res);
|
||||
hasInitCommands_ = true;
|
||||
}
|
||||
|
||||
void Thin3DVKContext::End() {
|
||||
// Stop collecting data in the frame data buffer.
|
||||
push_->End(device_);
|
||||
|
||||
// IF something needs to be uploaded etc, sneak it in before we actually run the main command buffer.
|
||||
if (hasInitCommands_) {
|
||||
VkResult res = vkEndCommandBuffer(initCmd_);
|
||||
assert(VK_SUCCESS == res);
|
||||
|
||||
// Run the texture uploads etc _before_ we execute the ordinary command buffer
|
||||
VkSubmitInfo submit = {};
|
||||
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit.pCommandBuffers = &initCmd_;
|
||||
submit.commandBufferCount = 1;
|
||||
res = vkQueueSubmit(queue_, 1, &submit, initFence_);
|
||||
assert(VK_SUCCESS == res);
|
||||
// Before we can begin, we must be sure that the command buffer is no longer in use, as we only have a single one for init
|
||||
// tasks (for now).
|
||||
vulkan_->WaitAndResetFence(initFence_);
|
||||
hasInitCommands_ = false;
|
||||
// Init cmd buffer is again available for writing.
|
||||
}
|
||||
|
||||
vulkan_->EndSurfaceRenderPass();
|
||||
|
||||
frameNum_++;
|
||||
|
@ -899,6 +841,8 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() {
|
|||
writes[0].dstArrayElement = 0;
|
||||
writes[0].dstBinding = 0;
|
||||
writes[0].pBufferInfo = &bufferDesc;
|
||||
writes[0].pImageInfo = nullptr;
|
||||
writes[0].pTexelBufferView = nullptr;
|
||||
writes[0].descriptorCount = 1;
|
||||
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
|
||||
|
@ -907,7 +851,9 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() {
|
|||
writes[1].dstSet = descSet;
|
||||
writes[1].dstArrayElement = 0;
|
||||
writes[1].dstBinding = 1;
|
||||
writes[1].pBufferInfo = nullptr;
|
||||
writes[1].pImageInfo = &imageDesc;
|
||||
writes[1].pTexelBufferView = nullptr;
|
||||
writes[1].descriptorCount = 1;
|
||||
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
||||
|
@ -1207,10 +1153,8 @@ void Thin3DVKContext::SetTextures(int start, int count, Thin3DTexture **textures
|
|||
boundTextures_[i] = static_cast<Thin3DVKTexture *>(textures[i]);
|
||||
// Bind simply copies the texture to VRAM if needed.
|
||||
if (boundTextures_[i]->NeedsUpload()) {
|
||||
if (!hasInitCommands_) {
|
||||
BeginInitCommands();
|
||||
}
|
||||
boundTextures_[i]->Upload(initCmd_);
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
boundTextures_[i]->Upload(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue