|
|
|
@@ -0,0 +1,240 @@
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
* The MIT License (MIT)
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2021 Baldur Karlsson
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
* THE SOFTWARE.
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "api/replay/data_types.h"
|
|
|
|
|
#include "api/replay/rdcarray.h"
|
|
|
|
|
#include "api/replay/rdcpair.h"
|
|
|
|
|
#include "api/replay/resourceid.h"
|
|
|
|
|
#include "api/replay/stringise.h"
|
|
|
|
|
|
|
|
|
|
namespace Sparse
|
|
|
|
|
{
|
|
|
|
|
class PageTable;
|
|
|
|
|
}; // namespace Sparse
|
|
|
|
|
|
|
|
|
|
// we pre-declare this function so we can make it a friend inside the PageTable implementation
|
|
|
|
|
template <class SerialiserType>
|
|
|
|
|
void DoSerialise(SerialiserType &ser, Sparse::PageTable &el);
|
|
|
|
|
|
|
|
|
|
namespace Sparse
|
|
|
|
|
{
|
|
|
|
|
// used for co-ordinates as well as dimensions
|
|
|
|
|
struct Coord
|
|
|
|
|
{
|
|
|
|
|
uint32_t x, y, z;
|
|
|
|
|
|
|
|
|
|
bool operator==(const Coord &o) const { return x == o.x && y == o.y && z == o.z; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Page
|
|
|
|
|
{
|
|
|
|
|
ResourceId memory;
|
|
|
|
|
uint64_t offset;
|
|
|
|
|
|
|
|
|
|
bool operator==(const Page &o) const { return memory == o.memory && offset == o.offset; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PageRangeMapping
|
|
|
|
|
{
|
|
|
|
|
bool hasSingleMapping() const { return pages.empty(); }
|
|
|
|
|
// the memory mapping if there's a single mapping. Only valid if pages below is empty
|
|
|
|
|
Page singleMapping;
|
|
|
|
|
|
|
|
|
|
// since with a single mapping we only store the 'base' page, we need an additional bool to
|
|
|
|
|
// indicate if it's a single page re-used or if it's all subsequent pages used.
|
|
|
|
|
bool singlePageReused = false;
|
|
|
|
|
|
|
|
|
|
// the memory mappings per-page if there are different mappings per-page
|
|
|
|
|
rdcarray<Page> pages;
|
|
|
|
|
|
|
|
|
|
void createPages(uint32_t numPages, uint32_t pageSize);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct MipTail
|
|
|
|
|
{
|
|
|
|
|
// the first mip that is in the mip tail
|
|
|
|
|
uint32_t firstMip = 0;
|
|
|
|
|
|
|
|
|
|
// the offset in bytes for the mip tail.
|
|
|
|
|
uint64_t byteOffset = 0;
|
|
|
|
|
|
|
|
|
|
// the stride in bytes for the mip tail between each array slice's mip stride. This is set to 0
|
|
|
|
|
// if there is only one mip tail
|
|
|
|
|
uint64_t byteStride = 0;
|
|
|
|
|
|
|
|
|
|
// the size in bytes for the mip tail
|
|
|
|
|
uint64_t totalPackedByteSize = 0;
|
|
|
|
|
|
|
|
|
|
// the pages for the mip tail (or buffer pages for buffers). If byteStride is 0 this is the
|
|
|
|
|
// single set of pages for all array slices. If byteStride is non-zero then the first N pages
|
|
|
|
|
// (with N = byteStride / pageByteSize) are for slice 0, the next N for slice 1, etc.
|
|
|
|
|
rdcarray<PageRangeMapping> mappings;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// shadows the page table for a sparse resource - buffer or texture - and handles updates and
|
|
|
|
|
// retrieval. Currently the system is simple - we first store a page mapping per subresource. This
|
|
|
|
|
// should hopefully cover most applications and avoids needing to allocate, update, and store/apply
|
|
|
|
|
// a whole page table. If we detect a partial update we allocate a full page table and store the
|
|
|
|
|
// mapping for each page.
|
|
|
|
|
class PageTable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
// initialise the page table for a buffer. We just need to know its size and the page size
|
|
|
|
|
void Initialise(uint64_t bufferByteSize, uint32_t pageByteSize);
|
|
|
|
|
// initialise the page table for a texture. We specify various properties about the texture and
|
|
|
|
|
// its page shape/size and mip tail.
|
|
|
|
|
// The mip tail starts at firstTailMip - if this is >= numMips then there is no mip tail.
|
|
|
|
|
// mipTailOffset is an arbitrary offset which all mip tail bindings are relative to. It does
|
|
|
|
|
// nothing but rebase the offset provided setMipTailRange.
|
|
|
|
|
// mipTailStride is the byte stride between mip tails if they are stored separately per slice in
|
|
|
|
|
// an array texture. Again this is arbitrary to offset the binding resource offset. If the stride
|
|
|
|
|
// is 0 this means all slices have their mip tails consecutively packed.
|
|
|
|
|
// mipTailTotalPackedSize is the *total packed size of the mip tails*. This can be calculated with
|
|
|
|
|
// the size of one array slice's mip tail multiplied by the number of array slices. If the stride
|
|
|
|
|
// is 0 this is the size of the whole mip tail of the resource.
|
|
|
|
|
void Initialise(const Coord &overallTexelDim, uint32_t numMips, uint32_t numArraySlices,
|
|
|
|
|
uint32_t pageByteSize, const Coord &pageTexelDim, uint32_t firstTailMip,
|
|
|
|
|
uint64_t mipTailOffset, uint64_t mipTailStride, uint64_t mipTailTotalPackedSize);
|
|
|
|
|
|
|
|
|
|
uint64_t GetSerialiseSize() const;
|
|
|
|
|
|
|
|
|
|
inline uint32_t getPageByteSize() const { return m_PageByteSize; }
|
|
|
|
|
inline Coord getPageTexelSize() const { return m_PageTexelSize; }
|
|
|
|
|
// useful for D3D where the mip tail is indexed by subresource/array slice even if we treat it all
|
|
|
|
|
// as one
|
|
|
|
|
inline uint64_t getMipTailByteOffsetForSubresource(uint32_t subresource) const
|
|
|
|
|
{
|
|
|
|
|
const uint32_t arraySlice = (subresource / m_MipCount) % m_ArraySize;
|
|
|
|
|
return m_MipTail.byteOffset + m_MipTail.byteStride * arraySlice;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline uint32_t calcSubresource(uint32_t arraySlice, uint32_t mipLevel) const
|
|
|
|
|
{
|
|
|
|
|
return arraySlice * m_MipCount + mipLevel;
|
|
|
|
|
}
|
|
|
|
|
Coord calcSubresourcePageDim(uint32_t subresource) const;
|
|
|
|
|
|
|
|
|
|
// is this subresource in the mip tail
|
|
|
|
|
inline bool isSubresourceInMipTail(uint32_t subresource) const
|
|
|
|
|
{
|
|
|
|
|
const uint32_t mipLevel = subresource % m_MipCount;
|
|
|
|
|
return mipLevel >= m_MipTail.firstMip;
|
|
|
|
|
}
|
|
|
|
|
// is this byte offset in the resource (according to the mip tail - no other offsets are known)
|
|
|
|
|
inline bool isByteOffsetInResource(uint64_t byteOffset) const
|
|
|
|
|
{
|
|
|
|
|
const uint64_t mipTailSize = m_MipTail.byteStride == 0 ? m_MipTail.totalPackedByteSize
|
|
|
|
|
: m_MipTail.byteStride * m_ArraySize;
|
|
|
|
|
return byteOffset >= m_MipTail.byteOffset && byteOffset < m_MipTail.byteOffset + mipTailSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read-only accessors to get current state
|
|
|
|
|
const uint32_t getNumSubresources() const { return (uint32_t)m_Subresources.size(); }
|
|
|
|
|
const uint32_t getArraySize() const { return m_ArraySize; }
|
|
|
|
|
const uint32_t getMipCount() const { return m_MipCount; }
|
|
|
|
|
const PageRangeMapping &getSubresource(uint32_t subresource) const
|
|
|
|
|
{
|
|
|
|
|
return m_Subresources[subresource];
|
|
|
|
|
}
|
|
|
|
|
const MipTail &getMipTail() const { return m_MipTail; }
|
|
|
|
|
const uint64_t getSubresourceByteSize(uint32_t subresource) const
|
|
|
|
|
{
|
|
|
|
|
const Coord subresourcePageDim = calcSubresourcePageDim(subresource);
|
|
|
|
|
|
|
|
|
|
return subresourcePageDim.x * subresourcePageDim.y * subresourcePageDim.z * m_PageByteSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set a contiguous range of pages, with offsets and sizes applied in bytes.
|
|
|
|
|
// This is when you are setting XYZ resource pages to point to ABC memory pages.
|
|
|
|
|
// useSinglePage means only one page of memory will be used for all pages in the resource. Think
|
|
|
|
|
// of the case of mapping a single 'black' page to large areas of the resource, or NULL'ing out
|
|
|
|
|
// mappings.
|
|
|
|
|
// as a convenience it returns the resource offset where it finishes, since D3D allows
|
|
|
|
|
// mismatched boundaries for tile ranges and memory regions
|
|
|
|
|
uint64_t setMipTailRange(uint64_t resourceByteOffset, ResourceId memory,
|
|
|
|
|
uint64_t memoryByteOffset, uint64_t byteSize, bool useSinglePage);
|
|
|
|
|
inline uint64_t setBufferRange(uint64_t resourceByteOffset, ResourceId memory,
|
|
|
|
|
uint64_t memoryByteOffset, uint64_t byteSize, bool useSinglePage)
|
|
|
|
|
{
|
|
|
|
|
return setMipTailRange(resourceByteOffset, memory, memoryByteOffset, byteSize, useSinglePage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set a 3D box of texel pages to a range of memory.
|
|
|
|
|
// useSinglePage means only one page of memory will be used for all pages in the resource. Think
|
|
|
|
|
// of the case of mapping a single 'black' page to large areas of the resource, or NULL'ing out
|
|
|
|
|
// mappings.
|
|
|
|
|
void setImageBoxRange(uint32_t subresource, const Coord &coord, const Coord &dim,
|
|
|
|
|
ResourceId memory, uint64_t memoryByteOffset, bool useSinglePage);
|
|
|
|
|
|
|
|
|
|
// set a series of tiles in x, y, z order starting at a given point and wrapping around the
|
|
|
|
|
// overall image dimensions. In theory you could e.g. map 10 pages starting at page 6, when there
|
|
|
|
|
// are only 8 pages to a row. It would update pages 6 and 7 in the first row then all pages in the
|
|
|
|
|
// next row. This also allows wrapping from one subresource to the next
|
|
|
|
|
// we accept a byte size for consistency with all other calls that work in bytes not pages, even
|
|
|
|
|
// though this is only expected to be used by D3D which has this wrapping update operation and it
|
|
|
|
|
// operates in pages - the calling code can mutiply by getPageByteSize().
|
|
|
|
|
// useSinglePage means only one page of memory will be used for all pages in the resource. Think
|
|
|
|
|
// of the case of mapping a single 'black' page to large areas of the resource, or NULL'ing out
|
|
|
|
|
// mappings
|
|
|
|
|
// as a convenience it returns the co-ordinate and subresource where it finishes, since D3D allows
|
|
|
|
|
// mismatched boundaries for tile ranges and memory regions
|
|
|
|
|
rdcpair<uint32_t, Coord> setImageWrappedRange(uint32_t subresource, const Coord &coord,
|
|
|
|
|
uint64_t byteSize, ResourceId memory,
|
|
|
|
|
uint64_t memoryByteOffset, bool useSinglePage);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// The image dimensions that we need. We don't care about the format or anything, we just need to
|
|
|
|
|
// know the size in pages and the subresource setup
|
|
|
|
|
Coord m_TextureDim = {};
|
|
|
|
|
uint32_t m_MipCount = 1;
|
|
|
|
|
uint32_t m_ArraySize = 1;
|
|
|
|
|
|
|
|
|
|
// the byte size of a page, constant over a resource
|
|
|
|
|
uint32_t m_PageByteSize = 0;
|
|
|
|
|
|
|
|
|
|
// the size of a page in texels
|
|
|
|
|
Coord m_PageTexelSize = {};
|
|
|
|
|
|
|
|
|
|
// the page tables for each subresource, if this is an image. Note for buffers everything goes in
|
|
|
|
|
// the "mipTail".
|
|
|
|
|
// For simplicity and robustness of access every subresource has an entry here, even those
|
|
|
|
|
// corresponding to mips that are in mip tails - the overhead is nominal with each entry being
|
|
|
|
|
// 5*ptrsize = 40 bytes.
|
|
|
|
|
rdcarray<PageRangeMapping> m_Subresources;
|
|
|
|
|
|
|
|
|
|
MipTail m_MipTail;
|
|
|
|
|
|
|
|
|
|
template <typename SerialiserType>
|
|
|
|
|
friend void ::DoSerialise(SerialiserType &ser, PageTable &el);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}; // namespace Sparse
|
|
|
|
|
|
|
|
|
|
DECLARE_REFLECTION_STRUCT(Sparse::Coord);
|
|
|
|
|
DECLARE_REFLECTION_STRUCT(Sparse::Page);
|
|
|
|
|
DECLARE_REFLECTION_STRUCT(Sparse::PageRangeMapping);
|
|
|
|
|
DECLARE_REFLECTION_STRUCT(Sparse::MipTail);
|
|
|
|
|
DECLARE_REFLECTION_STRUCT(Sparse::PageTable);
|