Implement descriptor set updating through StateUpdater

Will automatically resolve buffer views at record-time into descriptor write/copy structures then apply the write and bind the set.
This commit is contained in:
Billy Laws 2022-09-22 00:11:35 +01:00
parent d174ca950b
commit 04cea9239f
6 changed files with 149 additions and 43 deletions

View File

@ -69,4 +69,15 @@ namespace skyline::gpu::interconnect::maxwell3d {
using DirtyManager = dirty::Manager<soc::gm20b::engine::EngineMethodsEnd * sizeof(u32), sizeof(u32)>;
class StateUpdateBuilder;
struct DescriptorUpdateInfo {
span<vk::CopyDescriptorSet> copies; //!< These will be performed before writes
span<vk::WriteDescriptorSet> writes;
span<vk::DescriptorBufferInfo> bufferDescs;
span<DynamicBufferBinding> bufferDescDynamicBindings;
vk::PipelineLayout pipelineLayout;
vk::DescriptorSetLayout descriptorSetLayout;
vk::PipelineBindPoint bindPoint;
u32 descriptorSetIndex;
};
}

View File

@ -169,14 +169,29 @@ namespace skyline::gpu::interconnect::maxwell3d {
activeState.Update(ctx, builder, indexed, topology, count);
Pipeline *pipeline{activeState.GetPipeline()};
if (((oldPipeline == pipeline) || (oldPipeline && oldPipeline->CheckBindingMatch(pipeline))) && constantBuffers.quickBindEnabled) {
// If bindings between the old and new pipelines are the same we can reuse the descriptor sets given that quick bind is enabled (meaning that no buffer updates or calls to non-graphics engines have occurred that could invalidate them)
if (constantBuffers.quickBind)
// If only a single constant buffer has been rebound between draws we can perform a partial descriptor update
pipeline->SyncDescriptorsQuickBind(ctx, constantBuffers.boundConstantBuffers, *constantBuffers.quickBind);
} else {
// If bindings have changed or quick bind is disabled, perform a full descriptor update
pipeline->SyncDescriptors(ctx, constantBuffers.boundConstantBuffers);
auto *descUpdateInfo{[&]() -> DescriptorUpdateInfo * {
if (((oldPipeline == pipeline) || (oldPipeline && oldPipeline->CheckBindingMatch(pipeline))) && constantBuffers.quickBindEnabled) {
// If bindings between the old and new pipelines are the same we can reuse the descriptor sets given that quick bind is enabled (meaning that no buffer updates or calls to non-graphics engines have occurred that could invalidate them)
if (constantBuffers.quickBind)
// If only a single constant buffer has been rebound between draws we can perform a partial descriptor update
return pipeline->SyncDescriptorsQuickBind(ctx, constantBuffers.boundConstantBuffers, *constantBuffers.quickBind);
else
return nullptr;
} else {
// If bindings have changed or quick bind is disabled, perform a full descriptor update
return pipeline->SyncDescriptors(ctx, constantBuffers.boundConstantBuffers);
}
}()};
if (descUpdateInfo) {
auto newSet{std::make_shared<DescriptorAllocator::ActiveDescriptorSet>(ctx.gpu.descriptor.AllocateSet(descUpdateInfo->descriptorSetLayout))};
ctx.executor.cycle->AttachObject(newSet);
// Descriptor set lifetime is bound to the current cycle so we can safely use a raw pointer from now on
auto *oldSet{activeDescriptorSet};
activeDescriptorSet = newSet.get();
builder.SetDescriptorSetWithUpdate(descUpdateInfo, activeDescriptorSet, oldSet);
}
const auto &surfaceClip{clearEngineRegisters.surfaceClip};
@ -186,8 +201,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
};
auto stateUpdater{builder.Build()};
ctx.executor.AddSubpass([stateUpdater](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &, GPU &, vk::RenderPass, u32) {
stateUpdater.RecordAll(commandBuffer);
ctx.executor.AddSubpass([stateUpdater](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &, GPU &gpu, vk::RenderPass, u32) {
stateUpdater.RecordAll(gpu, commandBuffer);
}, scissor, {}, activeState.GetColorAttachments(), activeState.GetDepthAttachment());
constantBuffers.ResetQuickBind();

View File

@ -39,6 +39,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
ClearEngineRegisters clearEngineRegisters;
ConstantBuffers constantBuffers;
DescriptorAllocator::ActiveDescriptorSet *activeDescriptorSet{};
vk::Rect2D GetClearScissor();
public:

View File

@ -609,7 +609,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
}
// TODO: EXEC ID FOR STORAGE BUFS PURGE REMAP
void Pipeline::SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers) {
DescriptorUpdateInfo *Pipeline::SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers) {
u32 bindingIdx{};
u32 writeIdx{};
u32 bufferIdx{};
@ -617,7 +617,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
auto writes{ctx.executor.allocator->AllocateUntracked<vk::WriteDescriptorSet>(descriptorInfo.writeDescCount)};
auto bufferDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorBufferInfo>(descriptorInfo.totalBufferDescCount)};
auto bufferDescViews{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(descriptorInfo.totalBufferDescCount)};
auto bufferDescDynamicBindings{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(descriptorInfo.totalBufferDescCount)};
auto writeBufferDescs{[&](vk::DescriptorType type, const auto &descs, u32 count, auto getBufferCb) {
if (!descs.empty()) {
@ -631,7 +631,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
for (size_t descIdx{}; descIdx < descs.size(); descIdx++) {
const auto &shaderDesc{descs[descIdx]};
for (size_t arrayIdx{}; arrayIdx < shaderDesc.count; arrayIdx++)
bufferDescViews[bufferIdx++] = getBufferCb(shaderDesc, descIdx, arrayIdx);
bufferDescDynamicBindings[bufferIdx++] = getBufferCb(shaderDesc, descIdx, arrayIdx);
}
}
}};
@ -652,22 +652,35 @@ namespace skyline::gpu::interconnect::maxwell3d {
return GetStorageBufferBinding(ctx, stage.info, constantBuffers[i][desc.cbuf_index], storageBufferViews[descIdx], descIdx);
});
}
return ctx.executor.allocator->EmplaceUntracked<DescriptorUpdateInfo>(DescriptorUpdateInfo{
.writes = writes,
.bufferDescs = bufferDescs,
.bufferDescDynamicBindings = bufferDescDynamicBindings,
.pipelineLayout = compiledPipeline.pipelineLayout,
.descriptorSetLayout = compiledPipeline.descriptorSetLayout,
.bindPoint = vk::PipelineBindPoint::eGraphics,
.descriptorSetIndex = 0,
});
}
void Pipeline::SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, ConstantBuffers::QuickBind quickBind) {
const auto &cbufUsageInfo{descriptorInfo.cbufUsages[static_cast<size_t>(quickBind.stage)][quickBind.index]};
const auto &shaderInfo{shaderStages[static_cast<size_t>(quickBind.stage)].info};
auto &stageConstantBuffers{constantBuffers[static_cast<size_t>(quickBind.stage)]};
auto copy{ctx.executor.allocator->AllocateUntracked<vk::CopyDescriptorSet>()};
DescriptorUpdateInfo *Pipeline::SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, ConstantBuffers::QuickBind quickBind) {
size_t stageIndex{static_cast<size_t>(quickBind.stage)};
const auto &cbufUsageInfo{descriptorInfo.cbufUsages[stageIndex][quickBind.index]};
const auto &shaderInfo{shaderStages[stageIndex].info};
auto &stageConstantBuffers{constantBuffers[stageIndex]};
auto copies{ctx.executor.allocator->AllocateUntracked<vk::CopyDescriptorSet>(1)};
auto writes{ctx.executor.allocator->AllocateUntracked<vk::WriteDescriptorSet>(cbufUsageInfo.writeDescCount)};
size_t writeIdx{};
size_t bufferIdx{};
auto bufferDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorBufferInfo>(cbufUsageInfo.totalBufferDescCount)};
auto bufferDescViews{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(cbufUsageInfo.totalBufferDescCount)};
auto bufferDescDynamicBindings{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(cbufUsageInfo.totalBufferDescCount)};
// TODO: opt this to do partial copy
*copy = vk::CopyDescriptorSet{
// TODO: opt this to do partial copy and avoid updating twice
copies[0] = vk::CopyDescriptorSet{
.srcBinding = 0,
.srcArrayElement = 0,
.dstBinding = 0,
@ -687,7 +700,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
};
for (size_t i{}; i < shaderDesc.count; i++)
bufferDescViews[bufferIdx++] = getBufferCb(shaderDesc, usage.shaderDescIdx, i);
bufferDescDynamicBindings[bufferIdx++] = getBufferCb(shaderDesc, usage.shaderDescIdx, i);
}
}};
@ -701,6 +714,17 @@ namespace skyline::gpu::interconnect::maxwell3d {
[&](const Shader::StorageBufferDescriptor &desc, size_t descIdx, size_t arrayIdx) {
return GetStorageBufferBinding(ctx, shaderInfo, stageConstantBuffers[desc.cbuf_index], storageBufferViews[bufferIdx], descIdx);
});
return ctx.executor.allocator->EmplaceUntracked<DescriptorUpdateInfo>(DescriptorUpdateInfo{
.copies = copies,
.writes = writes,
.bufferDescs = bufferDescs,
.bufferDescDynamicBindings = bufferDescDynamicBindings,
.pipelineLayout = compiledPipeline.pipelineLayout,
.descriptorSetLayout = compiledPipeline.descriptorSetLayout,
.bindPoint = vk::PipelineBindPoint::eGraphics,
.descriptorSetIndex = 0,
});
}
}

