Source code

Revision control

Copy as Markdown

Other Tools

//
// 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
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 &params,
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.
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
// 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.
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