[d3d9] Only do one allocation for all texture subresources

This commit is contained in:
Robin Kertels 2022-08-18 19:20:06 +02:00 committed by Joshie
parent 145c1ce127
commit d8933ca175
5 changed files with 181 additions and 211 deletions

View File

@ -78,10 +78,16 @@ namespace dxvk {
}
}
for (uint32_t i = 0; i < CountSubresources(); i++) {
m_memoryOffset[i] = m_totalSize;
m_totalSize += GetMipSize(i);
}
// Initialization is handled by D3D9Initializer
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)
AllocData();
m_data = m_device->GetAllocator()->Alloc(m_totalSize);
else if (m_mapMode != D3D9_COMMON_TEXTURE_MAP_MODE_NONE && m_desc.Pool != D3DPOOL_DEFAULT)
CreateBuffers();
CreateBuffer(false);
m_exposedMipLevels = m_desc.MipLevels;
@ -174,22 +180,24 @@ namespace dxvk {
return D3D_OK;
}
void* D3D9CommonTexture::GetData(UINT Subresource) {
if (unlikely(m_mappedSlices[Subresource].mapPtr != nullptr || m_mapMode != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE))
return m_mappedSlices[Subresource].mapPtr;
D3D9Memory& memory = m_data[Subresource];
memory.Map();
return memory.Ptr();
void* D3D9CommonTexture::GetData(UINT Subresource) {
if (unlikely(m_buffer != nullptr))
return m_buffer->mapPtr(m_memoryOffset[Subresource]);
m_data.Map();
uint8_t* ptr = reinterpret_cast<uint8_t*>(m_data.Ptr());
ptr += m_memoryOffset[Subresource];
return ptr;
}
void D3D9CommonTexture::CreateBufferSubresource(UINT Subresource, bool Initialize) {
if (likely(m_buffers[Subresource] != nullptr)) {
void D3D9CommonTexture::CreateBuffer(bool Initialize) {
if (likely(m_buffer != nullptr))
return;
}
DxvkBufferCreateInfo info;
info.size = GetMipSize(Subresource);
info.size = m_totalSize;
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
@ -209,18 +217,17 @@ namespace dxvk {
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
m_buffers[Subresource] = m_device->GetDXVKDevice()->createBuffer(info, memType);
m_mappedSlices[Subresource] = m_buffers[Subresource]->getSliceHandle();
m_buffer = m_device->GetDXVKDevice()->createBuffer(info, memType);
if (Initialize) {
if (m_data[Subresource]) {
m_data[Subresource].Map();
memcpy(m_mappedSlices[Subresource].mapPtr, m_data[Subresource].Ptr(), info.size);
if (m_data) {
m_data.Map();
std::memcpy(m_buffer->mapPtr(0), m_data.Ptr(), m_totalSize);
} else {
memset(m_mappedSlices[Subresource].mapPtr, 0, info.size);
std::memset(m_buffer->mapPtr(0), 0, m_totalSize);
}
}
m_data[Subresource] = {};
m_data = {};
}
@ -233,7 +240,7 @@ namespace dxvk {
const VkExtent3D mipExtent = util::computeMipLevelExtent(
GetExtent(), MipLevel);
const VkExtent3D blockCount = util::computeBlockCount(
mipExtent, formatInfo->blockSize);
@ -661,16 +668,14 @@ namespace dxvk {
m_sampleView.Srgb = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, true);
}
void D3D9CommonTexture::AllocData() {
// D3D9Initializer will handle clearing the data
const uint32_t count = CountSubresources();
for (uint32_t i = 0; i < count; i++) {
m_data[i] = m_device->GetAllocator()->Alloc(GetMipSize(i));
}
const Rc<DxvkBuffer>& D3D9CommonTexture::GetBuffer() {
return m_buffer;
}
const Rc<DxvkBuffer>& D3D9CommonTexture::GetBuffer(UINT Subresource) {
return m_buffers[Subresource];
DxvkBufferSlice D3D9CommonTexture::GetBufferSlice(UINT Subresource) {
return DxvkBufferSlice(GetBuffer(), m_memoryOffset[Subresource], GetMipSize(Subresource));
}
}

View File

@ -155,19 +155,9 @@ namespace dxvk {
*/
void* GetData(UINT Subresource);
const Rc<DxvkBuffer>& GetBuffer(UINT Subresource);
const Rc<DxvkBuffer>& GetBuffer();
DxvkBufferSliceHandle GetMappedSlice(UINT Subresource) {
return m_mappedSlices[Subresource];
}
DxvkBufferSliceHandle DiscardMapSlice(UINT Subresource) {
DxvkBufferSliceHandle handle = m_buffers[Subresource]->allocSlice();
m_mappedSlices[Subresource] = handle;
return handle;
}
DxvkBufferSlice GetBufferSlice(UINT Subresource);
/**
* \brief Computes subresource from the subresource index
@ -235,24 +225,17 @@ namespace dxvk {
return Face * m_desc.MipLevels + MipLevel;
}
void UnmapData(UINT Subresource) {
m_data[Subresource].Unmap();
}
void UnmapData() {
const uint32_t subresources = CountSubresources();
for (uint32_t i = 0; i < subresources; i++) {
m_data[i].Unmap();
}
m_data.Unmap();
}
/**
* \brief Destroys a buffer
* Destroys mapping and staging buffers for a given subresource
*/
void DestroyBufferSubresource(UINT Subresource) {
m_buffers[Subresource] = nullptr;
SetNeedsReadback(Subresource, true);
void DestroyBuffer() {
m_buffer = nullptr;
MarkAllNeedReadback();
}
bool IsDynamic() const {
@ -476,13 +459,17 @@ namespace dxvk {
*/
VkDeviceSize GetMipSize(UINT Subresource) const;
uint32_t GetTotalSize() const {
return m_totalSize;
}
/**
* \brief Creates a buffer
* Creates mapping and staging buffers for a given subresource
* allocates new buffers if necessary
* Creates the mapping buffer if necessary
* \param [in] Initialize Whether to copy over existing data (or clear if there is no data)
* \returns Whether an allocation happened
*/
void CreateBufferSubresource(UINT Subresource, bool Initialize);
void CreateBuffer(bool Initialize);
ID3D9VkInteropTexture* GetVkInterop() { return &m_d3d9Interop; }
@ -495,15 +482,17 @@ namespace dxvk {
Rc<DxvkImage> m_image;
Rc<DxvkImage> m_resolveImage;
D3D9SubresourceArray<
Rc<DxvkBuffer>> m_buffers;
D3D9SubresourceArray<
DxvkBufferSliceHandle> m_mappedSlices = { };
D3D9SubresourceArray<
D3D9Memory> m_data = { };
Rc<DxvkBuffer> m_buffer;
D3D9Memory m_data = { };
D3D9SubresourceArray<
uint64_t> m_seqs = { };
D3D9SubresourceArray<
uint32_t> m_memoryOffset = { };
uint32_t m_totalSize = 0;
D3D9_VK_FORMAT_MAPPING m_mapping;
bool m_shadow; //< Depth Compare-ness
@ -561,20 +550,6 @@ namespace dxvk {
D3DRESOURCETYPE Dimension,
UINT Layer);
/**
* \brief Creates buffers
* Creates mapping and staging buffers for all subresources
* allocates new buffers if necessary
*/
void CreateBuffers() {
// D3D9Initializer will handle clearing the buffers
const uint32_t count = CountSubresources();
for (uint32_t i = 0; i < count; i++)
CreateBufferSubresource(i, false);
}
void AllocData();
static constexpr UINT AllLayers = UINT32_MAX;
};

View File

@ -911,11 +911,14 @@ namespace dxvk {
VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());
VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(src->GetMipLevel());
dstTexInfo->CreateBufferSubresource(dst->GetSubresource(), dstTexExtent.width > srcTexExtent.width || dstTexExtent.height > srcTexExtent.height);
Rc<DxvkBuffer> dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource());
const bool clearDst = dstTexInfo->Desc()->MipLevels > 1
|| dstTexExtent.width > srcTexExtent.width
|| dstTexExtent.height > srcTexExtent.height;
Rc<DxvkImage> srcImage = srcTexInfo->GetImage();
const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);
dstTexInfo->CreateBuffer(clearDst);
DxvkBufferSlice dstBufferSlice = dstTexInfo->GetBufferSlice(dst->GetSubresource());
Rc<DxvkImage> srcImage = srcTexInfo->GetImage();
const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);
const VkImageSubresource srcSubresource = srcTexInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, src->GetSubresource());
VkImageSubresourceLayers srcSubresourceLayers = {
@ -924,12 +927,12 @@ namespace dxvk {
srcSubresource.arrayLayer, 1 };
EmitCs([
cBuffer = dstBuffer,
cBufferSlice = std::move(dstBufferSlice),
cImage = srcImage,
cSubresources = srcSubresourceLayers,
cLevelExtent = srcTexExtent
] (DxvkContext* ctx) {
ctx->copyImageToBuffer(cBuffer, 0, 4, 0,
ctx->copyImageToBuffer(cBufferSlice.buffer(), cBufferSlice.offset(), 4, 0,
cImage, cSubresources, VkOffset3D { 0, 0, 0 },
cLevelExtent);
});
@ -4298,107 +4301,94 @@ namespace dxvk {
bool needsReadback = pResource->NeedsReadback(Subresource) || renderable;
pResource->SetNeedsReadback(Subresource, false);
if (unlikely(pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED || needsReadback)) {
// Create mapping buffer if it doesn't exist yet. (POOL_DEFAULT)
pResource->CreateBufferSubresource(Subresource, !needsReadback);
pResource->CreateBuffer(!needsReadback);
}
void* mapPtr;
// Don't use MapTexture here to keep the mapped list small while the resource is still locked.
void* mapPtr = pResource->GetData(Subresource);
if ((Flags & D3DLOCK_DISCARD) && needsReadback) {
// We do not have to preserve the contents of the
// buffer if the entire image gets discarded.
const Rc<DxvkBuffer> mappedBuffer = pResource->GetBuffer(Subresource);
DxvkBufferSliceHandle physSlice = pResource->DiscardMapSlice(Subresource);
mapPtr = physSlice.mapPtr;
if (needsReadback) {
DxvkBufferSlice mappedBufferSlice = pResource->GetBufferSlice(Subresource);
const Rc<DxvkBuffer> mappedBuffer = pResource->GetBuffer();
EmitCs([
cImageBuffer = std::move(mappedBuffer),
cBufferSlice = physSlice
] (DxvkContext* ctx) {
ctx->invalidateBuffer(cImageBuffer, cBufferSlice);
});
} else {
// Don't use MapTexture here to keep the mapped list small while the resource is still locked.
mapPtr = pResource->GetData(Subresource);
if (unlikely(needsReadback) && pResource->GetImage() != nullptr) {
Rc<DxvkImage> resourceImage = pResource->GetImage();
if (needsReadback) {
const Rc<DxvkBuffer> mappedBuffer = pResource->GetBuffer(Subresource);
if (unlikely(needsReadback) && pResource->GetImage() != nullptr) {
Rc<DxvkImage> resourceImage = pResource->GetImage();
Rc<DxvkImage> mappedImage = resourceImage->info().sampleCount != 1
? pResource->GetResolveImage()
: std::move(resourceImage);
Rc<DxvkImage> mappedImage = resourceImage->info().sampleCount != 1
? pResource->GetResolveImage()
: std::move(resourceImage);
// When using any map mode which requires the image contents
// to be preserved, and if the GPU has write access to the
// image, copy the current image contents into the buffer.
auto subresourceLayers = vk::makeSubresourceLayers(subresource);
// We need to resolve this, some games
// lock MSAA render targets even though
// that's entirely illegal and they explicitly
// tell us that they do NOT want to lock them...
if (resourceImage != nullptr) {
EmitCs([
cMainImage = resourceImage,
cResolveImage = mappedImage,
cSubresource = subresourceLayers
] (DxvkContext* ctx) {
VkImageResolve region;
region.srcSubresource = cSubresource;
region.srcOffset = VkOffset3D { 0, 0, 0 };
region.dstSubresource = cSubresource;
region.dstOffset = VkOffset3D { 0, 0, 0 };
region.extent = cMainImage->mipLevelExtent(cSubresource.mipLevel);
if (cSubresource.aspectMask != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
ctx->resolveImage(
cResolveImage, cMainImage, region,
cMainImage->info().format);
}
else {
ctx->resolveDepthStencilImage(
cResolveImage, cMainImage, region,
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);
}
});
}
VkFormat packedFormat = GetPackedDepthStencilFormat(desc.Format);
// When using any map mode which requires the image contents
// to be preserved, and if the GPU has write access to the
// image, copy the current image contents into the buffer.
auto subresourceLayers = vk::makeSubresourceLayers(subresource);
// We need to resolve this, some games
// lock MSAA render targets even though
// that's entirely illegal and they explicitly
// tell us that they do NOT want to lock them...
if (resourceImage != nullptr) {
EmitCs([
cImageBuffer = mappedBuffer,
cImage = std::move(mappedImage),
cSubresources = subresourceLayers,
cLevelExtent = levelExtent,
cPackedFormat = packedFormat
cMainImage = resourceImage,
cResolveImage = mappedImage,
cSubresource = subresourceLayers
] (DxvkContext* ctx) {
if (cSubresources.aspectMask != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
ctx->copyImageToBuffer(cImageBuffer, 0, 4, 0,
cImage, cSubresources, VkOffset3D { 0, 0, 0 },
cLevelExtent);
} else {
// Copying DS to a packed buffer is only supported for D24S8 and D32S8
// right now so the 4 byte row alignment is guaranteed by the format size
ctx->copyDepthStencilImageToPackedBuffer(
cImageBuffer, 0,
VkOffset2D { 0, 0 },
VkExtent2D { cLevelExtent.width, cLevelExtent.height },
cImage, cSubresources,
VkOffset2D { 0, 0 },
VkExtent2D { cLevelExtent.width, cLevelExtent.height },
cPackedFormat);
VkImageResolve region;
region.srcSubresource = cSubresource;
region.srcOffset = VkOffset3D { 0, 0, 0 };
region.dstSubresource = cSubresource;
region.dstOffset = VkOffset3D { 0, 0, 0 };
region.extent = cMainImage->mipLevelExtent(cSubresource.mipLevel);
if (cSubresource.aspectMask != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
ctx->resolveImage(
cResolveImage, cMainImage, region,
cMainImage->info().format);
}
else {
ctx->resolveDepthStencilImage(
cResolveImage, cMainImage, region,
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);
}
});
TrackTextureMappingBufferSequenceNumber(pResource, Subresource);
}
if (!WaitForResource(mappedBuffer, pResource->GetMappingBufferSequenceNumber(Subresource), Flags))
return D3DERR_WASSTILLDRAWING;
VkFormat packedFormat = GetPackedDepthStencilFormat(desc.Format);
EmitCs([
cImageBufferSlice = std::move(mappedBufferSlice),
cImage = std::move(mappedImage),
cSubresources = subresourceLayers,
cLevelExtent = levelExtent,
cPackedFormat = packedFormat
] (DxvkContext* ctx) {
if (cSubresources.aspectMask != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
ctx->copyImageToBuffer(cImageBufferSlice.buffer(),
cImageBufferSlice.offset(), 4, 0, cImage,
cSubresources, VkOffset3D { 0, 0, 0 },
cLevelExtent);
} else {
// Copying DS to a packed buffer is only supported for D24S8 and D32S8
// right now so the 4 byte row alignment is guaranteed by the format size
ctx->copyDepthStencilImageToPackedBuffer(
cImageBufferSlice.buffer(), cImageBufferSlice.offset(),
VkOffset2D { 0, 0 },
VkExtent2D { cLevelExtent.width, cLevelExtent.height },
cImage, cSubresources,
VkOffset2D { 0, 0 },
VkExtent2D { cLevelExtent.width, cLevelExtent.height },
cPackedFormat);
}
});
TrackTextureMappingBufferSequenceNumber(pResource, Subresource);
}
if (!WaitForResource(mappedBuffer, pResource->GetMappingBufferSequenceNumber(Subresource), Flags))
return D3DERR_WASSTILLDRAWING;
}
const bool atiHack = desc.Format == D3D9Format::ATI1 || desc.Format == D3D9Format::ATI2;
@ -4496,11 +4486,10 @@ namespace dxvk {
bool shouldToss = pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;
shouldToss &= !pResource->IsDynamic();
shouldToss &= !pResource->IsManaged();
shouldToss &= !pResource->IsAnySubresourceLocked();
if (shouldToss) {
pResource->DestroyBufferSubresource(Subresource);
pResource->SetNeedsReadback(Subresource, true);
}
if (shouldToss)
pResource->DestroyBuffer();
UnmapTextures();
return D3D_OK;
@ -4561,8 +4550,10 @@ namespace dxvk {
auto convertFormat = pDestTexture->GetFormatMapping().ConversionFormatInfo;
if (unlikely(pSrcTexture->NeedsReadback(SrcSubresource))) {
pSrcTexture->CreateBufferSubresource(SrcSubresource, true);
const Rc<DxvkBuffer>& buffer = pSrcTexture->GetBuffer(SrcSubresource);
// The src texutre has to be in POOL_SYSTEMEM, so it cannot use AUTOMIPGEN.
// That means that NeedsReadback is only true if the texture has been used with GetRTData or GetFrontbufferData before.
// Those functions create a buffer, so the buffer always exists here.
const Rc<DxvkBuffer>& buffer = pSrcTexture->GetBuffer();
WaitForResource(buffer, pSrcTexture->GetMappingBufferSequenceNumber(SrcSubresource), 0);
pSrcTexture->SetNeedsReadback(SrcSubresource, false);
}

View File

@ -113,41 +113,35 @@ namespace dxvk {
// If the buffer is mapped, we can write data directly
// to the mapped memory region instead of doing it on
// the GPU. Same goes for zero-initialization.
const D3D9_COMMON_TEXTURE_DESC* desc = pTexture->Desc();
for (uint32_t a = 0; a < desc->ArraySize; a++) {
for (uint32_t m = 0; m < desc->MipLevels; m++) {
uint32_t subresource = pTexture->CalcSubresource(a, m);
void* mapPtr = pTexture->GetData(subresource);
uint32_t length = pTexture->GetMipSize(subresource);
void* mapPtr = pTexture->GetData(0);
if (pInitialData) {
// Initial data is only supported for textures with 1 subresource
VkExtent3D mipExtent = pTexture->GetExtentMip(0);
const DxvkFormatInfo* formatInfo = lookupFormatInfo(pTexture->GetFormatMapping().FormatColor);
VkExtent3D blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize);
uint32_t pitch = blockCount.width * formatInfo->elementSize;
uint32_t alignedPitch = align(pitch, 4);
if (pInitialData != nullptr) {
VkExtent3D mipExtent = pTexture->GetExtentMip(m);
const DxvkFormatInfo* formatInfo = lookupFormatInfo(pTexture->GetFormatMapping().FormatColor);
VkExtent3D blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize);
uint32_t pitch = blockCount.width * formatInfo->elementSize;
uint32_t alignedPitch = align(pitch, 4);
util::packImageData(
mapPtr,
pInitialData,
pitch,
pitch * blockCount.height,
alignedPitch,
alignedPitch * blockCount.height,
D3D9CommonTexture::GetImageTypeFromResourceType(pTexture->GetType()),
mipExtent,
pTexture->Desc()->ArraySize,
formatInfo,
VK_IMAGE_ASPECT_COLOR_BIT);
} else {
std::memset(
mapPtr, 0,
length);
}
}
util::packImageData(
mapPtr,
pInitialData,
pitch,
pitch * blockCount.height,
alignedPitch,
alignedPitch * blockCount.height,
D3D9CommonTexture::GetImageTypeFromResourceType(pTexture->GetType()),
mipExtent,
pTexture->Desc()->ArraySize,
formatInfo,
VK_IMAGE_ASPECT_COLOR_BIT);
} else {
// All subresources are allocated in one chunk of memory.
// So we can just get the pointer for subresource 0 and memset all of them at once.
std::memset(
mapPtr, 0,
pTexture->GetTotalSize());
}
if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)
pTexture->UnmapData();
pTexture->UnmapData();
}

View File

@ -180,9 +180,13 @@ namespace dxvk {
VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());
VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0);
dstTexInfo->CreateBufferSubresource(dst->GetSubresource(), dstTexExtent.width > srcTexExtent.width || dstTexExtent.height > srcTexExtent.height);
Rc<DxvkBuffer> dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource());
Rc<DxvkImage> srcImage = srcTexInfo->GetImage();
const bool clearDst = dstTexInfo->Desc()->MipLevels > 1
|| dstTexExtent.width > srcTexExtent.width
|| dstTexExtent.height > srcTexExtent.height;
dstTexInfo->CreateBuffer(clearDst);
DxvkBufferSlice dstBufferSlice = dstTexInfo->GetBufferSlice(dst->GetSubresource());
Rc<DxvkImage> srcImage = srcTexInfo->GetImage();
if (srcImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT) {
DxvkImageCreateInfo resolveInfo;
@ -316,13 +320,14 @@ namespace dxvk {
VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);
m_parent->EmitCs([
cBuffer = dstBuffer,
cImage = srcImage,
cBufferSlice = std::move(dstBufferSlice),
cImage = std::move(srcImage),
cSubresources = srcSubresourceLayers,
cLevelExtent = srcExtent
] (DxvkContext* ctx) {
ctx->copyImageToBuffer(cBuffer, 0, 4, 0,
cImage, cSubresources, VkOffset3D { 0, 0, 0 },
ctx->copyImageToBuffer(cBufferSlice.buffer(),
cBufferSlice.offset(), 4, 0, cImage,
cSubresources, VkOffset3D { 0, 0, 0 },
cLevelExtent);
});