diff --git a/src/dxvk/dxvk_swapchain_blitter.cpp b/src/dxvk/dxvk_swapchain_blitter.cpp new file mode 100644 index 00000000..baab090e --- /dev/null +++ b/src/dxvk/dxvk_swapchain_blitter.cpp @@ -0,0 +1,372 @@ +#include "dxvk_swapchain_blitter.h" + +#include +#include +#include +#include +#include + +namespace dxvk { + + DxvkSwapchainBlitter::DxvkSwapchainBlitter(const Rc& device) + : m_device(device) { + this->createSampler(); + this->createShaders(); + } + + + DxvkSwapchainBlitter::~DxvkSwapchainBlitter() { + + } + + + void DxvkSwapchainBlitter::presentImage( + DxvkContext* ctx, + const Rc& dstView, + VkRect2D dstRect, + const Rc& srcView, + VkRect2D srcRect) { + if (m_gammaDirty) + this->updateGammaTexture(ctx); + + // Fix up default present areas if necessary + if (!dstRect.extent.width || !dstRect.extent.height) { + dstRect.offset = { 0, 0 }; + dstRect.extent = { + dstView->imageInfo().extent.width, + dstView->imageInfo().extent.height }; + } + + if (!srcRect.extent.width || !srcRect.extent.height) { + srcRect.offset = { 0, 0 }; + srcRect.extent = { + srcView->imageInfo().extent.width, + srcView->imageInfo().extent.height }; + } + + bool sameSize = dstRect.extent == srcRect.extent; + bool usedResolveImage = false; + + if (srcView->imageInfo().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->imageInfo().extent + || m_resolveImage->info().format != srcView->imageInfo().format) + this->createResolveImage(srcView->imageInfo()); + + this->resolve(ctx, m_resolveView, srcView); + this->draw(ctx, m_fsBlit, dstView, dstRect, m_resolveView, srcRect); + + usedResolveImage = true; + } + + if (!usedResolveImage) + this->destroyResolveImage(); + } + + + void DxvkSwapchainBlitter::setGammaRamp( + uint32_t cpCount, + const DxvkGammaCp* cpData) { + m_gammaRamp.resize(cpCount); + + for (uint32_t i = 0; i < cpCount; i++) + m_gammaRamp[i] = cpData[i]; + + 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.sampleCount = VK_SAMPLE_COUNT_1_BIT; + 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(renderTargets); + + VkExtent2D dstExtent = { + dstView->imageInfo().extent.width, + dstView->imageInfo().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(BindingIds::Image, m_samplerPresent); + ctx->bindResourceSampler(BindingIds::Gamma, m_samplerGamma); + + ctx->bindResourceView(BindingIds::Image, srcView, nullptr); + ctx->bindResourceView(BindingIds::Gamma, m_gammaView, nullptr); + + ctx->bindShader(VK_SHADER_STAGE_VERTEX_BIT, m_vs); + ctx->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, 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->imageInfo().sampleCount); + ctx->draw(3, 1, 0, 0); + ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 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->imageInfo().extent; + ctx->resolveImage(dstView->image(), srcView->image(), resolve, VK_FORMAT_UNDEFINED); + } + + + void DxvkSwapchainBlitter::updateGammaTexture(DxvkContext* ctx) { + uint32_t n = uint32_t(m_gammaRamp.size()); + + 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); + + DxvkImageViewCreateInfo viewInfo; + viewInfo.type = VK_IMAGE_VIEW_TYPE_1D; + viewInfo.format = VK_FORMAT_R16G16B16A16_UNORM; + viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.minLevel = 0; + viewInfo.numLevels = 1; + viewInfo.minLayer = 0; + viewInfo.numLayers = 1; + + m_gammaView = m_device->createImageView(m_gammaImage, viewInfo); + } + + ctx->updateImage(m_gammaImage, + VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, + VkOffset3D { 0, 0, 0 }, + VkExtent3D { n, 1, 1 }, + m_gammaRamp.data(), + sizeof(DxvkGammaCp) * n, + sizeof(DxvkGammaCp) * n); + } else { + m_gammaImage = nullptr; + m_gammaView = nullptr; + } + + m_gammaDirty = false; + } + + + void DxvkSwapchainBlitter::createSampler() { + DxvkSamplerCreateInfo samplerInfo; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + samplerInfo.mipmapLodBias = 0.0f; + samplerInfo.mipmapLodMin = 0.0f; + samplerInfo.mipmapLodMax = 0.0f; + samplerInfo.useAnisotropy = VK_FALSE; + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.compareToDepth = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.borderColor = VkClearColorValue(); + samplerInfo.usePixelCoord = VK_TRUE; + m_samplerPresent = m_device->createSampler(samplerInfo); + + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.usePixelCoord = VK_FALSE; + m_samplerGamma = m_device->createSampler(samplerInfo); + } + + void DxvkSwapchainBlitter::createShaders() { + const SpirvCodeBuffer vsCode(dxvk_present_vert); + const SpirvCodeBuffer fsCodeBlit(dxvk_present_frag_blit); + const SpirvCodeBuffer fsCodeCopy(dxvk_present_frag); + const SpirvCodeBuffer fsCodeResolve(dxvk_present_frag_ms); + const SpirvCodeBuffer fsCodeResolveAmd(dxvk_present_frag_ms_amd); + + const std::array fsResourceSlots = {{ + { BindingIds::Image, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_2D }, + { BindingIds::Gamma, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_1D }, + }}; + + m_vs = m_device->createShader( + VK_SHADER_STAGE_VERTEX_BIT, + 0, nullptr, { 0u, 1u }, + vsCode); + + m_fsBlit = m_device->createShader( + VK_SHADER_STAGE_FRAGMENT_BIT, + fsResourceSlots.size(), + fsResourceSlots.data(), + { 1u, 1u, 0u, sizeof(PresenterArgs) }, + fsCodeBlit); + + m_fsCopy = m_device->createShader( + VK_SHADER_STAGE_FRAGMENT_BIT, + fsResourceSlots.size(), + fsResourceSlots.data(), + { 0u, 1u, 0u, sizeof(PresenterArgs) }, + fsCodeCopy); + + m_fsResolve = m_device->createShader( + VK_SHADER_STAGE_FRAGMENT_BIT, + fsResourceSlots.size(), + fsResourceSlots.data(), + { 0u, 1u, 0u, sizeof(PresenterArgs) }, + m_device->extensions().amdShaderFragmentMask + ? fsCodeResolveAmd : fsCodeResolve); + } + + 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); + + DxvkImageViewCreateInfo viewInfo; + viewInfo.type = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = info.format; + viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.minLevel = 0; + viewInfo.numLevels = 1; + viewInfo.minLayer = 0; + viewInfo.numLayers = 1; + m_resolveView = m_device->createImageView(m_resolveImage, viewInfo); + } + + + void DxvkSwapchainBlitter::destroyResolveImage() { + m_resolveImage = nullptr; + m_resolveView = nullptr; + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_swapchain_blitter.h b/src/dxvk/dxvk_swapchain_blitter.h new file mode 100644 index 00000000..b6db5f81 --- /dev/null +++ b/src/dxvk/dxvk_swapchain_blitter.h @@ -0,0 +1,116 @@ +#pragma once + +#include "../dxvk/dxvk_device.h" +#include "../dxvk/dxvk_context.h" + +namespace dxvk { + + /** + * \brief Gamma control point + */ + struct DxvkGammaCp { + uint16_t r, g, b, a; + }; + + /** + * \brief Swap chain blitter + * + * Provides common rendering code for blitting + * rendered images to a swap chain image. + */ + class DxvkSwapchainBlitter : public RcObject { + + public: + + DxvkSwapchainBlitter(const Rc& device); + ~DxvkSwapchainBlitter(); + + /** + * \brief Records presentation commands + * + * \param [in] ctx Context + * \param [in] dstView Swap chain image view + * \param [in] srcView Image to present + * \param [in] dstRect Destination rectangle + * \param [in] srcRect Back buffer rectangle + */ + void presentImage( + DxvkContext* ctx, + const Rc& dstView, + VkRect2D dstRect, + const Rc& srcView, + VkRect2D srcRect); + + /** + * \brief Sets gamma ramp + * + * If the number of control points is non-zero, this + * will create a texture containing a gamma ramp that + * will be used for presentation. + * \param [in] cpCount Number of control points + * \param [in] cpData Control point data + */ + void setGammaRamp( + uint32_t cpCount, + const DxvkGammaCp* cpData); + + private: + + enum BindingIds : uint32_t { + Image = 0, + Gamma = 1, + }; + + struct PresenterArgs { + VkOffset2D srcOffset; + union { + VkExtent2D srcExtent; + VkOffset2D dstOffset; + }; + }; + + Rc m_device; + + Rc m_fsCopy; + Rc m_fsBlit; + Rc m_fsResolve; + Rc m_vs; + + Rc m_gammaImage; + Rc m_gammaView; + bool m_gammaDirty = false; + std::vector m_gammaRamp; + + 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); + + void resolve( + DxvkContext* ctx, + const Rc& dstView, + const Rc& srcView); + + void updateGammaTexture(DxvkContext* ctx); + + void createSampler(); + + void createShaders(); + + void createResolveImage( + const DxvkImageCreateInfo& info); + + void destroyResolveImage(); + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index d1785d75..d4c24ce2 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -33,6 +33,12 @@ dxvk_shaders = files([ 'shaders/dxvk_pack_d24s8.comp', 'shaders/dxvk_pack_d32s8.comp', + 'shaders/dxvk_present_frag.frag', + 'shaders/dxvk_present_frag_blit.frag', + 'shaders/dxvk_present_frag_ms.frag', + 'shaders/dxvk_present_frag_ms_amd.frag', + 'shaders/dxvk_present_vert.vert', + 'shaders/dxvk_resolve_frag_d.frag', 'shaders/dxvk_resolve_frag_ds.frag', 'shaders/dxvk_resolve_frag_f.frag', @@ -97,6 +103,7 @@ dxvk_src = files([ 'dxvk_staging.cpp', 'dxvk_state_cache.cpp', 'dxvk_stats.cpp', + 'dxvk_swapchain_blitter.cpp', 'dxvk_unbound.cpp', 'dxvk_util.cpp', diff --git a/src/dxvk/shaders/dxvk_present_frag.frag b/src/dxvk/shaders/dxvk_present_frag.frag new file mode 100644 index 00000000..7498a407 --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_frag.frag @@ -0,0 +1,27 @@ +#version 450 + +layout(constant_id = 1) const bool s_gamma_bound = true; + +layout(binding = 0) uniform sampler2D s_image; +layout(binding = 1) uniform sampler1D s_gamma; + +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 diff --git a/src/dxvk/shaders/dxvk_present_frag_blit.frag b/src/dxvk/shaders/dxvk_present_frag_blit.frag new file mode 100644 index 00000000..c5b66069 --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_frag_blit.frag @@ -0,0 +1,28 @@ +#version 450 + +layout(constant_id = 1) const bool s_gamma_bound = true; + +layout(binding = 0) uniform sampler2D s_image; +layout(binding = 1) uniform sampler1D s_gamma; + +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 diff --git a/src/dxvk/shaders/dxvk_present_frag_ms.frag b/src/dxvk/shaders/dxvk_present_frag_ms.frag new file mode 100644 index 00000000..9dd751ec --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_frag_ms.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(constant_id = 1) const bool s_gamma_bound = true; +layout(constant_id = 1225) const uint c_samples = 0; + +layout(binding = 0) uniform sampler2DMS s_image; +layout(binding = 1) uniform sampler1D s_gamma; + +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); + + for (uint i = 1; i < c_samples; i++) + o_color += texelFetch(s_image, 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 diff --git a/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag new file mode 100644 index 00000000..c234591f --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag @@ -0,0 +1,52 @@ +#version 450 + +#extension GL_AMD_shader_fragment_mask: enable + +layout(constant_id = 1) const bool s_gamma_bound = true; +layout(constant_id = 1225) const uint c_samples = 0; + +layout(binding = 0) uniform sampler2DMS s_image; +layout(binding = 1) uniform sampler1D s_gamma; + +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 fragCount = 0u; + + for (int i = 0; i < 4 * c_samples; i += 4) { + uint fragIndex = bitfieldExtract(fragMask, i, 4); + fragCount += 1u << (fragIndex << 2); + } + + o_color = vec4(0.0f); + + while (fragCount != 0) { + int fragIndex = findLSB(fragCount) >> 2; + int fragShift = fragIndex << 2; + + o_color += fragmentFetchAMD(s_image, 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 diff --git a/src/dxvk/shaders/dxvk_present_vert.vert b/src/dxvk/shaders/dxvk_present_vert.vert new file mode 100644 index 00000000..753a16f8 --- /dev/null +++ b/src/dxvk/shaders/dxvk_present_vert.vert @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec2 o_coord; + +void main() { + vec2 coord = vec2( + float(gl_VertexIndex & 2), + float(gl_VertexIndex & 1) * 2.0f); + + o_coord = coord; + gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f); +}