mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-13 16:08:50 +01:00
[dxvk] Implement lifetime tracking for graphics pipelines
This commit is contained in:
parent
764de6ff82
commit
ab1d629961
@ -219,11 +219,18 @@ namespace dxvk {
|
|||||||
m_signalTracker.reset();
|
m_signalTracker.reset();
|
||||||
m_statCounters.reset();
|
m_statCounters.reset();
|
||||||
|
|
||||||
|
// Recycle descriptor pools
|
||||||
for (const auto& descriptorPools : m_descriptorPools)
|
for (const auto& descriptorPools : m_descriptorPools)
|
||||||
descriptorPools.second->recycleDescriptorPool(descriptorPools.first);
|
descriptorPools.second->recycleDescriptorPool(descriptorPools.first);
|
||||||
|
|
||||||
m_descriptorPools.clear();
|
m_descriptorPools.clear();
|
||||||
|
|
||||||
|
// Release pipelines
|
||||||
|
for (auto pipeline : m_pipelines)
|
||||||
|
pipeline->releasePipeline();
|
||||||
|
|
||||||
|
m_pipelines.clear();
|
||||||
|
|
||||||
m_waitSemaphores.clear();
|
m_waitSemaphores.clear();
|
||||||
m_signalSemaphores.clear();
|
m_signalSemaphores.clear();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "dxvk_fence.h"
|
#include "dxvk_fence.h"
|
||||||
#include "dxvk_gpu_event.h"
|
#include "dxvk_gpu_event.h"
|
||||||
#include "dxvk_gpu_query.h"
|
#include "dxvk_gpu_query.h"
|
||||||
|
#include "dxvk_graphics.h"
|
||||||
#include "dxvk_lifetime.h"
|
#include "dxvk_lifetime.h"
|
||||||
#include "dxvk_limits.h"
|
#include "dxvk_limits.h"
|
||||||
#include "dxvk_pipelayout.h"
|
#include "dxvk_pipelayout.h"
|
||||||
@ -30,7 +31,7 @@ namespace dxvk {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using DxvkCmdBufferFlags = Flags<DxvkCmdBuffer>;
|
using DxvkCmdBufferFlags = Flags<DxvkCmdBuffer>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Queue submission info
|
* \brief Queue submission info
|
||||||
*
|
*
|
||||||
@ -176,6 +177,15 @@ namespace dxvk {
|
|||||||
m_gpuQueryTracker.trackQuery(handle);
|
m_gpuQueryTracker.trackQuery(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tracks a graphics pipeline
|
||||||
|
* \param [in] pipeline Pipeline
|
||||||
|
*/
|
||||||
|
void trackGraphicsPipeline(DxvkGraphicsPipeline* pipeline) {
|
||||||
|
pipeline->acquirePipeline();
|
||||||
|
m_pipelines.push_back(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Queues signal
|
* \brief Queues signal
|
||||||
*
|
*
|
||||||
@ -822,6 +832,8 @@ namespace dxvk {
|
|||||||
Rc<DxvkDescriptorPool>,
|
Rc<DxvkDescriptorPool>,
|
||||||
Rc<DxvkDescriptorManager>>> m_descriptorPools;
|
Rc<DxvkDescriptorManager>>> m_descriptorPools;
|
||||||
|
|
||||||
|
std::vector<DxvkGraphicsPipeline*> m_pipelines;
|
||||||
|
|
||||||
VkCommandBuffer getCmdBuffer(DxvkCmdBuffer cmdBuffer) const {
|
VkCommandBuffer getCmdBuffer(DxvkCmdBuffer cmdBuffer) const {
|
||||||
if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) return m_execBuffer;
|
if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) return m_execBuffer;
|
||||||
if (cmdBuffer == DxvkCmdBuffer::InitBuffer) return m_initBuffer;
|
if (cmdBuffer == DxvkCmdBuffer::InitBuffer) return m_initBuffer;
|
||||||
|
@ -49,6 +49,11 @@ namespace dxvk {
|
|||||||
|
|
||||||
if (m_device->features().extTransformFeedback.transformFeedback)
|
if (m_device->features().extTransformFeedback.transformFeedback)
|
||||||
m_globalRwGraphicsBarrier.access |= VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
|
m_globalRwGraphicsBarrier.access |= VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
|
||||||
|
|
||||||
|
// Store the lifetime tracking bit as a context feature so
|
||||||
|
// that we don't have to scan device features at draw time
|
||||||
|
if (m_device->mustTrackPipelineLifetime())
|
||||||
|
m_features.set(DxvkContextFeature::TrackGraphicsPipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4484,6 +4489,9 @@ namespace dxvk {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_features.test(DxvkContextFeature::TrackGraphicsPipeline))
|
||||||
|
m_cmd->trackGraphicsPipeline(newPipeline);
|
||||||
|
|
||||||
if (unlikely(newPipeline->getSpecConstantMask() != m_state.gp.constants.mask))
|
if (unlikely(newPipeline->getSpecConstantMask() != m_state.gp.constants.mask))
|
||||||
this->resetSpecConstants<VK_PIPELINE_BIND_POINT_GRAPHICS>(newPipeline->getSpecConstantMask());
|
this->resetSpecConstants<VK_PIPELINE_BIND_POINT_GRAPHICS>(newPipeline->getSpecConstantMask());
|
||||||
|
|
||||||
|
@ -60,7 +60,8 @@ namespace dxvk {
|
|||||||
/**
|
/**
|
||||||
* \brief Context feature bits
|
* \brief Context feature bits
|
||||||
*/
|
*/
|
||||||
enum class DxvkContextFeature {
|
enum class DxvkContextFeature : uint32_t {
|
||||||
|
TrackGraphicsPipeline,
|
||||||
FeatureCount
|
FeatureCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -880,15 +880,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {
|
DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {
|
||||||
for (const auto& instance : m_fastPipelines) {
|
this->destroyBasePipelines();
|
||||||
this->destroyPipeline(instance.second);
|
this->destroyOptimizedPipelines();
|
||||||
|
|
||||||
m_vsLibrary->releasePipelineHandle();
|
|
||||||
m_fsLibrary->releasePipelineHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& instance : m_basePipelines)
|
|
||||||
this->destroyPipeline(instance.second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -915,7 +908,7 @@ namespace dxvk {
|
|||||||
return std::make_pair(VK_NULL_HANDLE, DxvkGraphicsPipelineType::FastPipeline);
|
return std::make_pair(VK_NULL_HANDLE, DxvkGraphicsPipelineType::FastPipeline);
|
||||||
|
|
||||||
// Prevent other threads from adding new instances and check again
|
// Prevent other threads from adding new instances and check again
|
||||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
instance = this->findInstance(state);
|
instance = this->findInstance(state);
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
@ -924,6 +917,10 @@ namespace dxvk {
|
|||||||
bool canCreateBasePipeline = this->canCreateBasePipeline(state);
|
bool canCreateBasePipeline = this->canCreateBasePipeline(state);
|
||||||
instance = this->createInstance(state, canCreateBasePipeline);
|
instance = this->createInstance(state, canCreateBasePipeline);
|
||||||
|
|
||||||
|
// Unlock here since we may dispatch the pipeline to a worker,
|
||||||
|
// which will then acquire it to increment the use counter.
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
// If necessary, compile an optimized pipeline variant
|
// If necessary, compile an optimized pipeline variant
|
||||||
if (!instance->fastHandle.load())
|
if (!instance->fastHandle.load())
|
||||||
m_workers->compileGraphicsPipeline(this, state);
|
m_workers->compileGraphicsPipeline(this, state);
|
||||||
@ -965,7 +962,7 @@ namespace dxvk {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Prevent other threads from adding new instances and check again
|
// Prevent other threads from adding new instances and check again
|
||||||
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
instance = this->findInstance(state);
|
instance = this->findInstance(state);
|
||||||
|
|
||||||
if (!instance)
|
if (!instance)
|
||||||
@ -987,6 +984,45 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGraphicsPipeline::acquirePipeline() {
|
||||||
|
if (!m_device->mustTrackPipelineLifetime())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We need to lock here to make sure that any ongoing pipeline
|
||||||
|
// destruction finishes before the calling thread can access the
|
||||||
|
// pipeline, and that no pipelines get destroyed afterwards.
|
||||||
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
m_useCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGraphicsPipeline::releasePipeline() {
|
||||||
|
if (!m_device->mustTrackPipelineLifetime())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
if (!(--m_useCount)) {
|
||||||
|
// Don't destroy base pipelines if that's all we're going to
|
||||||
|
// use, since that would pretty much ruin the experience.
|
||||||
|
if (m_device->config().enableGraphicsPipelineLibrary == Tristate::True)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Exit early if there's nothing to do
|
||||||
|
if (m_basePipelines.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove any base pipeline references, but
|
||||||
|
// keep the optimized pipelines around.
|
||||||
|
for (auto& entry : m_pipelines)
|
||||||
|
entry.baseHandle.store(VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
// Destroy the actual Vulkan pipelines
|
||||||
|
this->destroyBasePipelines();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::createInstance(
|
DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::createInstance(
|
||||||
const DxvkGraphicsPipelineStateInfo& state,
|
const DxvkGraphicsPipelineStateInfo& state,
|
||||||
bool doCreateBasePipeline) {
|
bool doCreateBasePipeline) {
|
||||||
@ -1220,7 +1256,27 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkGraphicsPipeline::destroyPipeline(VkPipeline pipeline) const {
|
void DxvkGraphicsPipeline::destroyBasePipelines() {
|
||||||
|
for (const auto& instance : m_basePipelines) {
|
||||||
|
this->destroyVulkanPipeline(instance.second);
|
||||||
|
|
||||||
|
m_vsLibrary->releasePipelineHandle();
|
||||||
|
m_fsLibrary->releasePipelineHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_basePipelines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGraphicsPipeline::destroyOptimizedPipelines() {
|
||||||
|
for (const auto& instance : m_fastPipelines)
|
||||||
|
this->destroyVulkanPipeline(instance.second);
|
||||||
|
|
||||||
|
m_fastPipelines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGraphicsPipeline::destroyVulkanPipeline(VkPipeline pipeline) const {
|
||||||
auto vk = m_device->vkd();
|
auto vk = m_device->vkd();
|
||||||
|
|
||||||
vk->vkDestroyPipeline(vk->device(), pipeline, nullptr);
|
vk->vkDestroyPipeline(vk->device(), pipeline, nullptr);
|
||||||
|
@ -535,7 +535,24 @@ namespace dxvk {
|
|||||||
*/
|
*/
|
||||||
void compilePipeline(
|
void compilePipeline(
|
||||||
const DxvkGraphicsPipelineStateInfo& state);
|
const DxvkGraphicsPipelineStateInfo& state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Acquires the pipeline
|
||||||
|
*
|
||||||
|
* Increments the use count by one and prevents any Vulkan
|
||||||
|
* pipelines from being destroyed. Must be called before
|
||||||
|
* \ref getPipelineHandle or \ref compilePipeline.
|
||||||
|
*/
|
||||||
|
void acquirePipeline();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Releases the pipeline
|
||||||
|
*
|
||||||
|
* Decrements the use count by one. If the counter reaches
|
||||||
|
* zero, any Vulkan pipeline objects may be destroyed.
|
||||||
|
*/
|
||||||
|
void releasePipeline();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
DxvkDevice* m_device;
|
DxvkDevice* m_device;
|
||||||
@ -560,11 +577,12 @@ namespace dxvk {
|
|||||||
alignas(CACHE_LINE_SIZE)
|
alignas(CACHE_LINE_SIZE)
|
||||||
dxvk::mutex m_mutex;
|
dxvk::mutex m_mutex;
|
||||||
sync::List<DxvkGraphicsPipelineInstance> m_pipelines;
|
sync::List<DxvkGraphicsPipelineInstance> m_pipelines;
|
||||||
|
uint32_t m_useCount = 0;
|
||||||
|
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
DxvkGraphicsPipelineBaseInstanceKey,
|
DxvkGraphicsPipelineBaseInstanceKey,
|
||||||
VkPipeline, DxvkHash, DxvkEq> m_basePipelines;
|
VkPipeline, DxvkHash, DxvkEq> m_basePipelines;
|
||||||
|
|
||||||
alignas(CACHE_LINE_SIZE)
|
alignas(CACHE_LINE_SIZE)
|
||||||
dxvk::mutex m_fastMutex;
|
dxvk::mutex m_fastMutex;
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
@ -595,7 +613,11 @@ namespace dxvk {
|
|||||||
const DxvkGraphicsPipelineFastInstanceKey& key,
|
const DxvkGraphicsPipelineFastInstanceKey& key,
|
||||||
VkPipelineCreateFlags flags) const;
|
VkPipelineCreateFlags flags) const;
|
||||||
|
|
||||||
void destroyPipeline(
|
void destroyBasePipelines();
|
||||||
|
|
||||||
|
void destroyOptimizedPipelines();
|
||||||
|
|
||||||
|
void destroyVulkanPipeline(
|
||||||
VkPipeline pipeline) const;
|
VkPipeline pipeline) const;
|
||||||
|
|
||||||
SpirvCodeBuffer getShaderCode(
|
SpirvCodeBuffer getShaderCode(
|
||||||
|
@ -64,6 +64,7 @@ namespace dxvk {
|
|||||||
std::unique_lock lock(m_queueLock);
|
std::unique_lock lock(m_queueLock);
|
||||||
this->startWorkers();
|
this->startWorkers();
|
||||||
|
|
||||||
|
pipeline->acquirePipeline();
|
||||||
m_pendingTasks += 1;
|
m_pendingTasks += 1;
|
||||||
|
|
||||||
PipelineEntry e = { };
|
PipelineEntry e = { };
|
||||||
@ -148,10 +149,12 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
if (p->computePipeline)
|
if (p->computePipeline) {
|
||||||
p->computePipeline->compilePipeline(p->computeState);
|
p->computePipeline->compilePipeline(p->computeState);
|
||||||
else if (p->graphicsPipeline)
|
} else if (p->graphicsPipeline) {
|
||||||
p->graphicsPipeline->compilePipeline(p->graphicsState);
|
p->graphicsPipeline->compilePipeline(p->graphicsState);
|
||||||
|
p->graphicsPipeline->releasePipeline();
|
||||||
|
}
|
||||||
|
|
||||||
m_pendingTasks -= 1;
|
m_pendingTasks -= 1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user