mirror of
https://github.com/love2d/love.git
synced 2026-08-20 04:30:09 +02:00
vulkan: unify local uniform buffer
This commit is contained in:
@@ -153,6 +153,7 @@ Graphics::~Graphics()
|
|||||||
{
|
{
|
||||||
defaultConstantTexCoord.set(nullptr);
|
defaultConstantTexCoord.set(nullptr);
|
||||||
defaultConstantColor.set(nullptr);
|
defaultConstantColor.set(nullptr);
|
||||||
|
localUniformBuffer.set(nullptr);
|
||||||
|
|
||||||
Volatile::unloadAll();
|
Volatile::unloadAll();
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -650,6 +651,9 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
|||||||
createSyncObjects();
|
createSyncObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (localUniformBuffer == nullptr)
|
||||||
|
localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, 1024 * 512 * 1), Acquire::NORETAIN);
|
||||||
|
|
||||||
beginFrame();
|
beginFrame();
|
||||||
|
|
||||||
if (createBaseObjects)
|
if (createBaseObjects)
|
||||||
@@ -1302,9 +1306,11 @@ void Graphics::beginFrame()
|
|||||||
|
|
||||||
Vulkan::resetShaderSwitches();
|
Vulkan::resetShaderSwitches();
|
||||||
|
|
||||||
for (const auto shader : usedShadersInFrame)
|
for (const auto &shader : usedShadersInFrame)
|
||||||
shader->newFrame();
|
shader->newFrame();
|
||||||
usedShadersInFrame.clear();
|
usedShadersInFrame.clear();
|
||||||
|
|
||||||
|
localUniformBuffer->nextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::startRecordingGraphicsCommands()
|
void Graphics::startRecordingGraphicsCommands()
|
||||||
@@ -1330,11 +1336,6 @@ void Graphics::endRecordingGraphicsCommands() {
|
|||||||
throw love::Exception("failed to record command buffer");
|
throw love::Exception("failed to record command buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const
|
|
||||||
{
|
|
||||||
return minUniformBufferOffsetAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
|
VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
|
||||||
{
|
{
|
||||||
if (renderPassState.active)
|
if (renderPassState.active)
|
||||||
@@ -2869,6 +2870,21 @@ int Graphics::getVsync() const
|
|||||||
return vsync;
|
return vsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Graphics::mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo)
|
||||||
|
{
|
||||||
|
size_t alignedSize = static_cast<size_t>(std::ceil(static_cast<float>(size) / static_cast<float>(minUniformBufferOffsetAlignment))) * minUniformBufferOffsetAlignment;
|
||||||
|
|
||||||
|
if (localUniformBuffer->getUsableSize() < alignedSize)
|
||||||
|
localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, localUniformBuffer->getSize() * 2), Acquire::NORETAIN);
|
||||||
|
|
||||||
|
auto mapInfo = localUniformBuffer->map(alignedSize);
|
||||||
|
memcpy(mapInfo.data, data, size);
|
||||||
|
|
||||||
|
bufferInfo.buffer = (VkBuffer)localUniformBuffer->getHandle();
|
||||||
|
bufferInfo.offset = localUniformBuffer->unmap(alignedSize);
|
||||||
|
bufferInfo.range = size;
|
||||||
|
}
|
||||||
|
|
||||||
void Graphics::createColorResources()
|
void Graphics::createColorResources()
|
||||||
{
|
{
|
||||||
if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
|
if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ public:
|
|||||||
void queueCleanUp(std::function<void()> cleanUp);
|
void queueCleanUp(std::function<void()> cleanUp);
|
||||||
void addReadbackCallback(std::function<void()> callback);
|
void addReadbackCallback(std::function<void()> callback);
|
||||||
void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
|
void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
|
||||||
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
|
|
||||||
VkSampler getCachedSampler(const SamplerState &sampler);
|
VkSampler getCachedSampler(const SamplerState &sampler);
|
||||||
void setComputeShader(Shader *computeShader);
|
void setComputeShader(Shader *computeShader);
|
||||||
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
|
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
|
||||||
@@ -325,6 +324,7 @@ public:
|
|||||||
VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
|
VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
|
||||||
void setVsync(int vsync);
|
void setVsync(int vsync);
|
||||||
int getVsync() const;
|
int getVsync() const;
|
||||||
|
void mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo);
|
||||||
|
|
||||||
uint32 getDeviceApiVersion() const { return deviceApiVersion; }
|
uint32 getDeviceApiVersion() const { return deviceApiVersion; }
|
||||||
|
|
||||||
@@ -444,6 +444,7 @@ private:
|
|||||||
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
|
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
|
||||||
StrongRef<love::graphics::Buffer> defaultConstantColor;
|
StrongRef<love::graphics::Buffer> defaultConstantColor;
|
||||||
StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
|
StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
|
||||||
|
StrongRef<StreamBuffer> localUniformBuffer;
|
||||||
// functions that need to be called to cleanup objects that were needed for rendering a frame.
|
// functions that need to be called to cleanup objects that were needed for rendering a frame.
|
||||||
// We need a vector for each frame in flight.
|
// We need a vector for each frame in flight.
|
||||||
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
|
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
|
||||||
|
|||||||
@@ -157,14 +157,11 @@ bool Shader::loadVolatile()
|
|||||||
builtinUniformInfo[i] = nullptr;
|
builtinUniformInfo[i] = nullptr;
|
||||||
|
|
||||||
compileShaders();
|
compileShaders();
|
||||||
calculateUniformBufferSizeAligned();
|
|
||||||
createDescriptorSetLayout();
|
createDescriptorSetLayout();
|
||||||
createPipelineLayout();
|
createPipelineLayout();
|
||||||
createDescriptorPoolSizes();
|
createDescriptorPoolSizes();
|
||||||
createStreamBuffers();
|
|
||||||
descriptorPools.resize(MAX_FRAMES_IN_FLIGHT);
|
descriptorPools.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
currentFrame = 0;
|
currentFrame = 0;
|
||||||
currentUsedUniformStreamBuffersCount = 0;
|
|
||||||
newFrame();
|
newFrame();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -189,12 +186,8 @@ void Shader::unloadVolatile()
|
|||||||
vkDestroyPipeline(device, computePipeline, nullptr);
|
vkDestroyPipeline(device, computePipeline, nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const auto streamBuffer : streamBuffers)
|
|
||||||
streamBuffer->release();
|
|
||||||
|
|
||||||
shaderModules.clear();
|
shaderModules.clear();
|
||||||
shaderStages.clear();
|
shaderStages.clear();
|
||||||
streamBuffers.clear();
|
|
||||||
descriptorPools.clear();
|
descriptorPools.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,23 +210,8 @@ void Shader::newFrame()
|
|||||||
{
|
{
|
||||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
|
||||||
currentUsedUniformStreamBuffersCount = 0;
|
|
||||||
currentDescriptorPool = 0;
|
currentDescriptorPool = 0;
|
||||||
|
|
||||||
if (streamBuffers.size() > 1)
|
|
||||||
{
|
|
||||||
size_t newSize = 0;
|
|
||||||
for (auto streamBuffer : streamBuffers)
|
|
||||||
{
|
|
||||||
newSize += streamBuffer->getSize();
|
|
||||||
streamBuffer->release();
|
|
||||||
}
|
|
||||||
streamBuffers.clear();
|
|
||||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, newSize));
|
|
||||||
}
|
|
||||||
else if (streamBuffers.size() == 1)
|
|
||||||
streamBuffers.at(0)->nextFrame();
|
|
||||||
|
|
||||||
for (VkDescriptorPool pool : descriptorPools[currentFrame])
|
for (VkDescriptorPool pool : descriptorPools[currentFrame])
|
||||||
vkResetDescriptorPool(device, pool, 0);
|
vkResetDescriptorPool(device, pool, 0);
|
||||||
}
|
}
|
||||||
@@ -246,13 +224,6 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
|
|||||||
|
|
||||||
if (!localUniformData.empty())
|
if (!localUniformData.empty())
|
||||||
{
|
{
|
||||||
auto usedStreamBufferMemory = currentUsedUniformStreamBuffersCount * uniformBufferSizeAligned;
|
|
||||||
if (usedStreamBufferMemory >= streamBuffers.back()->getSize())
|
|
||||||
{
|
|
||||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
|
|
||||||
currentUsedUniformStreamBuffersCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builtinUniformDataOffset.hasValue)
|
if (builtinUniformDataOffset.hasValue)
|
||||||
{
|
{
|
||||||
auto builtinData = vgfx->getCurrentBuiltinUniformData();
|
auto builtinData = vgfx->getCurrentBuiltinUniformData();
|
||||||
@@ -260,19 +231,7 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
|
|||||||
memcpy(dst, &builtinData, sizeof(builtinData));
|
memcpy(dst, &builtinData, sizeof(builtinData));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto currentStreamBuffer = streamBuffers.back();
|
vgfx->mapLocalUniformData(localUniformData.data(), localUniformData.size(), descriptorBuffers[bufferIndex++]);
|
||||||
|
|
||||||
auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
|
|
||||||
memcpy(mapInfo.data, localUniformData.data(), localUniformData.size());
|
|
||||||
auto offset = currentStreamBuffer->unmap(uniformBufferSizeAligned);
|
|
||||||
currentStreamBuffer->markUsed(uniformBufferSizeAligned);
|
|
||||||
|
|
||||||
VkDescriptorBufferInfo &bufferInfo = descriptorBuffers[bufferIndex++];
|
|
||||||
bufferInfo.buffer = (VkBuffer)currentStreamBuffer->getHandle();
|
|
||||||
bufferInfo.offset = offset;
|
|
||||||
bufferInfo.range = localUniformData.size();
|
|
||||||
|
|
||||||
currentUsedUniformStreamBuffersCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: iteration order must match the order at the end of compileShaders right now.
|
// TODO: iteration order must match the order at the end of compileShaders right now.
|
||||||
@@ -436,14 +395,6 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::calculateUniformBufferSizeAligned()
|
|
||||||
{
|
|
||||||
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
|
|
||||||
size_t size = localUniformStagingData.size();
|
|
||||||
auto factor = static_cast<VkDeviceSize>(std::ceil(static_cast<float>(size) / static_cast<float>(minAlignment)));
|
|
||||||
uniformBufferSizeAligned = factor * minAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
|
void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
|
||||||
{
|
{
|
||||||
using namespace spirv_cross;
|
using namespace spirv_cross;
|
||||||
@@ -978,13 +929,6 @@ void Shader::createDescriptorPoolSizes()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::createStreamBuffers()
|
|
||||||
{
|
|
||||||
size_t size = STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned;
|
|
||||||
if (size > 0)
|
|
||||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)
|
void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)
|
||||||
{
|
{
|
||||||
std::array<graphics::Texture*, 3> textures = {
|
std::array<graphics::Texture*, 3> textures = {
|
||||||
|
|||||||
@@ -88,27 +88,20 @@ public:
|
|||||||
void setMainTex(graphics::Texture *texture);
|
void setMainTex(graphics::Texture *texture);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculateUniformBufferSizeAligned();
|
|
||||||
void compileShaders();
|
void compileShaders();
|
||||||
void createDescriptorSetLayout();
|
void createDescriptorSetLayout();
|
||||||
void createPipelineLayout();
|
void createPipelineLayout();
|
||||||
void createDescriptorPoolSizes();
|
void createDescriptorPoolSizes();
|
||||||
void createStreamBuffers();
|
|
||||||
void buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename);
|
void buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename);
|
||||||
void createDescriptorPool();
|
void createDescriptorPool();
|
||||||
VkDescriptorSet allocateDescriptorSet();
|
VkDescriptorSet allocateDescriptorSet();
|
||||||
|
|
||||||
VkDeviceSize uniformBufferSizeAligned;
|
|
||||||
|
|
||||||
VkPipeline computePipeline;
|
VkPipeline computePipeline;
|
||||||
|
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
VkDescriptorSetLayout descriptorSetLayout;
|
||||||
VkPipelineLayout pipelineLayout;
|
VkPipelineLayout pipelineLayout;
|
||||||
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
|
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
|
||||||
|
|
||||||
// we don't know how much memory we need per frame for the uniform buffer descriptors
|
|
||||||
// we keep a vector of stream buffers that gets dynamically increased if more memory is needed
|
|
||||||
std::vector<StreamBuffer*> streamBuffers;
|
|
||||||
std::vector<std::vector<VkDescriptorPool>> descriptorPools;
|
std::vector<std::vector<VkDescriptorPool>> descriptorPools;
|
||||||
|
|
||||||
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
|
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
|
||||||
@@ -135,7 +128,6 @@ private:
|
|||||||
std::unordered_map<std::string, int> attributes;
|
std::unordered_map<std::string, int> attributes;
|
||||||
|
|
||||||
uint32_t currentFrame;
|
uint32_t currentFrame;
|
||||||
uint32_t currentUsedUniformStreamBuffersCount;
|
|
||||||
uint32_t currentDescriptorPool;
|
uint32_t currentDescriptorPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user