Edit

kc3-lang/angle/src/libGLESv2/renderer/d3d11/BufferStorage11.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2014-03-17 10:23:18
    Hash : e708fc02
    Message : Only use map-write instead of map-write-discard. In D3D11, map-discard is reserved for DYNAMIC usage buffer, which are resources that live on the GPU. Staging buffers don't have the notion of map-discard, and using them with discard gives a D3D11 error. This patch fixes crashes in dEQP buffer.map.write.invalidate. BUG=angle:587 Change-Id: Ie225e72d4e226c69e73826c9fb67bbb940bbc466 Reviewed-on: https://chromium-review.googlesource.com/190072 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Shannon Woods <shannonwoods@chromium.org> Tested-by: Jamie Madill <jmadill@chromium.org>

  • src/libGLESv2/renderer/d3d11/BufferStorage11.cpp
  • #include "precompiled.h"
    //
    // Copyright (c) 2013-2014 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.
    //
    
    // BufferStorage11.cpp Defines the BufferStorage11 class.
    
    #include "libGLESv2/renderer/d3d11/BufferStorage11.h"
    #include "libGLESv2/main.h"
    #include "libGLESv2/renderer/d3d11/Renderer11.h"
    #include "libGLESv2/renderer/d3d11/formatutils11.h"
    
    namespace rx
    {
    
    namespace gl_d3d11
    {
    
    D3D11_MAP GetD3DMapTypeFromBits(GLbitfield access)
    {
        bool readBit = ((access & GL_MAP_READ_BIT) != 0);
        bool writeBit = ((access & GL_MAP_WRITE_BIT) != 0);
    
        ASSERT(readBit || writeBit);
    
        // Note : we ignore the discard bit, because in D3D11, staging buffers
        //  don't accept the map-discard flag (discard only works for DYNAMIC usage)
    
        if (readBit && !writeBit)
        {
            return D3D11_MAP_READ;
        }
        else if (writeBit && !readBit)
        {
            return D3D11_MAP_WRITE;
        }
        else if (writeBit && readBit)
        {
            return D3D11_MAP_READ_WRITE;
        }
        else
        {
            UNREACHABLE();
            return D3D11_MAP_READ;
        }
    }
    
    }
    
    BufferStorage11::BufferStorage11(Renderer11 *renderer)
        : mRenderer(renderer),
          mIsMapped(false),
          mResolvedDataRevision(0),
          mReadUsageCount(0),
          mWriteUsageCount(0),
          mSize(0)
    {
    }
    
    BufferStorage11::~BufferStorage11()
    {
        for (auto it = mDirectBuffers.begin(); it != mDirectBuffers.end(); it++)
        {
            SafeDelete(it->second);
        }
    }
    
    BufferStorage11 *BufferStorage11::makeBufferStorage11(BufferStorage *bufferStorage)
    {
        ASSERT(HAS_DYNAMIC_TYPE(BufferStorage11*, bufferStorage));
        return static_cast<BufferStorage11*>(bufferStorage);
    }
    
    void *BufferStorage11::getData()
    {
        DirectBufferStorage11 *stagingBuffer = getStagingBuffer();
        if (stagingBuffer->getDataRevision() > mResolvedDataRevision)
        {
            if (stagingBuffer->getSize() > mResolvedData.size())
            {
                mResolvedData.resize(stagingBuffer->getSize());
            }
    
            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
    
            D3D11_MAPPED_SUBRESOURCE mappedResource;
            HRESULT result = context->Map(stagingBuffer->getD3DBuffer(), 0, D3D11_MAP_READ, 0, &mappedResource);
            if (FAILED(result))
            {
                return gl::error(GL_OUT_OF_MEMORY, (void*)NULL);
            }
    
            memcpy(mResolvedData.data(), mappedResource.pData, stagingBuffer->getSize());
    
            context->Unmap(stagingBuffer->getD3DBuffer(), 0);
    
            mResolvedDataRevision = stagingBuffer->getDataRevision();
        }
    
        return mResolvedData.data();
    }
    
    void BufferStorage11::setData(const void* data, unsigned int size, unsigned int offset)
    {
        DirectBufferStorage11 *stagingBuffer = getStagingBuffer();
    
        // Explicitly resize the staging buffer, preserving data if the new data will not
        // completely fill the buffer
        size_t requiredSize = size + offset;
        if (stagingBuffer->getSize() < requiredSize)
        {
            bool preserveData = (offset > 0);
            stagingBuffer->resize(requiredSize, preserveData);
        }
    
        if (data)
        {
            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
    
            D3D11_MAPPED_SUBRESOURCE mappedResource;
            HRESULT result = context->Map(stagingBuffer->getD3DBuffer(), 0, D3D11_MAP_WRITE, 0, &mappedResource);
            if (FAILED(result))
            {
                return gl::error(GL_OUT_OF_MEMORY);
            }
    
            unsigned char *offsetBufferPointer = reinterpret_cast<unsigned char *>(mappedResource.pData) + offset;
            memcpy(offsetBufferPointer, data, size);
    
            context->Unmap(stagingBuffer->getD3DBuffer(), 0);
        }
    
        stagingBuffer->setDataRevision(stagingBuffer->getDataRevision() + 1);
        mWriteUsageCount = 0;
        mSize = std::max(mSize, requiredSize);
    }
    
    void BufferStorage11::copyData(BufferStorage* sourceStorage, unsigned int size,
                                   unsigned int sourceOffset, unsigned int destOffset)
    {
        BufferStorage11* sourceStorage11 = makeBufferStorage11(sourceStorage);
        if (sourceStorage11)
        {
            DirectBufferStorage11 *dest = getLatestStorage();
            if (!dest)
            {
                dest = getStagingBuffer();
            }
    
            DirectBufferStorage11 *source = sourceStorage11->getLatestStorage();
            if (source && dest)
            {
                dest->copyFromStorage(source, sourceOffset, size, destOffset);
                dest->setDataRevision(dest->getDataRevision() + 1);
            }
    
            mSize = std::max(mSize, destOffset + size);
        }
    }
    
    void BufferStorage11::clear()
    {
        mSize = 0;
        mResolvedDataRevision = 0;
    }
    
    void BufferStorage11::markTransformFeedbackUsage()
    {
        DirectBufferStorage11 *transformFeedbackStorage = getStorage(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK);
        transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1);
    }
    
    unsigned int BufferStorage11::getSize() const
    {
        return mSize;
    }
    
    bool BufferStorage11::supportsDirectBinding() const
    {
        return true;
    }
    
    void BufferStorage11::markBufferUsage()
    {
        mReadUsageCount++;
        mWriteUsageCount++;
    
        const unsigned int usageLimit = 5;
    
        if (mReadUsageCount > usageLimit && mResolvedData.size() > 0)
        {
            mResolvedData.resize(0);
            mResolvedDataRevision = 0;
        }
    }
    
    ID3D11Buffer *BufferStorage11::getBuffer(BufferUsage usage)
    {
        markBufferUsage();
        return getStorage(usage)->getD3DBuffer();
    }
    
    ID3D11ShaderResourceView *BufferStorage11::getSRV(DXGI_FORMAT srvFormat)
    {
        DirectBufferStorage11 *storage = getStorage(BUFFER_USAGE_PIXEL_UNPACK);
        ID3D11Buffer *buffer = storage->getD3DBuffer();
    
        auto bufferSRVIt = mBufferResourceViews.find(srvFormat);
    
        if (bufferSRVIt != mBufferResourceViews.end())
        {
            if (bufferSRVIt->second.first == buffer)
            {
                return bufferSRVIt->second.second;
            }
            else
            {
                // The underlying buffer has changed since the SRV was created: recreate the SRV.
                SafeRelease(bufferSRVIt->second.second);
            }
        }
    
        ID3D11Device *device = mRenderer->getDevice();
        ID3D11ShaderResourceView *bufferSRV = NULL;
    
        D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc;
        bufferSRVDesc.Buffer.ElementOffset = 0;
        bufferSRVDesc.Buffer.ElementWidth = mSize / d3d11::GetFormatPixelBytes(srvFormat);
        bufferSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
        bufferSRVDesc.Format = srvFormat;
    
        HRESULT result = device->CreateShaderResourceView(buffer, &bufferSRVDesc, &bufferSRV);
        ASSERT(SUCCEEDED(result));
    
        mBufferResourceViews[srvFormat] = BufferSRVPair(buffer, bufferSRV);
    
        return bufferSRV;
    }
    
    DirectBufferStorage11 *BufferStorage11::getStorage(BufferUsage usage)
    {
        DirectBufferStorage11 *directBuffer = NULL;
        auto directBufferIt = mDirectBuffers.find(usage);
        if (directBufferIt != mDirectBuffers.end())
        {
            directBuffer = directBufferIt->second;
        }
    
        if (!directBuffer)
        {
            // buffer is not allocated, create it
            directBuffer = new DirectBufferStorage11(mRenderer, usage);
            mDirectBuffers.insert(std::make_pair(usage, directBuffer));
        }
    
        DirectBufferStorage11 *latestBuffer = getLatestStorage();
        if (latestBuffer && latestBuffer->getDataRevision() > directBuffer->getDataRevision())
        {
            // if copyFromStorage returns true, the D3D buffer has been recreated
            // and we should update our serial
            if (directBuffer->copyFromStorage(latestBuffer, 0, latestBuffer->getSize(), 0))
            {
                updateSerial();
            }
            directBuffer->setDataRevision(latestBuffer->getDataRevision());
        }
    
        return directBuffer;
    }
    
    DirectBufferStorage11 *BufferStorage11::getLatestStorage() const
    {
        // Even though we iterate over all the direct buffers, it is expected that only
        // 1 or 2 will be present.
        DirectBufferStorage11 *latestStorage = NULL;
        DataRevision latestRevision = 0;
        for (auto it = mDirectBuffers.begin(); it != mDirectBuffers.end(); it++)
        {
            DirectBufferStorage11 *storage = it->second;
            if (storage->getDataRevision() > latestRevision)
            {
                latestStorage = storage;
                latestRevision = storage->getDataRevision();
            }
        }
    
        return latestStorage;
    }
    
    bool BufferStorage11::isMapped() const
    {
        return mIsMapped;
    }
    
    void *BufferStorage11::map(GLbitfield access)
    {
        ASSERT(!mIsMapped);
    
        D3D11_MAPPED_SUBRESOURCE mappedResource;
        HRESULT result;
        ID3D11DeviceContext *context = mRenderer->getDeviceContext();
        D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(access);
        UINT d3dMapFlag = ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0 ? D3D11_MAP_FLAG_DO_NOT_WAIT : 0);
        ID3D11Buffer *stagingBuffer = getStagingBuffer()->getD3DBuffer();
    
        result = context->Map(stagingBuffer, 0, d3dMapType, d3dMapFlag, &mappedResource);
        ASSERT(SUCCEEDED(result));
    
        mIsMapped = true;
    
        return mappedResource.pData;
    }
    
    void BufferStorage11::unmap()
    {
        ASSERT(mIsMapped);
    
        ID3D11DeviceContext *context = mRenderer->getDeviceContext();
        ID3D11Buffer *stagingBuffer = getStagingBuffer()->getD3DBuffer();
    
        context->Unmap(stagingBuffer, 0);
    
        mIsMapped = false;
    }
    
    DirectBufferStorage11 *BufferStorage11::getStagingBuffer()
    {
        return getStorage(BUFFER_USAGE_STAGING);
    }
    
    DirectBufferStorage11::DirectBufferStorage11(Renderer11 *renderer, BufferUsage usage)
        : mRenderer(renderer),
          mUsage(usage),
          mRevision(0),
          mDirectBuffer(NULL),
          mBufferSize(0)
    {
    }
    
    DirectBufferStorage11::~DirectBufferStorage11()
    {
        SafeRelease(mDirectBuffer);
    }
    
    BufferUsage DirectBufferStorage11::getUsage() const
    {
        return mUsage;
    }
    
    // Returns true if it recreates the direct buffer
    bool DirectBufferStorage11::copyFromStorage(DirectBufferStorage11 *source, size_t sourceOffset, size_t size, size_t destOffset)
    {
        ID3D11DeviceContext *context = mRenderer->getDeviceContext();
    
        size_t requiredSize = sourceOffset + size;
        bool createBuffer = !mDirectBuffer || mBufferSize < requiredSize;
    
        // (Re)initialize D3D buffer if needed
        if (createBuffer)
        {
            bool preserveData = (destOffset > 0);
            resize(source->getSize(), preserveData);
        }
    
        D3D11_BOX srcBox;
        srcBox.left = sourceOffset;
        srcBox.right = sourceOffset + size;
        srcBox.top = 0;
        srcBox.bottom = 1;
        srcBox.front = 0;
        srcBox.back = 1;
    
        context->CopySubresourceRegion(mDirectBuffer, 0, destOffset, 0, 0, source->getD3DBuffer(), 0, &srcBox);
    
        return createBuffer;
    }
    
    void DirectBufferStorage11::resize(size_t size, bool preserveData)
    {
        ID3D11Device *device = mRenderer->getDevice();
        ID3D11DeviceContext *context = mRenderer->getDeviceContext();
    
        D3D11_BUFFER_DESC bufferDesc;
        fillBufferDesc(&bufferDesc, mRenderer, mUsage, size);
    
        ID3D11Buffer *newBuffer;
        HRESULT result = device->CreateBuffer(&bufferDesc, NULL, &newBuffer);
    
        if (FAILED(result))
        {
            return gl::error(GL_OUT_OF_MEMORY);
        }
    
        if (mDirectBuffer && preserveData)
        {
            D3D11_BOX srcBox;
            srcBox.left = 0;
            srcBox.right = size;
            srcBox.top = 0;
            srcBox.bottom = 1;
            srcBox.front = 0;
            srcBox.back = 1;
    
            context->CopySubresourceRegion(newBuffer, 0, 0, 0, 0, mDirectBuffer, 0, &srcBox);
        }
    
        // No longer need the old buffer
        SafeRelease(mDirectBuffer);
        mDirectBuffer = newBuffer;
    
        mBufferSize = bufferDesc.ByteWidth;
    }
    
    void DirectBufferStorage11::fillBufferDesc(D3D11_BUFFER_DESC* bufferDesc, Renderer *renderer, BufferUsage usage, unsigned int bufferSize)
    {
        bufferDesc->ByteWidth = bufferSize;
        bufferDesc->MiscFlags = 0;
        bufferDesc->StructureByteStride = 0;
    
        switch (usage)
        {
          case BUFFER_USAGE_STAGING:
            bufferDesc->Usage = D3D11_USAGE_STAGING;
            bufferDesc->BindFlags = 0;
            bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
            break;
    
          case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
            bufferDesc->Usage = D3D11_USAGE_DEFAULT;
            bufferDesc->BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;
            bufferDesc->CPUAccessFlags = 0;
            break;
    
          case BUFFER_USAGE_INDEX:
            bufferDesc->Usage = D3D11_USAGE_DEFAULT;
            bufferDesc->BindFlags = D3D11_BIND_INDEX_BUFFER;
            bufferDesc->CPUAccessFlags = 0;
            break;
    
          case BUFFER_USAGE_PIXEL_UNPACK:
            bufferDesc->Usage = D3D11_USAGE_DEFAULT;
            bufferDesc->BindFlags = D3D11_BIND_SHADER_RESOURCE;
            bufferDesc->CPUAccessFlags = 0;
            break;
    
          case BUFFER_USAGE_UNIFORM:
            bufferDesc->Usage = D3D11_USAGE_DYNAMIC;
            bufferDesc->BindFlags = D3D11_BIND_CONSTANT_BUFFER;
            bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    
            // Constant buffers must be of a limited size, and aligned to 16 byte boundaries
            // For our purposes we ignore any buffer data past the maximum constant buffer size
            bufferDesc->ByteWidth = roundUp(bufferDesc->ByteWidth, 16u);
            bufferDesc->ByteWidth = std::min(bufferDesc->ByteWidth, renderer->getMaxUniformBufferSize());
            break;
    
        default:
            UNREACHABLE();
        }
    }
    
    }