1
0
mirror of https://github.com/EduApps-CDG/OpenDX synced 2024-12-30 09:45:37 +01:00

[d3d11] Re-implemented image mapping

Image mapping now returns the map pointer of a separate
buffer, rather than the the image itself. This fixes
issues with applications that ignore the RowPitch
and/or DepthPitch fields of the MappedSubresource struct.
This commit is contained in:
Philip Rebohle 2018-01-05 03:01:19 +01:00
parent 93a5cf093c
commit e7bf76f5ef
7 changed files with 227 additions and 58 deletions

View File

@ -208,7 +208,7 @@ namespace dxvk {
if (buffer->mapPtr(0) == nullptr) { if (buffer->mapPtr(0) == nullptr) {
Logger::err("D3D11: Cannot map a device-local buffer"); Logger::err("D3D11: Cannot map a device-local buffer");
return E_FAIL; return E_INVALIDARG;
} }
if (pMappedResource == nullptr) if (pMappedResource == nullptr)
@ -236,39 +236,61 @@ namespace dxvk {
pMappedResource->DepthPitch = buffer->info().size; pMappedResource->DepthPitch = buffer->info().size;
return S_OK; return S_OK;
} else { } 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); = GetCommonTextureInfo(pResource);
if (textureInfo->image->mapPtr(0) == nullptr) { if (textureInfo->imageBuffer == nullptr) {
Logger::err("D3D11DeviceContext: Cannot map a device-local image"); 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) 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 // Don't wait if the application tells us not to
if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)
return DXGI_ERROR_WAS_STILL_DRAWING; return DXGI_ERROR_WAS_STILL_DRAWING;
this->Flush(); if (MapType == D3D11_MAP_WRITE_DISCARD) {
this->Synchronize(); 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, GetSubresourceFromIndex(VK_IMAGE_ASPECT_COLOR_BIT,
imageInfo.mipLevels, Subresource); imageInfo.mipLevels, Subresource);
const VkSubresourceLayout layout = const VkExtent3D levelExtent = textureInfo->image
textureInfo->image->querySubresourceLayout(imageSubresource); ->mipLevelExtent(textureInfo->mappedSubresource.mipLevel);
// TODO handle undefined stuff const VkExtent3D blockCount = {
pMappedResource->pData = textureInfo->image->mapPtr(layout.offset); levelExtent.width / formatInfo->blockSize.width,
pMappedResource->RowPitch = layout.rowPitch; levelExtent.height / formatInfo->blockSize.height,
pMappedResource->DepthPitch = layout.depthPitch; 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; return S_OK;
} }
} }
@ -277,7 +299,28 @@ namespace dxvk {
void STDMETHODCALLTYPE D3D11DeviceContext::Unmap( void STDMETHODCALLTYPE D3D11DeviceContext::Unmap(
ID3D11Resource* pResource, ID3D11Resource* pResource,
UINT Subresource) { 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 });
}
} }

View File

