Program Listing for File directx-memory.h

Return to documentation for file (include\gpu-utility\directx-memory.h)

/******************************************************************************
© Intel Corporation.

This software and the related documents are Intel copyrighted materials,
and your use of them is governed by the express license under which they
were provided to you ("License"). Unless the License provides otherwise,
you may not use, modify, copy, publish, distribute, disclose or transmit
this software or the related documents without Intel's prior written
permission.


 This software and the related documents are provided as is, with no express
or implied warranties, other than those that are expressly stated in the
License.

******************************************************************************/
#pragma once

#include "utility/directx-utilities.h"

#include <d3d12.h>
#include <atlbase.h>
#include <gpgmm_d3d12.h>

#include <map>
#include <mutex>
#include <memory>
#include <vector>
#include <cstdint>
#include <functional>

namespace gpa {
namespace gpu_utility {
namespace directx {

class Buffer
{
public:
    Buffer() = default;  // Invalid
    Buffer(gpgmm::d3d12::IResourceAllocation* resourceAllocation);
    Buffer(gpgmm::d3d12::IResourceAllocator* allocator, D3D12_HEAP_TYPE heapType, D3D12_RESOURCE_FLAGS flags, D3D12_RESOURCE_STATES initialState, uint64_t size);

    gpgmm::d3d12::IResourceAllocation* GetMemory() const;

private:
    CComPtr<gpgmm::d3d12::IResourceAllocation> mResourceAllocation;
};

// RAII-style wrapper to automatically map and unmap D3D12 buffers, if needed.
template<typename DataT>
class ScopedMappableBuffer : public Buffer
{
public:
    ScopedMappableBuffer() = default;
    ScopedMappableBuffer(ScopedMappableBuffer const&) = delete;
    ScopedMappableBuffer& operator=(ScopedMappableBuffer const&) = delete;

    // Construction for an existing mapped buffer.
    explicit ScopedMappableBuffer(void* mappedPointer)
        : mMappedPointer(mappedPointer)
    {
    }

    // Construction for an existing unmapped buffer.
    explicit ScopedMappableBuffer(gpgmm::d3d12::IResourceAllocation* pUnmappedBuffer, const D3D12_RANGE* pRange = nullptr)
        : Buffer(pUnmappedBuffer)
    {
        GetMemory()->Map(0, pRange, &mMappedPointer);
    }

    ~ScopedMappableBuffer()
    {
        if (GetMemory() != nullptr) {
            GetMemory()->Unmap(0, nullptr);
        }
    }

    DataT* GetMappedPointer() const
    {
        return reinterpret_cast<DataT*>(mMappedPointer);
    }

private:
    void* mMappedPointer = nullptr;
};

class QueryHeap
{
public:
    QueryHeap(ID3D12Device* device, D3D12_QUERY_HEAP_TYPE heapType, uint64_t size);

    ID3D12QueryHeap* GetMemory();

private:
    CComPtr<ID3D12QueryHeap> mQueryHeap;
};

class IMemoryPool
{
public:
    virtual ~IMemoryPool() = default;
    virtual void OnRangeDestroy(size_t id, uint64_t offset, uint64_t size) = 0;
};

template<typename MemoryType>
class MemoryRange
{
public:
    MemoryRange(IMemoryPool& pool, MemoryType& memory, size_t id, uint64_t offset, uint64_t size)
        : mPool(pool)
        , mMemory(memory)
        , mId(id)
        , mOffset(offset)
        , mSize(size)
        , mValid(true)
    {
    }

    decltype(auto) GetMemory()
    {
        return mMemory.GetMemory();
    }

    uint64_t GetOffset()
    {
        return mOffset;
    }

    uint64_t GetSize()
    {
        return mSize;
    }

    void Invalidate()
    {
        mValid = false;
    }

    ~MemoryRange()
    {
        if (mValid) {
            mPool.OnRangeDestroy(mId, mOffset, mSize);
        }
    }

private:
    IMemoryPool& mPool;
    MemoryType& mMemory;
    size_t mId;
    uint64_t mOffset;
    uint64_t mSize;
    bool mValid;

    MemoryRange(MemoryRange<MemoryType> const&) = delete;
    MemoryRange<MemoryType>& operator=(MemoryRange<MemoryType> const&) = delete;
};

template<typename MemoryType>
class MemoryPool : public IMemoryPool
{
public:
    template<typename... Args>
    MemoryPool(uint64_t chunkSize, Args&&... args)
        : mChunkSize(chunkSize)
    {
        mMemoryFactory = [=](uint64_t size) {
            return std::make_shared<MemoryType>(args..., size);
        };
    }

    std::shared_ptr<MemoryRange<MemoryType>> Allocate(uint64_t size)
    {
        std::lock_guard<std::mutex> lock(mMutex);
        auto it = mEmptyRangesBySize.lower_bound(size);
        if (it == mEmptyRangesBySize.end()) {
            size_t chunkId = mPool.size();
            uint64_t chunkSize = std::max(mChunkSize, size);
            mPool.emplace_back(mMemoryFactory(chunkSize));
            it = mEmptyRangesBySize.emplace(chunkSize, EmptyRange{chunkId, 0});
        }

        size_t chunkId = it->second.chunkId;
        uint64_t rangeOffset = it->second.offset;
        uint64_t rangeSize = it->first;
        mEmptyRangesBySize.erase(it);
        if (rangeSize > size) {
            mEmptyRangesBySize.emplace(rangeSize - size, EmptyRange{chunkId, rangeOffset + size});
        }

        return std::make_shared<MemoryRange<MemoryType>>(*this, *mPool[chunkId], chunkId, rangeOffset, size);
    }

    void OnRangeDestroy(size_t chunkId, uint64_t offset, uint64_t size) override
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mEmptyRangesBySize.emplace(size, EmptyRange{chunkId, offset});
    }

private:
    struct EmptyRange
    {
        size_t chunkId;
        uint64_t offset;
    };
    uint64_t mChunkSize;
    std::vector<std::shared_ptr<MemoryType>> mPool;
    std::multimap<uint64_t, EmptyRange> mEmptyRangesBySize;
    std::function<std::shared_ptr<MemoryType>(uint64_t)> mMemoryFactory;
    std::mutex mMutex;
};

}  // namespace directx
}  // namespace gpu_utility
}  // namespace gpa