mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-15 07:29:17 +01:00
[dxvk] Add implicit resolve when app tries to sample multisampled image
This commit is contained in:
parent
2dd815049c
commit
8c98bbb634
@ -1,4 +1,5 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -15,7 +16,8 @@ namespace dxvk {
|
|||||||
m_initAcquires(DxvkCmdBuffer::InitBarriers),
|
m_initAcquires(DxvkCmdBuffer::InitBarriers),
|
||||||
m_initBarriers(DxvkCmdBuffer::InitBuffer),
|
m_initBarriers(DxvkCmdBuffer::InitBuffer),
|
||||||
m_execBarriers(DxvkCmdBuffer::ExecBuffer),
|
m_execBarriers(DxvkCmdBuffer::ExecBuffer),
|
||||||
m_queryManager(m_common->queryPool()) {
|
m_queryManager(m_common->queryPool()),
|
||||||
|
m_implicitResolves(device) {
|
||||||
// Init framebuffer info with default render pass in case
|
// Init framebuffer info with default render pass in case
|
||||||
// the app does not explicitly bind any render targets
|
// the app does not explicitly bind any render targets
|
||||||
m_state.om.framebufferInfo = makeFramebufferInfo(m_state.om.renderTargets);
|
m_state.om.framebufferInfo = makeFramebufferInfo(m_state.om.renderTargets);
|
||||||
@ -81,6 +83,8 @@ namespace dxvk {
|
|||||||
this->endCurrentCommands();
|
this->endCurrentCommands();
|
||||||
this->relocateQueuedResources();
|
this->relocateQueuedResources();
|
||||||
|
|
||||||
|
m_implicitResolves.cleanup(m_trackingId);
|
||||||
|
|
||||||
if (m_descriptorPool->shouldSubmit(false)) {
|
if (m_descriptorPool->shouldSubmit(false)) {
|
||||||
m_cmd->trackDescriptorPool(m_descriptorPool, m_descriptorManager);
|
m_cmd->trackDescriptorPool(m_descriptorPool, m_descriptorManager);
|
||||||
m_descriptorPool = m_descriptorManager->getDescriptorPool();
|
m_descriptorPool = m_descriptorManager->getDescriptorPool();
|
||||||
@ -418,10 +422,18 @@ namespace dxvk {
|
|||||||
clearRect.layerCount = imageView->info().layerCount;
|
clearRect.layerCount = imageView->info().layerCount;
|
||||||
|
|
||||||
m_cmd->cmdClearAttachments(1, &clearInfo, 1, &clearRect);
|
m_cmd->cmdClearAttachments(1, &clearInfo, 1, &clearRect);
|
||||||
} else
|
} else {
|
||||||
this->deferClear(imageView, clearAspects, clearValue);
|
this->deferClear(imageView, clearAspects, clearValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (imageView->isMultisampled()) {
|
||||||
|
auto subresources = imageView->imageSubresources();
|
||||||
|
subresources.aspectMask = clearAspects;
|
||||||
|
|
||||||
|
m_implicitResolves.invalidate(*imageView->image(), subresources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::clearImageView(
|
void DxvkContext::clearImageView(
|
||||||
const Rc<DxvkImageView>& imageView,
|
const Rc<DxvkImageView>& imageView,
|
||||||
@ -440,6 +452,13 @@ namespace dxvk {
|
|||||||
this->clearImageViewFb(imageView, offset, extent, aspect, value);
|
this->clearImageViewFb(imageView, offset, extent, aspect, value);
|
||||||
else if (viewUsage & VK_IMAGE_USAGE_STORAGE_BIT)
|
else if (viewUsage & VK_IMAGE_USAGE_STORAGE_BIT)
|
||||||
this->clearImageViewCs(imageView, offset, extent, value);
|
this->clearImageViewCs(imageView, offset, extent, value);
|
||||||
|
|
||||||
|
if (imageView->isMultisampled()) {
|
||||||
|
auto subresources = imageView->imageSubresources();
|
||||||
|
subresources.aspectMask = aspect;
|
||||||
|
|
||||||
|
m_implicitResolves.invalidate(*imageView->image(), subresources);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -579,6 +598,9 @@ namespace dxvk {
|
|||||||
srcImage, srcSubresource, srcOffset,
|
srcImage, srcSubresource, srcOffset,
|
||||||
extent);
|
extent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dstImage->info().sampleCount > VK_SAMPLE_COUNT_1_BIT)
|
||||||
|
m_implicitResolves.invalidate(*dstImage, vk::makeSubresourceRange(dstSubresource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -5659,8 +5681,19 @@ namespace dxvk {
|
|||||||
m_cmd->cmdClearAttachments(lateClearCount, lateClears.data(), 1, &clearRect);
|
m_cmd->cmdClearAttachments(lateClearCount, lateClears.data(), 1, &clearRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++)
|
for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++) {
|
||||||
m_cmd->track(framebufferInfo.getAttachment(i).view->image(), DxvkAccess::Write);
|
const auto& attachment = framebufferInfo.getAttachment(i);
|
||||||
|
m_cmd->track(attachment.view->image(), DxvkAccess::Write);
|
||||||
|
|
||||||
|
if (attachment.view->isMultisampled()) {
|
||||||
|
VkImageSubresourceRange subresources = attachment.view->imageSubresources();
|
||||||
|
|
||||||
|
if (subresources.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
|
||||||
|
subresources.aspectMask = vk::getWritableAspectsForLayout(attachment.layout);
|
||||||
|
|
||||||
|
m_implicitResolves.invalidate(*attachment.view->image(), subresources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_cmd->addStatCtr(DxvkStatCounter::CmdRenderPassCount, 1u);
|
m_cmd->addStatCtr(DxvkStatCounter::CmdRenderPassCount, 1u);
|
||||||
}
|
}
|
||||||
@ -6108,6 +6141,7 @@ namespace dxvk {
|
|||||||
viewHandle = res.imageView->handle(binding.viewType);
|
viewHandle = res.imageView->handle(binding.viewType);
|
||||||
|
|
||||||
if (viewHandle) {
|
if (viewHandle) {
|
||||||
|
if (likely(!res.imageView->isMultisampled() || binding.isMultisampled)) {
|
||||||
descriptorInfo.image.sampler = VK_NULL_HANDLE;
|
descriptorInfo.image.sampler = VK_NULL_HANDLE;
|
||||||
descriptorInfo.image.imageView = viewHandle;
|
descriptorInfo.image.imageView = viewHandle;
|
||||||
descriptorInfo.image.imageLayout = res.imageView->image()->info().layout;
|
descriptorInfo.image.imageLayout = res.imageView->image()->info().layout;
|
||||||
@ -6116,6 +6150,15 @@ namespace dxvk {
|
|||||||
accessImage(DxvkCmdBuffer::ExecBuffer, *res.imageView, util::pipelineStages(binding.stage), binding.access, DxvkAccessOp::None);
|
accessImage(DxvkCmdBuffer::ExecBuffer, *res.imageView, util::pipelineStages(binding.stage), binding.access, DxvkAccessOp::None);
|
||||||
|
|
||||||
m_cmd->track(res.imageView->image(), DxvkAccess::Read);
|
m_cmd->track(res.imageView->image(), DxvkAccess::Read);
|
||||||
|
} else {
|
||||||
|
auto view = m_implicitResolves.getResolveView(*res.imageView, m_trackingId);
|
||||||
|
|
||||||
|
descriptorInfo.image.sampler = VK_NULL_HANDLE;
|
||||||
|
descriptorInfo.image.imageView = view->handle(binding.viewType);
|
||||||
|
descriptorInfo.image.imageLayout = view->image()->info().layout;
|
||||||
|
|
||||||
|
m_cmd->track(view->image(), DxvkAccess::Read);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
descriptorInfo.image.sampler = VK_NULL_HANDLE;
|
descriptorInfo.image.sampler = VK_NULL_HANDLE;
|
||||||
descriptorInfo.image.imageView = VK_NULL_HANDLE;
|
descriptorInfo.image.imageView = VK_NULL_HANDLE;
|
||||||
@ -6157,6 +6200,7 @@ namespace dxvk {
|
|||||||
viewHandle = res.imageView->handle(binding.viewType);
|
viewHandle = res.imageView->handle(binding.viewType);
|
||||||
|
|
||||||
if (viewHandle) {
|
if (viewHandle) {
|
||||||
|
if (likely(!res.imageView->isMultisampled() || binding.isMultisampled)) {
|
||||||
descriptorInfo.image.sampler = res.sampler->handle();
|
descriptorInfo.image.sampler = res.sampler->handle();
|
||||||
descriptorInfo.image.imageView = viewHandle;
|
descriptorInfo.image.imageView = viewHandle;
|
||||||
descriptorInfo.image.imageLayout = res.imageView->image()->info().layout;
|
descriptorInfo.image.imageLayout = res.imageView->image()->info().layout;
|
||||||
@ -6164,8 +6208,18 @@ namespace dxvk {
|
|||||||
if (BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE || unlikely(res.imageView->image()->hasGfxStores()))
|
if (BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE || unlikely(res.imageView->image()->hasGfxStores()))
|
||||||
accessImage(DxvkCmdBuffer::ExecBuffer, *res.imageView, util::pipelineStages(binding.stage), binding.access, DxvkAccessOp::None);
|
accessImage(DxvkCmdBuffer::ExecBuffer, *res.imageView, util::pipelineStages(binding.stage), binding.access, DxvkAccessOp::None);
|
||||||
|
|
||||||
m_cmd->track(res.sampler);
|
|
||||||
m_cmd->track(res.imageView->image(), DxvkAccess::Read);
|
m_cmd->track(res.imageView->image(), DxvkAccess::Read);
|
||||||
|
m_cmd->track(res.sampler);
|
||||||
|
} else {
|
||||||
|
auto view = m_implicitResolves.getResolveView(*res.imageView, m_trackingId);
|
||||||
|
|
||||||
|
descriptorInfo.image.sampler = res.sampler->handle();
|
||||||
|
descriptorInfo.image.imageView = view->handle(binding.viewType);
|
||||||
|
descriptorInfo.image.imageLayout = view->image()->info().layout;
|
||||||
|
|
||||||
|
m_cmd->track(view->image(), DxvkAccess::Read);
|
||||||
|
m_cmd->track(res.sampler);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
descriptorInfo.image.sampler = m_common->dummyResources().samplerHandle();
|
descriptorInfo.image.sampler = m_common->dummyResources().samplerHandle();
|
||||||
descriptorInfo.image.imageView = VK_NULL_HANDLE;
|
descriptorInfo.image.imageView = VK_NULL_HANDLE;
|
||||||
@ -6822,6 +6876,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<bool Resolve>
|
||||||
bool DxvkContext::commitComputeState() {
|
bool DxvkContext::commitComputeState() {
|
||||||
this->spillRenderPass(false);
|
this->spillRenderPass(false);
|
||||||
|
|
||||||
@ -6843,9 +6898,15 @@ namespace dxvk {
|
|||||||
if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))
|
if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))
|
||||||
this->beginBarrierControlDebugRegion<VK_PIPELINE_BIND_POINT_COMPUTE>();
|
this->beginBarrierControlDebugRegion<VK_PIPELINE_BIND_POINT_COMPUTE>();
|
||||||
|
|
||||||
if (m_descriptorState.hasDirtyComputeSets())
|
if (m_descriptorState.hasDirtyComputeSets()) {
|
||||||
this->updateComputeShaderResources();
|
this->updateComputeShaderResources();
|
||||||
|
|
||||||
|
if (unlikely(Resolve && m_implicitResolves.hasPendingResolves())) {
|
||||||
|
this->flushImplicitResolves();
|
||||||
|
return this->commitComputeState<false>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_flags.test(DxvkContextFlag::DirtyPushConstants))
|
if (m_flags.test(DxvkContextFlag::DirtyPushConstants))
|
||||||
this->updatePushConstants<VK_PIPELINE_BIND_POINT_COMPUTE>();
|
this->updatePushConstants<VK_PIPELINE_BIND_POINT_COMPUTE>();
|
||||||
|
|
||||||
@ -6853,7 +6914,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<bool Indexed, bool Indirect>
|
template<bool Indexed, bool Indirect, bool Resolve>
|
||||||
bool DxvkContext::commitGraphicsState() {
|
bool DxvkContext::commitGraphicsState() {
|
||||||
if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {
|
if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {
|
||||||
if (unlikely(!this->updateGraphicsPipeline()))
|
if (unlikely(!this->updateGraphicsPipeline()))
|
||||||
@ -6920,9 +6981,19 @@ namespace dxvk {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_descriptorState.hasDirtyGraphicsSets())
|
if (m_descriptorState.hasDirtyGraphicsSets()) {
|
||||||
this->updateGraphicsShaderResources();
|
this->updateGraphicsShaderResources();
|
||||||
|
|
||||||
|
if (unlikely(Resolve && m_implicitResolves.hasPendingResolves())) {
|
||||||
|
// If implicit resolves are required for any of the shader bindings, we need
|
||||||
|
// to discard all the state setup that we've done so far and try again
|
||||||
|
this->spillRenderPass(true);
|
||||||
|
this->flushImplicitResolves();
|
||||||
|
|
||||||
|
return this->commitGraphicsState<Indexed, Indirect, false>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasTransformFeedback))
|
if (m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasTransformFeedback))
|
||||||
this->updateTransformFeedbackState();
|
this->updateTransformFeedbackState();
|
||||||
|
|
||||||
@ -7675,6 +7746,21 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkContext::flushImplicitResolves() {
|
||||||
|
spillRenderPass(true);
|
||||||
|
|
||||||
|
DxvkImplicitResolveOp op;
|
||||||
|
|
||||||
|
while (m_implicitResolves.extractResolve(op)) {
|
||||||
|
prepareImage(op.inputImage, vk::makeSubresourceRange(op.resolveRegion.srcSubresource));
|
||||||
|
prepareImage(op.resolveImage, vk::makeSubresourceRange(op.resolveRegion.dstSubresource));
|
||||||
|
|
||||||
|
resolveImageRp(op.resolveImage, op.inputImage, op.resolveRegion,
|
||||||
|
op.resolveFormat, op.resolveMode, op.resolveMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::beginCurrentCommands() {
|
void DxvkContext::beginCurrentCommands() {
|
||||||
beginActiveDebugRegions();
|
beginActiveDebugRegions();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "dxvk_bind_mask.h"
|
#include "dxvk_bind_mask.h"
|
||||||
#include "dxvk_cmdlist.h"
|
#include "dxvk_cmdlist.h"
|
||||||
#include "dxvk_context_state.h"
|
#include "dxvk_context_state.h"
|
||||||
|
#include "dxvk_implicit_resolve.h"
|
||||||
#include "dxvk_latency.h"
|
#include "dxvk_latency.h"
|
||||||
#include "dxvk_objects.h"
|
#include "dxvk_objects.h"
|
||||||
#include "dxvk_queue.h"
|
#include "dxvk_queue.h"
|
||||||
@ -1461,6 +1462,8 @@ namespace dxvk {
|
|||||||
uint64_t m_latencyFrameId = 0u;
|
uint64_t m_latencyFrameId = 0u;
|
||||||
bool m_endLatencyTracking = false;
|
bool m_endLatencyTracking = false;
|
||||||
|
|
||||||
|
DxvkImplicitResolveTracker m_implicitResolves;
|
||||||
|
|
||||||
void blitImageFb(
|
void blitImageFb(
|
||||||
Rc<DxvkImageView> dstView,
|
Rc<DxvkImageView> dstView,
|
||||||
const VkOffset3D* dstOffsets,
|
const VkOffset3D* dstOffsets,
|
||||||
@ -1776,9 +1779,10 @@ namespace dxvk {
|
|||||||
template<VkPipelineBindPoint BindPoint>
|
template<VkPipelineBindPoint BindPoint>
|
||||||
void updatePushConstants();
|
void updatePushConstants();
|
||||||
|
|
||||||
|
template<bool Resolve = true>
|
||||||
bool commitComputeState();
|
bool commitComputeState();
|
||||||
|
|
||||||
template<bool Indexed, bool Indirect>
|
template<bool Indexed, bool Indirect, bool Resolve = true>
|
||||||
bool commitGraphicsState();
|
bool commitGraphicsState();
|
||||||
|
|
||||||
template<VkPipelineBindPoint BindPoint>
|
template<VkPipelineBindPoint BindPoint>
|
||||||
@ -1876,6 +1880,8 @@ namespace dxvk {
|
|||||||
void resizeDescriptorArrays(
|
void resizeDescriptorArrays(
|
||||||
uint32_t bindingCount);
|
uint32_t bindingCount);
|
||||||
|
|
||||||
|
void flushImplicitResolves();
|
||||||
|
|
||||||
void beginCurrentCommands();
|
void beginCurrentCommands();
|
||||||
|
|
||||||
void endCurrentCommands();
|
void endCurrentCommands();
|
||||||
|
182
src/dxvk/dxvk_implicit_resolve.cpp
Normal file
182
src/dxvk/dxvk_implicit_resolve.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "dxvk_device.h"
|
||||||
|
#include "dxvk_implicit_resolve.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxvkImplicitResolveTracker::DxvkImplicitResolveTracker(Rc<DxvkDevice> device)
|
||||||
|
: m_device(std::move(device)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxvkImplicitResolveTracker::~DxvkImplicitResolveTracker() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rc<DxvkImageView> DxvkImplicitResolveTracker::getResolveView(
|
||||||
|
DxvkImageView& view,
|
||||||
|
uint64_t trackingId) {
|
||||||
|
// We generally only expect to have one or two views at most in games
|
||||||
|
// that hit this path at all, so iterating over the arras is fine
|
||||||
|
for (auto& v : m_resolveViews) {
|
||||||
|
if (v.inputView == &view) {
|
||||||
|
addResolveOp(v);
|
||||||
|
return v.resolveView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new resolve image with only the array layers covered by the
|
||||||
|
// input view. We expect resolve images to be somewhat short-lived.
|
||||||
|
DxvkImageCreateInfo imageInfo = view.image()->info();
|
||||||
|
|
||||||
|
DxvkImageCreateInfo resolveInfo = { };
|
||||||
|
resolveInfo.type = imageInfo.type;
|
||||||
|
resolveInfo.format = view.info().format;
|
||||||
|
resolveInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
resolveInfo.extent = imageInfo.extent;
|
||||||
|
resolveInfo.numLayers = view.info().layerCount;
|
||||||
|
resolveInfo.mipLevels = 1u;
|
||||||
|
resolveInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
resolveInfo.stages = m_device->getShaderPipelineStages();
|
||||||
|
resolveInfo.access = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
resolveInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
resolveInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
resolveInfo.transient = VK_TRUE;
|
||||||
|
resolveInfo.debugName = "Resolve image";
|
||||||
|
|
||||||
|
if (view.info().aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||||
|
resolveInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||||
|
resolveInfo.stages |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||||
|
resolveInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||||
|
} else {
|
||||||
|
resolveInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
resolveInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
resolveInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rc<DxvkImage> image = m_device->createImage(resolveInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
|
||||||
|
cleanup(image->getMemoryInfo().size, trackingId);
|
||||||
|
|
||||||
|
DxvkImageViewKey viewKey = view.info();
|
||||||
|
viewKey.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
viewKey.layerIndex = 0u;
|
||||||
|
|
||||||
|
auto& resolveView = m_resolveViews.emplace_back();
|
||||||
|
resolveView.inputView = &view;
|
||||||
|
resolveView.resolveView = image->createView(viewKey);
|
||||||
|
|
||||||
|
addResolveOp(resolveView);
|
||||||
|
|
||||||
|
return resolveView.resolveView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DxvkImplicitResolveTracker::extractResolve(
|
||||||
|
DxvkImplicitResolveOp& resolve) {
|
||||||
|
if (m_resolveOps.empty()) {
|
||||||
|
resolve = DxvkImplicitResolveOp();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve = std::move(m_resolveOps.back());
|
||||||
|
m_resolveOps.pop_back();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkImplicitResolveTracker::invalidate(
|
||||||
|
const DxvkImage& image,
|
||||||
|
const VkImageSubresourceRange& subresources) {
|
||||||
|
for (auto& v : m_resolveViews) {
|
||||||
|
if (v.resolveDone && v.inputView->image() == &image) {
|
||||||
|
auto viewSubresource = v.inputView->imageSubresources();
|
||||||
|
|
||||||
|
if ((subresources.aspectMask & viewSubresource.aspectMask)
|
||||||
|
&& vk::checkSubresourceRangeOverlap(viewSubresource, subresources))
|
||||||
|
v.resolveDone = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkImplicitResolveTracker::cleanup(
|
||||||
|
uint64_t trackingId) {
|
||||||
|
cleanup(0u, trackingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkImplicitResolveTracker::addResolveOp(
|
||||||
|
DxvkImplicitResolveView& view) {
|
||||||
|
if (view.resolveDone)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Determine resolve parameters based on the view format rather than the
|
||||||
|
// image format, since this will more likely represent what the app is
|
||||||
|
// trying to do
|
||||||
|
auto format = view.inputView->formatInfo();
|
||||||
|
|
||||||
|
auto& op = m_resolveOps.emplace_back();
|
||||||
|
op.inputImage = view.inputView->image();
|
||||||
|
op.resolveImage = view.resolveView->image();
|
||||||
|
op.resolveRegion.srcSubresource = vk::pickSubresourceLayers(view.inputView->imageSubresources(), 0u);
|
||||||
|
op.resolveRegion.srcSubresource.aspectMask = format->aspectMask;
|
||||||
|
op.resolveRegion.dstSubresource = vk::pickSubresourceLayers(view.resolveView->imageSubresources(), 0u);
|
||||||
|
op.resolveRegion.dstSubresource.aspectMask = format->aspectMask;
|
||||||
|
op.resolveRegion.dstSubresource.baseArrayLayer = 0u;
|
||||||
|
op.resolveRegion.extent = view.resolveView->mipLevelExtent(0u);
|
||||||
|
op.resolveFormat = view.inputView->info().format;
|
||||||
|
op.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
|
||||||
|
|
||||||
|
if ((format->flags.any(DxvkFormatFlag::SampledSInt, DxvkFormatFlag::SampledUInt)
|
||||||
|
|| (format->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))))
|
||||||
|
op.resolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
|
||||||
|
|
||||||
|
view.resolveDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkImplicitResolveTracker::cleanup(
|
||||||
|
VkDeviceSize allocationSize,
|
||||||
|
uint64_t trackingId) {
|
||||||
|
constexpr VkDeviceSize MaxMemory = 64ull << 20u;
|
||||||
|
|
||||||
|
constexpr uint64_t MaxLifetime = 256u;
|
||||||
|
constexpr uint64_t MinLifetime = 16u;
|
||||||
|
|
||||||
|
// Eliminate images that haven't been used in a long time
|
||||||
|
for (auto i = m_resolveViews.begin(); i != m_resolveViews.end(); ) {
|
||||||
|
if (i->resolveView->image()->getTrackId() + MaxLifetime < trackingId) {
|
||||||
|
i = m_resolveViews.erase(i);
|
||||||
|
} else {
|
||||||
|
allocationSize += i->resolveView->image()->getMemoryInfo().size;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're using a large amount of memory for resolve images, eliminate
|
||||||
|
// the least recently used resolve images until we drop below the size
|
||||||
|
// threshold again.
|
||||||
|
while (allocationSize > MaxMemory) {
|
||||||
|
auto lr = m_resolveViews.end();
|
||||||
|
|
||||||
|
for (auto i = m_resolveViews.begin(); i != m_resolveViews.end(); i++) {
|
||||||
|
if (i->resolveView->image()->getTrackId() + MinLifetime < trackingId) {
|
||||||
|
if (lr == m_resolveViews.end()
|
||||||
|
|| lr->resolveView->image()->getTrackId() > i->resolveView->image()->getTrackId())
|
||||||
|
lr = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lr == m_resolveViews.end())
|
||||||
|
break;
|
||||||
|
|
||||||
|
allocationSize -= lr->resolveView->image()->getMemoryInfo().size;
|
||||||
|
m_resolveViews.erase(lr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
108
src/dxvk/dxvk_implicit_resolve.h
Normal file
108
src/dxvk/dxvk_implicit_resolve.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dxvk_image.h"
|
||||||
|
|
||||||
|
#include "../util/util_small_vector.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct DxvkImplicitResolveView {
|
||||||
|
Rc<DxvkImageView> inputView = nullptr;
|
||||||
|
Rc<DxvkImageView> resolveView = nullptr;
|
||||||
|
bool resolveDone = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct DxvkImplicitResolveOp {
|
||||||
|
Rc<DxvkImage> inputImage = nullptr;
|
||||||
|
Rc<DxvkImage> resolveImage = nullptr;
|
||||||
|
VkImageResolve resolveRegion = { };
|
||||||
|
VkFormat resolveFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkResolveModeFlagBits resolveMode = VK_RESOLVE_MODE_NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DxvkDevice;
|
||||||
|
|
||||||
|
class DxvkImplicitResolveTracker {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxvkImplicitResolveTracker(Rc<DxvkDevice> device);
|
||||||
|
|
||||||
|
~DxvkImplicitResolveTracker();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether there are pending resolves
|
||||||
|
*
|
||||||
|
* \returns \c true if any there are any resolves that must
|
||||||
|
* be executed prior to submitting the current draw.
|
||||||
|
*/
|
||||||
|
bool hasPendingResolves() const {
|
||||||
|
return !m_resolveOps.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves resolve image view for a given input view
|
||||||
|
*
|
||||||
|
* \param [in] view Multisampled view bound to the context
|
||||||
|
* \returns Non-multisampled view to replace the bound view with
|
||||||
|
*/
|
||||||
|
Rc<DxvkImageView> getResolveView(
|
||||||
|
DxvkImageView& view,
|
||||||
|
uint64_t trackingId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Extracts a resolve operation to execute
|
||||||
|
*
|
||||||
|
* \param [out] resolve Extracted resolve parameters
|
||||||
|
* \returns \c true if a resolve was extracted, \c false
|
||||||
|
* if all resolves have already been processed.
|
||||||
|
*/
|
||||||
|
bool extractResolve(
|
||||||
|
DxvkImplicitResolveOp& resolve);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Invalidates resolve cache for a given set of image subresources
|
||||||
|
*
|
||||||
|
* Must be called any time the given set of subresources of this
|
||||||
|
* resource is written, so that the corresponding resolve image
|
||||||
|
* can get updated the next time it is read. Must not be called
|
||||||
|
* for any subresource that is only being read, since that may
|
||||||
|
* cause problems with read-only depth-stencil access.
|
||||||
|
* \param [in] image The multisampled image
|
||||||
|
* \param [in] subresources Image subresources written
|
||||||
|
*/
|
||||||
|
void invalidate(
|
||||||
|
const DxvkImage& image,
|
||||||
|
const VkImageSubresourceRange& subresources);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Cleans up resolve image cache
|
||||||
|
*
|
||||||
|
* Destroys resolve images that have not been used in a while
|
||||||
|
* in order to reduce memory wasted on unused images.
|
||||||
|
* \param [in] trackingId Current context command list ID
|
||||||
|
*/
|
||||||
|
void cleanup(
|
||||||
|
uint64_t trackingId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Rc<DxvkDevice> m_device;
|
||||||
|
|
||||||
|
std::vector<DxvkImplicitResolveView> m_resolveViews;
|
||||||
|
std::vector<DxvkImplicitResolveOp> m_resolveOps;
|
||||||
|
|
||||||
|
void addResolveOp(
|
||||||
|
DxvkImplicitResolveView& view);
|
||||||
|
|
||||||
|
void cleanup(
|
||||||
|
VkDeviceSize allocationSize,
|
||||||
|
uint64_t trackingId);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -535,6 +535,16 @@ namespace dxvk {
|
|||||||
return Rc<DxvkPagedResource>::unsafeCreate(this);
|
return Rc<DxvkPagedResource>::unsafeCreate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Queries tracking ID
|
||||||
|
*
|
||||||
|
* Used to determine when a resource has last been used.
|
||||||
|
* \returns Tracking ID
|
||||||
|
*/
|
||||||
|
uint64_t getTrackId() const {
|
||||||
|
return m_trackId >> 1u;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Sets tracked command list ID
|
* \brief Sets tracked command list ID
|
||||||
*
|
*
|
||||||
|
@ -89,6 +89,7 @@ dxvk_src = [
|
|||||||
'dxvk_gpu_query.cpp',
|
'dxvk_gpu_query.cpp',
|
||||||
'dxvk_graphics.cpp',
|
'dxvk_graphics.cpp',
|
||||||
'dxvk_image.cpp',
|
'dxvk_image.cpp',
|
||||||
|
'dxvk_implicit_resolve.cpp',
|
||||||
'dxvk_instance.cpp',
|
'dxvk_instance.cpp',
|
||||||
'dxvk_latency_builtin.cpp',
|
'dxvk_latency_builtin.cpp',
|
||||||
'dxvk_latency_reflex.cpp',
|
'dxvk_latency_reflex.cpp',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user