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:
parent
b790a336e9
commit
e09e895f22
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue