| // |
| // Copyright 2017 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. |
| // |
| // ResourceManager11: |
| // Centralized point of allocation for all D3D11 Resources. |
| |
| #include "libANGLE/renderer/d3d/d3d11/ResourceManager11.h" |
| |
| #include "common/debug.h" |
| #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" |
| #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| |
| constexpr uint8_t kDebugInitTextureDataValue = 0x48; |
| constexpr FLOAT kDebugColorInitClearValue[4] = {0.3f, 0.5f, 0.7f, 0.5f}; |
| constexpr FLOAT kDebugDepthInitValue = 0.2f; |
| constexpr UINT8 kDebugStencilInitValue = 3; |
| |
| // A hard limit on buffer size. This works around a problem in the NVIDIA drivers where buffer sizes |
| // close to MAX_UINT would give undefined results. The limit of MAX_UINT/2 should be generous enough |
| // for almost any demanding application. |
| constexpr UINT kMaximumBufferSizeHardLimit = std::numeric_limits<UINT>::max() >> 1; |
| |
| uint64_t ComputeMippedMemoryUsage(unsigned int width, |
| unsigned int height, |
| unsigned int depth, |
| uint64_t pixelSize, |
| unsigned int mipLevels) |
| { |
| uint64_t sizeSum = 0; |
| |
| for (unsigned int level = 0; level < mipLevels; ++level) |
| { |
| unsigned int mipWidth = std::max(width >> level, 1u); |
| unsigned int mipHeight = std::max(height >> level, 1u); |
| unsigned int mipDepth = std::max(depth >> level, 1u); |
| sizeSum += static_cast<uint64_t>(mipWidth * mipHeight * mipDepth) * pixelSize; |
| } |
| |
| return sizeSum; |
| } |
| |
| uint64_t ComputeMemoryUsage(const D3D11_TEXTURE2D_DESC *desc) |
| { |
| ASSERT(desc); |
| uint64_t pixelBytes = |
| static_cast<uint64_t>(d3d11::GetDXGIFormatSizeInfo(desc->Format).pixelBytes); |
| return ComputeMippedMemoryUsage(desc->Width, desc->Height, 1, pixelBytes, desc->MipLevels); |
| } |
| |
| uint64_t ComputeMemoryUsage(const D3D11_TEXTURE3D_DESC *desc) |
| { |
| ASSERT(desc); |
| uint64_t pixelBytes = |
| static_cast<uint64_t>(d3d11::GetDXGIFormatSizeInfo(desc->Format).pixelBytes); |
| return ComputeMippedMemoryUsage(desc->Width, desc->Height, desc->Depth, pixelBytes, |
| desc->MipLevels); |
| } |
| |
| uint64_t ComputeMemoryUsage(const D3D11_BUFFER_DESC *desc) |
| { |
| ASSERT(desc); |
| return static_cast<uint64_t>(desc->ByteWidth); |
| } |
| |
| template <typename T> |
| uint64_t ComputeMemoryUsage(const T *desc) |
| { |
| return 0; |
| } |
| |
| template <ResourceType ResourceT> |
| uint64_t ComputeGenericMemoryUsage(ID3D11DeviceChild *genericResource) |
| { |
| auto *typedResource = static_cast<GetD3D11Type<ResourceT> *>(genericResource); |
| GetDescType<ResourceT> desc; |
| typedResource->GetDesc(&desc); |
| return ComputeMemoryUsage(&desc); |
| } |
| |
| uint64_t ComputeGenericMemoryUsage(ResourceType resourceType, ID3D11DeviceChild *resource) |
| { |
| switch (resourceType) |
| { |
| case ResourceType::Texture2D: |
| return ComputeGenericMemoryUsage<ResourceType::Texture2D>(resource); |
| case ResourceType::Texture3D: |
| return ComputeGenericMemoryUsage<ResourceType::Texture3D>(resource); |
| case ResourceType::Buffer: |
| return ComputeGenericMemoryUsage<ResourceType::Buffer>(resource); |
| |
| default: |
| return 0; |
| } |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_BLEND_DESC *desc, |
| void * /*initData*/, |
| ID3D11BlendState **blendState) |
| { |
| return device->CreateBlendState(desc, blendState); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_BUFFER_DESC *desc, |
| const D3D11_SUBRESOURCE_DATA *initData, |
| ID3D11Buffer **buffer) |
| { |
| // Force buffers to be limited to a fixed max size. |
| if (desc->ByteWidth > kMaximumBufferSizeHardLimit) |
| { |
| return E_OUTOFMEMORY; |
| } |
| |
| return device->CreateBuffer(desc, initData, buffer); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const ShaderData *desc, |
| void * /*initData*/, |
| ID3D11ComputeShader **resourceOut) |
| { |
| return device->CreateComputeShader(desc->get(), desc->size(), nullptr, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_DEPTH_STENCIL_DESC *desc, |
| void * /*initData*/, |
| ID3D11DepthStencilState **resourceOut) |
| { |
| return device->CreateDepthStencilState(desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_DEPTH_STENCIL_VIEW_DESC *desc, |
| ID3D11Resource *resource, |
| ID3D11DepthStencilView **resourceOut) |
| { |
| return device->CreateDepthStencilView(resource, desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const ShaderData *desc, |
| const std::vector<D3D11_SO_DECLARATION_ENTRY> *initData, |
| ID3D11GeometryShader **resourceOut) |
| { |
| if (initData) |
| { |
| return device->CreateGeometryShaderWithStreamOutput( |
| desc->get(), desc->size(), initData->data(), static_cast<UINT>(initData->size()), |
| nullptr, 0, 0, nullptr, resourceOut); |
| } |
| else |
| { |
| return device->CreateGeometryShader(desc->get(), desc->size(), nullptr, resourceOut); |
| } |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const InputElementArray *desc, |
| const ShaderData *initData, |
| ID3D11InputLayout **resourceOut) |
| { |
| return device->CreateInputLayout(desc->get(), static_cast<UINT>(desc->size()), initData->get(), |
| initData->size(), resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const ShaderData *desc, |
| void * /*initData*/, |
| ID3D11PixelShader **resourceOut) |
| { |
| return device->CreatePixelShader(desc->get(), desc->size(), nullptr, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_QUERY_DESC *desc, |
| void * /*initData*/, |
| ID3D11Query **resourceOut) |
| { |
| return device->CreateQuery(desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_RASTERIZER_DESC *desc, |
| void * /*initData*/, |
| ID3D11RasterizerState **rasterizerState) |
| { |
| return device->CreateRasterizerState(desc, rasterizerState); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_RENDER_TARGET_VIEW_DESC *desc, |
| ID3D11Resource *resource, |
| ID3D11RenderTargetView **renderTargetView) |
| { |
| return device->CreateRenderTargetView(resource, desc, renderTargetView); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_SAMPLER_DESC *desc, |
| void * /*initData*/, |
| ID3D11SamplerState **resourceOut) |
| { |
| return device->CreateSamplerState(desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_SHADER_RESOURCE_VIEW_DESC *desc, |
| ID3D11Resource *resource, |
| ID3D11ShaderResourceView **resourceOut) |
| { |
| return device->CreateShaderResourceView(resource, desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_UNORDERED_ACCESS_VIEW_DESC *desc, |
| ID3D11Resource *resource, |
| ID3D11UnorderedAccessView **resourceOut) |
| { |
| return device->CreateUnorderedAccessView(resource, desc, resourceOut); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_TEXTURE2D_DESC *desc, |
| const D3D11_SUBRESOURCE_DATA *initData, |
| ID3D11Texture2D **texture) |
| { |
| return device->CreateTexture2D(desc, initData, texture); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const D3D11_TEXTURE3D_DESC *desc, |
| const D3D11_SUBRESOURCE_DATA *initData, |
| ID3D11Texture3D **texture) |
| { |
| return device->CreateTexture3D(desc, initData, texture); |
| } |
| |
| HRESULT CreateResource(ID3D11Device *device, |
| const ShaderData *desc, |
| void * /*initData*/, |
| ID3D11VertexShader **resourceOut) |
| { |
| return device->CreateVertexShader(desc->get(), desc->size(), nullptr, resourceOut); |
| } |
| |
| DXGI_FORMAT GetTypedDepthStencilFormat(DXGI_FORMAT dxgiFormat) |
| { |
| switch (dxgiFormat) |
| { |
| case DXGI_FORMAT_R16_TYPELESS: |
| return DXGI_FORMAT_D16_UNORM; |
| case DXGI_FORMAT_R24G8_TYPELESS: |
| return DXGI_FORMAT_D24_UNORM_S8_UINT; |
| case DXGI_FORMAT_R32_TYPELESS: |
| return DXGI_FORMAT_D32_FLOAT; |
| case DXGI_FORMAT_R32G8X24_TYPELESS: |
| return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; |
| default: |
| return dxgiFormat; |
| } |
| } |
| |
| template <typename DescT, typename ResourceT> |
| angle::Result ClearResource(d3d::Context *context, |
| Renderer11 *renderer, |
| const DescT *desc, |
| ResourceT *texture) |
| { |
| // No-op. |
| return angle::Result::Continue; |
| } |
| |
| template <> |
| angle::Result ClearResource(d3d::Context *context, |
| Renderer11 *renderer, |
| const D3D11_TEXTURE2D_DESC *desc, |
| ID3D11Texture2D *texture) |
| { |
| ID3D11DeviceContext *deviceContext = renderer->getDeviceContext(); |
| |
| if ((desc->BindFlags & D3D11_BIND_DEPTH_STENCIL) != 0) |
| { |
| D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; |
| dsvDesc.Flags = 0; |
| dsvDesc.Format = GetTypedDepthStencilFormat(desc->Format); |
| |
| const auto &format = d3d11_angle::GetFormat(dsvDesc.Format); |
| UINT clearFlags = (format.depthBits > 0 ? D3D11_CLEAR_DEPTH : 0) | |
| (format.stencilBits > 0 ? D3D11_CLEAR_STENCIL : 0); |
| |
| // Must process each mip level individually. |
| for (UINT mipLevel = 0; mipLevel < desc->MipLevels; ++mipLevel) |
| { |
| if (desc->SampleDesc.Count == 0) |
| { |
| dsvDesc.Texture2D.MipSlice = mipLevel; |
| dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; |
| } |
| else |
| { |
| dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; |
| } |
| |
| d3d11::DepthStencilView dsv; |
| ANGLE_TRY(renderer->allocateResource(context, dsvDesc, texture, &dsv)); |
| |
| deviceContext->ClearDepthStencilView(dsv.get(), clearFlags, kDebugDepthInitValue, |
| kDebugStencilInitValue); |
| } |
| } |
| else |
| { |
| ASSERT((desc->BindFlags & D3D11_BIND_RENDER_TARGET) != 0); |
| d3d11::RenderTargetView rtv; |
| ANGLE_TRY(renderer->allocateResourceNoDesc(context, texture, &rtv)); |
| |
| deviceContext->ClearRenderTargetView(rtv.get(), kDebugColorInitClearValue); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| template <> |
| angle::Result ClearResource(d3d::Context *context, |
| Renderer11 *renderer, |
| const D3D11_TEXTURE3D_DESC *desc, |
| ID3D11Texture3D *texture) |
| { |
| ID3D11DeviceContext *deviceContext = renderer->getDeviceContext(); |
| |
| ASSERT((desc->BindFlags & D3D11_BIND_DEPTH_STENCIL) == 0); |
| ASSERT((desc->BindFlags & D3D11_BIND_RENDER_TARGET) != 0); |
| |
| d3d11::RenderTargetView rtv; |
| ANGLE_TRY(renderer->allocateResourceNoDesc(context, texture, &rtv)); |
| |
| deviceContext->ClearRenderTargetView(rtv.get(), kDebugColorInitClearValue); |
| return angle::Result::Continue; |
| } |
| |
| #define ANGLE_RESOURCE_STRINGIFY_OP(NAME, RESTYPE, D3D11TYPE, DESCTYPE, INITDATATYPE) \ |
| "Error allocating " #RESTYPE, |
| |
| constexpr std::array<const char *, NumResourceTypes> kResourceTypeErrors = { |
| {ANGLE_RESOURCE_TYPE_OP(Stringify, ANGLE_RESOURCE_STRINGIFY_OP)}}; |
| static_assert(kResourceTypeErrors[NumResourceTypes - 1] != nullptr, |
| "All members must be initialized."); |
| |
| } // anonymous namespace |
| |
| // ResourceManager11 Implementation. |
| ResourceManager11::ResourceManager11() : mInitializeAllocations(false) |
| { |
| for (auto &count : mAllocatedResourceCounts) |
| { |
| count = 0; |
| } |
| for (auto &memorySize : mAllocatedResourceDeviceMemory) |
| { |
| memorySize = 0; |
| } |
| } |
| |
| ResourceManager11::~ResourceManager11() |
| { |
| for (size_t count : mAllocatedResourceCounts) |
| { |
| ASSERT(count == 0); |
| } |
| |
| for (uint64_t memorySize : mAllocatedResourceDeviceMemory) |
| { |
| ASSERT(memorySize == 0); |
| } |
| } |
| |
| template <typename T> |
| angle::Result ResourceManager11::allocate(d3d::Context *context, |
| Renderer11 *renderer, |
| const GetDescFromD3D11<T> *desc, |
| GetInitDataFromD3D11<T> *initData, |
| Resource11<T> *resourceOut) |
| { |
| ID3D11Device *device = renderer->getDevice(); |
| T *resource = nullptr; |
| |
| GetInitDataFromD3D11<T> *shadowInitData = initData; |
| if (!shadowInitData && mInitializeAllocations) |
| { |
| shadowInitData = createInitDataIfNeeded<T>(desc); |
| } |
| |
| HRESULT hr = CreateResource(device, desc, shadowInitData, &resource); |
| ANGLE_TRY_HR(context, hr, kResourceTypeErrors[ResourceTypeIndex<T>()]); |
| |
| if (!shadowInitData && mInitializeAllocations) |
| { |
| ANGLE_TRY(ClearResource(context, renderer, desc, resource)); |
| } |
| |
| ASSERT(resource); |
| incrResource(GetResourceTypeFromD3D11<T>(), ComputeMemoryUsage(desc)); |
| *resourceOut = std::move(Resource11<T>(resource, this)); |
| return angle::Result::Continue; |
| } |
| |
| void ResourceManager11::incrResource(ResourceType resourceType, uint64_t memorySize) |
| { |
| size_t typeIndex = ResourceTypeIndex(resourceType); |
| |
| mAllocatedResourceCounts[typeIndex]++; |
| mAllocatedResourceDeviceMemory[typeIndex] += memorySize; |
| |
| // This checks for integer overflow. |
| ASSERT(mAllocatedResourceCounts[typeIndex] > 0); |
| ASSERT(mAllocatedResourceDeviceMemory[typeIndex] >= memorySize); |
| } |
| |
| void ResourceManager11::decrResource(ResourceType resourceType, uint64_t memorySize) |
| { |
| size_t typeIndex = ResourceTypeIndex(resourceType); |
| |
| ASSERT(mAllocatedResourceCounts[typeIndex] > 0); |
| mAllocatedResourceCounts[typeIndex]--; |
| ASSERT(mAllocatedResourceDeviceMemory[typeIndex] >= memorySize); |
| mAllocatedResourceDeviceMemory[typeIndex] -= memorySize; |
| } |
| |
| void ResourceManager11::onReleaseGeneric(ResourceType resourceType, ID3D11DeviceChild *resource) |
| { |
| ASSERT(resource); |
| decrResource(resourceType, ComputeGenericMemoryUsage(resourceType, resource)); |
| } |
| |
| template <> |
| const D3D11_SUBRESOURCE_DATA *ResourceManager11::createInitDataIfNeeded<ID3D11Texture2D>( |
| const D3D11_TEXTURE2D_DESC *desc) |
| { |
| ASSERT(desc); |
| |
| if ((desc->BindFlags & (D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_RENDER_TARGET)) != 0) |
| { |
| // This will be done using ClearView methods. |
| return nullptr; |
| } |
| |
| size_t requiredSize = static_cast<size_t>(ComputeMemoryUsage(desc)); |
| if (mZeroMemory.size() < requiredSize) |
| { |
| mZeroMemory.resize(requiredSize); |
| mZeroMemory.fill(kDebugInitTextureDataValue); |
| } |
| |
| const auto &formatSizeInfo = d3d11::GetDXGIFormatSizeInfo(desc->Format); |
| |
| UINT subresourceCount = desc->MipLevels * desc->ArraySize; |
| if (mShadowInitData.size() < subresourceCount) |
| { |
| mShadowInitData.resize(subresourceCount); |
| } |
| |
| for (UINT mipLevel = 0; mipLevel < desc->MipLevels; ++mipLevel) |
| { |
| for (UINT arrayIndex = 0; arrayIndex < desc->ArraySize; ++arrayIndex) |
| { |
| UINT subresourceIndex = D3D11CalcSubresource(mipLevel, arrayIndex, desc->MipLevels); |
| D3D11_SUBRESOURCE_DATA *data = &mShadowInitData[subresourceIndex]; |
| |
| UINT levelWidth = std::max(desc->Width >> mipLevel, 1u); |
| UINT levelHeight = std::max(desc->Height >> mipLevel, 1u); |
| |
| data->SysMemPitch = levelWidth * formatSizeInfo.pixelBytes; |
| data->SysMemSlicePitch = data->SysMemPitch * levelHeight; |
| data->pSysMem = mZeroMemory.data(); |
| } |
| } |
| |
| return mShadowInitData.data(); |
| } |
| |
| template <> |
| const D3D11_SUBRESOURCE_DATA *ResourceManager11::createInitDataIfNeeded<ID3D11Texture3D>( |
| const D3D11_TEXTURE3D_DESC *desc) |
| { |
| ASSERT(desc); |
| |
| if ((desc->BindFlags & D3D11_BIND_RENDER_TARGET) != 0) |
| { |
| // This will be done using ClearView methods. |
| return nullptr; |
| } |
| |
| size_t requiredSize = static_cast<size_t>(ComputeMemoryUsage(desc)); |
| if (mZeroMemory.size() < requiredSize) |
| { |
| mZeroMemory.resize(requiredSize); |
| mZeroMemory.fill(kDebugInitTextureDataValue); |
| } |
| |
| const auto &formatSizeInfo = d3d11::GetDXGIFormatSizeInfo(desc->Format); |
| |
| UINT subresourceCount = desc->MipLevels; |
| if (mShadowInitData.size() < subresourceCount) |
| { |
| mShadowInitData.resize(subresourceCount); |
| } |
| |
| for (UINT mipLevel = 0; mipLevel < desc->MipLevels; ++mipLevel) |
| { |
| UINT subresourceIndex = D3D11CalcSubresource(mipLevel, 0, desc->MipLevels); |
| D3D11_SUBRESOURCE_DATA *data = &mShadowInitData[subresourceIndex]; |
| |
| UINT levelWidth = std::max(desc->Width >> mipLevel, 1u); |
| UINT levelHeight = std::max(desc->Height >> mipLevel, 1u); |
| |
| data->SysMemPitch = levelWidth * formatSizeInfo.pixelBytes; |
| data->SysMemSlicePitch = data->SysMemPitch * levelHeight; |
| data->pSysMem = mZeroMemory.data(); |
| } |
| |
| return mShadowInitData.data(); |
| } |
| |
| template <typename T> |
| GetInitDataFromD3D11<T> *ResourceManager11::createInitDataIfNeeded(const GetDescFromD3D11<T> *desc) |
| { |
| // No-op. |
| return nullptr; |
| } |
| |
| void ResourceManager11::setAllocationsInitialized(bool initialize) |
| { |
| mInitializeAllocations = initialize; |
| } |
| |
| #define ANGLE_INSTANTIATE_OP(NAME, RESTYPE, D3D11TYPE, DESCTYPE, INITDATATYPE) \ |
| \ |
| template angle::Result ResourceManager11::allocate( \ |
| d3d::Context *, Renderer11 *, const DESCTYPE *, INITDATATYPE *, Resource11<D3D11TYPE> *); |
| |
| ANGLE_RESOURCE_TYPE_OP(Instantitate, ANGLE_INSTANTIATE_OP) |
| } // namespace rx |