1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-11 10:24:10 +01:00

[dxvk] Refactor swap chain blitter to use plain Vulkan

Temporarily disable the HUD until that is refactored too.
This commit is contained in:
Philip Rebohle 2024-09-30 13:53:31 +02:00 committed by Philip Rebohle
parent 1c06431e18
commit 207e15eb24
12 changed files with 860 additions and 423 deletions

View File

@ -398,13 +398,16 @@ namespace dxvk {
m_context->beginRecording(
m_device->createCommandList());
m_blitter->presentImage(m_context.ptr(),
m_imageViews.at(imageIndex), VkRect2D(),
m_swapImageView, VkRect2D());
m_blitter->beginPresent(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex), m_colorspace, VkRect2D(),
m_swapImageView, m_colorspace, VkRect2D());
if (m_hud != nullptr)
m_hud->render(m_context, info.format, info.imageExtent);
// if (m_hud != nullptr)
// m_hud->render(m_context, info.format, info.imageExtent);
m_blitter->endPresent(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex));
SubmitPresent(immediateContext, sync, i);
}

View File

@ -847,12 +847,15 @@ namespace dxvk {
{ int32_t(m_dstRect.left), int32_t(m_dstRect.top) },
{ uint32_t(m_dstRect.right - m_dstRect.left), uint32_t(m_dstRect.bottom - m_dstRect.top) } };
m_blitter->presentImage(m_context.ptr(),
m_wctx->imageViews.at(imageIndex), dstRect,
swapImageView, srcRect);
m_blitter->beginPresent(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex), m_colorspace, dstRect,
swapImageView, m_colorspace, srcRect);
if (m_hud != nullptr)
m_hud->render(m_context, info.format, info.imageExtent);
// if (m_hud != nullptr)
// m_hud->render(m_context, info.format, info.imageExtent);
m_blitter->endPresent(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex));
SubmitPresent(sync, i);
}

View File

