| // |
| // Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // Renderer11.cpp: Implements a back-end specific class for the D3D11 renderer. |
| |
| #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" |
| |
| #include <EGL/eglext.h> |
| #include <sstream> |
| #include <versionhelpers.h> |
| |
| #include "common/tls.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Buffer.h" |
| #include "libANGLE/Display.h" |
| #include "libANGLE/formatutils.h" |
| #include "libANGLE/Framebuffer.h" |
| #include "libANGLE/FramebufferAttachment.h" |
| #include "libANGLE/histogram_macros.h" |
| #include "libANGLE/Program.h" |
| #include "libANGLE/renderer/d3d/CompilerD3D.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/dxgi_support_table.h" |
| #include "libANGLE/renderer/d3d/d3d11/Fence11.h" |
| #include "libANGLE/renderer/d3d/d3d11/formatutils11.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/Query11.h" |
| #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" |
| #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" |
| #include "libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h" |
| #include "libANGLE/renderer/d3d/d3d11/StreamProducerNV12.h" |
| #include "libANGLE/renderer/d3d/d3d11/SwapChain11.h" |
| #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" |
| #include "libANGLE/renderer/d3d/d3d11/TextureStorage11.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/CompilerD3D.h" |
| #include "libANGLE/renderer/d3d/DeviceD3D.h" |
| #include "libANGLE/renderer/d3d/FramebufferD3D.h" |
| #include "libANGLE/renderer/d3d/IndexDataManager.h" |
| #include "libANGLE/renderer/d3d/ProgramD3D.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/TransformFeedbackD3D.h" |
| #include "libANGLE/renderer/d3d/VertexDataManager.h" |
| #include "libANGLE/State.h" |
| #include "libANGLE/Surface.h" |
| #include "third_party/trace_event/trace_event.h" |
| |
| #ifdef ANGLE_ENABLE_WINDOWS_STORE |
| #include "libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h" |
| #else |
| #include "libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.h" |
| #endif |
| |
| // Include the D3D9 debug annotator header for use by the desktop D3D11 renderer |
| // because the D3D11 interface method ID3DUserDefinedAnnotation::GetStatus |
| // doesn't work with the Graphics Diagnostics tools in Visual Studio 2013. |
| #ifdef ANGLE_ENABLE_D3D9 |
| #include "libANGLE/renderer/d3d/d3d9/DebugAnnotator9.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 |
| |
| #ifdef _DEBUG |
| // this flag enables suppressing some spurious warnings that pop up in certain WebGL samples |
| // and conformance tests. to enable all warnings, remove this define. |
| #define ANGLE_SUPPRESS_D3D11_HAZARD_WARNINGS 1 |
| #endif |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| |
| enum |
| { |
| MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 = 16 |
| }; |
| |
| void CalculateConstantBufferParams(GLintptr offset, GLsizeiptr size, UINT *outFirstConstant, UINT *outNumConstants) |
| { |
| // The offset must be aligned to 256 bytes (should have been enforced by glBindBufferRange). |
| ASSERT(offset % 256 == 0); |
| |
| // firstConstant and numConstants are expressed in constants of 16-bytes. Furthermore they must be a multiple of 16 constants. |
| *outFirstConstant = static_cast<UINT>(offset / 16); |
| |
| // The GL size is not required to be aligned to a 256 bytes boundary. |
| // Round the size up to a 256 bytes boundary then express the results in constants of 16-bytes. |
| *outNumConstants = static_cast<UINT>(rx::roundUp(size, static_cast<GLsizeiptr>(256)) / 16); |
| |
| // Since the size is rounded up, firstConstant + numConstants may be bigger than the actual size of the buffer. |
| // This behaviour is explictly allowed according to the documentation on ID3D11DeviceContext1::PSSetConstantBuffers1 |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx |
| } |
| |
| 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; |
| // Note: we don't ever request a 11_1 device, because this gives |
| // an E_INVALIDARG error on systems that don't have the platform update. |
| 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 GLvoid *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; |
| } |
| } |
| |
| template <typename T> |
| void CopyLineLoopIndicesWithRestart(const GLvoid *indices, |
| size_t count, |
| GLenum indexType, |
| std::vector<GLuint> *bufferOut) |
| { |
| GLuint restartIndex = gl::GetPrimitiveRestartIndex(indexType); |
| GLuint d3dRestartIndex = static_cast<GLuint>(d3d11::GetPrimitiveRestartIndex()); |
| const T *srcPtr = static_cast<const T *>(indices); |
| Optional<GLuint> currentLoopStart; |
| |
| bufferOut->clear(); |
| |
| for (size_t indexIdx = 0; indexIdx < count; ++indexIdx) |
| { |
| GLuint value = static_cast<GLuint>(srcPtr[indexIdx]); |
| |
| if (value == restartIndex) |
| { |
| if (currentLoopStart.valid()) |
| { |
| bufferOut->push_back(currentLoopStart.value()); |
| bufferOut->push_back(d3dRestartIndex); |
| currentLoopStart.reset(); |
| } |
| } |
| else |
| { |
| bufferOut->push_back(value); |
| if (!currentLoopStart.valid()) |
| { |
| currentLoopStart = value; |
| } |
| } |
| } |
| |
| if (currentLoopStart.valid()) |
| { |
| bufferOut->push_back(currentLoopStart.value()); |
| } |
| } |
| |
| void GetLineLoopIndices(const GLvoid *indices, |
| GLenum indexType, |
| GLuint count, |
| bool usePrimitiveRestartFixedIndex, |
| std::vector<GLuint> *bufferOut) |
| { |
| if (indexType != GL_NONE && usePrimitiveRestartFixedIndex) |
| { |
| switch (indexType) |
| { |
| case GL_UNSIGNED_BYTE: |
| CopyLineLoopIndicesWithRestart<GLubyte>(indices, count, indexType, bufferOut); |
| break; |
| case GL_UNSIGNED_SHORT: |
| CopyLineLoopIndicesWithRestart<GLushort>(indices, count, indexType, bufferOut); |
| break; |
| case GL_UNSIGNED_INT: |
| CopyLineLoopIndicesWithRestart<GLuint>(indices, count, indexType, bufferOut); |
| 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_NONE: |
| SetLineLoopIndices(&(*bufferOut)[0], count); |
| break; |
| case GL_UNSIGNED_BYTE: |
| CopyLineLoopIndices<GLubyte>(indices, &(*bufferOut)[0], count); |
| break; |
| case GL_UNSIGNED_SHORT: |
| CopyLineLoopIndices<GLushort>(indices, &(*bufferOut)[0], count); |
| break; |
| case GL_UNSIGNED_INT: |
| CopyLineLoopIndices<GLuint>(indices, &(*bufferOut)[0], count); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| template <typename T> |
| void CopyTriangleFanIndices(const GLvoid *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 GLvoid *indices, |
| GLuint indexCount, |
| GLenum indexType, |
| std::vector<GLuint> *bufferOut) |
| { |
| GLuint restartIndex = gl::GetPrimitiveRestartIndex(indexType); |
| GLuint d3dRestartIndex = gl::GetPrimitiveRestartIndex(GL_UNSIGNED_INT); |
| 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 GLvoid *indices, |
| GLenum indexType, |
| GLuint count, |
| bool usePrimitiveRestartFixedIndex, |
| std::vector<GLuint> *bufferOut) |
| { |
| if (indexType != GL_NONE && usePrimitiveRestartFixedIndex) |
| { |
| switch (indexType) |
| { |
| case GL_UNSIGNED_BYTE: |
| CopyTriangleFanIndicesWithRestart<GLubyte>(indices, count, indexType, bufferOut); |
| break; |
| case GL_UNSIGNED_SHORT: |
| CopyTriangleFanIndicesWithRestart<GLushort>(indices, count, indexType, bufferOut); |
| break; |
| case GL_UNSIGNED_INT: |
| 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_NONE: |
| SetTriangleFanIndices(&(*bufferOut)[0], numTris); |
| break; |
| case GL_UNSIGNED_BYTE: |
| CopyTriangleFanIndices<GLubyte>(indices, &(*bufferOut)[0], numTris); |
| break; |
| case GL_UNSIGNED_SHORT: |
| CopyTriangleFanIndices<GLushort>(indices, &(*bufferOut)[0], numTris); |
| break; |
| case GL_UNSIGNED_INT: |
| CopyTriangleFanIndices<GLuint>(indices, &(*bufferOut)[0], numTris); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| int GetWrapBits(GLenum wrap) |
| { |
| switch (wrap) |
| { |
| case GL_CLAMP_TO_EDGE: |
| return 0x1; |
| case GL_REPEAT: |
| return 0x2; |
| case GL_MIRRORED_REPEAT: |
| return 0x3; |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| Renderer11::Renderer11(egl::Display *display) |
| : RendererD3D(display), |
| mStateCache(this), |
| mStateManager(this), |
| mLastHistogramUpdateTime(ANGLEPlatformCurrent()->monotonicallyIncreasingTime()), |
| mDebug(nullptr) |
| { |
| mVertexDataManager = NULL; |
| mIndexDataManager = NULL; |
| |
| mLineLoopIB = NULL; |
| mTriangleFanIB = NULL; |
| mAppliedIBChanged = false; |
| |
| mBlit = NULL; |
| mPixelTransfer = NULL; |
| |
| mClear = NULL; |
| |
| mTrim = NULL; |
| |
| mSyncQuery = NULL; |
| |
| mRenderer11DeviceCaps.supportsClearView = false; |
| mRenderer11DeviceCaps.supportsConstantBufferOffsets = false; |
| mRenderer11DeviceCaps.supportsDXGI1_2 = false; |
| mRenderer11DeviceCaps.B5G6R5support = 0; |
| mRenderer11DeviceCaps.B4G4R4A4support = 0; |
| mRenderer11DeviceCaps.B5G5R5A1support = 0; |
| |
| mD3d11Module = NULL; |
| mDxgiModule = NULL; |
| mDCompModule = NULL; |
| mCreatedWithDeviceEXT = false; |
| mEGLDevice = nullptr; |
| |
| mDevice = NULL; |
| mDeviceContext = NULL; |
| mDeviceContext1 = NULL; |
| mDxgiAdapter = NULL; |
| mDxgiFactory = NULL; |
| |
| mDriverConstantBufferVS = NULL; |
| mDriverConstantBufferPS = NULL; |
| |
| mAppliedVertexShader = NULL; |
| mAppliedGeometryShader = NULL; |
| mAppliedPixelShader = NULL; |
| |
| mAppliedNumXFBBindings = static_cast<size_t>(-1); |
| |
| ZeroMemory(&mAdapterDescription, sizeof(mAdapterDescription)); |
| |
| if (mDisplay->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE) |
| { |
| const auto &attributes = mDisplay->getAttributeMap(); |
| |
| 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 >= 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_WARP_ANGLE: |
| mRequestedDriverType = D3D_DRIVER_TYPE_WARP; |
| break; |
| |
| case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE: |
| mRequestedDriverType = D3D_DRIVER_TYPE_REFERENCE; |
| break; |
| |
| case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: |
| mRequestedDriverType = D3D_DRIVER_TYPE_NULL; |
| break; |
| |
| default: |
| UNREACHABLE(); |
| } |
| |
| 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); |
| } |
| else if (display->getPlatform() == EGL_PLATFORM_DEVICE_EXT) |
| { |
| mEGLDevice = GetImplAs<DeviceD3D>(display->getDevice()); |
| ASSERT(mEGLDevice != 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; |
| mPresentPathFastEnabled = false; |
| } |
| |
| initializeDebugAnnotator(); |
| } |
| |
| 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; |
| |
| egl::Error error = initializeD3DDevice(); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| #if !defined(ANGLE_ENABLE_WINDOWS_STORE) |
| #if !ANGLE_SKIP_DXGI_1_2_CHECK |
| { |
| 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 = NULL; |
| result = mDevice->QueryInterface(__uuidof(IDXGIDevice2), (void**)&dxgiDevice2); |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, |
| D3D11_INIT_INCOMPATIBLE_DXGI, |
| "DXGI 1.2 required to present to HWNDs owned by another process."); |
| } |
| SafeRelease(dxgiDevice2); |
| } |
| } |
| #endif |
| #endif |
| |
| { |
| TRACE_EVENT0("gpu.angle", "Renderer11::initialize (ComQueries)"); |
| // Cast the DeviceContext to a DeviceContext1. |
| // This could fail on Windows 7 without the Platform Update. |
| // Don't error in this case- just don't use mDeviceContext1. |
| mDeviceContext1 = d3d11::DynamicCastComObject<ID3D11DeviceContext1>(mDeviceContext); |
| |
| IDXGIDevice *dxgiDevice = NULL; |
| result = mDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); |
| |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, |
| D3D11_INIT_OTHER_ERROR, |
| "Could not query DXGI device."); |
| } |
| |
| result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&mDxgiAdapter); |
| |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, |
| 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 != NULL) |
| { |
| 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::Error(EGL_NOT_INITIALIZED, |
| 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::Error(EGL_NOT_INITIALIZED, |
| D3D11_INIT_OTHER_ERROR, |
| "Could not create DXGI factory."); |
| } |
| } |
| |
| // Disable some spurious D3D11 debug warnings to prevent them from flooding the output log |
| #if defined(ANGLE_SUPPRESS_D3D11_HAZARD_WARNINGS) && defined(_DEBUG) |
| { |
| 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 |
| }; |
| |
| D3D11_INFO_QUEUE_FILTER filter = {}; |
| filter.DenyList.NumIDs = static_cast<unsigned int>(ArraySize(hideMessages)); |
| filter.DenyList.pIDList = hideMessages; |
| |
| infoQueue->AddStorageFilterEntries(&filter); |
| SafeRelease(infoQueue); |
| } |
| } |
| #endif |
| |
| #if !defined(NDEBUG) |
| mDebug = d3d11::DynamicCastComObject<ID3D11Debug>(mDevice); |
| #endif |
| |
| initializeDevice(); |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error Renderer11::initializeD3DDevice() |
| { |
| HRESULT result = S_OK; |
| |
| if (!mCreatedWithDeviceEXT) |
| { |
| #if !defined(ANGLE_ENABLE_WINDOWS_STORE) |
| PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr; |
| { |
| SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDLLsMS"); |
| TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)"); |
| mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); |
| mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); |
| mDCompModule = LoadLibrary(TEXT("dcomp.dll")); |
| |
| if (mD3d11Module == nullptr || mDxgiModule == nullptr) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP, |
| "Could not load D3D11 or DXGI library."); |
| } |
| |
| // create the D3D11 device |
| ASSERT(mDevice == nullptr); |
| D3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>( |
| GetProcAddress(mD3d11Module, "D3D11CreateDevice")); |
| |
| if (D3D11CreateDevice == nullptr) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP, |
| "Could not retrieve D3D11CreateDevice address."); |
| } |
| } |
| #endif |
| |
| #ifdef _DEBUG |
| { |
| TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)"); |
| result = D3D11CreateDevice(nullptr, mRequestedDriverType, nullptr, |
| D3D11_CREATE_DEVICE_DEBUG, mAvailableFeatureLevels.data(), |
| static_cast<unsigned int>(mAvailableFeatureLevels.size()), |
| D3D11_SDK_VERSION, &mDevice, |
| &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); |
| } |
| |
| if (!mDevice || FAILED(result)) |
| { |
| ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n"); |
| } |
| |
| if (!mDevice || FAILED(result)) |
| #endif |
| { |
| SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3D11CreateDeviceMS"); |
| TRACE_EVENT0("gpu.angle", "D3D11CreateDevice"); |
| |
| result = D3D11CreateDevice( |
| nullptr, mRequestedDriverType, nullptr, 0, mAvailableFeatureLevels.data(), |
| static_cast<unsigned int>(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, |
| &mDevice, &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext); |
| |
| // Cleanup done by destructor |
| if (!mDevice || FAILED(result)) |
| { |
| ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError", |
| static_cast<int>(result)); |
| return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_CREATEDEVICE_ERROR, |
| "Could not create D3D11 device."); |
| } |
| } |
| } |
| else |
| { |
| // We should use the inputted D3D11 device instead |
| void *device = nullptr; |
| egl::Error error = mEGLDevice->getDevice(&device); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ID3D11Device *d3dDevice = reinterpret_cast<ID3D11Device *>(device); |
| if (FAILED(d3dDevice->GetDeviceRemovedReason())) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, "Inputted D3D11 device has been lost."); |
| } |
| |
| if (d3dDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_9_3) |
| { |
| return egl::Error(EGL_NOT_INITIALIZED, |
| "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(); |
| } |
| |
| d3d11::SetDebugName(mDeviceContext, "DeviceContext"); |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| // 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. |
| void Renderer11::initializeDevice() |
| { |
| SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDeviceMS"); |
| TRACE_EVENT0("gpu.angle", "Renderer11::initializeDevice"); |
| |
| populateRenderer11DeviceCaps(); |
| |
| mStateCache.initialize(mDevice); |
| mInputLayoutCache.initialize(mDevice, mDeviceContext); |
| |
| ASSERT(!mVertexDataManager && !mIndexDataManager); |
| mVertexDataManager = new VertexDataManager(this); |
| mIndexDataManager = new IndexDataManager(this, getRendererClass()); |
| |
| 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); |
| |
| const gl::Caps &rendererCaps = getRendererCaps(); |
| |
| mStateManager.initialize(rendererCaps); |
| |
| mForceSetVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits); |
| mCurVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits); |
| mSamplerMetadataVS.initData(rendererCaps.maxVertexTextureImageUnits); |
| |
| mForceSetPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits); |
| mCurPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits); |
| mSamplerMetadataPS.initData(rendererCaps.maxTextureImageUnits); |
| |
| mStateManager.initialize(rendererCaps); |
| |
| markAllStateDirty(); |
| |
| // 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); |
| } |
| |
| void Renderer11::populateRenderer11DeviceCaps() |
| { |
| HRESULT hr = S_OK; |
| |
| 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); |
| } |
| } |
| |
| hr = mDevice->CheckFormatSupport(DXGI_FORMAT_B5G6R5_UNORM, &(mRenderer11DeviceCaps.B5G6R5support)); |
| if (FAILED(hr)) |
| { |
| mRenderer11DeviceCaps.B5G6R5support = 0; |
| } |
| |
| hr = mDevice->CheckFormatSupport(DXGI_FORMAT_B4G4R4A4_UNORM, &(mRenderer11DeviceCaps.B4G4R4A4support)); |
| if (FAILED(hr)) |
| { |
| mRenderer11DeviceCaps.B4G4R4A4support = 0; |
| } |
| |
| hr = mDevice->CheckFormatSupport(DXGI_FORMAT_B5G5R5A1_UNORM, &(mRenderer11DeviceCaps.B5G5R5A1support)); |
| if (FAILED(hr)) |
| { |
| mRenderer11DeviceCaps.B5G5R5A1support = 0; |
| } |
| |
| IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject<IDXGIAdapter2>(mDxgiAdapter); |
| mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); |
| SafeRelease(dxgiAdapter2); |
| } |
| |
| egl::ConfigSet Renderer11::generateConfigs() const |
| { |
| std::vector<GLenum> colorBufferFormats; |
| fprintf(stderr, "ANGLE: D3D11: %s\n", __FUNCTION__); |
| |
| // 32-bit supported formats |
| colorBufferFormats.push_back(GL_RGBA8_OES); |
| colorBufferFormats.push_back(GL_BGRA8_EXT); |
| |
| // 24-bit supported formats |
| colorBufferFormats.push_back(GL_RGB8_OES); |
| |
| 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_COMPONENT16, |
| }; |
| |
| const gl::Caps &rendererCaps = getRendererCaps(); |
| const gl::TextureCapsMap &rendererTextureCaps = getRendererTextureCaps(); |
| |
| 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.renderable) |
| { |
| continue; |
| } |
| |
| for (GLenum depthStencilBufferInternalFormat : depthStencilBufferFormats) |
| { |
| const gl::TextureCaps &depthStencilBufferFormatCaps = |
| rendererTextureCaps.get(depthStencilBufferInternalFormat); |
| if (!depthStencilBufferFormatCaps.renderable && |
| depthStencilBufferInternalFormat != GL_NONE) |
| { |
| continue; |
| } |
| |
| const gl::InternalFormat &colorBufferFormatInfo = |
| gl::GetInternalFormatInfo(colorBufferInternalFormat); |
| const gl::InternalFormat &depthStencilBufferFormatInfo = |
| gl::GetInternalFormatInfo(depthStencilBufferInternalFormat); |
| |
| 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); |
| config.bindToTextureRGBA = (colorBufferFormatInfo.format == GL_RGBA || |
| colorBufferFormatInfo.format == GL_BGRA_EXT); |
| config.colorBufferType = EGL_RGB_BUFFER; |
| config.configCaveat = EGL_NONE; |
| config.configID = static_cast<EGLint>(configs.size() + 1); |
| // Can only support a conformant ES2 with feature level greater than 10.0. |
| config.conformant = (mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) |
| ? (EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR) |
| : 0; |
| |
| // PresentPathFast may not be conformant |
| if (mPresentPathFastEnabled) |
| { |
| config.conformant = 0; |
| } |
| |
| 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.0 |
| config.renderableType = |
| EGL_OPENGL_ES2_BIT | ((mRenderer11DeviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0) |
| ? EGL_OPENGL_ES3_BIT_KHR |
| : 0); |
| config.sampleBuffers = 0; // FIXME: enumerate multi-sampling |
| config.samples = 0; |
| 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; |
| |
| 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->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->createContext = true; |
| |
| outExtensions->deviceQuery = true; |
| |
| outExtensions->createContextNoError = true; |
| |
| outExtensions->image = true; |
| outExtensions->imageBase = true; |
| outExtensions->glTexture2DImage = true; |
| outExtensions->glTextureCubemapImage = true; |
| outExtensions->glRenderbufferImage = true; |
| |
| outExtensions->stream = true; |
| outExtensions->streamConsumerGLTexture = true; |
| outExtensions->streamConsumerGLTextureYUV = true; |
| // Not all D3D11 devices support NV12 textures |
| if (getNV12TextureSupport()) |
| { |
| outExtensions->streamProducerD3DTextureNV12 = true; |
| } |
| |
| outExtensions->flexibleSurfaceCompatibility = true; |
| outExtensions->directComposition = !!mDCompModule; |
| } |
| |
| gl::Error Renderer11::flush() |
| { |
| mDeviceContext->Flush(); |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::finish() |
| { |
| HRESULT result; |
| |
| if (!mSyncQuery) |
| { |
| D3D11_QUERY_DESC queryDesc; |
| queryDesc.Query = D3D11_QUERY_EVENT; |
| queryDesc.MiscFlags = 0; |
| |
| result = mDevice->CreateQuery(&queryDesc, &mSyncQuery); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create event query, result: 0x%X.", result); |
| } |
| } |
| |
| mDeviceContext->End(mSyncQuery); |
| mDeviceContext->Flush(); |
| |
| do |
| { |
| result = mDeviceContext->GetData(mSyncQuery, NULL, 0, D3D11_ASYNC_GETDATA_DONOTFLUSH); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to get event query data, result: 0x%X.", result); |
| } |
| |
| // Keep polling, but allow other threads to do something useful first |
| ScheduleYield(); |
| |
| if (testDeviceLost()) |
| { |
| mDisplay->notifyDeviceLost(); |
| return gl::Error(GL_OUT_OF_MEMORY, "Device was lost while waiting for sync."); |
| } |
| } |
| while (result == S_FALSE); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| bool Renderer11::isValidNativeWindow(EGLNativeWindowType window) const |
| { |
| #ifdef ANGLE_ENABLE_WINDOWS_STORE |
| return NativeWindow11WinRT::IsValidNativeWindow(window); |
| #else |
| return NativeWindow11Win32::IsValidNativeWindow(window); |
| #endif |
| } |
| |
| NativeWindowD3D *Renderer11::createNativeWindow(EGLNativeWindowType window, |
| const egl::Config *config, |
| const egl::AttributeMap &attribs) const |
| { |
| #ifdef ANGLE_ENABLE_WINDOWS_STORE |
| UNUSED_VARIABLE(attribs); |
| return new NativeWindow11WinRT(window, config->alphaSize > 0); |
| #else |
| return new NativeWindow11Win32( |
| window, config->alphaSize > 0, |
| attribs.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE); |
| #endif |
| } |
| |
| SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, |
| HANDLE shareHandle, |
| GLenum backBufferFormat, |
| GLenum depthBufferFormat, |
| EGLint orientation) |
| { |
| return new SwapChain11(this, GetAs<NativeWindow11>(nativeWindow), shareHandle, backBufferFormat, |
| depthBufferFormat, orientation); |
| } |
| |
| ContextImpl *Renderer11::createContext(const gl::ContextState &state) |
| { |
| return new Context11(state); |
| } |
| |
| CompilerImpl *Renderer11::createCompiler() |
| { |
| if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3) |
| { |
| return new CompilerD3D(SH_HLSL_4_0_FL9_3_OUTPUT); |
| } |
| else |
| { |
| return new CompilerD3D(SH_HLSL_4_1_OUTPUT); |
| } |
| } |
| |
| void *Renderer11::getD3DDevice() |
| { |
| return reinterpret_cast<void*>(mDevice); |
| } |
| |
| gl::Error Renderer11::generateSwizzle(gl::Texture *texture) |
| { |
| if (texture) |
| { |
| TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); |
| ASSERT(textureD3D); |
| |
| TextureStorage *texStorage = nullptr; |
| gl::Error error = textureD3D->getNativeTexture(&texStorage); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| if (texStorage) |
| { |
| TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage); |
| const gl::TextureState &textureState = texture->getTextureState(); |
| error = |
| storage11->generateSwizzles(textureState.swizzleRed, textureState.swizzleGreen, |
| textureState.swizzleBlue, textureState.swizzleAlpha); |
| if (error.isError()) |
| { |
| return error; |
| } |
| } |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::setSamplerState(gl::SamplerType type, |
| int index, |
| gl::Texture *texture, |
| const gl::SamplerState &samplerState) |
| { |
| // Make sure to add the level offset for our tiny compressed texture workaround |
| TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); |
| |
| TextureStorage *storage = nullptr; |
| gl::Error error = textureD3D->getNativeTexture(&storage); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Storage should exist, texture should be complete |
| ASSERT(storage); |
| |
| // Sampler metadata that's passed to shaders in uniforms is stored separately from rest of the |
| // sampler state since having it in contiguous memory makes it possible to memcpy to a constant |
| // buffer, and it doesn't affect the state set by PSSetSamplers/VSSetSamplers. |
| SamplerMetadataD3D11 *metadata = nullptr; |
| |
| if (type == gl::SAMPLER_PIXEL) |
| { |
| ASSERT(static_cast<unsigned int>(index) < getRendererCaps().maxTextureImageUnits); |
| |
| if (mForceSetPixelSamplerStates[index] || |
| memcmp(&samplerState, &mCurPixelSamplerStates[index], sizeof(gl::SamplerState)) != 0) |
| { |
| ID3D11SamplerState *dxSamplerState = NULL; |
| error = mStateCache.getSamplerState(samplerState, &dxSamplerState); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ASSERT(dxSamplerState != NULL); |
| mDeviceContext->PSSetSamplers(index, 1, &dxSamplerState); |
| |
| mCurPixelSamplerStates[index] = samplerState; |
| } |
| |
| mForceSetPixelSamplerStates[index] = false; |
| |
| metadata = &mSamplerMetadataPS; |
| } |
| else if (type == gl::SAMPLER_VERTEX) |
| { |
| ASSERT(static_cast<unsigned int>(index) < getRendererCaps().maxVertexTextureImageUnits); |
| |
| if (mForceSetVertexSamplerStates[index] || |
| memcmp(&samplerState, &mCurVertexSamplerStates[index], sizeof(gl::SamplerState)) != 0) |
| { |
| ID3D11SamplerState *dxSamplerState = NULL; |
| error = mStateCache.getSamplerState(samplerState, &dxSamplerState); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ASSERT(dxSamplerState != NULL); |
| mDeviceContext->VSSetSamplers(index, 1, &dxSamplerState); |
| |
| mCurVertexSamplerStates[index] = samplerState; |
| } |
| |
| mForceSetVertexSamplerStates[index] = false; |
| |
| metadata = &mSamplerMetadataVS; |
| } |
| else UNREACHABLE(); |
| |
| ASSERT(metadata != nullptr); |
| metadata->update(index, *texture); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::setTexture(gl::SamplerType type, int index, gl::Texture *texture) |
| { |
| ID3D11ShaderResourceView *textureSRV = NULL; |
| |
| if (texture) |
| { |
| TextureD3D *textureImpl = GetImplAs<TextureD3D>(texture); |
| |
| TextureStorage *texStorage = nullptr; |
| gl::Error error = textureImpl->getNativeTexture(&texStorage); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Texture should be complete and have a storage |
| ASSERT(texStorage); |
| |
| TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage); |
| |
| // Make sure to add the level offset for our tiny compressed texture workaround |
| gl::TextureState textureState = texture->getTextureState(); |
| textureState.baseLevel = texture->getEffectiveBaseLevel() + storage11->getTopLevel(); |
| |
| error = storage11->getSRV(textureState, &textureSRV); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // If we get NULL back from getSRV here, something went wrong in the texture class and we're unexpectedly |
| // missing the shader resource view |
| ASSERT(textureSRV != NULL); |
| |
| textureImpl->resetDirty(); |
| } |
| |
| ASSERT((type == gl::SAMPLER_PIXEL && static_cast<unsigned int>(index) < getRendererCaps().maxTextureImageUnits) || |
| (type == gl::SAMPLER_VERTEX && static_cast<unsigned int>(index) < getRendererCaps().maxVertexTextureImageUnits)); |
| |
| mStateManager.setShaderResource(type, index, textureSRV); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::setUniformBuffers(const gl::ContextState &data, |
| const std::vector<GLint> &vertexUniformBuffers, |
| const std::vector<GLint> &fragmentUniformBuffers) |
| { |
| for (size_t uniformBufferIndex = 0; uniformBufferIndex < vertexUniformBuffers.size(); uniformBufferIndex++) |
| { |
| GLint binding = vertexUniformBuffers[uniformBufferIndex]; |
| |
| if (binding == -1) |
| { |
| continue; |
| } |
| |
| const OffsetBindingPointer<gl::Buffer> &uniformBuffer = |
| data.state->getIndexedUniformBuffer(binding); |
| GLintptr uniformBufferOffset = uniformBuffer.getOffset(); |
| GLsizeiptr uniformBufferSize = uniformBuffer.getSize(); |
| |
| if (uniformBuffer.get() != nullptr) |
| { |
| Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get()); |
| ID3D11Buffer *constantBuffer; |
| |
| if (mRenderer11DeviceCaps.supportsConstantBufferOffsets) |
| { |
| auto bufferOrError = bufferStorage->getBuffer(BUFFER_USAGE_UNIFORM); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| constantBuffer = bufferOrError.getResult(); |
| } |
| else |
| { |
| auto bufferOrError = |
| bufferStorage->getConstantBufferRange(uniformBufferOffset, uniformBufferSize); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| constantBuffer = bufferOrError.getResult(); |
| } |
| |
| if (!constantBuffer) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY); |
| } |
| |
| if (mCurrentConstantBufferVS[uniformBufferIndex] != bufferStorage->getSerial() || |
| mCurrentConstantBufferVSOffset[uniformBufferIndex] != uniformBufferOffset || |
| mCurrentConstantBufferVSSize[uniformBufferIndex] != uniformBufferSize) |
| { |
| if (mRenderer11DeviceCaps.supportsConstantBufferOffsets && uniformBufferSize != 0) |
| { |
| UINT firstConstant = 0, numConstants = 0; |
| CalculateConstantBufferParams(uniformBufferOffset, uniformBufferSize, &firstConstant, &numConstants); |
| mDeviceContext1->VSSetConstantBuffers1( |
| getReservedVertexUniformBuffers() + |
| static_cast<unsigned int>(uniformBufferIndex), |
| 1, &constantBuffer, &firstConstant, &numConstants); |
| } |
| else |
| { |
| mDeviceContext->VSSetConstantBuffers( |
| getReservedVertexUniformBuffers() + |
| static_cast<unsigned int>(uniformBufferIndex), |
| 1, &constantBuffer); |
| } |
| |
| mCurrentConstantBufferVS[uniformBufferIndex] = bufferStorage->getSerial(); |
| mCurrentConstantBufferVSOffset[uniformBufferIndex] = uniformBufferOffset; |
| mCurrentConstantBufferVSSize[uniformBufferIndex] = uniformBufferSize; |
| } |
| } |
| } |
| |
| for (size_t uniformBufferIndex = 0; uniformBufferIndex < fragmentUniformBuffers.size(); uniformBufferIndex++) |
| { |
| GLint binding = fragmentUniformBuffers[uniformBufferIndex]; |
| |
| if (binding == -1) |
| { |
| continue; |
| } |
| |
| const OffsetBindingPointer<gl::Buffer> &uniformBuffer = |
| data.state->getIndexedUniformBuffer(binding); |
| GLintptr uniformBufferOffset = uniformBuffer.getOffset(); |
| GLsizeiptr uniformBufferSize = uniformBuffer.getSize(); |
| |
| if (uniformBuffer.get() != nullptr) |
| { |
| Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get()); |
| ID3D11Buffer *constantBuffer; |
| |
| if (mRenderer11DeviceCaps.supportsConstantBufferOffsets) |
| { |
| auto bufferOrError = bufferStorage->getBuffer(BUFFER_USAGE_UNIFORM); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| constantBuffer = bufferOrError.getResult(); |
| } |
| else |
| { |
| auto bufferOrError = |
| bufferStorage->getConstantBufferRange(uniformBufferOffset, uniformBufferSize); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| constantBuffer = bufferOrError.getResult(); |
| } |
| |
| if (!constantBuffer) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY); |
| } |
| |
| if (mCurrentConstantBufferPS[uniformBufferIndex] != bufferStorage->getSerial() || |
| mCurrentConstantBufferPSOffset[uniformBufferIndex] != uniformBufferOffset || |
| mCurrentConstantBufferPSSize[uniformBufferIndex] != uniformBufferSize) |
| { |
| if (mRenderer11DeviceCaps.supportsConstantBufferOffsets && uniformBufferSize != 0) |
| { |
| UINT firstConstant = 0, numConstants = 0; |
| CalculateConstantBufferParams(uniformBufferOffset, uniformBufferSize, &firstConstant, &numConstants); |
| mDeviceContext1->PSSetConstantBuffers1( |
| getReservedFragmentUniformBuffers() + |
| static_cast<unsigned int>(uniformBufferIndex), |
| 1, &constantBuffer, &firstConstant, &numConstants); |
| } |
| else |
| { |
| mDeviceContext->PSSetConstantBuffers( |
| getReservedFragmentUniformBuffers() + |
| static_cast<unsigned int>(uniformBufferIndex), |
| 1, &constantBuffer); |
| } |
| |
| mCurrentConstantBufferPS[uniformBufferIndex] = bufferStorage->getSerial(); |
| mCurrentConstantBufferPSOffset[uniformBufferIndex] = uniformBufferOffset; |
| mCurrentConstantBufferPSSize[uniformBufferIndex] = uniformBufferSize; |
| } |
| } |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::updateState(const gl::ContextState &data, GLenum drawMode) |
| { |
| // Applies the render target surface, depth stencil surface, viewport rectangle and |
| // scissor rectangle to the renderer |
| const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer(); |
| ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE); |
| gl::Error error = applyRenderTarget(framebufferObject); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Set the present path state |
| const bool presentPathFastActive = |
| UsePresentPathFast(this, framebufferObject->getFirstColorbuffer()); |
| mStateManager.updatePresentPath(presentPathFastActive, |
| framebufferObject->getFirstColorbuffer()); |
| |
| // Setting viewport state |
| mStateManager.setViewport(data.caps, data.state->getViewport(), data.state->getNearPlane(), |
| data.state->getFarPlane()); |
| |
| // Setting scissor state |
| mStateManager.setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled()); |
| |
| // Applying rasterizer state to D3D11 device |
| int samples = framebufferObject->getSamples(data); |
| gl::RasterizerState rasterizer = data.state->getRasterizerState(); |
| rasterizer.pointDrawMode = (drawMode == GL_POINTS); |
| rasterizer.multiSample = (samples != 0); |
| |
| error = mStateManager.setRasterizerState(rasterizer); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Setting blend state |
| unsigned int mask = GetBlendSampleMask(data, samples); |
| error = mStateManager.setBlendState(framebufferObject, data.state->getBlendState(), |
| data.state->getBlendColor(), mask); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Setting depth stencil state |
| error = mStateManager.setDepthStencilState(*data.state); |
| return error; |
| } |
| |
| void Renderer11::syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) |
| { |
| mStateManager.syncState(state, bitmask); |
| } |
| |
| bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize) |
| { |
| D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; |
| |
| GLsizei minCount = 0; |
| |
| switch (mode) |
| { |
| case GL_POINTS: primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; minCount = 1; break; |
| case GL_LINES: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; minCount = 2; break; |
| case GL_LINE_LOOP: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; minCount = 2; break; |
| case GL_LINE_STRIP: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; minCount = 2; break; |
| case GL_TRIANGLES: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; minCount = 3; break; |
| case GL_TRIANGLE_STRIP: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; minCount = 3; break; |
| // emulate fans via rewriting index buffer |
| case GL_TRIANGLE_FAN: primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; minCount = 3; break; |
| default: |
| UNREACHABLE(); |
| return false; |
| } |
| |
| // If instanced pointsprite emulation is being used and If gl_PointSize is used in the shader, |
| // GL_POINTS mode is expected to render pointsprites. |
| // Instanced PointSprite emulation requires that the topology to be D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST. |
| if (mode == GL_POINTS && usesPointSize && getWorkarounds().useInstancedPointSpriteEmulation) |
| { |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; |
| } |
| |
| if (primitiveTopology != mCurrentPrimitiveTopology) |
| { |
| mDeviceContext->IASetPrimitiveTopology(primitiveTopology); |
| mCurrentPrimitiveTopology = primitiveTopology; |
| } |
| |
| return count >= minCount; |
| } |
| |
| gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer) |
| { |
| return mStateManager.syncFramebuffer(framebuffer); |
| } |
| |
| gl::Error Renderer11::applyVertexBuffer(const gl::State &state, |
| GLenum mode, |
| GLint first, |
| GLsizei count, |
| GLsizei instances, |
| TranslatedIndexData *indexInfo) |
| { |
| const auto &vertexArray = state.getVertexArray(); |
| auto *vertexArray11 = GetImplAs<VertexArray11>(vertexArray); |
| |
| gl::Error error = vertexArray11->updateDirtyAndDynamicAttribs(mVertexDataManager, state, first, |
| count, instances); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| error = mStateManager.updateCurrentValueAttribs(state, mVertexDataManager); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // If index information is passed, mark it with the current changed status. |
| if (indexInfo) |
| { |
| indexInfo->srcIndexData.srcIndicesChanged = mAppliedIBChanged; |
| } |
| |
| GLsizei numIndicesPerInstance = 0; |
| if (instances > 0) |
| { |
| numIndicesPerInstance = count; |
| } |
| const auto &vertexArrayAttribs = vertexArray11->getTranslatedAttribs(); |
| const auto ¤tValueAttribs = mStateManager.getCurrentValueAttribs(); |
| ANGLE_TRY(mInputLayoutCache.applyVertexBuffers(state, vertexArrayAttribs, currentValueAttribs, |
| mode, first, indexInfo, numIndicesPerInstance)); |
| |
| // InputLayoutCache::applyVertexBuffers calls through to the Bufer11 to get the native vertex |
| // buffer (ID3D11Buffer *). Because we allocate these buffers lazily, this will trigger |
| // allocation. This in turn will signal that the buffer is dirty. Since we just resolved the |
| // dirty-ness in VertexArray11::updateDirtyAndDynamicAttribs, this can make us do a needless |
| // update on the second draw call. |
| // Hence we clear the flags here, after we've applied vertex data, since we know everything |
| // is clean. This is a bit of a hack. |
| vertexArray11->clearDirtyAndPromoteDynamicAttribs(state, count); |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error Renderer11::applyIndexBuffer(const gl::ContextState &data, |
| const GLvoid *indices, |
| GLsizei count, |
| GLenum mode, |
| GLenum type, |
| TranslatedIndexData *indexInfo) |
| { |
| gl::VertexArray *vao = data.state->getVertexArray(); |
| gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); |
| gl::Error error = |
| mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, indexInfo, |
| data.state->isPrimitiveRestartEnabled()); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ID3D11Buffer *buffer = NULL; |
| DXGI_FORMAT bufferFormat = (indexInfo->indexType == GL_UNSIGNED_INT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT; |
| |
| if (indexInfo->storage) |
| { |
| Buffer11 *storage = GetAs<Buffer11>(indexInfo->storage); |
| auto indexOrError = storage->getBuffer(BUFFER_USAGE_INDEX); |
| if (indexOrError.isError()) |
| { |
| return indexOrError.getError(); |
| } |
| buffer = indexOrError.getResult(); |
| } |
| else |
| { |
| IndexBuffer11* indexBuffer = GetAs<IndexBuffer11>(indexInfo->indexBuffer); |
| buffer = indexBuffer->getBuffer(); |
| } |
| |
| mAppliedIBChanged = false; |
| if (buffer != mAppliedIB || bufferFormat != mAppliedIBFormat || indexInfo->startOffset != mAppliedIBOffset) |
| { |
| mDeviceContext->IASetIndexBuffer(buffer, bufferFormat, indexInfo->startOffset); |
| |
| mAppliedIB = buffer; |
| mAppliedIBFormat = bufferFormat; |
| mAppliedIBOffset = indexInfo->startOffset; |
| mAppliedIBChanged = true; |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::applyTransformFeedbackBuffers(const gl::State &state) |
| { |
| size_t numXFBBindings = 0; |
| bool requiresUpdate = false; |
| |
| if (state.isTransformFeedbackActiveUnpaused()) |
| { |
| const gl::TransformFeedback *transformFeedback = state.getCurrentTransformFeedback(); |
| numXFBBindings = transformFeedback->getIndexedBufferCount(); |
| ASSERT(numXFBBindings <= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS); |
| |
| for (size_t i = 0; i < numXFBBindings; i++) |
| { |
| const OffsetBindingPointer<gl::Buffer> &binding = transformFeedback->getIndexedBuffer(i); |
| |
| ID3D11Buffer *d3dBuffer = nullptr; |
| if (binding.get() != nullptr) |
| { |
| Buffer11 *storage = GetImplAs<Buffer11>(binding.get()); |
| auto bufferOrError = storage->getBuffer(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| d3dBuffer = bufferOrError.getResult(); |
| } |
| |
| // TODO: mAppliedTFBuffers and friends should also be kept in a vector. |
| if (d3dBuffer != mAppliedTFBuffers[i] || binding.getOffset() != mAppliedTFOffsets[i]) |
| { |
| requiresUpdate = true; |
| } |
| } |
| } |
| |
| if (requiresUpdate || numXFBBindings != mAppliedNumXFBBindings) |
| { |
| const gl::TransformFeedback *transformFeedback = state.getCurrentTransformFeedback(); |
| for (size_t i = 0; i < numXFBBindings; ++i) |
| { |
| const OffsetBindingPointer<gl::Buffer> &binding = transformFeedback->getIndexedBuffer(i); |
| if (binding.get() != nullptr) |
| { |
| Buffer11 *storage = GetImplAs<Buffer11>(binding.get()); |
| auto bufferOrError = storage->getBuffer(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK); |
| if (bufferOrError.isError()) |
| { |
| return bufferOrError.getError(); |
| } |
| ID3D11Buffer *d3dBuffer = bufferOrError.getResult(); |
| |
| mCurrentD3DOffsets[i] = (mAppliedTFBuffers[i] != d3dBuffer || mAppliedTFOffsets[i] != binding.getOffset()) ? |
| static_cast<UINT>(binding.getOffset()) : -1; |
| mAppliedTFBuffers[i] = d3dBuffer; |
| } |
| else |
| { |
| mAppliedTFBuffers[i] = nullptr; |
| mCurrentD3DOffsets[i] = 0; |
| } |
| mAppliedTFOffsets[i] = binding.getOffset(); |
| } |
| |
| mAppliedNumXFBBindings = numXFBBindings; |
| |
| mDeviceContext->SOSetTargets(static_cast<unsigned int>(numXFBBindings), mAppliedTFBuffers, |
| mCurrentD3DOffsets); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::drawArraysImpl(const gl::ContextState &data, |
| GLenum mode, |
| GLint startVertex, |
| GLsizei count, |
| GLsizei instances) |
| { |
| ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.state->getProgram()); |
| |
| if (programD3D->usesGeometryShader(mode) && data.state->isTransformFeedbackActiveUnpaused()) |
| { |
| // 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. |
| mDeviceContext->PSSetShader(nullptr, nullptr, 0); |
| |
| if (instances > 0) |
| { |
| mDeviceContext->DrawInstanced(count, instances, 0, 0); |
| } |
| else |
| { |
| mDeviceContext->Draw(count, 0); |
| } |
| |
| rx::ShaderExecutableD3D *pixelExe = nullptr; |
| gl::Error error = programD3D->getPixelExecutableForFramebuffer(data.state->getDrawFramebuffer(), &pixelExe); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Skip the draw call if rasterizer discard is enabled (or no fragment shader). |
| if (!pixelExe || data.state->getRasterizerState().rasterizerDiscard) |
| { |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| ID3D11PixelShader *pixelShader = GetAs<ShaderExecutable11>(pixelExe)->getPixelShader(); |
| ASSERT(reinterpret_cast<uintptr_t>(pixelShader) == mAppliedPixelShader); |
| mDeviceContext->PSSetShader(pixelShader, NULL, 0); |
| |
| // Retrieve the geometry shader. |
| rx::ShaderExecutableD3D *geometryExe = nullptr; |
| error = |
| programD3D->getGeometryExecutableForPrimitiveType(data, mode, &geometryExe, nullptr); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ID3D11GeometryShader *geometryShader = |
| (geometryExe ? GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader() : NULL); |
| mAppliedGeometryShader = reinterpret_cast<uintptr_t>(geometryShader); |
| ASSERT(geometryShader); |
| mDeviceContext->GSSetShader(geometryShader, NULL, 0); |
| |
| if (instances > 0) |
| { |
| mDeviceContext->DrawInstanced(count, instances, 0, 0); |
| } |
| else |
| { |
| mDeviceContext->Draw(count, 0); |
| } |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| if (mode == GL_LINE_LOOP) |
| { |
| return drawLineLoop(data, count, GL_NONE, nullptr, nullptr, instances); |
| } |
| |
| if (mode == GL_TRIANGLE_FAN) |
| { |
| return drawTriangleFan(data, count, GL_NONE, nullptr, 0, instances); |
| } |
| |
| bool useInstancedPointSpriteEmulation = |
| programD3D->usesPointSize() && getWorkarounds().useInstancedPointSpriteEmulation; |
| |
| if (instances > 0) |
| { |
| if (mode == GL_POINTS && useInstancedPointSpriteEmulation) |
| { |
| // 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 < instances; i++) |
| { |
| gl::Error error = |
| mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(startVertex, i); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); |
| } |
| } |
| else |
| { |
| mDeviceContext->DrawInstanced(count, instances, 0, 0); |
| } |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| // 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 (mode == GL_POINTS && useInstancedPointSpriteEmulation) |
| { |
| mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); |
| } |
| else |
| { |
| mDeviceContext->Draw(count, 0); |
| } |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::drawElementsImpl(const gl::ContextState &data, |
| const TranslatedIndexData &indexInfo, |
| GLenum mode, |
| GLsizei count, |
| GLenum type, |
| const GLvoid *indices, |
| GLsizei instances) |
| { |
| int minIndex = static_cast<int>(indexInfo.indexRange.start); |
| |
| if (mode == GL_LINE_LOOP) |
| { |
| return drawLineLoop(data, count, type, indices, &indexInfo, instances); |
| } |
| |
| if (mode == GL_TRIANGLE_FAN) |
| { |
| return drawTriangleFan(data, count, type, indices, minIndex, instances); |
| } |
| |
| const ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.state->getProgram()); |
| if (instances > 0) |
| { |
| if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation()) |
| { |
| // 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. |
| GLsizei elementsToRender = static_cast<GLsizei>(indexInfo.indexRange.vertexCount()); |
| |
| // Each instance being rendered requires the inputlayout cache to reapply buffers and |
| // offsets. |
| for (GLsizei i = 0; i < instances; i++) |
| { |
| gl::Error error = |
| mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(minIndex, i); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| mDeviceContext->DrawIndexedInstanced(6, elementsToRender, 0, 0, 0); |
| } |
| } |
| else |
| { |
| mDeviceContext->DrawIndexedInstanced(count, instances, 0, -minIndex, 0); |
| } |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| // 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 (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation()) |
| { |
| // 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. |
| mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); |
| } |
| else |
| { |
| mDeviceContext->DrawIndexed(count, 0, -minIndex); |
| } |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::drawLineLoop(const gl::ContextState &data, |
| GLsizei count, |
| GLenum type, |
| const GLvoid *indexPointer, |
| const TranslatedIndexData *indexInfo, |
| int instances) |
| { |
| gl::VertexArray *vao = data.state->getVertexArray(); |
| gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); |
| |
| const GLvoid *indices = indexPointer; |
| |
| // Get the raw indices for an indexed draw |
| if (type != GL_NONE && elementArrayBuffer) |
| { |
| BufferD3D *storage = GetImplAs<BufferD3D>(elementArrayBuffer); |
| intptr_t offset = reinterpret_cast<intptr_t>(indices); |
| |
| const uint8_t *bufferData = NULL; |
| gl::Error error = storage->getData(&bufferData); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| indices = bufferData + offset; |
| } |
| |
| if (!mLineLoopIB) |
| { |
| mLineLoopIB = new StreamingIndexBufferInterface(this); |
| gl::Error error = mLineLoopIB->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT); |
| if (error.isError()) |
| { |
| SafeDelete(mLineLoopIB); |
| return error; |
| } |
| } |
| |
| // Checked by Renderer11::applyPrimitiveType |
| ASSERT(count >= 0); |
| |
| if (static_cast<unsigned int>(count) + 1 > (std::numeric_limits<unsigned int>::max() / sizeof(unsigned int))) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create a 32-bit looping index buffer for GL_LINE_LOOP, too many indices required."); |
| } |
| |
| GetLineLoopIndices(indices, type, static_cast<GLuint>(count), |
| data.state->isPrimitiveRestartEnabled(), &mScratchIndexDataBuffer); |
| |
| unsigned int spaceNeeded = |
| static_cast<unsigned int>(sizeof(GLuint) * mScratchIndexDataBuffer.size()); |
| gl::Error error = mLineLoopIB->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| void* mappedMemory = NULL; |
| unsigned int offset; |
| error = mLineLoopIB->mapBuffer(spaceNeeded, &mappedMemory, &offset); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // Copy over the converted index data. |
| memcpy(mappedMemory, &mScratchIndexDataBuffer[0], |
| sizeof(GLuint) * mScratchIndexDataBuffer.size()); |
| |
| error = mLineLoopIB->unmapBuffer(); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(mLineLoopIB->getIndexBuffer()); |
| ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); |
| DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); |
| |
| if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || |
| mAppliedIBOffset != offset) |
| { |
| mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, offset); |
| mAppliedIB = d3dIndexBuffer; |
| mAppliedIBFormat = indexFormat; |
| mAppliedIBOffset = offset; |
| } |
| |
| INT baseVertexLocation = (indexInfo ? -static_cast<int>(indexInfo->indexRange.start) : 0); |
| UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); |
| |
| if (instances > 0) |
| { |
| mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, baseVertexLocation, 0); |
| } |
| else |
| { |
| mDeviceContext->DrawIndexed(indexCount, 0, baseVertexLocation); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::drawTriangleFan(const gl::ContextState &data, |
| GLsizei count, |
| GLenum type, |
| const GLvoid *indices, |
| int minIndex, |
| int instances) |
| { |
| gl::VertexArray *vao = data.state->getVertexArray(); |
| gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); |
| |
| const GLvoid *indexPointer = indices; |
| |
| // Get the raw indices for an indexed draw |
| if (type != GL_NONE && elementArrayBuffer) |
| { |
| BufferD3D *storage = GetImplAs<BufferD3D>(elementArrayBuffer); |
| intptr_t offset = reinterpret_cast<intptr_t>(indices); |
| |
| const uint8_t *bufferData = NULL; |
| gl::Error error = storage->getData(&bufferData); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| indexPointer = bufferData + offset; |
| } |
| |
| if (!mTriangleFanIB) |
| { |
| mTriangleFanIB = new StreamingIndexBufferInterface(this); |
| gl::Error error = mTriangleFanIB->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT); |
| if (error.isError()) |
| { |
| SafeDelete(mTriangleFanIB); |
| return error; |
| } |
| } |
| |
| // Checked by Renderer11::applyPrimitiveType |
| ASSERT(count >= 3); |
| |
| const GLuint numTris = count - 2; |
| |
| if (numTris > (std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3))) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, too many indices required."); |
| } |
| |
| GetTriFanIndices(indexPointer, type, count, data.state->isPrimitiveRestartEnabled(), |
| &mScratchIndexDataBuffer); |
| |
| const unsigned int spaceNeeded = |
| static_cast<unsigned int>(mScratchIndexDataBuffer.size() * sizeof(unsigned int)); |
| gl::Error error = mTriangleFanIB->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| void *mappedMemory = nullptr; |
| unsigned int offset; |
| error = mTriangleFanIB->mapBuffer(spaceNeeded, &mappedMemory, &offset); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| memcpy(mappedMemory, &mScratchIndexDataBuffer[0], spaceNeeded); |
| |
| error = mTriangleFanIB->unmapBuffer(); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(mTriangleFanIB->getIndexBuffer()); |
| ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); |
| DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); |
| |
| if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || |
| mAppliedIBOffset != offset) |
| { |
| mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, offset); |
| mAppliedIB = d3dIndexBuffer; |
| mAppliedIBFormat = indexFormat; |
| mAppliedIBOffset = offset; |
| } |
| |
| UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); |
| |
| if (instances > 0) |
| { |
| mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, -minIndex, 0); |
| } |
| else |
| { |
| mDeviceContext->DrawIndexed(indexCount, 0, -minIndex); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::applyShadersImpl(const gl::ContextState &data, GLenum drawMode) |
| { |
| ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.state->getProgram()); |
| const auto &inputLayout = programD3D->getCachedInputLayout(); |
| |
| ShaderExecutableD3D *vertexExe = NULL; |
| gl::Error error = programD3D->getVertexExecutableForInputLayout(inputLayout, &vertexExe, nullptr); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| const gl::Framebuffer *drawFramebuffer = data.state->getDrawFramebuffer(); |
| ShaderExecutableD3D *pixelExe = NULL; |
| error = programD3D->getPixelExecutableForFramebuffer(drawFramebuffer, &pixelExe); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ShaderExecutableD3D *geometryExe = nullptr; |
| error = |
| programD3D->getGeometryExecutableForPrimitiveType(data, drawMode, &geometryExe, nullptr); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ID3D11VertexShader *vertexShader = (vertexExe ? GetAs<ShaderExecutable11>(vertexExe)->getVertexShader() : NULL); |
| |
| ID3D11PixelShader *pixelShader = NULL; |
| // Skip pixel shader if we're doing rasterizer discard. |
| bool rasterizerDiscard = data.state->getRasterizerState().rasterizerDiscard; |
| if (!rasterizerDiscard) |
| { |
| pixelShader = (pixelExe ? GetAs<ShaderExecutable11>(pixelExe)->getPixelShader() : NULL); |
| } |
| |
| ID3D11GeometryShader *geometryShader = NULL; |
| bool transformFeedbackActive = data.state->isTransformFeedbackActiveUnpaused(); |
| if (transformFeedbackActive) |
| { |
| geometryShader = (vertexExe ? GetAs<ShaderExecutable11>(vertexExe)->getStreamOutShader() : NULL); |
| } |
| else |
| { |
| geometryShader = (geometryExe ? GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader() : NULL); |
| } |
| |
| bool dirtyUniforms = false; |
| |
| if (reinterpret_cast<uintptr_t>(vertexShader) != mAppliedVertexShader) |
| { |
| mDeviceContext->VSSetShader(vertexShader, NULL, 0); |
| mAppliedVertexShader = reinterpret_cast<uintptr_t>(vertexShader); |
| dirtyUniforms = true; |
| } |
| |
| if (reinterpret_cast<uintptr_t>(geometryShader) != mAppliedGeometryShader) |
| { |
| mDeviceContext->GSSetShader(geometryShader, NULL, 0); |
| mAppliedGeometryShader = reinterpret_cast<uintptr_t>(geometryShader); |
| dirtyUniforms = true; |
| } |
| |
| if (reinterpret_cast<uintptr_t>(pixelShader) != mAppliedPixelShader) |
| { |
| mDeviceContext->PSSetShader(pixelShader, NULL, 0); |
| mAppliedPixelShader = reinterpret_cast<uintptr_t>(pixelShader); |
| dirtyUniforms = true; |
| } |
| |
| if (dirtyUniforms) |
| { |
| programD3D->dirtyAllUniforms(); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, |
| GLenum drawMode, |
| const std::vector<D3DUniform *> &uniformArray) |
| { |
| unsigned int totalRegisterCountVS = 0; |
| unsigned int totalRegisterCountPS = 0; |
| |
| bool vertexUniformsDirty = false; |
| bool pixelUniformsDirty = false; |
| |
| for (const D3DUniform *uniform : uniformArray) |
| { |
| if (uniform->isReferencedByVertexShader() && !uniform->isSampler()) |
| { |
| totalRegisterCountVS += uniform->registerCount; |
| vertexUniformsDirty = (vertexUniformsDirty || uniform->dirty); |
| } |
| |
| if (uniform->isReferencedByFragmentShader() && !uniform->isSampler()) |
| { |
| totalRegisterCountPS += uniform->registerCount; |
| pixelUniformsDirty = (pixelUniformsDirty || uniform->dirty); |
| } |
| } |
| |
| const UniformStorage11 *vertexUniformStorage = |
| GetAs<UniformStorage11>(&programD3D.getVertexUniformStorage()); |
| const UniformStorage11 *fragmentUniformStorage = |
| GetAs<UniformStorage11>(&programD3D.getFragmentUniformStorage()); |
| ASSERT(vertexUniformStorage); |
| ASSERT(fragmentUniformStorage); |
| |
| ID3D11Buffer *vertexConstantBuffer = vertexUniformStorage->getConstantBuffer(); |
| ID3D11Buffer *pixelConstantBuffer = fragmentUniformStorage->getConstantBuffer(); |
| |
| float (*mapVS)[4] = NULL; |
| float (*mapPS)[4] = NULL; |
| |
| if (totalRegisterCountVS > 0 && vertexUniformsDirty) |
| { |
| D3D11_MAPPED_SUBRESOURCE map = {0}; |
| HRESULT result = mDeviceContext->Map(vertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); |
| UNUSED_ASSERTION_VARIABLE(result); |
| ASSERT(SUCCEEDED(result)); |
| mapVS = (float(*)[4])map.pData; |
| } |
| |
| if (totalRegisterCountPS > 0 && pixelUniformsDirty) |
| { |
| D3D11_MAPPED_SUBRESOURCE map = {0}; |
| HRESULT result = mDeviceContext->Map(pixelConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); |
| UNUSED_ASSERTION_VARIABLE(result); |
| ASSERT(SUCCEEDED(result)); |
| mapPS = (float(*)[4])map.pData; |
| } |
| |
| for (const D3DUniform *uniform : uniformArray) |
| { |
| if (uniform->isSampler()) |
| continue; |
| |
| unsigned int componentCount = (4 - uniform->registerElement); |
| |
| // we assume that uniforms from structs are arranged in struct order in our uniforms list. |
| // otherwise we would overwrite previously written regions of memory. |
| |
| if (uniform->isReferencedByVertexShader() && mapVS) |
| { |
| memcpy(&mapVS[uniform->vsRegisterIndex][uniform->registerElement], uniform->data, |
| uniform->registerCount * sizeof(float) * componentCount); |
| } |
| |
| if (uniform->isReferencedByFragmentShader() && mapPS) |
| { |
| memcpy(&mapPS[uniform->psRegisterIndex][uniform->registerElement], uniform->data, |
| uniform->registerCount * sizeof(float) * componentCount); |
| } |
| } |
| |
| if (mapVS) |
| { |
| mDeviceContext->Unmap(vertexConstantBuffer, 0); |
| } |
| |
| if (mapPS) |
| { |
| mDeviceContext->Unmap(pixelConstantBuffer, 0); |
| } |
| |
| if (mCurrentVertexConstantBuffer != vertexConstantBuffer) |
| { |
| mDeviceContext->VSSetConstantBuffers( |
| d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK, 1, &vertexConstantBuffer); |
| mCurrentVertexConstantBuffer = vertexConstantBuffer; |
| } |
| |
| if (mCurrentPixelConstantBuffer != pixelConstantBuffer) |
| { |
| mDeviceContext->PSSetConstantBuffers( |
| d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK, 1, &pixelConstantBuffer); |
| mCurrentPixelConstantBuffer = pixelConstantBuffer; |
| } |
| |
| if (!mDriverConstantBufferVS) |
| { |
| D3D11_BUFFER_DESC constantBufferDescription = {0}; |
| d3d11::InitConstantBufferDesc( |
| &constantBufferDescription, |
| sizeof(dx_VertexConstants11) + mSamplerMetadataVS.sizeBytes()); |
| HRESULT result = |
| mDevice->CreateBuffer(&constantBufferDescription, nullptr, &mDriverConstantBufferVS); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create vertex shader constant buffer, result: 0x%X.", result); |
| } |
| mDeviceContext->VSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &mDriverConstantBufferVS); |
| } |
| if (!mDriverConstantBufferPS) |
| { |
| D3D11_BUFFER_DESC constantBufferDescription = {0}; |
| d3d11::InitConstantBufferDesc(&constantBufferDescription, |
| sizeof(dx_PixelConstants11) + mSamplerMetadataPS.sizeBytes()); |
| HRESULT result = |
| mDevice->CreateBuffer(&constantBufferDescription, nullptr, &mDriverConstantBufferPS); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create pixel shader constant buffer, result: 0x%X.", result); |
| } |
| mDeviceContext->PSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &mDriverConstantBufferPS); |
| } |
| |
| // Sampler metadata and driver constants need to coexist in the same constant buffer to conserve |
| // constant buffer slots. We update both in the constant buffer if needed. |
| const dx_VertexConstants11 &vertexConstants = mStateManager.getVertexConstants(); |
| size_t samplerMetadataReferencedBytesVS = sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) * |
| programD3D.getUsedSamplerRange(gl::SAMPLER_VERTEX); |
| applyDriverConstantsIfNeeded(&mAppliedVertexConstants, vertexConstants, &mSamplerMetadataVS, |
| samplerMetadataReferencedBytesVS, mDriverConstantBufferVS); |
| |
| const dx_PixelConstants11 &pixelConstants = mStateManager.getPixelConstants(); |
| size_t samplerMetadataReferencedBytesPS = sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) * |
| programD3D.getUsedSamplerRange(gl::SAMPLER_PIXEL); |
| applyDriverConstantsIfNeeded(&mAppliedPixelConstants, pixelConstants, &mSamplerMetadataPS, |
| samplerMetadataReferencedBytesPS, mDriverConstantBufferPS); |
| |
| // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it if necessary |
| if (programD3D.usesGeometryShader(drawMode)) |
| { |
| // needed for the point sprite geometry shader |
| if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS) |
| { |
| ASSERT(mDriverConstantBufferPS != nullptr); |
| mDeviceContext->GSSetConstantBuffers(0, 1, &mDriverConstantBufferPS); |
| mCurrentGeometryConstantBuffer = mDriverConstantBufferPS; |
| } |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| // SamplerMetadataD3D11 implementation |
| |
| Renderer11::SamplerMetadataD3D11::SamplerMetadataD3D11() : mDirty(true) |
| { |
| } |
| |
| Renderer11::SamplerMetadataD3D11::~SamplerMetadataD3D11() |
| { |
| } |
| |
| void Renderer11::SamplerMetadataD3D11::initData(unsigned int samplerCount) |
| { |
| mSamplerMetadata.resize(samplerCount); |
| } |
| |
| void Renderer11::SamplerMetadataD3D11::update(unsigned int samplerIndex, const gl::Texture &texture) |
| { |
| unsigned int baseLevel = texture.getEffectiveBaseLevel(); |
| GLenum internalFormat = texture.getInternalFormat(texture.getTarget(), baseLevel); |
| if (mSamplerMetadata[samplerIndex].baseLevel != static_cast<int>(baseLevel)) |
| { |
| mSamplerMetadata[samplerIndex].baseLevel = static_cast<int>(baseLevel); |
| mDirty = true; |
| } |
| |
| // Some metadata is needed only for integer textures. We avoid updating the constant buffer |
| // unnecessarily by changing the data only in case the texture is an integer texture and |
| // the values have changed. |
| bool needIntegerTextureMetadata = false; |
| // internalFormatBits == 0 means a 32-bit texture in the case of integer textures. |
| int internalFormatBits = 0; |
| switch (internalFormat) |
| { |
| case GL_RGBA32I: |
| case GL_RGBA32UI: |
| case GL_RGB32I: |
| case GL_RGB32UI: |
| case GL_RG32I: |
| case GL_RG32UI: |
| case GL_R32I: |
| case GL_R32UI: |
| needIntegerTextureMetadata = true; |
| break; |
| case GL_RGBA16I: |
| case GL_RGBA16UI: |
| case GL_RGB16I: |
| case GL_RGB16UI: |
| case GL_RG16I: |
| case GL_RG16UI: |
| case GL_R16I: |
| case GL_R16UI: |
| needIntegerTextureMetadata = true; |
| internalFormatBits = 16; |
| break; |
| case GL_RGBA8I: |
| case GL_RGBA8UI: |
| case GL_RGB8I: |
| case GL_RGB8UI: |
| case GL_RG8I: |
| case GL_RG8UI: |
| case GL_R8I: |
| case GL_R8UI: |
| needIntegerTextureMetadata = true; |
| internalFormatBits = 8; |
| break; |
| case GL_RGB10_A2UI: |
| needIntegerTextureMetadata = true; |
| internalFormatBits = 10; |
| break; |
| default: |
| break; |
| } |
| if (needIntegerTextureMetadata) |
| { |
| if (mSamplerMetadata[samplerIndex].internalFormatBits != internalFormatBits) |
| { |
| mSamplerMetadata[samplerIndex].internalFormatBits = internalFormatBits; |
| mDirty = true; |
| } |
| // Pack the wrap values into one integer so we can fit all the metadata in one 4-integer |
| // vector. |
| GLenum wrapS = texture.getWrapS(); |
| GLenum wrapT = texture.getWrapT(); |
| GLenum wrapR = texture.getWrapR(); |
| int wrapModes = GetWrapBits(wrapS) | (GetWrapBits(wrapT) << 2) | (GetWrapBits(wrapR) << 4); |
| if (mSamplerMetadata[samplerIndex].wrapModes != wrapModes) |
| { |
| mSamplerMetadata[samplerIndex].wrapModes = wrapModes; |
| mDirty = true; |
| } |
| } |
| } |
| |
| const Renderer11::SamplerMetadataD3D11::dx_SamplerMetadata * |
| Renderer11::SamplerMetadataD3D11::getData() const |
| { |
| return mSamplerMetadata.data(); |
| } |
| |
| size_t Renderer11::SamplerMetadataD3D11::sizeBytes() const |
| { |
| return sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) * mSamplerMetadata.size(); |
| } |
| |
| template <class TShaderConstants> |
| void Renderer11::applyDriverConstantsIfNeeded(TShaderConstants *appliedConstants, |
| const TShaderConstants &constants, |
| SamplerMetadataD3D11 *samplerMetadata, |
| size_t samplerMetadataReferencedBytes, |
| ID3D11Buffer *driverConstantBuffer) |
| { |
| ASSERT(driverConstantBuffer != nullptr); |
| if (memcmp(appliedConstants, &constants, sizeof(TShaderConstants)) != 0 || |
| samplerMetadata->isDirty()) |
| { |
| memcpy(appliedConstants, &constants, sizeof(TShaderConstants)); |
| |
| D3D11_MAPPED_SUBRESOURCE mapping = {0}; |
| HRESULT result = |
| mDeviceContext->Map(driverConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapping); |
| ASSERT(SUCCEEDED(result)); |
| UNUSED_ASSERTION_VARIABLE(result); |
| memcpy(mapping.pData, appliedConstants, sizeof(TShaderConstants)); |
| // Previous buffer contents were discarded, so we need to refresh also the area of the |
| // buffer that isn't used by this program. |
| memcpy(&reinterpret_cast<uint8_t *>(mapping.pData)[sizeof(TShaderConstants)], |
| samplerMetadata->getData(), samplerMetadata->sizeBytes()); |
| mDeviceContext->Unmap(driverConstantBuffer, 0); |
| |
| samplerMetadata->markClean(); |
| } |
| } |
| |
| template void Renderer11::applyDriverConstantsIfNeeded<dx_VertexConstants11>( |
| dx_VertexConstants11 *appliedConstants, |
| const dx_VertexConstants11 &constants, |
| SamplerMetadataD3D11 *samplerMetadata, |
| size_t samplerMetadataReferencedBytes, |
| ID3D11Buffer *driverConstantBuffer); |
| template void Renderer11::applyDriverConstantsIfNeeded<dx_PixelConstants11>( |
| dx_PixelConstants11 *appliedConstants, |
| const dx_PixelConstants11 &constants, |
| SamplerMetadataD3D11 *samplerMetadata, |
| size_t samplerMetadataReferencedBytes, |
| ID3D11Buffer *driverConstantBuffer); |
| |
| void Renderer11::markAllStateDirty() |
| { |
| TRACE_EVENT0("gpu.angle", "Renderer11::markAllStateDirty"); |
| |
| for (size_t vsamplerId = 0; vsamplerId < mForceSetVertexSamplerStates.size(); ++vsamplerId) |
| { |
| mForceSetVertexSamplerStates[vsamplerId] = true; |
| } |
| |
| for (size_t fsamplerId = 0; fsamplerId < mForceSetPixelSamplerStates.size(); ++fsamplerId) |
| { |
| mForceSetPixelSamplerStates[fsamplerId] = true; |
| } |
| |
| mStateManager.invalidateEverything(); |
| |
| mAppliedIB = NULL; |
| mAppliedIBFormat = DXGI_FORMAT_UNKNOWN; |
| mAppliedIBOffset = 0; |
| |
| mAppliedVertexShader = angle::DirtyPointer; |
| mAppliedGeometryShader = angle::DirtyPointer; |
| mAppliedPixelShader = angle::DirtyPointer; |
| |
| mAppliedNumXFBBindings = static_cast<size_t>(-1); |
| |
| for (size_t i = 0; i < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++) |
| { |
| mAppliedTFBuffers[i] = NULL; |
| mAppliedTFOffsets[i] = 0; |
| } |
| |
| memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants11)); |
| memset(&mAppliedPixelConstants, 0, sizeof(dx_PixelConstants11)); |
| |
| mInputLayoutCache.markDirty(); |
| |
| for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; i++) |
| { |
| mCurrentConstantBufferVS[i] = static_cast<unsigned int>(-1); |
| mCurrentConstantBufferVSOffset[i] = 0; |
| mCurrentConstantBufferVSSize[i] = 0; |
| mCurrentConstantBufferPS[i] = static_cast<unsigned int>(-1); |
| mCurrentConstantBufferPSOffset[i] = 0; |
| mCurrentConstantBufferPSSize[i] = 0; |
| } |
| |
| mCurrentVertexConstantBuffer = NULL; |
| mCurrentPixelConstantBuffer = NULL; |
| mCurrentGeometryConstantBuffer = NULL; |
| |
| mCurrentPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; |
| } |
| |
| void Renderer11::releaseDeviceResources() |
| { |
| mStateManager.deinitialize(); |
| mStateCache.clear(); |
| mInputLayoutCache.clear(); |
| |
| SafeDelete(mVertexDataManager); |
| SafeDelete(mIndexDataManager); |
| SafeDelete(mLineLoopIB); |
| SafeDelete(mTriangleFanIB); |
| SafeDelete(mBlit); |
| SafeDelete(mClear); |
| SafeDelete(mTrim); |
| SafeDelete(mPixelTransfer); |
| |
| SafeRelease(mDriverConstantBufferVS); |
| SafeRelease(mDriverConstantBufferPS); |
| SafeRelease(mSyncQuery); |
| } |
| |
| // set notify to true to broadcast a message to all contexts of the device loss |
| bool Renderer11::testDeviceLost() |
| { |
| bool isLost = false; |
| |
| // GetRemovedReason is used to test if the device is removed |
| HRESULT result = mDevice->GetDeviceRemovedReason(); |
| isLost = d3d11::isDeviceLostError(result); |
| |
| if (isLost) |
| { |
| // Log error if this is a new device lost event |
| if (mDeviceLost == false) |
| { |
| ERR("The D3D11 device was removed: 0x%08X", result); |
| } |
| |
| // ensure we note the device loss -- |
| // we'll probably get this done again by notifyDeviceLost |
| // but best to remember it! |
| // Note that we don't want to clear the device loss status here |
| // -- this needs to be done by resetDevice |
| mDeviceLost = true; |
| } |
| |
| return isLost; |
| } |
| |
| bool Renderer11::testDeviceResettable() |
| { |
| // determine if the device is resettable by creating a dummy device |
| PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice"); |
| |
| if (D3D11CreateDevice == NULL) |
| { |
| return false; |
| } |
| |
| ID3D11Device* dummyDevice; |
| D3D_FEATURE_LEVEL dummyFeatureLevel; |
| ID3D11DeviceContext* dummyContext; |
| |
| ASSERT(mRequestedDriverType != D3D_DRIVER_TYPE_UNKNOWN); |
| HRESULT result = D3D11CreateDevice( |
| NULL, mRequestedDriverType, NULL, |
| #if defined(_DEBUG) |
| D3D11_CREATE_DEVICE_DEBUG, |
| #else |
| 0, |
| #endif |
| mAvailableFeatureLevels.data(), static_cast<unsigned int>(mAvailableFeatureLevels.size()), |
| D3D11_SDK_VERSION, &dummyDevice, &dummyFeatureLevel, &dummyContext); |
| |
| if (!mDevice || FAILED(result)) |
| { |
| return false; |
| } |
| |
| SafeRelease(dummyContext); |
| SafeRelease(dummyDevice); |
| |
| return true; |
| } |
| |
| void Renderer11::release() |
| { |
| RendererD3D::cleanup(); |
| |
| releaseDeviceResources(); |
| |
| if (!mCreatedWithDeviceEXT) |
| { |
| // Only delete the device if the Renderer11 owns it |
| // Otherwise we should keep it around in case we try to reinitialize the renderer later |
| SafeDelete(mEGLDevice); |
| } |
| |
| SafeRelease(mDxgiFactory); |
| SafeRelease(mDxgiAdapter); |
| |
| SafeRelease(mDeviceContext1); |
| |
| if (mDeviceContext) |
| { |
| mDeviceContext->ClearState(); |
| mDeviceContext->Flush(); |
| SafeRelease(mDeviceContext); |
| } |
| |
| SafeRelease(mDevice); |
| SafeRelease(mDebug); |
| |
| if (mD3d11Module) |
| { |
| FreeLibrary(mD3d11Module); |
| mD3d11Module = NULL; |
| } |
| |
| if (mDxgiModule) |
| { |
| FreeLibrary(mDxgiModule); |
| mDxgiModule = NULL; |
| } |
| |
| if (mDCompModule) |
| { |
| FreeLibrary(mDCompModule); |
| mDCompModule = NULL; |
| } |
| |
| mCompiler.release(); |
| |
| mSupportsShareHandles.reset(); |
| } |
| |
| bool Renderer11::resetDevice() |
| { |
| // recreate everything |
| release(); |
| egl::Error result = initialize(); |
| |
| if (result.isError()) |
| { |
| ERR("Could not reinitialize D3D11 device: %08X", result.getCode()); |
| return false; |
| } |
| |
| mDeviceLost = 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 = { 0 }; |
| 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); |
| } |
| |
| unsigned int Renderer11::getReservedVertexUniformBuffers() const |
| { |
| // we reserve one buffer for the application uniforms, and one for driver uniforms |
| return 2; |
| } |
| |
| unsigned int Renderer11::getReservedFragmentUniformBuffers() const |
| { |
| // we reserve one buffer for the application uniforms, and one for driver uniforms |
| return 2; |
| } |
| |
| 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 (!getRendererExtensions().textureFormatBGRA8888) |
| { |
| mSupportsShareHandles = false; |
| return false; |
| } |
| |
| // PIX doesn't seem to support using share handles, so disable them. |
| if (gl::DebugAnnotationsActive()) |
| { |
| 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) |
| { |
| #ifndef ANGLE_ENABLE_WINDOWS_STORE |
| if (!IsWindows8OrGreater()) |
| { |
| // WARP on Windows 7 doesn't support shared handles |
| mSupportsShareHandles = false; |
| return false; |
| } |
| #endif // ANGLE_ENABLE_WINDOWS_STORE |
| |
| // 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; |
| } |
| |
| bool Renderer11::getNV12TextureSupport() const |
| { |
| HRESULT result; |
| UINT formatSupport; |
| result = mDevice->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport); |
| if (result == E_FAIL) |
| { |
| return false; |
| } |
| return (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0; |
| } |
| |
| int Renderer11::getMajorShaderModel() const |
| { |
| switch (mRenderer11DeviceCaps.featureLevel) |
| { |
| 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_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_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 ""; |
| } |
| } |
| |
| const WorkaroundsD3D &RendererD3D::getWorkarounds() const |
| { |
| if (!mWorkaroundsInitialized) |
| { |
| mWorkarounds = generateWorkarounds(); |
| mWorkaroundsInitialized = true; |
| } |
| |
| return mWorkarounds; |
| } |
| |
| gl::Error Renderer11::copyImage2D(const gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, |
| const gl::Offset &destOffset, TextureStorage *storage, GLint level) |
| { |
| const gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); |
| ASSERT(colorbuffer); |
| |
| RenderTarget11 *sourceRenderTarget = NULL; |
| gl::Error error = colorbuffer->getRenderTarget(&sourceRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(sourceRenderTarget); |
| |
| ID3D11ShaderResourceView *source = sourceRenderTarget->getBlitShaderResourceView(); |
| ASSERT(source); |
| |
| // TextureStorage11_2D *storage11 = GetAs<TextureStorage11_2D>(storage); |
| TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage); |
| ASSERT(storage11); |
| |
| gl::ImageIndex index = gl::ImageIndex::Make2D(level); |
| RenderTargetD3D *destRenderTarget = NULL; |
| error = storage11->getRenderTarget(index, &destRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(destRenderTarget); |
| |
| ID3D11RenderTargetView *dest = GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView(); |
| ASSERT(dest); |
| |
| 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, colorbuffer); |
| 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 |
| error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, |
| destFormat, GL_NEAREST, false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| storage11->invalidateSwizzleCacheLevel(level); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::copyImageCube(const gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, |
| const gl::Offset &destOffset, TextureStorage *storage, GLenum target, GLint level) |
| { |
| const gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); |
| ASSERT(colorbuffer); |
| |
| RenderTarget11 *sourceRenderTarget = NULL; |
| gl::Error error = colorbuffer->getRenderTarget(&sourceRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(sourceRenderTarget); |
| |
| ID3D11ShaderResourceView *source = sourceRenderTarget->getBlitShaderResourceView(); |
| ASSERT(source); |
| |
| TextureStorage11_Cube *storage11 = GetAs<TextureStorage11_Cube>(storage); |
| ASSERT(storage11); |
| |
| gl::ImageIndex index = gl::ImageIndex::MakeCube(target, level); |
| RenderTargetD3D *destRenderTarget = NULL; |
| error = storage11->getRenderTarget(index, &destRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(destRenderTarget); |
| |
| ID3D11RenderTargetView *dest = GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView(); |
| ASSERT(dest); |
| |
| 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, colorbuffer); |
| 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 |
| error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, |
| destFormat, GL_NEAREST, false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| storage11->invalidateSwizzleCacheLevel(level); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::copyImage3D(const gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, |
| const gl::Offset &destOffset, TextureStorage *storage, GLint level) |
| { |
| const gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); |
| ASSERT(colorbuffer); |
| |
| RenderTarget11 *sourceRenderTarget = NULL; |
| gl::Error error = colorbuffer->getRenderTarget(&sourceRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(sourceRenderTarget); |
| |
| ID3D11ShaderResourceView *source = sourceRenderTarget->getBlitShaderResourceView(); |
| ASSERT(source); |
| |
| TextureStorage11_3D *storage11 = GetAs<TextureStorage11_3D>(storage); |
| ASSERT(storage11); |
| |
| gl::ImageIndex index = gl::ImageIndex::Make3D(level, destOffset.z); |
| RenderTargetD3D *destRenderTarget = NULL; |
| error = storage11->getRenderTarget(index, &destRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(destRenderTarget); |
| |
| ID3D11RenderTargetView *dest = GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView(); |
| ASSERT(dest); |
| |
| gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); |
| gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); |
| |
| 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 |
| error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, |
| destFormat, GL_NEAREST, false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| storage11->invalidateSwizzleCacheLevel(level); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::copyImage2DArray(const gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, |
| const gl::Offset &destOffset, TextureStorage *storage, GLint level) |
| { |
| const gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); |
| ASSERT(colorbuffer); |
| |
| RenderTarget11 *sourceRenderTarget = NULL; |
| gl::Error error = colorbuffer->getRenderTarget(&sourceRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(sourceRenderTarget); |
| |
| ID3D11ShaderResourceView *source = sourceRenderTarget->getBlitShaderResourceView(); |
| ASSERT(source); |
| |
| TextureStorage11_2DArray *storage11 = GetAs<TextureStorage11_2DArray>(storage); |
| ASSERT(storage11); |
| |
| gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, destOffset.z); |
| RenderTargetD3D *destRenderTarget = NULL; |
| error = storage11->getRenderTarget(index, &destRenderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| ASSERT(destRenderTarget); |
| |
| ID3D11RenderTargetView *dest = GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView(); |
| ASSERT(dest); |
| |
| gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); |
| gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); |
| |
| 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 |
| error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, |
| destFormat, GL_NEAREST, false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| storage11->invalidateSwizzleCacheLevel(level); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT) |
| { |
| const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(format, mRenderer11DeviceCaps); |
| |
| const gl::TextureCaps &textureCaps = getRendererTextureCaps().get(format); |
| GLuint supportedSamples = textureCaps.getNearestSamples(samples); |
| |
| 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.formatSet->texFormat; |
| desc.SampleDesc.Count = (supportedSamples == 0) ? 1 : supportedSamples; |
| desc.SampleDesc.Quality = 0; |
| 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.formatSet->rtvFormat != DXGI_FORMAT_UNKNOWN); |
| bindDSV = (formatInfo.formatSet->dsvFormat != DXGI_FORMAT_UNKNOWN); |
| if (formatInfo.formatSet->srvFormat != DXGI_FORMAT_UNKNOWN) |
| { |
| // Multisample targets flagged for binding as depth stencil cannot also be |
| // flagged for binding as SRV, so make certain not to add the SRV flag for |
| // these targets. |
| bindSRV = !(formatInfo.formatSet->dsvFormat != DXGI_FORMAT_UNKNOWN && |
| desc.SampleDesc.Count > 1); |
| } |
| |
| 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); |
| |
| ID3D11Texture2D *texture = NULL; |
| HRESULT result = mDevice->CreateTexture2D(&desc, NULL, &texture); |
| if (FAILED(result)) |
| { |
| ASSERT(result == E_OUTOFMEMORY); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create render target texture, result: 0x%X.", result); |
| } |
| |
| ID3D11ShaderResourceView *srv = nullptr; |
| ID3D11ShaderResourceView *blitSRV = nullptr; |
| if (bindSRV) |
| { |
| D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; |
| srvDesc.Format = formatInfo.formatSet->srvFormat; |
| srvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_SRV_DIMENSION_TEXTURE2D : D3D11_SRV_DIMENSION_TEXTURE2DMS; |
| srvDesc.Texture2D.MostDetailedMip = 0; |
| srvDesc.Texture2D.MipLevels = 1; |
| |
| result = mDevice->CreateShaderResourceView(texture, &srvDesc, &srv); |
| if (FAILED(result)) |
| { |
| ASSERT(result == E_OUTOFMEMORY); |
| SafeRelease(texture); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create render target shader resource view, result: 0x%X.", result); |
| } |
| |
| if (formatInfo.formatSet->blitSRVFormat != formatInfo.formatSet->srvFormat) |
| { |
| D3D11_SHADER_RESOURCE_VIEW_DESC blitSRVDesc; |
| blitSRVDesc.Format = formatInfo.formatSet->blitSRVFormat; |
| blitSRVDesc.ViewDimension = (supportedSamples == 0) |
| ? D3D11_SRV_DIMENSION_TEXTURE2D |
| : D3D11_SRV_DIMENSION_TEXTURE2DMS; |
| blitSRVDesc.Texture2D.MostDetailedMip = 0; |
| blitSRVDesc.Texture2D.MipLevels = 1; |
| |
| result = mDevice->CreateShaderResourceView(texture, &blitSRVDesc, &blitSRV); |
| if (FAILED(result)) |
| { |
| ASSERT(result == E_OUTOFMEMORY); |
| SafeRelease(texture); |
| SafeRelease(srv); |
| return gl::Error(GL_OUT_OF_MEMORY, |
| "Failed to create render target shader resource view for " |
| "blits, result: 0x%X.", |
| result); |
| } |
| } |
| else |
| { |
| blitSRV = srv; |
| srv->AddRef(); |
| } |
| } |
| |
| if (bindDSV) |
| { |
| D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; |
| dsvDesc.Format = formatInfo.formatSet->dsvFormat; |
| dsvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_DSV_DIMENSION_TEXTURE2D : D3D11_DSV_DIMENSION_TEXTURE2DMS; |
| dsvDesc.Texture2D.MipSlice = 0; |
| dsvDesc.Flags = 0; |
| |
| ID3D11DepthStencilView *dsv = NULL; |
| result = mDevice->CreateDepthStencilView(texture, &dsvDesc, &dsv); |
| if (FAILED(result)) |
| { |
| ASSERT(result == E_OUTOFMEMORY); |
| SafeRelease(texture); |
| SafeRelease(srv); |
| SafeRelease(blitSRV); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create render target depth stencil view, result: 0x%X.", result); |
| } |
| |
| *outRT = |
| new TextureRenderTarget11(dsv, texture, srv, format, formatInfo.formatSet->format, |
| width, height, 1, supportedSamples); |
| |
| SafeRelease(dsv); |
| } |
| else if (bindRTV) |
| { |
| D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; |
| rtvDesc.Format = formatInfo.formatSet->rtvFormat; |
| rtvDesc.ViewDimension = (supportedSamples == 0) ? D3D11_RTV_DIMENSION_TEXTURE2D : D3D11_RTV_DIMENSION_TEXTURE2DMS; |
| rtvDesc.Texture2D.MipSlice = 0; |
| |
| ID3D11RenderTargetView *rtv = NULL; |
| result = mDevice->CreateRenderTargetView(texture, &rtvDesc, &rtv); |
| if (FAILED(result)) |
| { |
| ASSERT(result == E_OUTOFMEMORY); |
| SafeRelease(texture); |
| SafeRelease(srv); |
| SafeRelease(blitSRV); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create render target render target view, result: 0x%X.", result); |
| } |
| |
| if (formatInfo.dataInitializerFunction != NULL) |
| { |
| const float clearValues[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; |
| mDeviceContext->ClearRenderTargetView(rtv, clearValues); |
| } |
| |
| *outRT = new TextureRenderTarget11(rtv, texture, srv, blitSRV, format, |
| formatInfo.formatSet->format, width, height, 1, |
| supportedSamples); |
| |
| SafeRelease(rtv); |
| } |
| else |
| { |
| UNREACHABLE(); |
| } |
| |
| SafeRelease(texture); |
| SafeRelease(srv); |
| SafeRelease(blitSRV); |
| } |
| else |
| { |
| *outRT = new TextureRenderTarget11(static_cast<ID3D11RenderTargetView *>(nullptr), nullptr, |
| nullptr, nullptr, format, d3d11::ANGLE_FORMAT_NONE, |
| width, height, 1, supportedSamples); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT) |
| { |
| ASSERT(source != nullptr); |
| |
| RenderTargetD3D *newRT = nullptr; |
| gl::Error error = createRenderTarget(source->getWidth(), source->getHeight(), |
| source->getInternalFormat(), source->getSamples(), &newRT); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| RenderTarget11 *source11 = GetAs<RenderTarget11>(source); |
| RenderTarget11 *dest11 = GetAs<RenderTarget11>(newRT); |
| |
| mDeviceContext->CopySubresourceRegion(dest11->getTexture(), dest11->getSubresourceIndex(), 0, 0, |
| 0, source11->getTexture(), |
| source11->getSubresourceIndex(), nullptr); |
| *outRT = newRT; |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| FramebufferImpl *Renderer11::createFramebuffer(const gl::FramebufferState &data) |
| { |
| return new Framebuffer11(data, this); |
| } |
| |
| ShaderImpl *Renderer11::createShader(const gl::ShaderState &data) |
| { |
| return new ShaderD3D(data); |
| } |
| |
| ProgramImpl *Renderer11::createProgram(const gl::ProgramState &data) |
| { |
| return new ProgramD3D(data, this); |
| } |
| |
| gl::Error Renderer11::loadExecutable(const void *function, |
| size_t length, |
| ShaderType type, |
| const std::vector<D3DVarying> &streamOutVaryings, |
| bool separatedOutputBuffers, |
| ShaderExecutableD3D **outExecutable) |
| { |
| switch (type) |
| { |
| case SHADER_VERTEX: |
| { |
| ID3D11VertexShader *vertexShader = NULL; |
| ID3D11GeometryShader *streamOutShader = NULL; |
| |
| HRESULT result = mDevice->CreateVertexShader(function, length, NULL, &vertexShader); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create vertex shader, result: 0x%X.", result); |
| } |
| |
| if (!streamOutVaryings.empty()) |
| { |
| std::vector<D3D11_SO_DECLARATION_ENTRY> soDeclaration; |
| soDeclaration.reserve(streamOutVaryings.size()); |
| |
| for (const auto &streamOutVarying : streamOutVaryings) |
| { |
| D3D11_SO_DECLARATION_ENTRY entry = {0}; |
| 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); |
| } |
| |
| result = mDevice->CreateGeometryShaderWithStreamOutput( |
| function, static_cast<unsigned int>(length), soDeclaration.data(), |
| static_cast<unsigned int>(soDeclaration.size()), NULL, 0, 0, NULL, |
| &streamOutShader); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create steam output shader, result: 0x%X.", result); |
| } |
| } |
| |
| *outExecutable = new ShaderExecutable11(function, length, vertexShader, streamOutShader); |
| } |
| break; |
| case SHADER_PIXEL: |
| { |
| ID3D11PixelShader *pixelShader = NULL; |
| |
| HRESULT result = mDevice->CreatePixelShader(function, length, NULL, &pixelShader); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create pixel shader, result: 0x%X.", result); |
| } |
| |
| *outExecutable = new ShaderExecutable11(function, length, pixelShader); |
| } |
| break; |
| case SHADER_GEOMETRY: |
| { |
| ID3D11GeometryShader *geometryShader = NULL; |
| |
| HRESULT result = mDevice->CreateGeometryShader(function, length, NULL, &geometryShader); |
| ASSERT(SUCCEEDED(result)); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create geometry shader, result: 0x%X.", result); |
| } |
| |
| *outExecutable = new ShaderExecutable11(function, length, geometryShader); |
| } |
| break; |
| default: |
| UNREACHABLE(); |
| return gl::Error(GL_INVALID_OPERATION); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, |
| const std::string &shaderHLSL, |
| ShaderType type, |
| const std::vector<D3DVarying> &streamOutVaryings, |
| bool separatedOutputBuffers, |
| const D3DCompilerWorkarounds &workarounds, |
| ShaderExecutableD3D **outExectuable) |
| { |
| const char *profileType = NULL; |
| switch (type) |
| { |
| case SHADER_VERTEX: |
| profileType = "vs"; |
| break; |
| case SHADER_PIXEL: |
| profileType = "ps"; |
| break; |
| case SHADER_GEOMETRY: |
| profileType = "gs"; |
| break; |
| default: |
| UNREACHABLE(); |
| return gl::Error(GL_INVALID_OPERATION); |
| } |
| |
| std::string profile = FormatString("%s_%d_%d%s", profileType, getMajorShaderModel(), getMinorShaderModel(), getShaderModelSuffix().c_str()); |
| |
| UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; |
| |
| if (gl::DebugAnnotationsActive()) |
| { |
| #ifndef NDEBUG |
| flags = D3DCOMPILE_SKIP_OPTIMIZATION; |
| #endif |
| |
| flags |= D3DCOMPILE_DEBUG; |
| } |
| |
| 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} }; |
| |
| ID3DBlob *binary = NULL; |
| std::string debugInfo; |
| gl::Error error = mCompiler.compileToBinary(infoLog, shaderHLSL, profile, configs, loopMacros, &binary, &debugInfo); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| // 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 = NULL; |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| error = loadExecutable(binary->GetBufferPointer(), binary->GetBufferSize(), type, |
| streamOutVaryings, separatedOutputBuffers, outExectuable); |
| |
| SafeRelease(binary); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| if (!debugInfo.empty()) |
| { |
| (*outExectuable)->appendDebugInfo(debugInfo); |
| } |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| UniformStorageD3D *Renderer11::createUniformStorage(size_t storageSize) |
| { |
| return new UniformStorage11(this, storageSize); |
| } |
| |
| VertexBuffer *Renderer11::createVertexBuffer() |
| { |
| return new VertexBuffer11(this); |
| } |
| |
| IndexBuffer *Renderer11::createIndexBuffer() |
| { |
| return new IndexBuffer11(this); |
| } |
| |
| BufferImpl *Renderer11::createBuffer() |
| { |
| Buffer11 *buffer = new Buffer11(this); |
| mAliveBuffers.insert(buffer); |
| return buffer; |
| } |
| |
| VertexArrayImpl *Renderer11::createVertexArray(const gl::VertexArrayState &data) |
| { |
| return new VertexArray11(data); |
| } |
| |
| QueryImpl *Renderer11::createQuery(GLenum type) |
| { |
| return new Query11(this, type); |
| } |
| |
| FenceNVImpl *Renderer11::createFenceNV() |
| { |
| return new FenceNV11(this); |
| } |
| |
| FenceSyncImpl *Renderer11::createFenceSync() |
| { |
| return new FenceSync11(this); |
| } |
| |
| TransformFeedbackImpl* Renderer11::createTransformFeedback() |
| { |
| return new TransformFeedbackD3D(); |
| } |
| |
| StreamProducerImpl *Renderer11::createStreamProducerD3DTextureNV12( |
| egl::Stream::ConsumerType consumerType, |
| const egl::AttributeMap &attribs) |
| { |
| return new StreamProducerNV12(this); |
| } |
| |
| bool Renderer11::supportsFastCopyBufferToTexture(GLenum internalFormat) const |
| { |
| ASSERT(getRendererExtensions().pixelBufferObject); |
| |
| const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat); |
| const d3d11::TextureFormat &d3d11FormatInfo = d3d11::GetTextureFormatInfo(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.formatSet->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.formatSet->glInternalFormat != internalFormat) |
| { |
| return false; |
| } |
| |
| // Buffer SRV creation in this format was not working on Windows 10, repro at least on Intel |
| // and NVIDIA. |
| if (internalFormat == GL_RGB5_A1) |
| { |
| return false; |
| } |
| |
| // This format does not work. |
| // BUG: 64484749 |
| if (internalFormat == GL_ALPHA8_EXT) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| gl::Error Renderer11::fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget, |
| GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea) |
| { |
| ASSERT(supportsFastCopyBufferToTexture(destinationFormat)); |
| return mPixelTransfer->copyBufferToTexture(unpack, offset, destRenderTarget, destinationFormat, sourcePixelsType, destArea); |
| } |
| |
| ImageD3D *Renderer11::createImage() |
| { |
| return new Image11(this); |
| } |
| |
| gl::Error Renderer11::generateMipmap(ImageD3D *dest, ImageD3D *src) |
| { |
| Image11 *dest11 = GetAs<Image11>(dest); |
| Image11 *src11 = GetAs<Image11>(src); |
| return Image11::generateMipmap(dest11, src11, mRenderer11DeviceCaps); |
| } |
| |
| gl::Error Renderer11::generateMipmapsUsingD3D(TextureStorage *storage, |
| const gl::TextureState &textureState) |
| { |
| TextureStorage11 *storage11 = GetAs<TextureStorage11>(storage); |
| |
| ASSERT(storage11->isRenderTarget()); |
| ASSERT(storage11->supportsNativeMipmapFunction()); |
| |
| ID3D11ShaderResourceView *srv; |
| gl::Error error = storage11->getSRVLevels(textureState.baseLevel, textureState.maxLevel, &srv); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| mDeviceContext->GenerateMips(srv); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorage2D(SwapChainD3D *swapChain) |
| { |
| SwapChain11 *swapChain11 = GetAs<SwapChain11>(swapChain); |
| return new TextureStorage11_2D(this, swapChain11); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorageEGLImage(EGLImageD3D *eglImage) |
| { |
| return new TextureStorage11_EGLImage(this, eglImage); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorageExternal( |
| egl::Stream *stream, |
| const egl::Stream::GLTextureDescription &desc) |
| { |
| return new TextureStorage11_External(this, stream, desc); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) |
| { |
| return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) |
| { |
| return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels, hintLevelZeroOnly); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) |
| { |
| return new TextureStorage11_3D(this, internalformat, renderTarget, width, height, depth, levels); |
| } |
| |
| TextureStorage *Renderer11::createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) |
| { |
| return new TextureStorage11_2DArray(this, internalformat, renderTarget, width, height, depth, levels); |
| } |
| |
| TextureImpl *Renderer11::createTexture(GLenum target) |
| { |
| switch(target) |
| { |
| case GL_TEXTURE_2D: return new TextureD3D_2D(this); |
| case GL_TEXTURE_CUBE_MAP: return new TextureD3D_Cube(this); |
| case GL_TEXTURE_3D: return new TextureD3D_3D(this); |
| case GL_TEXTURE_2D_ARRAY: return new TextureD3D_2DArray(this); |
| case GL_TEXTURE_EXTERNAL_OES: |
| return new TextureD3D_External(this); |
| default: |
| UNREACHABLE(); |
| } |
| |
| return NULL; |
| } |
| |
| RenderbufferImpl *Renderer11::createRenderbuffer() |
| { |
| RenderbufferD3D *renderbuffer = new RenderbufferD3D(this); |
| return renderbuffer; |
| } |
| |
| gl::Error Renderer11::readFromAttachment(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); |
| |
| RenderTargetD3D *renderTarget = nullptr; |
| gl::Error error = srcAttachment.getRenderTarget(&renderTarget); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| RenderTarget11 *rt11 = GetAs<RenderTarget11>(renderTarget); |
| ASSERT(rt11->getTexture()); |
| |
| TextureHelper11 textureHelper = |
| TextureHelper11::MakeAndReference(rt11->getTexture(), rt11->getANGLEFormat()); |
| unsigned int sourceSubResource = rt11->getSubresourceIndex(); |
| |
| const gl::Extents &texSize = textureHelper.getExtents(); |
| |
| gl::Rectangle actualArea = sourceArea; |
| if (invertTexture) |
| { |
| actualArea.y = texSize.height - actualArea.y - actualArea.height; |
| } |
| |
| // 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 gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Extents safeSize(safeArea.width, safeArea.height, 1); |
| auto errorOrResult = |
| CreateStagingTexture(textureHelper.getTextureType(), textureHelper.getFormat(), |
| textureHelper.getANGLEFormat(), safeSize, mDevice); |
| if (errorOrResult.isError()) |
| { |
| return errorOrResult.getError(); |
| } |
| |
| TextureHelper11 stagingHelper(errorOrResult.getResult()); |
| 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.getTextureType() == GL_TEXTURE_2D && 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; |
| |
| ID3D11Texture2D *resolveTex2D = nullptr; |
| HRESULT result = mDevice->CreateTexture2D(&resolveDesc, nullptr, &resolveTex2D); |
| if (FAILED(result)) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, |
| "Renderer11::readTextureData failed to create internal resolve " |
| "texture for ReadPixels, HRESULT: 0x%X.", |
| result); |
| } |
| |
| mDeviceContext->ResolveSubresource(resolveTex2D, 0, textureHelper.getTexture2D(), |
| sourceSubResource, textureHelper.getFormat()); |
| resolvedTextureHelper = |
| TextureHelper11::MakeAndReference(resolveTex2D, textureHelper.getANGLEFormat()); |
| |
| 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.getTextureType() == GL_TEXTURE_3D) |
| { |
| srcBox.front = static_cast<UINT>(srcAttachment.layer()); |
| } |
| srcBox.back = srcBox.front + 1; |
| |
| mDeviceContext->CopySubresourceRegion(stagingHelper.getResource(), 0, 0, 0, 0, |
| srcTexture->getResource(), sourceSubResource, &srcBox); |
| |
| if (invertTexture) |
| { |
| gl::PixelPackState invertTexturePack; |
| |
| // Create a new PixelPackState with reversed row order. Note that we can't just assign |
| // 'invertTexturePack' to be 'pack' (or memcpy) since that breaks the ref counting/object |
| // tracking in the 'pixelBuffer' members, causing leaks. Instead we must use |
| // pixelBuffer.set() twice, which performs the addRef/release correctly |
| invertTexturePack.alignment = pack.alignment; |
| invertTexturePack.pixelBuffer.set(pack.pixelBuffer.get()); |
| invertTexturePack.reverseRowOrder = !pack.reverseRowOrder; |
| |
| PackPixelsParams packParams(safeArea, format, type, outputPitch, invertTexturePack, 0); |
| error = packPixels(stagingHelper, packParams, pixelsOut); |
| |
| invertTexturePack.pixelBuffer.set(nullptr); |
| |
| return error; |
| } |
| else |
| { |
| PackPixelsParams packParams(safeArea, format, type, outputPitch, pack, 0); |
| return packPixels(stagingHelper, packParams, pixelsOut); |
| } |
| } |
| |
| gl::Error Renderer11::packPixels(const TextureHelper11 &textureHelper, |
| const PackPixelsParams ¶ms, |
| uint8_t *pixelsOut) |
| { |
| ID3D11Resource *readResource = textureHelper.getResource(); |
| |
| D3D11_MAPPED_SUBRESOURCE mapping; |
| HRESULT hr = mDeviceContext->Map(readResource, 0, D3D11_MAP_READ, 0, &mapping); |
| if (FAILED(hr)) |
| { |
| ASSERT(hr == E_OUTOFMEMORY); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to map internal texture for reading, result: 0x%X.", hr); |
| } |
| |
| uint8_t *source; |
| int inputPitch; |
| if (params.pack.reverseRowOrder) |
| { |
| source = static_cast<uint8_t*>(mapping.pData) + mapping.RowPitch * (params.area.height - 1); |
| inputPitch = -static_cast<int>(mapping.RowPitch); |
| } |
| else |
| { |
| source = static_cast<uint8_t*>(mapping.pData); |
| inputPitch = static_cast<int>(mapping.RowPitch); |
| } |
| |
| const auto &angleFormatInfo = d3d11::GetANGLEFormatSet(textureHelper.getANGLEFormat()); |
| const gl::InternalFormat &sourceFormatInfo = |
| gl::GetInternalFormatInfo(angleFormatInfo.glInternalFormat); |
| if (sourceFormatInfo.format == params.format && sourceFormatInfo.type == params.type) |
| { |
| uint8_t *dest = pixelsOut + params.offset; |
| for (int y = 0; y < params.area.height; y++) |
| { |
| memcpy(dest + y * params.outputPitch, source + y * inputPitch, params.area.width * sourceFormatInfo.pixelBytes); |
| } |
| } |
| else |
| { |
| const d3d11::DXGIFormat &dxgiFormatInfo = |
| d3d11::GetDXGIFormatInfo(textureHelper.getFormat()); |
| ColorCopyFunction fastCopyFunc = |
| dxgiFormatInfo.getFastCopyFunction(params.format, params.type); |
| GLenum sizedDestInternalFormat = gl::GetSizedInternalFormat(params.format, params.type); |
| const gl::InternalFormat &destFormatInfo = gl::GetInternalFormatInfo(sizedDestInternalFormat); |
| |
| if (fastCopyFunc) |
| { |
| // Fast copy is possible through some special function |
| for (int y = 0; y < params.area.height; y++) |
| { |
| for (int x = 0; x < params.area.width; x++) |
| { |
| uint8_t *dest = pixelsOut + params.offset + y * params.outputPitch + x * destFormatInfo.pixelBytes; |
| const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; |
| |
| fastCopyFunc(src, dest); |
| } |
| } |
| } |
| else |
| { |
| ColorReadFunction colorReadFunction = angleFormatInfo.colorReadFunction; |
| ColorWriteFunction colorWriteFunction = GetColorWriteFunction(params.format, params.type); |
| |
| uint8_t temp[16]; // Maximum size of any Color<T> type used. |
| static_assert(sizeof(temp) >= sizeof(gl::ColorF) && |
| sizeof(temp) >= sizeof(gl::ColorUI) && |
| sizeof(temp) >= sizeof(gl::ColorI), |
| "Unexpected size of gl::Color struct."); |
| |
| for (int y = 0; y < params.area.height; y++) |
| { |
| for (int x = 0; x < params.area.width; x++) |
| { |
| uint8_t *dest = pixelsOut + params.offset + y * params.outputPitch + x * destFormatInfo.pixelBytes; |
| const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; |
| |
| // readFunc and writeFunc will be using the same type of color, CopyTexImage |
| // will not allow the copy otherwise. |
| colorReadFunction(src, temp); |
| colorWriteFunction(temp, dest); |
| } |
| } |
| } |
| } |
| |
| mDeviceContext->Unmap(readResource, 0); |
| |
| return gl::Error(GL_NO_ERROR); |
| } |
| |
| gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRectIn, |
| const gl::Rectangle &drawRectIn, |
| 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); |
| if (!drawRenderTarget) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to retrieve the internal draw render target from the draw framebuffer."); |
| } |
| |
| ID3D11Resource *drawTexture = drawRenderTarget11->getTexture(); |
| unsigned int drawSubresource = drawRenderTarget11->getSubresourceIndex(); |
| ID3D11RenderTargetView *drawRTV = drawRenderTarget11->getRenderTargetView(); |
| ID3D11DepthStencilView *drawDSV = drawRenderTarget11->getDepthStencilView(); |
| |
| RenderTarget11 *readRenderTarget11 = GetAs<RenderTarget11>(readRenderTarget); |
| if (!readRenderTarget) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to retrieve the internal read render target from the read framebuffer."); |
| } |
| |
| ID3D11Resource *readTexture = NULL; |
| ID3D11ShaderResourceView *readSRV = NULL; |
| unsigned int readSubresource = 0; |
| if (readRenderTarget->getSamples() > 0) |
| { |
| ID3D11Resource *unresolvedResource = readRenderTarget11->getTexture(); |
| ID3D11Texture2D *unresolvedTexture = d3d11::DynamicCastComObject<ID3D11Texture2D>(unresolvedResource); |
| |
| if (unresolvedTexture) |
| { |
| readTexture = resolveMultisampledTexture(unresolvedTexture, readRenderTarget11->getSubresourceIndex()); |
| readSubresource = 0; |
| |
| SafeRelease(unresolvedTexture); |
| |
| HRESULT hresult = mDevice->CreateShaderResourceView(readTexture, NULL, &readSRV); |
| if (FAILED(hresult)) |
| { |
| SafeRelease(readTexture); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to create shader resource view to resolve multisampled framebuffer."); |
| } |
| } |
| } |
| else |
| { |
| readTexture = readRenderTarget11->getTexture(); |
| readTexture->AddRef(); |
| readSubresource = readRenderTarget11->getSubresourceIndex(); |
| readSRV = readRenderTarget11->getBlitShaderResourceView(); |
| if (readSRV == nullptr) |
| { |
| ASSERT(depthBlit || stencilBlit); |
| readSRV = readRenderTarget11->getShaderResourceView(); |
| } |
| readSRV->AddRef(); |
| } |
| |
| if (!readTexture || !readSRV) |
| { |
| SafeRelease(readTexture); |
| SafeRelease(readSRV); |
| return gl::Error(GL_OUT_OF_MEMORY, "Failed to retrieve the internal read render target view from the read render target."); |
| } |
| |
| gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1); |
| 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. |
| gl::Rectangle readRect = readRectIn; |
| gl::Rectangle drawRect = drawRectIn; |
| auto readToDrawX = [&drawRectIn, &readRectIn](int readOffset) |
| { |
| double readToDrawScale = |
| static_cast<double>(drawRectIn.width) / static_cast<double>(readRectIn.width); |
| return static_cast<int>(round(static_cast<double>(readOffset) * readToDrawScale)); |
| }; |
| if (readRect.x < 0) |
| { |
| int readOffset = -readRect.x; |
| readRect.x += readOffset; |
| readRect.width -= readOffset; |
| |
| int drawOffset = readToDrawX(readOffset); |
| drawRect.x += drawOffset; |
| drawRect.width -= drawOffset; |
| } |
| |
| auto readToDrawY = [&drawRectIn, &readRectIn](int readOffset) |
| { |
| double readToDrawScale = |
| static_cast<double>(drawRectIn.height) / static_cast<double>(readRectIn.height); |
| return static_cast<int>(round(static_cast<double>(readOffset) * readToDrawScale)); |
| }; |
| if (readRect.y < 0) |
| { |
| int readOffset = -readRect.y; |
| readRect.y += readOffset; |
| readRect.height -= readOffset; |
| |
| int drawOffset = readToDrawY(readOffset); |
| drawRect.y += drawOffset; |
| drawRect.height -= drawOffset; |
| } |
| |
| if (readRect.x1() < 0) |
| { |
| int readOffset = -readRect.x1(); |
| readRect.width += readOffset; |
| |
| int drawOffset = readToDrawX(readOffset); |
| drawRect.width += drawOffset; |
| } |
| |
| if (readRect.y1() < 0) |
| { |
| int readOffset = -readRect.y1(); |
| readRect.height += readOffset; |
| |
| int drawOffset = readToDrawY(readOffset); |
| drawRect.height += drawOffset; |
| } |
| |
| bool scissorNeeded = scissor && gl::ClipRectangle(drawRect, *scissor, NULL); |
| |
| const auto &destFormatInfo = gl::GetInternalFormatInfo(drawRenderTarget->getInternalFormat()); |
| const auto &srcFormatInfo = gl::GetInternalFormatInfo(readRenderTarget->getInternalFormat()); |
| const auto &formatSet = d3d11::GetANGLEFormatSet(drawRenderTarget11->getANGLEFormat()); |
| const DXGI_FORMAT drawDXGIFormat = colorBlit ? formatSet.rtvFormat : formatSet.dsvFormat; |
| const auto &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(drawDXGIFormat); |
| |
| // 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) && |
| (dxgiFormatInfo.redBits > 0); |
| colorMask.green = (srcFormatInfo.greenBits > 0) && (destFormatInfo.greenBits == 0) && |
| (dxgiFormatInfo.greenBits > 0); |
| colorMask.blue = (srcFormatInfo.blueBits > 0) && (destFormatInfo.blueBits == 0) && |
| (dxgiFormatInfo.blueBits > 0); |
| colorMask.alpha = (srcFormatInfo.alphaBits > 0) && (destFormatInfo.alphaBits == 0) && |
| (dxgiFormatInfo.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; |
| |
| bool flipRequired = readRect.width < 0 || readRect.height < 0 || drawRect.width < 0 || drawRect.height < 0; |
| |
| 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 = (dxgiFormatInfo.depthBits > 0 && depthBlit) != (dxgiFormatInfo.stencilBits > 0 && stencilBlit); |
| |
| gl::Error result(GL_NO_ERROR); |
| |
| if (readRenderTarget11->getANGLEFormat() == drawRenderTarget11->getANGLEFormat() && |
| !stretchRequired && !outOfBounds && !flipRequired && !partialDSBlit && |
| !colorMaskingNeeded && (!(depthBlit || stencilBlit) || wholeBufferCopy)) |
| { |
| UINT dstX = drawRect.x; |
| UINT dstY = drawRect.y; |
| |
| 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 = 0; |
| readBox.back = 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 ? NULL : &readBox; |
| |
| mDeviceContext->CopySubresourceRegion(drawTexture, drawSubresource, dstX, dstY, 0, |
| readTexture, readSubresource, pSrcBox); |
| result = gl::Error(GL_NO_ERROR); |
| } |
| 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) |
| { |
| result = mBlit->copyDepthStencil(readTexture, readSubresource, readArea, readSize, |
| drawTexture, drawSubresource, drawArea, drawSize, |
| scissor); |
| } |
| else if (depthBlit) |
| { |
| result = mBlit->copyDepth(readSRV, readArea, readSize, drawDSV, drawArea, drawSize, |
| scissor); |
| } |
| else if (stencilBlit) |
| { |
| result = mBlit->copyStencil(readTexture, readSubresource, readArea, readSize, |
| drawTexture, drawSubresource, drawArea, drawSize, |
| scissor); |
| } |
| else |
| { |
| // We don't currently support masking off any other channel than alpha |
| bool maskOffAlpha = colorMaskingNeeded && colorMask.alpha; |
| result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize, |
| scissor, destFormatInfo.format, filter, maskOffAlpha); |
| } |
| } |
| |
| SafeRelease(readTexture); |
| SafeRelease(readSRV); |
| |
| return result; |
| } |
| |
| bool Renderer11::isES3Capable() const |
| { |
| return (d3d11_gl::GetMaximumClientVersion(mRenderer11DeviceCaps.featureLevel) > 2); |
| }; |
| |
| void Renderer11::onSwap() |
| { |
| // Send histogram updates every half hour |
| const double kHistogramUpdateInterval = 30 * 60; |
| |
| const double currentTime = ANGLEPlatformCurrent()->monotonicallyIncreasingTime(); |
| 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 (auto &buffer : mAliveBuffers) |
| { |
| sizeSum += buffer->getTotalCPUBufferMemoryBytes(); |
| } |
| const int kOneMegaByte = 1024 * 1024; |
| ANGLE_HISTOGRAM_MEMORY_MB("GPU.ANGLE.Buffer11CPUMemoryMB", |
| static_cast<int>(sizeSum) / kOneMegaByte); |
| } |
| } |
| |
| void Renderer11::onBufferDelete(const Buffer11 *deleted) |
| { |
| mAliveBuffers.erase(deleted); |
| } |
| |
| void Renderer11::onMakeCurrent(const gl::ContextState &data) |
| { |
| mStateManager.onMakeCurrent(data); |
| } |
| |
| ID3D11Texture2D *Renderer11::resolveMultisampledTexture(ID3D11Texture2D *source, unsigned int subresource) |
| { |
| D3D11_TEXTURE2D_DESC textureDesc; |
| source->GetDesc(&textureDesc); |
| |
| if (textureDesc.SampleDesc.Count > 1) |
| { |
| D3D11_TEXTURE2D_DESC resolveDesc; |
| resolveDesc.Width = textureDesc.Width; |
| resolveDesc.Height = textureDesc.Height; |
| resolveDesc.MipLevels = 1; |
| resolveDesc.ArraySize = 1; |
| resolveDesc.Format = textureDesc.Format; |
| resolveDesc.SampleDesc.Count = 1; |
| resolveDesc.SampleDesc.Quality = 0; |
| resolveDesc.Usage = textureDesc.Usage; |
| resolveDesc.BindFlags = textureDesc.BindFlags; |
| resolveDesc.CPUAccessFlags = 0; |
| resolveDesc.MiscFlags = 0; |
| |
| ID3D11Texture2D *resolveTexture = NULL; |
| HRESULT result = mDevice->CreateTexture2D(&resolveDesc, NULL, &resolveTexture); |
| if (FAILED(result)) |
| { |
| ERR("Failed to create a multisample resolve texture, HRESULT: 0x%X.", result); |
| return NULL; |
| } |
| |
| mDeviceContext->ResolveSubresource(resolveTexture, 0, source, subresource, textureDesc.Format); |
| return resolveTexture; |
| } |
| else |
| { |
| source->AddRef(); |
| return source; |
| } |
| } |
| |
| 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(gl::VertexFormatType vertexFormatType) const |
| { |
| return d3d11::GetVertexFormatInfo(vertexFormatType, mRenderer11DeviceCaps.featureLevel).conversionType; |
| } |
| |
| GLenum Renderer11::getVertexComponentType(gl::VertexFormatType vertexFormatType) const |
| { |
| return d3d11::GetDXGIFormatInfo(d3d11::GetVertexFormatInfo(vertexFormatType, mRenderer11DeviceCaps.featureLevel).nativeFormat).componentType; |
| } |
| |
| gl::ErrorOrResult<unsigned int> Renderer11::getVertexSpaceRequired( |
| const gl::VertexAttribute &attrib, |
| GLsizei count, |
| GLsizei instances) const |
| { |
| if (!attrib.enabled) |
| { |
| return 16u; |
| } |
| |
| unsigned int elementCount = 0; |
| if (instances == 0 || attrib.divisor == 0) |
| { |
| elementCount = count; |
| } |
| else |
| { |
| // Round up to divisor, if possible |
| elementCount = UnsignedCeilDivide(static_cast<unsigned int>(instances), attrib.divisor); |
| } |
| |
| gl::VertexFormatType formatType = gl::GetVertexFormatType(attrib); |
| const D3D_FEATURE_LEVEL featureLevel = mRenderer11DeviceCaps.featureLevel; |
| const d3d11::VertexFormat &vertexFormatInfo = |
| d3d11::GetVertexFormatInfo(formatType, featureLevel); |
| const d3d11::DXGIFormatSize &dxgiFormatInfo = |
| d3d11::GetDXGIFormatSizeInfo(vertexFormatInfo.nativeFormat); |
| unsigned int elementSize = dxgiFormatInfo.pixelBytes; |
| if (elementSize > std::numeric_limits<unsigned int>::max() / elementCount) |
| { |
| return gl::Error(GL_OUT_OF_MEMORY, "New vertex buffer size would result in an overflow."); |
| } |
| |
| return elementSize * elementCount; |
| } |
| |
| void Renderer11::generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, |
| gl::Extensions *outExtensions, gl::Limitations *outLimitations) const |
| { |
| d3d11_gl::GenerateCaps(mDevice, mDeviceContext, mRenderer11DeviceCaps, outCaps, outTextureCaps, |
| outExtensions, outLimitations); |
| } |
| |
| WorkaroundsD3D Renderer11::generateWorkarounds() const |
| { |
| return d3d11::GenerateWorkarounds(mRenderer11DeviceCaps.featureLevel); |
| } |
| |
| void Renderer11::createAnnotator() |
| { |
| // The D3D11 renderer must choose the D3D9 debug annotator because the D3D11 interface |
| // method ID3DUserDefinedAnnotation::GetStatus on desktop builds doesn't work with the Graphics |
| // Diagnostics tools in Visual Studio 2013. |
| // The D3D9 annotator works properly for both D3D11 and D3D9. |
| // Incorrect status reporting can cause ANGLE to log unnecessary debug events. |
| #ifdef ANGLE_ENABLE_D3D9 |
| mAnnotator = new DebugAnnotator9(); |
| #else |
| mAnnotator = new DebugAnnotator11(); |
| #endif |
| } |
| |
| gl::Error Renderer11::clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) |
| { |
| return mStateManager.clearTextures(samplerType, rangeStart, rangeEnd); |
| } |
| |
| egl::Error Renderer11::getEGLDevice(DeviceImpl **device) |
| { |
| if (mEGLDevice == nullptr) |
| { |
| ASSERT(mDevice != nullptr); |
| mEGLDevice = new DeviceD3D(); |
| egl::Error error = mEGLDevice->initialize(reinterpret_cast<void *>(mDevice), |
| EGL_D3D11_DEVICE_ANGLE, EGL_FALSE); |
| |
| if (error.isError()) |
| { |
| SafeDelete(mEGLDevice); |
| return error; |
| } |
| } |
| |
| *device = static_cast<DeviceImpl *>(mEGLDevice); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| } // namespace rx |