1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 19:54:19 +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) {
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 });
}
}

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
*
@ -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);

View File

@ -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<DxvkBuffer> buffer;
VkDeviceSize bytesPerRow;
VkDeviceSize bytesPerLayer;
};
/**
* \brief Common texture info
*
@ -29,8 +17,11 @@ namespace dxvk {
*/
struct D3D11TextureInfo {
DxgiFormatMode formatMode;
D3D11ImageBuffer imageBuffer;
Rc<DxvkBuffer> imageBuffer;
Rc<DxvkImage> 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);
/**

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(
VkImage srcImage,
VkImageLayout srcImageLayout,

View File

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

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(
const Rc<DxvkImage>& 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,

View File

@ -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<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource,
VkOffset3D dstOffset,
VkExtent3D dstExtent,
const Rc<DxvkBuffer>& 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
*