@ -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<DxvkBuffer> CreateImageBuffer(
const Rc<DxvkDevice>& 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 * \brief Fills in image info stage and access flags
* *
@ -101,7 +137,6 @@ namespace dxvk {
} }
if (CPUAccessFlags != 0) { if (CPUAccessFlags != 0) {
pImageInfo->tiling = VK_IMAGE_TILING_LINEAR;
pImageInfo->stages |= VK_PIPELINE_STAGE_HOST_BIT; pImageInfo->stages |= VK_PIPELINE_STAGE_HOST_BIT;
if (CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) if (CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)
@ -159,8 +194,10 @@ namespace dxvk {
// Create the image and, if necessary, the image buffer // Create the image and, if necessary, the image buffer
m_texInfo.formatMode = formatMode; m_texInfo.formatMode = formatMode;
m_texInfo.image = pDevice->GetDXVKDevice()->createImage( m_texInfo.image = pDevice->GetDXVKDevice()->createImage(
info, GetMemoryFlagsForUsage(pDesc->Usage)); info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; 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 // Create the image and, if necessary, the image buffer
m_texInfo.formatMode = formatMode; m_texInfo.formatMode = formatMode;
m_texInfo.image = pDevice->GetDXVKDevice()->createImage( m_texInfo.image = pDevice->GetDXVKDevice()->createImage(
info, GetMemoryFlagsForUsage(pDesc->Usage)); info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; 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 // Create the image and, if necessary, the image buffer
m_texInfo.formatMode = formatMode; m_texInfo.formatMode = formatMode;
m_texInfo.image = pDevice->GetDXVKDevice()->createImage( m_texInfo.image = pDevice->GetDXVKDevice()->createImage(
info, GetMemoryFlagsForUsage(pDesc->Usage)); info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_texInfo.imageBuffer = D3D11ImageBuffer { nullptr, 0, 0 }; 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; D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN;
pResource->GetType(&dimension); pResource->GetType(&dimension);

View File

@ -9,18 +9,6 @@ namespace dxvk {
class D3D11Device; class D3D11Device;
/**
* \brief Image buffer info
*
* Stores the buffer used for mapping
* an image and the row/layer strides.
*/
struct D3D11ImageBuffer {
Rc<DxvkBuffer> buffer;
VkDeviceSize bytesPerRow;
VkDeviceSize bytesPerLayer;
};
/** /**
* \brief Common texture info * \brief Common texture info
* *
@ -29,8 +17,11 @@ namespace dxvk {
*/ */
struct D3D11TextureInfo { struct D3D11TextureInfo {
DxgiFormatMode formatMode; DxgiFormatMode formatMode;
D3D11ImageBuffer imageBuffer; Rc<DxvkBuffer> imageBuffer;
Rc<DxvkImage> image; Rc<DxvkImage> image;
VkImageSubresource mappedSubresource = {
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
}; };
@ -64,7 +55,7 @@ namespace dxvk {
void STDMETHODCALLTYPE GetDesc( void STDMETHODCALLTYPE GetDesc(
D3D11_TEXTURE1D_DESC *pDesc) final; D3D11_TEXTURE1D_DESC *pDesc) final;
const D3D11TextureInfo* GetTextureInfo() const { D3D11TextureInfo* GetTextureInfo() {
return &m_texInfo; return &m_texInfo;
} }
@ -106,7 +97,7 @@ namespace dxvk {
void STDMETHODCALLTYPE GetDesc( void STDMETHODCALLTYPE GetDesc(
D3D11_TEXTURE2D_DESC *pDesc) final; D3D11_TEXTURE2D_DESC *pDesc) final;
const D3D11TextureInfo* GetTextureInfo() const { D3D11TextureInfo* GetTextureInfo() {
return &m_texInfo; return &m_texInfo;
} }
@ -148,7 +139,7 @@ namespace dxvk {
void STDMETHODCALLTYPE GetDesc( void STDMETHODCALLTYPE GetDesc(
D3D11_TEXTURE3D_DESC *pDesc) final; D3D11_TEXTURE3D_DESC *pDesc) final;
const D3D11TextureInfo* GetTextureInfo() const { D3D11TextureInfo* GetTextureInfo() {
return &m_texInfo; return &m_texInfo;
} }
@ -168,7 +159,7 @@ namespace dxvk {
* \param [out] pTextureInfo Pointer to the texture info struct. * \param [out] pTextureInfo Pointer to the texture info struct.
* \returns E_INVALIDARG if the resource is not a texture * \returns E_INVALIDARG if the resource is not a texture
*/ */
const D3D11TextureInfo* GetCommonTextureInfo( D3D11TextureInfo* GetCommonTextureInfo(
ID3D11Resource* pResource); ID3D11Resource* pResource);
/** /**

View File

@ -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( void DxvkCommandList::cmdCopyImage(
VkImage srcImage, VkImage srcImage,
VkImageLayout srcImageLayout, VkImageLayout srcImageLayout,

View File

@ -133,6 +133,13 @@ namespace dxvk {
uint32_t regionCount, uint32_t regionCount,
const VkBufferCopy* pRegions); const VkBufferCopy* pRegions);
void cmdCopyBufferToImage(
VkBuffer srcBuffer,
VkImage dstImage,
VkImageLayout dstImageLayout,
uint32_t regionCount,
const VkBufferImageCopy* pRegions);
void cmdCopyImage( void cmdCopyImage(
VkImage srcImage, VkImage srcImage,
VkImageLayout srcImageLayout, VkImageLayout srcImageLayout,

View File

@ -286,6 +286,63 @@ namespace dxvk {
} }
void DxvkContext::copyBufferToImage(
const Rc<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource,
VkOffset3D dstOffset,
VkExtent3D dstExtent,
const Rc<DxvkBuffer>& 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, &copyRegion);
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( void DxvkContext::copyImage(
const Rc<DxvkImage>& dstImage, const Rc<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource, VkImageSubresourceLayers dstSubresource,
@ -296,25 +353,23 @@ namespace dxvk {
VkExtent3D extent) { VkExtent3D extent) {
this->renderPassEnd(); this->renderPassEnd();
VkImageSubresourceRange dstSubresourceRange; const VkImageSubresourceRange dstSubresourceRange = {
dstSubresourceRange.aspectMask = dstSubresource.aspectMask; dstSubresource.aspectMask,
dstSubresourceRange.baseMipLevel = dstSubresource.mipLevel; dstSubresource.mipLevel, 1,
dstSubresourceRange.levelCount = 1; dstSubresource.baseArrayLayer,
dstSubresourceRange.baseArrayLayer = dstSubresource.baseArrayLayer; dstSubresource.layerCount };
dstSubresourceRange.layerCount = dstSubresource.layerCount;
VkImageSubresourceRange srcSubresourceRange; const VkImageSubresourceRange srcSubresourceRange = {
srcSubresourceRange.aspectMask = srcSubresource.aspectMask; srcSubresource.aspectMask,
srcSubresourceRange.baseMipLevel = srcSubresource.mipLevel; srcSubresource.mipLevel, 1,
srcSubresourceRange.levelCount = 1; srcSubresource.baseArrayLayer,
srcSubresourceRange.baseArrayLayer = srcSubresource.baseArrayLayer; srcSubresource.layerCount };
srcSubresourceRange.layerCount = srcSubresource.layerCount;
// TODO if the entire destination resource
// gets overwritten, discard the contents.
m_barriers.accessImage( m_barriers.accessImage(
dstImage, dstSubresourceRange, dstImage, dstSubresourceRange,
dstImage->info().layout, dstImage->info().extent == extent
? VK_IMAGE_LAYOUT_UNDEFINED
: dstImage->info().layout,
dstImage->info().stages, dstImage->info().stages,
dstImage->info().access, dstImage->info().access,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,

View File

@ -185,6 +185,26 @@ namespace dxvk {
VkDeviceSize srcOffset, VkDeviceSize srcOffset,
VkDeviceSize numBytes); 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<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource,
VkOffset3D dstOffset,
VkExtent3D dstExtent,
const Rc<DxvkBuffer>& srcBuffer,
VkDeviceSize srcOffset,
VkExtent2D srcExtent);
/** /**
* \brief Copies data from one image to another * \brief Copies data from one image to another
* *
@ -204,7 +224,7 @@ namespace dxvk {
VkImageSubresourceLayers srcSubresource, VkImageSubresourceLayers srcSubresource,
VkOffset3D srcOffset, VkOffset3D srcOffset,
VkExtent3D extent); VkExtent3D extent);
/** /**
* \brief Starts compute jobs * \brief Starts compute jobs
* *