// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "GPUDeviceDX12.h"
#include "ResourceOwnerDX12.h"
#if GRAPHICS_API_DIRECTX12
#define DX12_DEFAULT_UPLOAD_PAGE_SIZE (4 * 1014 * 1024) // 4 MB
// Upload buffer generations timeout to dispose
#define DX12_UPLOAD_PAGE_GEN_TIMEOUT DX12_BACK_BUFFER_COUNT
// Upload buffer pages that are not used for a few frames are disposed
#define DX12_UPLOAD_PAGE_NOT_USED_FRAME_TIMEOUT 60
class GPUTextureDX12;
///
/// Single page for the upload buffer
///
class UploadBufferPageDX12 : public GPUResourceDX12, public ResourceOwnerDX12
{
public:
///
/// Init
///
/// Graphics Device
/// Page size
UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size);
public:
///
/// Last generation that has been using that page
///
uint64 LastGen;
///
/// CPU memory address of the page
///
void* CPUAddress;
///
/// GPU memory address of the page
///
D3D12_GPU_VIRTUAL_ADDRESS GPUAddress;
///
/// Page size in bytes
///
uint64 Size;
public:
// [GPUResourceDX12]
GPUResourceType GetResourceType() const final override
{
return GPUResourceType::Buffer;
}
// [ResourceOwnerDX12]
GPUResource* AsGPUResource() const override
{
return (GPUResource*)this;
}
protected:
// [GPUResourceDX12]
void OnReleaseGPU() final override;
};
///
/// Upload buffer allocation
///
struct DynamicAllocation
{
///
/// CPU memory address of the allocation start.
///
void* CPUAddress;
///
/// Allocation offset in bytes (from the start of the heap buffer).
///
uint64 Offset;
///
/// Allocation size in bytes
///
uint64 Size;
///
/// GPU virtual memory address of the allocation start.
///
D3D12_GPU_VIRTUAL_ADDRESS GPUAddress;
///
/// Upload buffer page that owns that allocation
///
UploadBufferPageDX12* Page;
///
/// Generation number of that allocation (generally allocation is invalid after one or two generations)
///
uint64 Generation;
///
/// Init
///
DynamicAllocation()
: CPUAddress(nullptr)
, Offset(0)
, Size(0)
, GPUAddress(0)
, Page(nullptr)
, Generation(0)
{
}
///
/// Init
///
/// CPU memory address
/// Offset in byes
/// Size in byes
/// GPU memory address
/// Parent page
/// Generation
DynamicAllocation(void* address, uint64 offset, uint64 size, D3D12_GPU_VIRTUAL_ADDRESS gpuAddress, UploadBufferPageDX12* page, uint64 generation)
: CPUAddress(address)
, Offset(offset)
, Size(size)
, GPUAddress(gpuAddress)
, Page(page)
, Generation(generation)
{
}
///
/// Returns true if allocation is invalid.
///
bool IsInvalid() const
{
return CPUAddress == nullptr || Size == 0 || Page == nullptr;
}
};
///
/// Uploading data to GPU buffer utility
///
class UploadBufferDX12
{
private:
GPUDeviceDX12* _device;
UploadBufferPageDX12* _currentPage;
uint64 _currentOffset;
uint64 _currentGeneration;
Array> _freePages;
Array> _usedPages;
public:
///
/// Init
///
/// Graphics Device
UploadBufferDX12(GPUDeviceDX12* device);
///
/// Destructor
///
~UploadBufferDX12();
public:
///
/// Gets the current generation number.
///
FORCE_INLINE uint64 GetCurrentGeneration() const
{
return _currentGeneration;
}
public:
///
/// Allocates memory for custom data in the buffer.
///
/// Size of the data in bytes
/// Data alignment in buffer in bytes
/// Dynamic location
DynamicAllocation Allocate(uint64 size, uint64 align);
///
/// Uploads data to the buffer.
///
/// GPU context to record upload command to it
/// Destination buffer
/// Destination buffer offset in bytes.
/// Data to allocate
/// Size of the data in bytes
/// True if cannot upload data, otherwise false.
bool UploadBuffer(GPUContextDX12* context, ID3D12Resource* buffer, uint32 bufferOffset, const void* data, uint64 size);
///
/// Uploads data to the texture.
///
/// GPU context to record upload command to it
/// Destination texture
/// Data to allocate
/// Source data row pitch value to upload.
/// Source data slice pitch value to upload.
/// Mip map to stream index
/// Texture array index
/// True if cannot upload data, otherwise false.
bool UploadTexture(GPUContextDX12* context, ID3D12Resource* texture, const void* srcData, uint32 srcRowPitch, uint32 srcSlicePitch, int32 mipIndex, int32 arrayIndex);
public:
///
/// Begins new generation.
///
/// The generation ID to begin.
void BeginGeneration(uint64 generation);
private:
UploadBufferPageDX12* requestPage(uint64 size);
};
#endif