vulkan: better image update. Don't kill in-flight command buffers

Transition images back to transfer and update them instead of
deleting/recreating.
Keep track of in-flight texture command buffers and free them when idle.
This commit is contained in:
Flyinghead 2019-10-07 17:45:11 +02:00
parent b790a336e9
commit e09e895f22
5 changed files with 63 additions and 29 deletions

View File

@ -498,12 +498,12 @@ vk::UniqueShaderModule ShaderManager::compileShader(const FragmentShaderParams&
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eFragment, buf);
}
vk::UniqueShaderModule ShaderManager::compileVertexModVolShader()
vk::UniqueShaderModule ShaderManager::compileModVolVertexShader()
{
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eVertex, ModVolVertexShaderSource);
}
vk::UniqueShaderModule ShaderManager::compileModVolShader()
vk::UniqueShaderModule ShaderManager::compileModVolFragmentShader()
{
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eFragment, ModVolFragmentShaderSource);
}

View File

@ -86,13 +86,13 @@ public:
vk::ShaderModule GetModVolVertexShader()
{
if (!modVolVertexShader)
modVolVertexShader = compileVertexModVolShader();
modVolVertexShader = compileModVolVertexShader();
return *modVolVertexShader;
}
vk::ShaderModule GetModVolShader()
{
if (!modVolShader)
modVolShader = compileModVolShader();
modVolShader = compileModVolFragmentShader();
return *modVolShader;
}
@ -108,8 +108,8 @@ private:
}
vk::UniqueShaderModule compileShader(const VertexShaderParams& params);
vk::UniqueShaderModule compileShader(const FragmentShaderParams& params);
vk::UniqueShaderModule compileVertexModVolShader();
vk::UniqueShaderModule compileModVolShader();
vk::UniqueShaderModule compileModVolVertexShader();
vk::UniqueShaderModule compileModVolFragmentShader();
std::map<u32, vk::UniqueShaderModule> vertexShaders;
std::map<u32, vk::UniqueShaderModule> fragmentShaders;

View File

@ -34,6 +34,7 @@ static void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image ima
break;
case vk::ImageLayout::eGeneral: // sourceAccessMask is empty
case vk::ImageLayout::eUndefined:
case vk::ImageLayout::eShaderReadOnlyOptimal:
break;
default:
verify(false);
@ -53,6 +54,9 @@ static void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image ima
case vk::ImageLayout::eUndefined:
sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
break;
case vk::ImageLayout::eShaderReadOnlyOptimal:
sourceStage = vk::PipelineStageFlagBits::eFragmentShader;
break;
default:
verify(false);
break;
@ -150,8 +154,12 @@ void Texture::UploadToGPU(int width, int height, u8 *data)
dataSize /= 2;
break;
}
Init(width, height, format);
SetImage(VulkanContext::Instance()->GetCurrentCommandPool(), dataSize, data);
bool isNew = true;
if (width != extent.width || height != extent.height || format != this->format)
Init(width, height, format);
else
isNew = false;
SetImage(dataSize, data, isNew);
}
void Texture::Init(u32 width, u32 height, vk::Format format)
@ -205,11 +213,12 @@ void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk:
imageView = device.createImageViewUnique(imageViewCreateInfo);
}
void Texture::SetImage(const vk::CommandPool& commandPool, u32 srcSize, void *srcData)
void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
{
vk::UniqueCommandBuffer commandBuffer = std::move(device.allocateCommandBuffersUnique(
vk::CommandBufferAllocateInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1)).front());
commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (!isNew && !needsStaging)
setImageLayout(commandBuffer, image.get(), format, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eGeneral);
vk::DeviceSize size = needsStaging
? device.getBufferMemoryRequirements(stagingBufferData->buffer.get()).size
@ -223,25 +232,19 @@ void Texture::SetImage(const vk::CommandPool& commandPool, u32 srcSize, void *sr
if (needsStaging)
{
// Since we're going to blit to the texture image, set its layout to eTransferDstOptimal
setImageLayout(*commandBuffer, image.get(), format, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
setImageLayout(commandBuffer, image.get(), format, isNew ? vk::ImageLayout::eUndefined : vk::ImageLayout::eShaderReadOnlyOptimal,
vk::ImageLayout::eTransferDstOptimal);
vk::BufferImageCopy copyRegion(0, extent.width, extent.height, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), vk::Offset3D(0, 0, 0), vk::Extent3D(extent, 1));
commandBuffer->copyBufferToImage(stagingBufferData->buffer.get(), image.get(), vk::ImageLayout::eTransferDstOptimal, copyRegion);
commandBuffer.copyBufferToImage(stagingBufferData->buffer.get(), image.get(), vk::ImageLayout::eTransferDstOptimal, copyRegion);
// Set the layout for the texture image from eTransferDstOptimal to SHADER_READ_ONLY
setImageLayout(*commandBuffer, image.get(), format, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
setImageLayout(commandBuffer, image.get(), format, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
}
else
{
// If we can use the linear tiled image as a texture, just do it
setImageLayout(*commandBuffer, image.get(), format, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
setImageLayout(commandBuffer, image.get(), format, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
}
commandBuffer->end();
VulkanContext::Instance()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &(*commandBuffer)), nullptr);
commandBuffer.end();
// FIXME we need to wait for the command buffer to finish executing before freeing the staging and command buffers
VulkanContext::Instance()->GetGraphicsQueue().waitIdle();
if (needsStaging)
{
// Free staging buffer
stagingBufferData = nullptr;
}
VulkanContext::Instance()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &commandBuffer), nullptr);
}

