//----------------------------------------------------------------------------
Fluid3InitializeSource::Fluid3InitializeSource(int xSize, int ySize,
    int zSize, int numXThreads, int numYThreads, int numZThreads,
    std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumZGroups(zSize/numZThreads)
{
    // Create the resources for generating velocity from vortices.
    mVortex.reset(new ConstantBuffer(sizeof(Vortex), true));
    mVelocity0.reset(new Texture3(DF_R32G32B32A32_FLOAT, xSize, ySize, zSize));
    mVelocity0->SetUsage(Resource::SHADER_OUTPUT);
    mVelocity1.reset(new Texture3(DF_R32G32B32A32_FLOAT, xSize, ySize, zSize));
    mVelocity1->SetUsage(Resource::SHADER_OUTPUT);

    // Create the resources for generating velocity from wind and gravity.
    mExternal.reset(new ConstantBuffer(sizeof(External), false));
    External& e = *mExternal->Get<External>();
    e.densityProducer = { 0.5f, 0.5f, 0.5f, 0.0f };
    e.densityPData = { 0.01f, 16.0f, 0.0f, 0.0f };
    e.densityConsumer = { 0.75f, 0.75f, 0.75f, 0.0f };
    e.densityCData = { 0.01f, 0.0f, 0.0f, 0.0f };
    e.gravity = { 0.0f, 0.0f, 0.0f, 0.0f };
    e.windData = { 0.001f, 0.0f, 0.0f, 0.0f };
    mSource.reset(new Texture3(DF_R32G32B32A32_FLOAT, xSize, ySize, zSize));
    mSource->SetUsage(Resource::SHADER_OUTPUT);

    // Create the shader for generating velocity from vortices.
    HLSLDefiner definer;
    definer.SetInt("NUM_X_THREADS", numXThreads);
    definer.SetInt("NUM_Y_THREADS", numYThreads);
    definer.SetInt("NUM_Z_THREADS", numZThreads);
    mGenerateVortex.reset(ShaderFactory::CreateCompute("GenerateVortex",
        msHLSLGenerateVortexString, definer));
    LogAssert(mGenerateVortex != nullptr, "Cannot create compute shader.");
    mGenerateVortex->Set("Parameters", parameters);
    mGenerateVortex->Set("Vortex", mVortex);
    mGenerateVortex->Set("inVelocity", mVelocity0);
    mGenerateVortex->Set("outVelocity", mVelocity1);

    // Create the shader for generating the sources to the fluid simulation.
    mInitializeSource.reset(ShaderFactory::CreateCompute("InitializeSource",
        msHLSLInitializeSourceString, definer));
    LogAssert(mInitializeSource != nullptr, "Cannot create compute shader.");
    mInitializeSource->Set("Parameters", parameters);
    mInitializeSource->Set("External", mExternal);
    mInitializeSource->Set("source", mSource);
}
//----------------------------------------------------------------------------
Fluid2UpdateState::Fluid2UpdateState(int xSize, int ySize, int numXThreads,
    int numYThreads, std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads)
{
    HLSLDefiner definer;
    definer.SetInt("NUM_X_THREADS", numXThreads);
    definer.SetInt("NUM_Y_THREADS", numYThreads);

    // Create the shader for generating velocity from vortices.
    mComputeUpdateState.reset(ShaderFactory::CreateCompute("UpdateState",
        msHLSLUpdateStateString, definer));
    LogAssert(mComputeUpdateState != nullptr, "Cannot create compute shader.");

    mUpdateState.reset(new Texture2(DF_R32G32B32A32_FLOAT, xSize, ySize));
    mUpdateState->SetUsage(Resource::SHADER_OUTPUT);

    mAdvectionSampler.reset(new SamplerState());
    mAdvectionSampler->filter = SamplerState::MIN_L_MAG_L_MIP_P;
    mAdvectionSampler->mode[0] = SamplerState::CLAMP;
    mAdvectionSampler->mode[1] = SamplerState::CLAMP;

    mComputeUpdateState->Set("Parameters", parameters);
    mComputeUpdateState->Set("advectionSampler", mAdvectionSampler);
    mComputeUpdateState->Set("updateState", mUpdateState);
}
//----------------------------------------------------------------------------
void PerformanceAMDWindow::CreateTextureGenerator()
{
    // Load the texture.
    std::string path = mEnvironment.GetPath("MedicineBag.jpg");
    mOriginalTexture.reset(WICFileIO::Load(path, false));

    // Create a texture that will be generated by a compute shader from the
    // original texture.
    unsigned int const width = mOriginalTexture->GetWidth();
    unsigned int const height = mOriginalTexture->GetHeight();
    mBlurredTexture.reset(new Texture2(DF_R8G8B8A8_UNORM, width, height));
    mBlurredTexture->SetUsage(Resource::SHADER_OUTPUT);

    // Create the compute shader for blurring the original texture.
    unsigned int const numThreads = 8;
    HLSLDefiner definer;
    definer.SetInt("DELTA", 3);
    definer.SetUnsignedInt("NUM_X_THREADS", numThreads);
    definer.SetUnsignedInt("NUM_Y_THREADS", numThreads);
    mNumXGroups = width / numThreads;
    mNumYGroups = height / numThreads;
    mGenerateTexture.reset(ShaderFactory::CreateCompute(
        mEnvironment.GetPath("GenerateTexture.hlsl"), definer));
    mGenerateTexture->Set("input", mOriginalTexture);
    mGenerateTexture->Set("output", mBlurredTexture);
}
//----------------------------------------------------------------------------
Fluid3ComputeDivergence::Fluid3ComputeDivergence(int xSize, int ySize,
    int zSize, int numXThreads, int numYThreads, int numZThreads,
    std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumZGroups(zSize/numZThreads)
{
    HLSLDefiner definer;
    definer.SetInt("NUM_X_THREADS", numXThreads);
    definer.SetInt("NUM_Y_THREADS", numYThreads);
    definer.SetInt("NUM_Z_THREADS", numZThreads);

    // Create the shader for computing the divergence of the velocity.
    mComputeDivergence.reset(ShaderFactory::CreateCompute("ComputeDivergence",
        msHLSLComputeDivergenceString, definer));
    LogAssert(mComputeDivergence != nullptr, "Cannot create compute shader.");

    mDivergence.reset(new Texture3(DF_R32_FLOAT, xSize, ySize, zSize));
    mDivergence->SetUsage(Resource::SHADER_OUTPUT);

    mComputeDivergence->Set("Parameters", parameters);
    mComputeDivergence->Set("divergence", mDivergence);
}
//----------------------------------------------------------------------------
Fluid2AdjustVelocity::Fluid2AdjustVelocity(int xSize, int ySize, 
    int numXThreads, int numYThreads,
    std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads)
{
    HLSLDefiner definer;
    definer.SetInt("NUM_X_THREADS", numXThreads);
    definer.SetInt("NUM_Y_THREADS", numYThreads);

    mAdjustVelocity.reset(ShaderFactory::CreateCompute("AdjustVelocity",
        msHLSLAdjustVelocityString, definer));
    LogAssert(mAdjustVelocity != nullptr, "Cannot create compute shader.");

    mAdjustVelocity->Set("Parameters", parameters);
}
//----------------------------------------------------------------------------
Fluid2InitializeState::Fluid2InitializeState(int xSize, int ySize,
    int numXThreads, int numYThreads)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads)
{
    // Use a Mersenne twister engine for random numbers.
    std::mt19937 mte;
    std::uniform_real_distribution<float> unirnd(0.0f, 1.0f);

    // Initial density values are randomly generated.
    mDensity.reset(new Texture2(DF_R32_FLOAT, xSize, ySize));
    float* data = mDensity->Get<float>();
    for (unsigned int i = 0; i < mDensity->GetNumElements(); ++i, ++data)
    {
        *data = unirnd(mte);
    }

    // Initial velocity values are zero.
    mVelocity.reset(new Texture2(DF_R32G32_FLOAT, xSize, ySize));
    memset(mVelocity->GetData(), 0, mVelocity->GetNumBytes());

    // The states at time 0 and time -dt are initialized by a compute shader.
    mStateTm1.reset(new Texture2(DF_R32G32B32A32_FLOAT, xSize, ySize));
    mStateTm1->SetUsage(Resource::SHADER_OUTPUT);

    mStateT.reset(new Texture2(DF_R32G32B32A32_FLOAT, xSize, ySize));
    mStateT->SetUsage(Resource::SHADER_OUTPUT);

    // Create the shader for initializing velocity and density.
    HLSLDefiner definer;
    definer.SetInt("NUM_X_THREADS", numXThreads);
    definer.SetInt("NUM_Y_THREADS", numYThreads);
    mInitializeState.reset(ShaderFactory::CreateCompute("InitializeState",
        msHLSLInitializeStateString, definer));
    LogAssert(mInitializeState != nullptr, "Cannot create compute shader.");
    mInitializeState->Set("density", mDensity);
    mInitializeState->Set("velocity", mVelocity);
    mInitializeState->Set("stateTm1", mStateTm1);
    mInitializeState->Set("stateT", mStateT);
}
//----------------------------------------------------------------------------
bool StructuredBuffersWindow::CreateScene()
{
    // Create the shaders and associated resources
    HLSLDefiner definer;
    definer.SetInt("WINDOW_WIDTH", mXSize);
    std::shared_ptr<VertexShader> vshader(ShaderFactory::CreateVertex(
        mEnvironment.GetPath("StructuredBuffers.hlsl"), definer));
    if (!vshader)
    {
        return false;
    }

    std::shared_ptr<PixelShader> pshader(ShaderFactory::CreatePixel(
        mEnvironment.GetPath("StructuredBuffers.hlsl"), definer));
    if (!pshader)
    {
        return false;
    }

    std::shared_ptr<ConstantBuffer> cbuffer(new ConstantBuffer(
        sizeof(Matrix4x4<float>), true));
    vshader->Set("PVWMatrix", cbuffer);

    // Create the pixel shader and associated resources.
    std::string path = mEnvironment.GetPath("StoneWall.png");
    std::shared_ptr<Texture2> baseTexture(WICFileIO::Load(path, false));
    pshader->Set("baseTexture", baseTexture);

    std::shared_ptr<SamplerState> baseSampler(new SamplerState());
    baseSampler->filter = SamplerState::MIN_L_MAG_L_MIP_P;
    baseSampler->mode[0] = SamplerState::CLAMP;
    baseSampler->mode[1] = SamplerState::CLAMP;
    pshader->Set("baseSampler", baseSampler);

    mDrawnPixels.reset(new StructuredBuffer(mXSize*mYSize,
        sizeof(Vector4<float>)));
    mDrawnPixels->SetUsage(Resource::SHADER_OUTPUT);
    mDrawnPixels->SetCopyType(Resource::COPY_BIDIRECTIONAL);
    memset(mDrawnPixels->GetData(), 0, mDrawnPixels->GetNumBytes());
    pshader->Set("drawnPixels", mDrawnPixels);

    // Create the visual effect for the square.
    std::shared_ptr<VisualEffect> effect(new VisualEffect(vshader, pshader));

    // Create a vertex buffer for a single triangle.  The PNG is stored in
    // left-handed coordinates.  The texture coordinates are chosen to reflect
    // the texture in the y-direction.
    struct Vertex
    {
        Vector3<float> position;
        Vector2<float> tcoord;
    };
    VertexFormat vformat;
    vformat.Bind(VA_POSITION, DF_R32G32B32_FLOAT, 0);
    vformat.Bind(VA_TEXCOORD, DF_R32G32_FLOAT, 0);
    std::shared_ptr<VertexBuffer> vbuffer(new VertexBuffer(vformat, 4));
    Vertex* vertex = vbuffer->Get<Vertex>();
    vertex[0].position = Vector3<float>(0.0f, 0.0f, 0.0f);
    vertex[0].tcoord = Vector2<float>(0.0f, 1.0f);
    vertex[1].position = Vector3<float>(1.0f, 0.0f, 0.0f);
    vertex[1].tcoord = Vector2<float>(1.0f, 1.0f);
    vertex[2].position = Vector3<float>(0.0f, 1.0f, 0.0f);
    vertex[2].tcoord = Vector2<float>(0.0f, 0.0f);
    vertex[3].position = Vector3<float>(1.0f, 1.0f, 0.0f);
    vertex[3].tcoord = Vector2<float>(1.0f, 0.0f);

    // Create an indexless buffer for a triangle mesh with two triangles.
    std::shared_ptr<IndexBuffer> ibuffer(new IndexBuffer(IP_TRISTRIP, 2));

    // Create the geometric object for drawing.  Translate it so that its
    // center of mass is at the origin.  This supports virtual trackball
    // motion about the object "center".
    mSquare.reset(new Visual(vbuffer, ibuffer, effect));
    mSquare->localTransform.SetTranslation(-0.5f, -0.5f, 0.0f);
    mSquare->Update();

    // Enable automatic updates of pvw-matrices and w-matrices.
    SubscribeCW(mSquare, cbuffer);

    // The structured buffer is written in the pixel shader.  This texture
    // will receive a copy of it so that we can write the results to disk
    // as a PNG file.
    mDrawnPixelsTexture = new Texture2(DF_R8G8B8A8_UNORM, mXSize, mYSize);
    return true;
}