From 5f0e94138eb075f88556cd2a8e39361709697cee Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 12 Dec 2017 00:27:49 +0100 Subject: [PATCH] [dxvk] Implemented support for multisampled images and render targets --- src/dxgi/dxgi_presenter.cpp | 118 +++++++++++++++++++++++++++++++++++- src/dxgi/dxgi_presenter.h | 32 +++++++--- src/dxgi/dxgi_resource.cpp | 9 +++ src/dxgi/dxgi_resource.h | 5 ++ src/dxgi/dxgi_swapchain.cpp | 93 ++++++++-------------------- src/dxgi/dxgi_swapchain.h | 7 ++- src/dxvk/dxvk_cmdlist.cpp | 14 +++++ src/dxvk/dxvk_cmdlist.h | 8 +++ src/dxvk/dxvk_context.cpp | 75 ++++++++++++++++++++++- src/dxvk/dxvk_context.h | 17 ++++++ src/dxvk/dxvk_framebuffer.h | 8 +++ src/dxvk/dxvk_renderpass.h | 8 +++ 12 files changed, 312 insertions(+), 82 deletions(-) diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index c6d7988a..23125f1d 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -156,10 +156,22 @@ namespace dxvk { } - void DxgiPresenter::presentImage(const Rc& view) { + void DxgiPresenter::presentImage() { m_context->beginRecording( m_device->createCommandList()); + VkImageSubresourceLayers resolveSubresources; + resolveSubresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + resolveSubresources.mipLevel = 0; + resolveSubresources.baseArrayLayer = 0; + resolveSubresources.layerCount = 1; + + if (m_backBufferResolve != nullptr) { + m_context->resolveImage( + m_backBufferResolve, resolveSubresources, + m_backBuffer, resolveSubresources); + } + auto framebuffer = m_swapchain->getFramebuffer(m_acquireSync); auto framebufferSize = framebuffer->size(); @@ -186,7 +198,7 @@ namespace dxvk { BindingIds::Sampler, m_sampler); m_context->bindResourceImage( VK_PIPELINE_BIND_POINT_GRAPHICS, - BindingIds::Texture, view); + BindingIds::Texture, m_backBufferView); m_context->draw(4, 1, 0, 0); m_device->submitCommandList( @@ -201,6 +213,108 @@ namespace dxvk { } + Rc DxgiPresenter::createBackBuffer( + uint32_t bufferWidth, + uint32_t bufferHeight, + VkFormat bufferFormat, + VkSampleCountFlagBits sampleCount) { + Logger::info(str::format("DxgiPresenter: Creating back buffer with ", bufferFormat)); + + // Explicitly destroy the old stuff + m_backBuffer = nullptr; + m_backBufferResolve = nullptr; + m_backBufferView = nullptr; + + // Create an image that can be rendered to + // and that can be used as a sampled texture. + DxvkImageCreateInfo imageInfo; + imageInfo.type = VK_IMAGE_TYPE_2D; + imageInfo.format = bufferFormat; + imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + imageInfo.sampleCount = sampleCount; + imageInfo.extent.width = bufferWidth; + imageInfo.extent.height = bufferHeight; + imageInfo.extent.depth = 1; + imageInfo.numLayers = 1; + imageInfo.mipLevels = 1; + imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT + | VK_PIPELINE_STAGE_TRANSFER_BIT; + imageInfo.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT + | VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_SHADER_READ_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + if (m_device->features().geometryShader) + imageInfo.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + + if (m_device->features().tessellationShader) { + imageInfo.stages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT + | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; + } + + m_backBuffer = m_device->createImage(imageInfo, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + // If a multisampled back buffer was requested, we also need to + // create a resolve image with otherwise identical properties. + // Multisample images cannot be sampled from. + if (sampleCount != VK_SAMPLE_COUNT_1_BIT) { + DxvkImageCreateInfo resolveInfo; + resolveInfo.type = VK_IMAGE_TYPE_2D; + resolveInfo.format = bufferFormat; + resolveInfo.flags = 0; + resolveInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; + resolveInfo.extent.width = bufferWidth; + resolveInfo.extent.height = bufferHeight; + resolveInfo.extent.depth = 1; + resolveInfo.numLayers = 1; + resolveInfo.mipLevels = 1; + resolveInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + resolveInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT + | VK_PIPELINE_STAGE_TRANSFER_BIT; + resolveInfo.access = VK_ACCESS_SHADER_READ_BIT + | VK_ACCESS_TRANSFER_WRITE_BIT; + resolveInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + resolveInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + m_backBufferResolve = m_device->createImage( + resolveInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + + // Create an image view that allows the + // image to be bound as a shader resource. + DxvkImageViewCreateInfo viewInfo; + viewInfo.type = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = imageInfo.format; + viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.minLevel = 0; + viewInfo.numLevels = 1; + viewInfo.minLayer = 0; + viewInfo.numLayers = 1; + + m_backBufferView = m_device->createImageView( + m_backBufferResolve != nullptr + ? m_backBufferResolve + : m_backBuffer, + viewInfo); + + // TODO move this elsewhere + this->initBackBuffer(m_backBuffer); + return m_backBuffer; + } + + void DxgiPresenter::recreateSwapchain( uint32_t bufferWidth, uint32_t bufferHeight, diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index d246806c..7389d8b0 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -29,7 +29,7 @@ namespace dxvk { DXGI_FORMAT bufferFormat); ~DxgiPresenter(); - + /** * \brief Initializes back buffer image * \param [in] image Back buffer image @@ -38,20 +38,32 @@ namespace dxvk { const Rc& image); /** - * \brief Renders image to the screen - * \param [in] view Source image view + * \brief Renders back buffer to the screen */ - void presentImage( - const Rc& view); + void presentImage(); + + /** + * \brief Recreates back buffer + * + * \param [in] bufferWidth Buffer width + * \param [in] bufferHeight Buffer height + * \param [in] bufferFormat Buffer format + * \returns Back buffer image + */ + Rc createBackBuffer( + uint32_t bufferWidth, + uint32_t bufferHeight, + VkFormat bufferFormat, + VkSampleCountFlagBits sampleCount); /** * \brief Renders image to the screen * \param [in] view Source image view */ void recreateSwapchain( - uint32_t bufferWidth, - uint32_t bufferHeight, - DXGI_FORMAT bufferFormat); + uint32_t bufferWidth, + uint32_t bufferHeight, + DXGI_FORMAT bufferFormat); private: @@ -71,6 +83,10 @@ namespace dxvk { Rc m_sampler; + Rc m_backBuffer; + Rc m_backBufferResolve; + Rc m_backBufferView; + VkSurfaceFormatKHR pickFormat(DXGI_FORMAT fmt) const; Rc createVertexShader(); diff --git a/src/dxgi/dxgi_resource.cpp b/src/dxgi/dxgi_resource.cpp index 0f475443..6525de70 100644 --- a/src/dxgi/dxgi_resource.cpp +++ b/src/dxgi/dxgi_resource.cpp @@ -2,6 +2,15 @@ namespace dxvk { + DxgiImageResource::DxgiImageResource( + IDXGIDevicePrivate* pDevice, + const Rc& image, + UINT usageFlags) + : Base(pDevice, usageFlags), m_image(image) { + + } + + DxgiImageResource::DxgiImageResource( IDXGIDevicePrivate* pDevice, const dxvk::DxvkImageCreateInfo* pCreateInfo, diff --git a/src/dxgi/dxgi_resource.h b/src/dxgi/dxgi_resource.h index 9aefbcb3..e67f5721 100644 --- a/src/dxgi/dxgi_resource.h +++ b/src/dxgi/dxgi_resource.h @@ -68,6 +68,11 @@ namespace dxvk { using Base = DxgiResource; public: + DxgiImageResource( + IDXGIDevicePrivate* pDevice, + const Rc& image, + UINT usageFlags); + DxgiImageResource( IDXGIDevicePrivate* pDevice, const dxvk::DxvkImageCreateInfo* pCreateInfo, diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 7a807aa2..252dd868 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -59,10 +59,12 @@ namespace dxvk { this->createPresenter(); this->createBackBuffer(); + TRACE(this); } DxgiSwapChain::~DxgiSwapChain() { + TRACE(this); // We do not release the SDL window handle here since // that would destroy the underlying window as well. } @@ -177,7 +179,7 @@ namespace dxvk { // TODO implement sync interval // TODO implement flags - m_presenter->presentImage(m_backBufferView); + m_presenter->presentImage(); return S_OK; } catch (const DxvkError& err) { Logger::err(err.message()); @@ -322,80 +324,24 @@ namespace dxvk { void DxgiSwapChain::createBackBuffer() { - // Pick the back buffer format based on the requested swap chain format - DxgiFormatPair bufferFormat = m_adapter->LookupFormat(m_desc.BufferDesc.Format); - Logger::info(str::format("DxgiSwapChain: Creating back buffer with ", bufferFormat.actual)); + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; - // TODO support proper multi-sampling - const Rc dxvkDevice = m_device->GetDXVKDevice(); + if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount))) + throw DxvkError("DxgiSwapChain::createBackBuffer: Invalid sample count"); - // Create an image that can be rendered to - // and that can be used as a sampled texture. - Com resource; + const Rc backBuffer = m_presenter->createBackBuffer( + m_desc.BufferDesc.Width, m_desc.BufferDesc.Height, + m_adapter->LookupFormat(m_desc.BufferDesc.Format).actual, + sampleCount); - DxvkImageCreateInfo imageInfo; - imageInfo.type = VK_IMAGE_TYPE_2D; - imageInfo.format = bufferFormat.actual; - imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; - imageInfo.extent.width = m_desc.BufferDesc.Width; - imageInfo.extent.height = m_desc.BufferDesc.Height; - imageInfo.extent.depth = 1; - imageInfo.numLayers = 1; - imageInfo.mipLevels = 1; - imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT - | VK_IMAGE_USAGE_SAMPLED_BIT - | VK_IMAGE_USAGE_TRANSFER_DST_BIT - | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - imageInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT - | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT - | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT - | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT - | VK_PIPELINE_STAGE_TRANSFER_BIT; - imageInfo.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT - | VK_ACCESS_TRANSFER_WRITE_BIT - | VK_ACCESS_TRANSFER_READ_BIT - | VK_ACCESS_SHADER_READ_BIT; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - if (dxvkDevice->features().geometryShader) - imageInfo.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; - - if (dxvkDevice->features().tessellationShader) { - imageInfo.stages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT - | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; - } - - if (FAILED(DXGICreateImageResourcePrivate(m_device.ptr(), &imageInfo, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, DXGI_USAGE_BACK_BUFFER | m_desc.BufferUsage, - &resource))) - throw DxvkError("DxgiSwapChain::createBackBuffer: Failed to create back buffer"); - - m_backBuffer = resource->GetDXVKImage(); - - // Create an image view that allows the - // image to be bound as a shader resource. - DxvkImageViewCreateInfo viewInfo; - viewInfo.type = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageInfo.format; - viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.minLevel = 0; - viewInfo.numLevels = 1; - viewInfo.minLayer = 0; - viewInfo.numLayers = 1; - - m_backBufferView = dxvkDevice->createImageView(m_backBuffer, viewInfo); + const Com resource + = new DxgiImageResource(m_device.ptr(), backBuffer, + DXGI_USAGE_BACK_BUFFER | m_desc.BufferUsage); // Wrap the back buffer image into an interface // that the device can use to access the image. if (FAILED(m_presentDevice->WrapSwapChainBackBuffer(resource.ptr(), &m_desc, &m_backBufferIface))) throw DxvkError("DxgiSwapChain::createBackBuffer: Failed to create back buffer interface"); - - // Initialize the image properly so that - // it can be used in a DXVK context - m_presenter->initBackBuffer(m_backBuffer); } @@ -411,4 +357,17 @@ namespace dxvk { return result; } + + HRESULT DxgiSwapChain::GetSampleCount(UINT Count, VkSampleCountFlagBits* pCount) const { + switch (Count) { + case 1: *pCount = VK_SAMPLE_COUNT_1_BIT; return S_OK; + case 2: *pCount = VK_SAMPLE_COUNT_2_BIT; return S_OK; + case 4: *pCount = VK_SAMPLE_COUNT_4_BIT; return S_OK; + case 8: *pCount = VK_SAMPLE_COUNT_8_BIT; return S_OK; + case 16: *pCount = VK_SAMPLE_COUNT_16_BIT; return S_OK; + } + + return E_INVALIDARG; + } + } diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index ee58d78d..7a642cd1 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -94,9 +94,6 @@ namespace dxvk { SDL_Window* m_window = nullptr; Rc m_presenter; - - Rc m_backBuffer; - Rc m_backBufferView; Com m_backBufferIface; void createPresenter(); @@ -105,6 +102,10 @@ namespace dxvk { void createContext(); VkExtent2D getWindowSize() const; + + HRESULT GetSampleCount( + UINT Count, + VkSampleCountFlagBits* pCount) const; }; diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 057fdf2d..cb9b8383 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -274,6 +274,20 @@ namespace dxvk { } + void DxvkCommandList::cmdResolveImage( + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions) { + m_vkd->vkCmdResolveImage(m_buffer, + srcImage, srcImageLayout, + dstImage, dstImageLayout, + regionCount, pRegions); + } + + void DxvkCommandList::cmdUpdateBuffer( VkBuffer dstBuffer, VkDeviceSize dstOffset, diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index 1a07af80..7a99930c 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -163,6 +163,14 @@ namespace dxvk { uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); + void cmdResolveImage( + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions); + void cmdUpdateBuffer( VkBuffer dstBuffer, VkDeviceSize dstOffset, diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index da00f94f..7202c434 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -129,7 +129,7 @@ namespace dxvk { if (image != nullptr) { descriptor.image.imageView = image->handle(); - descriptor.image.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + descriptor.image.imageLayout = image->imageInfo().layout; } rc->bindShaderResource(slot, resource, descriptor); @@ -368,6 +368,77 @@ namespace dxvk { } + void DxvkContext::resolveImage( + const Rc& dstImage, + const VkImageSubresourceLayers& dstSubresources, + const Rc& srcImage, + const VkImageSubresourceLayers& srcSubresources) { + VkImageSubresourceRange dstSubresourceRange = { + dstSubresources.aspectMask, + dstSubresources.mipLevel, 1, + dstSubresources.baseArrayLayer, + dstSubresources.layerCount, + }; + + VkImageSubresourceRange srcSubresourceRange = { + srcSubresources.aspectMask, + srcSubresources.mipLevel, 1, + srcSubresources.baseArrayLayer, + srcSubresources.layerCount, + }; + + // We only support resolving to the entire image + // area, so we might as well discard its contents + m_barriers.accessImage( + dstImage, dstSubresourceRange, + VK_IMAGE_LAYOUT_UNDEFINED, 0, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + m_barriers.accessImage( + srcImage, srcSubresourceRange, + srcImage->info().layout, + srcImage->info().stages, + srcImage->info().access, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT); + m_barriers.recordCommands(m_cmd); + + VkImageResolve imageRegion; + imageRegion.srcSubresource = srcSubresources; + imageRegion.srcOffset = VkOffset3D { 0, 0, 0 }; + imageRegion.dstSubresource = dstSubresources; + imageRegion.dstOffset = VkOffset3D { 0, 0, 0 }; + imageRegion.extent = srcImage->mipLevelExtent(srcSubresources.mipLevel); + + m_cmd->cmdResolveImage( + srcImage->handle(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage->handle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &imageRegion); + + m_barriers.accessImage( + dstImage, dstSubresourceRange, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + dstImage->info().layout, + dstImage->info().stages, + dstImage->info().access); + m_barriers.accessImage( + srcImage, srcSubresourceRange, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + srcImage->info().layout, + srcImage->info().stages, + srcImage->info().access); + m_barriers.recordCommands(m_cmd); + } + + void DxvkContext::updateBuffer( const Rc& buffer, VkDeviceSize offset, @@ -718,7 +789,7 @@ namespace dxvk { gpState.rsViewportCount = m_state.vp.viewportCount; // TODO implement multisampling support properly - gpState.msSampleCount = VK_SAMPLE_COUNT_1_BIT; + gpState.msSampleCount = m_state.om.framebuffer->sampleCount(); gpState.msSampleMask = m_state.ms.sampleMask; gpState.msEnableAlphaToCoverage = m_state.ms.enableAlphaToCoverage; gpState.msEnableAlphaToOne = m_state.ms.enableAlphaToOne; diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 9211b47a..22fe1dd8 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -247,6 +247,23 @@ namespace dxvk { const Rc& image, const VkImageSubresourceRange& subresources); + /** + * \brief Resolves a multisampled image resource + * + * Resolves a multisampled image into a non-multisampled + * image. The subresources of both images must have the + * same size and compatible formats + * \param [in] dstImage Destination image + * \param [in] dstSubresources Subresources to write to + * \param [in] srcImage Source image + * \param [in] srcSubresources Subresources to read from + */ + void resolveImage( + const Rc& dstImage, + const VkImageSubresourceLayers& dstSubresources, + const Rc& srcImage, + const VkImageSubresourceLayers& srcSubresources); + /** * \brief Updates a buffer * diff --git a/src/dxvk/dxvk_framebuffer.h b/src/dxvk/dxvk_framebuffer.h index 5851bf47..b4fa9d0d 100644 --- a/src/dxvk/dxvk_framebuffer.h +++ b/src/dxvk/dxvk_framebuffer.h @@ -155,6 +155,14 @@ namespace dxvk { return m_renderTargets; } + /** + * \brief Sample count + * \returns Sample count + */ + VkSampleCountFlagBits sampleCount() const { + return m_renderPass->sampleCount(); + } + private: Rc m_vkd; diff --git a/src/dxvk/dxvk_renderpass.h b/src/dxvk/dxvk_renderpass.h index e2297578..80b76abd 100644 --- a/src/dxvk/dxvk_renderpass.h +++ b/src/dxvk/dxvk_renderpass.h @@ -123,6 +123,14 @@ namespace dxvk { return m_renderPass; } + /** + * \brief Render pass sample count + * \returns Render pass sample count + */ + VkSampleCountFlagBits sampleCount() const { + return m_format.getSampleCount(); + } + private: Rc m_vkd;