├── entrypoint.sh ├── Dockerfile ├── README.md └── pipeline.patch /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DXVK_VERSION=$1 3 | 4 | git clone https://github.com/doitsujin/dxvk 5 | cd dxvk 6 | git checkout "$DXVK_VERSION" 7 | git apply /root/pipeline.patch 8 | ./package-release.sh "$DXVK_VERSION" /output --no-package 9 | 10 | exec "$@" 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | WORKDIR /root 4 | 5 | RUN apt-get update \ 6 | && apt-get install -y --no-install-recommends \ 7 | meson \ 8 | git \ 9 | build-essential \ 10 | ca-certificates \ 11 | software-properties-common \ 12 | wget \ 13 | gpg-agent 14 | 15 | RUN dpkg --add-architecture i386 \ 16 | && wget -O- https://dl.winehq.org/wine-builds/winehq.key | apt-key add -\ 17 | && apt-add-repository -y 'deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main' \ 18 | && apt-get install -y --install-recommends winehq-staging \ 19 | && wine --version 20 | 21 | RUN add-apt-repository -y ppa:graphics-drivers/ppa \ 22 | && apt-get install -y --no-install-recommends \ 23 | glslang-dev \ 24 | glslang-tools 25 | 26 | RUN add-apt-repository -y ppa:mati865/mingw-w64 \ 27 | && apt-get upgrade -y \ 28 | && apt-get install -y mingw-w64 29 | 30 | RUN echo 1 | update-alternatives --config x86_64-w64-mingw32-gcc \ 31 | && echo 1 | update-alternatives --config x86_64-w64-mingw32-g++ \ 32 | && echo 1 | update-alternatives --config i686-w64-mingw32-gcc \ 33 | && echo 1 | update-alternatives --config i686-w64-mingw32-g++ 34 | 35 | COPY pipeline.patch ./pipeline.patch 36 | COPY entrypoint.sh ./entrypoint.sh 37 | 38 | ENTRYPOINT ["/root/entrypoint.sh"] 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unmaintained 2 | I don't use this for anything currently, so you are on your own. Sorry! 3 | 4 | 5 | # WARNING 6 | Use only at your discretion. 7 | 8 | While this has nothing to do with cheating, you may be banned from online games if anti-cheat systems doesn't like it. 9 | The repository's name contains the word 'hack' only because the author didn't think of its association to cheating. 10 | 11 | ## What? 12 | Originally started as hacky solution for shader compilation stutter in dxvk. Similar solution was later added to dxvk itself and promptly 13 | removed. This patch restores it again. 14 | 15 | Not enabled by default 16 | 17 | ### Games tested 18 | 19 | * Path of Exile: seems fine. Disabling state cache (`DXVK_STATE_CACHE=0`) is recommended, though. 20 | 21 | ### Instructions 22 | 23 | * Patch dxvk with pipeline.patch 24 | * Set environment variable `DXVK_ASYNC=1` 25 | 26 | ### Docker instructions 27 | 28 | * Clone the repository 29 | * cd into it 30 | * Build image with `docker build . -t image_name` 31 | * Run container with `docker run -it -v $(pwd)/dxvk_output:/output image_name v0.95`. Replace v0.95 with desired release. 32 | * Binaries will be available in dxvk_output directory. They will have root ownership. Change it to your user using `sudo chown your_user:your_user_group dxvk_output -R` 33 | -------------------------------------------------------------------------------- /pipeline.patch: -------------------------------------------------------------------------------- 1 | diff --git a/meson.build b/meson.build 2 | index 1e3c8ccd..b93ae1d7 100644 3 | --- a/meson.build 4 | +++ b/meson.build 5 | @@ -90,7 +90,7 @@ else 6 | endif 7 | 8 | dxvk_version = vcs_tag( 9 | - command: ['git', 'describe', '--dirty=+'], 10 | + command: ['git', 'describe', '--dirty=-async'], 11 | input: 'version.h.in', 12 | output: 'version.h') 13 | 14 | diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp 15 | index f96ef293..d2551494 100644 16 | --- a/src/dxvk/dxvk_context.cpp 17 | +++ b/src/dxvk/dxvk_context.cpp 18 | @@ -598,7 +598,7 @@ namespace dxvk { 19 | const Rc& imageView, 20 | VkImageAspectFlags clearAspects, 21 | VkClearValue clearValue) { 22 | - this->updateFramebuffer(); 23 | + this->updateFramebuffer(false); 24 | 25 | // Prepare attachment ops 26 | DxvkColorAttachmentOps colorOp; 27 | @@ -2258,7 +2258,7 @@ namespace dxvk { 28 | VkExtent3D extent, 29 | VkImageAspectFlags aspect, 30 | VkClearValue value) { 31 | - this->updateFramebuffer(); 32 | + this->updateFramebuffer(false); 33 | 34 | // Find out if the render target view is currently bound, 35 | // so that we can avoid spilling the render pass if it is. 36 | @@ -3379,7 +3379,7 @@ namespace dxvk { 37 | // Retrieve and bind actual Vulkan pipeline handle 38 | m_gpActivePipeline = m_state.gp.pipeline != nullptr && m_state.om.framebuffer != nullptr 39 | ? m_state.gp.pipeline->getPipelineHandle(m_state.gp.state, 40 | - m_state.om.framebuffer->getRenderPass()) 41 | + m_state.om.framebuffer->getRenderPass(), this->checkAsyncCompilationCompat()) 42 | : VK_NULL_HANDLE; 43 | 44 | if (m_gpActivePipeline != VK_NULL_HANDLE) { 45 | @@ -3648,7 +3648,7 @@ namespace dxvk { 46 | } 47 | 48 | 49 | - void DxvkContext::updateFramebuffer() { 50 | + void DxvkContext::updateFramebuffer(bool isDraw) { 51 | if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) { 52 | m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer); 53 | 54 | @@ -3667,6 +3667,11 @@ namespace dxvk { 55 | : VkComponentMapping(); 56 | } 57 | 58 | + if (isDraw) { 59 | + for (uint32_t i = 0; i < fb->numAttachments(); i++) 60 | + fb->getAttachment(i).view->setRtBindingFrameId(m_device->getCurrentFrameId()); 61 | + } 62 | + 63 | m_flags.set(DxvkContextFlag::GpDirtyPipelineState); 64 | } 65 | } 66 | @@ -3901,7 +3906,7 @@ namespace dxvk { 67 | 68 | void DxvkContext::commitGraphicsState(bool indexed) { 69 | if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) 70 | - this->updateFramebuffer(); 71 | + this->updateFramebuffer(true); 72 | 73 | if (!m_flags.test(DxvkContextFlag::GpRenderPassBound)) 74 | this->startRenderPass(); 75 | @@ -4149,4 +4154,13 @@ namespace dxvk { 76 | } 77 | } 78 | 79 | -} 80 | \ No newline at end of file 81 | + 82 | + bool DxvkContext::checkAsyncCompilationCompat() { 83 | + bool fbCompat = true; 84 | + for (uint32_t i = 0; fbCompat && i < m_state.om.framebuffer->numAttachments(); i++) { 85 | + const auto& attachment = m_state.om.framebuffer->getAttachment(i); 86 | + fbCompat &= attachment.view->getRtBindingAsyncCompilationCompat(); 87 | + } 88 | + return fbCompat; 89 | + } 90 | +} 91 | diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h 92 | index 9daf2b4c..9fad59ef 100644 93 | --- a/src/dxvk/dxvk_context.h 94 | +++ b/src/dxvk/dxvk_context.h 95 | @@ -1079,7 +1079,7 @@ namespace dxvk { 96 | VkDescriptorSet set, 97 | const DxvkPipelineLayout* layout); 98 | 99 | - void updateFramebuffer(); 100 | + void updateFramebuffer(bool isDraw); 101 | 102 | void updateIndexBufferBinding(); 103 | void updateVertexBufferBindings(); 104 | @@ -1105,6 +1105,8 @@ namespace dxvk { 105 | 106 | void commitGraphicsPostBarriers(); 107 | 108 | + bool checkAsyncCompilationCompat(); 109 | + 110 | void emitMemoryBarrier( 111 | VkPipelineStageFlags srcStages, 112 | VkAccessFlags srcAccess, 113 | diff --git a/src/dxvk/dxvk_framebuffer.h b/src/dxvk/dxvk_framebuffer.h 114 | index 49133845..ff80297b 100644 115 | --- a/src/dxvk/dxvk_framebuffer.h 116 | +++ b/src/dxvk/dxvk_framebuffer.h 117 | @@ -120,8 +120,8 @@ namespace dxvk { 118 | * \brief Retrieves render pass 119 | * \returns Render pass reference 120 | */ 121 | - const DxvkRenderPass& getRenderPass() const { 122 | - return *m_renderPass; 123 | + const Rc& getRenderPass() const { 124 | + return m_renderPass; 125 | } 126 | 127 | /** 128 | @@ -221,4 +221,4 @@ namespace dxvk { 129 | 130 | }; 131 | 132 | -} 133 | \ No newline at end of file 134 | +} 135 | diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp 136 | index 49d2c168..286e859d 100644 137 | --- a/src/dxvk/dxvk_graphics.cpp 138 | +++ b/src/dxvk/dxvk_graphics.cpp 139 | @@ -4,6 +4,7 @@ 140 | #include "dxvk_device.h" 141 | #include "dxvk_graphics.h" 142 | #include "dxvk_pipemanager.h" 143 | +#include "dxvk_pipecompiler.h" 144 | #include "dxvk_spec_const.h" 145 | #include "dxvk_state_cache.h" 146 | 147 | @@ -79,8 +80,6 @@ namespace dxvk { 148 | 149 | 150 | DxvkGraphicsPipeline::~DxvkGraphicsPipeline() { 151 | - for (const auto& instance : m_pipelines) 152 | - this->destroyPipeline(instance.pipeline()); 153 | } 154 | 155 | 156 | @@ -100,8 +99,9 @@ namespace dxvk { 157 | 158 | VkPipeline DxvkGraphicsPipeline::getPipelineHandle( 159 | const DxvkGraphicsPipelineStateInfo& state, 160 | - const DxvkRenderPass& renderPass) { 161 | - VkRenderPass renderPassHandle = renderPass.getDefaultHandle(); 162 | + const Rc& renderPass, 163 | + bool async) { 164 | + VkRenderPass renderPassHandle = renderPass->getDefaultHandle(); 165 | 166 | VkPipeline newPipelineHandle = VK_NULL_HANDLE; 167 | 168 | @@ -119,29 +119,49 @@ namespace dxvk { 169 | 170 | // If no pipeline instance exists with the given state 171 | // vector, create a new one and add it to the list. 172 | - newPipelineHandle = this->compilePipeline(state, renderPass, m_basePipeline); 173 | + Rc newPipeline = 174 | + new DxvkGraphicsPipelineInstance(m_pipeMgr->m_device->vkd(), 175 | + state, renderPassHandle, VK_NULL_HANDLE); 176 | + 177 | + if (async && m_pipeMgr->m_compiler != nullptr) 178 | + m_pipeMgr->m_compiler->queueCompilation(this, newPipeline, renderPass); 179 | + else 180 | + newPipelineHandle = this->compileInstance(newPipeline, renderPass); 181 | 182 | // Add new pipeline to the set 183 | - m_pipelines.emplace_back(state, renderPassHandle, newPipelineHandle); 184 | + m_pipelines.push_back(newPipeline); 185 | m_pipeMgr->m_numGraphicsPipelines += 1; 186 | - 187 | - if (!m_basePipeline && newPipelineHandle) 188 | - m_basePipeline = newPipelineHandle; 189 | } 190 | 191 | - if (newPipelineHandle != VK_NULL_HANDLE) 192 | - this->writePipelineStateToCache(state, renderPass.format()); 193 | - 194 | return newPipelineHandle; 195 | } 196 | 197 | 198 | - const DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance( 199 | + VkPipeline DxvkGraphicsPipeline::compileInstance( 200 | + const Rc& instance, 201 | + const Rc& renderPass) { 202 | + VkPipeline newPipelineHandle = this->compilePipeline( 203 | + instance->m_stateVector, *renderPass, m_basePipeline); 204 | + 205 | + if (!instance->setPipeline(newPipelineHandle)) { 206 | + m_vkd->vkDestroyPipeline(m_vkd->device(), newPipelineHandle, nullptr); 207 | + } else { 208 | + if (!m_basePipeline && newPipelineHandle) 209 | + m_basePipeline = newPipelineHandle; 210 | + if (newPipelineHandle != VK_NULL_HANDLE) 211 | + this->writePipelineStateToCache(instance->m_stateVector, renderPass->format()); 212 | + } 213 | + 214 | + return newPipelineHandle; 215 | + } 216 | + 217 | + 218 | + DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance( 219 | const DxvkGraphicsPipelineStateInfo& state, 220 | VkRenderPass renderPass) const { 221 | for (const auto& instance : m_pipelines) { 222 | - if (instance.isCompatible(state, renderPass)) 223 | - return &instance; 224 | + if (instance->isCompatible(state, renderPass)) 225 | + return instance.ptr(); 226 | } 227 | 228 | return nullptr; 229 | diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h 230 | index e5686950..8811f10c 100644 231 | --- a/src/dxvk/dxvk_graphics.h 232 | +++ b/src/dxvk/dxvk_graphics.h 233 | @@ -132,19 +132,25 @@ namespace dxvk { 234 | * Stores a state vector and the 235 | * corresponding pipeline handle. 236 | */ 237 | - class DxvkGraphicsPipelineInstance { 238 | - 239 | + class DxvkGraphicsPipelineInstance: public RcObject { 240 | + friend class DxvkGraphicsPipeline; 241 | public: 242 | 243 | DxvkGraphicsPipelineInstance() { } 244 | DxvkGraphicsPipelineInstance( 245 | + const Rc& vkd, 246 | const DxvkGraphicsPipelineStateInfo& state, 247 | VkRenderPass rp, 248 | VkPipeline pipe) 249 | - : m_stateVector (state), 250 | + : m_vkd (vkd), 251 | + m_stateVector (state), 252 | m_renderPass (rp), 253 | m_pipeline (pipe) { } 254 | 255 | + ~DxvkGraphicsPipelineInstance() { 256 | + m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr); 257 | + } 258 | + 259 | /** 260 | * \brief Checks for matching pipeline state 261 | * 262 | @@ -159,23 +165,38 @@ namespace dxvk { 263 | && m_renderPass == rp; 264 | } 265 | 266 | + /** 267 | + * \brief Sets the pipeline handle 268 | + * 269 | + * If a pipeline handle has already been 270 | + * set up, this method will fail and the new pipeline 271 | + * handle should be destroyed. 272 | + * \param [in] pipeline The pipeline 273 | + */ 274 | + bool setPipeline(VkPipeline pipeline) { 275 | + VkPipeline expected = VK_NULL_HANDLE; 276 | + return m_pipeline.compare_exchange_strong(expected, pipeline); 277 | + } 278 | + 279 | /** 280 | * \brief Retrieves pipeline 281 | * \returns The pipeline handle 282 | */ 283 | VkPipeline pipeline() const { 284 | - return m_pipeline; 285 | + return m_pipeline.load(); 286 | } 287 | 288 | private: 289 | 290 | + const Rc m_vkd; 291 | DxvkGraphicsPipelineStateInfo m_stateVector; 292 | VkRenderPass m_renderPass; 293 | - VkPipeline m_pipeline; 294 | + std::atomic m_pipeline; 295 | 296 | }; 297 | 298 | 299 | + 300 | /** 301 | * \brief Graphics pipeline 302 | * 303 | @@ -234,20 +255,20 @@ namespace dxvk { 304 | * state. If necessary, a new pipeline will be created. 305 | * \param [in] state Pipeline state vector 306 | * \param [in] renderPass The render pass 307 | + * \param [in] async Compile asynchronously 308 | * \returns Pipeline handle 309 | */ 310 | VkPipeline getPipelineHandle( 311 | const DxvkGraphicsPipelineStateInfo& state, 312 | - const DxvkRenderPass& renderPass); 313 | + const Rc& renderPass, 314 | + bool async); 315 | + 316 | + VkPipeline compileInstance( 317 | + const Rc& instance, 318 | + const Rc& renderPass); 319 | 320 | private: 321 | 322 | - struct PipelineStruct { 323 | - DxvkGraphicsPipelineStateInfo stateVector; 324 | - VkRenderPass renderPass; 325 | - VkPipeline pipeline; 326 | - }; 327 | - 328 | Rc m_vkd; 329 | DxvkPipelineManager* m_pipeMgr; 330 | 331 | @@ -267,13 +288,13 @@ namespace dxvk { 332 | DxvkGraphicsCommonPipelineStateInfo m_common; 333 | 334 | // List of pipeline instances, shared between threads 335 | - alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex; 336 | - std::vector m_pipelines; 337 | + alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex; 338 | + std::vector> m_pipelines; 339 | 340 | // Pipeline handles used for derivative pipelines 341 | VkPipeline m_basePipeline = VK_NULL_HANDLE; 342 | 343 | - const DxvkGraphicsPipelineInstance* findInstance( 344 | + DxvkGraphicsPipelineInstance* findInstance( 345 | const DxvkGraphicsPipelineStateInfo& state, 346 | VkRenderPass renderPass) const; 347 | 348 | @@ -302,4 +323,4 @@ namespace dxvk { 349 | 350 | }; 351 | 352 | -} 353 | \ No newline at end of file 354 | +} 355 | diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h 356 | index df06930e..a06df9a7 100644 357 | --- a/src/dxvk/dxvk_image.h 358 | +++ b/src/dxvk/dxvk_image.h 359 | @@ -431,6 +431,37 @@ namespace dxvk { 360 | return result; 361 | } 362 | 363 | + /** 364 | + * \brief Sets render target usage frame number 365 | + * 366 | + * The image view will track internally when 367 | + * it was last used as a render target. This 368 | + * info is used for async shader compilation. 369 | + * \param [in] frameId Frame number 370 | + */ 371 | + void setRtBindingFrameId(uint32_t frameId) { 372 | + if (frameId != m_rtBindingFrameId) { 373 | + if (frameId == m_rtBindingFrameId + 1) 374 | + m_rtBindingFrameCount += 1; 375 | + else 376 | + m_rtBindingFrameCount = 0; 377 | + 378 | + m_rtBindingFrameId = frameId; 379 | + } 380 | + } 381 | + 382 | + /** 383 | + * \brief Checks for async pipeline compatibility 384 | + * 385 | + * Asynchronous pipeline compilation may be enabled if the 386 | + * render target has been drawn to in the previous frames. 387 | + * \param [in] frameId Current frame ID 388 | + * \returns \c true if async compilation is supported 389 | + */ 390 | + bool getRtBindingAsyncCompilationCompat() const { 391 | + return m_rtBindingFrameCount >= 5; 392 | + } 393 | + 394 | private: 395 | 396 | Rc m_vkd; 397 | @@ -439,6 +470,9 @@ namespace dxvk { 398 | DxvkImageViewCreateInfo m_info; 399 | VkImageView m_views[ViewCount]; 400 | 401 | + uint32_t m_rtBindingFrameId = 0; 402 | + uint32_t m_rtBindingFrameCount = 0; 403 | + 404 | void createView(VkImageViewType type, uint32_t numLayers); 405 | 406 | }; 407 | diff --git a/src/dxvk/dxvk_pipecompiler.cpp b/src/dxvk/dxvk_pipecompiler.cpp 408 | new file mode 100644 409 | index 00000000..a70c9908 410 | --- /dev/null 411 | +++ b/src/dxvk/dxvk_pipecompiler.cpp 412 | @@ -0,0 +1,69 @@ 413 | +#include "dxvk_graphics.h" 414 | +#include "dxvk_pipecompiler.h" 415 | + 416 | +namespace dxvk { 417 | + 418 | + DxvkPipelineCompiler::DxvkPipelineCompiler() { 419 | + uint32_t sysCpuCount = dxvk::thread::hardware_concurrency(); 420 | + uint32_t threadCount = sysCpuCount > 2 ? sysCpuCount - 2 : 1; 421 | + 422 | + Logger::info(str::format( 423 | + "DxvkPipelineCompiler: Using ", 424 | + threadCount, " workers")); 425 | + 426 | + // Start the compiler threads 427 | + m_compilerThreads.resize(threadCount); 428 | + 429 | + for (uint32_t i = 0; i < threadCount; i++) { 430 | + m_compilerThreads.at(i) = dxvk::thread( 431 | + [this] { this->runCompilerThread(); }); 432 | + } 433 | + } 434 | + 435 | + 436 | + DxvkPipelineCompiler::~DxvkPipelineCompiler() { 437 | + { std::unique_lock lock(m_compilerLock); 438 | + m_compilerStop.store(true); 439 | + } 440 | + 441 | + m_compilerCond.notify_all(); 442 | + for (auto& thread : m_compilerThreads) 443 | + thread.join(); 444 | + } 445 | + 446 | + 447 | + void DxvkPipelineCompiler::queueCompilation( 448 | + const Rc& pipeline, 449 | + const Rc& instance, 450 | + const Rc& renderPass) { 451 | + std::unique_lock lock(m_compilerLock); 452 | + m_compilerQueue.push({ pipeline, instance, renderPass }); 453 | + m_compilerCond.notify_one(); 454 | + } 455 | + 456 | + 457 | + void DxvkPipelineCompiler::runCompilerThread() { 458 | + env::setThreadName("dxvk-pcompiler"); 459 | + 460 | + while (!m_compilerStop.load()) { 461 | + PipelineEntry entry; 462 | + 463 | + { std::unique_lock lock(m_compilerLock); 464 | + 465 | + m_compilerCond.wait(lock, [this] { 466 | + return m_compilerStop.load() 467 | + || m_compilerQueue.size() != 0; 468 | + }); 469 | + 470 | + if (m_compilerQueue.size() != 0) { 471 | + entry = std::move(m_compilerQueue.front()); 472 | + m_compilerQueue.pop(); 473 | + } 474 | + } 475 | + 476 | + if (entry.pipeline != nullptr && entry.instance != nullptr) 477 | + entry.pipeline->compileInstance(entry.instance, entry.renderPass); 478 | + } 479 | + } 480 | + 481 | +} 482 | diff --git a/src/dxvk/dxvk_pipecompiler.h b/src/dxvk/dxvk_pipecompiler.h 483 | new file mode 100644 484 | index 00000000..26e983cc 485 | --- /dev/null 486 | +++ b/src/dxvk/dxvk_pipecompiler.h 487 | @@ -0,0 +1,60 @@ 488 | +#pragma once 489 | + 490 | +#include 491 | +#include 492 | +#include 493 | +#include 494 | + 495 | +#include "../util/thread.h" 496 | +#include "dxvk_include.h" 497 | + 498 | +namespace dxvk { 499 | + 500 | + class DxvkGraphicsPipeline; 501 | + class DxvkGraphicsPipelineInstance; 502 | + 503 | + /** 504 | + * \brief Pipeline compiler 505 | + * 506 | + * Asynchronous pipeline compiler 507 | + */ 508 | + class DxvkPipelineCompiler : public RcObject { 509 | + 510 | + public: 511 | + 512 | + DxvkPipelineCompiler(); 513 | + ~DxvkPipelineCompiler(); 514 | + 515 | + /** 516 | + * \brief Compiles a pipeline asynchronously 517 | + * 518 | + * This should be used to compile graphics 519 | + * pipeline instances asynchronously. 520 | + * \param [in] pipeline The pipeline object 521 | + * \param [in] instance The pipeline instance 522 | + * \param [in] renderPass 523 | + */ 524 | + void queueCompilation( 525 | + const Rc& pipeline, 526 | + const Rc& instance, 527 | + const Rc& renderPass); 528 | + 529 | + private: 530 | + 531 | + struct PipelineEntry { 532 | + Rc pipeline; 533 | + Rc instance; 534 | + Rc renderPass; 535 | + }; 536 | + 537 | + std::atomic m_compilerStop = { false }; 538 | + std::mutex m_compilerLock; 539 | + std::condition_variable m_compilerCond; 540 | + std::queue m_compilerQueue; 541 | + std::vector m_compilerThreads; 542 | + 543 | + void runCompilerThread(); 544 | + 545 | + }; 546 | + 547 | +} 548 | diff --git a/src/dxvk/dxvk_pipemanager.cpp b/src/dxvk/dxvk_pipemanager.cpp 549 | index 682db83d..2783c847 100644 550 | --- a/src/dxvk/dxvk_pipemanager.cpp 551 | +++ b/src/dxvk/dxvk_pipemanager.cpp 552 | @@ -45,9 +45,13 @@ namespace dxvk { 553 | : m_device (device), 554 | m_cache (new DxvkPipelineCache(device->vkd())) { 555 | std::string useStateCache = env::getEnvVar("DXVK_STATE_CACHE"); 556 | + std::string useAsync = env::getEnvVar("DXVK_ASYNC"); 557 | 558 | if (useStateCache != "0" && device->config().enableStateCache) 559 | m_stateCache = new DxvkStateCache(device, this, passManager); 560 | + 561 | + if (useAsync == "1") 562 | + m_compiler = new DxvkPipelineCompiler(); 563 | } 564 | 565 | 566 | diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h 567 | index fb62cb9a..c3b846cf 100644 568 | --- a/src/dxvk/dxvk_pipemanager.h 569 | +++ b/src/dxvk/dxvk_pipemanager.h 570 | @@ -5,6 +5,7 @@ 571 | 572 | #include "dxvk_compute.h" 573 | #include "dxvk_graphics.h" 574 | +#include "dxvk_pipecompiler.h" 575 | 576 | namespace dxvk { 577 | 578 | @@ -139,6 +140,7 @@ namespace dxvk { 579 | const DxvkDevice* m_device; 580 | Rc m_cache; 581 | Rc m_stateCache; 582 | + Rc m_compiler; 583 | 584 | std::atomic m_numComputePipelines = { 0 }; 585 | std::atomic m_numGraphicsPipelines = { 0 }; 586 | diff --git a/src/dxvk/dxvk_state_cache.cpp b/src/dxvk/dxvk_state_cache.cpp 587 | index a9249a3a..4d296c19 100644 588 | --- a/src/dxvk/dxvk_state_cache.cpp 589 | +++ b/src/dxvk/dxvk_state_cache.cpp 590 | @@ -263,7 +263,7 @@ namespace dxvk { 591 | const auto& entry = m_entries[e->second]; 592 | 593 | auto rp = m_passManager->getRenderPass(entry.format); 594 | - pipeline->getPipelineHandle(entry.gpState, *rp); 595 | + pipeline->getPipelineHandle(entry.gpState, rp, false); 596 | } 597 | } else { 598 | auto pipeline = m_pipeManager->createComputePipeline(item.cs); 599 | diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build 600 | index 64f20998..bd11e390 100644 601 | --- a/src/dxvk/meson.build 602 | +++ b/src/dxvk/meson.build 603 | @@ -78,6 +78,7 @@ dxvk_src = files([ 604 | 'dxvk_openvr.cpp', 605 | 'dxvk_options.cpp', 606 | 'dxvk_pipecache.cpp', 607 | + 'dxvk_pipecompiler.cpp', 608 | 'dxvk_pipelayout.cpp', 609 | 'dxvk_pipemanager.cpp', 610 | 'dxvk_queue.cpp', 611 | --------------------------------------------------------------------------------