std::size_t operator()(const Vector_f& v) const
 {
   size_t h1 = std::hash<float>()(v.x);
   size_t h2 = std::hash<float>()(v.y);
   size_t h3 = std::hash<float>()(v.z);
   return CombineHashes(h1, CombineHashes(h2, h3));
 }
std::size_t
ResourceId::ResourceIdHasher::operator()(const ResourceId &rid) const {
  const std::size_t hash1 = std::hash<database_id>()(rid.db_id_);
  const std::size_t hash2 = std::hash<relation_id>()(rid.rel_id_);
  const std::size_t hash3 = std::hash<block_id>()(rid.block_id_);
  const std::size_t hash4 = std::hash<tuple_id>()(rid.tuple_id_);

  const std::size_t comb1 = CombineHashes(hash1, hash2);
  const std::size_t comb2 = CombineHashes(hash3, hash4);

  return CombineHashes(comb1, comb2);
}
void PostProcessHelper::PostProcess(CompiledShaderPtr pixelShader, const char* name, const uint32* inputs, uint64 numInputs,
                                    const RenderTexture*const* outputs, uint64 numOutputs)
{
    Assert_(cmdList != nullptr);
    Assert_(numOutputs > 0);
    Assert_(outputs != nullptr);
    Assert_(numInputs == 0 || inputs != nullptr);
    Assert_(numInputs <= MaxInputs);

    PIXMarker marker(cmdList, name);

    HashSource hashSource;
    for(uint64 i = 0; i < numOutputs; ++i)
    {
        hashSource.OutputFormats[i] = outputs[i]->Texture.Format;
        hashSource.MSAASamples = outputs[i]->MSAASamples;
    }

    Hash psoHash = GenerateHash(&hashSource, sizeof(HashSource));
    psoHash = CombineHashes(psoHash, pixelShader->ByteCodeHash);

    ID3D12PipelineState* pso = nullptr;

    // The most linear of searches!
    const uint64 numPSOs = pipelineStates.Count();
    for(uint64 i = 0; i < numPSOs; ++i)
    {
        if(pipelineStates[i].Hash == psoHash)
        {
            pso = pipelineStates[i].PSO;
            break;
        }
    }

    if(pso == nullptr)
    {
        D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
        psoDesc.pRootSignature = rootSignature;
        psoDesc.VS = fullScreenTriVS.ByteCode();
        psoDesc.PS = pixelShader.ByteCode();
        psoDesc.RasterizerState = DX12::GetRasterizerState(RasterizerState::NoCull);
        psoDesc.BlendState = DX12::GetBlendState(BlendState::Disabled);
        psoDesc.DepthStencilState = DX12::GetDepthState(DepthState::Disabled);
        psoDesc.SampleMask = UINT_MAX;
        psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        psoDesc.NumRenderTargets = uint32(numOutputs);
        for(uint64 i = 0; i < numOutputs; ++i)
            psoDesc.RTVFormats[i] = hashSource.OutputFormats[i];
        psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
        psoDesc.SampleDesc.Count = uint32(hashSource.MSAASamples);
        DXCall(DX12::Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pso)));

        CachedPSO cachedPSO;
        cachedPSO.Hash = psoHash;
        cachedPSO.PSO = pso;
        pipelineStates.Add(cachedPSO);
    }

    D3D12_CPU_DESCRIPTOR_HANDLE rtvHandles[8] = { };
    for(uint64 i = 0; i < numOutputs; ++i)
        rtvHandles[i] = outputs[i]->RTV;
    cmdList->OMSetRenderTargets(uint32(numOutputs), rtvHandles, false, nullptr);

    cmdList->SetGraphicsRootSignature(rootSignature);
    cmdList->SetPipelineState(pso);

    DX12::BindStandardDescriptorTable(cmdList, RootParam_StandardDescriptors, CmdListMode::Graphics);

    AppSettings::BindCBufferGfx(cmdList, RootParam_AppSettings);

    uint32 srvIndices[MaxInputs] = { };
    for(uint64 i = 0; i < numInputs; ++i)
        srvIndices[i] = inputs[i];
    for(uint64 i = numInputs; i < MaxInputs; ++i)
        srvIndices[i] = DX12::NullTexture2DSRV;

    DX12::BindTempConstantBuffer(cmdList, srvIndices, RootParam_SRVIndices, CmdListMode::Graphics);

    DX12::SetViewport(cmdList, outputs[0]->Texture.Width, outputs[0]->Texture.Height);

    cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    cmdList->DrawInstanced(3, 1, 0, 0);

}