@ -4,30 +4,46 @@
#include <dxvk_present_frag_blit.h>
#include <dxvk_present_frag_ms.h>
#include <dxvk_present_frag_ms_amd.h>
#include <dxvk_present_frag_ms_blit.h>
#include <dxvk_present_vert.h>
namespace dxvk {
DxvkSwapchainBlitter::DxvkSwapchainBlitter(const Rc<DxvkDevice>& device)
: m_device(device) {
: m_device(device),
m_setLayout(createSetLayout()),
m_pipelineLayout(createPipelineLayout()) {
this->createSampler();
this->createShaders();
}
DxvkSwapchainBlitter::~DxvkSwapchainBlitter() {
auto vk = m_device->vkd();
for (const auto& p : m_pipelines)
vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_shaderVsBlit.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_shaderFsBlit.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_shaderFsCopy.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_shaderFsMsBlit.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_shaderFsMsResolve.stageInfo.module, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr);
}
void DxvkSwapchainBlitter::presentImage(
DxvkContext* ctx,
void DxvkSwapchainBlitter::beginPresent(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
VkRect2D dstRect,
const Rc<DxvkImageView>& srcView,
VkColorSpaceKHR srcColorSpace,
VkRect2D srcRect) {
if (m_gammaDirty)
this->updateGammaTexture(ctx);
std::unique_lock lock(m_mutex);
// Fix up default present areas if necessary
if (!dstRect.extent.width || !dstRect.extent.height) {
@ -44,244 +60,250 @@ namespace dxvk {
srcView->image()->info().extent.height };
}
bool sameSize = dstRect.extent == srcRect.extent;
bool usedResolveImage = false;
if (m_gammaBuffer)
uploadGammaImage(ctx);
if (srcView->image()->info().sampleCount == VK_SAMPLE_COUNT_1_BIT) {
this->draw(ctx, sameSize ? m_fsCopy : m_fsBlit,
dstView, dstRect, srcView, srcRect);
} else if (sameSize) {
this->draw(ctx, m_fsResolve,
dstView, dstRect, srcView, srcRect);
} else {
if (m_resolveImage == nullptr
|| m_resolveImage->info().extent != srcView->image()->info().extent
|| m_resolveImage->info().format != srcView->image()->info().format)
this->createResolveImage(srcView->image()->info());
VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
barrier.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = dstView->image()->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = dstView->image()->handle();
barrier.subresourceRange = dstView->imageSubresources();
this->resolve(ctx, m_resolveView, srcView);
this->draw(ctx, m_fsBlit, dstView, dstRect, m_resolveView, srcRect);
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.imageMemoryBarrierCount = 1;
depInfo.pImageMemoryBarriers = &barrier;
usedResolveImage = true;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);
VkExtent3D dstExtent = dstView->mipLevelExtent(0u);
VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };
attachmentInfo.imageView = dstView->handle();
attachmentInfo.imageLayout = dstView->image()->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
if (srcRect.extent != dstRect.extent)
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
VkRenderingInfo renderInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };
renderInfo.renderArea.offset = { 0u, 0u };
renderInfo.renderArea.extent = { dstExtent.width, dstExtent.height };
renderInfo.layerCount = 1u;
renderInfo.colorAttachmentCount = 1;
renderInfo.pColorAttachments = &attachmentInfo;
ctx.cmd->cmdBeginRendering(&renderInfo);
DxvkSwapchainPipelineKey key;
key.srcSpace = srcColorSpace;
key.srcSamples = srcView->image()->info().sampleCount;
key.srcIsSrgb = srcView->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
key.dstSpace = dstColorSpace;
key.dstFormat = dstView->info().format;
key.needsGamma = m_gammaView != nullptr;
key.needsBlit = dstRect.extent != srcRect.extent;
VkPipeline pipeline = getPipeline(key);
VkViewport viewport = { };
viewport.x = float(dstRect.offset.x);
viewport.y = float(dstRect.offset.y);
viewport.width = float(dstRect.extent.width);
viewport.height = float(dstRect.extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 0.0f;
ctx.cmd->cmdSetViewport(1, &viewport);
ctx.cmd->cmdSetScissor(1, &dstRect);
ctx.cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout);
VkDescriptorImageInfo imageDescriptor = { };
imageDescriptor.sampler = m_samplerPresent->handle();
imageDescriptor.imageView = srcView->handle();
imageDescriptor.imageLayout = srcView->image()->info().layout;
VkDescriptorImageInfo gammaDescriptor = { };
gammaDescriptor.sampler = m_samplerGamma->handle();
if (m_gammaView) {
gammaDescriptor.imageView = m_gammaView->handle();
gammaDescriptor.imageLayout = m_gammaView->image()->info().layout;
}
if (!usedResolveImage)
this->destroyResolveImage();
std::array<VkWriteDescriptorSet, 2> descriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageDescriptor },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &gammaDescriptor },
}};
ctx.cmd->updateDescriptorSets(descriptorWrites.size(), descriptorWrites.data());
ctx.cmd->cmdBindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, set, 0, nullptr);
PushConstants args = { };
args.srcOffset = srcRect.offset;
args.srcExtent = srcRect.extent;
args.dstOffset = dstRect.offset;
ctx.cmd->cmdPushConstants(m_pipelineLayout,
VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(args), &args);
ctx.cmd->cmdDraw(3, 1, 0, 0);
// Make sure to keep used resources alive
ctx.cmd->trackResource<DxvkAccess::Read>(srcView->image());
ctx.cmd->trackResource<DxvkAccess::Write>(dstView->image());
if (m_gammaImage)
ctx.cmd->trackResource<DxvkAccess::Read>(m_gammaImage->getAllocation());
ctx.cmd->trackSampler(m_samplerGamma);
ctx.cmd->trackSampler(m_samplerPresent);
}
void DxvkSwapchainBlitter::endPresent(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView) {
ctx.cmd->cmdEndRendering();
VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT;
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
barrier.oldLayout = dstView->image()->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
barrier.newLayout = dstView->image()->info().layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = dstView->image()->handle();
barrier.subresourceRange = dstView->imageSubresources();
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.imageMemoryBarrierCount = 1;
depInfo.pImageMemoryBarriers = &barrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);
}
void DxvkSwapchainBlitter::setGammaRamp(
uint32_t cpCount,
const DxvkGammaCp* cpData) {
VkDeviceSize size = cpCount * sizeof(*cpData);
std::unique_lock lock(m_mutex);
if (cpCount) {
if (!m_gammaBuffer || m_gammaBuffer->info().size < size) {
DxvkBufferCreateInfo bufInfo;
bufInfo.size = size;
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
bufInfo.access = VK_ACCESS_TRANSFER_READ_BIT;
// Create temporary upload buffer for the curve
DxvkBufferCreateInfo bufferInfo = { };
bufferInfo.size = cpCount * sizeof(*cpData);
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;
m_gammaBuffer = m_device->createBuffer(bufInfo,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_gammaSlice = m_gammaBuffer->getAllocation();
} else {
m_gammaSlice = m_gammaBuffer->allocateSlice();
}
m_gammaBuffer = m_device->createBuffer(bufferInfo,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_gammaCpCount = cpCount;
std::memcpy(m_gammaSlice->mapPtr(), cpData, size);
std::memcpy(m_gammaBuffer->mapPtr(0), cpData, cpCount * sizeof(*cpData));
} else {
// Destroy gamma image altogether
m_gammaBuffer = nullptr;
m_gammaSlice = nullptr;
}
m_gammaCpCount = cpCount;
m_gammaDirty = true;
}
void DxvkSwapchainBlitter::draw(
DxvkContext* ctx,
const Rc<DxvkShader>& fs,
const Rc<DxvkImageView>& dstView,
VkRect2D dstRect,
const Rc<DxvkImageView>& srcView,
VkRect2D srcRect) {
DxvkInputAssemblyState iaState;
iaState.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
iaState.primitiveRestart = VK_FALSE;
iaState.patchVertexCount = 0;
ctx->setInputAssemblyState(iaState);
ctx->setInputLayout(0, nullptr, 0, nullptr);
DxvkRasterizerState rsState;
rsState.polygonMode = VK_POLYGON_MODE_FILL;
rsState.cullMode = VK_CULL_MODE_BACK_BIT;
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rsState.depthClipEnable = VK_FALSE;
rsState.depthBiasEnable = VK_FALSE;
rsState.conservativeMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
rsState.sampleCount = VK_SAMPLE_COUNT_1_BIT;
rsState.flatShading = VK_FALSE;
rsState.lineMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT;
ctx->setRasterizerState(rsState);
DxvkMultisampleState msState;
msState.sampleMask = 0xffffffff;
msState.enableAlphaToCoverage = VK_FALSE;
ctx->setMultisampleState(msState);
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;
DxvkDepthStencilState dsState;
dsState.enableDepthTest = VK_FALSE;
dsState.enableDepthWrite = VK_FALSE;
dsState.enableStencilTest = VK_FALSE;
dsState.depthCompareOp = VK_COMPARE_OP_ALWAYS;
dsState.stencilOpFront = stencilOp;
dsState.stencilOpBack = stencilOp;
ctx->setDepthStencilState(dsState);
DxvkLogicOpState loState;
loState.enableLogicOp = VK_FALSE;
loState.logicOp = VK_LOGIC_OP_NO_OP;
ctx->setLogicOpState(loState);
DxvkBlendMode blendMode;
blendMode.enableBlending = VK_FALSE;
blendMode.colorSrcFactor = VK_BLEND_FACTOR_ONE;
blendMode.colorDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendMode.colorBlendOp = VK_BLEND_OP_ADD;
blendMode.alphaSrcFactor = VK_BLEND_FACTOR_ONE;
blendMode.alphaDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendMode.alphaBlendOp = VK_BLEND_OP_ADD;
blendMode.writeMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
ctx->setBlendMode(0, blendMode);
VkViewport viewport;
viewport.x = float(dstRect.offset.x);
viewport.y = float(dstRect.offset.y);
viewport.width = float(dstRect.extent.width);
viewport.height = float(dstRect.extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
ctx->setViewports(1, &viewport, &dstRect);
DxvkRenderTargets renderTargets;
renderTargets.color[0].view = dstView;
renderTargets.color[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
ctx->bindRenderTargets(std::move(renderTargets), 0u);
VkExtent2D dstExtent = {
dstView->image()->info().extent.width,
dstView->image()->info().extent.height };
if (dstRect.extent == dstExtent)
ctx->discardImageView(dstView, VK_IMAGE_ASPECT_COLOR_BIT);
else
ctx->clearRenderTarget(dstView, VK_IMAGE_ASPECT_COLOR_BIT, VkClearValue());
ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Image, Rc<DxvkSampler>(m_samplerPresent));
ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Gamma, Rc<DxvkSampler>(m_samplerGamma));
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Image, Rc<DxvkImageView>(srcView));
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Gamma, Rc<DxvkImageView>(m_gammaView));
ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(fs));
PresenterArgs args;
args.srcOffset = srcRect.offset;
if (dstRect.extent == srcRect.extent)
args.dstOffset = dstRect.offset;
else
args.srcExtent = srcRect.extent;
ctx->pushConstants(0, sizeof(args), &args);
ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 0, srcView->image()->info().sampleCount);
ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 1, m_gammaView != nullptr);
ctx->draw(3, 1, 0, 0);
}
void DxvkSwapchainBlitter::resolve(
DxvkContext* ctx,
const Rc<DxvkImageView>& dstView,
const Rc<DxvkImageView>& srcView) {
VkImageResolve resolve;
resolve.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
resolve.srcOffset = { 0, 0, 0 };
resolve.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
resolve.dstOffset = { 0, 0, 0 };
resolve.extent = dstView->image()->info().extent;
ctx->resolveImage(dstView->image(), srcView->image(), resolve, VK_FORMAT_UNDEFINED);
}
void DxvkSwapchainBlitter::updateGammaTexture(DxvkContext* ctx) {
uint32_t n = m_gammaCpCount;
if (n) {
// Reuse existing image if possible
if (m_gammaImage == nullptr || m_gammaImage->info().extent.width != n) {
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 = { n, 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_gammaImage = m_device->createImage(
imgInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageViewKey viewInfo;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.format = VK_FORMAT_R16G16B16A16_UNORM;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
m_gammaView = m_gammaImage->createView(viewInfo);
}
ctx->invalidateBuffer(m_gammaBuffer, std::move(m_gammaSlice));
ctx->copyBufferToImage(m_gammaImage,
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
VkOffset3D { 0, 0, 0 },
VkExtent3D { n, 1, 1 },
m_gammaBuffer, 0, 0, 0);
} else {
m_gammaImage = nullptr;
m_gammaView = nullptr;
m_gammaView = nullptr;
m_gammaCpCount = 0;
}
}
void DxvkSwapchainBlitter::uploadGammaImage(
const DxvkContextObjects& ctx) {
if (!m_gammaImage || m_gammaImage->info().extent.width != m_gammaCpCount) {
DxvkImageCreateInfo imageInfo = { };
imageInfo.type = VK_IMAGE_TYPE_1D;
imageInfo.format = VK_FORMAT_R16G16B16A16_UNORM;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent = { m_gammaCpCount, 1u, 1u };
imageInfo.numLayers = 1u;
imageInfo.mipLevels = 1u;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
imageInfo.access = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
m_gammaImage = m_device->createImage(imageInfo,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageViewKey viewInfo = { };
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.format = imageInfo.format;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.mipIndex = 0u;
viewInfo.mipCount = 1u;
viewInfo.layerIndex = 0u;
viewInfo.layerCount = 1u;
m_gammaView = m_gammaImage->createView(viewInfo);
}
m_gammaDirty = false;
VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
barrier.srcStageMask = m_gammaImage->info().stages;
barrier.srcAccessMask = m_gammaImage->info().access;
barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = m_gammaImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_gammaImage->handle();
barrier.subresourceRange = m_gammaImage->getAvailableSubresources();
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.imageMemoryBarrierCount = 1;
depInfo.pImageMemoryBarriers = &barrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);
DxvkBufferSliceHandle bufferSlice = m_gammaBuffer->getSliceHandle();
VkBufferImageCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };
copyRegion.bufferOffset = bufferSlice.offset;
copyRegion.imageExtent = { m_gammaCpCount, 1u, 1u };
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyRegion.imageSubresource.layerCount = 1u;
VkCopyBufferToImageInfo2 copy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };
copy.srcBuffer = bufferSlice.handle;
copy.dstImage = m_gammaImage->handle();
copy.dstImageLayout = m_gammaImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copy.regionCount = 1;
copy.pRegions = &copyRegion;
ctx.cmd->cmdCopyBufferToImage(DxvkCmdBuffer::ExecBuffer, &copy);
barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstStageMask = m_gammaImage->info().stages;
barrier.dstAccessMask = m_gammaImage->info().access;
barrier.oldLayout = barrier.newLayout;
barrier.newLayout = m_gammaImage->info().layout;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);
ctx.cmd->trackResource<DxvkAccess::Read>(m_gammaBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Write>(m_gammaImage->getAllocation());
m_gammaBuffer = nullptr;
}
@ -306,80 +328,219 @@ namespace dxvk {
m_samplerGamma = m_device->createSampler(samplerInfo);
}
void DxvkSwapchainBlitter::createShaders() {
SpirvCodeBuffer vsCode(dxvk_present_vert);
SpirvCodeBuffer fsCodeBlit(dxvk_present_frag_blit);
SpirvCodeBuffer fsCodeCopy(dxvk_present_frag);
SpirvCodeBuffer fsCodeResolve(dxvk_present_frag_ms);
SpirvCodeBuffer fsCodeResolveAmd(dxvk_present_frag_ms_amd);
const std::array<DxvkBindingInfo, 2> fsBindings = {{
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, BindingIds::Image, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, BindingIds::Gamma, VK_IMAGE_VIEW_TYPE_1D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
void DxvkSwapchainBlitter::createShaders() {
createShaderModule(m_shaderVsBlit, VK_SHADER_STAGE_VERTEX_BIT,
sizeof(dxvk_present_vert), dxvk_present_vert);
createShaderModule(m_shaderFsBlit, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(dxvk_present_frag_blit), dxvk_present_frag_blit);
createShaderModule(m_shaderFsCopy, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(dxvk_present_frag), dxvk_present_frag);
createShaderModule(m_shaderFsMsBlit, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(dxvk_present_frag_ms_blit), dxvk_present_frag_ms_blit);
if (m_device->features().amdShaderFragmentMask) {
createShaderModule(m_shaderFsMsResolve, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(dxvk_present_frag_ms_amd), dxvk_present_frag_ms_amd);
} else {
createShaderModule(m_shaderFsMsResolve, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(dxvk_present_frag_ms), dxvk_present_frag_ms);
}
}
void DxvkSwapchainBlitter::createShaderModule(
ShaderModule& shader,
VkShaderStageFlagBits stage,
size_t size,
const uint32_t* code) {
shader.moduleInfo.codeSize = size;
shader.moduleInfo.pCode = code;
shader.stageInfo.stage = stage;
shader.stageInfo.pName = "main";
if (m_device->features().khrMaintenance5.maintenance5
|| m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) {
shader.stageInfo.pNext = &shader.moduleInfo;
return;
}
auto vk = m_device->vkd();
VkResult vr = vk->vkCreateShaderModule(vk->device(),
&shader.moduleInfo, nullptr, &shader.stageInfo.module);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit shader module: ", vr));
}
VkDescriptorSetLayout DxvkSwapchainBlitter::createSetLayout() {
auto vk = m_device->vkd();
std::array<VkDescriptorSetLayoutBinding, 3> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
{ 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
{ 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
}};
DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(PresenterArgs);
vsInfo.outputMask = 0x1;
m_vs = new DxvkShader(vsInfo, std::move(vsCode));
DxvkShaderCreateInfo fsInfo;
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(PresenterArgs);
fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1;
m_fsBlit = new DxvkShader(fsInfo, std::move(fsCodeBlit));
fsInfo.inputMask = 0;
m_fsCopy = new DxvkShader(fsInfo, std::move(fsCodeCopy));
m_fsResolve = new DxvkShader(fsInfo, m_device->features().amdShaderFragmentMask
? std::move(fsCodeResolveAmd)
: std::move(fsCodeResolve));
}
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
info.bindingCount = bindings.size();
info.pBindings = bindings.data();
void DxvkSwapchainBlitter::createResolveImage(const DxvkImageCreateInfo& info) {
DxvkImageCreateInfo newInfo;
newInfo.type = VK_IMAGE_TYPE_2D;
newInfo.format = info.format;
newInfo.flags = 0;
newInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
newInfo.extent = info.extent;
newInfo.numLayers = 1;
newInfo.mipLevels = 1;
newInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
newInfo.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
| VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
newInfo.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT;
newInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
newInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
m_resolveImage = m_device->createImage(newInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
DxvkImageViewKey viewInfo;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = info.format;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
m_resolveView = m_resolveImage->createView(viewInfo);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit descriptor set layout: ", vr));
return layout;
}
void DxvkSwapchainBlitter::destroyResolveImage() {
m_resolveImage = nullptr;
m_resolveView = nullptr;
VkPipelineLayout DxvkSwapchainBlitter::createPipelineLayout() {
auto vk = m_device->vkd();
VkPushConstantRange pushConst = { };
pushConst.size = sizeof(PushConstants);
pushConst.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
info.setLayoutCount = 1;
info.pSetLayouts = &m_setLayout;
info.pushConstantRangeCount = 1;
info.pPushConstantRanges = &pushConst;
VkPipelineLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit pipeline layout: ", vr));
return layout;
}
VkPipeline DxvkSwapchainBlitter::createPipeline(
const DxvkSwapchainPipelineKey& key) {
auto vk = m_device->vkd();
static const std::array<VkSpecializationMapEntry, 6> specMap = {{
{ 0, offsetof(SpecConstants, sampleCount), sizeof(VkSampleCountFlagBits) },
{ 1, offsetof(SpecConstants, gammaBound), sizeof(VkBool32) },
{ 2, offsetof(SpecConstants, srcSpace), sizeof(VkColorSpaceKHR) },
{ 3, offsetof(SpecConstants, srcIsSrgb), sizeof(VkBool32) },
{ 4, offsetof(SpecConstants, dstSpace), sizeof(VkColorSpaceKHR) },
{ 5, offsetof(SpecConstants, dstIsSrgb), sizeof(VkBool32) },
}};
SpecConstants specConstants = { };
specConstants.sampleCount = key.srcSamples;
specConstants.gammaBound = key.needsGamma && key.srcSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
specConstants.srcSpace = key.srcSpace;
specConstants.srcIsSrgb = key.srcIsSrgb;
specConstants.dstSpace = key.dstSpace;
specConstants.dstIsSrgb = lookupFormatInfo(key.dstFormat)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
// Avoid redundant color space conversions if color spaces
// and images properties match and we don't do a resolve
if (specConstants.srcSpace == specConstants.dstSpace && key.srcSamples == VK_SAMPLE_COUNT_1_BIT) {
specConstants.srcSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
specConstants.dstSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
}
VkSpecializationInfo specInfo = { };
specInfo.mapEntryCount = specMap.size();
specInfo.pMapEntries = specMap.data();
specInfo.dataSize = sizeof(specConstants);
specInfo.pData = &specConstants;
std::array<VkPipelineShaderStageCreateInfo, 2> blitStages = { };
blitStages[0] = m_shaderVsBlit.stageInfo;
if (key.srcSamples == VK_SAMPLE_COUNT_1_BIT)
blitStages[1] = key.needsBlit ? m_shaderFsBlit.stageInfo : m_shaderFsCopy.stageInfo;
else
blitStages[1] = key.needsBlit ? m_shaderFsMsBlit.stageInfo : m_shaderFsMsResolve.stageInfo;
blitStages[1].pSpecializationInfo = &specInfo;
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
rtInfo.colorAttachmentCount = 1;
rtInfo.pColorAttachmentFormats = &key.dstFormat;
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
rsState.cullMode = VK_CULL_MODE_NONE;
rsState.polygonMode = VK_POLYGON_MODE_FILL;
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rsState.lineWidth = 1.0f;
constexpr uint32_t sampleMask = 0x1;
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
msState.pSampleMask = &sampleMask;
VkPipelineColorBlendAttachmentState cbOpaqueAttachment = { };
cbOpaqueAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
cbOpaqueState.attachmentCount = 1;
cbOpaqueState.pAttachments = &cbOpaqueAttachment;
static const std::array<VkDynamicState, 2> dynStates = {
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
};
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynState.dynamicStateCount = dynStates.size();
dynState.pDynamicStates = dynStates.data();
VkGraphicsPipelineCreateInfo blitInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
blitInfo.stageCount = blitStages.size();
blitInfo.pStages = blitStages.data();
blitInfo.pVertexInputState = &viState;
blitInfo.pInputAssemblyState = &iaState;
blitInfo.pViewportState = &vpState;
blitInfo.pRasterizationState = &rsState;
blitInfo.pMultisampleState = &msState;
blitInfo.pColorBlendState = &cbOpaqueState;
blitInfo.pDynamicState = &dynState;
blitInfo.layout = m_pipelineLayout;
blitInfo.basePipelineIndex = -1;
VkPipeline pipeline = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
1, &blitInfo, nullptr, &pipeline);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit pipeline: ", vr));
return pipeline;
}
VkPipeline DxvkSwapchainBlitter::getPipeline(
const DxvkSwapchainPipelineKey& key) {
auto entry = m_pipelines.find(key);
if (entry != m_pipelines.end())
return entry->second;
VkPipeline pipeline = createPipeline(key);
m_pipelines.insert({ key, pipeline });
return pipeline;
}
}

View File

@ -1,5 +1,11 @@
#pragma once
#include <functional>
#include <thread>
#include <unordered_map>
#include "../util/thread.h"
#include "../dxvk/dxvk_device.h"
#include "../dxvk/dxvk_context.h"
@ -11,7 +17,57 @@ namespace dxvk {
struct DxvkGammaCp {
uint16_t r, g, b, a;
};
/**
* \brief Swap chain blitter pipeline key
*
* Used to look up specific pipelines.
*/
struct DxvkSwapchainPipelineKey {
/// Input color space. If this does not match the output color
/// space, the input will be converted to match the output.
VkColorSpaceKHR srcSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;
/// Source image sample count. Used to determine the shader to
/// use, and passed to it via a spec constant.
VkSampleCountFlagBits srcSamples = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
/// Whether the source image uses an sRGB format. Relevant for
/// automatic color space conversion.
VkBool32 srcIsSrgb = VK_FALSE;
/// Output color space.
VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;
/// Output image format. Used as pipeline state, but also to
/// determine the sRGB-ness of the format.
VkFormat dstFormat = VK_FORMAT_UNDEFINED;
/// Bit indicating whether the input and output dimensions match.
VkBool32 needsBlit = VK_FALSE;
/// Bit indicating whether a gamma curve is to be applied.
VkBool32 needsGamma = VK_FALSE;
size_t hash() const {
DxvkHashState hash;
hash.add(uint32_t(srcSpace));
hash.add(uint32_t(srcSamples));
hash.add(uint32_t(srcIsSrgb));
hash.add(uint32_t(dstSpace));
hash.add(uint32_t(dstFormat));
hash.add(uint32_t(needsBlit));
hash.add(uint32_t(needsGamma));
return hash;
}
bool eq(const DxvkSwapchainPipelineKey& other) const {
return srcSpace == other.srcSpace
&& srcSamples == other.srcSamples
&& srcIsSrgb == other.srcIsSrgb
&& dstSpace == other.dstSpace
&& dstFormat == other.dstFormat
&& needsBlit == other.needsBlit
&& needsGamma == other.needsGamma;
}
};
/**
* \brief Swap chain blitter
*
@ -26,21 +82,39 @@ namespace dxvk {
~DxvkSwapchainBlitter();
/**
* \brief Records presentation commands
* \brief Begins recording presentation commands
*
* \param [in] ctx Context
* Sets up the swap chain image and all internal resources, and
* blits the source image onto the swap chain appropriately.
* The swap chain image will remain bound for rendering.
* \param [in] ctx Context objects
* \param [in] dstView Swap chain image view
* \param [in] srcView Image to present
* \param [in] dstColorSpace Swap chain color space
* \param [in] dstRect Destination rectangle
* \param [in] srcRect Back buffer rectangle
* \param [in] srcView Image to present
* \param [in] srcColorSpace Image color space
* \param [in] srcRect Source rectangle to present
*/
void presentImage(
DxvkContext* ctx,
void beginPresent(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
VkRect2D dstRect,
const Rc<DxvkImageView>& srcView,
VkColorSpaceKHR srcColorSpace,
VkRect2D srcRect);
/**
* \brief Finalizes presentation commands
*
* Finishes rendering and prepares the image for presentation.
* \param [in] ctx Context objects
* \param [in] dstView Swap chain image view
*/
void endPresent(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView);
/**
* \brief Sets gamma ramp
*
@ -56,62 +130,71 @@ namespace dxvk {
private:
enum BindingIds : uint32_t {
Image = 0,
Gamma = 1,
struct SpecConstants {
VkSampleCountFlagBits sampleCount;
VkBool32 gammaBound;
VkColorSpaceKHR srcSpace;
VkBool32 srcIsSrgb;
VkColorSpaceKHR dstSpace;
VkBool32 dstIsSrgb;
};
struct PresenterArgs {
struct PushConstants {
VkOffset2D srcOffset;
union {
VkExtent2D srcExtent;
VkOffset2D dstOffset;
};
VkExtent2D srcExtent;
VkOffset2D dstOffset;
};
struct ShaderModule {
VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
VkPipelineShaderStageCreateInfo stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
};
Rc<DxvkDevice> m_device;
Rc<DxvkShader> m_fsCopy;
Rc<DxvkShader> m_fsBlit;
Rc<DxvkShader> m_fsResolve;
Rc<DxvkShader> m_vs;
ShaderModule m_shaderVsBlit;
ShaderModule m_shaderFsCopy;
ShaderModule m_shaderFsBlit;
ShaderModule m_shaderFsMsResolve;
ShaderModule m_shaderFsMsBlit;
dxvk::mutex m_mutex;
Rc<DxvkBuffer> m_gammaBuffer;
Rc<DxvkImage> m_gammaImage;
Rc<DxvkImageView> m_gammaView;
uint32_t m_gammaCpCount = 0;
bool m_gammaDirty = false;
Rc<DxvkResourceAllocation> m_gammaSlice = { };
Rc<DxvkImage> m_resolveImage;
Rc<DxvkImageView> m_resolveView;
Rc<DxvkSampler> m_samplerPresent;
Rc<DxvkSampler> m_samplerGamma;
void draw(
DxvkContext* ctx,
const Rc<DxvkShader>& fs,
const Rc<DxvkImageView>& dstView,
VkRect2D dstRect,
const Rc<DxvkImageView>& srcView,
VkRect2D srcRect);
VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
void resolve(
DxvkContext* ctx,
const Rc<DxvkImageView>& dstView,
const Rc<DxvkImageView>& srcView);
std::unordered_map<DxvkSwapchainPipelineKey,
VkPipeline, DxvkHash, DxvkEq> m_pipelines;
void updateGammaTexture(DxvkContext* ctx);
void uploadGammaImage(
const DxvkContextObjects& ctx);
void createSampler();
void createShaders();
void createResolveImage(
const DxvkImageCreateInfo& info);
void createShaderModule(
ShaderModule& shader,
VkShaderStageFlagBits stage,
size_t size,
const uint32_t* code);
void destroyResolveImage();
VkDescriptorSetLayout createSetLayout();
VkPipelineLayout createPipelineLayout();
VkPipeline createPipeline(
const DxvkSwapchainPipelineKey& key);
VkPipeline getPipeline(
const DxvkSwapchainPipelineKey& key);
};

View File

@ -37,6 +37,7 @@ dxvk_shaders = files([
'shaders/dxvk_present_frag_blit.frag',
'shaders/dxvk_present_frag_ms.frag',
'shaders/dxvk_present_frag_ms_amd.frag',
'shaders/dxvk_present_frag_ms_blit.frag',
'shaders/dxvk_present_vert.vert',
'shaders/dxvk_resolve_frag_d.frag',

View File

@ -0,0 +1,81 @@
#define VK_COLOR_SPACE_SRGB_NONLINEAR_KHR (0)
#define VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT (1000104002)
#define VK_COLOR_SPACE_HDR10_ST2084_EXT (1000104008)
#define VK_COLOR_SPACE_PASS_THROUGH_EXT (1000104013)
#define SDR_NITS (203.0f)
const mat3 rec709_to_xyz = mat3(
0.4123908, 0.2126390, 0.0193308,
0.3575843, 0.7151687, 0.1191948,
0.1804808, 0.0721923, 0.9505322);
const mat3 xyz_to_rec2020 = mat3(
1.7166512, -0.6666844, 0.0176399,
-0.3556708, 1.6164812, -0.0427706,
-0.2533663, 0.0157685, 0.9421031);
mat3 rec709_to_rec2020 = xyz_to_rec2020 * rec709_to_xyz;
mat3 rec2020_to_rec709 = inverse(rec709_to_rec2020);
// sRGB functions
vec3 linear_to_srgb(vec3 linear) {
bvec3 isLo = lessThanEqual(linear, vec3(0.0031308f));
vec3 loPart = linear * 12.92f;
vec3 hiPart = pow(linear, vec3(5.0f / 12.0f)) * 1.055f - 0.055f;
return mix(hiPart, loPart, isLo);
}
vec3 srgb_to_linear(vec3 srgb) {
bvec3 isLo = lessThanEqual(srgb, vec3(0.04045f));
vec3 loPart = srgb / 12.92f;
vec3 hiPart = pow((srgb + 0.055f) / 1.055f, vec3(12.0f / 5.0f));
return mix(hiPart, loPart, isLo);
}
// Perceptual quantizer conversion
vec3 nits_to_pq(vec3 nits) {
const float c1 = 0.8359375f;
const float c2 = 18.8515625f;
const float c3 = 18.6875f;
const float m1 = 0.1593017578125f;
const float m2 = 78.84375f;
vec3 y = clamp(nits / 10000.0f, vec3(0.0f), vec3(1.0f));
vec3 y_m1 = pow(y, vec3(m1));
vec3 num = c1 + c2 * y_m1;
vec3 den = 1.0f + c3 * y_m1;
return pow(num / den, vec3(m2));
}
vec3 pq_to_nits(vec3 pq) {
const float c1 = 0.8359375f;
const float c2 = 18.8515625f;
const float c3 = 18.6875f;
const float m1 = 0.1593017578125f;
const float m2 = 78.84375f;
vec3 pq_m2 = pow(pq, vec3(1.0f / m2));
vec3 num = max(pq_m2 - c1, 0.0f);
vec3 den = c2 - c3 * pq_m2;
vec3 y = pow(num / den, vec3(1.0f / m1));
return 10000.0f * y;
}
// scRGB conversion
vec3 nits_to_sc_rgb(vec3 nits) {
return nits / 80.0f;
}
vec3 sc_rgb_to_nits(vec3 sc_rgb) {
return sc_rgb * 80.0f;
}

View File

@ -0,0 +1,100 @@
#include "dxvk_color_space.glsl"
layout(constant_id = 0) const uint c_samples = 0u;
layout(constant_id = 1) const bool c_gamma = false;
layout(constant_id = 2) const uint c_src_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
layout(constant_id = 3) const bool c_src_is_srgb = true;
layout(constant_id = 4) const uint c_dst_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
layout(constant_id = 5) const bool c_dst_is_srgb = true;
layout(set = 0, binding = 0) uniform sampler2D s_image;
layout(set = 0, binding = 0) uniform sampler2DMS s_image_ms;
layout(set = 0, binding = 1) uniform sampler1D s_gamma;
layout(push_constant)
uniform present_info_t {
ivec2 src_offset;
ivec2 src_extent;
ivec2 dst_offset;
};
vec4 input_to_sc_rgb(vec4 color) {
switch (c_src_color_space) {
default:
case VK_COLOR_SPACE_PASS_THROUGH_EXT:
return color;
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {
if (!c_src_is_srgb)
color.rgb = srgb_to_linear(color.rgb);
color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);
return color;
}
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return color;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
color.rgb = nits_to_sc_rgb(pq_to_nits(color.rgb));
color.rgb = rec2020_to_rec709 * color.rgb;
return color;
}
}
vec4 sc_rgb_to_output(vec4 color) {
if (c_gamma) {
// If we need to apply a gamma curve, convert to sRGB, perform
// the lookup, and then convert back to scRGB as necessary
if (c_src_color_space != VK_COLOR_SPACE_PASS_THROUGH_EXT) {
color.rgb = sc_rgb_to_nits(color.rgb) / SDR_NITS;
color.rgb = linear_to_srgb(color.rgb);
} else if (c_src_is_srgb) {
color.rgb = linear_to_srgb(color.rgb);
}
color.rgb = vec3(
texture(s_gamma, color.r).r,
texture(s_gamma, color.g).g,
texture(s_gamma, color.b).b);
if (c_dst_color_space != VK_COLOR_SPACE_PASS_THROUGH_EXT) {
color.rgb = srgb_to_linear(color.rgb);
color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);
} else if (c_dst_is_srgb) {
color.rgb = srgb_to_linear(color.rgb);
}
}
switch (c_dst_color_space) {
default:
case VK_COLOR_SPACE_PASS_THROUGH_EXT:
// If we applied a gamma curve, the output is already correct
if (!c_gamma && c_src_is_srgb != c_dst_is_srgb) {
color.rgb = c_src_is_srgb
? linear_to_srgb(color.rgb)
: srgb_to_linear(color.rgb);
}
return color;
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {
color.rgb = sc_rgb_to_nits(color.rgb) / SDR_NITS;
if (!c_dst_is_srgb)
color.rgb = linear_to_srgb(color.rgb);
return color;
}
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return color;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
color.rgb = rec709_to_rec2020 * color.rgb;
color.rgb = nits_to_pq(sc_rgb_to_nits(color.rgb));
return color;
}
}

View File

@ -1,27 +1,14 @@
#version 450
layout(constant_id = 1) const bool s_gamma_bound = false;
#extension GL_GOOGLE_include_directive : enable
layout(binding = 0) uniform sampler2D s_image;
layout(binding = 1) uniform sampler1D s_gamma;
#include "dxvk_present_common.glsl"
layout(location = 0) out vec4 o_color;
layout(push_constant)
uniform present_info_t {
ivec2 src_offset;
ivec2 dst_offset;
};
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset;
o_color = texelFetch(s_image, coord, 0);
if (s_gamma_bound) {
o_color = vec4(
texture(s_gamma, o_color.r).r,
texture(s_gamma, o_color.g).g,
texture(s_gamma, o_color.b).b,
o_color.a);
}
}
o_color = input_to_sc_rgb(texelFetch(s_image, coord, 0));
o_color = sc_rgb_to_output(o_color);
}

View File

@ -1,28 +1,14 @@
#version 450
layout(constant_id = 1) const bool s_gamma_bound = false;
#extension GL_GOOGLE_include_directive : enable
layout(binding = 0) uniform sampler2D s_image;
layout(binding = 1) uniform sampler1D s_gamma;
#include "dxvk_present_common.glsl"
layout(location = 0) in vec2 i_coord;
layout(location = 0) out vec4 o_color;
layout(push_constant)
uniform present_info_t {
ivec2 src_offset;
uvec2 src_extent;
};
void main() {
vec2 coord = vec2(src_offset) + vec2(src_extent) * i_coord;
o_color = textureLod(s_image, coord, 0.0f);
if (s_gamma_bound) {
o_color = vec4(
texture(s_gamma, o_color.r).r,
texture(s_gamma, o_color.g).g,
texture(s_gamma, o_color.b).b,
o_color.a);
}
}
o_color = input_to_sc_rgb(textureLod(s_image, coord, 0.0f));
o_color = sc_rgb_to_output(o_color);
}

View File

@ -1,33 +1,17 @@
#version 450
layout(constant_id = 0) const uint c_samples = 0;
layout(constant_id = 1) const bool s_gamma_bound = false;
#extension GL_GOOGLE_include_directive : enable
layout(binding = 0) uniform sampler2DMS s_image;
layout(binding = 1) uniform sampler1D s_gamma;
#include "dxvk_present_common.glsl"
layout(location = 0) out vec4 o_color;
layout(push_constant)
uniform present_info_t {
ivec2 src_offset;
ivec2 dst_offset;
};
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset;
o_color = texelFetch(s_image, coord, 0);
o_color = input_to_sc_rgb(texelFetch(s_image_ms, coord, 0));
for (uint i = 1; i < c_samples; i++)
o_color += texelFetch(s_image, coord, int(i));
o_color += input_to_sc_rgb(texelFetch(s_image_ms, coord, int(i)));
o_color /= float(c_samples);
if (s_gamma_bound) {
o_color = vec4(
texture(s_gamma, o_color.r).r,
texture(s_gamma, o_color.g).g,
texture(s_gamma, o_color.b).b,
o_color.a);
}
}
o_color = sc_rgb_to_output(o_color / float(c_samples));
}

View File

@ -1,26 +1,17 @@
#version 450
#extension GL_AMD_shader_fragment_mask: enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_AMD_shader_fragment_mask : enable
layout(constant_id = 0) const uint c_samples = 0;
layout(constant_id = 1) const bool s_gamma_bound = false;
layout(binding = 0) uniform sampler2DMS s_image;
layout(binding = 1) uniform sampler1D s_gamma;
#include "dxvk_present_common.glsl"
layout(location = 0) out vec4 o_color;
layout(push_constant)
uniform present_info_t {
ivec2 src_offset;
ivec2 dst_offset;
};
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset;
// check dxvk_resolve_frag_f_amd.frag for documentation
uint fragMask = fragmentMaskFetchAMD(s_image, coord);
uint fragMask = fragmentMaskFetchAMD(s_image_ms, coord);
uint fragCount = 0u;
for (int i = 0; i < 4 * c_samples; i += 4) {
@ -34,19 +25,11 @@ void main() {
int fragIndex = findLSB(fragCount) >> 2;
int fragShift = fragIndex << 2;
o_color += fragmentFetchAMD(s_image, coord, fragIndex)
o_color += input_to_sc_rgb(fragmentFetchAMD(s_image_ms, coord, fragIndex))
* float(bitfieldExtract(fragCount, fragShift, 4));
fragCount = bitfieldInsert(fragCount, 0, fragShift, 4);
}
o_color /= float(c_samples);
if (s_gamma_bound) {
o_color = vec4(
texture(s_gamma, o_color.r).r,
texture(s_gamma, o_color.g).g,
texture(s_gamma, o_color.b).b,
o_color.a);
}
}
o_color = sc_rgb_to_output(o_color / float(c_samples));
}

View File

@ -0,0 +1,65 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
#include "dxvk_present_common.glsl"
layout(location = 0) in vec2 i_coord;
layout(location = 0) out vec4 o_color;
const vec2 sample_positions[] = {
/* 2 samples */
vec2( 0.25f, 0.25f),
vec2(-0.25f,-0.25f),
/* 4 samples */
vec2(-0.125f,-0.375f),
vec2( 0.375f,-0.125f),
vec2(-0.375f, 0.125f),
vec2( 0.125f, 0.375f),
/* 8 samples */
vec2( 0.0625f,-0.1875f),
vec2(-0.0625f, 0.1875f),
vec2( 0.3125f, 0.0625f),
vec2(-0.1875f,-0.3125f),
vec2(-0.3125f, 0.3125f),
vec2(-0.4375f,-0.0625f),
vec2( 0.1875f, 0.4375f),
vec2( 0.4375f,-0.4375f),
/* 16 samples */
vec2( 0.0625f, 0.0625f),
vec2(-0.0625f,-0.1875f),
vec2(-0.1875f, 0.1250f),
vec2( 0.2500f,-0.0625f),
vec2(-0.3125f,-0.1250f),
vec2( 0.1250f, 0.3125f),
vec2( 0.3125f, 0.1875f),
vec2( 0.1875f,-0.3125f),
vec2(-0.1250f, 0.3750f),
vec2( 0.0000f,-0.4375f),
vec2(-0.2500f,-0.3750f),
vec2(-0.3750f, 0.2500f),
vec2(-0.5000f, 0.0000f),
vec2( 0.4375f,-0.2500f),
vec2( 0.3750f, 0.4375f),
vec2(-0.4375f,-0.5000f),
};
void main() {
vec2 coord = vec2(src_offset) + vec2(src_extent) * i_coord;
ivec2 cint = ivec2(coord);
vec2 cfrac = fract(coord) - 0.5f;
uint pos_index = c_samples - 2u;
o_color = vec4(0.0f);
for (uint i = 0; i < c_samples; i++) {
vec2 sample_pos = sample_positions[pos_index + i];
ivec2 coffset = ivec2(greaterThan(cfrac - sample_pos, vec2(0.5f)));
o_color += input_to_sc_rgb(texelFetch(s_image_ms, cint + coffset, int(i)));
}
o_color = sc_rgb_to_output(o_color / float(c_samples));
}