diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index ef93e9473..b14ff99ff 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -40,7 +40,8 @@ namespace dxvk { m_shadow = DetermineShadowState(); m_supportsFetch4 = DetermineFetch4Compatibility(); - if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) { + const bool createImage = m_desc.Pool != D3DPOOL_SYSTEMMEM && m_desc.Pool != D3DPOOL_SCRATCH && m_desc.Format != D3D9Format::NULL_FORMAT; + if (createImage) { bool plainSurface = m_type == D3DRTYPE_SURFACE && !(m_desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)); @@ -73,7 +74,9 @@ namespace dxvk { } } - if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM) + if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE) + AllocData(); + else if (m_mapMode != D3D9_COMMON_TEXTURE_MAP_MODE_NONE && m_desc.Pool != D3DPOOL_DEFAULT) CreateBuffers(); m_exposedMipLevels = m_desc.MipLevels; @@ -165,11 +168,16 @@ 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; - bool D3D9CommonTexture::CreateBufferSubresource(UINT Subresource) { - if (m_buffers[Subresource] != nullptr) - return false; + D3D9Memory& memory = m_data[Subresource]; + memory.Map(); + return memory.Ptr(); + } + void D3D9CommonTexture::CreateBufferSubresource(UINT Subresource) { DxvkBufferCreateInfo info; info.size = GetMipSize(Subresource); info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT @@ -190,8 +198,6 @@ namespace dxvk { m_buffers[Subresource] = m_device->GetDXVKDevice()->createBuffer(info, memType); m_mappedSlices[Subresource] = m_buffers[Subresource]->getSliceHandle(); - - return true; } @@ -477,6 +483,20 @@ namespace dxvk { return VK_IMAGE_LAYOUT_GENERAL; } + D3D9_COMMON_TEXTURE_MAP_MODE D3D9CommonTexture::DetermineMapMode() const { + if (m_desc.Format == D3D9Format::NULL_FORMAT) + return D3D9_COMMON_TEXTURE_MAP_MODE_NONE; + +#ifdef D3D9_ALLOW_UNMAPPING + if (m_desc.Pool != D3DPOOL_DEFAULT) + return D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE; +#endif + + if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH) + return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM; + + return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED; + } void D3D9CommonTexture::ExportImageInfo() { /* From MSDN: @@ -606,5 +626,30 @@ 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& D3D9CommonTexture::GetBuffer(UINT Subresource, bool Initialize) { + if (unlikely(m_buffers[Subresource] == nullptr)) { + CreateBufferSubresource(Subresource); + + if (Initialize) { + if (m_data[Subresource]) { + m_data[Subresource].Map(); + memcpy(m_mappedSlices[Subresource].mapPtr, m_data[Subresource].Ptr(), GetMipSize(Subresource)); + } else { + memset(m_mappedSlices[Subresource].mapPtr, 0, GetMipSize(Subresource)); + } + } + m_data[Subresource] = {}; + } + + return m_buffers[Subresource]; + } } diff --git a/src/d3d9/d3d9_common_texture.h b/src/d3d9/d3d9_common_texture.h index a3c74eb4e..c29a9243c 100644 --- a/src/d3d9/d3d9_common_texture.h +++ b/src/d3d9/d3d9_common_texture.h @@ -3,6 +3,7 @@ #include "d3d9_format.h" #include "d3d9_util.h" #include "d3d9_caps.h" +#include "d3d9_mem.h" #include "../dxvk/dxvk_device.h" @@ -22,6 +23,7 @@ namespace dxvk { D3D9_COMMON_TEXTURE_MAP_MODE_NONE, ///< No mapping available D3D9_COMMON_TEXTURE_MAP_MODE_BACKED, ///< Mapped image through buffer D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image + D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE, ///< Non-Vulkan memory that can be unmapped }; /** @@ -141,9 +143,17 @@ namespace dxvk { return m_resolveImage; } - const Rc& GetBuffer(UINT Subresource) { - return m_buffers[Subresource]; - } + /** + * \brief Returns a pointer to the internal data used for LockRect/LockBox + * + * This works regardless of the map mode used by this texture + * and will map the memory if necessary. + * \param [in] Subresource Subresource index + * @return Pointer to locking data + */ + void* GetData(UINT Subresource); + + const Rc& GetBuffer(UINT Subresource, bool Initialize); DxvkBufferSliceHandle GetMappedSlice(UINT Subresource) { @@ -215,24 +225,16 @@ namespace dxvk { return Face * m_desc.MipLevels + MipLevel; } - /** - * \brief Creates buffers - * Creates mapping and staging buffers for all subresources - * allocates new buffers if necessary - */ - void CreateBuffers() { - const uint32_t count = CountSubresources(); - for (uint32_t i = 0; i < count; i++) - CreateBufferSubresource(i); + void UnmapData(UINT Subresource) { + m_data[Subresource].Unmap(); } - /** - * \brief Creates a buffer - * Creates mapping and staging buffers for a given subresource - * allocates new buffers if necessary - * \returns Whether an allocation happened - */ - bool CreateBufferSubresource(UINT Subresource); + void UnmapData() { + const uint32_t subresources = CountSubresources(); + for (uint32_t i = 0; i < subresources; i++) { + m_data[i].Unmap(); + } + } /** * \brief Destroys a buffer @@ -456,6 +458,12 @@ namespace dxvk { : 0ull; } + /** + * \brief Mip level + * \returns Size of packed mip level in bytes + */ + VkDeviceSize GetMipSize(UINT Subresource) const; + private: D3D9DeviceEx* m_device; @@ -468,7 +476,9 @@ namespace dxvk { D3D9SubresourceArray< Rc> m_buffers; D3D9SubresourceArray< - DxvkBufferSliceHandle> m_mappedSlices; + DxvkBufferSliceHandle> m_mappedSlices = { }; + D3D9SubresourceArray< + D3D9Memory> m_data = { }; D3D9SubresourceArray< uint64_t> m_seqs = { }; @@ -503,12 +513,6 @@ namespace dxvk { std::array m_dirtyBoxes; - /** - * \brief Mip level - * \returns Size of packed mip level in bytes - */ - VkDeviceSize GetMipSize(UINT Subresource) const; - Rc CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const; Rc CreateResolveImage() const; @@ -525,15 +529,7 @@ namespace dxvk { VkFormat Format, VkImageTiling Tiling) const; - D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const { - if (m_desc.Format == D3D9Format::NULL_FORMAT) - return D3D9_COMMON_TEXTURE_MAP_MODE_NONE; - - if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH) - return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM; - - return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED; - } + D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const; VkImageLayout OptimizeLayout( VkImageUsageFlags Usage) const; @@ -544,6 +540,28 @@ namespace dxvk { D3DRESOURCETYPE Dimension, UINT Layer); + /** + * \brief Creates a buffer + * Creates mapping and staging buffers for a given subresource + * allocates new buffers if necessary + * \returns Whether an allocation happened + */ + void CreateBufferSubresource(UINT Subresource); + + /** + * \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); + } + + void AllocData(); + static constexpr UINT AllLayers = UINT32_MAX; }; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 4b9954181..fbc4c64b2 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -876,7 +876,10 @@ namespace dxvk { if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT) return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE); - Rc dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource()); + VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel()); + VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(src->GetMipLevel()); + + Rc dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource(), dstTexExtent.width > srcTexExtent.width || dstTexExtent.height > srcTexExtent.height); Rc srcImage = srcTexInfo->GetImage(); const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format); @@ -887,9 +890,8 @@ namespace dxvk { srcSubresource.mipLevel, srcSubresource.arrayLayer, 1 }; - VkExtent3D srcExtent = srcTexInfo->GetExtentMip(src->GetMipLevel()); - VkExtent3D texLevelExtentBlockCount = util::computeBlockCount(srcExtent, srcFormatInfo->blockSize); + VkExtent3D texLevelExtentBlockCount = util::computeBlockCount(srcTexExtent, srcFormatInfo->blockSize); VkDeviceSize pitch = align(texLevelExtentBlockCount.width * uint32_t(srcFormatInfo->elementSize), 4); uint32_t pitchBlocks = uint32_t(pitch / srcFormatInfo->elementSize); VkExtent2D dstExtent = VkExtent2D{ pitchBlocks, @@ -899,7 +901,7 @@ namespace dxvk { cBuffer = dstBuffer, cImage = srcImage, cSubresources = srcSubresourceLayers, - cLevelExtent = srcExtent, + cLevelExtent = srcTexExtent, cDstExtent = dstExtent ] (DxvkContext* ctx) { ctx->copyImageToBuffer(cBuffer, 0, 4, 0, @@ -4128,10 +4130,6 @@ namespace dxvk { auto& desc = *(pResource->Desc()); - bool alloced = pResource->CreateBufferSubresource(Subresource); - - const Rc mappedBuffer = pResource->GetBuffer(Subresource); - auto& formatMapping = pResource->GetFormatMapping(); const DxvkFormatInfo* formatInfo = formatMapping.IsValid() @@ -4182,12 +4180,14 @@ namespace dxvk { bool needsReadback = pResource->NeedsReachback(Subresource) || renderable; pResource->SetNeedsReadback(Subresource, false); - DxvkBufferSliceHandle physSlice; + void* mapPtr; if ((Flags & D3DLOCK_DISCARD) && needsReadback) { // We do not have to preserve the contents of the // buffer if the entire image gets discarded. - physSlice = pResource->DiscardMapSlice(Subresource); + const Rc mappedBuffer = pResource->GetBuffer(Subresource, false); + DxvkBufferSliceHandle physSlice = pResource->DiscardMapSlice(Subresource); + mapPtr = physSlice.mapPtr; EmitCs([ cImageBuffer = std::move(mappedBuffer), @@ -4196,10 +4196,15 @@ namespace dxvk { ctx->invalidateBuffer(cImageBuffer, cBufferSlice); }); } else { - physSlice = pResource->GetMappedSlice(Subresource); + if (unlikely(pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED)) { + // Create mapping buffer if it doesn't exist yet. (POOL_DEFAULT) + pResource->GetBuffer(Subresource, !needsReadback); + } + + mapPtr = pResource->GetData(Subresource); if (needsReadback) { - const Rc mappedBuffer = pResource->GetBuffer(Subresource); + const Rc mappedBuffer = pResource->GetBuffer(Subresource, false); if (unlikely(needsReadback) && pResource->GetImage() != nullptr) { Rc resourceImage = pResource->GetImage(); @@ -4274,8 +4279,6 @@ namespace dxvk { if (!WaitForResource(mappedBuffer, pResource->GetMappingBufferSequenceNumber(Subresource), Flags)) return D3DERR_WASSTILLDRAWING; - } else if (alloced) { - std::memset(physSlice.mapPtr, 0, physSlice.length); } } @@ -4332,8 +4335,7 @@ namespace dxvk { (!atiHack) ? formatInfo : nullptr, pBox); - - uint8_t* data = reinterpret_cast(physSlice.mapPtr); + uint8_t* data = reinterpret_cast(mapPtr); data += offset; pLockedBox->pBits = data; return D3D_OK; @@ -4419,7 +4421,6 @@ namespace dxvk { // Now that data has been written into the buffer, // we need to copy its contents into the image - const DxvkBufferSliceHandle srcSlice = pSrcTexture->GetMappedSlice(SrcSubresource); auto formatInfo = lookupFormatInfo(image->info().format); auto srcSubresource = pSrcTexture->GetSubresourceFromIndex( @@ -4462,9 +4463,10 @@ namespace dxvk { + srcOffsetBlockCount.y * pitch + srcOffsetBlockCount.x * formatInfo->elementSize; + const void* mapPtr = pSrcTexture->GetData(SrcSubresource); VkDeviceSize dirtySize = extentBlockCount.width * extentBlockCount.height * extentBlockCount.depth * formatInfo->elementSize; D3D9BufferSlice slice = AllocStagingBuffer(dirtySize); - const void* srcData = reinterpret_cast(srcSlice.mapPtr) + copySrcOffset; + const void* srcData = reinterpret_cast(mapPtr) + copySrcOffset; util::packImageData( slice.mapPtr, srcData, extentBlockCount, formatInfo->elementSize, pitch, pitch * srcTexLevelExtentBlockCount.height); @@ -4487,6 +4489,7 @@ namespace dxvk { } else { const DxvkFormatInfo* formatInfo = lookupFormatInfo(pDestTexture->GetFormatMapping().FormatColor); + const void* mapPtr = pSrcTexture->GetData(SrcSubresource); // Add more blocks for the other planes that we might have. // TODO: PLEASE CLEAN ME @@ -4504,11 +4507,11 @@ namespace dxvk { } // the converter can not handle the 4 aligned pitch so we always repack into a staging buffer - D3D9BufferSlice slice = AllocStagingBuffer(srcSlice.length); + D3D9BufferSlice slice = AllocStagingBuffer(pSrcTexture->GetMipSize(SrcSubresource)); VkDeviceSize pitch = align(srcTexLevelExtentBlockCount.width * formatInfo->elementSize, 4); util::packImageData( - slice.mapPtr, srcSlice.mapPtr, srcTexLevelExtentBlockCount, formatInfo->elementSize, + slice.mapPtr, mapPtr, srcTexLevelExtentBlockCount, formatInfo->elementSize, pitch, std::min(convertFormat.PlaneCount, 2u) * pitch * srcTexLevelExtentBlockCount.height); Flush(); @@ -5214,7 +5217,7 @@ namespace dxvk { void D3D9DeviceEx::UploadManagedTexture(D3D9CommonTexture* pResource) { for (uint32_t subresource = 0; subresource < pResource->CountSubresources(); subresource++) { - if (!pResource->NeedsUpload(subresource) || pResource->GetBuffer(subresource) == nullptr) + if (!pResource->NeedsUpload(subresource)) continue; this->FlushImage(pResource, subresource); diff --git a/src/d3d9/d3d9_initializer.cpp b/src/d3d9/d3d9_initializer.cpp index 2695bdaf5..d9a2eceac 100644 --- a/src/d3d9/d3d9_initializer.cpp +++ b/src/d3d9/d3d9_initializer.cpp @@ -40,13 +40,15 @@ namespace dxvk { void D3D9Initializer::InitTexture( D3D9CommonTexture* pTexture, - void* pInitialData) { + void* pInitialData) { if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_NONE) return; - (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) - ? InitDeviceLocalTexture(pTexture) - : InitHostVisibleTexture(pTexture, pInitialData); + if (pTexture->GetImage() != nullptr) + InitDeviceLocalTexture(pTexture); + + if (pTexture->Desc()->Pool != D3DPOOL_DEFAULT) + InitHostVisibleTexture(pTexture, pInitialData); } @@ -115,7 +117,8 @@ namespace dxvk { 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); - DxvkBufferSliceHandle mapSlice = pTexture->GetBuffer(subresource)->getSliceHandle(); + void* mapPtr = pTexture->GetData(subresource); + uint32_t length = pTexture->GetMipSize(subresource); if (pInitialData != nullptr) { VkExtent3D mipExtent = pTexture->GetExtentMip(m); @@ -125,7 +128,7 @@ namespace dxvk { uint32_t alignedPitch = align(pitch, 4); util::packImageData( - mapSlice.mapPtr, + mapPtr, pInitialData, pitch, pitch * blockCount.height, @@ -138,11 +141,13 @@ namespace dxvk { VK_IMAGE_ASPECT_COLOR_BIT); } else { std::memset( - mapSlice.mapPtr, 0, - mapSlice.length); + mapPtr, 0, + length); } } } + if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE) + pTexture->UnmapData(); } diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index fd063c22f..0caa562d8 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -341,7 +341,10 @@ namespace dxvk { if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH)) return D3DERR_INVALIDCALL; - Rc dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource()); + VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel()); + VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0); + + Rc dstBuffer = dstTexInfo->GetBuffer(dst->GetSubresource(), dstTexExtent.width > srcTexExtent.width || dstTexExtent.height > srcTexExtent.height); Rc srcImage = srcTexInfo->GetImage(); if (srcImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT) {