View File

@ -34,10 +34,11 @@ struct Texture : BaseTextureCacheData
std::string GetId() override { char s[20]; sprintf(s, "%p", this); return s; }
bool IsNew() const { return !image.get(); }
vk::ImageView GetImageView() const { return *imageView; }
void SetCommandBuffer(vk::CommandBuffer commandBuffer) { this->commandBuffer = commandBuffer; }
private:
void Init(u32 width, u32 height, vk::Format format);
void SetImage(const vk::CommandPool& commandPool, u32 size, void *data);
void SetImage(u32 size, void *data, bool isNew);
void CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask);
@ -45,6 +46,7 @@ private:
vk::Extent2D extent;
bool needsStaging = false;
std::unique_ptr<BufferData> stagingBufferData;
vk::CommandBuffer commandBuffer;
vk::UniqueDeviceMemory deviceMemory;
vk::UniqueImageView imageView;

View File

@ -19,6 +19,7 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory>
#include <unordered_set>
#include <math.h>
#include "vulkan.h"
#include "hw/pvr/Renderer_if.h"
@ -55,6 +56,7 @@ public:
printf("VulkanRenderer::Term\n");
GetContext()->WaitIdle();
killtex();
inFlightCommandBuffers.clear();
glslang::FinalizeProcess();
}
@ -66,7 +68,15 @@ public:
// TODO RenderFramebuffer();
return false;
}
// FIXME We shouldn't wait for the next vk image if doing a RTT
if (ctx->rend.isRTT)
return false;
GetContext()->NewFrame();
if (inFlightCommandBuffers.size() != GetContext()->GetSwapChainSize())
inFlightCommandBuffers.resize(GetContext()->GetSwapChainSize());
inFlightCommandBuffers[GetCurrentImage()].clear();
if (ProcessFrame(ctx))
return true;
@ -157,7 +167,7 @@ public:
dc2s_scale_h = screen_height / 480.0f;
ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0f * screen_stretching) / 2;
vtxUniforms.scale[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching;
vtxUniforms.scale[1] = 1.5f / dc_height; // FIXME 1.5 WTF?
vtxUniforms.scale[1] = 2.0f / dc_height;
vtxUniforms.scale[2] = 1 - 2 * ds2s_offs_x / screen_width;
vtxUniforms.scale[3] = 1;
}
@ -219,8 +229,8 @@ public:
cmdBuffer.bindVertexBuffers(0, 1, &mainBuffers[GetCurrentImage()]->buffer.get(), offsets);
cmdBuffer.bindIndexBuffer(*mainBuffers[GetCurrentImage()]->buffer, pvrrc.verts.bytes() + pvrrc.modtrig.bytes(), vk::IndexType::eUint32);
cmdBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, static_cast<float>(GetContext()->GetViewPort().width),
static_cast<float>(GetContext()->GetViewPort().width), 1.0f, 0.0f));
cmdBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)GetContext()->GetViewPort().width,
(float)GetContext()->GetViewPort().height, 1.0f, 0.0f));
cmdBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), GetContext()->GetViewPort()));
RenderPass previous_pass = {};
@ -276,7 +286,16 @@ public:
//update if needed
if (tf->NeedsUpdate())
{
int previousImage = GetCurrentImage() - 1;
if (previousImage < 0)
previousImage = GetContext()->GetSwapChainSize() - 1;
inFlightCommandBuffers[GetCurrentImage()].emplace_back(std::move(GetContext()->GetDevice()->allocateCommandBuffersUnique(
vk::CommandBufferAllocateInfo(GetContext()->GetCurrentCommandPool(), vk::CommandBufferLevel::ePrimary, 1)).front()));
tf->SetCommandBuffer(*inFlightCommandBuffers[GetCurrentImage()].back());
tf->Update();
tf->SetCommandBuffer(nullptr);
}
else
tf->CheckCustomTexture();
@ -592,19 +611,29 @@ private:
fog_needs_update = false;
u8 texData[256];
MakeFogTexture(texData);
inFlightCommandBuffers[GetCurrentImage()].emplace_back(std::move(GetContext()->GetDevice()->allocateCommandBuffersUnique(
vk::CommandBufferAllocateInfo(GetContext()->GetCurrentCommandPool(), vk::CommandBufferLevel::ePrimary, 1)).front()));
fogTexture->SetCommandBuffer(*inFlightCommandBuffers[GetCurrentImage()].back());
fogTexture->UploadToGPU(128, 2, texData);
fogTexture->SetCommandBuffer(nullptr);
}
// temp stuff
float scale_x;
float scale_y;
// Per-triangle sort results
std::vector<std::vector<SortTrigDrawParam>> sortedPolys;
std::vector<std::vector<u32>> sortedIndexes;
u32 sortedIndexCount;
std::unique_ptr<Texture> fogTexture;
std::vector<std::vector<vk::UniqueCommandBuffer>> inFlightCommandBuffers;
// Uniforms
// TODO put these in the main buffer
vk::UniqueBuffer vertexUniformBuffer;
vk::UniqueBuffer fragmentUniformBuffer;
vk::UniqueDeviceMemory vertexUniformMemory;