diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp | 4454 |
1 files changed, 4454 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp new file mode 100644 index 0000000000..fdd264e92b --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp @@ -0,0 +1,4454 @@ +// +// Copyright 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. +// + +// Renderer11.cpp: Implements a back-end specific class for the D3D11 renderer. + +#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" + +#include <EGL/eglext.h> +#include <versionhelpers.h> +#include <sstream> + +#include "anglebase/no_destructor.h" +#include "common/tls.h" +#include "common/utilities.h" +#include "libANGLE/Buffer.h" +#include "libANGLE/Context.h" +#include "libANGLE/Display.h" +#include "libANGLE/Framebuffer.h" +#include "libANGLE/FramebufferAttachment.h" +#include "libANGLE/Program.h" +#include "libANGLE/State.h" +#include "libANGLE/Surface.h" +#include "libANGLE/formatutils.h" +#include "libANGLE/histogram_macros.h" +#include "libANGLE/renderer/d3d/CompilerD3D.h" +#include "libANGLE/renderer/d3d/DeviceD3D.h" +#include "libANGLE/renderer/d3d/DisplayD3D.h" +#include "libANGLE/renderer/d3d/FramebufferD3D.h" +#include "libANGLE/renderer/d3d/IndexDataManager.h" +#include "libANGLE/renderer/d3d/RenderbufferD3D.h" +#include "libANGLE/renderer/d3d/ShaderD3D.h" +#include "libANGLE/renderer/d3d/SurfaceD3D.h" +#include "libANGLE/renderer/d3d/TextureD3D.h" +#include "libANGLE/renderer/d3d/VertexDataManager.h" +#include "libANGLE/renderer/d3d/d3d11/Blit11.h" +#include "libANGLE/renderer/d3d/d3d11/Buffer11.h" +#include "libANGLE/renderer/d3d/d3d11/Clear11.h" +#include "libANGLE/renderer/d3d/d3d11/Context11.h" +#include "libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h" +#include "libANGLE/renderer/d3d/d3d11/Fence11.h" +#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h" +#include "libANGLE/renderer/d3d/d3d11/Image11.h" +#include "libANGLE/renderer/d3d/d3d11/IndexBuffer11.h" +#include "libANGLE/renderer/d3d/d3d11/PixelTransfer11.h" +#include "libANGLE/renderer/d3d/d3d11/Program11.h" +#include "libANGLE/renderer/d3d/d3d11/Query11.h" +#include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" +#include "libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h" +#include "libANGLE/renderer/d3d/d3d11/StreamProducerD3DTexture.h" +#include "libANGLE/renderer/d3d/d3d11/SwapChain11.h" +#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h" +#include "libANGLE/renderer/d3d/d3d11/TransformFeedback11.h" +#include "libANGLE/renderer/d3d/d3d11/Trim11.h" +#include "libANGLE/renderer/d3d/d3d11/VertexArray11.h" +#include "libANGLE/renderer/d3d/d3d11/VertexBuffer11.h" +#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" +#include "libANGLE/renderer/d3d/driver_utils_d3d.h" +#include "libANGLE/renderer/driver_utils.h" +#include "libANGLE/renderer/dxgi_support_table.h" +#include "libANGLE/renderer/renderer_utils.h" +#include "libANGLE/trace.h" + +#ifdef ANGLE_ENABLE_WINDOWS_UWP +# include "libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h" +#else +# include "libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.h" +#endif + +#ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW +# include "libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h" +#endif + +// Enable ANGLE_SKIP_DXGI_1_2_CHECK if there is not a possibility of using cross-process +// HWNDs or the Windows 7 Platform Update (KB2670838) is expected to be installed. +#ifndef ANGLE_SKIP_DXGI_1_2_CHECK +# define ANGLE_SKIP_DXGI_1_2_CHECK 0 +#endif + +namespace rx +{ + +namespace +{ + +enum +{ + MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 = 16 +}; + +enum ANGLEFeatureLevel +{ + ANGLE_FEATURE_LEVEL_INVALID, + ANGLE_FEATURE_LEVEL_9_3, + ANGLE_FEATURE_LEVEL_10_0, + ANGLE_FEATURE_LEVEL_10_1, + ANGLE_FEATURE_LEVEL_11_0, + ANGLE_FEATURE_LEVEL_11_1, + NUM_ANGLE_FEATURE_LEVELS +}; + +ANGLEFeatureLevel GetANGLEFeatureLevel(D3D_FEATURE_LEVEL d3dFeatureLevel) +{ + switch (d3dFeatureLevel) + { + case D3D_FEATURE_LEVEL_9_3: + return ANGLE_FEATURE_LEVEL_9_3; + case D3D_FEATURE_LEVEL_10_0: + return ANGLE_FEATURE_LEVEL_10_0; + case D3D_FEATURE_LEVEL_10_1: + return ANGLE_FEATURE_LEVEL_10_1; + case D3D_FEATURE_LEVEL_11_0: + return ANGLE_FEATURE_LEVEL_11_0; + case D3D_FEATURE_LEVEL_11_1: + return ANGLE_FEATURE_LEVEL_11_1; + default: + return ANGLE_FEATURE_LEVEL_INVALID; + } +} + +void SetLineLoopIndices(GLuint *dest, size_t count) +{ + for (size_t i = 0; i < count; i++) + { + dest[i] = static_cast<GLuint>(i); + } + dest[count] = 0; +} + +template <typename T> +void CopyLineLoopIndices(const void *indices, GLuint *dest, size_t count) +{ + const T *srcPtr = static_cast<const T *>(indices); + for (size_t i = 0; i < count; ++i) + { + dest[i] = static_cast<GLuint>(srcPtr[i]); + } + dest[count] = static_cast<GLuint>(srcPtr[0]); +} + +void SetTriangleFanIndices(GLuint *destPtr, size_t numTris) +{ + for (size_t i = 0; i < numTris; i++) + { + destPtr[i * 3 + 0] = 0; + destPtr[i * 3 + 1] = static_cast<GLuint>(i) + 1; + destPtr[i * 3 + 2] = static_cast<GLuint>(i) + 2; + } +} + +void GetLineLoopIndices(const void *indices, + gl::DrawElementsType indexType, + GLuint count, + bool usePrimitiveRestartFixedIndex, + std::vector<GLuint> *bufferOut) +{ + if (indexType != gl::DrawElementsType::InvalidEnum && usePrimitiveRestartFixedIndex) + { + size_t indexCount = GetLineLoopWithRestartIndexCount(indexType, count, + static_cast<const uint8_t *>(indices)); + bufferOut->resize(indexCount); + switch (indexType) + { + case gl::DrawElementsType::UnsignedByte: + CopyLineLoopIndicesWithRestart<GLubyte, GLuint>( + count, static_cast<const uint8_t *>(indices), + reinterpret_cast<uint8_t *>(bufferOut->data())); + break; + case gl::DrawElementsType::UnsignedShort: + CopyLineLoopIndicesWithRestart<GLushort, GLuint>( + count, static_cast<const uint8_t *>(indices), + reinterpret_cast<uint8_t *>(bufferOut->data())); + break; + case gl::DrawElementsType::UnsignedInt: + CopyLineLoopIndicesWithRestart<GLuint, GLuint>( + count, static_cast<const uint8_t *>(indices), + reinterpret_cast<uint8_t *>(bufferOut->data())); + break; + default: + UNREACHABLE(); + break; + } + return; + } + + // For non-primitive-restart draws, the index count is static. + bufferOut->resize(static_cast<size_t>(count) + 1); + + switch (indexType) + { + // Non-indexed draw + case gl::DrawElementsType::InvalidEnum: + SetLineLoopIndices(&(*bufferOut)[0], count); + break; + case gl::DrawElementsType::UnsignedByte: + CopyLineLoopIndices<GLubyte>(indices, &(*bufferOut)[0], count); + break; + case gl::DrawElementsType::UnsignedShort: + CopyLineLoopIndices<GLushort>(indices, &(*bufferOut)[0], count); + break; + case gl::DrawElementsType::UnsignedInt: + CopyLineLoopIndices<GLuint>(indices, &(*bufferOut)[0], count); + break; + default: + UNREACHABLE(); + break; + } +} + +template <typename T> +void CopyTriangleFanIndices(const void *indices, GLuint *destPtr, size_t numTris) +{ + const T *srcPtr = static_cast<const T *>(indices); + + for (size_t i = 0; i < numTris; i++) + { + destPtr[i * 3 + 0] = static_cast<GLuint>(srcPtr[0]); + destPtr[i * 3 + 1] = static_cast<GLuint>(srcPtr[i + 1]); + destPtr[i * 3 + 2] = static_cast<GLuint>(srcPtr[i + 2]); + } +} + +template <typename T> +void CopyTriangleFanIndicesWithRestart(const void *indices, + GLuint indexCount, + gl::DrawElementsType indexType, + std::vector<GLuint> *bufferOut) +{ + GLuint restartIndex = gl::GetPrimitiveRestartIndex(indexType); + GLuint d3dRestartIndex = gl::GetPrimitiveRestartIndex(gl::DrawElementsType::UnsignedInt); + const T *srcPtr = static_cast<const T *>(indices); + Optional<GLuint> vertexA; + Optional<GLuint> vertexB; + + bufferOut->clear(); + + for (size_t indexIdx = 0; indexIdx < indexCount; ++indexIdx) + { + GLuint value = static_cast<GLuint>(srcPtr[indexIdx]); + + if (value == restartIndex) + { + bufferOut->push_back(d3dRestartIndex); + vertexA.reset(); + vertexB.reset(); + } + else + { + if (!vertexA.valid()) + { + vertexA = value; + } + else if (!vertexB.valid()) + { + vertexB = value; + } + else + { + bufferOut->push_back(vertexA.value()); + bufferOut->push_back(vertexB.value()); + bufferOut->push_back(value); + vertexB = value; + } + } + } +} + +void GetTriFanIndices(const void *indices, + gl::DrawElementsType indexType, + GLuint count, + bool usePrimitiveRestartFixedIndex, + std::vector<GLuint> *bufferOut) +{ + if (indexType != gl::DrawElementsType::InvalidEnum && usePrimitiveRestartFixedIndex) + { + switch (indexType) + { + case gl::DrawElementsType::UnsignedByte: + CopyTriangleFanIndicesWithRestart<GLubyte>(indices, count, indexType, bufferOut); + break; + case gl::DrawElementsType::UnsignedShort: + CopyTriangleFanIndicesWithRestart<GLushort>(indices, count, indexType, bufferOut); + break; + case gl::DrawElementsType::UnsignedInt: + CopyTriangleFanIndicesWithRestart<GLuint>(indices, count, indexType, bufferOut); + break; + default: + UNREACHABLE(); + break; + } + return; + } + + // For non-primitive-restart draws, the index count is static. + GLuint numTris = count - 2; + bufferOut->resize(numTris * 3); + + switch (indexType) + { + // Non-indexed draw + case gl::DrawElementsType::InvalidEnum: + SetTriangleFanIndices(&(*bufferOut)[0], numTris); + break; + case gl::DrawElementsType::UnsignedByte: + CopyTriangleFanIndices<GLubyte>(indices, &(*bufferOut)[0], numTris); + break; + case gl::DrawElementsType::UnsignedShort: + CopyTriangleFanIndices<GLushort>(indices, &(*bufferOut)[0], numTris); + break; + case gl::DrawElementsType::UnsignedInt: + CopyTriangleFanIndices<GLuint>(indices, &(*bufferOut)[0], numTris); + break; + default: + UNREACHABLE(); + break; + } +} + +bool IsArrayRTV(ID3D11RenderTargetView *rtv) +{ + D3D11_RENDER_TARGET_VIEW_DESC desc; + rtv->GetDesc(&desc); + if (desc.ViewDimension == D3D11_RTV_DIMENSION_TEXTURE1DARRAY && + desc.Texture1DArray.ArraySize > 1) + return true; + if (desc.ViewDimension == D3D11_RTV_DIMENSION_TEXTURE2DARRAY && + desc.Texture2DArray.ArraySize > 1) + return true; + if (desc.ViewDimension == D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY && + desc.Texture2DMSArray.ArraySize > 1) + return true; + return false; +} + +GLsizei GetAdjustedInstanceCount(const ProgramD3D *program, GLsizei instanceCount) +{ + if (!program->getState().usesMultiview()) + { + return instanceCount; + } + if (instanceCount == 0) + { + return program->getState().getNumViews(); + } + return program->getState().getNumViews() * instanceCount; +} + +const uint32_t ScratchMemoryBufferLifetime = 1000; + +void PopulateFormatDeviceCaps(ID3D11Device *device, + DXGI_FORMAT format, + UINT *outSupport, + UINT *outMaxSamples) +{ + if (FAILED(device->CheckFormatSupport(format, outSupport))) + { + *outSupport = 0; + } + + *outMaxSamples = 0; + for (UINT sampleCount = 2; sampleCount <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; sampleCount *= 2) + { + UINT qualityCount = 0; + if (FAILED(device->CheckMultisampleQualityLevels(format, sampleCount, &qualityCount)) || + qualityCount == 0) + { + break; + } + + *outMaxSamples = sampleCount; + } +} + +angle::Result GetTextureD3DResourceFromStorageOrImage(const gl::Context *context, + TextureD3D *texture, + const gl::ImageIndex &index, + const TextureHelper11 **outResource, + UINT *outSubresource) +{ + // If the storage exists, use it. Otherwise, copy directly from the images to avoid + // allocating a new storage. + if (texture->hasStorage()) + { + TextureStorage *storage = nullptr; + ANGLE_TRY(texture->getNativeTexture(context, &storage)); + + TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage); + ANGLE_TRY(storage11->getResource(context, outResource)); + ANGLE_TRY(storage11->getSubresourceIndex(context, index, outSubresource)); + } + else + { + ImageD3D *image = texture->getImage(index); + Image11 *image11 = GetAs<Image11>(image); + ANGLE_TRY(image11->getStagingTexture(context, outResource, outSubresource)); + } + + return angle::Result::Continue; +} + +} // anonymous namespace + +Renderer11DeviceCaps::Renderer11DeviceCaps() = default; + +Renderer11::Renderer11(egl::Display *display) + : RendererD3D(display), + mCreateDebugDevice(false), + mStateCache(), + mStateManager(this), + mLastHistogramUpdateTime( + ANGLEPlatformCurrent()->monotonicallyIncreasingTime(ANGLEPlatformCurrent())), + mDebug(nullptr), + mScratchMemoryBuffer(ScratchMemoryBufferLifetime) +{ + mLineLoopIB = nullptr; + mTriangleFanIB = nullptr; + + mBlit = nullptr; + mPixelTransfer = nullptr; + + mClear = nullptr; + + mTrim = nullptr; + + mRenderer11DeviceCaps.supportsClearView = false; + mRenderer11DeviceCaps.supportsConstantBufferOffsets = false; + mRenderer11DeviceCaps.supportsVpRtIndexWriteFromVertexShader = false; + mRenderer11DeviceCaps.supportsDXGI1_2 = false; + mRenderer11DeviceCaps.allowES3OnFL10_0 = false; + mRenderer11DeviceCaps.supportsTypedUAVLoadAdditionalFormats = false; + mRenderer11DeviceCaps.supportsRasterizerOrderViews = false; + mRenderer11DeviceCaps.B5G6R5support = 0; + mRenderer11DeviceCaps.B4G4R4A4support = 0; + mRenderer11DeviceCaps.B5G5R5A1support = 0; + + mD3d11Module = nullptr; + mD3d12Module = nullptr; + mDxgiModule = nullptr; + mDCompModule = nullptr; + mCreatedWithDeviceEXT = false; + + mDevice = nullptr; + mDevice1 = nullptr; + mDeviceContext = nullptr; + mDeviceContext1 = nullptr; + mDeviceContext3 = nullptr; + mDxgiAdapter = nullptr; + mDxgiFactory = nullptr; + + ZeroMemory(&mAdapterDescription, sizeof(mAdapterDescription)); + + const auto &attributes = mDisplay->getAttributeMap(); + + if (mDisplay->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE) + { + EGLint requestedMajorVersion = static_cast<EGLint>( + attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE)); + EGLint requestedMinorVersion = static_cast<EGLint>( + attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE)); + + if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 11) + { + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1) + { + // This could potentially lead to failed context creation if done on a system + // without the platform update which installs DXGI 1.2. Currently, for Chrome users + // D3D11 contexts are only created if the platform update is available, so this + // should not cause any issues. + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_1); + } + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_0); + } + } + + if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 10) + { + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_1); + } + if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_0); + } + } + + if (requestedMajorVersion == 9 && requestedMinorVersion == 3) + { + mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_9_3); + } + + EGLint requestedDeviceType = static_cast<EGLint>(attributes.get( + EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE)); + switch (requestedDeviceType) + { + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_HARDWARE; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_WARP; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_REFERENCE; + break; + + case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: + mRequestedDriverType = D3D_DRIVER_TYPE_NULL; + break; + + default: + UNREACHABLE(); + } + + mCreateDebugDevice = ShouldUseDebugLayers(attributes); + } + else if (mDisplay->getPlatform() == EGL_PLATFORM_DEVICE_EXT) + { + ASSERT(mDisplay->getDevice() != nullptr); + mCreatedWithDeviceEXT = true; + + // Also set EGL_PLATFORM_ANGLE_ANGLE variables, in case they're used elsewhere in ANGLE + // mAvailableFeatureLevels defaults to empty + mRequestedDriverType = D3D_DRIVER_TYPE_UNKNOWN; + } + + const EGLenum presentPath = static_cast<EGLenum>(attributes.get( + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE)); + mPresentPathFastEnabled = (presentPath == EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE); +} + +Renderer11::~Renderer11() +{ + release(); +} + +#ifndef __d3d11_1_h__ +# define D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET ((D3D11_MESSAGE_ID)3146081) +#endif + +egl::Error Renderer11::initialize() +{ + HRESULT result = S_OK; + + ANGLE_TRY(initializeD3DDevice()); + +#if !defined(ANGLE_ENABLE_WINDOWS_UWP) +# if !ANGLE_SKIP_DXGI_1_2_CHECK + { + ANGLE_TRACE_EVENT0("gpu.angle", "Renderer11::initialize (DXGICheck)"); + // In order to create a swap chain for an HWND owned by another process, DXGI 1.2 is + // required. + // The easiest way to check is to query for a IDXGIDevice2. + bool requireDXGI1_2 = false; + HWND hwnd = WindowFromDC(mDisplay->getNativeDisplayId()); + if (hwnd) + { + DWORD currentProcessId = GetCurrentProcessId(); + DWORD wndProcessId; + GetWindowThreadProcessId(hwnd, &wndProcessId); + requireDXGI1_2 = (currentProcessId != wndProcessId); + } + else + { + requireDXGI1_2 = true; + } + + if (requireDXGI1_2) + { + IDXGIDevice2 *dxgiDevice2 = nullptr; + result = mDevice->QueryInterface(__uuidof(IDXGIDevice2), (void **)&dxgiDevice2); + if (FAILED(result)) + { + return egl::EglNotInitialized(D3D11_INIT_INCOMPATIBLE_DXGI) + << "DXGI 1.2 required to present to HWNDs owned by another process."; + } + SafeRelease(dxgiDevice2); + } + } +# endif +#endif + + { + ANGLE_TRACE_EVENT0("gpu.angle", "Renderer11::initialize (ComQueries)"); + // Cast the DeviceContext to a DeviceContext1 and DeviceContext3. + // This could fail on Windows 7 without the Platform Update. + // Don't error in this case- just don't use mDeviceContext1 or mDeviceContext3. + mDeviceContext1 = d3d11::DynamicCastComObject<ID3D11DeviceContext1>(mDeviceContext); + mDeviceContext3 = d3d11::DynamicCastComObject<ID3D11DeviceContext3>(mDeviceContext); + + IDXGIDevice *dxgiDevice = nullptr; + result = mDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice); + + if (FAILED(result)) + { + return egl::EglNotInitialized(D3D11_INIT_OTHER_ERROR) << "Could not query DXGI device."; + } + + result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&mDxgiAdapter); + + if (FAILED(result)) + { + return egl::EglNotInitialized(D3D11_INIT_OTHER_ERROR) + << "Could not retrieve DXGI adapter"; + } + + SafeRelease(dxgiDevice); + + IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject<IDXGIAdapter2>(mDxgiAdapter); + + // On D3D_FEATURE_LEVEL_9_*, IDXGIAdapter::GetDesc returns "Software Adapter" for the + // description string. + // If DXGI1.2 is available then IDXGIAdapter2::GetDesc2 can be used to get the actual + // hardware values. + if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3 && dxgiAdapter2 != nullptr) + { + DXGI_ADAPTER_DESC2 adapterDesc2 = {}; + result = dxgiAdapter2->GetDesc2(&adapterDesc2); + if (SUCCEEDED(result)) + { + // Copy the contents of the DXGI_ADAPTER_DESC2 into mAdapterDescription (a + // DXGI_ADAPTER_DESC). + memcpy(mAdapterDescription.Description, adapterDesc2.Description, + sizeof(mAdapterDescription.Description)); + mAdapterDescription.VendorId = adapterDesc2.VendorId; + mAdapterDescription.DeviceId = adapterDesc2.DeviceId; + mAdapterDescription.SubSysId = adapterDesc2.SubSysId; + mAdapterDescription.Revision = adapterDesc2.Revision; + mAdapterDescription.DedicatedVideoMemory = adapterDesc2.DedicatedVideoMemory; + mAdapterDescription.DedicatedSystemMemory = adapterDesc2.DedicatedSystemMemory; + mAdapterDescription.SharedSystemMemory = adapterDesc2.SharedSystemMemory; + mAdapterDescription.AdapterLuid = adapterDesc2.AdapterLuid; + } + } + else + { + result = mDxgiAdapter->GetDesc(&mAdapterDescription); + } + + SafeRelease(dxgiAdapter2); + + if (FAILED(result)) + { + return egl::EglNotInitialized(D3D11_INIT_OTHER_ERROR) + << "Could not read DXGI adaptor description."; + } + + memset(mDescription, 0, sizeof(mDescription)); + wcstombs(mDescription, mAdapterDescription.Description, sizeof(mDescription) - 1); + + result = mDxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&mDxgiFactory); + + if (!mDxgiFactory || FAILED(result)) + { + return egl::EglNotInitialized(D3D11_INIT_OTHER_ERROR) + << "Could not create DXGI factory."; + } + } + + // Disable some spurious D3D11 debug warnings to prevent them from flooding the output log + if (mCreateDebugDevice) + { + ANGLE_TRACE_EVENT0("gpu.angle", "Renderer11::initialize (HideWarnings)"); + ID3D11InfoQueue *infoQueue; + result = mDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&infoQueue); + + if (SUCCEEDED(result)) + { + D3D11_MESSAGE_ID hideMessages[] = { + D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET, + + // Robust access behaviour makes out of bounds messages safe + D3D11_MESSAGE_ID_DEVICE_DRAW_VERTEX_BUFFER_TOO_SMALL, + }; + + D3D11_INFO_QUEUE_FILTER filter = {}; + filter.DenyList.NumIDs = static_cast<unsigned int>(ArraySize(hideMessages)); + filter.DenyList.pIDList = hideMessages; + + infoQueue->AddStorageFilterEntries(&filter); + SafeRelease(infoQueue); + } + } + +#if !defined(NDEBUG) + mDebug = d3d11::DynamicCastComObject<ID3D11Debug>(mDevice); +#endif + + ANGLE_TRY(initializeDevice()); + + return egl::NoError(); +} + +HRESULT Renderer11::callD3D11CreateDevice(PFN_D3D11_CREATE_DEVICE createDevice, bool debug) +{ + angle::ComPtr<IDXGIAdapter> adapter; + + const egl::AttributeMap &attributes = mDisplay->getAttributeMap(); + // Check EGL_ANGLE_platform_angle_d3d_luid + long high = static_cast<long>(attributes.get(EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE, 0)); + unsigned long low = + static_cast<unsigned long>(attributes.get(EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE, 0)); + // Check EGL_ANGLE_platform_angle_device_id + if (high == 0 && low == 0) + { + high = static_cast<long>(attributes.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0)); + low = static_cast<unsigned long>(attributes.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0)); + } + if (high != 0 || low != 0) + { + angle::ComPtr<IDXGIFactory1> factory; + if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) + { + angle::ComPtr<IDXGIAdapter> temp; + for (UINT i = 0; SUCCEEDED(factory->EnumAdapters(i, &temp)); i++) + { + DXGI_ADAPTER_DESC desc; + if (SUCCEEDED(temp->GetDesc(&desc))) + { + // EGL_ANGLE_platform_angle_d3d_luid + if (desc.AdapterLuid.HighPart == high && desc.AdapterLuid.LowPart == low) + { + adapter = temp; + break; + } + + // EGL_ANGLE_platform_angle_device_id + // NOTE: If there are multiple GPUs with the same PCI + // vendor and device IDs, this will arbitrarily choose one + // of them. To select a specific GPU, use the LUID instead. + if ((high == 0 || desc.VendorId == static_cast<UINT>(high)) && + (low == 0 || desc.DeviceId == static_cast<UINT>(low))) + { + adapter = temp; + break; + } + } + } + } + } + + // If adapter is not nullptr, the driver type must be D3D_DRIVER_TYPE_UNKNOWN or + // D3D11CreateDevice will return E_INVALIDARG. + return createDevice( + adapter.Get(), adapter ? D3D_DRIVER_TYPE_UNKNOWN : mRequestedDriverType, nullptr, + debug ? D3D11_CREATE_DEVICE_DEBUG : 0, mAvailableFeatureLevels.data(), + static_cast<unsigned int>(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, &mDevice, + &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); +} + +HRESULT Renderer11::callD3D11On12CreateDevice(PFN_D3D12_CREATE_DEVICE createDevice12, + PFN_D3D11ON12_CREATE_DEVICE createDevice11on12, + bool debug) +{ + angle::ComPtr<IDXGIFactory4> factory; + HRESULT result = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); + if (FAILED(result)) + { + return result; + } + + if (mRequestedDriverType == D3D_DRIVER_TYPE_WARP) + { + angle::ComPtr<IDXGIAdapter> warpAdapter; + result = factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)); + if (SUCCEEDED(result)) + { + result = createDevice12(warpAdapter.Get(), mAvailableFeatureLevels[0], + IID_PPV_ARGS(&mDevice12)); + } + } + else + { + // Passing nullptr into pAdapter chooses the default adapter which will be the hardware + // adapter if it exists. + result = createDevice12(nullptr, mAvailableFeatureLevels[0], IID_PPV_ARGS(&mDevice12)); + } + + if (SUCCEEDED(result)) + { + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + result = mDevice12->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)); + } + + if (SUCCEEDED(result)) + { + result = createDevice11on12( + mDevice12.Get(), debug ? D3D11_CREATE_DEVICE_DEBUG : 0, mAvailableFeatureLevels.data(), + static_cast<unsigned int>(mAvailableFeatureLevels.size()), + reinterpret_cast<IUnknown **>(mCommandQueue.GetAddressOf()), 1 /* NumQueues */, + 0 /* NodeMask */, &mDevice, &mDeviceContext, &(mRenderer11DeviceCaps.featureLevel)); + } + + return result; +} + +egl::Error Renderer11::initializeD3DDevice() +{ + HRESULT result = S_OK; + bool createD3D11on12Device = false; + + if (!mCreatedWithDeviceEXT) + { +#if !defined(ANGLE_ENABLE_WINDOWS_UWP) + PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr; + PFN_D3D12_CREATE_DEVICE D3D12CreateDevice = nullptr; + PFN_D3D11ON12_CREATE_DEVICE D3D11On12CreateDevice = nullptr; + { + ANGLE_TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)"); + mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); + mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); + mDCompModule = LoadLibrary(TEXT("dcomp.dll")); + + // create the D3D11 device + ASSERT(mDevice == nullptr); + + const egl::AttributeMap &attributes = mDisplay->getAttributeMap(); + createD3D11on12Device = + attributes.get(EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE, EGL_FALSE) == EGL_TRUE; + + if (createD3D11on12Device) + { + mD3d12Module = LoadLibrary(TEXT("d3d12.dll")); + if (mD3d12Module == nullptr) + { + return egl::EglNotInitialized(D3D11_INIT_MISSING_DEP) + << "Could not load D3D12 library."; + } + + D3D12CreateDevice = reinterpret_cast<PFN_D3D12_CREATE_DEVICE>( + GetProcAddress(mD3d12Module, "D3D12CreateDevice")); + if (D3D12CreateDevice == nullptr) + { + return egl::EglNotInitialized(D3D11_INIT_MISSING_DEP) + << "Could not retrieve D3D12CreateDevice address."; + } + + D3D11On12CreateDevice = reinterpret_cast<PFN_D3D11ON12_CREATE_DEVICE>( + GetProcAddress(mD3d11Module, "D3D11On12CreateDevice")); + if (D3D11On12CreateDevice == nullptr) + { + return egl::EglNotInitialized(D3D11_INIT_MISSING_DEP) + << "Could not retrieve D3D11On12CreateDevice address."; + } + } + else + { + if (mD3d11Module == nullptr || mDxgiModule == nullptr) + { + return egl::EglNotInitialized(D3D11_INIT_MISSING_DEP) + << "Could not load D3D11 or DXGI library."; + } + + D3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>( + GetProcAddress(mD3d11Module, "D3D11CreateDevice")); + + if (D3D11CreateDevice == nullptr) + { + return egl::EglNotInitialized(D3D11_INIT_MISSING_DEP) + << "Could not retrieve D3D11CreateDevice address."; + } + } + } +#endif + + if (mCreateDebugDevice) + { + ANGLE_TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)"); + if (createD3D11on12Device) + { + result = callD3D11On12CreateDevice(D3D12CreateDevice, D3D11On12CreateDevice, true); + } + else + { + result = callD3D11CreateDevice(D3D11CreateDevice, true); + } + + if (result == E_INVALIDARG && mAvailableFeatureLevels.size() > 1u && + mAvailableFeatureLevels[0] == D3D_FEATURE_LEVEL_11_1) + { + // On older Windows platforms, D3D11.1 is not supported which returns E_INVALIDARG. + // Try again without passing D3D_FEATURE_LEVEL_11_1 in case we have other feature + // levels to fall back on. + mAvailableFeatureLevels.erase(mAvailableFeatureLevels.begin()); + if (createD3D11on12Device) + { + result = + callD3D11On12CreateDevice(D3D12CreateDevice, D3D11On12CreateDevice, true); + } + else + { + result = callD3D11CreateDevice(D3D11CreateDevice, true); + } + } + + if (!mDevice || FAILED(result)) + { + WARN() << "Failed creating Debug D3D11 device - falling back to release runtime."; + } + } + + if (!mDevice || FAILED(result)) + { + ANGLE_TRACE_EVENT0("gpu.angle", "D3D11CreateDevice"); + if (createD3D11on12Device) + { + result = callD3D11On12CreateDevice(D3D12CreateDevice, D3D11On12CreateDevice, false); + } + else + { + result = callD3D11CreateDevice(D3D11CreateDevice, false); + } + + if (result == E_INVALIDARG && mAvailableFeatureLevels.size() > 1u && + mAvailableFeatureLevels[0] == D3D_FEATURE_LEVEL_11_1) + { + // On older Windows platforms, D3D11.1 is not supported which returns E_INVALIDARG. + // Try again without passing D3D_FEATURE_LEVEL_11_1 in case we have other feature + // levels to fall back on. + mAvailableFeatureLevels.erase(mAvailableFeatureLevels.begin()); + if (createD3D11on12Device) + { + result = + callD3D11On12CreateDevice(D3D12CreateDevice, D3D11On12CreateDevice, false); + } + else + { + result = callD3D11CreateDevice(D3D11CreateDevice, false); + } + } + + // Cleanup done by destructor + if (!mDevice || FAILED(result)) + { + ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError", + static_cast<int>(result)); + return egl::EglNotInitialized(D3D11_INIT_CREATEDEVICE_ERROR) + << "Could not create D3D11 device."; + } + } + } + else + { + DeviceD3D *deviceD3D = GetImplAs<DeviceD3D>(mDisplay->getDevice()); + ASSERT(deviceD3D != nullptr); + + // We should use the inputted D3D11 device instead + void *device = nullptr; + ANGLE_TRY(deviceD3D->getAttribute(mDisplay, EGL_D3D11_DEVICE_ANGLE, &device)); + + ID3D11Device *d3dDevice = static_cast<ID3D11Device *>(device); + if (FAILED(d3dDevice->GetDeviceRemovedReason())) + { + return egl::EglNotInitialized() << "Inputted D3D11 device has been lost."; + } + + if (d3dDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_9_3) + { + return egl::EglNotInitialized() + << "Inputted D3D11 device must be Feature Level 9_3 or greater."; + } + + // The Renderer11 adds a ref to the inputted D3D11 device, like D3D11CreateDevice does. + mDevice = d3dDevice; + mDevice->AddRef(); + mDevice->GetImmediateContext(&mDeviceContext); + mRenderer11DeviceCaps.featureLevel = mDevice->GetFeatureLevel(); + } + + mResourceManager11.setAllocationsInitialized(mCreateDebugDevice); + + d3d11::SetDebugName(mDeviceContext, "DeviceContext", nullptr); + + mAnnotatorContext.initialize(mDeviceContext); + + mDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void **>(&mDevice1)); + + return egl::NoError(); +} + +void Renderer11::setGlobalDebugAnnotator() +{ + static angle::base::NoDestructor<std::mutex> gMutex; + static angle::base::NoDestructor<DebugAnnotator11> gGlobalAnnotator; + + std::lock_guard<std::mutex> lg(*gMutex); + gl::InitializeDebugAnnotations(gGlobalAnnotator.get()); +} + +// do any one-time device initialization +// NOTE: this is also needed after a device lost/reset +// to reset the scene status and ensure the default states are reset. +egl::Error Renderer11::initializeDevice() +{ + ANGLE_TRACE_EVENT0("gpu.angle", "Renderer11::initializeDevice"); + + populateRenderer11DeviceCaps(); + + mStateCache.clear(); + + ASSERT(!mBlit); + mBlit = new Blit11(this); + + ASSERT(!mClear); + mClear = new Clear11(this); + + const auto &attributes = mDisplay->getAttributeMap(); + // If automatic trim is enabled, DXGIDevice3::Trim( ) is called for the application + // automatically when an application is suspended by the OS. This feature is currently + // only supported for Windows Store applications. + EGLint enableAutoTrim = static_cast<EGLint>( + attributes.get(EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_FALSE)); + + if (enableAutoTrim == EGL_TRUE) + { + ASSERT(!mTrim); + mTrim = new Trim11(this); + } + + ASSERT(!mPixelTransfer); + mPixelTransfer = new PixelTransfer11(this); + + // Gather stats on DXGI and D3D feature level + ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_2", mRenderer11DeviceCaps.supportsDXGI1_2); + + ANGLEFeatureLevel angleFeatureLevel = GetANGLEFeatureLevel(mRenderer11DeviceCaps.featureLevel); + + // We don't actually request a 11_1 device, because of complications with the platform + // update. Instead we check if the mDeviceContext1 pointer cast succeeded. + // Note: we should support D3D11_0 always, but we aren't guaranteed to be at FL11_0 + // because the app can specify a lower version (such as 9_3) on Display creation. + if (mDeviceContext1 != nullptr) + { + angleFeatureLevel = ANGLE_FEATURE_LEVEL_11_1; + } + + ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.D3D11FeatureLevel", angleFeatureLevel, + NUM_ANGLE_FEATURE_LEVELS); + + return egl::NoError(); +} + +void Renderer11::populateRenderer11DeviceCaps() +{ + HRESULT hr = S_OK; + + LARGE_INTEGER version; + hr = mDxgiAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &version); + if (FAILED(hr)) + { + mRenderer11DeviceCaps.driverVersion.reset(); + ERR() << "Error querying driver version from DXGI Adapter."; + } + else + { + mRenderer11DeviceCaps.driverVersion = version; + } + + if (mDeviceContext1) + { + D3D11_FEATURE_DATA_D3D11_OPTIONS d3d11Options; + HRESULT result = mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &d3d11Options, + sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS)); + if (SUCCEEDED(result)) + { + mRenderer11DeviceCaps.supportsClearView = (d3d11Options.ClearView != FALSE); + mRenderer11DeviceCaps.supportsConstantBufferOffsets = + (d3d11Options.ConstantBufferOffsetting != FALSE); + } + } + + if (mDeviceContext3) + { + D3D11_FEATURE_DATA_D3D11_OPTIONS3 d3d11Options3; + HRESULT result = mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &d3d11Options3, + sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS3)); + if (SUCCEEDED(result)) + { + mRenderer11DeviceCaps.supportsVpRtIndexWriteFromVertexShader = + (d3d11Options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer == TRUE); + } + D3D11_FEATURE_DATA_D3D11_OPTIONS2 d3d11Options2; + result = mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &d3d11Options2, + sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS2)); + if (SUCCEEDED(result)) + { + mRenderer11DeviceCaps.supportsTypedUAVLoadAdditionalFormats = + d3d11Options2.TypedUAVLoadAdditionalFormats; + if (!getFeatures().disableRasterizerOrderViews.enabled) + { + mRenderer11DeviceCaps.supportsRasterizerOrderViews = d3d11Options2.ROVsSupported; + } + } + } + + mRenderer11DeviceCaps.supportsMultisampledDepthStencilSRVs = + mRenderer11DeviceCaps.featureLevel > D3D_FEATURE_LEVEL_10_0; + + if (getFeatures().disableB5G6R5Support.enabled) + { + mRenderer11DeviceCaps.B5G6R5support = 0; + mRenderer11DeviceCaps.B5G6R5maxSamples = 0; + } + else + { + PopulateFormatDeviceCaps(mDevice, DXGI_FORMAT_B5G6R5_UNORM, + &mRenderer11DeviceCaps.B5G6R5support, + &mRenderer11DeviceCaps.B5G6R5maxSamples); + } + + if (getFeatures().allowES3OnFL100.enabled) + { + mRenderer11DeviceCaps.allowES3OnFL10_0 = true; + } + + PopulateFormatDeviceCaps(mDevice, DXGI_FORMAT_B4G4R4A4_UNORM, + &mRenderer11DeviceCaps.B4G4R4A4support, + &mRenderer11DeviceCaps.B4G4R4A4maxSamples); + PopulateFormatDeviceCaps(mDevice, DXGI_FORMAT_B5G5R5A1_UNORM, + &mRenderer11DeviceCaps.B5G5R5A1support, + &mRenderer11DeviceCaps.B5G5R5A1maxSamples); + + IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject<IDXGIAdapter2>(mDxgiAdapter); + mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); + SafeRelease(dxgiAdapter2); +} + +gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( + const gl::TextureCaps &colorBufferFormatCaps, + const gl::TextureCaps &depthStencilBufferFormatCaps) const +{ + gl::SupportedSampleSet sampleCounts; + + // Generate a new set from the set intersection of sample counts between the color and depth + // format caps. + std::set_intersection(colorBufferFormatCaps.sampleCounts.begin(), + colorBufferFormatCaps.sampleCounts.end(), + depthStencilBufferFormatCaps.sampleCounts.begin(), + depthStencilBufferFormatCaps.sampleCounts.end(), + std::inserter(sampleCounts, sampleCounts.begin())); + + // Format of GL_NONE results in no supported sample counts. + // Add back the color sample counts to the supported sample set. + if (depthStencilBufferFormatCaps.sampleCounts.empty()) + { + sampleCounts = colorBufferFormatCaps.sampleCounts; + } + else if (colorBufferFormatCaps.sampleCounts.empty()) + { + // Likewise, add back the depth sample counts to the supported sample set. + sampleCounts = depthStencilBufferFormatCaps.sampleCounts; + } + + // Always support 0 samples + sampleCounts.insert(0); + + return sampleCounts; +} + +egl::ConfigSet Renderer11::generateConfigs() +{ + std::vector<GLenum> colorBufferFormats; + + // 32-bit supported formats + colorBufferFormats.push_back(GL_BGRA8_EXT); + colorBufferFormats.push_back(GL_RGBA8_OES); + + // 24-bit supported formats + colorBufferFormats.push_back(GL_RGB8_OES); + + if (mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) + { + // Additional high bit depth formats added in D3D 10.0 + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb173064.aspx + colorBufferFormats.push_back(GL_RGBA16F); + colorBufferFormats.push_back(GL_RGB10_A2); + } + + if (!mPresentPathFastEnabled) + { + // 16-bit supported formats + // These aren't valid D3D11 swapchain formats, so don't expose them as configs + // if present path fast is active + colorBufferFormats.push_back(GL_RGBA4); + colorBufferFormats.push_back(GL_RGB5_A1); + colorBufferFormats.push_back(GL_RGB565); + } + + static const GLenum depthStencilBufferFormats[] = { + GL_NONE, GL_DEPTH24_STENCIL8_OES, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT16, + GL_STENCIL_INDEX8, + }; + + const gl::Caps &rendererCaps = getNativeCaps(); + const gl::TextureCapsMap &rendererTextureCaps = getNativeTextureCaps(); + + const EGLint optimalSurfaceOrientation = + mPresentPathFastEnabled ? 0 : EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE; + + egl::ConfigSet configs; + for (GLenum colorBufferInternalFormat : colorBufferFormats) + { + const gl::TextureCaps &colorBufferFormatCaps = + rendererTextureCaps.get(colorBufferInternalFormat); + if (!colorBufferFormatCaps.renderbuffer) + { + ASSERT(!colorBufferFormatCaps.textureAttachment); + continue; + } + + for (GLenum depthStencilBufferInternalFormat : depthStencilBufferFormats) + { + const gl::TextureCaps &depthStencilBufferFormatCaps = + rendererTextureCaps.get(depthStencilBufferInternalFormat); + if (!depthStencilBufferFormatCaps.renderbuffer && + depthStencilBufferInternalFormat != GL_NONE) + { + ASSERT(!depthStencilBufferFormatCaps.textureAttachment); + continue; + } + + const gl::InternalFormat &colorBufferFormatInfo = + gl::GetSizedInternalFormatInfo(colorBufferInternalFormat); + const gl::InternalFormat &depthStencilBufferFormatInfo = + gl::GetSizedInternalFormatInfo(depthStencilBufferInternalFormat); + const gl::Version &maxVersion = getMaxSupportedESVersion(); + + const gl::SupportedSampleSet sampleCounts = + generateSampleSetForEGLConfig(colorBufferFormatCaps, depthStencilBufferFormatCaps); + + for (GLuint sampleCount : sampleCounts) + { + egl::Config config; + config.renderTargetFormat = colorBufferInternalFormat; + config.depthStencilFormat = depthStencilBufferInternalFormat; + config.bufferSize = colorBufferFormatInfo.pixelBytes * 8; + config.redSize = colorBufferFormatInfo.redBits; + config.greenSize = colorBufferFormatInfo.greenBits; + config.blueSize = colorBufferFormatInfo.blueBits; + config.luminanceSize = colorBufferFormatInfo.luminanceBits; + config.alphaSize = colorBufferFormatInfo.alphaBits; + config.alphaMaskSize = 0; + config.bindToTextureRGB = + ((colorBufferFormatInfo.format == GL_RGB) && (sampleCount <= 1)); + config.bindToTextureRGBA = (((colorBufferFormatInfo.format == GL_RGBA) || + (colorBufferFormatInfo.format == GL_BGRA_EXT)) && + (sampleCount <= 1)); + config.colorBufferType = EGL_RGB_BUFFER; + config.configCaveat = EGL_NONE; + config.configID = static_cast<EGLint>(configs.size() + 1); + + // PresentPathFast may not be conformant + config.conformant = 0; + if (!mPresentPathFastEnabled) + { + // Can only support a conformant ES2 with feature level greater than 10.0. + if (mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) + { + config.conformant |= EGL_OPENGL_ES2_BIT; + } + + // We can only support conformant ES3 on FL 10.1+ + if (maxVersion.major >= 3) + { + config.conformant |= EGL_OPENGL_ES3_BIT_KHR; + } + } + + config.depthSize = depthStencilBufferFormatInfo.depthBits; + config.level = 0; + config.matchNativePixmap = EGL_NONE; + config.maxPBufferWidth = rendererCaps.max2DTextureSize; + config.maxPBufferHeight = rendererCaps.max2DTextureSize; + config.maxPBufferPixels = + rendererCaps.max2DTextureSize * rendererCaps.max2DTextureSize; + config.maxSwapInterval = 4; + config.minSwapInterval = 0; + config.nativeRenderable = EGL_FALSE; + config.nativeVisualID = 0; + config.nativeVisualType = EGL_NONE; + + // Can't support ES3 at all without feature level 10.1 + config.renderableType = EGL_OPENGL_ES2_BIT; + if (maxVersion.major >= 3) + { + config.renderableType |= EGL_OPENGL_ES3_BIT_KHR; + } + + config.sampleBuffers = (sampleCount == 0) ? 0 : 1; + config.samples = sampleCount; + config.stencilSize = depthStencilBufferFormatInfo.stencilBits; + config.surfaceType = + EGL_PBUFFER_BIT | EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT; + config.transparentType = EGL_NONE; + config.transparentRedValue = 0; + config.transparentGreenValue = 0; + config.transparentBlueValue = 0; + config.optimalOrientation = optimalSurfaceOrientation; + config.colorComponentType = gl_egl::GLComponentTypeToEGLColorComponentType( + colorBufferFormatInfo.componentType); + + configs.add(config); + } + } + } + + ASSERT(configs.size() > 0); + return configs; +} + +void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions) const +{ + outExtensions->createContextRobustness = true; + + if (getShareHandleSupport()) + { + outExtensions->d3dShareHandleClientBuffer = true; + outExtensions->surfaceD3DTexture2DShareHandle = true; + } + outExtensions->d3dTextureClientBuffer = true; + outExtensions->imageD3D11Texture = true; + + outExtensions->keyedMutex = true; + outExtensions->querySurfacePointer = true; + outExtensions->windowFixedSize = true; + + // If present path fast is active then the surface orientation extension isn't supported + outExtensions->surfaceOrientation = !mPresentPathFastEnabled; + + // D3D11 does not support present with dirty rectangles until DXGI 1.2. + outExtensions->postSubBuffer = mRenderer11DeviceCaps.supportsDXGI1_2; + + outExtensions->image = true; + outExtensions->imageBase = true; + outExtensions->glTexture2DImage = true; + outExtensions->glTextureCubemapImage = true; + outExtensions->glRenderbufferImage = true; + + outExtensions->stream = true; + outExtensions->streamConsumerGLTexture = true; + outExtensions->streamConsumerGLTextureYUV = true; + outExtensions->streamProducerD3DTexture = true; + + outExtensions->noConfigContext = true; + outExtensions->directComposition = !!mDCompModule; + + // Contexts are virtualized so textures and semaphores can be shared globally + outExtensions->displayTextureShareGroup = true; + outExtensions->displaySemaphoreShareGroup = true; + + // syncControlCHROMIUM requires direct composition. + outExtensions->syncControlCHROMIUM = outExtensions->directComposition; + + // D3D11 can be used without a swap chain + outExtensions->surfacelessContext = true; + + // All D3D feature levels support robust resource init + outExtensions->robustResourceInitializationANGLE = true; + +#ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW + // Compositor Native Window capabilies require WinVer >= 1803 + if (CompositorNativeWindow11::IsSupportedWinRelease()) + { + outExtensions->windowsUIComposition = true; + } +#endif +} + +angle::Result Renderer11::flush(Context11 *context11) +{ + mDeviceContext->Flush(); + return angle::Result::Continue; +} + +angle::Result Renderer11::finish(Context11 *context11) +{ + if (!mSyncQuery.valid()) + { + D3D11_QUERY_DESC queryDesc; + queryDesc.Query = D3D11_QUERY_EVENT; + queryDesc.MiscFlags = 0; + + ANGLE_TRY(allocateResource(context11, queryDesc, &mSyncQuery)); + } + + mDeviceContext->End(mSyncQuery.get()); + + HRESULT result = S_OK; + unsigned int attempt = 0; + do + { + unsigned int flushFrequency = 100; + UINT flags = (attempt % flushFrequency == 0) ? 0 : D3D11_ASYNC_GETDATA_DONOTFLUSH; + attempt++; + + result = mDeviceContext->GetData(mSyncQuery.get(), nullptr, 0, flags); + ANGLE_TRY_HR(context11, result, "Failed to get event query data"); + + if (result == S_FALSE) + { + // Keep polling, but allow other threads to do something useful first + ScheduleYield(); + } + + // Attempt is incremented before checking if we should test for device loss so that device + // loss is not checked on the first iteration + bool checkDeviceLost = (attempt % kPollingD3DDeviceLostCheckFrequency) == 0; + if (checkDeviceLost && testDeviceLost()) + { + mDisplay->notifyDeviceLost(); + ANGLE_CHECK(context11, false, "Device was lost while waiting for sync.", + GL_OUT_OF_MEMORY); + } + } while (result == S_FALSE); + + return angle::Result::Continue; +} + +bool Renderer11::isValidNativeWindow(EGLNativeWindowType window) const +{ +#if defined(ANGLE_ENABLE_WINDOWS_UWP) + if (NativeWindow11WinRT::IsValidNativeWindow(window)) + { + return true; + } +#else + if (NativeWindow11Win32::IsValidNativeWindow(window)) + { + return true; + } +#endif + +#ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW + static_assert(sizeof(ABI::Windows::UI::Composition::SpriteVisual *) == sizeof(HWND), + "Pointer size must match Window Handle size"); + if (CompositorNativeWindow11::IsValidNativeWindow(window)) + { + return true; + } +#endif + + return false; +} + +NativeWindowD3D *Renderer11::createNativeWindow(EGLNativeWindowType window, + const egl::Config *config, + const egl::AttributeMap &attribs) const +{ +#if defined(ANGLE_ENABLE_WINDOWS_UWP) + if (window == nullptr || NativeWindow11WinRT::IsValidNativeWindow(window)) + { + return new NativeWindow11WinRT(window, config->alphaSize > 0); + } +#else + if (window == nullptr || NativeWindow11Win32::IsValidNativeWindow(window)) + { + return new NativeWindow11Win32( + window, config->alphaSize > 0, + attribs.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE); + } +#endif + +#ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW + if (CompositorNativeWindow11::IsValidNativeWindow(window)) + { + return new CompositorNativeWindow11(window, config->alphaSize > 0); + } +#endif + + UNREACHABLE(); + return nullptr; +} + +egl::Error Renderer11::getD3DTextureInfo(const egl::Config *configuration, + IUnknown *texture, + const egl::AttributeMap &attribs, + EGLint *width, + EGLint *height, + GLsizei *samples, + gl::Format *glFormat, + const angle::Format **angleFormat, + UINT *arraySlice) const +{ + angle::ComPtr<ID3D11Texture2D> d3dTexture = + d3d11::DynamicCastComObjectToComPtr<ID3D11Texture2D>(texture); + if (d3dTexture == nullptr) + { + return egl::EglBadParameter() << "client buffer is not a ID3D11Texture2D"; + } + + angle::ComPtr<ID3D11Device> textureDevice; + d3dTexture->GetDevice(&textureDevice); + if (textureDevice.Get() != mDevice) + { + return egl::EglBadParameter() << "Texture's device does not match."; + } + + D3D11_TEXTURE2D_DESC desc = {}; + d3dTexture->GetDesc(&desc); + + EGLint imageWidth = static_cast<EGLint>(desc.Width); + EGLint imageHeight = static_cast<EGLint>(desc.Height); + + GLsizei sampleCount = static_cast<GLsizei>(desc.SampleDesc.Count); + if (configuration && (configuration->samples != sampleCount)) + { + // Both the texture and EGL config sample count may not be the same when multi-sampling + // is disabled. The EGL sample count can be 0 but a D3D texture is always 1. Therefore, + // we must only check for a invalid match when the EGL config is non-zero or the texture is + // not one. + if (configuration->samples != 0 || sampleCount != 1) + { + return egl::EglBadParameter() << "Texture's sample count does not match."; + } + } + + const angle::Format *textureAngleFormat = nullptr; + GLenum sizedInternalFormat = GL_NONE; + + // From table egl.restrictions in EGL_ANGLE_d3d_texture_client_buffer. + if (desc.Format == DXGI_FORMAT_NV12 || desc.Format == DXGI_FORMAT_P010 || + desc.Format == DXGI_FORMAT_P016) + { + if (!attribs.contains(EGL_D3D11_TEXTURE_PLANE_ANGLE)) + { + return egl::EglBadParameter() + << "EGL_D3D11_TEXTURE_PLANE_ANGLE must be specified for YUV textures."; + } + + EGLint plane = attribs.getAsInt(EGL_D3D11_TEXTURE_PLANE_ANGLE); + + // P010 and P016 have the same memory layout, SRV/RTV format, etc. + const bool isNV12 = (desc.Format == DXGI_FORMAT_NV12); + if (plane == 0) + { + textureAngleFormat = isNV12 ? &angle::Format::Get(angle::FormatID::R8_UNORM) + : &angle::Format::Get(angle::FormatID::R16_UNORM); + } + else if (plane == 1) + { + textureAngleFormat = isNV12 ? &angle::Format::Get(angle::FormatID::R8G8_UNORM) + : &angle::Format::Get(angle::FormatID::R16G16_UNORM); + imageWidth /= 2; + imageHeight /= 2; + } + else + { + return egl::EglBadParameter() << "Invalid client buffer texture plane: " << plane; + } + + ASSERT(textureAngleFormat); + sizedInternalFormat = textureAngleFormat->glInternalFormat; + } + else + { + switch (desc.Format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16G16_UNORM: + break; + + default: + return egl::EglBadParameter() + << "Invalid client buffer texture format: " << desc.Format; + } + + textureAngleFormat = &d3d11_angle::GetFormat(desc.Format); + ASSERT(textureAngleFormat); + + sizedInternalFormat = textureAngleFormat->glInternalFormat; + + if (attribs.contains(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE)) + { + const GLenum internalFormat = + static_cast<GLenum>(attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE)); + switch (internalFormat) + { + case GL_RGBA: + case GL_BGRA_EXT: + case GL_RGB: + case GL_RED_EXT: + case GL_RG_EXT: + case GL_RGB10_A2_EXT: + case GL_R16_EXT: + case GL_RG16_EXT: + break; + default: + return egl::EglBadParameter() + << "Invalid client buffer texture internal format: " << std::hex + << internalFormat; + } + + const GLenum type = gl::GetSizedInternalFormatInfo(sizedInternalFormat).type; + + const auto format = gl::Format(internalFormat, type); + if (!format.valid()) + { + return egl::EglBadParameter() + << "Invalid client buffer texture internal format: " << std::hex + << internalFormat; + } + + sizedInternalFormat = format.info->sizedInternalFormat; + } + } + + UINT textureArraySlice = + static_cast<UINT>(attribs.getAsInt(EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE, 0)); + if (textureArraySlice >= desc.ArraySize) + { + return egl::EglBadParameter() + << "Invalid client buffer texture array slice: " << textureArraySlice; + } + + if (width) + { + *width = imageWidth; + } + if (height) + { + *height = imageHeight; + } + + if (samples) + { + // EGL samples 0 corresponds to D3D11 sample count 1. + *samples = sampleCount != 1 ? sampleCount : 0; + } + + if (glFormat) + { + *glFormat = gl::Format(sizedInternalFormat); + } + + if (angleFormat) + { + *angleFormat = textureAngleFormat; + } + + if (arraySlice) + { + *arraySlice = textureArraySlice; + } + + return egl::NoError(); +} + +egl::Error Renderer11::validateShareHandle(const egl::Config *config, + HANDLE shareHandle, + const egl::AttributeMap &attribs) const +{ + if (shareHandle == nullptr) + { + return egl::EglBadParameter() << "NULL share handle."; + } + + ID3D11Resource *tempResource11 = nullptr; + HRESULT result = mDevice->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource), + (void **)&tempResource11); + if (FAILED(result) && mDevice1) + { + result = mDevice1->OpenSharedResource1(shareHandle, __uuidof(ID3D11Resource), + (void **)&tempResource11); + } + + if (FAILED(result)) + { + return egl::EglBadParameter() << "Failed to open share handle, " << gl::FmtHR(result); + } + + ID3D11Texture2D *texture2D = d3d11::DynamicCastComObject<ID3D11Texture2D>(tempResource11); + SafeRelease(tempResource11); + + if (texture2D == nullptr) + { + return egl::EglBadParameter() + << "Failed to query ID3D11Texture2D object from share handle."; + } + + D3D11_TEXTURE2D_DESC desc = {}; + texture2D->GetDesc(&desc); + SafeRelease(texture2D); + + EGLint width = attribs.getAsInt(EGL_WIDTH, 0); + EGLint height = attribs.getAsInt(EGL_HEIGHT, 0); + ASSERT(width != 0 && height != 0); + + const d3d11::Format &backbufferFormatInfo = + d3d11::Format::Get(config->renderTargetFormat, getRenderer11DeviceCaps()); + + if (desc.Width != static_cast<UINT>(width) || desc.Height != static_cast<UINT>(height) || + desc.Format != backbufferFormatInfo.texFormat || desc.MipLevels != 1 || desc.ArraySize != 1) + { + return egl::EglBadParameter() << "Invalid texture parameters in share handle texture."; + } + + return egl::NoError(); +} + +SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, + HANDLE shareHandle, + IUnknown *d3dTexture, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, + EGLint samples) +{ + return new SwapChain11(this, GetAs<NativeWindow11>(nativeWindow), shareHandle, d3dTexture, + backBufferFormat, depthBufferFormat, orientation, samples); +} + +void *Renderer11::getD3DDevice() +{ + return mDevice; +} + +angle::Result Renderer11::drawWithGeometryShaderAndTransformFeedback(Context11 *context11, + gl::PrimitiveMode mode, + UINT instanceCount, + UINT vertexCount) +{ + const gl::State &glState = context11->getState(); + ProgramD3D *programD3D = mStateManager.getProgramD3D(); + + // Since we use a geometry if-and-only-if we rewrite vertex streams, transform feedback + // won't get the correct output. To work around this, draw with *only* the stream out + // first (no pixel shader) to feed the stream out buffers and then draw again with the + // geometry shader + pixel shader to rasterize the primitives. + mStateManager.setPixelShader(nullptr); + + if (instanceCount > 0) + { + mDeviceContext->DrawInstanced(vertexCount, instanceCount, 0, 0); + } + else + { + mDeviceContext->Draw(vertexCount, 0); + } + + rx::ShaderExecutableD3D *pixelExe = nullptr; + ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(context11, &pixelExe, nullptr)); + + // Skip the draw call if rasterizer discard is enabled (or no fragment shader). + if (!pixelExe || glState.getRasterizerState().rasterizerDiscard) + { + return angle::Result::Continue; + } + + mStateManager.setPixelShader(&GetAs<ShaderExecutable11>(pixelExe)->getPixelShader()); + + // Retrieve the geometry shader. + rx::ShaderExecutableD3D *geometryExe = nullptr; + ANGLE_TRY(programD3D->getGeometryExecutableForPrimitiveType(context11, glState, mode, + &geometryExe, nullptr)); + + mStateManager.setGeometryShader(&GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader()); + + if (instanceCount > 0) + { + mDeviceContext->DrawInstanced(vertexCount, instanceCount, 0, 0); + } + else + { + mDeviceContext->Draw(vertexCount, 0); + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::drawArrays(const gl::Context *context, + gl::PrimitiveMode mode, + GLint firstVertex, + GLsizei vertexCount, + GLsizei instanceCount, + GLuint baseInstance, + bool isInstancedDraw) +{ + if (mStateManager.getCullEverything()) + { + return angle::Result::Continue; + } + + ANGLE_TRY(markRawBufferUsage(context)); + + ProgramD3D *programD3D = mStateManager.getProgramD3D(); + GLsizei adjustedInstanceCount = GetAdjustedInstanceCount(programD3D, instanceCount); + + // Note: vertex indexes can be arbitrarily large. + UINT clampedVertexCount = gl::GetClampedVertexCount<UINT>(vertexCount); + + const auto &glState = context->getState(); + if (glState.getCurrentTransformFeedback() && glState.isTransformFeedbackActiveUnpaused()) + { + ANGLE_TRY(markTransformFeedbackUsage(context)); + + if (programD3D->usesGeometryShader(glState, mode)) + { + return drawWithGeometryShaderAndTransformFeedback( + GetImplAs<Context11>(context), mode, adjustedInstanceCount, clampedVertexCount); + } + } + + switch (mode) + { + case gl::PrimitiveMode::LineLoop: + return drawLineLoop(context, clampedVertexCount, gl::DrawElementsType::InvalidEnum, + nullptr, 0, adjustedInstanceCount); + case gl::PrimitiveMode::TriangleFan: + return drawTriangleFan(context, clampedVertexCount, gl::DrawElementsType::InvalidEnum, + nullptr, 0, adjustedInstanceCount); + case gl::PrimitiveMode::Points: + if (getFeatures().useInstancedPointSpriteEmulation.enabled) + { + // This code should not be reachable by multi-view programs. + ASSERT(programD3D->getState().usesMultiview() == false); + + // If the shader is writing to gl_PointSize, then pointsprites are being rendered. + // Emulating instanced point sprites for FL9_3 requires the topology to be + // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. + if (adjustedInstanceCount == 0) + { + mDeviceContext->DrawIndexedInstanced(6, clampedVertexCount, 0, 0, baseInstance); + return angle::Result::Continue; + } + + // If pointsprite emulation is used with glDrawArraysInstanced then we need to take + // a less efficent code path. Instanced rendering of emulated pointsprites requires + // a loop to draw each batch of points. An offset into the instanced data buffer is + // calculated and applied on each iteration to ensure all instances are rendered + // correctly. Each instance being rendered requires the inputlayout cache to reapply + // buffers and offsets. + for (GLsizei i = 0; i < instanceCount; i++) + { + ANGLE_TRY(mStateManager.updateVertexOffsetsForPointSpritesEmulation( + context, firstVertex, i)); + mDeviceContext->DrawIndexedInstanced(6, clampedVertexCount, 0, 0, baseInstance); + } + + // This required by updateVertexOffsets... above but is outside of the loop for + // speed. + mStateManager.invalidateVertexBuffer(); + return angle::Result::Continue; + } + break; + default: + break; + } + + // "Normal" draw case. + if (!isInstancedDraw && adjustedInstanceCount == 0) + { + mDeviceContext->Draw(clampedVertexCount, 0); + } + else + { + mDeviceContext->DrawInstanced(clampedVertexCount, adjustedInstanceCount, 0, baseInstance); + } + return angle::Result::Continue; +} + +angle::Result Renderer11::drawElements(const gl::Context *context, + gl::PrimitiveMode mode, + GLint startVertex, + GLsizei indexCount, + gl::DrawElementsType indexType, + const void *indices, + GLsizei instanceCount, + GLint baseVertex, + GLuint baseInstance, + bool isInstancedDraw) +{ + if (mStateManager.getCullEverything()) + { + return angle::Result::Continue; + } + + ANGLE_TRY(markRawBufferUsage(context)); + + // Transform feedback is not allowed for DrawElements, this error should have been caught at the + // API validation layer. + const gl::State &glState = context->getState(); + ASSERT(!glState.isTransformFeedbackActiveUnpaused()); + + // If this draw call is coming from an indirect call, offset by the indirect call's base vertex. + GLint baseVertexAdjusted = baseVertex - startVertex; + + const ProgramD3D *programD3D = mStateManager.getProgramD3D(); + GLsizei adjustedInstanceCount = GetAdjustedInstanceCount(programD3D, instanceCount); + + if (mode == gl::PrimitiveMode::LineLoop) + { + return drawLineLoop(context, indexCount, indexType, indices, baseVertexAdjusted, + adjustedInstanceCount); + } + + if (mode == gl::PrimitiveMode::TriangleFan) + { + return drawTriangleFan(context, indexCount, indexType, indices, baseVertexAdjusted, + adjustedInstanceCount); + } + + if (mode != gl::PrimitiveMode::Points || !programD3D->usesInstancedPointSpriteEmulation()) + { + if (!isInstancedDraw && adjustedInstanceCount == 0) + { + mDeviceContext->DrawIndexed(indexCount, 0, baseVertexAdjusted); + } + else + { + mDeviceContext->DrawIndexedInstanced(indexCount, adjustedInstanceCount, 0, + baseVertexAdjusted, baseInstance); + } + return angle::Result::Continue; + } + + // This code should not be reachable by multi-view programs. + ASSERT(programD3D->getState().usesMultiview() == false); + + // If the shader is writing to gl_PointSize, then pointsprites are being rendered. + // Emulating instanced point sprites for FL9_3 requires the topology to be + // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. + // + // The count parameter passed to drawElements represents the total number of instances to be + // rendered. Each instance is referenced by the bound index buffer from the the caller. + // + // Indexed pointsprite emulation replicates data for duplicate entries found in the index + // buffer. This is not an efficent rendering mechanism and is only used on downlevel renderers + // that do not support geometry shaders. + if (instanceCount == 0) + { + mDeviceContext->DrawIndexedInstanced(6, indexCount, 0, baseVertexAdjusted, baseInstance); + return angle::Result::Continue; + } + + // If pointsprite emulation is used with glDrawElementsInstanced then we need to take a less + // efficent code path. Instanced rendering of emulated pointsprites requires a loop to draw each + // batch of points. An offset into the instanced data buffer is calculated and applied on each + // iteration to ensure all instances are rendered correctly. + gl::IndexRange indexRange; + ANGLE_TRY(glState.getVertexArray()->getIndexRange(context, indexType, indexCount, indices, + &indexRange)); + + UINT clampedVertexCount = gl::clampCast<UINT>(indexRange.vertexCount()); + + // Each instance being rendered requires the inputlayout cache to reapply buffers and offsets. + for (GLsizei i = 0; i < instanceCount; i++) + { + ANGLE_TRY( + mStateManager.updateVertexOffsetsForPointSpritesEmulation(context, startVertex, i)); + mDeviceContext->DrawIndexedInstanced(6, clampedVertexCount, 0, baseVertexAdjusted, + baseInstance); + } + mStateManager.invalidateVertexBuffer(); + return angle::Result::Continue; +} + +angle::Result Renderer11::drawArraysIndirect(const gl::Context *context, const void *indirect) +{ + if (mStateManager.getCullEverything()) + { + return angle::Result::Continue; + } + + ANGLE_TRY(markRawBufferUsage(context)); + + const gl::State &glState = context->getState(); + ASSERT(!glState.isTransformFeedbackActiveUnpaused()); + + gl::Buffer *drawIndirectBuffer = glState.getTargetBuffer(gl::BufferBinding::DrawIndirect); + ASSERT(drawIndirectBuffer); + Buffer11 *storage = GetImplAs<Buffer11>(drawIndirectBuffer); + + uintptr_t offset = reinterpret_cast<uintptr_t>(indirect); + + ID3D11Buffer *buffer = nullptr; + ANGLE_TRY(storage->getBuffer(context, BUFFER_USAGE_INDIRECT, &buffer)); + mDeviceContext->DrawInstancedIndirect(buffer, static_cast<unsigned int>(offset)); + return angle::Result::Continue; +} + +angle::Result Renderer11::drawElementsIndirect(const gl::Context *context, const void *indirect) +{ + if (mStateManager.getCullEverything()) + { + return angle::Result::Continue; + } + + ANGLE_TRY(markRawBufferUsage(context)); + + const gl::State &glState = context->getState(); + ASSERT(!glState.isTransformFeedbackActiveUnpaused()); + + gl::Buffer *drawIndirectBuffer = glState.getTargetBuffer(gl::BufferBinding::DrawIndirect); + ASSERT(drawIndirectBuffer); + Buffer11 *storage = GetImplAs<Buffer11>(drawIndirectBuffer); + uintptr_t offset = reinterpret_cast<uintptr_t>(indirect); + + ID3D11Buffer *buffer = nullptr; + ANGLE_TRY(storage->getBuffer(context, BUFFER_USAGE_INDIRECT, &buffer)); + mDeviceContext->DrawIndexedInstancedIndirect(buffer, static_cast<unsigned int>(offset)); + return angle::Result::Continue; +} + +angle::Result Renderer11::drawLineLoop(const gl::Context *context, + GLuint count, + gl::DrawElementsType type, + const void *indexPointer, + int baseVertex, + int instances) +{ + const gl::State &glState = context->getState(); + gl::VertexArray *vao = glState.getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); + + const void *indices = indexPointer; + + // Get the raw indices for an indexed draw + if (type != gl::DrawElementsType::InvalidEnum && elementArrayBuffer) + { + BufferD3D *storage = GetImplAs<BufferD3D>(elementArrayBuffer); + intptr_t offset = reinterpret_cast<intptr_t>(indices); + + const uint8_t *bufferData = nullptr; + ANGLE_TRY(storage->getData(context, &bufferData)); + + indices = bufferData + offset; + } + + if (!mLineLoopIB) + { + mLineLoopIB = new StreamingIndexBufferInterface(this); + ANGLE_TRY(mLineLoopIB->reserveBufferSpace(context, INITIAL_INDEX_BUFFER_SIZE, + gl::DrawElementsType::UnsignedInt)); + } + + // Checked by Renderer11::applyPrimitiveType + bool indexCheck = static_cast<unsigned int>(count) + 1 > + (std::numeric_limits<unsigned int>::max() / sizeof(unsigned int)); + ANGLE_CHECK(GetImplAs<Context11>(context), !indexCheck, + "Failed to create a 32-bit looping index buffer for " + "GL_LINE_LOOP, too many indices required.", + GL_OUT_OF_MEMORY); + + GetLineLoopIndices(indices, type, static_cast<GLuint>(count), + glState.isPrimitiveRestartEnabled(), &mScratchIndexDataBuffer); + + unsigned int spaceNeeded = + static_cast<unsigned int>(sizeof(GLuint) * mScratchIndexDataBuffer.size()); + ANGLE_TRY( + mLineLoopIB->reserveBufferSpace(context, spaceNeeded, gl::DrawElementsType::UnsignedInt)); + + void *mappedMemory = nullptr; + unsigned int offset; + ANGLE_TRY(mLineLoopIB->mapBuffer(context, spaceNeeded, &mappedMemory, &offset)); + + // Copy over the converted index data. + memcpy(mappedMemory, &mScratchIndexDataBuffer[0], + sizeof(GLuint) * mScratchIndexDataBuffer.size()); + + ANGLE_TRY(mLineLoopIB->unmapBuffer(context)); + + IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(mLineLoopIB->getIndexBuffer()); + const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer(); + DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); + + mStateManager.setIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset); + + UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); + + if (instances > 0) + { + mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, baseVertex, 0); + } + else + { + mDeviceContext->DrawIndexed(indexCount, 0, baseVertex); + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::drawTriangleFan(const gl::Context *context, + GLuint count, + gl::DrawElementsType type, + const void *indices, + int baseVertex, + int instances) +{ + const gl::State &glState = context->getState(); + gl::VertexArray *vao = glState.getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); + + const void *indexPointer = indices; + + // Get the raw indices for an indexed draw + if (type != gl::DrawElementsType::InvalidEnum && elementArrayBuffer) + { + BufferD3D *storage = GetImplAs<BufferD3D>(elementArrayBuffer); + intptr_t offset = reinterpret_cast<intptr_t>(indices); + + const uint8_t *bufferData = nullptr; + ANGLE_TRY(storage->getData(context, &bufferData)); + + indexPointer = bufferData + offset; + } + + if (!mTriangleFanIB) + { + mTriangleFanIB = new StreamingIndexBufferInterface(this); + ANGLE_TRY(mTriangleFanIB->reserveBufferSpace(context, INITIAL_INDEX_BUFFER_SIZE, + gl::DrawElementsType::UnsignedInt)); + } + + // Checked by Renderer11::applyPrimitiveType + ASSERT(count >= 3); + + const GLuint numTris = count - 2; + + bool indexCheck = + (numTris > std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3)); + ANGLE_CHECK(GetImplAs<Context11>(context), !indexCheck, + "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, " + "too many indices required.", + GL_OUT_OF_MEMORY); + + GetTriFanIndices(indexPointer, type, count, glState.isPrimitiveRestartEnabled(), + &mScratchIndexDataBuffer); + + const unsigned int spaceNeeded = + static_cast<unsigned int>(mScratchIndexDataBuffer.size() * sizeof(unsigned int)); + ANGLE_TRY(mTriangleFanIB->reserveBufferSpace(context, spaceNeeded, + gl::DrawElementsType::UnsignedInt)); + + void *mappedMemory = nullptr; + unsigned int offset; + ANGLE_TRY(mTriangleFanIB->mapBuffer(context, spaceNeeded, &mappedMemory, &offset)); + + memcpy(mappedMemory, &mScratchIndexDataBuffer[0], spaceNeeded); + + ANGLE_TRY(mTriangleFanIB->unmapBuffer(context)); + + IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(mTriangleFanIB->getIndexBuffer()); + const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer(); + DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); + + mStateManager.setIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset); + + UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); + + if (instances > 0) + { + mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, baseVertex, 0); + } + else + { + mDeviceContext->DrawIndexed(indexCount, 0, baseVertex); + } + + return angle::Result::Continue; +} + +void Renderer11::releaseDeviceResources() +{ + mStateManager.deinitialize(); + mStateCache.clear(); + + SafeDelete(mLineLoopIB); + SafeDelete(mTriangleFanIB); + SafeDelete(mBlit); + SafeDelete(mClear); + SafeDelete(mTrim); + SafeDelete(mPixelTransfer); + + mSyncQuery.reset(); + + mCachedResolveTexture.reset(); +} + +// set notify to true to broadcast a message to all contexts of the device loss +bool Renderer11::testDeviceLost() +{ + if (!mDevice) + { + return true; + } + + // GetRemovedReason is used to test if the device is removed + HRESULT result = mDevice->GetDeviceRemovedReason(); + bool isLost = FAILED(result); + + if (isLost) + { + ERR() << "The D3D11 device was removed, " << gl::FmtHR(result); + } + + return isLost; +} + +bool Renderer11::testDeviceResettable() +{ + // determine if the device is resettable by creating a mock device + PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = + (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice"); + + if (D3D11CreateDevice == nullptr) + { + return false; + } + + ID3D11Device *mockDevice; + D3D_FEATURE_LEVEL mockFeatureLevel; + ID3D11DeviceContext *mockContext; + UINT flags = (mCreateDebugDevice ? D3D11_CREATE_DEVICE_DEBUG : 0); + + ASSERT(mRequestedDriverType != D3D_DRIVER_TYPE_UNKNOWN); + HRESULT result = D3D11CreateDevice( + nullptr, mRequestedDriverType, nullptr, flags, mAvailableFeatureLevels.data(), + static_cast<unsigned int>(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, &mockDevice, + &mockFeatureLevel, &mockContext); + + if (!mDevice || FAILED(result)) + { + return false; + } + + SafeRelease(mockContext); + SafeRelease(mockDevice); + + return true; +} + +void Renderer11::release() +{ + mScratchMemoryBuffer.clear(); + + mAnnotatorContext.release(); + gl::UninitializeDebugAnnotations(); + + releaseDeviceResources(); + + SafeRelease(mDxgiFactory); + SafeRelease(mDxgiAdapter); + + SafeRelease(mDeviceContext3); + SafeRelease(mDeviceContext1); + + if (mDeviceContext) + { + mDeviceContext->ClearState(); + mDeviceContext->Flush(); + SafeRelease(mDeviceContext); + } + + SafeRelease(mDevice); + SafeRelease(mDevice1); + SafeRelease(mDebug); + + if (mD3d11Module) + { + FreeLibrary(mD3d11Module); + mD3d11Module = nullptr; + } + + if (mDxgiModule) + { + FreeLibrary(mDxgiModule); + mDxgiModule = nullptr; + } + + if (mDCompModule) + { + FreeLibrary(mDCompModule); + mDCompModule = nullptr; + } + + mDevice12.Reset(); + mCommandQueue.Reset(); + + if (mD3d12Module) + { + FreeLibrary(mD3d12Module); + mD3d12Module = nullptr; + } + + mCompiler.release(); + + mSupportsShareHandles.reset(); +} + +bool Renderer11::resetDevice() +{ + // recreate everything + release(); + egl::Error result = initialize(); + + if (result.isError()) + { + ERR() << "Could not reinitialize D3D11 device: " << result; + return false; + } + + return true; +} + +std::string Renderer11::getRendererDescription() const +{ + std::ostringstream rendererString; + + rendererString << mDescription; + rendererString << " Direct3D11"; + + rendererString << " vs_" << getMajorShaderModel() << "_" << getMinorShaderModel() + << getShaderModelSuffix(); + rendererString << " ps_" << getMajorShaderModel() << "_" << getMinorShaderModel() + << getShaderModelSuffix(); + + return rendererString.str(); +} + +DeviceIdentifier Renderer11::getAdapterIdentifier() const +{ + // Don't use the AdapterLuid here, since that doesn't persist across reboot. + DeviceIdentifier deviceIdentifier = {}; + deviceIdentifier.VendorId = mAdapterDescription.VendorId; + deviceIdentifier.DeviceId = mAdapterDescription.DeviceId; + deviceIdentifier.SubSysId = mAdapterDescription.SubSysId; + deviceIdentifier.Revision = mAdapterDescription.Revision; + deviceIdentifier.FeatureLevel = static_cast<UINT>(mRenderer11DeviceCaps.featureLevel); + + return deviceIdentifier; +} + +unsigned int Renderer11::getReservedVertexUniformVectors() const +{ + // Driver uniforms are stored in a separate constant buffer + return d3d11_gl::GetReservedVertexUniformVectors(mRenderer11DeviceCaps.featureLevel); +} + +unsigned int Renderer11::getReservedFragmentUniformVectors() const +{ + // Driver uniforms are stored in a separate constant buffer + return d3d11_gl::GetReservedFragmentUniformVectors(mRenderer11DeviceCaps.featureLevel); +} + +gl::ShaderMap<unsigned int> Renderer11::getReservedShaderUniformBuffers() const +{ + gl::ShaderMap<unsigned int> shaderReservedUniformBuffers = {}; + + // we reserve one buffer for the application uniforms, and one for driver uniforms + shaderReservedUniformBuffers[gl::ShaderType::Vertex] = 2; + shaderReservedUniformBuffers[gl::ShaderType::Fragment] = 2; + + return shaderReservedUniformBuffers; +} + +d3d11::ANGLED3D11DeviceType Renderer11::getDeviceType() const +{ + if (mCreatedWithDeviceEXT) + { + return d3d11::GetDeviceType(mDevice); + } + + if ((mRequestedDriverType == D3D_DRIVER_TYPE_SOFTWARE) || + (mRequestedDriverType == D3D_DRIVER_TYPE_REFERENCE) || + (mRequestedDriverType == D3D_DRIVER_TYPE_NULL)) + { + return d3d11::ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL; + } + + if (mRequestedDriverType == D3D_DRIVER_TYPE_WARP) + { + return d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP; + } + + return d3d11::ANGLE_D3D11_DEVICE_TYPE_HARDWARE; +} + +bool Renderer11::getShareHandleSupport() const +{ + if (mSupportsShareHandles.valid()) + { + return mSupportsShareHandles.value(); + } + + // We only currently support share handles with BGRA surfaces, because + // chrome needs BGRA. Once chrome fixes this, we should always support them. + if (!getNativeExtensions().textureFormatBGRA8888EXT) + { + mSupportsShareHandles = false; + return false; + } + + // PIX doesn't seem to support using share handles, so disable them. + if (mAnnotatorContext.getStatus()) + { + mSupportsShareHandles = false; + return false; + } + + // Also disable share handles on Feature Level 9_3, since it doesn't support share handles on + // RGBA8 textures/swapchains. + if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) + { + mSupportsShareHandles = false; + return false; + } + + // Find out which type of D3D11 device the Renderer11 is using + d3d11::ANGLED3D11DeviceType deviceType = getDeviceType(); + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_UNKNOWN) + { + mSupportsShareHandles = false; + return false; + } + + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_SOFTWARE_REF_OR_NULL) + { + // Software/Reference/NULL devices don't support share handles + mSupportsShareHandles = false; + return false; + } + + if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP) + { +#if !defined(ANGLE_ENABLE_WINDOWS_UWP) + if (!IsWindows8OrGreater()) + { + // WARP on Windows 7 doesn't support shared handles + mSupportsShareHandles = false; + return false; + } +#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP) + + // WARP on Windows 8.0+ supports shared handles when shared with another WARP device + // TODO: allow applications to query for HARDWARE or WARP-specific share handles, + // to prevent them trying to use a WARP share handle with an a HW device (or + // vice-versa) + // e.g. by creating EGL_D3D11_[HARDWARE/WARP]_DEVICE_SHARE_HANDLE_ANGLE + mSupportsShareHandles = true; + return true; + } + + ASSERT(mCreatedWithDeviceEXT || mRequestedDriverType == D3D_DRIVER_TYPE_HARDWARE); + mSupportsShareHandles = true; + return true; +} + +int Renderer11::getMajorShaderModel() const +{ + switch (mRenderer11DeviceCaps.featureLevel) + { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + return D3D11_SHADER_MAJOR_VERSION; // 5 + case D3D_FEATURE_LEVEL_10_1: + return D3D10_1_SHADER_MAJOR_VERSION; // 4 + case D3D_FEATURE_LEVEL_10_0: + return D3D10_SHADER_MAJOR_VERSION; // 4 + case D3D_FEATURE_LEVEL_9_3: + return D3D10_SHADER_MAJOR_VERSION; // 4 + default: + UNREACHABLE(); + return 0; + } +} + +int Renderer11::getMinorShaderModel() const +{ + switch (mRenderer11DeviceCaps.featureLevel) + { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + return D3D11_SHADER_MINOR_VERSION; // 0 + case D3D_FEATURE_LEVEL_10_1: + return D3D10_1_SHADER_MINOR_VERSION; // 1 + case D3D_FEATURE_LEVEL_10_0: + return D3D10_SHADER_MINOR_VERSION; // 0 + case D3D_FEATURE_LEVEL_9_3: + return D3D10_SHADER_MINOR_VERSION; // 0 + default: + UNREACHABLE(); + return 0; + } +} + +std::string Renderer11::getShaderModelSuffix() const +{ + switch (mRenderer11DeviceCaps.featureLevel) + { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + return ""; + case D3D_FEATURE_LEVEL_10_1: + return ""; + case D3D_FEATURE_LEVEL_10_0: + return ""; + case D3D_FEATURE_LEVEL_9_3: + return "_level_9_3"; + default: + UNREACHABLE(); + return ""; + } +} + +angle::Result Renderer11::copyImageInternal(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::Rectangle &sourceRect, + GLenum destFormat, + const gl::Offset &destOffset, + RenderTargetD3D *destRenderTarget) +{ + const gl::FramebufferAttachment *colorAttachment = framebuffer->getReadColorAttachment(); + ASSERT(colorAttachment); + + RenderTarget11 *sourceRenderTarget = nullptr; + ANGLE_TRY(colorAttachment->getRenderTarget(context, 0, &sourceRenderTarget)); + ASSERT(sourceRenderTarget); + + const d3d11::RenderTargetView &dest = + GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView(); + ASSERT(dest.valid()); + + gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + + const bool invertSource = UsePresentPathFast(this, colorAttachment); + if (invertSource) + { + sourceArea.y = sourceSize.height - sourceRect.y; + sourceArea.height = -sourceArea.height; + } + + gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1); + + // Use nearest filtering because source and destination are the same size for the direct copy. + // Convert to the unsized format before calling copyTexture. + GLenum sourceFormat = colorAttachment->getFormat().info->format; + if (sourceRenderTarget->getTexture().is2D() && sourceRenderTarget->isMultisampled()) + { + TextureHelper11 tex; + ANGLE_TRY(resolveMultisampledTexture(context, sourceRenderTarget, + colorAttachment->getDepthSize() > 0, + colorAttachment->getStencilSize() > 0, &tex)); + + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + viewDesc.Format = sourceRenderTarget->getFormatSet().srvFormat; + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipLevels = 1; + viewDesc.Texture2D.MostDetailedMip = 0; + + d3d11::SharedSRV readSRV; + ANGLE_TRY(allocateResource(GetImplAs<Context11>(context), viewDesc, tex.get(), &readSRV)); + ASSERT(readSRV.valid()); + + ANGLE_TRY(mBlit->copyTexture(context, readSRV, sourceArea, sourceSize, sourceFormat, dest, + destArea, destSize, nullptr, gl::GetUnsizedFormat(destFormat), + GL_NONE, GL_NEAREST, false, false, false)); + + return angle::Result::Continue; + } + + ASSERT(!sourceRenderTarget->isMultisampled()); + + const d3d11::SharedSRV *source; + ANGLE_TRY(sourceRenderTarget->getBlitShaderResourceView(context, &source)); + ASSERT(source->valid()); + + ANGLE_TRY(mBlit->copyTexture(context, *source, sourceArea, sourceSize, sourceFormat, dest, + destArea, destSize, nullptr, gl::GetUnsizedFormat(destFormat), + GL_NONE, GL_NEAREST, false, false, false)); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyImage2D(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::Rectangle &sourceRect, + GLenum destFormat, + const gl::Offset &destOffset, + TextureStorage *storage, + GLint level) +{ + TextureStorage11_2D *storage11 = GetAs<TextureStorage11_2D>(storage); + ASSERT(storage11); + + gl::ImageIndex index = gl::ImageIndex::Make2D(level); + RenderTargetD3D *destRenderTarget = nullptr; + ANGLE_TRY(storage11->getRenderTarget(context, index, storage11->getRenderToTextureSamples(), + &destRenderTarget)); + ASSERT(destRenderTarget); + + ANGLE_TRY(copyImageInternal(context, framebuffer, sourceRect, destFormat, destOffset, + destRenderTarget)); + + storage11->markLevelDirty(level); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyImageCube(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::Rectangle &sourceRect, + GLenum destFormat, + const gl::Offset &destOffset, + TextureStorage *storage, + gl::TextureTarget target, + GLint level) +{ + TextureStorage11_Cube *storage11 = GetAs<TextureStorage11_Cube>(storage); + ASSERT(storage11); + + gl::ImageIndex index = gl::ImageIndex::MakeCubeMapFace(target, level); + RenderTargetD3D *destRenderTarget = nullptr; + ANGLE_TRY(storage11->getRenderTarget(context, index, storage11->getRenderToTextureSamples(), + &destRenderTarget)); + ASSERT(destRenderTarget); + + ANGLE_TRY(copyImageInternal(context, framebuffer, sourceRect, destFormat, destOffset, + destRenderTarget)); + + storage11->markLevelDirty(level); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyImage3D(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::Rectangle &sourceRect, + GLenum destFormat, + const gl::Offset &destOffset, + TextureStorage *storage, + GLint level) +{ + TextureStorage11_3D *storage11 = GetAs<TextureStorage11_3D>(storage); + ASSERT(storage11); + + gl::ImageIndex index = gl::ImageIndex::Make3D(level, destOffset.z); + RenderTargetD3D *destRenderTarget = nullptr; + ANGLE_TRY(storage11->getRenderTarget(context, index, storage11->getRenderToTextureSamples(), + &destRenderTarget)); + ASSERT(destRenderTarget); + + ANGLE_TRY(copyImageInternal(context, framebuffer, sourceRect, destFormat, destOffset, + destRenderTarget)); + + storage11->markLevelDirty(level); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyImage2DArray(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::Rectangle &sourceRect, + GLenum destFormat, + const gl::Offset &destOffset, + TextureStorage *storage, + GLint level) +{ + TextureStorage11_2DArray *storage11 = GetAs<TextureStorage11_2DArray>(storage); + ASSERT(storage11); + + gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, destOffset.z); + RenderTargetD3D *destRenderTarget = nullptr; + ANGLE_TRY(storage11->getRenderTarget(context, index, storage11->getRenderToTextureSamples(), + &destRenderTarget)); + ASSERT(destRenderTarget); + + ANGLE_TRY(copyImageInternal(context, framebuffer, sourceRect, destFormat, destOffset, + destRenderTarget)); + storage11->markLevelDirty(level); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyTexture(const gl::Context *context, + const gl::Texture *source, + GLint sourceLevel, + gl::TextureTarget srcTarget, + const gl::Box &sourceBox, + GLenum destFormat, + GLenum destType, + const gl::Offset &destOffset, + TextureStorage *storage, + gl::TextureTarget destTarget, + GLint destLevel, + bool unpackFlipY, + bool unpackPremultiplyAlpha, + bool unpackUnmultiplyAlpha) +{ + + TextureD3D *sourceD3D = GetImplAs<TextureD3D>(source); + const gl::ImageDesc &sourceImageDesc = source->getTextureState().getImageDesc( + NonCubeTextureTypeToTarget(source->getType()), sourceLevel); + + TextureStorage11 *destStorage11 = GetAs<TextureStorage11>(storage); + ASSERT(destStorage11); + + // Check for fast path where a CopySubresourceRegion can be used. + if (unpackPremultiplyAlpha == unpackUnmultiplyAlpha && !unpackFlipY && + sourceImageDesc.format.info->sizedInternalFormat == + destStorage11->getFormatSet().internalFormat) + { + const TextureHelper11 *destResource = nullptr; + ANGLE_TRY(destStorage11->getResource(context, &destResource)); + + if (srcTarget == gl::TextureTarget::_2D || srcTarget == gl::TextureTarget::_3D) + { + gl::ImageIndex sourceIndex = gl::ImageIndex::MakeFromTarget(srcTarget, sourceLevel, 1); + const TextureHelper11 *sourceResource = nullptr; + UINT sourceSubresource = 0; + ANGLE_TRY(GetTextureD3DResourceFromStorageOrImage(context, sourceD3D, sourceIndex, + &sourceResource, &sourceSubresource)); + + gl::ImageIndex destIndex = gl::ImageIndex::MakeFromTarget(destTarget, destLevel, 1); + + UINT destSubresource = 0; + ANGLE_TRY(destStorage11->getSubresourceIndex(context, destIndex, &destSubresource)); + + D3D11_BOX d3dBox{static_cast<UINT>(sourceBox.x), + static_cast<UINT>(sourceBox.y), + static_cast<UINT>(sourceBox.z), + static_cast<UINT>(sourceBox.x + sourceBox.width), + static_cast<UINT>(sourceBox.y + sourceBox.height), + static_cast<UINT>(sourceBox.z + sourceBox.depth)}; + + mDeviceContext->CopySubresourceRegion( + destResource->get(), destSubresource, destOffset.x, destOffset.y, destOffset.z, + sourceResource->get(), sourceSubresource, &d3dBox); + } + else if (srcTarget == gl::TextureTarget::_2DArray) + { + D3D11_BOX d3dBox{static_cast<UINT>(sourceBox.x), + static_cast<UINT>(sourceBox.y), + 0, + static_cast<UINT>(sourceBox.x + sourceBox.width), + static_cast<UINT>(sourceBox.y + sourceBox.height), + 1u}; + + for (int i = 0; i < sourceBox.depth; i++) + { + gl::ImageIndex sourceIndex = + gl::ImageIndex::Make2DArray(sourceLevel, i + sourceBox.z); + const TextureHelper11 *sourceResource = nullptr; + UINT sourceSubresource = 0; + ANGLE_TRY(GetTextureD3DResourceFromStorageOrImage( + context, sourceD3D, sourceIndex, &sourceResource, &sourceSubresource)); + + gl::ImageIndex dIndex = gl::ImageIndex::Make2DArray(destLevel, i + destOffset.z); + UINT destSubresource = 0; + ANGLE_TRY(destStorage11->getSubresourceIndex(context, dIndex, &destSubresource)); + + mDeviceContext->CopySubresourceRegion( + destResource->get(), destSubresource, destOffset.x, destOffset.y, 0, + sourceResource->get(), sourceSubresource, &d3dBox); + } + } + else + { + UNREACHABLE(); + } + } + else + { + TextureStorage *sourceStorage = nullptr; + ANGLE_TRY(sourceD3D->getNativeTexture(context, &sourceStorage)); + + TextureStorage11 *sourceStorage11 = GetAs<TextureStorage11>(sourceStorage); + ASSERT(sourceStorage11); + + const d3d11::SharedSRV *sourceSRV = nullptr; + ANGLE_TRY(sourceStorage11->getSRVLevels(context, sourceLevel, sourceLevel, &sourceSRV)); + + gl::ImageIndex destIndex; + if (destTarget == gl::TextureTarget::_2D || destTarget == gl::TextureTarget::_3D || + gl::IsCubeMapFaceTarget(destTarget)) + { + destIndex = gl::ImageIndex::MakeFromTarget(destTarget, destLevel, 1); + } + else if (destTarget == gl::TextureTarget::_2DArray) + { + destIndex = gl::ImageIndex::Make2DArrayRange(destLevel, 0, sourceImageDesc.size.depth); + } + else + { + UNREACHABLE(); + } + + RenderTargetD3D *destRenderTargetD3D = nullptr; + ANGLE_TRY(destStorage11->getRenderTarget( + context, destIndex, destStorage11->getRenderToTextureSamples(), &destRenderTargetD3D)); + + RenderTarget11 *destRenderTarget11 = GetAs<RenderTarget11>(destRenderTargetD3D); + + const d3d11::RenderTargetView &destRTV = destRenderTarget11->getRenderTargetView(); + ASSERT(destRTV.valid()); + + gl::Box sourceArea(sourceBox.x, sourceBox.y, sourceBox.z, sourceBox.width, sourceBox.height, + sourceBox.depth); + + if (unpackFlipY) + { + sourceArea.y += sourceArea.height; + sourceArea.height = -sourceArea.height; + } + + gl::Box destArea(destOffset.x, destOffset.y, destOffset.z, sourceBox.width, + sourceBox.height, sourceBox.depth); + + gl::Extents destSize(destRenderTarget11->getWidth(), destRenderTarget11->getHeight(), + sourceBox.depth); + + // Use nearest filtering because source and destination are the same size for the direct + // copy + GLenum sourceFormat = source->getFormat(srcTarget, sourceLevel).info->format; + ANGLE_TRY(mBlit->copyTexture(context, *sourceSRV, sourceArea, sourceImageDesc.size, + sourceFormat, destRTV, destArea, destSize, nullptr, destFormat, + destType, GL_NEAREST, false, unpackPremultiplyAlpha, + unpackUnmultiplyAlpha)); + } + + destStorage11->markLevelDirty(destLevel); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyCompressedTexture(const gl::Context *context, + const gl::Texture *source, + GLint sourceLevel, + TextureStorage *storage, + GLint destLevel) +{ + TextureStorage11_2D *destStorage11 = GetAs<TextureStorage11_2D>(storage); + ASSERT(destStorage11); + + const TextureHelper11 *destResource = nullptr; + ANGLE_TRY(destStorage11->getResource(context, &destResource)); + + gl::ImageIndex destIndex = gl::ImageIndex::Make2D(destLevel); + UINT destSubresource = 0; + ANGLE_TRY(destStorage11->getSubresourceIndex(context, destIndex, &destSubresource)); + + TextureD3D *sourceD3D = GetImplAs<TextureD3D>(source); + ASSERT(sourceD3D); + + TextureStorage *sourceStorage = nullptr; + ANGLE_TRY(sourceD3D->getNativeTexture(context, &sourceStorage)); + + TextureStorage11_2D *sourceStorage11 = GetAs<TextureStorage11_2D>(sourceStorage); + ASSERT(sourceStorage11); + + const TextureHelper11 *sourceResource = nullptr; + ANGLE_TRY(sourceStorage11->getResource(context, &sourceResource)); + + gl::ImageIndex sourceIndex = gl::ImageIndex::Make2D(sourceLevel); + UINT sourceSubresource = 0; + ANGLE_TRY(sourceStorage11->getSubresourceIndex(context, sourceIndex, &sourceSubresource)); + + mDeviceContext->CopySubresourceRegion(destResource->get(), destSubresource, 0, 0, 0, + sourceResource->get(), sourceSubresource, nullptr); + + return angle::Result::Continue; +} + +angle::Result Renderer11::createRenderTarget(const gl::Context *context, + int width, + int height, + GLenum format, + GLsizei samples, + RenderTargetD3D **outRT) +{ + const d3d11::Format &formatInfo = d3d11::Format::Get(format, mRenderer11DeviceCaps); + + const gl::TextureCaps &textureCaps = getNativeTextureCaps().get(format); + GLuint supportedSamples = textureCaps.getNearestSamples(samples); + + Context11 *context11 = GetImplAs<Context11>(context); + + if (width > 0 && height > 0) + { + // Create texture resource + D3D11_TEXTURE2D_DESC desc; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = formatInfo.texFormat; + desc.SampleDesc.Count = (supportedSamples == 0) ? 1 : supportedSamples; + desc.SampleDesc.Quality = getSampleDescQuality(supportedSamples); + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + // If a rendertarget or depthstencil format exists for this texture format, + // we'll flag it to allow binding that way. Shader resource views are a little + // more complicated. + bool bindRTV = false, bindDSV = false, bindSRV = false; + bindRTV = (formatInfo.rtvFormat != DXGI_FORMAT_UNKNOWN); + bindDSV = (formatInfo.dsvFormat != DXGI_FORMAT_UNKNOWN); + bindSRV = (formatInfo.srvFormat != DXGI_FORMAT_UNKNOWN); + + bool isMultisampledDepthStencil = bindDSV && desc.SampleDesc.Count > 1; + if (isMultisampledDepthStencil && + !mRenderer11DeviceCaps.supportsMultisampledDepthStencilSRVs) + { + bindSRV = false; + } + + desc.BindFlags = (bindRTV ? D3D11_BIND_RENDER_TARGET : 0) | + (bindDSV ? D3D11_BIND_DEPTH_STENCIL : 0) | + (bindSRV ? D3D11_BIND_SHADER_RESOURCE : 0); + + // The format must be either an RTV or a DSV + ASSERT(bindRTV != bindDSV); + + TextureHelper11 texture; + ANGLE_TRY(allocateTexture(context11, desc, formatInfo, &texture)); + texture.setInternalName("createRenderTarget.Texture"); + + d3d11::SharedSRV srv; + d3d11::SharedSRV blitSRV; + if (bindSRV) + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = formatInfo.srvFormat; + srvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_SRV_DIMENSION_TEXTURE2D + : D3D11_SRV_DIMENSION_TEXTURE2DMS; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + + ANGLE_TRY(allocateResource(context11, srvDesc, texture.get(), &srv)); + srv.setInternalName("createRenderTarget.SRV"); + + if (formatInfo.blitSRVFormat != formatInfo.srvFormat) + { + D3D11_SHADER_RESOURCE_VIEW_DESC blitSRVDesc; + blitSRVDesc.Format = formatInfo.blitSRVFormat; + blitSRVDesc.ViewDimension = (supportedSamples == 0) + ? D3D11_SRV_DIMENSION_TEXTURE2D + : D3D11_SRV_DIMENSION_TEXTURE2DMS; + blitSRVDesc.Texture2D.MostDetailedMip = 0; + blitSRVDesc.Texture2D.MipLevels = 1; + + ANGLE_TRY(allocateResource(context11, blitSRVDesc, texture.get(), &blitSRV)); + blitSRV.setInternalName("createRenderTarget.BlitSRV"); + } + else + { + blitSRV = srv.makeCopy(); + } + } + + if (bindDSV) + { + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; + dsvDesc.Format = formatInfo.dsvFormat; + dsvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_DSV_DIMENSION_TEXTURE2D + : D3D11_DSV_DIMENSION_TEXTURE2DMS; + dsvDesc.Texture2D.MipSlice = 0; + dsvDesc.Flags = 0; + + d3d11::DepthStencilView dsv; + ANGLE_TRY(allocateResource(context11, dsvDesc, texture.get(), &dsv)); + dsv.setInternalName("createRenderTarget.DSV"); + + *outRT = new TextureRenderTarget11(std::move(dsv), texture, srv, format, formatInfo, + width, height, 1, supportedSamples); + } + else if (bindRTV) + { + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.Format = formatInfo.rtvFormat; + rtvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_RTV_DIMENSION_TEXTURE2D + : D3D11_RTV_DIMENSION_TEXTURE2DMS; + rtvDesc.Texture2D.MipSlice = 0; + + d3d11::RenderTargetView rtv; + ANGLE_TRY(allocateResource(context11, rtvDesc, texture.get(), &rtv)); + rtv.setInternalName("createRenderTarget.RTV"); + + if (formatInfo.dataInitializerFunction != nullptr) + { + const float clearValues[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + mDeviceContext->ClearRenderTargetView(rtv.get(), clearValues); + } + + *outRT = new TextureRenderTarget11(std::move(rtv), texture, srv, blitSRV, format, + formatInfo, width, height, 1, supportedSamples); + } + else + { + UNREACHABLE(); + } + } + else + { + *outRT = new TextureRenderTarget11(d3d11::RenderTargetView(), TextureHelper11(), + d3d11::SharedSRV(), d3d11::SharedSRV(), format, + d3d11::Format::Get(GL_NONE, mRenderer11DeviceCaps), + width, height, 1, supportedSamples); + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::createRenderTargetCopy(const gl::Context *context, + RenderTargetD3D *source, + RenderTargetD3D **outRT) +{ + ASSERT(source != nullptr); + + RenderTargetD3D *newRT = nullptr; + ANGLE_TRY(createRenderTarget(context, source->getWidth(), source->getHeight(), + source->getInternalFormat(), source->getSamples(), &newRT)); + + RenderTarget11 *source11 = GetAs<RenderTarget11>(source); + RenderTarget11 *dest11 = GetAs<RenderTarget11>(newRT); + + mDeviceContext->CopySubresourceRegion(dest11->getTexture().get(), dest11->getSubresourceIndex(), + 0, 0, 0, source11->getTexture().get(), + source11->getSubresourceIndex(), nullptr); + *outRT = newRT; + return angle::Result::Continue; +} + +angle::Result Renderer11::loadExecutable(d3d::Context *context, + const uint8_t *function, + size_t length, + gl::ShaderType type, + const std::vector<D3DVarying> &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) +{ + ShaderData shaderData(function, length); + + switch (type) + { + case gl::ShaderType::Vertex: + { + d3d11::VertexShader vertexShader; + d3d11::GeometryShader streamOutShader; + ANGLE_TRY(allocateResource(context, shaderData, &vertexShader)); + + if (!streamOutVaryings.empty()) + { + std::vector<D3D11_SO_DECLARATION_ENTRY> soDeclaration; + soDeclaration.reserve(streamOutVaryings.size()); + + for (const auto &streamOutVarying : streamOutVaryings) + { + D3D11_SO_DECLARATION_ENTRY entry = {}; + entry.Stream = 0; + entry.SemanticName = streamOutVarying.semanticName.c_str(); + entry.SemanticIndex = streamOutVarying.semanticIndex; + entry.StartComponent = 0; + entry.ComponentCount = static_cast<BYTE>(streamOutVarying.componentCount); + entry.OutputSlot = static_cast<BYTE>( + (separatedOutputBuffers ? streamOutVarying.outputSlot : 0)); + soDeclaration.push_back(entry); + } + + ANGLE_TRY(allocateResource(context, shaderData, &soDeclaration, &streamOutShader)); + } + + *outExecutable = new ShaderExecutable11(function, length, std::move(vertexShader), + std::move(streamOutShader)); + } + break; + case gl::ShaderType::Fragment: + { + d3d11::PixelShader pixelShader; + ANGLE_TRY(allocateResource(context, shaderData, &pixelShader)); + *outExecutable = new ShaderExecutable11(function, length, std::move(pixelShader)); + } + break; + case gl::ShaderType::Geometry: + { + d3d11::GeometryShader geometryShader; + ANGLE_TRY(allocateResource(context, shaderData, &geometryShader)); + *outExecutable = new ShaderExecutable11(function, length, std::move(geometryShader)); + } + break; + case gl::ShaderType::Compute: + { + d3d11::ComputeShader computeShader; + ANGLE_TRY(allocateResource(context, shaderData, &computeShader)); + *outExecutable = new ShaderExecutable11(function, length, std::move(computeShader)); + } + break; + default: + ANGLE_HR_UNREACHABLE(context); + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::compileToExecutable(d3d::Context *context, + gl::InfoLog &infoLog, + const std::string &shaderHLSL, + gl::ShaderType type, + const std::vector<D3DVarying> &streamOutVaryings, + bool separatedOutputBuffers, + const CompilerWorkaroundsD3D &workarounds, + ShaderExecutableD3D **outExectuable) +{ + std::stringstream profileStream; + + switch (type) + { + case gl::ShaderType::Vertex: + profileStream << "vs"; + break; + case gl::ShaderType::Fragment: + profileStream << "ps"; + break; + case gl::ShaderType::Geometry: + profileStream << "gs"; + break; + case gl::ShaderType::Compute: + profileStream << "cs"; + break; + default: + ANGLE_HR_UNREACHABLE(context); + } + + profileStream << "_" << getMajorShaderModel() << "_" << getMinorShaderModel() + << getShaderModelSuffix(); + std::string profile = profileStream.str(); + + UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; + +#if defined(ANGLE_ENABLE_DEBUG_TRACE) +# ifndef NDEBUG + flags = D3DCOMPILE_SKIP_OPTIMIZATION; +# endif // NDEBUG + flags |= D3DCOMPILE_DEBUG; +#endif // defined(ANGLE_ENABLE_DEBUG_TRACE) + + if (workarounds.enableIEEEStrictness) + flags |= D3DCOMPILE_IEEE_STRICTNESS; + + // Sometimes D3DCompile will fail with the default compilation flags for complicated shaders + // when it would otherwise pass with alternative options. + // Try the default flags first and if compilation fails, try some alternatives. + std::vector<CompileConfig> configs; + configs.push_back(CompileConfig(flags, "default")); + configs.push_back(CompileConfig(flags | D3DCOMPILE_SKIP_VALIDATION, "skip validation")); + configs.push_back(CompileConfig(flags | D3DCOMPILE_SKIP_OPTIMIZATION, "skip optimization")); + + if (getMajorShaderModel() == 4 && getShaderModelSuffix() != "") + { + // Some shaders might cause a "blob content mismatch between level9 and d3d10 shader". + // e.g. dEQP-GLES2.functional.shaders.struct.local.loop_nested_struct_array_*. + // Using the [unroll] directive works around this, as does this D3DCompile flag. + configs.push_back( + CompileConfig(flags | D3DCOMPILE_AVOID_FLOW_CONTROL, "avoid flow control")); + } + + D3D_SHADER_MACRO loopMacros[] = {{"ANGLE_ENABLE_LOOP_FLATTEN", "1"}, {0, 0}}; + + // TODO(jmadill): Use ComPtr? + ID3DBlob *binary = nullptr; + std::string debugInfo; + ANGLE_TRY(mCompiler.compileToBinary(context, infoLog, shaderHLSL, profile, configs, loopMacros, + &binary, &debugInfo)); + + // It's possible that binary is NULL if the compiler failed in all configurations. Set the + // executable to NULL and return GL_NO_ERROR to signify that there was a link error but the + // internal state is still OK. + if (!binary) + { + *outExectuable = nullptr; + return angle::Result::Continue; + } + + angle::Result error = loadExecutable( + context, static_cast<const uint8_t *>(binary->GetBufferPointer()), binary->GetBufferSize(), + type, streamOutVaryings, separatedOutputBuffers, outExectuable); + + SafeRelease(binary); + if (error == angle::Result::Stop) + { + return error; + } + + if (!debugInfo.empty()) + { + (*outExectuable)->appendDebugInfo(debugInfo); + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::ensureHLSLCompilerInitialized(d3d::Context *context) +{ + return mCompiler.ensureInitialized(context); +} + +UniformStorageD3D *Renderer11::createUniformStorage(size_t storageSize) +{ + return new UniformStorage11(storageSize); +} + +VertexBuffer *Renderer11::createVertexBuffer() +{ + return new VertexBuffer11(this); +} + +IndexBuffer *Renderer11::createIndexBuffer() +{ + return new IndexBuffer11(this); +} + +StreamProducerImpl *Renderer11::createStreamProducerD3DTexture( + egl::Stream::ConsumerType consumerType, + const egl::AttributeMap &attribs) +{ + return new StreamProducerD3DTexture(this); +} + +bool Renderer11::supportsFastCopyBufferToTexture(GLenum internalFormat) const +{ + ASSERT(getNativeExtensions().pixelBufferObjectNV); + + const gl::InternalFormat &internalFormatInfo = gl::GetSizedInternalFormatInfo(internalFormat); + const d3d11::Format &d3d11FormatInfo = + d3d11::Format::Get(internalFormat, mRenderer11DeviceCaps); + + // sRGB formats do not work with D3D11 buffer SRVs + if (internalFormatInfo.colorEncoding == GL_SRGB) + { + return false; + } + + // We cannot support direct copies to non-color-renderable formats + if (d3d11FormatInfo.rtvFormat == DXGI_FORMAT_UNKNOWN) + { + return false; + } + + // We skip all 3-channel formats since sometimes format support is missing + if (internalFormatInfo.componentCount == 3) + { + return false; + } + + // We don't support formats which we can't represent without conversion + if (d3d11FormatInfo.format().glInternalFormat != internalFormat) + { + return false; + } + + // Buffer SRV creation for this format was not working on Windows 10. + if (d3d11FormatInfo.texFormat == DXGI_FORMAT_B5G5R5A1_UNORM) + { + return false; + } + + // This format is not supported as a buffer SRV. + if (d3d11FormatInfo.texFormat == DXGI_FORMAT_A8_UNORM) + { + return false; + } + + return true; +} + +angle::Result Renderer11::fastCopyBufferToTexture(const gl::Context *context, + const gl::PixelUnpackState &unpack, + gl::Buffer *unpackBuffer, + unsigned int offset, + RenderTargetD3D *destRenderTarget, + GLenum destinationFormat, + GLenum sourcePixelsType, + const gl::Box &destArea) +{ + ASSERT(supportsFastCopyBufferToTexture(destinationFormat)); + return mPixelTransfer->copyBufferToTexture(context, unpack, unpackBuffer, offset, + destRenderTarget, destinationFormat, + sourcePixelsType, destArea); +} + +ImageD3D *Renderer11::createImage() +{ + return new Image11(this); +} + +ExternalImageSiblingImpl *Renderer11::createExternalImageSibling(const gl::Context *context, + EGLenum target, + EGLClientBuffer buffer, + const egl::AttributeMap &attribs) +{ + switch (target) + { + case EGL_D3D11_TEXTURE_ANGLE: + return new ExternalImageSiblingImpl11(this, buffer, attribs); + + default: + UNREACHABLE(); + return nullptr; + } +} + +angle::Result Renderer11::generateMipmap(const gl::Context *context, ImageD3D *dest, ImageD3D *src) +{ + Image11 *dest11 = GetAs<Image11>(dest); + Image11 *src11 = GetAs<Image11>(src); + return Image11::GenerateMipmap(context, dest11, src11, mRenderer11DeviceCaps); +} + +angle::Result Renderer11::generateMipmapUsingD3D(const gl::Context *context, + TextureStorage *storage, + const gl::TextureState &textureState) +{ + TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage); + + ASSERT(storage11->isRenderTarget()); + ASSERT(storage11->supportsNativeMipmapFunction()); + + const d3d11::SharedSRV *srv = nullptr; + ANGLE_TRY(storage11->getSRVLevels(context, textureState.getEffectiveBaseLevel(), + textureState.getEffectiveMaxLevel(), &srv)); + + mDeviceContext->GenerateMips(srv->get()); + + return angle::Result::Continue; +} + +angle::Result Renderer11::copyImage(const gl::Context *context, + ImageD3D *dest, + ImageD3D *source, + const gl::Box &sourceBox, + const gl::Offset &destOffset, + bool unpackFlipY, + bool unpackPremultiplyAlpha, + bool unpackUnmultiplyAlpha) +{ + Image11 *dest11 = GetAs<Image11>(dest); + Image11 *src11 = GetAs<Image11>(source); + return Image11::CopyImage(context, dest11, src11, sourceBox, destOffset, unpackFlipY, + unpackPremultiplyAlpha, unpackUnmultiplyAlpha, mRenderer11DeviceCaps); +} + +TextureStorage *Renderer11::createTextureStorage2D(SwapChainD3D *swapChain, + const std::string &label) +{ + SwapChain11 *swapChain11 = GetAs<SwapChain11>(swapChain); + return new TextureStorage11_2D(this, swapChain11, label); +} + +TextureStorage *Renderer11::createTextureStorageEGLImage(EGLImageD3D *eglImage, + RenderTargetD3D *renderTargetD3D, + const std::string &label) +{ + return new TextureStorage11_EGLImage(this, eglImage, GetAs<RenderTarget11>(renderTargetD3D), + label); +} + +TextureStorage *Renderer11::createTextureStorageExternal( + egl::Stream *stream, + const egl::Stream::GLTextureDescription &desc, + const std::string &label) +{ + return new TextureStorage11_External(this, stream, desc, label); +} + +TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, + BindFlags bindFlags, + GLsizei width, + GLsizei height, + int levels, + const std::string &label, + bool hintLevelZeroOnly) +{ + return new TextureStorage11_2D(this, internalformat, bindFlags, width, height, levels, label, + hintLevelZeroOnly); +} + +TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, + BindFlags bindFlags, + int size, + int levels, + bool hintLevelZeroOnly, + const std::string &label) +{ + return new TextureStorage11_Cube(this, internalformat, bindFlags, size, levels, + hintLevelZeroOnly, label); +} + +TextureStorage *Renderer11::createTextureStorage3D(GLenum internalformat, + BindFlags bindFlags, + GLsizei width, + GLsizei height, + GLsizei depth, + int levels, + const std::string &label) +{ + return new TextureStorage11_3D(this, internalformat, bindFlags, width, height, depth, levels, + label); +} + +TextureStorage *Renderer11::createTextureStorage2DArray(GLenum internalformat, + BindFlags bindFlags, + GLsizei width, + GLsizei height, + GLsizei depth, + int levels, + const std::string &label) +{ + return new TextureStorage11_2DArray(this, internalformat, bindFlags, width, height, depth, + levels, label); +} + +TextureStorage *Renderer11::createTextureStorage2DMultisample(GLenum internalformat, + GLsizei width, + GLsizei height, + int levels, + int samples, + bool fixedSampleLocations, + const std::string &label) +{ + return new TextureStorage11_2DMultisample(this, internalformat, width, height, levels, samples, + fixedSampleLocations, label); +} + +TextureStorage *Renderer11::createTextureStorageBuffer( + const gl::OffsetBindingPointer<gl::Buffer> &buffer, + GLenum internalFormat, + const std::string &label) +{ + return new TextureStorage11_Buffer(this, buffer, internalFormat, label); +} + +TextureStorage *Renderer11::createTextureStorage2DMultisampleArray(GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + int levels, + int samples, + bool fixedSampleLocations, + const std::string &label) +{ + return new TextureStorage11_2DMultisampleArray(this, internalformat, width, height, depth, + levels, samples, fixedSampleLocations, label); +} + +angle::Result Renderer11::readFromAttachment(const gl::Context *context, + const gl::FramebufferAttachment &srcAttachment, + const gl::Rectangle &sourceArea, + GLenum format, + GLenum type, + GLuint outputPitch, + const gl::PixelPackState &pack, + uint8_t *pixelsOut) +{ + ASSERT(sourceArea.width >= 0); + ASSERT(sourceArea.height >= 0); + + const bool invertTexture = UsePresentPathFast(this, &srcAttachment); + + RenderTarget11 *rt11 = nullptr; + ANGLE_TRY(srcAttachment.getRenderTarget(context, 0, &rt11)); + ASSERT(rt11->getTexture().valid()); + + const TextureHelper11 &textureHelper = rt11->getTexture(); + unsigned int sourceSubResource = rt11->getSubresourceIndex(); + + const gl::Extents &texSize = textureHelper.getExtents(); + + gl::Rectangle actualArea = sourceArea; + bool reverseRowOrder = pack.reverseRowOrder; + if (invertTexture) + { + actualArea.y = texSize.height - actualArea.y - actualArea.height; + reverseRowOrder = !reverseRowOrder; + } + + // Clamp read region to the defined texture boundaries, preventing out of bounds reads + // and reads of uninitialized data. + gl::Rectangle safeArea; + safeArea.x = gl::clamp(actualArea.x, 0, texSize.width); + safeArea.y = gl::clamp(actualArea.y, 0, texSize.height); + safeArea.width = + gl::clamp(actualArea.width + std::min(actualArea.x, 0), 0, texSize.width - safeArea.x); + safeArea.height = + gl::clamp(actualArea.height + std::min(actualArea.y, 0), 0, texSize.height - safeArea.y); + + ASSERT(safeArea.x >= 0 && safeArea.y >= 0); + ASSERT(safeArea.x + safeArea.width <= texSize.width); + ASSERT(safeArea.y + safeArea.height <= texSize.height); + + if (safeArea.width == 0 || safeArea.height == 0) + { + // no work to do + return angle::Result::Continue; + } + + gl::Extents safeSize(safeArea.width, safeArea.height, 1); + TextureHelper11 stagingHelper; + ANGLE_TRY(createStagingTexture(context, textureHelper.getTextureType(), + textureHelper.getFormatSet(), safeSize, StagingAccess::READ, + &stagingHelper)); + stagingHelper.setInternalName("readFromAttachment::stagingHelper"); + + TextureHelper11 resolvedTextureHelper; + + // "srcTexture" usually points to the source texture. + // For 2D multisampled textures, it points to the multisampled resolve texture. + const TextureHelper11 *srcTexture = &textureHelper; + + if (textureHelper.is2D() && textureHelper.getSampleCount() > 1) + { + D3D11_TEXTURE2D_DESC resolveDesc; + resolveDesc.Width = static_cast<UINT>(texSize.width); + resolveDesc.Height = static_cast<UINT>(texSize.height); + resolveDesc.MipLevels = 1; + resolveDesc.ArraySize = 1; + resolveDesc.Format = textureHelper.getFormat(); + resolveDesc.SampleDesc.Count = 1; + resolveDesc.SampleDesc.Quality = 0; + resolveDesc.Usage = D3D11_USAGE_DEFAULT; + resolveDesc.BindFlags = 0; + resolveDesc.CPUAccessFlags = 0; + resolveDesc.MiscFlags = 0; + + ANGLE_TRY(allocateTexture(GetImplAs<Context11>(context), resolveDesc, + textureHelper.getFormatSet(), &resolvedTextureHelper)); + resolvedTextureHelper.setInternalName("readFromAttachment::resolvedTextureHelper"); + + mDeviceContext->ResolveSubresource(resolvedTextureHelper.get(), 0, textureHelper.get(), + sourceSubResource, textureHelper.getFormat()); + + sourceSubResource = 0; + srcTexture = &resolvedTextureHelper; + } + + D3D11_BOX srcBox; + srcBox.left = static_cast<UINT>(safeArea.x); + srcBox.right = static_cast<UINT>(safeArea.x + safeArea.width); + srcBox.top = static_cast<UINT>(safeArea.y); + srcBox.bottom = static_cast<UINT>(safeArea.y + safeArea.height); + + // Select the correct layer from a 3D attachment + srcBox.front = 0; + if (textureHelper.is3D()) + { + srcBox.front = static_cast<UINT>(srcAttachment.layer()); + } + srcBox.back = srcBox.front + 1; + + mDeviceContext->CopySubresourceRegion(stagingHelper.get(), 0, 0, 0, 0, srcTexture->get(), + sourceSubResource, &srcBox); + + const angle::Format &angleFormat = GetFormatFromFormatType(format, type); + gl::Buffer *packBuffer = context->getState().getTargetBuffer(gl::BufferBinding::PixelPack); + + PackPixelsParams packParams(safeArea, angleFormat, outputPitch, reverseRowOrder, packBuffer, 0); + return packPixels(context, stagingHelper, packParams, pixelsOut); +} + +angle::Result Renderer11::packPixels(const gl::Context *context, + const TextureHelper11 &textureHelper, + const PackPixelsParams ¶ms, + uint8_t *pixelsOut) +{ + ID3D11Resource *readResource = textureHelper.get(); + + D3D11_MAPPED_SUBRESOURCE mapping; + ANGLE_TRY(mapResource(context, readResource, 0, D3D11_MAP_READ, 0, &mapping)); + + uint8_t *source = static_cast<uint8_t *>(mapping.pData); + int inputPitch = static_cast<int>(mapping.RowPitch); + + const auto &formatInfo = textureHelper.getFormatSet(); + ASSERT(formatInfo.format().glInternalFormat != GL_NONE); + + PackPixels(params, formatInfo.format(), inputPitch, source, pixelsOut); + + mDeviceContext->Unmap(readResource, 0); + + return angle::Result::Continue; +} + +angle::Result Renderer11::blitRenderbufferRect(const gl::Context *context, + const gl::Rectangle &readRectIn, + const gl::Rectangle &drawRectIn, + UINT readLayer, + UINT drawLayer, + RenderTargetD3D *readRenderTarget, + RenderTargetD3D *drawRenderTarget, + GLenum filter, + const gl::Rectangle *scissor, + bool colorBlit, + bool depthBlit, + bool stencilBlit) +{ + // Since blitRenderbufferRect is called for each render buffer that needs to be blitted, + // it should never be the case that both color and depth/stencil need to be blitted at + // at the same time. + ASSERT(colorBlit != (depthBlit || stencilBlit)); + + RenderTarget11 *drawRenderTarget11 = GetAs<RenderTarget11>(drawRenderTarget); + ASSERT(drawRenderTarget11); + + const TextureHelper11 &drawTexture = drawRenderTarget11->getTexture(); + unsigned int drawSubresource = drawRenderTarget11->getSubresourceIndex(); + + RenderTarget11 *readRenderTarget11 = GetAs<RenderTarget11>(readRenderTarget); + ASSERT(readRenderTarget11); + + const gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1); + const gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1); + + // From the spec: + // "The actual region taken from the read framebuffer is limited to the intersection of the + // source buffers being transferred, which may include the color buffer selected by the read + // buffer, the depth buffer, and / or the stencil buffer depending on mask." + // This means negative x and y are out of bounds, and not to be read from. We handle this here + // by internally scaling the read and draw rectangles. + + // Remove reversal from readRect to simplify further operations. + gl::Rectangle readRect = readRectIn; + gl::Rectangle drawRect = drawRectIn; + if (readRect.isReversedX()) + { + readRect.x = readRect.x + readRect.width; + readRect.width = -readRect.width; + drawRect.x = drawRect.x + drawRect.width; + drawRect.width = -drawRect.width; + } + if (readRect.isReversedY()) + { + readRect.y = readRect.y + readRect.height; + readRect.height = -readRect.height; + drawRect.y = drawRect.y + drawRect.height; + drawRect.height = -drawRect.height; + } + + gl::Rectangle readBounds(0, 0, readSize.width, readSize.height); + gl::Rectangle inBoundsReadRect; + if (!gl::ClipRectangle(readRect, readBounds, &inBoundsReadRect)) + { + return angle::Result::Continue; + } + + { + // Calculate the drawRect that corresponds to inBoundsReadRect. + auto readToDrawX = [&drawRect, &readRect](int readOffset) { + double readToDrawScale = + static_cast<double>(drawRect.width) / static_cast<double>(readRect.width); + return static_cast<int>( + round(static_cast<double>(readOffset - readRect.x) * readToDrawScale) + drawRect.x); + }; + auto readToDrawY = [&drawRect, &readRect](int readOffset) { + double readToDrawScale = + static_cast<double>(drawRect.height) / static_cast<double>(readRect.height); + return static_cast<int>( + round(static_cast<double>(readOffset - readRect.y) * readToDrawScale) + drawRect.y); + }; + + gl::Rectangle drawRectMatchingInBoundsReadRect; + drawRectMatchingInBoundsReadRect.x = readToDrawX(inBoundsReadRect.x); + drawRectMatchingInBoundsReadRect.y = readToDrawY(inBoundsReadRect.y); + drawRectMatchingInBoundsReadRect.width = + readToDrawX(inBoundsReadRect.x1()) - drawRectMatchingInBoundsReadRect.x; + drawRectMatchingInBoundsReadRect.height = + readToDrawY(inBoundsReadRect.y1()) - drawRectMatchingInBoundsReadRect.y; + drawRect = drawRectMatchingInBoundsReadRect; + readRect = inBoundsReadRect; + } + + bool scissorNeeded = false; + if (scissor) + { + gl::Rectangle scissoredDrawRect; + if (!gl::ClipRectangle(drawRect, *scissor, &scissoredDrawRect)) + { + return angle::Result::Continue; + } + scissorNeeded = scissoredDrawRect != drawRect; + } + + const auto &destFormatInfo = + gl::GetSizedInternalFormatInfo(drawRenderTarget->getInternalFormat()); + const auto &srcFormatInfo = + gl::GetSizedInternalFormatInfo(readRenderTarget->getInternalFormat()); + const auto &formatSet = drawRenderTarget11->getFormatSet(); + const auto &nativeFormat = formatSet.format(); + + // Some blits require masking off emulated texture channels. eg: from RGBA8 to RGB8, we + // emulate RGB8 with RGBA8, so we need to mask off the alpha channel when we copy. + + gl::Color<bool> colorMask; + colorMask.red = + (srcFormatInfo.redBits > 0) && (destFormatInfo.redBits == 0) && (nativeFormat.redBits > 0); + colorMask.green = (srcFormatInfo.greenBits > 0) && (destFormatInfo.greenBits == 0) && + (nativeFormat.greenBits > 0); + colorMask.blue = (srcFormatInfo.blueBits > 0) && (destFormatInfo.blueBits == 0) && + (nativeFormat.blueBits > 0); + colorMask.alpha = (srcFormatInfo.alphaBits > 0) && (destFormatInfo.alphaBits == 0) && + (nativeFormat.alphaBits > 0); + + // We only currently support masking off the alpha channel. + bool colorMaskingNeeded = colorMask.alpha; + ASSERT(!colorMask.red && !colorMask.green && !colorMask.blue); + + bool wholeBufferCopy = !scissorNeeded && !colorMaskingNeeded && readRect.x == 0 && + readRect.width == readSize.width && readRect.y == 0 && + readRect.height == readSize.height && drawRect.x == 0 && + drawRect.width == drawSize.width && drawRect.y == 0 && + drawRect.height == drawSize.height; + + bool stretchRequired = readRect.width != drawRect.width || readRect.height != drawRect.height; + + ASSERT(!readRect.isReversedX() && !readRect.isReversedY()); + bool reversalRequired = drawRect.isReversedX() || drawRect.isReversedY(); + + bool outOfBounds = readRect.x < 0 || readRect.x + readRect.width > readSize.width || + readRect.y < 0 || readRect.y + readRect.height > readSize.height || + drawRect.x < 0 || drawRect.x + drawRect.width > drawSize.width || + drawRect.y < 0 || drawRect.y + drawRect.height > drawSize.height; + + bool partialDSBlit = + (nativeFormat.depthBits > 0 && depthBlit) != (nativeFormat.stencilBits > 0 && stencilBlit); + + const bool canCopySubresource = + drawRenderTarget->getSamples() == readRenderTarget->getSamples() && + readRenderTarget11->getFormatSet().formatID == + drawRenderTarget11->getFormatSet().formatID && + !stretchRequired && !outOfBounds && !reversalRequired && !partialDSBlit && + !colorMaskingNeeded && (!(depthBlit || stencilBlit) || wholeBufferCopy); + + TextureHelper11 readTexture; + unsigned int readSubresource = 0; + d3d11::SharedSRV readSRV; + + if (readRenderTarget->isMultisampled()) + { + ANGLE_TRY(resolveMultisampledTexture(context, readRenderTarget11, depthBlit, stencilBlit, + &readTexture)); + + if (!stencilBlit && !canCopySubresource) + { + const auto &readFormatSet = readTexture.getFormatSet(); + + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + viewDesc.Format = readFormatSet.srvFormat; + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipLevels = 1; + viewDesc.Texture2D.MostDetailedMip = 0; + + ANGLE_TRY(allocateResource(GetImplAs<Context11>(context), viewDesc, readTexture.get(), + &readSRV)); + } + } + else + { + ASSERT(readRenderTarget11); + readTexture = readRenderTarget11->getTexture(); + readSubresource = readRenderTarget11->getSubresourceIndex(); + if (!canCopySubresource) + { + const d3d11::SharedSRV *blitSRV; + ANGLE_TRY(readRenderTarget11->getBlitShaderResourceView(context, &blitSRV)); + readSRV = blitSRV->makeCopy(); + if (!readSRV.valid()) + { + ASSERT(depthBlit || stencilBlit); + const d3d11::SharedSRV *srv; + ANGLE_TRY(readRenderTarget11->getShaderResourceView(context, &srv)); + readSRV = srv->makeCopy(); + } + ASSERT(readSRV.valid()); + } + } + + if (canCopySubresource) + { + UINT dstX = drawRect.x; + UINT dstY = drawRect.y; + UINT dstZ = drawLayer; + + D3D11_BOX readBox; + readBox.left = readRect.x; + readBox.right = readRect.x + readRect.width; + readBox.top = readRect.y; + readBox.bottom = readRect.y + readRect.height; + readBox.front = readLayer; + readBox.back = readLayer + 1; + + if (scissorNeeded) + { + // drawRect is guaranteed to have positive width and height because stretchRequired is + // false. + ASSERT(drawRect.width >= 0 || drawRect.height >= 0); + + if (drawRect.x < scissor->x) + { + dstX = scissor->x; + readBox.left += (scissor->x - drawRect.x); + } + if (drawRect.y < scissor->y) + { + dstY = scissor->y; + readBox.top += (scissor->y - drawRect.y); + } + if (drawRect.x + drawRect.width > scissor->x + scissor->width) + { + readBox.right -= ((drawRect.x + drawRect.width) - (scissor->x + scissor->width)); + } + if (drawRect.y + drawRect.height > scissor->y + scissor->height) + { + readBox.bottom -= ((drawRect.y + drawRect.height) - (scissor->y + scissor->height)); + } + } + + // D3D11 needs depth-stencil CopySubresourceRegions to have a NULL pSrcBox + // We also require complete framebuffer copies for depth-stencil blit. + D3D11_BOX *pSrcBox = wholeBufferCopy && readLayer == 0 ? nullptr : &readBox; + + mDeviceContext->CopySubresourceRegion(drawTexture.get(), drawSubresource, dstX, dstY, dstZ, + readTexture.get(), readSubresource, pSrcBox); + } + else + { + gl::Box readArea(readRect.x, readRect.y, 0, readRect.width, readRect.height, 1); + gl::Box drawArea(drawRect.x, drawRect.y, 0, drawRect.width, drawRect.height, 1); + + if (depthBlit && stencilBlit) + { + ANGLE_TRY(mBlit->copyDepthStencil(context, readTexture, readSubresource, readArea, + readSize, drawTexture, drawSubresource, drawArea, + drawSize, scissor)); + } + else if (depthBlit) + { + const d3d11::DepthStencilView &drawDSV = drawRenderTarget11->getDepthStencilView(); + ASSERT(readSRV.valid()); + ANGLE_TRY(mBlit->copyDepth(context, readSRV, readArea, readSize, drawDSV, drawArea, + drawSize, scissor)); + } + else if (stencilBlit) + { + ANGLE_TRY(mBlit->copyStencil(context, readTexture, readSubresource, readArea, readSize, + drawTexture, drawSubresource, drawArea, drawSize, + scissor)); + } + else + { + const d3d11::RenderTargetView &drawRTV = drawRenderTarget11->getRenderTargetView(); + + // We don't currently support masking off any other channel than alpha + bool maskOffAlpha = colorMaskingNeeded && colorMask.alpha; + ASSERT(readSRV.valid()); + ANGLE_TRY(mBlit->copyTexture(context, readSRV, readArea, readSize, srcFormatInfo.format, + drawRTV, drawArea, drawSize, scissor, + destFormatInfo.format, GL_NONE, filter, maskOffAlpha, + false, false)); + } + } + + return angle::Result::Continue; +} + +bool Renderer11::isES3Capable() const +{ + return (d3d11_gl::GetMaximumClientVersion(mRenderer11DeviceCaps).major > 2); +} + +RendererClass Renderer11::getRendererClass() const +{ + return RENDERER_D3D11; +} + +void Renderer11::onSwap() +{ + // Send histogram updates every half hour + const double kHistogramUpdateInterval = 30 * 60; + + auto *platform = ANGLEPlatformCurrent(); + const double currentTime = platform->monotonicallyIncreasingTime(platform); + const double timeSinceLastUpdate = currentTime - mLastHistogramUpdateTime; + + if (timeSinceLastUpdate > kHistogramUpdateInterval) + { + updateHistograms(); + mLastHistogramUpdateTime = currentTime; + } +} + +void Renderer11::updateHistograms() +{ + // Update the buffer CPU memory histogram + { + size_t sizeSum = 0; + for (const Buffer11 *buffer : mAliveBuffers) + { + sizeSum += buffer->getTotalCPUBufferMemoryBytes(); + } + const int kOneMegaByte = 1024 * 1024; + ANGLE_HISTOGRAM_MEMORY_MB("GPU.ANGLE.Buffer11CPUMemoryMB", + static_cast<int>(sizeSum) / kOneMegaByte); + } +} + +void Renderer11::onBufferCreate(const Buffer11 *created) +{ + mAliveBuffers.insert(created); +} + +void Renderer11::onBufferDelete(const Buffer11 *deleted) +{ + mAliveBuffers.erase(deleted); +} + +angle::Result Renderer11::resolveMultisampledTexture(const gl::Context *context, + RenderTarget11 *renderTarget, + bool depth, + bool stencil, + TextureHelper11 *textureOut) +{ + if (depth && !stencil) + { + return mBlit->resolveDepth(context, renderTarget, textureOut); + } + + if (stencil) + { + return mBlit->resolveStencil(context, renderTarget, depth, textureOut); + } + + const auto &formatSet = renderTarget->getFormatSet(); + + ASSERT(renderTarget->isMultisampled()); + const d3d11::SharedSRV *sourceSRV; + ANGLE_TRY(renderTarget->getShaderResourceView(context, &sourceSRV)); + D3D11_SHADER_RESOURCE_VIEW_DESC sourceSRVDesc; + sourceSRV->get()->GetDesc(&sourceSRVDesc); + ASSERT(sourceSRVDesc.ViewDimension == D3D_SRV_DIMENSION_TEXTURE2DMS || + sourceSRVDesc.ViewDimension == D3D_SRV_DIMENSION_TEXTURE2DMSARRAY); + + if (!mCachedResolveTexture.valid() || + mCachedResolveTexture.getExtents().width != renderTarget->getWidth() || + mCachedResolveTexture.getExtents().height != renderTarget->getHeight() || + mCachedResolveTexture.getFormat() != formatSet.texFormat) + { + D3D11_TEXTURE2D_DESC resolveDesc; + resolveDesc.Width = renderTarget->getWidth(); + resolveDesc.Height = renderTarget->getHeight(); + resolveDesc.MipLevels = 1; + resolveDesc.ArraySize = 1; + resolveDesc.Format = formatSet.texFormat; + resolveDesc.SampleDesc.Count = 1; + resolveDesc.SampleDesc.Quality = 0; + resolveDesc.Usage = D3D11_USAGE_DEFAULT; + resolveDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + resolveDesc.CPUAccessFlags = 0; + resolveDesc.MiscFlags = 0; + + ANGLE_TRY(allocateTexture(GetImplAs<Context11>(context), resolveDesc, formatSet, + &mCachedResolveTexture)); + } + + mDeviceContext->ResolveSubresource(mCachedResolveTexture.get(), 0, + renderTarget->getTexture().get(), + renderTarget->getSubresourceIndex(), formatSet.texFormat); + *textureOut = mCachedResolveTexture; + return angle::Result::Continue; +} + +bool Renderer11::getLUID(LUID *adapterLuid) const +{ + adapterLuid->HighPart = 0; + adapterLuid->LowPart = 0; + + if (!mDxgiAdapter) + { + return false; + } + + DXGI_ADAPTER_DESC adapterDesc; + if (FAILED(mDxgiAdapter->GetDesc(&adapterDesc))) + { + return false; + } + + *adapterLuid = adapterDesc.AdapterLuid; + return true; +} + +VertexConversionType Renderer11::getVertexConversionType(angle::FormatID vertexFormatID) const +{ + return d3d11::GetVertexFormatInfo(vertexFormatID, mRenderer11DeviceCaps.featureLevel) + .conversionType; +} + +GLenum Renderer11::getVertexComponentType(angle::FormatID vertexFormatID) const +{ + const auto &format = + d3d11::GetVertexFormatInfo(vertexFormatID, mRenderer11DeviceCaps.featureLevel); + return d3d11::GetComponentType(format.nativeFormat); +} + +angle::Result Renderer11::getVertexSpaceRequired(const gl::Context *context, + const gl::VertexAttribute &attrib, + const gl::VertexBinding &binding, + size_t count, + GLsizei instances, + GLuint baseInstance, + unsigned int *bytesRequiredOut) const +{ + if (!attrib.enabled) + { + *bytesRequiredOut = 16u; + return angle::Result::Continue; + } + + unsigned int elementCount = 0; + const unsigned int divisor = binding.getDivisor(); + if (instances == 0 || divisor == 0) + { + // This could be a clipped cast. + elementCount = gl::clampCast<unsigned int>(count); + } + else + { + // Round up to divisor, if possible + elementCount = + UnsignedCeilDivide(static_cast<unsigned int>(instances + baseInstance), divisor); + } + + ASSERT(elementCount > 0); + + const D3D_FEATURE_LEVEL featureLevel = mRenderer11DeviceCaps.featureLevel; + const d3d11::VertexFormat &vertexFormatInfo = + d3d11::GetVertexFormatInfo(attrib.format->id, featureLevel); + const d3d11::DXGIFormatSize &dxgiFormatInfo = + d3d11::GetDXGIFormatSizeInfo(vertexFormatInfo.nativeFormat); + unsigned int elementSize = dxgiFormatInfo.pixelBytes; + bool check = (elementSize > std::numeric_limits<unsigned int>::max() / elementCount); + ANGLE_CHECK(GetImplAs<Context11>(context), !check, + "New vertex buffer size would result in an overflow.", GL_OUT_OF_MEMORY); + + *bytesRequiredOut = elementSize * elementCount; + return angle::Result::Continue; +} + +void Renderer11::generateCaps(gl::Caps *outCaps, + gl::TextureCapsMap *outTextureCaps, + gl::Extensions *outExtensions, + gl::Limitations *outLimitations) const +{ + d3d11_gl::GenerateCaps(mDevice, mDeviceContext, mRenderer11DeviceCaps, getFeatures(), + mDescription, outCaps, outTextureCaps, outExtensions, outLimitations); +} + +void Renderer11::initializeFeatures(angle::FeaturesD3D *features) const +{ + if (!mDisplay->getState().featuresAllDisabled) + { + d3d11::InitializeFeatures(mRenderer11DeviceCaps, mAdapterDescription, features); + } + ApplyFeatureOverrides(features, mDisplay->getState()); +} + +void Renderer11::initializeFrontendFeatures(angle::FrontendFeatures *features) const +{ + if (!mDisplay->getState().featuresAllDisabled) + { + d3d11::InitializeFrontendFeatures(mAdapterDescription, features); + } + ApplyFeatureOverrides(features, mDisplay->getState()); +} + +DeviceImpl *Renderer11::createEGLDevice() +{ + return new DeviceD3D(EGL_D3D11_DEVICE_ANGLE, mDevice); +} + +ContextImpl *Renderer11::createContext(const gl::State &state, gl::ErrorSet *errorSet) +{ + return new Context11(state, errorSet, this); +} + +FramebufferImpl *Renderer11::createDefaultFramebuffer(const gl::FramebufferState &state) +{ + return new Framebuffer11(state, this); +} + +angle::Result Renderer11::getScratchMemoryBuffer(Context11 *context11, + size_t requestedSize, + angle::MemoryBuffer **bufferOut) +{ + ANGLE_CHECK_GL_ALLOC(context11, mScratchMemoryBuffer.get(requestedSize, bufferOut)); + return angle::Result::Continue; +} + +gl::Version Renderer11::getMaxSupportedESVersion() const +{ + return d3d11_gl::GetMaximumClientVersion(mRenderer11DeviceCaps); +} + +gl::Version Renderer11::getMaxConformantESVersion() const +{ + // 3.1 support is in progress. + return std::min(getMaxSupportedESVersion(), gl::Version(3, 0)); +} + +DebugAnnotatorContext11 *Renderer11::getDebugAnnotatorContext() +{ + return &mAnnotatorContext; +} + +angle::Result Renderer11::dispatchCompute(const gl::Context *context, + GLuint numGroupsX, + GLuint numGroupsY, + GLuint numGroupsZ) +{ + const gl::State &glState = context->getState(); + const gl::Program *program = glState.getProgram(); + if (program->getActiveShaderStorageBlockCount() > 0 || + program->getActiveAtomicCounterBufferCount() > 0) + { + ANGLE_TRY(markRawBufferUsage(context)); + } + ANGLE_TRY(markTypedBufferUsage(context)); + ANGLE_TRY(mStateManager.updateStateForCompute(context, numGroupsX, numGroupsY, numGroupsZ)); + mDeviceContext->Dispatch(numGroupsX, numGroupsY, numGroupsZ); + + return angle::Result::Continue; +} +angle::Result Renderer11::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect) +{ + const auto &glState = context->getState(); + const gl::Program *program = glState.getProgram(); + if (program->getActiveShaderStorageBlockCount() > 0 || + program->getActiveAtomicCounterBufferCount() > 0) + { + ANGLE_TRY(markRawBufferUsage(context)); + } + + auto *dispatchIndirectBuffer = glState.getTargetBuffer(gl::BufferBinding::DispatchIndirect); + ASSERT(dispatchIndirectBuffer); + + Buffer11 *storage = GetImplAs<Buffer11>(dispatchIndirectBuffer); + const uint8_t *bufferData = nullptr; + // TODO(jie.a.chen@intel.com): num_groups_x,y,z have to be written into the driver constant + // buffer for the built-in variable gl_NumWorkGroups. There is an opportunity for optimization + // to use GPU->GPU copy instead. + // http://anglebug.com/2807 + ANGLE_TRY(storage->getData(context, &bufferData)); + const GLuint *groups = reinterpret_cast<const GLuint *>(bufferData + indirect); + ANGLE_TRY(mStateManager.updateStateForCompute(context, groups[0], groups[1], groups[2])); + + ID3D11Buffer *buffer = nullptr; + ANGLE_TRY(storage->getBuffer(context, BUFFER_USAGE_INDIRECT, &buffer)); + + mDeviceContext->DispatchIndirect(buffer, static_cast<UINT>(indirect)); + return angle::Result::Continue; +} + +angle::Result Renderer11::createStagingTexture(const gl::Context *context, + ResourceType textureType, + const d3d11::Format &formatSet, + const gl::Extents &size, + StagingAccess readAndWriteAccess, + TextureHelper11 *textureOut) +{ + Context11 *context11 = GetImplAs<Context11>(context); + + if (textureType == ResourceType::Texture2D) + { + D3D11_TEXTURE2D_DESC stagingDesc; + stagingDesc.Width = size.width; + stagingDesc.Height = size.height; + stagingDesc.MipLevels = 1; + stagingDesc.ArraySize = 1; + stagingDesc.Format = formatSet.texFormat; + stagingDesc.SampleDesc.Count = 1; + stagingDesc.SampleDesc.Quality = 0; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + if (readAndWriteAccess == StagingAccess::READ_WRITE) + { + stagingDesc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE; + } + + ANGLE_TRY(allocateTexture(context11, stagingDesc, formatSet, textureOut)); + return angle::Result::Continue; + } + ASSERT(textureType == ResourceType::Texture3D); + + D3D11_TEXTURE3D_DESC stagingDesc; + stagingDesc.Width = size.width; + stagingDesc.Height = size.height; + stagingDesc.Depth = 1; + stagingDesc.MipLevels = 1; + stagingDesc.Format = formatSet.texFormat; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + ANGLE_TRY(allocateTexture(context11, stagingDesc, formatSet, textureOut)); + return angle::Result::Continue; +} + +angle::Result Renderer11::allocateTexture(d3d::Context *context, + const D3D11_TEXTURE2D_DESC &desc, + const d3d11::Format &format, + const D3D11_SUBRESOURCE_DATA *initData, + TextureHelper11 *textureOut) +{ + d3d11::Texture2D texture; + ANGLE_TRY(mResourceManager11.allocate(context, this, &desc, initData, &texture)); + textureOut->init(std::move(texture), desc, format); + return angle::Result::Continue; +} + +angle::Result Renderer11::allocateTexture(d3d::Context *context, + const D3D11_TEXTURE3D_DESC &desc, + const d3d11::Format &format, + const D3D11_SUBRESOURCE_DATA *initData, + TextureHelper11 *textureOut) +{ + d3d11::Texture3D texture; + ANGLE_TRY(mResourceManager11.allocate(context, this, &desc, initData, &texture)); + textureOut->init(std::move(texture), desc, format); + return angle::Result::Continue; +} + +angle::Result Renderer11::getBlendState(const gl::Context *context, + const d3d11::BlendStateKey &key, + const d3d11::BlendState **outBlendState) +{ + return mStateCache.getBlendState(context, this, key, outBlendState); +} + +angle::Result Renderer11::getRasterizerState(const gl::Context *context, + const gl::RasterizerState &rasterState, + bool scissorEnabled, + ID3D11RasterizerState **outRasterizerState) +{ + return mStateCache.getRasterizerState(context, this, rasterState, scissorEnabled, + outRasterizerState); +} + +angle::Result Renderer11::getDepthStencilState(const gl::Context *context, + const gl::DepthStencilState &dsState, + const d3d11::DepthStencilState **outDSState) +{ + return mStateCache.getDepthStencilState(context, this, dsState, outDSState); +} + +angle::Result Renderer11::getSamplerState(const gl::Context *context, + const gl::SamplerState &samplerState, + ID3D11SamplerState **outSamplerState) +{ + return mStateCache.getSamplerState(context, this, samplerState, outSamplerState); +} + +UINT Renderer11::getSampleDescQuality(GLuint supportedSamples) const +{ + // Per the documentation on + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_standard_multisample_quality_levels + // applications can only request the standard multisample pattern on + // feature levels 10_1 and above. + if (supportedSamples > 0 && mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_1) + { + return D3D11_STANDARD_MULTISAMPLE_PATTERN; + } + return 0; +} + +angle::Result Renderer11::clearRenderTarget(const gl::Context *context, + RenderTargetD3D *renderTarget, + const gl::ColorF &clearColorValue, + const float clearDepthValue, + const unsigned int clearStencilValue) +{ + RenderTarget11 *rt11 = GetAs<RenderTarget11>(renderTarget); + + if (rt11->getFormatSet().dsvFormat != DXGI_FORMAT_UNKNOWN) + { + ASSERT(rt11->getDepthStencilView().valid()); + + const auto &format = rt11->getFormatSet(); + const UINT clearFlags = (format.format().depthBits > 0 ? D3D11_CLEAR_DEPTH : 0) | + (format.format().stencilBits ? D3D11_CLEAR_STENCIL : 0); + mDeviceContext->ClearDepthStencilView(rt11->getDepthStencilView().get(), clearFlags, + clearDepthValue, + static_cast<UINT8>(clearStencilValue)); + return angle::Result::Continue; + } + + ASSERT(rt11->getRenderTargetView().valid()); + ID3D11RenderTargetView *rtv = rt11->getRenderTargetView().get(); + + // There are complications with some types of RTV and FL 9_3 with ClearRenderTargetView. + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ff476388(v=vs.85).aspx + ASSERT(mRenderer11DeviceCaps.featureLevel > D3D_FEATURE_LEVEL_9_3 || !IsArrayRTV(rtv)); + + const auto &d3d11Format = rt11->getFormatSet(); + const auto &glFormat = gl::GetSizedInternalFormatInfo(renderTarget->getInternalFormat()); + + gl::ColorF safeClearColor = clearColorValue; + + if (d3d11Format.format().alphaBits > 0 && glFormat.alphaBits == 0) + { + safeClearColor.alpha = 1.0f; + } + + mDeviceContext->ClearRenderTargetView(rtv, &safeClearColor.red); + return angle::Result::Continue; +} + +bool Renderer11::canSelectViewInVertexShader() const +{ + return !getFeatures().selectViewInGeometryShader.enabled && + getRenderer11DeviceCaps().supportsVpRtIndexWriteFromVertexShader; +} + +angle::Result Renderer11::mapResource(const gl::Context *context, + ID3D11Resource *resource, + UINT subResource, + D3D11_MAP mapType, + UINT mapFlags, + D3D11_MAPPED_SUBRESOURCE *mappedResource) +{ + HRESULT hr = mDeviceContext->Map(resource, subResource, mapType, mapFlags, mappedResource); + ANGLE_TRY_HR(GetImplAs<Context11>(context), hr, "Failed to map D3D11 resource."); + return angle::Result::Continue; +} + +angle::Result Renderer11::markTypedBufferUsage(const gl::Context *context) +{ + const gl::State &glState = context->getState(); + ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + gl::RangeUI imageRange = programD3D->getUsedImageRange(gl::ShaderType::Compute, false); + for (unsigned int imageIndex = imageRange.low(); imageIndex < imageRange.high(); imageIndex++) + { + GLint imageUnitIndex = programD3D->getImageMapping(gl::ShaderType::Compute, imageIndex, + false, context->getCaps()); + ASSERT(imageUnitIndex != -1); + const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex); + if (imageUnit.texture.get()->getType() == gl::TextureType::Buffer) + { + Buffer11 *buffer11 = GetImplAs<Buffer11>(imageUnit.texture.get()->getBuffer().get()); + ANGLE_TRY(buffer11->markTypedBufferUsage(context)); + } + } + return angle::Result::Continue; +} + +angle::Result Renderer11::markRawBufferUsage(const gl::Context *context) +{ + const gl::State &glState = context->getState(); + const gl::Program *program = glState.getProgram(); + for (size_t blockIndex = 0; blockIndex < program->getActiveShaderStorageBlockCount(); + blockIndex++) + { + GLuint binding = program->getShaderStorageBlockBinding(static_cast<GLuint>(blockIndex)); + const auto &shaderStorageBuffer = glState.getIndexedShaderStorageBuffer(binding); + if (shaderStorageBuffer.get() != nullptr) + { + Buffer11 *bufferStorage = GetImplAs<Buffer11>(shaderStorageBuffer.get()); + ANGLE_TRY(bufferStorage->markRawBufferUsage(context)); + } + } + + for (const auto &atomicCounterBuffer : program->getState().getAtomicCounterBuffers()) + { + GLuint binding = atomicCounterBuffer.binding; + const auto &buffer = glState.getIndexedAtomicCounterBuffer(binding); + + if (buffer.get() != nullptr) + { + Buffer11 *bufferStorage = GetImplAs<Buffer11>(buffer.get()); + ANGLE_TRY(bufferStorage->markRawBufferUsage(context)); + } + } + return angle::Result::Continue; +} + +angle::Result Renderer11::markTransformFeedbackUsage(const gl::Context *context) +{ + const gl::State &glState = context->getState(); + const gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback(); + for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++) + { + const gl::OffsetBindingPointer<gl::Buffer> &binding = + transformFeedback->getIndexedBuffer(i); + if (binding.get() != nullptr) + { + BufferD3D *bufferD3D = GetImplAs<BufferD3D>(binding.get()); + ANGLE_TRY(bufferD3D->markTransformFeedbackUsage(context)); + } + } + + return angle::Result::Continue; +} + +angle::Result Renderer11::getIncompleteTexture(const gl::Context *context, + gl::TextureType type, + gl::Texture **textureOut) +{ + return GetImplAs<Context11>(context)->getIncompleteTexture(context, type, textureOut); +} + +std::string Renderer11::getVendorString() const +{ + return GetVendorString(mAdapterDescription.VendorId); +} + +std::string Renderer11::getVersionString(bool includeFullVersion) const +{ + std::ostringstream versionString; + versionString << "D3D11"; + if (includeFullVersion && mRenderer11DeviceCaps.driverVersion.valid()) + { + versionString << "-" << GetDriverVersionString(mRenderer11DeviceCaps.driverVersion.value()); + } + return versionString.str(); +} + +RendererD3D *CreateRenderer11(egl::Display *display) +{ + return new Renderer11(display); +} + +} // namespace rx |