//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// RenderTarget11.cpp: Implements a DX11-specific wrapper for ID3D11View pointers
// retained by Renderbuffers.

#include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h"

#include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
#include "libANGLE/renderer/d3d/d3d11/SwapChain11.h"
#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h"

namespace rx
{

namespace
{
bool GetTextureProperties(ID3D11Resource *resource, unsigned int *mipLevels, unsigned int *samples)
{
    ID3D11Texture1D *texture1D = d3d11::DynamicCastComObject<ID3D11Texture1D>(resource);
    if (texture1D)
    {
        D3D11_TEXTURE1D_DESC texDesc;
        texture1D->GetDesc(&texDesc);
        SafeRelease(texture1D);

        *mipLevels = texDesc.MipLevels;
        *samples = 0;

        return true;
    }

    ID3D11Texture2D *texture2D = d3d11::DynamicCastComObject<ID3D11Texture2D>(resource);
    if (texture2D)
    {
        D3D11_TEXTURE2D_DESC texDesc;
        texture2D->GetDesc(&texDesc);
        SafeRelease(texture2D);

        *mipLevels = texDesc.MipLevels;
        *samples = texDesc.SampleDesc.Count > 1 ? texDesc.SampleDesc.Count : 0;

        return true;
    }

    ID3D11Texture3D *texture3D = d3d11::DynamicCastComObject<ID3D11Texture3D>(resource);
    if (texture3D)
    {
        D3D11_TEXTURE3D_DESC texDesc;
        texture3D->GetDesc(&texDesc);
        SafeRelease(texture3D);

        *mipLevels = texDesc.MipLevels;
        *samples = 0;

        return true;
    }

    return false;
}

unsigned int GetRTVSubresourceIndex(ID3D11Resource *resource, ID3D11RenderTargetView *view)
{
    D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
    view->GetDesc(&rtvDesc);

    unsigned int mipSlice = 0;
    unsigned int arraySlice = 0;

    switch (rtvDesc.ViewDimension)
    {
        case D3D11_RTV_DIMENSION_TEXTURE1D:
            mipSlice   = rtvDesc.Texture1D.MipSlice;
            arraySlice = 0;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
            mipSlice   = rtvDesc.Texture1DArray.MipSlice;
            arraySlice = rtvDesc.Texture1DArray.FirstArraySlice;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE2D:
            mipSlice   = rtvDesc.Texture2D.MipSlice;
            arraySlice = 0;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
            mipSlice   = rtvDesc.Texture2DArray.MipSlice;
            arraySlice = rtvDesc.Texture2DArray.FirstArraySlice;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE2DMS:
            mipSlice   = 0;
            arraySlice = 0;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
            mipSlice   = 0;
            arraySlice = rtvDesc.Texture2DMSArray.FirstArraySlice;
            break;

        case D3D11_RTV_DIMENSION_TEXTURE3D:
            mipSlice   = rtvDesc.Texture3D.MipSlice;
            arraySlice = 0;
            break;

        case D3D11_RTV_DIMENSION_UNKNOWN:
        case D3D11_RTV_DIMENSION_BUFFER:
            UNIMPLEMENTED();
            break;

        default:
            UNREACHABLE();
            break;
    }

    unsigned int mipLevels, samples;
    GetTextureProperties(resource, &mipLevels, &samples);

    return D3D11CalcSubresource(mipSlice, arraySlice, mipLevels);
}

unsigned int GetDSVSubresourceIndex(ID3D11Resource *resource, ID3D11DepthStencilView *view)
{
    D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
    view->GetDesc(&dsvDesc);

    unsigned int mipSlice = 0;
    unsigned int arraySlice = 0;

    switch (dsvDesc.ViewDimension)
    {
        case D3D11_DSV_DIMENSION_TEXTURE1D:
            mipSlice   = dsvDesc.Texture1D.MipSlice;
            arraySlice = 0;
            break;

        case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:
            mipSlice   = dsvDesc.Texture1DArray.MipSlice;
            arraySlice = dsvDesc.Texture1DArray.FirstArraySlice;
            break;

        case D3D11_DSV_DIMENSION_TEXTURE2D:
            mipSlice   = dsvDesc.Texture2D.MipSlice;
            arraySlice = 0;
            break;

        case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:
            mipSlice   = dsvDesc.Texture2DArray.MipSlice;
            arraySlice = dsvDesc.Texture2DArray.FirstArraySlice;
            break;

        case D3D11_DSV_DIMENSION_TEXTURE2DMS:
            mipSlice   = 0;
            arraySlice = 0;
            break;

        case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:
            mipSlice   = 0;
            arraySlice = dsvDesc.Texture2DMSArray.FirstArraySlice;
            break;

        case D3D11_DSV_DIMENSION_UNKNOWN:
            UNIMPLEMENTED();
            break;

        default:
            UNREACHABLE();
            break;
    }

    unsigned int mipLevels, samples;
    GetTextureProperties(resource, &mipLevels, &samples);

    return D3D11CalcSubresource(mipSlice, arraySlice, mipLevels);
}

GLenum GetSurfaceRTFormat(bool depth, SwapChain11 *swapChain)
{
    return (depth ? swapChain->getDepthBufferInternalFormat()
                  : swapChain->getRenderTargetInternalFormat());
}

const d3d11::Format &GetSurfaceFormatSet(bool depth, SwapChain11 *swapChain, Renderer11 *renderer)
{
    return d3d11::Format::Get(GetSurfaceRTFormat(depth, swapChain),
                              renderer->getRenderer11DeviceCaps());
}

}  // anonymous namespace

RenderTarget11::RenderTarget11(const d3d11::Format &formatSet) : mFormatSet(formatSet)
{
}

RenderTarget11::~RenderTarget11()
{
    signalDirty();
}

void RenderTarget11::signalDirty()
{
    mBroadcastChannel.signal();

    // Clear the list. We can't do this in the receiver because it would mutate during iteration.
    mBroadcastChannel.reset();
}

TextureRenderTarget11::TextureRenderTarget11(ID3D11RenderTargetView *rtv,
                                             ID3D11Resource *resource,
                                             ID3D11ShaderResourceView *srv,
                                             ID3D11ShaderResourceView *blitSRV,
                                             GLenum internalFormat,
                                             const d3d11::Format &formatSet,
                                             GLsizei width,
                                             GLsizei height,
                                             GLsizei depth,
                                             GLsizei samples)
    : RenderTarget11(formatSet),
      mWidth(width),
      mHeight(height),
      mDepth(depth),
      mInternalFormat(internalFormat),
      mSamples(samples),
      mSubresourceIndex(0),
      mTexture(resource),
      mRenderTarget(rtv),
      mDepthStencil(NULL),
      mShaderResource(srv),
      mBlitShaderResource(blitSRV)
{
    if (mTexture)
    {
        mTexture->AddRef();
    }

    if (mRenderTarget)
    {
        mRenderTarget->AddRef();
    }

    if (mShaderResource)
    {
        mShaderResource->AddRef();
    }

    if (mBlitShaderResource)
    {
        mBlitShaderResource->AddRef();
    }

    if (mRenderTarget && mTexture)
    {
        mSubresourceIndex = GetRTVSubresourceIndex(mTexture, mRenderTarget);
    }
    ASSERT(mFormatSet.format.id != angle::Format::ID::NONE || mWidth == 0 || mHeight == 0);
}

TextureRenderTarget11::TextureRenderTarget11(ID3D11DepthStencilView *dsv,
                                             ID3D11Resource *resource,
                                             ID3D11ShaderResourceView *srv,
                                             GLenum internalFormat,
                                             const d3d11::Format &formatSet,
                                             GLsizei width,
                                             GLsizei height,
                                             GLsizei depth,
                                             GLsizei samples)
    : RenderTarget11(formatSet),
      mWidth(width),
      mHeight(height),
      mDepth(depth),
      mInternalFormat(internalFormat),
      mSamples(samples),
      mSubresourceIndex(0),
      mTexture(resource),
      mRenderTarget(NULL),
      mDepthStencil(dsv),
      mShaderResource(srv),
      mBlitShaderResource(nullptr)
{
    if (mTexture)
    {
        mTexture->AddRef();
    }

    if (mDepthStencil)
    {
        mDepthStencil->AddRef();
    }

    if (mShaderResource)
    {
        mShaderResource->AddRef();
    }

    if (mDepthStencil && mTexture)
    {
        mSubresourceIndex = GetDSVSubresourceIndex(mTexture, mDepthStencil);
    }
    ASSERT(mFormatSet.format.id != angle::Format::ID::NONE || mWidth == 0 || mHeight == 0);
}

TextureRenderTarget11::~TextureRenderTarget11()
{
    SafeRelease(mTexture);
    SafeRelease(mRenderTarget);
    SafeRelease(mDepthStencil);
    SafeRelease(mShaderResource);
    SafeRelease(mBlitShaderResource);
}

ID3D11Resource *TextureRenderTarget11::getTexture() const
{
    return mTexture;
}

ID3D11RenderTargetView *TextureRenderTarget11::getRenderTargetView() const
{
    return mRenderTarget;
}

ID3D11DepthStencilView *TextureRenderTarget11::getDepthStencilView() const
{
    return mDepthStencil;
}

ID3D11ShaderResourceView *TextureRenderTarget11::getShaderResourceView() const
{
    return mShaderResource;
}

ID3D11ShaderResourceView *TextureRenderTarget11::getBlitShaderResourceView() const
{
    return mBlitShaderResource;
}

GLsizei TextureRenderTarget11::getWidth() const
{
    return mWidth;
}

GLsizei TextureRenderTarget11::getHeight() const
{
    return mHeight;
}

GLsizei TextureRenderTarget11::getDepth() const
{
    return mDepth;
}

GLenum TextureRenderTarget11::getInternalFormat() const
{
    return mInternalFormat;
}

GLsizei TextureRenderTarget11::getSamples() const
{
    return mSamples;
}

unsigned int TextureRenderTarget11::getSubresourceIndex() const
{
    return mSubresourceIndex;
}

SurfaceRenderTarget11::SurfaceRenderTarget11(SwapChain11 *swapChain,
                                             Renderer11 *renderer,
                                             bool depth)
    : RenderTarget11(GetSurfaceFormatSet(depth, swapChain, renderer)),
      mSwapChain(swapChain),
      mDepth(depth)
{
    ASSERT(mSwapChain);
}

SurfaceRenderTarget11::~SurfaceRenderTarget11()
{
}

GLsizei SurfaceRenderTarget11::getWidth() const
{
    return mSwapChain->getWidth();
}

GLsizei SurfaceRenderTarget11::getHeight() const
{
    return mSwapChain->getHeight();
}

GLsizei SurfaceRenderTarget11::getDepth() const
{
    return 1;
}

GLenum SurfaceRenderTarget11::getInternalFormat() const
{
    return GetSurfaceRTFormat(mDepth, mSwapChain);
}

GLsizei SurfaceRenderTarget11::getSamples() const
{
    // Our EGL surfaces do not support multisampling.
    return 0;
}

ID3D11Resource *SurfaceRenderTarget11::getTexture() const
{
    return (mDepth ? mSwapChain->getDepthStencilTexture() : mSwapChain->getOffscreenTexture());
}

ID3D11RenderTargetView *SurfaceRenderTarget11::getRenderTargetView() const
{
    return (mDepth ? NULL : mSwapChain->getRenderTarget());
}

ID3D11DepthStencilView *SurfaceRenderTarget11::getDepthStencilView() const
{
    return (mDepth ? mSwapChain->getDepthStencil() : NULL);
}

ID3D11ShaderResourceView *SurfaceRenderTarget11::getShaderResourceView() const
{
    return (mDepth ? mSwapChain->getDepthStencilShaderResource()
                   : mSwapChain->getRenderTargetShaderResource());
}

ID3D11ShaderResourceView *SurfaceRenderTarget11::getBlitShaderResourceView() const
{
    // The SurfaceRenderTargetView format should always be such that the normal SRV works for blits.
    return getShaderResourceView();
}

unsigned int SurfaceRenderTarget11::getSubresourceIndex() const
{
    return 0;
}

}  // namespace rx
