static string GetExpandedShaderCode(const wchar* path, GrowableList<wstring>& filePaths)
{
    for(uint64 i = 0; i < filePaths.Count(); ++i)
        if(filePaths[i] == path)
            return string();

    filePaths.Add(path);

    string fileContents = ReadFileAsString(path);

    // Look for includes
    size_t lineStart = 0;
    while(true)
    {
        size_t lineEnd = fileContents.find('\n', lineStart);
        size_t lineLength = 0;
        if(lineEnd == string::npos)
            lineLength = string::npos;
        else
            lineLength = lineEnd - lineStart;

        string line = fileContents.substr(lineStart, lineLength);
        if(line.find("#include") == 0)
        {
            wstring fullIncludePath;
            size_t startQuote = line.find('\"');
            if(startQuote != -1)
            {
                size_t endQuote = line.find('\"', startQuote + 1);
                string includePath = line.substr(startQuote + 1, endQuote - startQuote - 1);
                fullIncludePath = AnsiToWString(includePath.c_str());
            }
            else
            {
                startQuote = line.find('<');
                if(startQuote == -1)
                    throw Exception(L"Malformed include statement: \"" + AnsiToWString(line.c_str()) + L"\" in file " + path);
                size_t endQuote = line.find('>', startQuote + 1);
                string includePath = line.substr(startQuote + 1, endQuote - startQuote - 1);
                fullIncludePath = SampleFrameworkDir() + L"Shaders\\" + AnsiToWString(includePath.c_str());
            }

            if(FileExists(fullIncludePath.c_str()) == false)
                throw Exception(L"Couldn't find #included file \"" + fullIncludePath + L"\" in file " + path);

            string includeCode = GetExpandedShaderCode(fullIncludePath.c_str(), filePaths);
            fileContents.insert(lineEnd + 1, includeCode);
            lineEnd += includeCode.length();
        }

        if(lineEnd == string::npos)
            break;

        lineStart = lineEnd + 1;
    }

    return fileContents;
}
    HRESULT Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override
    {
        std::wstring filePath;
        if(IncludeType == D3D_INCLUDE_LOCAL)
            filePath = AnsiToWString(pFileName);
        else if(IncludeType == D3D_INCLUDE_SYSTEM)
            filePath = SampleFrameworkDir() + L"Shaders\\" + AnsiToWString(pFileName);
        else
            return E_FAIL;

        if(FileExists(filePath.c_str()) == false)
            return E_FAIL;
        File file(filePath.c_str(), FileOpenMode::Read);
        *pBytes = UINT(file.Size());
        uint8* data = reinterpret_cast<uint8*>(std::malloc(*pBytes));
        file.Read(*pBytes, data);
        *ppData = data;
        return S_OK;
    }
