dxvk/src/d3d11/d3d11_swapchain.cpp

890 lines
29 KiB
C++

#include "d3d11_context_imm.h"
#include "d3d11_device.h"
#include "d3d11_swapchain.h"
#include <dxgi_presenter_frag.h>
#include <dxgi_presenter_vert.h>
namespace dxvk {
static uint16_t MapGammaControlPoint(float x) {
if (x < 0.0f) x = 0.0f;
if (x > 1.0f) x = 1.0f;
return uint16_t(65535.0f * x);
}
D3D11SwapChain::D3D11SwapChain(
D3D11DXGIDevice* pContainer,
D3D11Device* pDevice,
HWND hWnd,
const DXGI_SWAP_CHAIN_DESC1* pDesc)
: m_dxgiDevice(pContainer),
m_parent (pDevice),
m_window (hWnd),
m_desc (*pDesc),
m_device (pDevice->GetDXVKDevice()),
m_context (m_device->createContext()),
m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) {
CreateFrameLatencyEvent();
if (!pDevice->GetOptions()->deferSurfaceCreation)
CreatePresenter();
CreateBackBuffer();
CreateHud();
InitRenderState();
InitSamplers();
InitShaders();
}
D3D11SwapChain::~D3D11SwapChain() {
m_device->waitForSubmission(&m_presentStatus);
m_device->waitForIdle();
if (m_backBuffer)
m_backBuffer->ReleasePrivate();
DestroyFrameLatencyEvent();
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::QueryInterface(
REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
InitReturnPtr(ppvObject);
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDXGIVkSwapChain)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D11SwapChain::QueryInterface: Unknown interface query");
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDesc(
DXGI_SWAP_CHAIN_DESC1* pDesc) {
*pDesc = m_desc;
return S_OK;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetAdapter(
REFIID riid,
void** ppvObject) {
return m_dxgiDevice->GetParent(riid, ppvObject);
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDevice(
REFIID riid,
void** ppDevice) {
return m_dxgiDevice->QueryInterface(riid, ppDevice);
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetImage(
UINT BufferId,
REFIID riid,
void** ppBuffer) {
InitReturnPtr(ppBuffer);
if (BufferId > 0) {
Logger::err("D3D11: GetImage: BufferId > 0 not supported");
return DXGI_ERROR_UNSUPPORTED;
}
return m_backBuffer->QueryInterface(riid, ppBuffer);
}
UINT STDMETHODCALLTYPE D3D11SwapChain::GetImageIndex() {
return 0;
}
UINT STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatency() {
return m_frameLatency;
}
HANDLE STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatencyEvent() {
return m_frameLatencyEvent;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::ChangeProperties(
const DXGI_SWAP_CHAIN_DESC1* pDesc) {
m_dirty |= m_desc.Format != pDesc->Format
|| m_desc.Width != pDesc->Width
|| m_desc.Height != pDesc->Height
|| m_desc.BufferCount != pDesc->BufferCount
|| m_desc.Flags != pDesc->Flags;
m_desc = *pDesc;
CreateBackBuffer();
return S_OK;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetPresentRegion(
const RECT* pRegion) {
// TODO implement
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetGammaControl(
UINT NumControlPoints,
const DXGI_RGB* pControlPoints) {
bool isIdentity = true;
if (NumControlPoints > 1) {
std::array<D3D11_VK_GAMMA_CP, 1025> cp;
if (NumControlPoints > cp.size())
return E_INVALIDARG;
for (uint32_t i = 0; i < NumControlPoints; i++) {
uint16_t identity = MapGammaControlPoint(float(i) / float(NumControlPoints - 1));
cp[i].R = MapGammaControlPoint(pControlPoints[i].Red);
cp[i].G = MapGammaControlPoint(pControlPoints[i].Green);
cp[i].B = MapGammaControlPoint(pControlPoints[i].Blue);
cp[i].A = 0;
isIdentity &= cp[i].R == identity
&& cp[i].G == identity
&& cp[i].B == identity;
}
if (!isIdentity)
CreateGammaTexture(NumControlPoints, cp.data());
}
if (isIdentity)
DestroyGammaTexture();
return S_OK;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetFrameLatency(
UINT MaxLatency) {
if (MaxLatency == 0 || MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS)
return DXGI_ERROR_INVALID_CALL;
m_frameLatency = MaxLatency;
m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency());
SignalFrameLatencyEvent();
return S_OK;
}
HRESULT STDMETHODCALLTYPE D3D11SwapChain::Present(
UINT SyncInterval,
UINT PresentFlags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
auto options = m_parent->GetOptions();
if (options->syncInterval >= 0)
SyncInterval = options->syncInterval;
if (!(PresentFlags & DXGI_PRESENT_TEST)) {
bool vsync = SyncInterval != 0;
m_dirty |= vsync != m_vsync;
m_vsync = vsync;
}
if (m_presenter == nullptr)
CreatePresenter();
HRESULT hr = S_OK;
if (!m_presenter->hasSwapChain()) {
RecreateSwapChain(m_vsync);
m_dirty = false;
}
if (!m_presenter->hasSwapChain())
hr = DXGI_STATUS_OCCLUDED;
if (m_device->getDeviceStatus() != VK_SUCCESS)
hr = DXGI_ERROR_DEVICE_RESET;
if ((PresentFlags & DXGI_PRESENT_TEST) || hr != S_OK)
return hr;
if (std::exchange(m_dirty, false))
RecreateSwapChain(m_vsync);
try {
PresentImage(SyncInterval);
} catch (const DxvkError& e) {
Logger::err(e.message());
hr = E_FAIL;
}
return hr;
}
HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) {
Com<ID3D11DeviceContext> deviceContext = nullptr;
m_parent->GetImmediateContext(&deviceContext);
// Flush pending rendering commands before
auto immediateContext = static_cast<D3D11ImmediateContext*>(deviceContext.ptr());
immediateContext->Flush();
// Wait for the sync event so that we respect the maximum frame latency
uint64_t frameId = ++m_frameId;
m_frameLatencySignal->wait(frameId - GetActualFrameLatency());
for (uint32_t i = 0; i < SyncInterval || i < 1; i++) {
SynchronizePresent();
if (!m_presenter->hasSwapChain())
return DXGI_STATUS_OCCLUDED;
// Presentation semaphores and WSI swap chain image
vk::PresenterInfo info = m_presenter->info();
vk::PresenterSync sync = m_presenter->getSyncSemaphores();
uint32_t imageIndex = 0;
VkResult status = m_presenter->acquireNextImage(
sync.acquire, VK_NULL_HANDLE, imageIndex);
while (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR) {
RecreateSwapChain(m_vsync);
if (!m_presenter->hasSwapChain())
return DXGI_STATUS_OCCLUDED;
info = m_presenter->info();
sync = m_presenter->getSyncSemaphores();
status = m_presenter->acquireNextImage(
sync.acquire, VK_NULL_HANDLE, imageIndex);
}
// Resolve back buffer if it is multisampled. We
// only have to do it only for the first frame.
m_context->beginRecording(
m_device->createCommandList());
if (m_swapImageResolve != nullptr && i == 0) {
VkImageSubresourceLayers resolveSubresource;
resolveSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
resolveSubresource.mipLevel = 0;
resolveSubresource.baseArrayLayer = 0;
resolveSubresource.layerCount = 1;
VkImageResolve resolveRegion;
resolveRegion.srcSubresource = resolveSubresource;
resolveRegion.srcOffset = VkOffset3D { 0, 0, 0 };
resolveRegion.dstSubresource = resolveSubresource;
resolveRegion.dstOffset = VkOffset3D { 0, 0, 0 };
resolveRegion.extent = m_swapImage->info().extent;
m_context->resolveImage(
m_swapImageResolve, m_swapImage,
resolveRegion, VK_FORMAT_UNDEFINED);
}
// Use an appropriate texture filter depending on whether
// the back buffer size matches the swap image size
bool fitSize = m_swapImage->info().extent.width == info.imageExtent.width
&& m_swapImage->info().extent.height == info.imageExtent.height;
m_context->bindShader(VK_SHADER_STAGE_VERTEX_BIT, m_vertShader);
m_context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_fragShader);
DxvkRenderTargets renderTargets;
renderTargets.color[0].view = m_imageViews.at(imageIndex);
renderTargets.color[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
m_context->bindRenderTargets(renderTargets);
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = float(info.imageExtent.width);
viewport.height = float(info.imageExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor;
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = info.imageExtent.width;
scissor.extent.height = info.imageExtent.height;
m_context->setViewports(1, &viewport, &scissor);
m_context->setRasterizerState(m_rsState);
m_context->setMultisampleState(m_msState);
m_context->setDepthStencilState(m_dsState);
m_context->setLogicOpState(m_loState);
m_context->setBlendMode(0, m_blendMode);
m_context->setInputAssemblyState(m_iaState);
m_context->setInputLayout(0, nullptr, 0, nullptr);
m_context->bindResourceSampler(BindingIds::Image, fitSize ? m_samplerFitting : m_samplerScaling);
m_context->bindResourceSampler(BindingIds::Gamma, m_gammaSampler);
m_context->bindResourceView(BindingIds::Image, m_swapImageView, nullptr);
m_context->bindResourceView(BindingIds::Gamma, m_gammaTextureView, nullptr);
m_context->draw(3, 1, 0, 0);
if (m_hud != nullptr)
m_hud->render(m_context, info.format, info.imageExtent);
if (i + 1 >= SyncInterval)
m_context->signal(m_frameLatencySignal, frameId);
SubmitPresent(immediateContext, sync, i);
}
SignalFrameLatencyEvent();
return S_OK;
}
void D3D11SwapChain::SubmitPresent(
D3D11ImmediateContext* pContext,
const vk::PresenterSync& Sync,
uint32_t FrameId) {
auto lock = pContext->LockContext();
// Present from CS thread so that we don't
// have to synchronize with it first.
m_presentStatus.result = VK_NOT_READY;
pContext->EmitCs([this,
cFrameId = FrameId,
cSync = Sync,
cHud = m_hud,
cCommandList = m_context->endRecording()
] (DxvkContext* ctx) {
m_device->submitCommandList(cCommandList,
cSync.acquire, cSync.present);
if (cHud != nullptr && !cFrameId)
cHud->update();
m_device->presentImage(m_presenter,
cSync.present, &m_presentStatus);
});
pContext->FlushCsChunk();
}
void D3D11SwapChain::SynchronizePresent() {
// Recreate swap chain if the previous present call failed
VkResult status = m_device->waitForSubmission(&m_presentStatus);
if (status != VK_SUCCESS)
RecreateSwapChain(m_vsync);
}
void D3D11SwapChain::RecreateSwapChain(BOOL Vsync) {
// Ensure that we can safely destroy the swap chain
m_device->waitForSubmission(&m_presentStatus);
m_device->waitForIdle();
m_presentStatus.result = VK_SUCCESS;
vk::PresenterDesc presenterDesc;
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(Vsync, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
if (m_presenter->recreateSwapChain(presenterDesc) != VK_SUCCESS)
throw DxvkError("D3D11SwapChain: Failed to recreate swap chain");
CreateRenderTargetViews();
}
void D3D11SwapChain::CreateFrameLatencyEvent() {
m_frameLatencySignal = new sync::Win32Fence(m_frameId);
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)
m_frameLatencyEvent = CreateEvent(nullptr, false, true, nullptr);
}
void D3D11SwapChain::CreatePresenter() {
DxvkDeviceQueue graphicsQueue = m_device->queues().graphics;
vk::PresenterDevice presenterDevice;
presenterDevice.queueFamily = graphicsQueue.queueFamily;
presenterDevice.queue = graphicsQueue.queueHandle;
presenterDevice.adapter = m_device->adapter()->handle();
presenterDevice.features.fullScreenExclusive = m_device->extensions().extFullScreenExclusive;
vk::PresenterDesc presenterDesc;
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(false, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
m_presenter = new vk::Presenter(m_window,
m_device->adapter()->vki(),
m_device->vkd(),
presenterDevice,
presenterDesc);
CreateRenderTargetViews();
}
void D3D11SwapChain::CreateRenderTargetViews() {
vk::PresenterInfo info = m_presenter->info();
m_imageViews.clear();
m_imageViews.resize(info.imageCount);
DxvkImageCreateInfo imageInfo;
imageInfo.type = VK_IMAGE_TYPE_2D;
imageInfo.format = info.format.format;
imageInfo.flags = 0;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent = { info.imageExtent.width, info.imageExtent.height, 1 };
imageInfo.numLayers = 1;
imageInfo.mipLevels = 1;
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageInfo.stages = 0;
imageInfo.access = 0;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
DxvkImageViewCreateInfo viewInfo;
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = info.format.format;
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
for (uint32_t i = 0; i < info.imageCount; i++) {
VkImage imageHandle = m_presenter->getImage(i).image;
Rc<DxvkImage> image = new DxvkImage(
m_device->vkd(), imageInfo, imageHandle);
m_imageViews[i] = new DxvkImageView(
m_device->vkd(), image, viewInfo);
}
}
void D3D11SwapChain::CreateBackBuffer() {
// Explicitly destroy current swap image before
// creating a new one to free up resources
if (m_backBuffer)
m_backBuffer->ReleasePrivate();
m_swapImage = nullptr;
m_swapImageResolve = nullptr;
m_swapImageView = nullptr;
m_backBuffer = nullptr;
// Create new back buffer
D3D11_COMMON_TEXTURE_DESC desc;
desc.Width = std::max(m_desc.Width, 1u);
desc.Height = std::max(m_desc.Height, 1u);
desc.Depth = 1;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = m_desc.Format;
desc.SampleDesc = m_desc.SampleDesc;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET
| D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED;
if (m_desc.BufferUsage & DXGI_USAGE_UNORDERED_ACCESS)
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE)
desc.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
m_backBuffer = new D3D11Texture2D(m_parent, &desc);
m_backBuffer->AddRefPrivate();
m_swapImage = GetCommonTexture(m_backBuffer)->GetImage();
// If the image is multisampled, we need to create
// another image which we'll use as a resolve target
if (m_swapImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT) {
DxvkImageCreateInfo resolveInfo;
resolveInfo.type = VK_IMAGE_TYPE_2D;
resolveInfo.format = m_swapImage->info().format;
resolveInfo.flags = 0;
resolveInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
resolveInfo.extent = m_swapImage->info().extent;
resolveInfo.numLayers = 1;
resolveInfo.mipLevels = 1;
resolveInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT;
resolveInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
| VK_PIPELINE_STAGE_TRANSFER_BIT;
resolveInfo.access = VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
resolveInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
resolveInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
m_swapImageResolve = m_device->createImage(
resolveInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
// 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.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_swapImageResolve != nullptr
? m_swapImageResolve
: m_swapImage,
viewInfo);
// Initialize the image so that we can use it. Clearing
// to black prevents garbled output for the first frame.
VkImageSubresourceRange subresources;
subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresources.baseMipLevel = 0;
subresources.levelCount = 1;
subresources.baseArrayLayer = 0;
subresources.layerCount = 1;
VkClearColorValue clearColor;
clearColor.float32[0] = 0.0f;
clearColor.float32[1] = 0.0f;
clearColor.float32[2] = 0.0f;
clearColor.float32[3] = 0.0f;
m_context->beginRecording(
m_device->createCommandList());
m_context->clearColorImage(
m_swapImage, clearColor, subresources);
m_device->submitCommandList(
m_context->endRecording(),
VK_NULL_HANDLE,
VK_NULL_HANDLE);
}
void D3D11SwapChain::CreateGammaTexture(
UINT NumControlPoints,
const D3D11_VK_GAMMA_CP* pControlPoints) {
if (m_gammaTexture == nullptr
|| m_gammaTexture->info().extent.width != NumControlPoints) {
DxvkImageCreateInfo imgInfo;
imgInfo.type = VK_IMAGE_TYPE_1D;
imgInfo.format = VK_FORMAT_R16G16B16A16_UNORM;
imgInfo.flags = 0;
imgInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imgInfo.extent = { NumControlPoints, 1, 1 };
imgInfo.numLayers = 1;
imgInfo.mipLevels = 1;
imgInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
imgInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
imgInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT;
imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
m_gammaTexture = m_device->createImage(
imgInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageViewCreateInfo viewInfo;
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.format = VK_FORMAT_R16G16B16A16_UNORM;
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_gammaTextureView = m_device->createImageView(m_gammaTexture, viewInfo);
}
m_context->beginRecording(
m_device->createCommandList());
m_context->updateImage(m_gammaTexture,
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
VkOffset3D { 0, 0, 0 },
VkExtent3D { NumControlPoints, 1, 1 },
pControlPoints, 0, 0);
m_device->submitCommandList(
m_context->endRecording(),
VK_NULL_HANDLE,
VK_NULL_HANDLE);
}
void D3D11SwapChain::DestroyFrameLatencyEvent() {
CloseHandle(m_frameLatencyEvent);
}
void D3D11SwapChain::DestroyGammaTexture() {
m_gammaTexture = nullptr;
m_gammaTextureView = nullptr;
}
void D3D11SwapChain::CreateHud() {
m_hud = hud::Hud::createHud(m_device);
if (m_hud != nullptr)
m_hud->addItem<hud::HudClientApiItem>("api", 1, GetApiName());
}
void D3D11SwapChain::InitRenderState() {
m_iaState.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
m_iaState.primitiveRestart = VK_FALSE;
m_iaState.patchVertexCount = 0;
m_rsState.polygonMode = VK_POLYGON_MODE_FILL;
m_rsState.cullMode = VK_CULL_MODE_BACK_BIT;
m_rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
m_rsState.depthClipEnable = VK_FALSE;
m_rsState.depthBiasEnable = VK_FALSE;
m_rsState.sampleCount = VK_SAMPLE_COUNT_1_BIT;
m_msState.sampleMask = 0xffffffff;
m_msState.enableAlphaToCoverage = VK_FALSE;
VkStencilOpState stencilOp;
stencilOp.failOp = VK_STENCIL_OP_KEEP;
stencilOp.passOp = VK_STENCIL_OP_KEEP;
stencilOp.depthFailOp = VK_STENCIL_OP_KEEP;
stencilOp.compareOp = VK_COMPARE_OP_ALWAYS;
stencilOp.compareMask = 0xFFFFFFFF;
stencilOp.writeMask = 0xFFFFFFFF;
stencilOp.reference = 0;
m_dsState.enableDepthTest = VK_FALSE;
m_dsState.enableDepthWrite = VK_FALSE;
m_dsState.enableStencilTest = VK_FALSE;
m_dsState.depthCompareOp = VK_COMPARE_OP_ALWAYS;
m_dsState.stencilOpFront = stencilOp;
m_dsState.stencilOpBack = stencilOp;
m_loState.enableLogicOp = VK_FALSE;
m_loState.logicOp = VK_LOGIC_OP_NO_OP;
m_blendMode.enableBlending = VK_FALSE;
m_blendMode.colorSrcFactor = VK_BLEND_FACTOR_ONE;
m_blendMode.colorDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_blendMode.colorBlendOp = VK_BLEND_OP_ADD;
m_blendMode.alphaSrcFactor = VK_BLEND_FACTOR_ONE;
m_blendMode.alphaDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_blendMode.alphaBlendOp = VK_BLEND_OP_ADD;
m_blendMode.writeMask = VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
| VK_COLOR_COMPONENT_A_BIT;
}
void D3D11SwapChain::InitSamplers() {
DxvkSamplerCreateInfo samplerInfo;
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.mipmapLodBias = 0.0f;
samplerInfo.mipmapLodMin = 0.0f;
samplerInfo.mipmapLodMax = 0.0f;
samplerInfo.useAnisotropy = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.compareToDepth = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.borderColor = VkClearColorValue();
samplerInfo.usePixelCoord = VK_FALSE;
m_samplerFitting = m_device->createSampler(samplerInfo);
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
m_samplerScaling = m_device->createSampler(samplerInfo);
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
m_gammaSampler = m_device->createSampler(samplerInfo);
}
void D3D11SwapChain::InitShaders() {
const SpirvCodeBuffer vsCode(dxgi_presenter_vert);
const SpirvCodeBuffer fsCode(dxgi_presenter_frag);
const std::array<DxvkResourceSlot, 2> fsResourceSlots = {{
{ BindingIds::Image, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_2D },
{ BindingIds::Gamma, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_1D },
}};
m_vertShader = m_device->createShader(
VK_SHADER_STAGE_VERTEX_BIT,
0, nullptr, { 0u, 1u },
vsCode);
m_fragShader = m_device->createShader(
VK_SHADER_STAGE_FRAGMENT_BIT,
fsResourceSlots.size(),
fsResourceSlots.data(),
{ 1u, 1u }, fsCode);
}
void D3D11SwapChain::SignalFrameLatencyEvent() {
if (m_frameLatencyEvent) {
// Signal event with the same value that we'd wait for during the next present.
m_frameLatencySignal->setEvent(m_frameLatencyEvent, m_frameId - GetActualFrameLatency() + 1);
}
}
uint32_t D3D11SwapChain::GetActualFrameLatency() {
uint32_t maxFrameLatency = m_frameLatency;
if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
m_dxgiDevice->GetMaximumFrameLatency(&maxFrameLatency);
if (m_frameLatencyCap)
maxFrameLatency = std::min(maxFrameLatency, m_frameLatencyCap);
maxFrameLatency = std::min(maxFrameLatency, m_desc.BufferCount + 1);
return maxFrameLatency;
}
uint32_t D3D11SwapChain::PickFormats(
DXGI_FORMAT Format,
VkSurfaceFormatKHR* pDstFormats) {
uint32_t n = 0;
switch (Format) {
default:
Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format));
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM: {
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
} break;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: {
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
} break;
case DXGI_FORMAT_R10G10B10A2_UNORM: {
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
} break;
case DXGI_FORMAT_R16G16B16A16_FLOAT: {
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
} break;
}
return n;
}
uint32_t D3D11SwapChain::PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes) {
uint32_t n = 0;
if (Vsync) {
pDstModes[n++] = VK_PRESENT_MODE_FIFO_KHR;
} else {
if (!m_parent->GetOptions()->tearFree)
pDstModes[n++] = VK_PRESENT_MODE_IMMEDIATE_KHR;
pDstModes[n++] = VK_PRESENT_MODE_MAILBOX_KHR;
pDstModes[n++] = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
}
return n;
}
uint32_t D3D11SwapChain::PickImageCount(
UINT Preferred) {
int32_t option = m_parent->GetOptions()->numBackBuffers;
return option > 0 ? uint32_t(option) : uint32_t(Preferred);
}
VkFullScreenExclusiveEXT D3D11SwapChain::PickFullscreenMode() {
return m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT
: VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;
}
std::string D3D11SwapChain::GetApiName() const {
Com<IDXGIDXVKDevice> device;
m_parent->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast<void**>(&device));
uint32_t apiVersion = device->GetAPIVersion();
uint32_t featureLevel = m_parent->GetFeatureLevel();
uint32_t flHi = (featureLevel >> 12);
uint32_t flLo = (featureLevel >> 8) & 0x7;
return str::format("D3D", apiVersion, " FL", flHi, "_", flLo);
}
}