View File

@ -94,9 +94,9 @@ namespace skyline::gpu::interconnect::maxwell3d {
bool CheckBindingMatch(Pipeline *other);
void SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers);
DescriptorUpdateInfo *SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers);
void SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, ConstantBuffers::QuickBind quickBind);
DescriptorUpdateInfo *SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, ConstantBuffers::QuickBind quickBind);
};
class PipelineManager {

View File

@ -12,7 +12,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
*/
struct StateUpdateCmdHeader {
StateUpdateCmdHeader *next;
using RecordFunc = void (*)(vk::raii::CommandBuffer &commandBuffer, StateUpdateCmdHeader *header);
using RecordFunc = void (*)(GPU &gpu, vk::raii::CommandBuffer &commandBuffer, StateUpdateCmdHeader *header);
RecordFunc record;
};
@ -30,13 +30,13 @@ namespace skyline::gpu::interconnect::maxwell3d {
CmdHolder() = default;
static void Record(vk::raii::CommandBuffer &commandBuffer, StateUpdateCmdHeader *header) {
reinterpret_cast<CmdHolder *>(header)->cmd.Record(commandBuffer);
static void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer, StateUpdateCmdHeader *header) {
reinterpret_cast<CmdHolder *>(header)->cmd.Record(gpu, commandBuffer);
}
};
struct SetVertexBuffersCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.bindVertexBuffers(firstBinding,
span(buffers).subspan(firstBinding, bindingCount),
span(offsets).subspan(firstBinding, bindingCount));
@ -50,7 +50,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetVertexBuffersCmd = CmdHolder<SetVertexBuffersCmdImpl>;
struct SetVertexBuffersDynamicCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
for (u32 i{base.firstBinding}; i < base.firstBinding + base.bindingCount; i++) {
base.buffers[i] = views[i].GetBuffer()->GetBacking();
base.offsets[i] = views[i].GetOffset();
@ -59,13 +59,13 @@ namespace skyline::gpu::interconnect::maxwell3d {
base.Record(commandBuffer);
}
SetVertexBuffersCmdImpl base;
SetVertexBuffersCmdImpl base{};
std::array<BufferView, engine::VertexStreamCount> views;
};
using SetVertexBuffersDynamicCmd = CmdHolder<SetVertexBuffersDynamicCmdImpl>;
struct SetIndexBufferCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.bindIndexBuffer(buffer, offset, indexType);
}
@ -76,7 +76,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetIndexBufferCmd = CmdHolder<SetIndexBufferCmdImpl>;
struct SetIndexBufferDynamicCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
base.buffer = view.GetBuffer()->GetBacking();
base.offset = view.GetOffset();
base.Record(commandBuffer);
@ -88,7 +88,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetIndexBufferDynamicCmd = CmdHolder<SetIndexBufferDynamicCmdImpl>;
struct SetTransformFeedbackBufferCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.bindTransformFeedbackBuffersEXT(binding, buffer, offset, size);
}
@ -100,7 +100,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetTransformFeedbackBufferCmd = CmdHolder<SetTransformFeedbackBufferCmdImpl>;
struct SetTransformFeedbackBufferDynamicCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
base.buffer = view.GetBuffer()->GetBacking();
base.offset = view.GetOffset();
base.Record(commandBuffer);
@ -112,7 +112,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetTransformFeedbackBufferDynamicCmd = CmdHolder<SetTransformFeedbackBufferDynamicCmdImpl>;
struct SetViewportCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setViewport(index, viewport);
}
@ -122,7 +122,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetViewportCmd = CmdHolder<SetViewportCmdImpl>;
struct SetScissorCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setScissor(index, scissor);
}
@ -132,7 +132,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetScissorCmd = CmdHolder<SetScissorCmdImpl>;
struct SetLineWidthCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setLineWidth(lineWidth);
}
@ -141,7 +141,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetLineWidthCmd = CmdHolder<SetLineWidthCmdImpl>;
struct SetDepthBiasCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setDepthBias(depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor);
}
@ -152,7 +152,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetDepthBiasCmd = CmdHolder<SetDepthBiasCmdImpl>;
struct SetBlendConstantsCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setBlendConstants(blendConstants.data());
}
@ -161,7 +161,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetBlendConstantsCmd = CmdHolder<SetBlendConstantsCmdImpl>;
struct SetDepthBoundsCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setDepthBounds(minDepthBounds, maxDepthBounds);
}
@ -171,7 +171,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
using SetDepthBoundsCmd = CmdHolder<SetDepthBoundsCmdImpl>;
struct SetBaseStencilStateCmdImpl {
void Record(vk::raii::CommandBuffer &commandBuffer) {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
commandBuffer.setStencilCompareMask(flags, funcMask);
commandBuffer.setStencilReference(flags, funcRef);
commandBuffer.setStencilWriteMask(flags, mask);
@ -184,6 +184,52 @@ namespace skyline::gpu::interconnect::maxwell3d {
};
using SetBaseStencilStateCmd = CmdHolder<SetBaseStencilStateCmdImpl>;
struct SetDescriptorSetWithUpdateCmdImpl {
void Record(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) {
// Resolve descriptor infos from dynamic bindings
for (size_t i{}; i < updateInfo->bufferDescDynamicBindings.size(); i++) {
auto &dynamicBinding{updateInfo->bufferDescDynamicBindings[i]};
if (auto view{std::get_if<BufferView>(&dynamicBinding)}) {
updateInfo->bufferDescs[i] = vk::DescriptorBufferInfo{
.buffer = view->GetBuffer()->GetBacking(),
.offset = view->GetOffset(),
.range = view->size
};
} else if (auto binding{std::get_if<BufferBinding>(&dynamicBinding)}) {
updateInfo->bufferDescs[i] = vk::DescriptorBufferInfo{
.buffer = binding->buffer,
.offset = binding->offset,
.range = binding->size
};
}
}
// Set the destination/(source) descriptor set(s) for all writes/(copies)
for (auto &write : updateInfo->writes)
write.dstSet = **dstSet;
for (auto &copy : updateInfo->copies) {
copy.dstSet = **dstSet;
copy.srcSet = **srcSet;
}
// Perform the updates, doing copies first to avoid overwriting
if (!updateInfo->copies.empty())
gpu.vkDevice.updateDescriptorSets({}, updateInfo->copies);
if (!updateInfo->writes.empty())
gpu.vkDevice.updateDescriptorSets(updateInfo->writes, {});
// Bind the updated descriptor set and we're done!
commandBuffer.bindDescriptorSets(updateInfo->bindPoint, updateInfo->pipelineLayout, updateInfo->descriptorSetIndex, **dstSet, 0);
}
DescriptorUpdateInfo *updateInfo;
DescriptorAllocator::ActiveDescriptorSet *srcSet;
DescriptorAllocator::ActiveDescriptorSet *dstSet;
};
using SetDescriptorSetWithUpdateCmd = CmdHolder<SetDescriptorSetWithUpdateCmdImpl>;
/**
* @brief Single-use helper for recording a batch of state updates into a command buffer
*/
@ -197,9 +243,9 @@ namespace skyline::gpu::interconnect::maxwell3d {
/**
* @brief Records all contained state updates into the given command buffer
*/
void RecordAll(vk::raii::CommandBuffer &commandBuffer) const {
void RecordAll(GPU &gpu, vk::raii::CommandBuffer &commandBuffer) const {
for (StateUpdateCmdHeader *cmd{first}; cmd; cmd = cmd->next)
cmd->record(commandBuffer, cmd);
cmd->record(gpu, commandBuffer, cmd);
}
};
@ -359,5 +405,13 @@ namespace skyline::gpu::interconnect::maxwell3d {
.mask = mask,
});
}
void SetDescriptorSetWithUpdate(DescriptorUpdateInfo *updateInfo, DescriptorAllocator::ActiveDescriptorSet *dstSet, DescriptorAllocator::ActiveDescriptorSet *srcSet) {
AppendCmd<SetDescriptorSetWithUpdateCmd>({
.updateInfo = updateInfo,
.srcSet = srcSet,
.dstSet = dstSet,
});
}
};
}