d3d12: Reuse texture resources

This commit is contained in:
Vincent Lejeune 2015-09-30 23:50:28 +02:00
parent 3d643fbc0b
commit 8d986e77d1
3 changed files with 94 additions and 17 deletions

View file

@ -822,7 +822,6 @@ void D3D12GSRender::Flip()
storage.m_frameFinishedFence->SetEventOnCompletion(storage.m_fenceValue, storage.m_frameFinishedHandle); storage.m_frameFinishedFence->SetEventOnCompletion(storage.m_fenceValue, storage.m_frameFinishedHandle);
storage.m_fenceValue++; storage.m_fenceValue++;
storage.m_dirtyTextures = m_textureCache.getCurrentDisposableTexture();
storage.m_inUse = true; storage.m_inUse = true;
// Get the put pos - 1. This way after cleaning we can set the get ptr to // Get the put pos - 1. This way after cleaning we can set the get ptr to

View file

@ -190,6 +190,26 @@ struct DataHeap
} }
}; };
struct TextureEntry
{
int m_format;
size_t m_width;
size_t m_height;
size_t m_mipmap;
bool m_isDirty;
TextureEntry() : m_format(0), m_width(0), m_height(0), m_isDirty(true)
{}
TextureEntry(int f, size_t w, size_t h, size_t m) : m_format(f), m_width(w), m_height(h), m_isDirty(false)
{}
bool operator==(const TextureEntry &other)
{
return (m_format == other.m_format && m_width == other.m_width && m_height == other.m_height);
}
};
/** /**
* Manages cache of data (texture/vertex/index) * Manages cache of data (texture/vertex/index)
*/ */
@ -203,14 +223,22 @@ private:
*/ */
std::mutex mut; std::mutex mut;
std::unordered_map<u64, ComPtr<ID3D12Resource> > m_dataCache; // Storage std::unordered_map<u64, std::pair<TextureEntry, ComPtr<ID3D12Resource>> > m_dataCache; // Storage
std::list <std::tuple<u64, u32, u32> > m_protectedRange; // address, start of protected range, size of protected range std::list <std::tuple<u64, u32, u32> > m_protectedRange; // address, start of protected range, size of protected range
std::list<ComPtr<ID3D12Resource> > m_dataToDispose;
public: public:
void storeAndProtectData(u64 key, u32 start, size_t size, ComPtr<ID3D12Resource> data) void storeAndProtectData(u64 key, u32 start, size_t size, int format, size_t w, size_t h, size_t m, ComPtr<ID3D12Resource> data)
{ {
std::lock_guard<std::mutex> lock(mut); std::lock_guard<std::mutex> lock(mut);
m_dataCache[key] = data; m_dataCache[key] = std::make_pair(TextureEntry(format, w, h, m), data);
protectData(key, start, size);
}
/**
* Make memory from start to start + size write protected.
* Associate key to this range so that when a write is detected, data at key is marked dirty.
*/
void protectData(u64 key, u32 start, size_t size)
{
/// align start to 4096 byte /// align start to 4096 byte
u32 protected_range_start = align(start, 4096); u32 protected_range_start = align(start, 4096);
u32 protected_range_size = (u32)align(size, 4096); u32 protected_range_size = (u32)align(size, 4096);
@ -233,8 +261,7 @@ public:
{ {
std::lock_guard<std::mutex> lock(mut); std::lock_guard<std::mutex> lock(mut);
u64 texadrr = std::get<0>(protectedTexture); u64 texadrr = std::get<0>(protectedTexture);
m_dataToDispose.push_back(m_dataCache[texadrr]); m_dataCache[texadrr].first.m_isDirty = true;
m_dataCache.erase(texadrr);
vm::page_protect(protectedRangeStart, protectedRangeSize, 0, vm::page_writable, 0); vm::page_protect(protectedRangeStart, protectedRangeSize, 0, vm::page_writable, 0);
m_protectedRange.erase(currentIt); m_protectedRange.erase(currentIt);
@ -244,13 +271,13 @@ public:
return handled; return handled;
} }
ID3D12Resource *findDataIfAvailable(u64 key) std::pair<TextureEntry, ComPtr<ID3D12Resource> > *findDataIfAvailable(u64 key)
{ {
std::lock_guard<std::mutex> lock(mut); std::lock_guard<std::mutex> lock(mut);
std::unordered_map<u64, ComPtr<ID3D12Resource> >::const_iterator It = m_dataCache.find(key); auto It = m_dataCache.find(key);
if (It == m_dataCache.end()) if (It == m_dataCache.end())
return nullptr; return nullptr;
return It->second.Get(); return &It->second;
} }
void unprotedAll() void unprotedAll()
@ -263,10 +290,14 @@ public:
} }
} }
std::list<ComPtr<ID3D12Resource> > getCurrentDisposableTexture() /**
* Remove data stored at key, and returns a ComPtr owning it.
* The caller is responsible for releasing the ComPtr.
*/
ComPtr<ID3D12Resource> removeFromCache(u64 key)
{ {
std::list<ComPtr<ID3D12Resource> > result; auto result = m_dataCache[key].second;
result.swap(m_dataToDispose); m_dataCache.erase(key);
return result; return result;
} }
}; };

