diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 8bacfb84..639e166b 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -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); } diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 9e2c7d3a..ed55d24d 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -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); } diff --git a/src/dxvk/dxvk_swapchain_blitter.cpp b/src/dxvk/dxvk_swapchain_blitter.cpp index 4d57e47e..8eff9598 100644 --- a/src/dxvk/dxvk_swapchain_blitter.cpp +++ b/src/dxvk/dxvk_swapchain_blitter.cpp @@ -4,30 +4,46 @@ #include #include #include +#include #include namespace dxvk { DxvkSwapchainBlitter::DxvkSwapchainBlitter(const Rc& 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& dstView, + VkColorSpaceKHR dstColorSpace, VkRect2D dstRect, const Rc& 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 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(srcView->image()); + ctx.cmd->trackResource(dstView->image()); + + if (m_gammaImage) + ctx.cmd->trackResource(m_gammaImage->getAllocation()); + + ctx.cmd->trackSampler(m_samplerGamma); + ctx.cmd->trackSampler(m_samplerPresent); + } + + + void DxvkSwapchainBlitter::endPresent( + const DxvkContextObjects& ctx, + const Rc& 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& fs, - const Rc& dstView, - VkRect2D dstRect, - const Rc& 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(m_samplerPresent)); - ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Gamma, Rc(m_samplerGamma)); - - ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Image, Rc(srcView)); - ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, BindingIds::Gamma, Rc(m_gammaView)); - - ctx->bindShader(Rc(m_vs)); - ctx->bindShader(Rc(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& dstView, - const Rc& 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 = ©Region; + + ctx.cmd->cmdCopyBufferToImage(DxvkCmdBuffer::ExecBuffer, ©); + + 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(m_gammaBuffer->getAllocation()); + ctx.cmd->trackResource(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 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 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 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 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 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; } } \ No newline at end of file diff --git a/src/dxvk/dxvk_swapchain_blitter.h b/src/dxvk/dxvk_swapchain_blitter.h index 690ff205..5b7d8f7d 100644 --- a/src/dxvk/dxvk_swapchain_blitter.h +++ b/src/dxvk/dxvk_swapchain_blitter.h @@ -1,5 +1,11 @@ #pragma once +#include +#include +#include + +#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& dstView, + VkColorSpaceKHR dstColorSpace, VkRect2D dstRect, const Rc& 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& 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 m_device; - Rc m_fsCopy; - Rc m_fsBlit; - Rc m_fsResolve; - Rc m_vs; + ShaderModule m_shaderVsBlit; + ShaderModule m_shaderFsCopy; + ShaderModule m_shaderFsBlit; + ShaderModule m_shaderFsMsResolve; + ShaderModule m_shaderFsMsBlit; + dxvk::mutex m_mutex; Rc m_gammaBuffer; Rc m_gammaImage; Rc m_gammaView; uint32_t m_gammaCpCount = 0; - bool m_gammaDirty = false; - Rc m_gammaSlice = { }; - - Rc m_resolveImage; - Rc m_resolveView; Rc m_samplerPresent; Rc m_samplerGamma; - void draw( - DxvkContext* ctx, - const Rc& fs, - const Rc& dstView, - VkRect2D dstRect, - const Rc& srcView, - VkRect2D srcRect); + VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; - void resolve( - DxvkContext* ctx, - const Rc& dstView, - const Rc& srcView); + std::unordered_map 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); }; diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 917320f3..ee5fac5e 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -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', diff --git a/src/dxvk/shaders/dxvk_color_space.glsl b/src/dxvk/shaders/dxvk_color_space.glsl new file mode 100644 index 00000000..bc40cdcd --- /dev/null +++ b/src/dxvk/shaders/dxvk_color_space.glsl @@ -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; +} diff --git a/src/dxvk/shaders/dxvk_present_common.glsl b/src/dxvk/shaders/dxvk_present_common.glsl new file mode 100644 index 00000000..df07724d --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_common.glsl @@ -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; + } +} diff --git a/src/dxvk/shaders/dxvk_present_frag.frag b/src/dxvk/shaders/dxvk_present_frag.frag index 3501a4f2..94fce7b2 100644 --- a/src/dxvk/shaders/dxvk_present_frag.frag +++ b/src/dxvk/shaders/dxvk_present_frag.frag @@ -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); - } -} \ No newline at end of file + + o_color = input_to_sc_rgb(texelFetch(s_image, coord, 0)); + o_color = sc_rgb_to_output(o_color); +} diff --git a/src/dxvk/shaders/dxvk_present_frag_blit.frag b/src/dxvk/shaders/dxvk_present_frag_blit.frag index e14ae311..4c9e2dd4 100644 --- a/src/dxvk/shaders/dxvk_present_frag_blit.frag +++ b/src/dxvk/shaders/dxvk_present_frag_blit.frag @@ -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); - } -} \ No newline at end of file + o_color = input_to_sc_rgb(textureLod(s_image, coord, 0.0f)); + o_color = sc_rgb_to_output(o_color); +} diff --git a/src/dxvk/shaders/dxvk_present_frag_ms.frag b/src/dxvk/shaders/dxvk_present_frag_ms.frag index 18771d38..8f109c4b 100644 --- a/src/dxvk/shaders/dxvk_present_frag_ms.frag +++ b/src/dxvk/shaders/dxvk_present_frag_ms.frag @@ -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); - } -} \ No newline at end of file + o_color = sc_rgb_to_output(o_color / float(c_samples)); +} diff --git a/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag index df391fb1..6f332fac 100644 --- a/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag +++ b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag @@ -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); - } -} \ No newline at end of file + o_color = sc_rgb_to_output(o_color / float(c_samples)); +} diff --git a/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag b/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag new file mode 100644 index 00000000..710c4fc1 --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag @@ -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)); +}