mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-06 22:54:16 +01:00
[dxvk] Support splitting command lists into multipe submissions
This commit is contained in:
parent
e378be826e
commit
6f2ff2562d
@ -72,7 +72,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
VkResult vr = VK_SUCCESS;
|
VkResult vr = VK_SUCCESS;
|
||||||
|
|
||||||
if (submitInfo.waitSemaphoreInfoCount || submitInfo.commandBufferInfoCount || submitInfo.signalSemaphoreInfoCount)
|
if (!this->isEmpty())
|
||||||
vr = vk->vkQueueSubmit2(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
vr = vk->vkQueueSubmit2(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||||
|
|
||||||
this->reset();
|
this->reset();
|
||||||
@ -87,6 +87,13 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DxvkCommandSubmission::isEmpty() const {
|
||||||
|
return m_semaphoreWaits.empty()
|
||||||
|
&& m_semaphoreSignals.empty()
|
||||||
|
&& m_commandBuffers.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DxvkCommandPool::DxvkCommandPool(
|
DxvkCommandPool::DxvkCommandPool(
|
||||||
DxvkDevice* device,
|
DxvkDevice* device,
|
||||||
uint32_t queueFamily)
|
uint32_t queueFamily)
|
||||||
@ -190,10 +197,29 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_commandSubmission.reset();
|
m_commandSubmission.reset();
|
||||||
|
|
||||||
if (m_cmd.usedFlags.test(DxvkCmdBuffer::SdmaBuffer)) {
|
for (size_t i = 0; i < m_cmdSubmissions.size(); i++) {
|
||||||
m_commandSubmission.executeCommandBuffer(m_cmd.sdmaBuffer);
|
bool isFirst = i == 0;
|
||||||
|
bool isLast = i == m_cmdSubmissions.size() - 1;
|
||||||
|
|
||||||
if (m_device->hasDedicatedTransferQueue()) {
|
const auto& cmd = m_cmdSubmissions[i];
|
||||||
|
|
||||||
|
if (isFirst) {
|
||||||
|
// Wait for per-command list semaphores on first submission
|
||||||
|
for (const auto& entry : m_waitSemaphores) {
|
||||||
|
m_commandSubmission.waitSemaphore(
|
||||||
|
entry.fence->handle(),
|
||||||
|
entry.value, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit transfer commands as necessary
|
||||||
|
if (cmd.usedFlags.test(DxvkCmdBuffer::SdmaBuffer))
|
||||||
|
m_commandSubmission.executeCommandBuffer(cmd.sdmaBuffer);
|
||||||
|
|
||||||
|
// If we had either a transfer command or a semaphore wait,
|
||||||
|
// submit to the transfer queue so that all subsequent commands
|
||||||
|
// get stalled appropriately.
|
||||||
|
if (m_device->hasDedicatedTransferQueue() && !m_commandSubmission.isEmpty()) {
|
||||||
m_commandSubmission.signalSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
m_commandSubmission.signalSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
||||||
|
|
||||||
if ((status = m_commandSubmission.submit(m_device, transfer.queueHandle)))
|
if ((status = m_commandSubmission.submit(m_device, transfer.queueHandle)))
|
||||||
@ -201,58 +227,101 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_commandSubmission.waitSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
m_commandSubmission.waitSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We promise to never do weird stuff to WSI images on
|
||||||
|
// the transfer queue, so blocking graphics is sufficient
|
||||||
|
if (isFirst && m_wsiSemaphores.acquire) {
|
||||||
|
m_commandSubmission.waitSemaphore(m_wsiSemaphores.acquire,
|
||||||
|
0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit graphics commands
|
||||||
|
if (cmd.usedFlags.test(DxvkCmdBuffer::InitBuffer))
|
||||||
|
m_commandSubmission.executeCommandBuffer(cmd.initBuffer);
|
||||||
|
|
||||||
|
if (cmd.usedFlags.test(DxvkCmdBuffer::ExecBuffer))
|
||||||
|
m_commandSubmission.executeCommandBuffer(cmd.execBuffer);
|
||||||
|
|
||||||
|
// Signal global timeline semaphore at the end of every submission
|
||||||
|
m_commandSubmission.signalSemaphore(semaphore,
|
||||||
|
++semaphoreValue, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
// Signal per-command list semaphores on the final submission
|
||||||
|
for (const auto& entry : m_signalSemaphores) {
|
||||||
|
m_commandSubmission.signalSemaphore(
|
||||||
|
entry.fence->handle(),
|
||||||
|
entry.value, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal WSI semaphore on the final submission
|
||||||
|
if (m_wsiSemaphores.present) {
|
||||||
|
m_commandSubmission.signalSemaphore(m_wsiSemaphores.present,
|
||||||
|
0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, submit all graphics commands of the current submission
|
||||||
|
if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle)))
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cmd.usedFlags.test(DxvkCmdBuffer::InitBuffer))
|
|
||||||
m_commandSubmission.executeCommandBuffer(m_cmd.initBuffer);
|
|
||||||
|
|
||||||
if (m_cmd.usedFlags.test(DxvkCmdBuffer::ExecBuffer))
|
|
||||||
m_commandSubmission.executeCommandBuffer(m_cmd.execBuffer);
|
|
||||||
|
|
||||||
if (m_wsiSemaphores.acquire) {
|
|
||||||
m_commandSubmission.waitSemaphore(m_wsiSemaphores.acquire,
|
|
||||||
0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_wsiSemaphores.present) {
|
|
||||||
m_commandSubmission.signalSemaphore(m_wsiSemaphores.present,
|
|
||||||
0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& entry : m_waitSemaphores) {
|
|
||||||
m_commandSubmission.waitSemaphore(
|
|
||||||
entry.fence->handle(),
|
|
||||||
entry.value, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& entry : m_signalSemaphores)
|
|
||||||
m_commandSubmission.signalSemaphore(entry.fence->handle(), entry.value, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
|
||||||
|
|
||||||
m_commandSubmission.signalSemaphore(semaphore,
|
|
||||||
++semaphoreValue, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
|
|
||||||
|
|
||||||
if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle)))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkCommandList::beginRecording() {
|
void DxvkCommandList::init() {
|
||||||
m_cmd = DxvkCommandSubmissionInfo();
|
m_cmd = DxvkCommandSubmissionInfo();
|
||||||
|
|
||||||
|
// Grab a fresh set of command buffers from the pools
|
||||||
m_cmd.execBuffer = m_graphicsPool->getCommandBuffer();
|
m_cmd.execBuffer = m_graphicsPool->getCommandBuffer();
|
||||||
m_cmd.initBuffer = m_graphicsPool->getCommandBuffer();
|
m_cmd.initBuffer = m_graphicsPool->getCommandBuffer();
|
||||||
m_cmd.sdmaBuffer = m_transferPool->getCommandBuffer();
|
m_cmd.sdmaBuffer = m_transferPool->getCommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkCommandList::endRecording() {
|
void DxvkCommandList::finalize() {
|
||||||
if (m_vkd->vkEndCommandBuffer(m_cmd.execBuffer) != VK_SUCCESS
|
if (m_cmdSubmissions.empty() || m_cmd.usedFlags != 0)
|
||||||
|| m_vkd->vkEndCommandBuffer(m_cmd.initBuffer) != VK_SUCCESS
|
m_cmdSubmissions.push_back(m_cmd);
|
||||||
|| m_vkd->vkEndCommandBuffer(m_cmd.sdmaBuffer) != VK_SUCCESS)
|
|
||||||
Logger::err("DxvkCommandList::endRecording: Failed to record command buffer");
|
// For consistency, end all command buffers here,
|
||||||
|
// regardless of whether they have been used.
|
||||||
|
this->endCommandBuffer(m_cmd.execBuffer);
|
||||||
|
this->endCommandBuffer(m_cmd.initBuffer);
|
||||||
|
this->endCommandBuffer(m_cmd.sdmaBuffer);
|
||||||
|
|
||||||
|
// Reset all command buffer handles
|
||||||
|
m_cmd = DxvkCommandSubmissionInfo();
|
||||||
|
|
||||||
|
// Increment queue submission count
|
||||||
|
uint64_t submissionCount = m_cmdSubmissions.size();
|
||||||
|
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, submissionCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkCommandList::next() {
|
||||||
|
if (m_cmd.usedFlags != 0)
|
||||||
|
m_cmdSubmissions.push_back(m_cmd);
|
||||||
|
|
||||||
|
// Only replace used command buffer to save resources
|
||||||
|
if (m_cmd.usedFlags.test(DxvkCmdBuffer::ExecBuffer)) {
|
||||||
|
this->endCommandBuffer(m_cmd.execBuffer);
|
||||||
|
m_cmd.execBuffer = m_graphicsPool->getCommandBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cmd.usedFlags.test(DxvkCmdBuffer::InitBuffer)) {
|
||||||
|
this->endCommandBuffer(m_cmd.initBuffer);
|
||||||
|
m_cmd.initBuffer = m_graphicsPool->getCommandBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cmd.usedFlags.test(DxvkCmdBuffer::SdmaBuffer)) {
|
||||||
|
this->endCommandBuffer(m_cmd.sdmaBuffer);
|
||||||
|
m_cmd.sdmaBuffer = m_transferPool->getCommandBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cmd.usedFlags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkCommandList::reset() {
|
void DxvkCommandList::reset() {
|
||||||
// Free resources and other objects
|
// Free resources and other objects
|
||||||
@ -284,6 +353,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_waitSemaphores.clear();
|
m_waitSemaphores.clear();
|
||||||
m_signalSemaphores.clear();
|
m_signalSemaphores.clear();
|
||||||
|
m_cmdSubmissions.clear();
|
||||||
|
|
||||||
m_wsiSemaphores = vk::PresenterSync();
|
m_wsiSemaphores = vk::PresenterSync();
|
||||||
|
|
||||||
@ -292,4 +362,12 @@ namespace dxvk {
|
|||||||
m_transferPool->reset();
|
m_transferPool->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkCommandList::endCommandBuffer(VkCommandBuffer cmdBuffer) {
|
||||||
|
auto vk = m_device->vkd();
|
||||||
|
|
||||||
|
if (vk->vkEndCommandBuffer(cmdBuffer))
|
||||||
|
throw DxvkError("DxvkCommandList: Failed to end command buffer");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,14 @@ namespace dxvk {
|
|||||||
*/
|
*/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether the submission is empty
|
||||||
|
*
|
||||||
|
* \returns \c true if there are no command
|
||||||
|
* buffers or semaphores.
|
||||||
|
*/
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<VkSemaphoreSubmitInfo> m_semaphoreWaits;
|
std::vector<VkSemaphoreSubmitInfo> m_semaphoreWaits;
|
||||||
@ -215,21 +223,29 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Begins recording
|
* \brief Initializes command buffers
|
||||||
*
|
*
|
||||||
* Resets the command buffer and
|
* Prepares command list for command recording.
|
||||||
* begins command buffer recording.
|
|
||||||
*/
|
*/
|
||||||
void beginRecording();
|
void init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Ends recording
|
* \brief Ends recording
|
||||||
*
|
*
|
||||||
* Ends command buffer recording, making
|
* Ends command buffer recording, making
|
||||||
* the command list ready for submission.
|
* the command list ready for submission.
|
||||||
* \param [in] stats Stat counters
|
* \param [in] stats Stat counters
|
||||||
*/
|
*/
|
||||||
void endRecording();
|
void finalize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Interrupts recording
|
||||||
|
*
|
||||||
|
* Begins a new set of command buffers while adding the
|
||||||
|
* current set to the submission list. This can be useful
|
||||||
|
* to split the command list into multiple submissions.
|
||||||
|
*/
|
||||||
|
void next();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Frees buffer slice
|
* \brief Frees buffer slice
|
||||||
@ -972,6 +988,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
std::vector<DxvkFenceValuePair> m_waitSemaphores;
|
std::vector<DxvkFenceValuePair> m_waitSemaphores;
|
||||||
std::vector<DxvkFenceValuePair> m_signalSemaphores;
|
std::vector<DxvkFenceValuePair> m_signalSemaphores;
|
||||||
|
|
||||||
|
std::vector<DxvkCommandSubmissionInfo> m_cmdSubmissions;
|
||||||
|
|
||||||
std::vector<std::pair<
|
std::vector<std::pair<
|
||||||
Rc<DxvkDescriptorPool>,
|
Rc<DxvkDescriptorPool>,
|
||||||
@ -985,7 +1003,9 @@ namespace dxvk {
|
|||||||
if (cmdBuffer == DxvkCmdBuffer::SdmaBuffer) return m_cmd.sdmaBuffer;
|
if (cmdBuffer == DxvkCmdBuffer::SdmaBuffer) return m_cmd.sdmaBuffer;
|
||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void endCommandBuffer(VkCommandBuffer cmdBuffer);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
void DxvkContext::beginRecording(const Rc<DxvkCommandList>& cmdList) {
|
void DxvkContext::beginRecording(const Rc<DxvkCommandList>& cmdList) {
|
||||||
m_cmd = cmdList;
|
m_cmd = cmdList;
|
||||||
m_cmd->beginRecording();
|
m_cmd->init();
|
||||||
|
|
||||||
// Mark all resources as untracked
|
// Mark all resources as untracked
|
||||||
m_vbTracked.clear();
|
m_vbTracked.clear();
|
||||||
@ -120,7 +120,7 @@ namespace dxvk {
|
|||||||
m_descriptorPool = m_descriptorManager->getDescriptorPool();
|
m_descriptorPool = m_descriptorManager->getDescriptorPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cmd->endRecording();
|
m_cmd->finalize();
|
||||||
return std::exchange(m_cmd, nullptr);
|
return std::exchange(m_cmd, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +274,6 @@ namespace dxvk {
|
|||||||
|
|
||||||
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
||||||
m_statCounters.merge(commandList->statCounters());
|
m_statCounters.merge(commandList->statCounters());
|
||||||
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user