[d3d11] Implement DXGI_SWAP_EFFECT_SEQUENTIAL and FLIP_SEQUENTIAL

Requires sparse since we have no other means to swap the backing image.
This commit is contained in:
Philip Rebohle 2023-08-16 17:16:07 +02:00
parent 53a68635b2
commit 79f6239df3
3 changed files with 153 additions and 35 deletions

View File

@ -140,12 +140,12 @@ namespace dxvk {
void** ppBuffer) {
InitReturnPtr(ppBuffer);
if (BufferId > 0) {
Logger::err("D3D11: GetImage: BufferId > 0 not supported");
if (BufferId > m_backBuffers.size()) {
Logger::err(str::format("D3D11: Invalid buffer index: ", BufferId));
return DXGI_ERROR_UNSUPPORTED;
}
return m_backBuffer->QueryInterface(riid, ppBuffer);
return m_backBuffers[BufferId]->QueryInterface(riid, ppBuffer);
}
@ -396,7 +396,10 @@ namespace dxvk {
if (m_hud != nullptr)
m_hud->render(m_context, info.format, info.imageExtent);
if (!SyncInterval || i == SyncInterval - 1)
RotateBackBuffer();
SubmitPresent(immediateContext, sync, i);
}
@ -559,11 +562,25 @@ namespace dxvk {
void D3D11SwapChain::CreateBackBuffer() {
bool sequentialPresent = false;
if (IsSequentialSwapChain()) {
if (!(sequentialPresent = SupportsSparseImages())) {
Logger::warn("Sequential present mode requeted, but sparse images not supported"
"by the Vulkan implementation. Falling back to Discard semantics.");
}
}
// Explicitly destroy current swap image before
// creating a new one to free up resources
m_swapImage = nullptr;
m_swapImageView = nullptr;
m_backBuffer = nullptr;
m_swapImages.clear();
m_backBuffers.clear();
m_swapImageView = nullptr;
uint32_t bufferCount = 1u;
if (sequentialPresent)
bufferCount = m_desc.BufferCount;
// Create new back buffer
D3D11_COMMON_TEXTURE_DESC desc;
@ -592,27 +609,47 @@ namespace dxvk {
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE)
desc.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
if (sequentialPresent)
desc.MiscFlags |= D3D11_RESOURCE_MISC_TILED;
DXGI_USAGE dxgiUsage = DXGI_USAGE_BACK_BUFFER;
if (m_desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD
|| m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD)
dxgiUsage |= DXGI_USAGE_DISCARD_ON_PRESENT;
m_backBuffer = new D3D11Texture2D(m_parent, this, &desc, dxgiUsage);
m_swapImage = GetCommonTexture(m_backBuffer.ptr())->GetImage();
for (uint32_t i = 0; i < bufferCount; i++) {
m_backBuffers.push_back(new D3D11Texture2D(m_parent, this, &desc, dxgiUsage));
m_swapImages.push_back(GetCommonTexture(m_backBuffers.back().ptr())->GetImage());
dxgiUsage |= DXGI_USAGE_READ_ONLY;
}
// If necessary, create a sparse page allocator
m_sparseFrameIndex = 0;
if (sequentialPresent) {
m_sparsePagesPerImage = m_swapImages.front()->getSparsePageTable()->getPageCount();
m_sparseAllocator = m_device->createSparsePageAllocator();
m_sparseAllocator->setCapacity(m_sparsePagesPerImage * bufferCount);
} else {
m_sparsePagesPerImage = 0;
m_sparseAllocator = nullptr;
}
// Create an image view that allows the
// image to be bound as a shader resource.
DxvkImageViewCreateInfo viewInfo;
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = m_swapImage->info().format;
viewInfo.format = m_swapImages.front()->info().format;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
m_swapImageView = m_device->createImageView(m_swapImage, viewInfo);
m_swapImageView = m_device->createImageView(m_swapImages.front(), viewInfo);
// Initialize the image so that we can use it. Clearing
// to black prevents garbled output for the first frame.
@ -626,8 +663,30 @@ namespace dxvk {
m_context->beginRecording(
m_device->createCommandList());
m_context->initImage(m_swapImage,
subresources, VK_IMAGE_LAYOUT_UNDEFINED);
for (uint32_t i = 0; i < m_swapImages.size(); i++) {
if (sequentialPresent) {
DxvkSparseBindInfo sparseBind;
sparseBind.dstResource = m_swapImages[i];
sparseBind.srcAllocator = m_sparseAllocator;
for (uint32_t j = 0; j < m_sparsePagesPerImage; j++) {
auto& bind = sparseBind.binds.emplace_back();
bind.mode = DxvkSparseBindMode::Bind;
bind.srcPage = j + i * m_sparsePagesPerImage;
bind.dstPage = j;
}
m_context->updatePageTable(sparseBind,
DxvkSparseBindFlag::SkipSynchronization);
m_context->initSparseImage(m_swapImages[i]);
Rc<DxvkImageView> view = m_device->createImageView(m_swapImages.front(), viewInfo);
m_context->clearRenderTarget(view, VK_IMAGE_ASPECT_COLOR_BIT, VkClearValue());
} else {
m_context->initImage(m_swapImages[i],
subresources, VK_IMAGE_LAYOUT_UNDEFINED);
}
}
m_device->submitCommandList(
m_context->endRecording(),
@ -738,6 +797,51 @@ namespace dxvk {
}
void D3D11SwapChain::RotateBackBuffer() {
uint32_t bufferCount = m_swapImages.size();
if (bufferCount < 2)
return;
m_sparseFrameIndex += 1;
m_sparseFrameIndex %= bufferCount;
for (uint32_t i = 0; i < bufferCount; i++) {
uint32_t firstImage = (m_sparseFrameIndex + i) % bufferCount;
DxvkSparseBindInfo sparseBind;
sparseBind.dstResource = m_swapImages[i];
sparseBind.srcAllocator = m_sparseAllocator;
for (uint32_t j = 0; j < m_sparsePagesPerImage; j++) {
auto& bind = sparseBind.binds.emplace_back();
bind.mode = DxvkSparseBindMode::Bind;
bind.srcPage = j + firstImage * m_sparsePagesPerImage;
bind.dstPage = j;
}
m_context->updatePageTable(sparseBind, 0);
}
}
bool D3D11SwapChain::IsSequentialSwapChain() const {
return m_desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL
|| m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
}
bool D3D11SwapChain::SupportsSparseImages() const {
const auto& properties = m_device->properties().core.properties;
const auto& features = m_device->features().core.features;
return features.sparseBinding
&& features.sparseResidencyImage2D
&& features.sparseResidencyAliased
&& properties.sparseProperties.residencyStandard2DBlockShape;
}
std::string D3D11SwapChain::GetApiName() const {
Com<IDXGIDXVKDevice> device;
m_parent->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast<void**>(&device));

View File

@ -95,42 +95,46 @@ namespace dxvk {
Com<D3D11DXGIDevice, false> m_dxgiDevice;
D3D11Device* m_parent;
Com<IDXGIVkSurfaceFactory> m_surfaceFactory;
D3D11Device* m_parent;
Com<IDXGIVkSurfaceFactory> m_surfaceFactory;
DXGI_SWAP_CHAIN_DESC1 m_desc;
DXGI_SWAP_CHAIN_DESC1 m_desc;
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
Rc<Presenter> m_presenter;
Rc<Presenter> m_presenter;
Rc<DxvkImage> m_swapImage;
Rc<DxvkImageView> m_swapImageView;
Rc<DxvkSwapchainBlitter> m_blitter;
uint32_t m_sparseFrameIndex = 0u;
uint32_t m_sparsePagesPerImage = 0u;
Rc<DxvkSparsePageAllocator> m_sparseAllocator;
Rc<hud::Hud> m_hud;
std::vector<Rc<DxvkImage>> m_swapImages;
Rc<DxvkImageView> m_swapImageView;
Rc<DxvkSwapchainBlitter> m_blitter;
Com<D3D11Texture2D, false> m_backBuffer;
DxvkSubmitStatus m_presentStatus;
Rc<hud::Hud> m_hud;
std::vector<Com<D3D11Texture2D, false>> m_backBuffers;
DxvkSubmitStatus m_presentStatus;
std::vector<Rc<DxvkImageView>> m_imageViews;
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
uint32_t m_frameLatency = DefaultFrameLatency;
uint32_t m_frameLatencyCap = 0;
HANDLE m_frameLatencyEvent = nullptr;
Rc<sync::CallbackFence> m_frameLatencySignal;
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
uint32_t m_frameLatency = DefaultFrameLatency;
uint32_t m_frameLatencyCap = 0;
HANDLE m_frameLatencyEvent = nullptr;
Rc<sync::CallbackFence> m_frameLatencySignal;
bool m_dirty = true;
bool m_dirty = true;
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
bool m_dirtyHdrMetadata = true;
bool m_dirtyHdrMetadata = true;
dxvk::mutex m_frameStatisticsLock;
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
dxvk::mutex m_frameStatisticsLock;
DXGI_VK_FRAME_STATISTICS m_frameStatistics = { };
HRESULT PresentImage(UINT SyncInterval);
@ -172,6 +176,12 @@ namespace dxvk {
VkFullScreenExclusiveEXT PickFullscreenMode();
void RotateBackBuffer();
bool IsSequentialSwapChain() const;
bool SupportsSparseImages() const;
std::string GetApiName() const;
};

View File

@ -249,6 +249,10 @@ namespace dxvk {
if (imageInfo.sharing.mode == DxvkSharedHandleMode::Export)
ExportImageInfo();
// Hide some internal flags as necessary
if (DxgiUsage & DXGI_USAGE_BACK_BUFFER)
m_desc.MiscFlags &= ~D3D11_RESOURCE_MISC_TILED;
}