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_statCounters.reset();
|
||||
|
||||
// Recycle descriptor pools
|
||||
for (const auto& descriptorPools : m_descriptorPools)
|
||||
descriptorPools.second->recycleDescriptorPool(descriptorPools.first);
|
||||
|
||||
m_descriptorPools.clear();
|
||||
|
||||
// Release pipelines
|
||||
for (auto pipeline : m_pipelines)
|
||||
pipeline->releasePipeline();
|
||||
|
||||
m_pipelines.clear();
|
||||
|
||||
m_waitSemaphores.clear();
|
||||
m_signalSemaphores.clear();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "dxvk_fence.h"
|
||||
#include "dxvk_gpu_event.h"
|
||||
#include "dxvk_gpu_query.h"
|
||||
#include "dxvk_graphics.h"
|
||||
#include "dxvk_lifetime.h"
|
||||
#include "dxvk_limits.h"
|
||||
#include "dxvk_pipelayout.h"
|
||||
@ -30,7 +31,7 @@ namespace dxvk {
|
||||
};
|
||||
|
||||
using DxvkCmdBufferFlags = Flags<DxvkCmdBuffer>;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Queue submission info
|
||||
*
|
||||
@ -176,6 +177,15 @@ namespace dxvk {
|
||||
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
|
||||
*
|
||||
@ -822,6 +832,8 @@ namespace dxvk {
|
||||
Rc<DxvkDescriptorPool>,
|
||||
Rc<DxvkDescriptorManager>>> m_descriptorPools;
|
||||
|
||||
std::vector<DxvkGraphicsPipeline*> m_pipelines;
|
||||
|
||||
VkCommandBuffer getCmdBuffer(DxvkCmdBuffer cmdBuffer) const {
|
||||
if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) return m_execBuffer;
|
||||
if (cmdBuffer == DxvkCmdBuffer::InitBuffer) return m_initBuffer;
|
||||
|
@ -49,6 +49,11 @@ namespace dxvk {
|
||||
|
||||
if (m_device->features().extTransformFeedback.transformFeedback)
|
||||
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;
|
||||
}
|
||||
|
||||
if (m_features.test(DxvkContextFeature::TrackGraphicsPipeline))
|
||||
m_cmd->trackGraphicsPipeline(newPipeline);
|
||||
|
||||
if (unlikely(newPipeline->getSpecConstantMask() != m_state.gp.constants.mask))
|
||||
this->resetSpecConstants<VK_PIPELINE_BIND_POINT_GRAPHICS>(newPipeline->getSpecConstantMask());
|
||||
|
||||
|
@ -60,7 +60,8 @@ namespace dxvk {
|
||||
/**
|
||||
* \brief Context feature bits
|
||||
*/
|
||||
enum class DxvkContextFeature {
|
||||
enum class DxvkContextFeature : uint32_t {
|
||||
TrackGraphicsPipeline,
|
||||
FeatureCount
|
||||
};
|
||||
|
||||
|
@ -880,15 +880,8 @@ namespace dxvk {
|
||||
|
||||
|
||||
DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {
|
||||
for (const auto& instance : m_fastPipelines) {
|
||||
this->destroyPipeline(instance.second);
|
||||
|
||||
m_vsLibrary->releasePipelineHandle();
|
||||
m_fsLibrary->releasePipelineHandle();
|
||||
}
|
||||
|
||||
for (const auto& instance : m_basePipelines)
|
||||
this->destroyPipeline(instance.second);
|
||||
this->destroyBasePipelines();
|
||||
this->destroyOptimizedPipelines();
|
||||
}
|
||||
|
||||
|
||||
@ -915,7 +908,7 @@ namespace dxvk {
|
||||
return std::make_pair(VK_NULL_HANDLE, DxvkGraphicsPipelineType::FastPipeline);
|
||||
|
||||
// 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);
|
||||
|
||||
if (!instance) {
|
||||
@ -924,6 +917,10 @@ namespace dxvk {
|
||||
bool canCreateBasePipeline = this->canCreateBasePipeline(state);
|
||||
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 (!instance->fastHandle.load())
|
||||
m_workers->compileGraphicsPipeline(this, state);
|
||||
@ -965,7 +962,7 @@ namespace dxvk {
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
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(
|
||||
const DxvkGraphicsPipelineStateInfo& state,
|
||||
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();
|
||||
|
||||
vk->vkDestroyPipeline(vk->device(), pipeline, nullptr);
|
||||
|
@ -535,7 +535,24 @@ namespace dxvk {
|
||||
*/
|
||||
void compilePipeline(
|
||||
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:
|
||||
|
||||
DxvkDevice* m_device;
|
||||
@ -560,11 +577,12 @@ namespace dxvk {
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
dxvk::mutex m_mutex;
|
||||
sync::List<DxvkGraphicsPipelineInstance> m_pipelines;
|
||||
uint32_t m_useCount = 0;
|
||||
|
||||
std::unordered_map<
|
||||
DxvkGraphicsPipelineBaseInstanceKey,
|
||||
VkPipeline, DxvkHash, DxvkEq> m_basePipelines;
|
||||
|
||||
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
dxvk::mutex m_fastMutex;
|
||||
std::unordered_map<
|
||||
@ -595,7 +613,11 @@ namespace dxvk {
|
||||
const DxvkGraphicsPipelineFastInstanceKey& key,
|
||||
VkPipelineCreateFlags flags) const;
|
||||
|
||||
void destroyPipeline(
|
||||
void destroyBasePipelines();
|
||||
|
||||
void destroyOptimizedPipelines();
|
||||
|
||||
void destroyVulkanPipeline(
|
||||
VkPipeline pipeline) const;
|
||||
|
||||
SpirvCodeBuffer getShaderCode(
|
||||
|
@ -64,6 +64,7 @@ namespace dxvk {
|
||||
std::unique_lock lock(m_queueLock);
|
||||
this->startWorkers();
|
||||
|
||||
pipeline->acquirePipeline();
|
||||
m_pendingTasks += 1;
|
||||
|
||||
PipelineEntry e = { };
|
||||
@ -148,10 +149,12 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
if (p) {
|
||||
if (p->computePipeline)
|
||||
if (p->computePipeline) {
|
||||
p->computePipeline->compilePipeline(p->computeState);
|
||||
else if (p->graphicsPipeline)
|
||||
} else if (p->graphicsPipeline) {
|
||||
p->graphicsPipeline->compilePipeline(p->graphicsState);
|
||||
p->graphicsPipeline->releasePipeline();
|
||||
}
|
||||
|
||||
m_pendingTasks -= 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user