View file

@ -172,6 +172,46 @@ ComPtr<ID3D12Resource> uploadSingleTexture(
return vramTexture; return vramTexture;
} }
/**
*
*/
static
void updateExistingTexture(
const RSXTexture &texture,
ID3D12GraphicsCommandList *commandList,
DataHeap<ID3D12Resource, 65536> &textureBuffersHeap,
ID3D12Resource *existingTexture)
{
size_t w = texture.GetWidth(), h = texture.GetHeight();
int format = texture.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
DXGI_FORMAT dxgiFormat = getTextureDXGIFormat(format);
size_t textureSize = getPlacedTextureStorageSpace(texture, 256);
assert(textureBuffersHeap.canAlloc(textureSize));
size_t heapOffset = textureBuffersHeap.alloc(textureSize);
void *buffer;
ThrowIfFailed(textureBuffersHeap.m_heap->Map(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize), &buffer));
void *textureData = (char*)buffer + heapOffset;
std::vector<MipmapLevelInfo> mipInfos = uploadPlacedTexture(texture, 256, textureData);
textureBuffersHeap.m_heap->Unmap(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize));
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(existingTexture, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST));
size_t miplevel = 0;
for (const MipmapLevelInfo mli : mipInfos)
{
commandList->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(existingTexture, (UINT)miplevel), 0, 0, 0,
&CD3DX12_TEXTURE_COPY_LOCATION(textureBuffersHeap.m_heap, { heapOffset + mli.offset,{ dxgiFormat, (UINT)mli.width, (UINT)mli.height, 1, (UINT)mli.rowPitch } }), nullptr);
miplevel++;
}
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(existingTexture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));
}
/** /**
* Get number of bytes occupied by texture in RSX mem * Get number of bytes occupied by texture in RSX mem
*/ */
@ -262,22 +302,29 @@ size_t D3D12GSRender::UploadTextures(ID3D12GraphicsCommandList *cmdlist)
ID3D12Resource *vramTexture; ID3D12Resource *vramTexture;
std::unordered_map<u32, ID3D12Resource* >::const_iterator ItRTT = m_rtts.m_renderTargets.find(texaddr); std::unordered_map<u32, ID3D12Resource* >::const_iterator ItRTT = m_rtts.m_renderTargets.find(texaddr);
ID3D12Resource *cachedTex = m_textureCache.findDataIfAvailable(texaddr); std::pair<TextureEntry, ComPtr<ID3D12Resource> > *cachedTex = m_textureCache.findDataIfAvailable(texaddr);
bool isRenderTarget = false; bool isRenderTarget = false;
if (ItRTT != m_rtts.m_renderTargets.end()) if (ItRTT != m_rtts.m_renderTargets.end())
{ {
vramTexture = ItRTT->second; vramTexture = ItRTT->second;
isRenderTarget = true; isRenderTarget = true;
} }
else if (cachedTex != nullptr) else if (cachedTex != nullptr && (cachedTex->first == TextureEntry(format, w, h, m_textures[i].GetMipmap())))
{ {
vramTexture = cachedTex; if (cachedTex->first.m_isDirty)
{
updateExistingTexture(m_textures[i], cmdlist, m_textureUploadData, cachedTex->second.Get());
m_textureCache.protectData(texaddr, texaddr, getTextureSize(m_textures[i]));
}
vramTexture = cachedTex->second.Get();
} }
else else
{ {
if (cachedTex != nullptr)
getCurrentResourceStorage().m_dirtyTextures.push_back(m_textureCache.removeFromCache(texaddr));
ComPtr<ID3D12Resource> tex = uploadSingleTexture(m_textures[i], m_device.Get(), cmdlist, m_textureUploadData); ComPtr<ID3D12Resource> tex = uploadSingleTexture(m_textures[i], m_device.Get(), cmdlist, m_textureUploadData);
vramTexture = tex.Get(); vramTexture = tex.Get();
m_textureCache.storeAndProtectData(texaddr, texaddr, getTextureSize(m_textures[i]), tex); m_textureCache.storeAndProtectData(texaddr, texaddr, getTextureSize(m_textures[i]), format, w, h, m_textures[i].GetMipmap(), tex);
} }
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};