/*
 * Copyright 2020 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrD3DCpuDescriptorManager_DEFINED
#define GrD3DCpuDescriptorManager_DEFINED

#include "src/gpu/ganesh/d3d/GrD3DDescriptorHeap.h"
#include <vector>

class GrD3DGpu;

class GrD3DCpuDescriptorManager {
public:
    GrD3DCpuDescriptorManager(GrD3DGpu*);

    GrD3DDescriptorHeap::CPUHandle createRenderTargetView(GrD3DGpu*,
                                                          ID3D12Resource* textureResource);
    void recycleRenderTargetView(const GrD3DDescriptorHeap::CPUHandle&);

    GrD3DDescriptorHeap::CPUHandle createDepthStencilView(GrD3DGpu*,
                                                          ID3D12Resource* textureResource);
    void recycleDepthStencilView(const GrD3DDescriptorHeap::CPUHandle&);

    GrD3DDescriptorHeap::CPUHandle createConstantBufferView(GrD3DGpu*,
                                                            ID3D12Resource* bufferResource,
                                                            size_t offset,
                                                            size_t size);
    GrD3DDescriptorHeap::CPUHandle createShaderResourceView(GrD3DGpu*,
                                                            ID3D12Resource* resource,
                                                            unsigned int mostDetailedMip,
                                                            unsigned int mipLevels);
    GrD3DDescriptorHeap::CPUHandle createUnorderedAccessView(GrD3DGpu*,
                                                             ID3D12Resource* resource,
                                                             unsigned int mipSlice);
    void recycleShaderView(const GrD3DDescriptorHeap::CPUHandle&);

    GrD3DDescriptorHeap::CPUHandle createSampler(GrD3DGpu*,
                                                 D3D12_FILTER filter,
                                                 float maxLOD,
                                                 unsigned int maxAnisotropy,
                                                 D3D12_TEXTURE_ADDRESS_MODE addressModeU,
                                                 D3D12_TEXTURE_ADDRESS_MODE addressModeV);
    void recycleSampler(const GrD3DDescriptorHeap::CPUHandle&);

private:
    class Heap {
    public:
        static std::unique_ptr<Heap> Make(GrD3DGpu* gpu, D3D12_DESCRIPTOR_HEAP_TYPE type,
                                          unsigned int numDescriptors);

        GrD3DDescriptorHeap::CPUHandle allocateCPUHandle();
        void freeCPUHandle(const GrD3DDescriptorHeap::CPUHandle&);
        bool ownsHandle(const GrD3DDescriptorHeap::CPUHandle& handle) {
            return handle.fHeapID == fHeap->uniqueID();
        }

        bool canAllocate() { return fFreeCount > 0; }

    private:
        Heap(std::unique_ptr<GrD3DDescriptorHeap>& heap, unsigned int numDescriptors)
            : fHeap(std::move(heap))
            , fFreeBlocks(numDescriptors)
            , fFreeCount(numDescriptors) {
            for (unsigned int i = 0; i < numDescriptors; ++i) {
                fFreeBlocks.set(i);
            }
        }

        std::unique_ptr<GrD3DDescriptorHeap> fHeap;
        SkBitSet fFreeBlocks;
        unsigned int fFreeCount;
    };

    class HeapPool {
    public:
        HeapPool(GrD3DGpu*, D3D12_DESCRIPTOR_HEAP_TYPE);

        GrD3DDescriptorHeap::CPUHandle allocateHandle(GrD3DGpu*);
        void releaseHandle(const GrD3DDescriptorHeap::CPUHandle&);

    private:
        std::vector<std::unique_ptr<Heap>> fDescriptorHeaps;
        int fMaxAvailableDescriptors;
        D3D12_DESCRIPTOR_HEAP_TYPE fHeapType;
    };

    HeapPool fRTVDescriptorPool;
    HeapPool fDSVDescriptorPool;
    HeapPool fShaderViewDescriptorPool;
    HeapPool fSamplerDescriptorPool;
};

#endif