void PostProcessHelper::Initialize()
{
    // Load the shaders
    std::wstring fullScreenTriPath = SampleFrameworkDir() + L"Shaders\\FullScreenTriangle.hlsl";
    fullScreenTriVS = CompileFromFile(fullScreenTriPath.c_str(), "FullScreenTriangleVS", ShaderType::Vertex);

    {
        D3D12_ROOT_PARAMETER1 rootParameters[NumRootParams] = {};

        rootParameters[RootParam_StandardDescriptors].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        rootParameters[RootParam_StandardDescriptors].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
        rootParameters[RootParam_StandardDescriptors].DescriptorTable.pDescriptorRanges = DX12::StandardDescriptorRanges();
        rootParameters[RootParam_StandardDescriptors].DescriptorTable.NumDescriptorRanges = DX12::NumStandardDescriptorRanges;

        rootParameters[RootParam_SRVIndices].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
        rootParameters[RootParam_SRVIndices].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
        rootParameters[RootParam_SRVIndices].Descriptor.RegisterSpace = 0;
        rootParameters[RootParam_SRVIndices].Descriptor.ShaderRegister = 0;

        rootParameters[RootParam_AppSettings].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
        rootParameters[RootParam_AppSettings].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
        rootParameters[RootParam_AppSettings].Descriptor.RegisterSpace = 0;
        rootParameters[RootParam_AppSettings].Descriptor.ShaderRegister = AppSettings::CBufferRegister;

        D3D12_STATIC_SAMPLER_DESC staticSamplers[4] = {};
        staticSamplers[0] = DX12::GetStaticSamplerState(SamplerState::Point, 0);
        staticSamplers[1] = DX12::GetStaticSamplerState(SamplerState::LinearClamp, 1);
        staticSamplers[2] = DX12::GetStaticSamplerState(SamplerState::Linear, 2);
        staticSamplers[3] = DX12::GetStaticSamplerState(SamplerState::LinearBorder, 3);

        D3D12_ROOT_SIGNATURE_DESC1 rootSignatureDesc = {};
        rootSignatureDesc.NumParameters = ArraySize_(rootParameters);
        rootSignatureDesc.pParameters = rootParameters;
        rootSignatureDesc.NumStaticSamplers = ArraySize_(staticSamplers);
        rootSignatureDesc.pStaticSamplers = staticSamplers;
        rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;

        DX12::CreateRootSignature(&rootSignature, rootSignatureDesc);
        rootSignature->SetName(L"PostProcessHelper");
    }
}
Esempio n. 4
0
static void GetTextureData(ID3D11Device* device, ID3D11ShaderResourceView* textureSRV,
                           DXGI_FORMAT outFormat, TextureData<T>& texData)
{
    static ComputeShaderPtr decodeTextureCS;
    static ComputeShaderPtr decodeTextureArrayCS;

    static const uint32 TGSize = 16;

    if(decodeTextureCS.Valid() == false)
    {
        CompileOptions opts;
        opts.Add("TGSize_", TGSize);
        const std::wstring shaderPath = SampleFrameworkDir() + L"Shaders\\DecodeTextureCS.hlsl";
        decodeTextureCS = CompileCSFromFile(device, shaderPath.c_str(), "DecodeTextureCS", "cs_5_0", opts);

        decodeTextureArrayCS = CompileCSFromFile(device, shaderPath.c_str(), "DecodeTextureArrayCS", "cs_5_0", opts);
    }

    ID3D11Texture2DPtr texture;
    textureSRV->GetResource(reinterpret_cast<ID3D11Resource**>(&texture));

    D3D11_TEXTURE2D_DESC texDesc;
    texture->GetDesc(&texDesc);

    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
    textureSRV->GetDesc(&srvDesc);

    ID3D11ShaderResourceViewPtr sourceSRV = textureSRV;
    uint32 arraySize = texDesc.ArraySize;
    if(srvDesc.ViewDimension == D3D11_SRV_DIMENSION_TEXTURECUBE
       || srvDesc.ViewDimension == D3D11_SRV_DIMENSION_TEXTURECUBEARRAY)
    {
        srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
        srvDesc.Texture2DArray.ArraySize = arraySize;
        srvDesc.Texture2DArray.FirstArraySlice = 0;
        srvDesc.Texture2DArray.MostDetailedMip = 0;
        srvDesc.Texture2DArray.MipLevels = -1;
        DXCall(device->CreateShaderResourceView(texture, &srvDesc, &sourceSRV));
    }

    D3D11_TEXTURE2D_DESC decodeTextureDesc;
    decodeTextureDesc.Width = texDesc.Width;
    decodeTextureDesc.Height = texDesc.Height;
    decodeTextureDesc.ArraySize = arraySize;
    decodeTextureDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
    decodeTextureDesc.Format = outFormat;
    decodeTextureDesc.MipLevels = 1;
    decodeTextureDesc.MiscFlags = 0;
    decodeTextureDesc.SampleDesc.Count = 1;
    decodeTextureDesc.SampleDesc.Quality = 0;
    decodeTextureDesc.Usage = D3D11_USAGE_DEFAULT;
    decodeTextureDesc.CPUAccessFlags = 0;

    ID3D11Texture2DPtr decodeTexture;
    DXCall(device->CreateTexture2D(&decodeTextureDesc, nullptr, &decodeTexture));

    ID3D11UnorderedAccessViewPtr decodeTextureUAV;
    DXCall(device->CreateUnorderedAccessView(decodeTexture, nullptr, &decodeTextureUAV));

    ID3D11DeviceContextPtr context;
    device->GetImmediateContext(&context);

    SetCSInputs(context, sourceSRV);
    SetCSOutputs(context, decodeTextureUAV);
    SetCSShader(context, arraySize > 1 ? decodeTextureArrayCS : decodeTextureCS);

    context->Dispatch(DispatchSize(TGSize, texDesc.Width), DispatchSize(TGSize, texDesc.Height), arraySize);

    ClearCSInputs(context);
    ClearCSOutputs(context);

    StagingTexture2D stagingTexture;
    stagingTexture.Initialize(device, texDesc.Width, texDesc.Height, outFormat, 1, 1, 0, arraySize);
    context->CopyResource(stagingTexture.Texture, decodeTexture);

    texData.Init(texDesc.Width, texDesc.Height, arraySize);

    for(uint32 slice = 0; slice < arraySize; ++slice)
    {
        uint32 pitch = 0;
        const uint8* srcData = reinterpret_cast<const uint8*>(stagingTexture.Map(context, slice, pitch));
        Assert_(pitch >= texDesc.Width * sizeof(T));

        const uint32 sliceOffset = texDesc.Width * texDesc.Height * slice;

        for(uint32 y = 0; y < texDesc.Height; ++y)
        {
            const T* rowData = reinterpret_cast<const T*>(srcData);

            for(uint32 x = 0; x < texDesc.Width; ++x)
                texData.Texels[y * texDesc.Width + x + sliceOffset] = rowData[x];

            srcData += pitch;
        }
    }
}
Esempio n. 5
0
void PostProcessorBase::Initialize(ID3D11Device* device)
{
    this->device = device;

    // Create resources for the full-screen quad

    // Load the shaders
    std::wstring quadPath = SampleFrameworkDir() + L"Shaders\\Quad.hlsl";
    quadVS = CompileVSFromFile(device, quadPath.c_str(), "QuadVS");

    // Create the input layout
    D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };

    DXCall(device->CreateInputLayout(layout, 2, quadVS->ByteCode->GetBufferPointer(),
                                     quadVS->ByteCode->GetBufferSize(), &quadInputLayout));

    // Create and initialize the vertex and index buffers
    QuadVertex verts[4] =
    {
        { XMFLOAT4(1, 1, 1, 1), XMFLOAT2(1, 0) },
        { XMFLOAT4(1, -1, 1, 1), XMFLOAT2(1, 1) },
        { XMFLOAT4(-1, -1, 1, 1), XMFLOAT2(0, 1) },
        { XMFLOAT4(-1, 1, 1, 1), XMFLOAT2(0, 0) }
    };

    D3D11_BUFFER_DESC desc;
    desc.Usage = D3D11_USAGE_IMMUTABLE;
    desc.ByteWidth = sizeof(verts);
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = 0;
    desc.MiscFlags = 0;
    D3D11_SUBRESOURCE_DATA initData;
    initData.pSysMem = verts;
    initData.SysMemPitch = 0;
    initData.SysMemSlicePitch = 0;
    DXCall(device->CreateBuffer(&desc, &initData, &quadVB));

    unsigned short indices[6] = { 0, 1, 2, 2, 3, 0 };

    desc.Usage = D3D11_USAGE_IMMUTABLE;
    desc.ByteWidth = sizeof(indices);
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = 0;
    initData.pSysMem = indices;
    DXCall(device->CreateBuffer(&desc, &initData, &quadIB));

    // Create the constant buffer
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.ByteWidth = CBSize(sizeof(PSConstants));
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    DXCall(device->CreateBuffer(&desc, nullptr, &psConstants));

    // Create a depth-stencil state
    D3D11_DEPTH_STENCIL_DESC dsDesc;
    dsDesc.DepthEnable = false;
    dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
    dsDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
    dsDesc.StencilEnable = false;
    dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
    dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
    dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
    dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    dsDesc.BackFace = dsDesc.FrontFace;
    DXCall(device->CreateDepthStencilState(&dsDesc, &dsState));

    // Create a blend state
    D3D11_BLEND_DESC blendDesc;
    blendDesc.AlphaToCoverageEnable = false;
    blendDesc.IndependentBlendEnable = false;
    for (uint32 i = 0; i < 8; ++i)
    {
        blendDesc.RenderTarget[i].BlendEnable = false;
        blendDesc.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;
        blendDesc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;
        blendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_ONE;
        blendDesc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ONE;
        blendDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
        blendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_ONE;
        blendDesc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_ONE;
    }
    DXCall(device->CreateBlendState(&blendDesc, &blendState));

    // Create a rasterizer state
    D3D11_RASTERIZER_DESC rastDesc;
    rastDesc.AntialiasedLineEnable = false;
    rastDesc.CullMode = D3D11_CULL_NONE;
    rastDesc.DepthBias = 0;
    rastDesc.DepthBiasClamp = 0.0f;
    rastDesc.DepthClipEnable = true;
    rastDesc.FillMode = D3D11_FILL_SOLID;
    rastDesc.FrontCounterClockwise = false;
    rastDesc.MultisampleEnable = true;
    rastDesc.ScissorEnable = false;
    rastDesc.SlopeScaledDepthBias = 0;
    DXCall(device->CreateRasterizerState(&rastDesc, &rastState));

    // Create sampler states
    D3D11_SAMPLER_DESC sampDesc;
    sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    sampDesc.BorderColor[0] = 0;
    sampDesc.BorderColor[1] = 0;
    sampDesc.BorderColor[2] = 0;
    sampDesc.BorderColor[3] = 0;
    sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
    sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    sampDesc.MaxAnisotropy = 1;
    sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
    sampDesc.MinLOD = 0;
    sampDesc.MipLODBias = 0;
    DXCall(device->CreateSamplerState(&sampDesc, &linearSamplerState));

    sampDesc.AddressU = sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    DXCall(device->CreateSamplerState(&sampDesc, &linearWrapSamplerState));

    sampDesc.AddressU = sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
    DXCall(device->CreateSamplerState(&sampDesc, &linearBorderSamplerState));

    sampDesc.AddressU = sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
    DXCall(device->CreateSamplerState(&sampDesc, &pointSamplerState));

    sampDesc.AddressU = sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
    DXCall(device->CreateSamplerState(&sampDesc, &pointBorderSamplerState));
}
Esempio n. 6
0
void Initialize_Upload()
{
    for(uint64 i = 0; i < MaxUploadSubmissions; ++i)
        DXCall(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&UploadSubmissions[i].CmdAllocator)));

    DXCall(Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, UploadSubmissions[0].CmdAllocator, nullptr, IID_PPV_ARGS(&UploadCmdList)));
    DXCall(UploadCmdList->Close());

    D3D12_COMMAND_QUEUE_DESC queueDesc = { };
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
    DXCall(Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&UploadCmdQueue)));

    UploadFence.Init(0);

    D3D12_RESOURCE_DESC resourceDesc = { };
    resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resourceDesc.Width = uint32(UploadBufferSize);
    resourceDesc.Height = 1;
    resourceDesc.DepthOrArraySize = 1;
    resourceDesc.MipLevels = 1;
    resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
    resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
    resourceDesc.SampleDesc.Count = 1;
    resourceDesc.SampleDesc.Quality = 0;
    resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resourceDesc.Alignment = 0;

    DXCall(Device->CreateCommittedResource(DX12::GetUploadHeapProps(), D3D12_HEAP_FLAG_NONE, &resourceDesc,
                                           D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&UploadBuffer)));

    D3D12_RANGE readRange = { };
    DXCall(UploadBuffer->Map(0, &readRange, reinterpret_cast<void**>(&UploadBufferCPUAddr)));

    // Temporary buffer memory that swaps every frame
    resourceDesc.Width = uint32(TempBufferSize);

    for(uint64 i = 0; i < RenderLatency; ++i)
    {
        DXCall(Device->CreateCommittedResource(DX12::GetUploadHeapProps(), D3D12_HEAP_FLAG_NONE, &resourceDesc,
                                               D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&TempFrameBuffers[i])));

        DXCall(TempFrameBuffers[i]->Map(0, &readRange, reinterpret_cast<void**>(&TempFrameCPUMem[i])));
        TempFrameGPUMem[i] = TempFrameBuffers[i]->GetGPUVirtualAddress();
    }

    // Texture conversion resources
    DXCall(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COMPUTE, IID_PPV_ARGS(&convertCmdAllocator)));
    DXCall(Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, convertCmdAllocator, nullptr, IID_PPV_ARGS(&convertCmdList)));
    DXCall(convertCmdList->Close());
    DXCall(convertCmdList->Reset(convertCmdAllocator, nullptr));

    D3D12_COMMAND_QUEUE_DESC convertQueueDesc = {};
    convertQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    convertQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
    DXCall(Device->CreateCommandQueue(&convertQueueDesc, IID_PPV_ARGS(&convertCmdQueue)));

    CompileOptions opts;
    opts.Add("TGSize_", convertTGSize);
    const std::wstring shaderPath = SampleFrameworkDir() + L"Shaders\\DecodeTextureCS.hlsl";
    convertCS = CompileFromFile(shaderPath.c_str(), "DecodeTextureCS", ShaderType::Compute, ShaderProfile::SM51, opts);
    convertArrayCS = CompileFromFile(shaderPath.c_str(), "DecodeTextureArrayCS", ShaderType::Compute, ShaderProfile::SM51, opts);

    {
        D3D12_DESCRIPTOR_RANGE ranges[2] = {};
        ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
        ranges[0].NumDescriptors = 1;
        ranges[0].BaseShaderRegister = 0;
        ranges[0].RegisterSpace = 0;
        ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;

        ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
        ranges[1].NumDescriptors = 1;
        ranges[1].BaseShaderRegister = 0;
        ranges[1].RegisterSpace = 0;
        ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;

        D3D12_ROOT_PARAMETER rootParameters[1] = {};
        rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
        rootParameters[0].DescriptorTable.pDescriptorRanges = ranges;
        rootParameters[0].DescriptorTable.NumDescriptorRanges = ArraySize_(ranges);

        D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
        rootSignatureDesc.NumParameters = ArraySize_(rootParameters);
        rootSignatureDesc.pParameters = rootParameters;
        rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;

        DX12::CreateRootSignature(&convertRootSignature, rootSignatureDesc);
    }

    {
        D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = { };
        psoDesc.CS = convertCS.ByteCode();
        psoDesc.pRootSignature = convertRootSignature;
        psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
        Device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&convertPSO));

        psoDesc.CS = convertArrayCS.ByteCode();
        Device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&convertArrayPSO));
    }

    convertFence.Init(0);

    // Readback resources
    DXCall(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&readbackCmdAllocator)));
    DXCall(Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, readbackCmdAllocator, nullptr, IID_PPV_ARGS(&readbackCmdList)));
    DXCall(readbackCmdList->Close());
    DXCall(readbackCmdList->Reset(readbackCmdAllocator, nullptr));

    readbackFence.Init(0);
}