[dxvk] Implemented support for multisampled images and render targets

This commit is contained in:
Philip Rebohle 2017-12-12 00:27:49 +01:00
parent de47fa29e1
commit 5f0e94138e
12 changed files with 312 additions and 82 deletions

View File

@ -156,10 +156,22 @@ namespace dxvk {
}
void DxgiPresenter::presentImage(const Rc<DxvkImageView>& 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<DxvkImage> 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,

View File

@ -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<DxvkImage>& image);
/**
* \brief Renders image to the screen
* \param [in] view Source image view
* \brief Renders back buffer to the screen
*/
void presentImage(
const Rc<DxvkImageView>& 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<DxvkImage> 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<DxvkSampler> m_sampler;
Rc<DxvkImage> m_backBuffer;
Rc<DxvkImage> m_backBufferResolve;
Rc<DxvkImageView> m_backBufferView;
VkSurfaceFormatKHR pickFormat(DXGI_FORMAT fmt) const;
Rc<DxvkShader> createVertexShader();

View File

@ -2,6 +2,15 @@
namespace dxvk {
DxgiImageResource::DxgiImageResource(
IDXGIDevicePrivate* pDevice,
const Rc<DxvkImage>& image,
UINT usageFlags)
: Base(pDevice, usageFlags), m_image(image) {
}
DxgiImageResource::DxgiImageResource(
IDXGIDevicePrivate* pDevice,
const dxvk::DxvkImageCreateInfo* pCreateInfo,

View File

@ -68,6 +68,11 @@ namespace dxvk {
using Base = DxgiResource<IDXGIImageResourcePrivate>;
public:
DxgiImageResource(
IDXGIDevicePrivate* pDevice,
const Rc<DxvkImage>& image,
UINT usageFlags);
DxgiImageResource(
IDXGIDevicePrivate* pDevice,
const dxvk::DxvkImageCreateInfo* pCreateInfo,

View File

@ -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> 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<IDXGIImageResourcePrivate> resource;
const Rc<DxvkImage> 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<IDXGIImageResourcePrivate> 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;
}
}

View File

@ -94,9 +94,6 @@ namespace dxvk {
SDL_Window* m_window = nullptr;
Rc<DxgiPresenter> m_presenter;
Rc<DxvkImage> m_backBuffer;
Rc<DxvkImageView> m_backBufferView;
Com<IUnknown> m_backBufferIface;
void createPresenter();
@ -105,6 +102,10 @@ namespace dxvk {
void createContext();
VkExtent2D getWindowSize() const;
HRESULT GetSampleCount(
UINT Count,
VkSampleCountFlagBits* pCount) const;
};

View File

@ -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,

View File

@ -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,

View File

@ -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<DxvkImage>& dstImage,
const VkImageSubresourceLayers& dstSubresources,
const Rc<DxvkImage>& 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<DxvkBuffer>& 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;

View File

@ -247,6 +247,23 @@ namespace dxvk {
const Rc<DxvkImage>& 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<DxvkImage>& dstImage,
const VkImageSubresourceLayers& dstSubresources,
const Rc<DxvkImage>& srcImage,
const VkImageSubresourceLayers& srcSubresources);
/**
* \brief Updates a buffer
*

View File

@ -155,6 +155,14 @@ namespace dxvk {
return m_renderTargets;
}
/**
* \brief Sample count
* \returns Sample count
*/
VkSampleCountFlagBits sampleCount() const {
return m_renderPass->sampleCount();
}
private:
Rc<vk::DeviceFn> m_vkd;

View File

@ -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<vk::DeviceFn> m_vkd;