diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index b9f64020..ab5cb8bb 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -208,7 +208,7 @@ namespace dxvk { if (buffer->mapPtr(0) == nullptr) { Logger::err("D3D11: Cannot map a device-local buffer"); - return E_FAIL; + return E_INVALIDARG; } if (pMappedResource == nullptr) @@ -236,39 +236,61 @@ namespace dxvk { pMappedResource->DepthPitch = buffer->info().size; return S_OK; } else { - const D3D11TextureInfo* textureInfo + // Mapping an image is sadly not as simple as mapping a buffer + // because applications tend to ignore row and layer strides. + // We use a buffer instead and then perform a copy. + D3D11TextureInfo* textureInfo = GetCommonTextureInfo(pResource); - if (textureInfo->image->mapPtr(0) == nullptr) { + if (textureInfo->imageBuffer == nullptr) { Logger::err("D3D11DeviceContext: Cannot map a device-local image"); - return E_FAIL; + return E_INVALIDARG; + } + + // FIXME copy current image contents into the + // buffer unless D3D11_MAP_DISCARD is used. + if (MapType == D3D11_MAP_READ || MapType == D3D11_MAP_READ_WRITE) { + Logger::err("D3D11DeviceContext: Read-only and Read-Write mapping of images currently not supported"); + return E_INVALIDARG; } if (pMappedResource == nullptr) - return S_OK; + return S_FALSE; - if (textureInfo->image->isInUse()) { + if (textureInfo->imageBuffer->isInUse()) { // Don't wait if the application tells us not to if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) return DXGI_ERROR_WAS_STILL_DRAWING; - this->Flush(); - this->Synchronize(); + if (MapType == D3D11_MAP_WRITE_DISCARD) { + m_context->invalidateBuffer(textureInfo->imageBuffer); + } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) { + this->Flush(); + this->Synchronize(); + } } - const DxvkImageCreateInfo imageInfo = textureInfo->image->info(); + // Query format and subresource in order to compute + // the row pitch and layer pitch properly. + const DxvkImageCreateInfo& imageInfo = textureInfo->image->info(); + const DxvkFormatInfo* formatInfo = imageFormatInfo(imageInfo.format); - const VkImageSubresource imageSubresource = + textureInfo->mappedSubresource = GetSubresourceFromIndex(VK_IMAGE_ASPECT_COLOR_BIT, imageInfo.mipLevels, Subresource); - const VkSubresourceLayout layout = - textureInfo->image->querySubresourceLayout(imageSubresource); + const VkExtent3D levelExtent = textureInfo->image + ->mipLevelExtent(textureInfo->mappedSubresource.mipLevel); - // TODO handle undefined stuff - pMappedResource->pData = textureInfo->image->mapPtr(layout.offset); - pMappedResource->RowPitch = layout.rowPitch; - pMappedResource->DepthPitch = layout.depthPitch; + const VkExtent3D blockCount = { + levelExtent.width / formatInfo->blockSize.width, + levelExtent.height / formatInfo->blockSize.height, + levelExtent.depth / formatInfo->blockSize.depth }; + + // Set up map pointer. Data is tightly packed within the mapped buffer. + pMappedResource->pData = textureInfo->imageBuffer->mapPtr(0); + pMappedResource->RowPitch = formatInfo->elementSize * blockCount.width; + pMappedResource->DepthPitch = formatInfo->elementSize * blockCount.width * blockCount.height; return S_OK; } } @@ -277,7 +299,28 @@ namespace dxvk { void STDMETHODCALLTYPE D3D11DeviceContext::Unmap( ID3D11Resource* pResource, UINT Subresource) { - // Nothing to do here, resources are persistently mapped + D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; + pResource->GetType(&resourceDim); + + if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) { + // Now that data has been written into the buffer, + // we need to copy its contents into the image + const D3D11TextureInfo* textureInfo + = GetCommonTextureInfo(pResource); + + const VkExtent3D levelExtent = textureInfo->image + ->mipLevelExtent(textureInfo->mappedSubresource.mipLevel); + + const VkImageSubresourceLayers subresourceLayers = { + textureInfo->mappedSubresource.aspectMask, + textureInfo->mappedSubresource.mipLevel, + textureInfo->mappedSubresource.arrayLayer, 1 }; + + m_context->copyBufferToImage( + textureInfo->image, subresourceLayers, + VkOffset3D { 0, 0, 0 }, levelExtent, + textureInfo->imageBuffer, 0, { 0u, 0u }); + } } diff --git a/src/d3d11/d3d11_texture.cpp b/src/d3d11/d3d11_texture.cpp index fb8111f3..8ae3912d 100644 --- a/src/d3d11/d3d11_texture.cpp +++ b/src/d3d11/d3d11_texture.cpp @@ -56,6 +56,42 @@ namespace dxvk { } + /** + * \brief Creates a buffer to map an image object + * + * When mapping an image, some applications make the incorrect + * assumption that image data is tightly packed, which may lead + * to corrupted textures in memory. To prevent this, we create + * a tightly packed buffer and use it when mapping the image. + */ + Rc CreateImageBuffer( + const Rc& device, + VkFormat format, + VkExtent3D extent) { + const DxvkFormatInfo* formatInfo = imageFormatInfo(format); + + const VkExtent3D blockCount = { + extent.width / formatInfo->blockSize.width, + extent.height / formatInfo->blockSize.height, + extent.depth / formatInfo->blockSize.depth }; + + DxvkBufferCreateInfo info; + info.size = formatInfo->elementSize + * blockCount.width + * blockCount.height + * blockCount.depth; + info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; + info.access = VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_TRANSFER_WRITE_BIT; + + return device->createBuffer(info, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + } + + /** * \brief Fills in image info stage and access flags * @@ -101,7 +137,6 @@ namespace dxvk { } if (CPUAccessFlags != 0) { - pImageInfo->tiling = VK_IMAGE_TILING_LINEAR; pImageInfo->stages |= VK_PIPELINE_STAGE_HOST_BIT; if (CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) @@ -159,8 +194,10 @@ namespace dxvk { // Create the image and, if necessary, the image buffer m_texInfo.formatMode = formatMode; m_texInfo.image = pDevice->GetDXVKDevice()->createImage( - info, GetMemoryFlagsForUsage(pDesc->Usage)); - m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; + info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + m_texInfo.imageBuffer = pDesc->CPUAccessFlags != 0 + ? CreateImageBuffer(pDevice->GetDXVKDevice(), info.format, info.extent) + : nullptr; } /////////////////////////////////////////// @@ -249,8 +286,10 @@ namespace dxvk { // Create the image and, if necessary, the image buffer m_texInfo.formatMode = formatMode; m_texInfo.image = pDevice->GetDXVKDevice()->createImage( - info, GetMemoryFlagsForUsage(pDesc->Usage)); - m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; + info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + m_texInfo.imageBuffer = pDesc->CPUAccessFlags != 0 + ? CreateImageBuffer(pDevice->GetDXVKDevice(), info.format, info.extent) + : nullptr; } @@ -336,8 +375,10 @@ namespace dxvk { // Create the image and, if necessary, the image buffer m_texInfo.formatMode = formatMode; m_texInfo.image = pDevice->GetDXVKDevice()->createImage( - info, GetMemoryFlagsForUsage(pDesc->Usage)); - m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; + info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + m_texInfo.imageBuffer = pDesc->CPUAccessFlags != 0 + ? CreateImageBuffer(pDevice->GetDXVKDevice(), info.format, info.extent) + : nullptr; } @@ -384,7 +425,7 @@ namespace dxvk { - const D3D11TextureInfo* GetCommonTextureInfo(ID3D11Resource* pResource) { + D3D11TextureInfo* GetCommonTextureInfo(ID3D11Resource* pResource) { D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN; pResource->GetType(&dimension); diff --git a/src/d3d11/d3d11_texture.h b/src/d3d11/d3d11_texture.h index 7862ed62..d185367d 100644 --- a/src/d3d11/d3d11_texture.h +++ b/src/d3d11/d3d11_texture.h @@ -9,18 +9,6 @@ namespace dxvk { class D3D11Device; - /** - * \brief Image buffer info - * - * Stores the buffer used for mapping - * an image and the row/layer strides. - */ - struct D3D11ImageBuffer { - Rc buffer; - VkDeviceSize bytesPerRow; - VkDeviceSize bytesPerLayer; - }; - /** * \brief Common texture info * @@ -29,8 +17,11 @@ namespace dxvk { */ struct D3D11TextureInfo { DxgiFormatMode formatMode; - D3D11ImageBuffer imageBuffer; + Rc imageBuffer; Rc image; + + VkImageSubresource mappedSubresource = { + VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; }; @@ -64,7 +55,7 @@ namespace dxvk { void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE1D_DESC *pDesc) final; - const D3D11TextureInfo* GetTextureInfo() const { + D3D11TextureInfo* GetTextureInfo() { return &m_texInfo; } @@ -106,7 +97,7 @@ namespace dxvk { void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE2D_DESC *pDesc) final; - const D3D11TextureInfo* GetTextureInfo() const { + D3D11TextureInfo* GetTextureInfo() { return &m_texInfo; } @@ -148,7 +139,7 @@ namespace dxvk { void STDMETHODCALLTYPE GetDesc( D3D11_TEXTURE3D_DESC *pDesc) final; - const D3D11TextureInfo* GetTextureInfo() const { + D3D11TextureInfo* GetTextureInfo() { return &m_texInfo; } @@ -168,7 +159,7 @@ namespace dxvk { * \param [out] pTextureInfo Pointer to the texture info struct. * \returns E_INVALIDARG if the resource is not a texture */ - const D3D11TextureInfo* GetCommonTextureInfo( + D3D11TextureInfo* GetCommonTextureInfo( ID3D11Resource* pResource); /** diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 4e3ca3b9..23808fd0 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -217,6 +217,18 @@ namespace dxvk { } + void DxvkCommandList::cmdCopyBufferToImage( + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + m_vkd->vkCmdCopyBufferToImage(m_buffer, + srcBuffer, dstImage, dstImageLayout, + regionCount, pRegions); + } + + void DxvkCommandList::cmdCopyImage( VkImage srcImage, VkImageLayout srcImageLayout, diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index 1ea886eb..ca5d8c5c 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -133,6 +133,13 @@ namespace dxvk { uint32_t regionCount, const VkBufferCopy* pRegions); + void cmdCopyBufferToImage( + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + void cmdCopyImage( VkImage srcImage, VkImageLayout srcImageLayout, diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 0209e929..c0b7abac 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -286,6 +286,63 @@ namespace dxvk { } + void DxvkContext::copyBufferToImage( + const Rc& dstImage, + VkImageSubresourceLayers dstSubresource, + VkOffset3D dstOffset, + VkExtent3D dstExtent, + const Rc& srcBuffer, + VkDeviceSize srcOffset, + VkExtent2D srcExtent) { + this->renderPassEnd(); + + const VkImageSubresourceRange dstSubresourceRange = { + dstSubresource.aspectMask, + dstSubresource.mipLevel, 1, + dstSubresource.baseArrayLayer, + dstSubresource.layerCount }; + + m_barriers.accessImage( + dstImage, dstSubresourceRange, + dstImage->info().extent == dstExtent + ? VK_IMAGE_LAYOUT_UNDEFINED + : dstImage->info().layout, + dstImage->info().stages, + dstImage->info().access, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + m_barriers.recordCommands(m_cmd); + + VkBufferImageCopy copyRegion; + copyRegion.bufferOffset = srcOffset; + copyRegion.bufferRowLength = srcExtent.width; + copyRegion.bufferImageHeight = srcExtent.height; + copyRegion.imageSubresource = dstSubresource; + copyRegion.imageOffset = dstOffset; + copyRegion.imageExtent = dstExtent; + + m_cmd->cmdCopyBufferToImage( + srcBuffer->handle(), + dstImage->handle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©Region); + + 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.recordCommands(m_cmd); + + m_cmd->trackResource(dstImage); + m_cmd->trackResource(srcBuffer->resource()); + } + + void DxvkContext::copyImage( const Rc& dstImage, VkImageSubresourceLayers dstSubresource, @@ -296,25 +353,23 @@ namespace dxvk { VkExtent3D extent) { this->renderPassEnd(); - VkImageSubresourceRange dstSubresourceRange; - dstSubresourceRange.aspectMask = dstSubresource.aspectMask; - dstSubresourceRange.baseMipLevel = dstSubresource.mipLevel; - dstSubresourceRange.levelCount = 1; - dstSubresourceRange.baseArrayLayer = dstSubresource.baseArrayLayer; - dstSubresourceRange.layerCount = dstSubresource.layerCount; + const VkImageSubresourceRange dstSubresourceRange = { + dstSubresource.aspectMask, + dstSubresource.mipLevel, 1, + dstSubresource.baseArrayLayer, + dstSubresource.layerCount }; - VkImageSubresourceRange srcSubresourceRange; - srcSubresourceRange.aspectMask = srcSubresource.aspectMask; - srcSubresourceRange.baseMipLevel = srcSubresource.mipLevel; - srcSubresourceRange.levelCount = 1; - srcSubresourceRange.baseArrayLayer = srcSubresource.baseArrayLayer; - srcSubresourceRange.layerCount = srcSubresource.layerCount; + const VkImageSubresourceRange srcSubresourceRange = { + srcSubresource.aspectMask, + srcSubresource.mipLevel, 1, + srcSubresource.baseArrayLayer, + srcSubresource.layerCount }; - // TODO if the entire destination resource - // gets overwritten, discard the contents. m_barriers.accessImage( dstImage, dstSubresourceRange, - dstImage->info().layout, + dstImage->info().extent == extent + ? VK_IMAGE_LAYOUT_UNDEFINED + : dstImage->info().layout, dstImage->info().stages, dstImage->info().access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 6e8c2470..ba903b41 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -185,6 +185,26 @@ namespace dxvk { VkDeviceSize srcOffset, VkDeviceSize numBytes); + /** + * \brief Copies data from a buffer to an image + * + * \param [in] dstImage Destination image + * \param [in] dstSubresource Destination subresource + * \param [in] dstOffset Destination area offset + * \param [in] dstExtent Destination area size + * \param [in] srcBuffer Source buffer + * \param [in] srcOffset Source offset, in bytes + * \param [in] srcExtent Source data extent + */ + void copyBufferToImage( + const Rc& dstImage, + VkImageSubresourceLayers dstSubresource, + VkOffset3D dstOffset, + VkExtent3D dstExtent, + const Rc& srcBuffer, + VkDeviceSize srcOffset, + VkExtent2D srcExtent); + /** * \brief Copies data from one image to another * @@ -204,7 +224,7 @@ namespace dxvk { VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, VkExtent3D extent); - + /** * \brief Starts compute jobs *