// // 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; 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(mipWidth * mipHeight * mipDepth) * pixelSize; } return sizeSum; } uint64_t ComputeMemoryUsage(const D3D11_TEXTURE2D_DESC *desc) { ASSERT(desc); uint64_t pixelBytes = static_cast(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(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(desc->ByteWidth); } template uint64_t ComputeMemoryUsage(const T *desc) { return 0; } template uint64_t ComputeGenericMemoryUsage(ID3D11DeviceChild *genericResource) { auto *typedResource = static_cast *>(genericResource); GetDescType desc; typedResource->GetDesc(&desc); return ComputeMemoryUsage(&desc); } uint64_t ComputeGenericMemoryUsage(ResourceType resourceType, ID3D11DeviceChild *resource) { switch (resourceType) { case ResourceType::Texture2D: return ComputeGenericMemoryUsage(resource); case ResourceType::Texture3D: return ComputeGenericMemoryUsage(resource); case ResourceType::Buffer: return ComputeGenericMemoryUsage(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) { 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 *initData, ID3D11GeometryShader **resourceOut) { if (initData) { return device->CreateGeometryShaderWithStreamOutput( desc->get(), desc->size(), initData->data(), static_cast(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(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_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 gl::Error ClearResource(Renderer11 *renderer, const DescT *desc, ResourceT *texture) { // No-op. return gl::NoError(); } template <> gl::Error ClearResource(Renderer11 *renderer, const D3D11_TEXTURE2D_DESC *desc, ID3D11Texture2D *texture) { ID3D11DeviceContext *context = 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(dsvDesc, texture, &dsv)); context->ClearDepthStencilView(dsv.get(), clearFlags, kDebugDepthInitValue, kDebugStencilInitValue); } } else { ASSERT((desc->BindFlags & D3D11_BIND_RENDER_TARGET) != 0); d3d11::RenderTargetView rtv; ANGLE_TRY(renderer->allocateResourceNoDesc(texture, &rtv)); context->ClearRenderTargetView(rtv.get(), kDebugColorInitClearValue); } return gl::NoError(); } template <> gl::Error ClearResource(Renderer11 *renderer, const D3D11_TEXTURE3D_DESC *desc, ID3D11Texture3D *texture) { ID3D11DeviceContext *context = 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(texture, &rtv)); context->ClearRenderTargetView(rtv.get(), kDebugColorInitClearValue); return gl::NoError(); } #define ANGLE_RESOURCE_STRINGIFY_OP(NAME, RESTYPE, D3D11TYPE, DESCTYPE, INITDATATYPE) #RESTYPE, constexpr std::array kResourceTypeNames = { {ANGLE_RESOURCE_TYPE_OP(Stringify, ANGLE_RESOURCE_STRINGIFY_OP)}}; static_assert(kResourceTypeNames[NumResourceTypes - 1] != nullptr, "All members must be initialized."); } // anonymous namespace // ResourceManager11 Implementation. ResourceManager11::ResourceManager11() : mInitializeAllocations(false), mAllocatedResourceCounts({{}}), mAllocatedResourceDeviceMemory({{}}) { } ResourceManager11::~ResourceManager11() { for (size_t count : mAllocatedResourceCounts) { ASSERT(count == 0); } for (uint64_t memorySize : mAllocatedResourceDeviceMemory) { ASSERT(memorySize == 0); } } template gl::Error ResourceManager11::allocate(Renderer11 *renderer, const GetDescFromD3D11 *desc, GetInitDataFromD3D11 *initData, Resource11 *resourceOut) { ID3D11Device *device = renderer->getDevice(); T *resource = nullptr; GetInitDataFromD3D11 *shadowInitData = initData; if (!shadowInitData && mInitializeAllocations) { shadowInitData = createInitDataIfNeeded(desc); } HRESULT hr = CreateResource(device, desc, shadowInitData, &resource); if (FAILED(hr)) { ASSERT(!resource); if (d3d11::isDeviceLostError(hr)) { renderer->notifyDeviceLost(); } return gl::OutOfMemory() << "Error allocating " << std::string(kResourceTypeNames[ResourceTypeIndex()]) << ". " << gl::FmtHR(hr); } if (!shadowInitData && mInitializeAllocations) { ANGLE_TRY(ClearResource(renderer, desc, resource)); } ASSERT(resource); incrResource(GetResourceTypeFromD3D11(), ComputeMemoryUsage(desc)); *resourceOut = std::move(Resource11(resource, this)); return gl::NoError(); } 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( 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(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( 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(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 GetInitDataFromD3D11 *ResourceManager11::createInitDataIfNeeded(const GetDescFromD3D11 *desc) { // No-op. return nullptr; } void ResourceManager11::setAllocationsInitialized(bool initialize) { mInitializeAllocations = initialize; } #define ANGLE_INSTANTIATE_OP(NAME, RESTYPE, D3D11TYPE, DESCTYPE, INITDATATYPE) \ \ template \ gl::Error \ ResourceManager11::allocate(Renderer11 *, const DESCTYPE *, INITDATATYPE *, \ Resource11 *); ANGLE_RESOURCE_TYPE_OP(Instantitate, ANGLE_INSTANTIATE_OP) } // namespace rx