diff options
Diffstat (limited to 'src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp | 3527 |
1 files changed, 3527 insertions, 0 deletions
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp new file mode 100644 index 0000000000..17a13f97d6 --- /dev/null +++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp @@ -0,0 +1,3527 @@ +#include "precompiled.h" +// +// 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 "libGLESv2/main.h" +#include "common/utilities.h" +#include "common/platform.h" +#include "libGLESv2/Buffer.h" +#include "libGLESv2/FramebufferAttachment.h" +#include "libGLESv2/ProgramBinary.h" +#include "libGLESv2/Framebuffer.h" +#include "libGLESv2/renderer/d3d/TextureD3D.h" +#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h" +#include "libGLESv2/renderer/d3d/d3d11/RenderTarget11.h" +#include "libGLESv2/renderer/d3d/d3d11/renderer11_utils.h" +#include "libGLESv2/renderer/d3d/d3d11/formatutils11.h" +#include "libGLESv2/renderer/d3d/d3d11/ShaderExecutable11.h" +#include "libGLESv2/renderer/d3d/d3d11/SwapChain11.h" +#include "libGLESv2/renderer/d3d/d3d11/Image11.h" +#include "libGLESv2/renderer/d3d/d3d11/VertexBuffer11.h" +#include "libGLESv2/renderer/d3d/d3d11/IndexBuffer11.h" +#include "libGLESv2/renderer/d3d/d3d11/Buffer11.h" +#include "libGLESv2/renderer/d3d/VertexDataManager.h" +#include "libGLESv2/renderer/d3d/IndexDataManager.h" +#include "libGLESv2/renderer/d3d/d3d11/TextureStorage11.h" +#include "libGLESv2/renderer/d3d/d3d11/Query11.h" +#include "libGLESv2/renderer/d3d/d3d11/Fence11.h" +#include "libGLESv2/renderer/d3d/d3d11/Blit11.h" +#include "libGLESv2/renderer/d3d/d3d11/Clear11.h" +#include "libGLESv2/renderer/d3d/d3d11/PixelTransfer11.h" +#include "libGLESv2/renderer/d3d/d3d11/VertexArray11.h" +#include "libGLESv2/renderer/d3d/d3d11/Buffer11.h" +#include "libEGL/Display.h" + +// 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 +{ +static const DXGI_FORMAT RenderTargetFormats[] = + { + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM + }; + +static const DXGI_FORMAT DepthStencilFormats[] = + { + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT, + DXGI_FORMAT_D16_UNORM + }; + +enum +{ + MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 = 16 +}; + +Renderer11::Renderer11(egl::Display *display, EGLNativeDisplayType hDc, EGLint requestedDisplay) + : Renderer(display), + mDc(hDc), + mRequestedDisplay(requestedDisplay) +{ + mVertexDataManager = NULL; + mIndexDataManager = NULL; + + mLineLoopIB = NULL; + mTriangleFanIB = NULL; + + mBlit = NULL; + mPixelTransfer = NULL; + + mClear = NULL; + + mSyncQuery = NULL; + + mD3d11Module = NULL; + mDxgiModule = NULL; + + mDeviceLost = false; + + mMaxSupportedSamples = 0; + + mDevice = NULL; + mDeviceContext = NULL; + mDxgiAdapter = NULL; + mDxgiFactory = NULL; + + mDriverConstantBufferVS = NULL; + mDriverConstantBufferPS = NULL; + + mAppliedVertexShader = NULL; + mAppliedGeometryShader = NULL; + mCurPointGeometryShader = NULL; + mAppliedPixelShader = NULL; +} + +Renderer11::~Renderer11() +{ + release(); +} + +Renderer11 *Renderer11::makeRenderer11(Renderer *renderer) +{ + ASSERT(HAS_DYNAMIC_TYPE(rx::Renderer11*, renderer)); + return static_cast<rx::Renderer11*>(renderer); +} + +#ifndef __d3d11_1_h__ +#define D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET ((D3D11_MESSAGE_ID)3146081) +#endif + +EGLint Renderer11::initialize() +{ + if (!mCompiler.initialize()) + { + return EGL_NOT_INITIALIZED; + } + +#if !defined(ANGLE_PLATFORM_WINRT) + mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); + mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); + + if (mD3d11Module == NULL || mDxgiModule == NULL) + { + ERR("Could not load D3D11 or DXGI library - aborting!\n"); + return EGL_NOT_INITIALIZED; + } + + // create the D3D11 device + ASSERT(mDevice == NULL); + PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice"); + + if (D3D11CreateDevice == NULL) + { + ERR("Could not retrieve D3D11CreateDevice address - aborting!\n"); + return EGL_NOT_INITIALIZED; + } +#endif + + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, +#if !defined(ANGLE_ENABLE_D3D9) + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, +#endif + }; + + D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_HARDWARE; + if (mRequestedDisplay == EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE) + { + driverType = D3D_DRIVER_TYPE_WARP; + } + + HRESULT result = S_OK; + +#ifdef _DEBUG + result = D3D11CreateDevice(NULL, + driverType, + NULL, + D3D11_CREATE_DEVICE_DEBUG, + featureLevels, + ArraySize(featureLevels), + D3D11_SDK_VERSION, + &mDevice, + &mFeatureLevel, + &mDeviceContext); + + if (!mDevice || FAILED(result)) + { + ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n"); + } + + if (!mDevice || FAILED(result)) +#endif + { + result = D3D11CreateDevice(NULL, + driverType, + NULL, + 0, + featureLevels, + ArraySize(featureLevels), + D3D11_SDK_VERSION, + &mDevice, + &mFeatureLevel, + &mDeviceContext); + + if (!mDevice || FAILED(result)) + { + ERR("Could not create D3D11 device - aborting!\n"); + return EGL_NOT_INITIALIZED; // Cleanup done by destructor through glDestroyRenderer + } + } + +#if !ANGLE_SKIP_DXGI_1_2_CHECK && !defined(ANGLE_PLATFORM_WINRT) + // 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(mDc); + 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)) + { + ERR("DXGI 1.2 required to present to HWNDs owned by another process.\n"); + return EGL_NOT_INITIALIZED; + } + SafeRelease(dxgiDevice2); + } +#endif + +#if !defined(ANGLE_PLATFORM_WINRT) + IDXGIDevice *dxgiDevice = NULL; +#else + IDXGIDevice1 *dxgiDevice = NULL; +#endif + result = mDevice->QueryInterface(IID_PPV_ARGS(&dxgiDevice)); + + if (FAILED(result)) + { + ERR("Could not query DXGI device - aborting!\n"); + return EGL_NOT_INITIALIZED; + } + + result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&mDxgiAdapter); + + if (FAILED(result)) + { + ERR("Could not retrieve DXGI adapter - aborting!\n"); + return EGL_NOT_INITIALIZED; + } + + SafeRelease(dxgiDevice); + + mDxgiAdapter->GetDesc(&mAdapterDescription); + memset(mDescription, 0, sizeof(mDescription)); + wcstombs(mDescription, mAdapterDescription.Description, sizeof(mDescription) - 1); + + result = mDxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&mDxgiFactory); + + if (!mDxgiFactory || FAILED(result)) + { + ERR("Could not create DXGI factory - aborting!\n"); + return EGL_NOT_INITIALIZED; + } + + // Disable some spurious D3D11 debug warnings to prevent them from flooding the output log +#if defined(ANGLE_SUPPRESS_D3D11_HAZARD_WARNINGS) && defined(_DEBUG) + 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 = {0}; + filter.DenyList.NumIDs = ArraySize(hideMessages); + filter.DenyList.pIDList = hideMessages; + + infoQueue->AddStorageFilterEntries(&filter); + SafeRelease(infoQueue); + } +#endif + + mMaxSupportedSamples = 0; + + const d3d11::DXGIFormatSet &dxgiFormats = d3d11::GetAllUsedDXGIFormats(); + for (d3d11::DXGIFormatSet::const_iterator i = dxgiFormats.begin(); i != dxgiFormats.end(); ++i) + { + MultisampleSupportInfo support = getMultisampleSupportInfo(*i); + mMultisampleSupportMap.insert(std::make_pair(*i, support)); + mMaxSupportedSamples = std::max(mMaxSupportedSamples, support.maxSupportedSamples); + } + + initializeDevice(); + + return 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() +{ + mStateCache.initialize(mDevice); + mInputLayoutCache.initialize(mDevice, mDeviceContext); + + ASSERT(!mVertexDataManager && !mIndexDataManager); + mVertexDataManager = new VertexDataManager(this); + mIndexDataManager = new IndexDataManager(this); + + ASSERT(!mBlit); + mBlit = new Blit11(this); + + ASSERT(!mClear); + mClear = new Clear11(this); + + ASSERT(!mPixelTransfer); + mPixelTransfer = new PixelTransfer11(this); + + markAllStateDirty(); +} + +int Renderer11::generateConfigs(ConfigDesc **configDescList) +{ + unsigned int numRenderFormats = ArraySize(RenderTargetFormats); + unsigned int numDepthFormats = ArraySize(DepthStencilFormats); + (*configDescList) = new ConfigDesc[numRenderFormats * numDepthFormats]; + int numConfigs = 0; + + for (unsigned int formatIndex = 0; formatIndex < numRenderFormats; formatIndex++) + { + for (unsigned int depthStencilIndex = 0; depthStencilIndex < numDepthFormats; depthStencilIndex++) + { + DXGI_FORMAT renderTargetFormat = RenderTargetFormats[formatIndex]; + + UINT formatSupport = 0; + HRESULT result = mDevice->CheckFormatSupport(renderTargetFormat, &formatSupport); + + if (SUCCEEDED(result) && (formatSupport & D3D11_FORMAT_SUPPORT_RENDER_TARGET)) + { + DXGI_FORMAT depthStencilFormat = DepthStencilFormats[depthStencilIndex]; + + bool depthStencilFormatOK = true; + + if (depthStencilFormat != DXGI_FORMAT_UNKNOWN) + { + UINT depthStencilSupport = 0; + result = mDevice->CheckFormatSupport(depthStencilFormat, &depthStencilSupport); + depthStencilFormatOK = SUCCEEDED(result) && (depthStencilSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + } + + if (depthStencilFormatOK) + { + ConfigDesc newConfig; + newConfig.renderTargetFormat = d3d11_gl::GetInternalFormat(renderTargetFormat); + newConfig.depthStencilFormat = d3d11_gl::GetInternalFormat(depthStencilFormat); + newConfig.multiSample = 0; // FIXME: enumerate multi-sampling + newConfig.fastConfig = true; // Assume all DX11 format conversions to be fast + newConfig.es3Capable = true; + + (*configDescList)[numConfigs++] = newConfig; + } + } + } + } + + return numConfigs; +} + +void Renderer11::deleteConfigs(ConfigDesc *configDescList) +{ + delete [] (configDescList); +} + +void Renderer11::sync(bool block) +{ + if (block) + { + HRESULT result; + + if (!mSyncQuery) + { + D3D11_QUERY_DESC queryDesc; + queryDesc.Query = D3D11_QUERY_EVENT; + queryDesc.MiscFlags = 0; + + result = mDevice->CreateQuery(&queryDesc, &mSyncQuery); + ASSERT(SUCCEEDED(result)); + } + + mDeviceContext->End(mSyncQuery); + mDeviceContext->Flush(); + + do + { + result = mDeviceContext->GetData(mSyncQuery, NULL, 0, D3D11_ASYNC_GETDATA_DONOTFLUSH); + + // Keep polling, but allow other threads to do something useful first + Sleep(0); + + if (testDeviceLost(true)) + { + return; + } + } + while (result == S_FALSE); + } + else + { + mDeviceContext->Flush(); + } +} + +SwapChain *Renderer11::createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) +{ + return new rx::SwapChain11(this, window, shareHandle, backBufferFormat, depthBufferFormat); +} + +void Renderer11::generateSwizzle(gl::Texture *texture) +{ + if (texture) + { + TextureStorageInterface *texStorage = texture->getNativeTexture(); + if (texStorage) + { + TextureStorage11 *storage11 = TextureStorage11::makeTextureStorage11(texStorage->getStorageInstance()); + + storage11->generateSwizzles(texture->getSamplerState().swizzleRed, + texture->getSamplerState().swizzleGreen, + texture->getSamplerState().swizzleBlue, + texture->getSamplerState().swizzleAlpha); + } + } +} + +void Renderer11::setSamplerState(gl::SamplerType type, int index, const gl::SamplerState &samplerState) +{ + if (type == gl::SAMPLER_PIXEL) + { + if (index < 0 || index >= gl::MAX_TEXTURE_IMAGE_UNITS) + { + ERR("Pixel shader sampler index %i is not valid.", index); + return; + } + + if (mForceSetPixelSamplerStates[index] || memcmp(&samplerState, &mCurPixelSamplerStates[index], sizeof(gl::SamplerState)) != 0) + { + ID3D11SamplerState *dxSamplerState = mStateCache.getSamplerState(samplerState); + + if (!dxSamplerState) + { + ERR("NULL sampler state returned by RenderStateCache::getSamplerState, setting the default" + "sampler state for pixel shaders at slot %i.", index); + } + + mDeviceContext->PSSetSamplers(index, 1, &dxSamplerState); + + mCurPixelSamplerStates[index] = samplerState; + } + + mForceSetPixelSamplerStates[index] = false; + } + else if (type == gl::SAMPLER_VERTEX) + { + if (index < 0 || index >= (int)getMaxVertexTextureImageUnits()) + { + ERR("Vertex shader sampler index %i is not valid.", index); + return; + } + + if (mForceSetVertexSamplerStates[index] || memcmp(&samplerState, &mCurVertexSamplerStates[index], sizeof(gl::SamplerState)) != 0) + { + ID3D11SamplerState *dxSamplerState = mStateCache.getSamplerState(samplerState); + + if (!dxSamplerState) + { + ERR("NULL sampler state returned by RenderStateCache::getSamplerState, setting the default" + "sampler state for vertex shaders at slot %i.", index); + } + + mDeviceContext->VSSetSamplers(index, 1, &dxSamplerState); + + mCurVertexSamplerStates[index] = samplerState; + } + + mForceSetVertexSamplerStates[index] = false; + } + else UNREACHABLE(); +} + +void Renderer11::setTexture(gl::SamplerType type, int index, gl::Texture *texture) +{ + ID3D11ShaderResourceView *textureSRV = NULL; + bool forceSetTexture = false; + + if (texture) + { + TextureStorageInterface *texStorage = texture->getNativeTexture(); + if (texStorage) + { + TextureStorage11 *storage11 = TextureStorage11::makeTextureStorage11(texStorage->getStorageInstance()); + gl::SamplerState samplerState; + texture->getSamplerStateWithNativeOffset(&samplerState); + textureSRV = storage11->getSRV(samplerState); + } + + // 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); + + forceSetTexture = texture->hasDirtyImages(); + } + + if (type == gl::SAMPLER_PIXEL) + { + if (index < 0 || index >= gl::MAX_TEXTURE_IMAGE_UNITS) + { + ERR("Pixel shader sampler index %i is not valid.", index); + return; + } + + if (forceSetTexture || mCurPixelSRVs[index] != textureSRV) + { + mDeviceContext->PSSetShaderResources(index, 1, &textureSRV); + } + + mCurPixelSRVs[index] = textureSRV; + } + else if (type == gl::SAMPLER_VERTEX) + { + if (index < 0 || index >= (int)getMaxVertexTextureImageUnits()) + { + ERR("Vertex shader sampler index %i is not valid.", index); + return; + } + + if (forceSetTexture || mCurVertexSRVs[index] != textureSRV) + { + mDeviceContext->VSSetShaderResources(index, 1, &textureSRV); + } + + mCurVertexSRVs[index] = textureSRV; + } + else UNREACHABLE(); +} + +bool Renderer11::setUniformBuffers(const gl::Buffer *vertexUniformBuffers[], const gl::Buffer *fragmentUniformBuffers[]) +{ + for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; uniformBufferIndex++) + { + const gl::Buffer *uniformBuffer = vertexUniformBuffers[uniformBufferIndex]; + if (uniformBuffer) + { + Buffer11 *bufferStorage = Buffer11::makeBuffer11(uniformBuffer->getImplementation()); + ID3D11Buffer *constantBuffer = bufferStorage->getBuffer(BUFFER_USAGE_UNIFORM); + + if (!constantBuffer) + { + return false; + } + + if (mCurrentConstantBufferVS[uniformBufferIndex] != bufferStorage->getSerial()) + { + mDeviceContext->VSSetConstantBuffers(getReservedVertexUniformBuffers() + uniformBufferIndex, + 1, &constantBuffer); + mCurrentConstantBufferVS[uniformBufferIndex] = bufferStorage->getSerial(); + } + } + } + + for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS; uniformBufferIndex++) + { + const gl::Buffer *uniformBuffer = fragmentUniformBuffers[uniformBufferIndex]; + if (uniformBuffer) + { + Buffer11 *bufferStorage = Buffer11::makeBuffer11(uniformBuffer->getImplementation()); + ID3D11Buffer *constantBuffer = bufferStorage->getBuffer(BUFFER_USAGE_UNIFORM); + + if (!constantBuffer) + { + return false; + } + + if (mCurrentConstantBufferPS[uniformBufferIndex] != bufferStorage->getSerial()) + { + mDeviceContext->PSSetConstantBuffers(getReservedFragmentUniformBuffers() + uniformBufferIndex, + 1, &constantBuffer); + mCurrentConstantBufferPS[uniformBufferIndex] = bufferStorage->getSerial(); + } + } + } + + return true; +} + +void Renderer11::setRasterizerState(const gl::RasterizerState &rasterState) +{ + if (mForceSetRasterState || memcmp(&rasterState, &mCurRasterState, sizeof(gl::RasterizerState)) != 0) + { + ID3D11RasterizerState *dxRasterState = mStateCache.getRasterizerState(rasterState, mScissorEnabled); + if (!dxRasterState) + { + ERR("NULL rasterizer state returned by RenderStateCache::getRasterizerState, setting the default" + "rasterizer state."); + } + + mDeviceContext->RSSetState(dxRasterState); + + mCurRasterState = rasterState; + } + + mForceSetRasterState = false; +} + +void Renderer11::setBlendState(gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, + unsigned int sampleMask) +{ + if (mForceSetBlendState || + memcmp(&blendState, &mCurBlendState, sizeof(gl::BlendState)) != 0 || + memcmp(&blendColor, &mCurBlendColor, sizeof(gl::ColorF)) != 0 || + sampleMask != mCurSampleMask) + { + ID3D11BlendState *dxBlendState = mStateCache.getBlendState(framebuffer, blendState); + if (!dxBlendState) + { + ERR("NULL blend state returned by RenderStateCache::getBlendState, setting the default " + "blend state."); + } + + float blendColors[4] = {0.0f}; + if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_CONSTANT_ALPHA && blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) + { + blendColors[0] = blendColor.red; + blendColors[1] = blendColor.green; + blendColors[2] = blendColor.blue; + blendColors[3] = blendColor.alpha; + } + else + { + blendColors[0] = blendColor.alpha; + blendColors[1] = blendColor.alpha; + blendColors[2] = blendColor.alpha; + blendColors[3] = blendColor.alpha; + } + + mDeviceContext->OMSetBlendState(dxBlendState, blendColors, sampleMask); + + mCurBlendState = blendState; + mCurBlendColor = blendColor; + mCurSampleMask = sampleMask; + } + + mForceSetBlendState = false; +} + +void Renderer11::setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, + int stencilBackRef, bool frontFaceCCW) +{ + if (mForceSetDepthStencilState || + memcmp(&depthStencilState, &mCurDepthStencilState, sizeof(gl::DepthStencilState)) != 0 || + stencilRef != mCurStencilRef || stencilBackRef != mCurStencilBackRef) + { + ASSERT(depthStencilState.stencilWritemask == depthStencilState.stencilBackWritemask); + ASSERT(stencilRef == stencilBackRef); + ASSERT(depthStencilState.stencilMask == depthStencilState.stencilBackMask); + + ID3D11DepthStencilState *dxDepthStencilState = mStateCache.getDepthStencilState(depthStencilState); + if (!dxDepthStencilState) + { + ERR("NULL depth stencil state returned by RenderStateCache::getDepthStencilState, " + "setting the default depth stencil state."); + } + + // Max D3D11 stencil reference value is 0xFF, corresponding to the max 8 bits in a stencil buffer + // GL specifies we should clamp the ref value to the nearest bit depth when doing stencil ops + META_ASSERT(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF); + META_ASSERT(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF); + UINT dxStencilRef = std::min<UINT>(stencilRef, 0xFFu); + + mDeviceContext->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef); + + mCurDepthStencilState = depthStencilState; + mCurStencilRef = stencilRef; + mCurStencilBackRef = stencilBackRef; + } + + mForceSetDepthStencilState = false; +} + +void Renderer11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled) +{ + if (mForceSetScissor || memcmp(&scissor, &mCurScissor, sizeof(gl::Rectangle)) != 0 || + enabled != mScissorEnabled) + { + if (enabled) + { + D3D11_RECT rect; + rect.left = std::max(0, scissor.x); + rect.top = std::max(0, scissor.y); + rect.right = scissor.x + std::max(0, scissor.width); + rect.bottom = scissor.y + std::max(0, scissor.height); + + mDeviceContext->RSSetScissorRects(1, &rect); + } + + if (enabled != mScissorEnabled) + { + mForceSetRasterState = true; + } + + mCurScissor = scissor; + mScissorEnabled = enabled; + } + + mForceSetScissor = false; +} + +bool Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, + bool ignoreViewport) +{ + gl::Rectangle actualViewport = viewport; + float actualZNear = gl::clamp01(zNear); + float actualZFar = gl::clamp01(zFar); + if (ignoreViewport) + { + actualViewport.x = 0; + actualViewport.y = 0; + actualViewport.width = mRenderTargetDesc.width; + actualViewport.height = mRenderTargetDesc.height; + actualZNear = 0.0f; + actualZFar = 1.0f; + } + + const gl::Caps& caps = getRendererCaps(); + + // Clamp width and height first to the gl maximum, then clamp further if we extend past the D3D maximum bounds + D3D11_VIEWPORT dxViewport; + dxViewport.TopLeftX = gl::clamp(actualViewport.x, -static_cast<int>(caps.maxViewportWidth), static_cast<int>(caps.maxViewportWidth)); + dxViewport.TopLeftY = gl::clamp(actualViewport.y, -static_cast<int>(caps.maxViewportHeight), static_cast<int>(caps.maxViewportHeight)); + dxViewport.Width = gl::clamp(actualViewport.width, 0, static_cast<int>(caps.maxViewportWidth - dxViewport.TopLeftX)); + dxViewport.Height = gl::clamp(actualViewport.height, 0, static_cast<int>(caps.maxViewportHeight - dxViewport.TopLeftY)); + dxViewport.MinDepth = actualZNear; + dxViewport.MaxDepth = actualZFar; + + if (dxViewport.Width <= 0 || dxViewport.Height <= 0) + { + return false; // Nothing to render + } + + bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 || + actualZNear != mCurNear || actualZFar != mCurFar; + + if (viewportChanged) + { + mDeviceContext->RSSetViewports(1, &dxViewport); + + mCurViewport = actualViewport; + mCurNear = actualZNear; + mCurFar = actualZFar; + + mPixelConstants.viewCoords[0] = actualViewport.width * 0.5f; + mPixelConstants.viewCoords[1] = actualViewport.height * 0.5f; + mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); + mPixelConstants.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f); + + mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f; + mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f; + + mVertexConstants.depthRange[0] = actualZNear; + mVertexConstants.depthRange[1] = actualZFar; + mVertexConstants.depthRange[2] = actualZFar - actualZNear; + + mPixelConstants.depthRange[0] = actualZNear; + mPixelConstants.depthRange[1] = actualZFar; + mPixelConstants.depthRange[2] = actualZFar - actualZNear; + } + + mForceSetViewport = false; + return true; +} + +bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count) +{ + 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 (primitiveTopology != mCurrentPrimitiveTopology) + { + mDeviceContext->IASetPrimitiveTopology(primitiveTopology); + mCurrentPrimitiveTopology = primitiveTopology; + } + + return count >= minCount; +} + +bool Renderer11::applyRenderTarget(gl::Framebuffer *framebuffer) +{ + // Get the color render buffer and serial + // Also extract the render target dimensions and view + unsigned int renderTargetWidth = 0; + unsigned int renderTargetHeight = 0; + GLenum renderTargetFormat = 0; + unsigned int renderTargetSerials[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS] = {0}; + ID3D11RenderTargetView* framebufferRTVs[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS] = {NULL}; + bool missingColorRenderTarget = true; + + for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++) + { + const GLenum drawBufferState = framebuffer->getDrawBufferState(colorAttachment); + gl::FramebufferAttachment *colorbuffer = framebuffer->getColorbuffer(colorAttachment); + + if (colorbuffer && drawBufferState != GL_NONE) + { + // the draw buffer must be either "none", "back" for the default buffer or the same index as this color (in order) + ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + colorAttachment)); + + // check for zero-sized default framebuffer, which is a special case. + // in this case we do not wish to modify any state and just silently return false. + // this will not report any gl error but will cause the calling method to return. + if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0) + { + return false; + } + + renderTargetSerials[colorAttachment] = colorbuffer->getSerial(); + + // Extract the render target dimensions and view + RenderTarget11 *renderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (!renderTarget) + { + ERR("render target pointer unexpectedly null."); + return false; + } + + framebufferRTVs[colorAttachment] = renderTarget->getRenderTargetView(); + if (!framebufferRTVs[colorAttachment]) + { + ERR("render target view pointer unexpectedly null."); + return false; + } + + if (missingColorRenderTarget) + { + renderTargetWidth = colorbuffer->getWidth(); + renderTargetHeight = colorbuffer->getHeight(); + renderTargetFormat = colorbuffer->getActualFormat(); + missingColorRenderTarget = false; + } + + // TODO: Detect if this color buffer is already bound as a texture and unbind it first to prevent + // D3D11 warnings. + } + } + + // Get the depth stencil render buffer and serials + gl::FramebufferAttachment *depthStencil = framebuffer->getDepthbuffer(); + unsigned int depthbufferSerial = 0; + unsigned int stencilbufferSerial = 0; + if (depthStencil) + { + depthbufferSerial = depthStencil->getSerial(); + } + else if (framebuffer->getStencilbuffer()) + { + depthStencil = framebuffer->getStencilbuffer(); + stencilbufferSerial = depthStencil->getSerial(); + } + + ID3D11DepthStencilView* framebufferDSV = NULL; + if (depthStencil) + { + RenderTarget11 *depthStencilRenderTarget = RenderTarget11::makeRenderTarget11(depthStencil->getDepthStencil()); + if (!depthStencilRenderTarget) + { + ERR("render target pointer unexpectedly null."); + SafeRelease(framebufferRTVs); + return false; + } + + framebufferDSV = depthStencilRenderTarget->getDepthStencilView(); + if (!framebufferDSV) + { + ERR("depth stencil view pointer unexpectedly null."); + SafeRelease(framebufferRTVs); + return false; + } + + // If there is no render buffer, the width, height and format values come from + // the depth stencil + if (missingColorRenderTarget) + { + renderTargetWidth = depthStencil->getWidth(); + renderTargetHeight = depthStencil->getHeight(); + renderTargetFormat = depthStencil->getActualFormat(); + } + } + + // Apply the render target and depth stencil + if (!mRenderTargetDescInitialized || !mDepthStencilInitialized || + memcmp(renderTargetSerials, mAppliedRenderTargetSerials, sizeof(renderTargetSerials)) != 0 || + depthbufferSerial != mAppliedDepthbufferSerial || + stencilbufferSerial != mAppliedStencilbufferSerial) + { + mDeviceContext->OMSetRenderTargets(getRendererCaps().maxDrawBuffers, framebufferRTVs, framebufferDSV); + + mRenderTargetDesc.width = renderTargetWidth; + mRenderTargetDesc.height = renderTargetHeight; + mRenderTargetDesc.format = renderTargetFormat; + mForceSetViewport = true; + mForceSetScissor = true; + mForceSetBlendState = true; + + if (!mDepthStencilInitialized) + { + mForceSetRasterState = true; + } + + for (unsigned int rtIndex = 0; rtIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; rtIndex++) + { + mAppliedRenderTargetSerials[rtIndex] = renderTargetSerials[rtIndex]; + } + mAppliedDepthbufferSerial = depthbufferSerial; + mAppliedStencilbufferSerial = stencilbufferSerial; + mRenderTargetDescInitialized = true; + mDepthStencilInitialized = true; + } + + invalidateFramebufferSwizzles(framebuffer); + + return true; +} + +GLenum Renderer11::applyVertexBuffer(gl::ProgramBinary *programBinary, const gl::VertexAttribute vertexAttributes[], const gl::VertexAttribCurrentValueData currentValues[], + GLint first, GLsizei count, GLsizei instances) +{ + TranslatedAttribute attributes[gl::MAX_VERTEX_ATTRIBS]; + GLenum err = mVertexDataManager->prepareVertexData(vertexAttributes, currentValues, programBinary, first, count, attributes, instances); + if (err != GL_NO_ERROR) + { + return err; + } + + return mInputLayoutCache.applyVertexBuffers(attributes, programBinary); +} + +GLenum Renderer11::applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo) +{ + GLenum err = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, indexInfo); + + if (err == GL_NO_ERROR) + { + IndexBuffer11* indexBuffer = IndexBuffer11::makeIndexBuffer11(indexInfo->indexBuffer); + + ID3D11Buffer *buffer = NULL; + DXGI_FORMAT bufferFormat = indexBuffer->getIndexFormat(); + + if (indexInfo->storage) + { + Buffer11 *storage = Buffer11::makeBuffer11(indexInfo->storage); + buffer = storage->getBuffer(BUFFER_USAGE_INDEX); + } + else + { + buffer = indexBuffer->getBuffer(); + } + + if (buffer != mAppliedIB || bufferFormat != mAppliedIBFormat || indexInfo->startOffset != mAppliedIBOffset) + { + mDeviceContext->IASetIndexBuffer(buffer, bufferFormat, indexInfo->startOffset); + + mAppliedIB = buffer; + mAppliedIBFormat = bufferFormat; + mAppliedIBOffset = indexInfo->startOffset; + } + } + + return err; +} + +void Renderer11::applyTransformFeedbackBuffers(gl::Buffer *transformFeedbackBuffers[], GLintptr offsets[]) +{ + ID3D11Buffer* d3dBuffers[gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS]; + UINT d3dOffsets[gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS]; + bool requiresUpdate = false; + for (size_t i = 0; i < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++) + { + if (transformFeedbackBuffers[i]) + { + Buffer11 *storage = Buffer11::makeBuffer11(transformFeedbackBuffers[i]->getImplementation()); + ID3D11Buffer *buffer = storage->getBuffer(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK); + + d3dBuffers[i] = buffer; + d3dOffsets[i] = (mAppliedTFBuffers[i] != buffer) ? static_cast<UINT>(offsets[i]) : -1; + } + else + { + d3dBuffers[i] = NULL; + d3dOffsets[i] = 0; + } + + if (d3dBuffers[i] != mAppliedTFBuffers[i] || offsets[i] != mAppliedTFOffsets[i]) + { + requiresUpdate = true; + } + } + + if (requiresUpdate) + { + mDeviceContext->SOSetTargets(ArraySize(d3dBuffers), d3dBuffers, d3dOffsets); + for (size_t i = 0; i < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++) + { + mAppliedTFBuffers[i] = d3dBuffers[i]; + mAppliedTFOffsets[i] = offsets[i]; + } + } +} + +void Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive) +{ + if (mode == GL_POINTS && transformFeedbackActive) + { + // Since point sprites are generated with a geometry shader, too many vertices will + // be written if transform feedback is active. To work around this, draw only the points + // with the stream out shader and no pixel shader to feed the stream out buffers and then + // draw again with the point sprite geometry shader to rasterize the point sprites. + + mDeviceContext->PSSetShader(NULL, NULL, 0); + + if (instances > 0) + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); + } + else + { + mDeviceContext->Draw(count, 0); + } + + mDeviceContext->GSSetShader(mCurPointGeometryShader, NULL, 0); + mDeviceContext->PSSetShader(mAppliedPixelShader, NULL, 0); + + if (instances > 0) + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); + } + else + { + mDeviceContext->Draw(count, 0); + } + + mDeviceContext->GSSetShader(mAppliedGeometryShader, NULL, 0); + } + else if (mode == GL_LINE_LOOP) + { + drawLineLoop(count, GL_NONE, NULL, 0, NULL); + } + else if (mode == GL_TRIANGLE_FAN) + { + drawTriangleFan(count, GL_NONE, NULL, 0, NULL, instances); + } + else if (instances > 0) + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); + } + else + { + mDeviceContext->Draw(count, 0); + } +} + +void Renderer11::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, + gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances) +{ + if (mode == GL_LINE_LOOP) + { + drawLineLoop(count, type, indices, indexInfo.minIndex, elementArrayBuffer); + } + else if (mode == GL_TRIANGLE_FAN) + { + drawTriangleFan(count, type, indices, indexInfo.minIndex, elementArrayBuffer, instances); + } + else if (instances > 0) + { + mDeviceContext->DrawIndexedInstanced(count, instances, 0, -static_cast<int>(indexInfo.minIndex), 0); + } + else + { + mDeviceContext->DrawIndexed(count, 0, -static_cast<int>(indexInfo.minIndex)); + } +} + +template<typename T> +static void fillLineLoopIndices(GLenum type, GLsizei count, const GLvoid *indices, T *data) +{ + switch (type) + { + case GL_NONE: // Non-indexed draw + for (int i = 0; i < count; i++) + { + data[i] = i; + } + data[count] = 0; + break; + case GL_UNSIGNED_BYTE: + for (int i = 0; i < count; i++) + { + data[i] = static_cast<const GLubyte*>(indices)[i]; + } + data[count] = static_cast<const GLubyte*>(indices)[0]; + break; + case GL_UNSIGNED_SHORT: + for (int i = 0; i < count; i++) + { + data[i] = static_cast<const GLushort*>(indices)[i]; + } + data[count] = static_cast<const GLushort*>(indices)[0]; + break; + case GL_UNSIGNED_INT: + for (int i = 0; i < count; i++) + { + data[i] = static_cast<const GLuint*>(indices)[i]; + } + data[count] = static_cast<const GLuint*>(indices)[0]; + break; + default: UNREACHABLE(); + } +} + +template<typename T> +static void fillTriangleFanIndices(GLenum type, unsigned int numTris, const GLvoid *indices, T *data) +{ + switch (type) + { + case GL_NONE: // Non-indexed draw + for (unsigned int i = 0; i < numTris; i++) + { + data[i*3 + 0] = 0; + data[i*3 + 1] = i + 1; + data[i*3 + 2] = i + 2; + } + break; + case GL_UNSIGNED_BYTE: + for (unsigned int i = 0; i < numTris; i++) + { + data[i*3 + 0] = static_cast<const GLubyte*>(indices)[0]; + data[i*3 + 1] = static_cast<const GLubyte*>(indices)[i + 1]; + data[i*3 + 2] = static_cast<const GLubyte*>(indices)[i + 2]; + } + break; + case GL_UNSIGNED_SHORT: + for (unsigned int i = 0; i < numTris; i++) + { + data[i*3 + 0] = static_cast<const GLushort*>(indices)[0]; + data[i*3 + 1] = static_cast<const GLushort*>(indices)[i + 1]; + data[i*3 + 2] = static_cast<const GLushort*>(indices)[i + 2]; + } + break; + case GL_UNSIGNED_INT: + for (unsigned int i = 0; i < numTris; i++) + { + data[i*3 + 0] = static_cast<const GLuint*>(indices)[0]; + data[i*3 + 1] = static_cast<const GLuint*>(indices)[i + 1]; + data[i*3 + 2] = static_cast<const GLuint*>(indices)[i + 2]; + } + break; + default: UNREACHABLE(); + } +} + +void Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer) +{ + // Get the raw indices for an indexed draw + if (type != GL_NONE && elementArrayBuffer) + { + gl::Buffer *indexBuffer = elementArrayBuffer; + BufferImpl *storage = indexBuffer->getImplementation(); + intptr_t offset = reinterpret_cast<intptr_t>(indices); + indices = static_cast<const GLubyte*>(storage->getData()) + offset; + } + + // TODO: some level 9 hardware supports 32-bit indices; test and store support instead + const int indexType = isLevel9() ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + + if (!mLineLoopIB) + { + mLineLoopIB = new StreamingIndexBufferInterface(this); + if (!mLineLoopIB->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, indexType)) + { + delete mLineLoopIB; + mLineLoopIB = NULL; + + ERR("Could not create a 32-bit looping index buffer for GL_LINE_LOOP."); + return gl::error(GL_OUT_OF_MEMORY); + } + } + + // Checked by Renderer11::applyPrimitiveType + ASSERT(count >= 0); + + int indexTypeSize = indexType == GL_UNSIGNED_SHORT ? sizeof(unsigned short) : sizeof(unsigned int); + if (static_cast<unsigned int>(count) + 1 > (std::numeric_limits<unsigned int>::max() / indexTypeSize)) + { + ERR("Could not create a 32-bit looping index buffer for GL_LINE_LOOP, too many indices required."); + return gl::error(GL_OUT_OF_MEMORY); + } + + const unsigned int spaceNeeded = (static_cast<unsigned int>(count) + 1) * indexTypeSize; + if (!mLineLoopIB->reserveBufferSpace(spaceNeeded, indexType)) + { + ERR("Could not reserve enough space in looping index buffer for GL_LINE_LOOP."); + return gl::error(GL_OUT_OF_MEMORY); + } + + void* mappedMemory = NULL; + unsigned int offset; + if (!mLineLoopIB->mapBuffer(spaceNeeded, &mappedMemory, &offset)) + { + ERR("Could not map index buffer for GL_LINE_LOOP."); + return gl::error(GL_OUT_OF_MEMORY); + } + + if (indexType == GL_UNSIGNED_SHORT) + fillLineLoopIndices(type, count, indices, reinterpret_cast<unsigned short*>(mappedMemory)); + else + fillLineLoopIndices(type, count, indices, reinterpret_cast<unsigned int*>(mappedMemory)); + unsigned int indexBufferOffset = offset; + + if (!mLineLoopIB->unmapBuffer()) + { + ERR("Could not unmap index buffer for GL_LINE_LOOP."); + return gl::error(GL_OUT_OF_MEMORY); + } + + IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mLineLoopIB->getIndexBuffer()); + ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); + DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); + + if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset) + { + mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset); + mAppliedIB = d3dIndexBuffer; + mAppliedIBFormat = indexFormat; + mAppliedIBOffset = indexBufferOffset; + } + + mDeviceContext->DrawIndexed(count + 1, 0, -minIndex); +} + +void Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer, int instances) +{ + // Get the raw indices for an indexed draw + if (type != GL_NONE && elementArrayBuffer) + { + gl::Buffer *indexBuffer = elementArrayBuffer; + BufferImpl *storage = indexBuffer->getImplementation(); + intptr_t offset = reinterpret_cast<intptr_t>(indices); + indices = static_cast<const GLubyte*>(storage->getData()) + offset; + } + + const int indexType = isLevel9() ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + + if (!mTriangleFanIB) + { + mTriangleFanIB = new StreamingIndexBufferInterface(this); + if (!mTriangleFanIB->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, indexType)) + { + delete mTriangleFanIB; + mTriangleFanIB = NULL; + + ERR("Could not create a scratch index buffer for GL_TRIANGLE_FAN."); + return gl::error(GL_OUT_OF_MEMORY); + } + } + + // Checked by Renderer11::applyPrimitiveType + ASSERT(count >= 3); + + const unsigned int numTris = count - 2; + + int indexTypeSize = indexType == GL_UNSIGNED_SHORT ? sizeof(unsigned short) : sizeof(unsigned int); + if (numTris > (std::numeric_limits<unsigned int>::max() / (indexTypeSize * 3))) + { + ERR("Could not create a scratch index buffer for GL_TRIANGLE_FAN, too many indices required."); + return gl::error(GL_OUT_OF_MEMORY); + } + + const unsigned int spaceNeeded = (numTris * 3) * indexTypeSize; + if (!mTriangleFanIB->reserveBufferSpace(spaceNeeded, indexType)) + { + ERR("Could not reserve enough space in scratch index buffer for GL_TRIANGLE_FAN."); + return gl::error(GL_OUT_OF_MEMORY); + } + + void* mappedMemory = NULL; + unsigned int offset; + if (!mTriangleFanIB->mapBuffer(spaceNeeded, &mappedMemory, &offset)) + { + ERR("Could not map scratch index buffer for GL_TRIANGLE_FAN."); + return gl::error(GL_OUT_OF_MEMORY); + } + + if (indexType == GL_UNSIGNED_SHORT) + fillTriangleFanIndices(type, numTris, indices, reinterpret_cast<unsigned short*>(mappedMemory)); + else + fillTriangleFanIndices(type, numTris, indices, reinterpret_cast<unsigned int*>(mappedMemory)); + unsigned int indexBufferOffset = offset; + + if (!mTriangleFanIB->unmapBuffer()) + { + ERR("Could not unmap scratch index buffer for GL_TRIANGLE_FAN."); + return gl::error(GL_OUT_OF_MEMORY); + } + + IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mTriangleFanIB->getIndexBuffer()); + ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); + DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); + + if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset) + { + mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset); + mAppliedIB = d3dIndexBuffer; + mAppliedIBFormat = indexFormat; + mAppliedIBOffset = indexBufferOffset; + } + + if (instances > 0) + { + mDeviceContext->DrawIndexedInstanced(numTris * 3, instances, 0, -minIndex, 0); + } + else + { + mDeviceContext->DrawIndexed(numTris * 3, 0, -minIndex); + } +} + +void Renderer11::applyShaders(gl::ProgramBinary *programBinary, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer, + bool rasterizerDiscard, bool transformFeedbackActive) +{ + ShaderExecutable *vertexExe = programBinary->getVertexExecutableForInputLayout(inputLayout); + ShaderExecutable *pixelExe = programBinary->getPixelExecutableForFramebuffer(framebuffer); + ShaderExecutable *geometryExe = programBinary->getGeometryExecutable(); + + ID3D11VertexShader *vertexShader = (vertexExe ? ShaderExecutable11::makeShaderExecutable11(vertexExe)->getVertexShader() : NULL); + + ID3D11PixelShader *pixelShader = NULL; + // Skip pixel shader if we're doing rasterizer discard. + if (!rasterizerDiscard) + { + pixelShader = (pixelExe ? ShaderExecutable11::makeShaderExecutable11(pixelExe)->getPixelShader() : NULL); + } + + ID3D11GeometryShader *geometryShader = NULL; + if (transformFeedbackActive) + { + geometryShader = (vertexExe ? ShaderExecutable11::makeShaderExecutable11(vertexExe)->getStreamOutShader() : NULL); + } + else if (mCurRasterState.pointDrawMode) + { + geometryShader = (geometryExe ? ShaderExecutable11::makeShaderExecutable11(geometryExe)->getGeometryShader() : NULL); + } + + bool dirtyUniforms = false; + + if (vertexShader != mAppliedVertexShader) + { + mDeviceContext->VSSetShader(vertexShader, NULL, 0); + mAppliedVertexShader = vertexShader; + dirtyUniforms = true; + } + + if (geometryShader != mAppliedGeometryShader) + { + mDeviceContext->GSSetShader(geometryShader, NULL, 0); + mAppliedGeometryShader = geometryShader; + dirtyUniforms = true; + } + + if (geometryExe && mCurRasterState.pointDrawMode) + { + mCurPointGeometryShader = ShaderExecutable11::makeShaderExecutable11(geometryExe)->getGeometryShader(); + } + else + { + mCurPointGeometryShader = NULL; + } + + if (pixelShader != mAppliedPixelShader) + { + mDeviceContext->PSSetShader(pixelShader, NULL, 0); + mAppliedPixelShader = pixelShader; + dirtyUniforms = true; + } + + if (dirtyUniforms) + { + programBinary->dirtyAllUniforms(); + } +} + +void Renderer11::applyUniforms(const gl::ProgramBinary &programBinary) +{ + const std::vector<gl::LinkedUniform*> &uniformArray = programBinary.getUniforms(); + + unsigned int totalRegisterCountVS = 0; + unsigned int totalRegisterCountPS = 0; + + bool vertexUniformsDirty = false; + bool pixelUniformsDirty = false; + + for (size_t uniformIndex = 0; uniformIndex < uniformArray.size(); uniformIndex++) + { + const gl::LinkedUniform &uniform = *uniformArray[uniformIndex]; + + 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 = UniformStorage11::makeUniformStorage11(&programBinary.getVertexUniformStorage()); + const UniformStorage11 *fragmentUniformStorage = UniformStorage11::makeUniformStorage11(&programBinary.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 (size_t uniformIndex = 0; uniformIndex < uniformArray.size(); uniformIndex++) + { + gl::LinkedUniform *uniform = uniformArray[uniformIndex]; + + if (!uniform->isSampler()) + { + 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(0, 1, &vertexConstantBuffer); + mCurrentVertexConstantBuffer = vertexConstantBuffer; + } + + if (mCurrentPixelConstantBuffer != pixelConstantBuffer) + { + mDeviceContext->PSSetConstantBuffers(0, 1, &pixelConstantBuffer); + mCurrentPixelConstantBuffer = pixelConstantBuffer; + } + + // Driver uniforms + if (!mDriverConstantBufferVS) + { + D3D11_BUFFER_DESC constantBufferDescription = {0}; + constantBufferDescription.ByteWidth = sizeof(dx_VertexConstants); + constantBufferDescription.Usage = D3D11_USAGE_DEFAULT; + constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + constantBufferDescription.CPUAccessFlags = 0; + constantBufferDescription.MiscFlags = 0; + constantBufferDescription.StructureByteStride = 0; + + HRESULT result = mDevice->CreateBuffer(&constantBufferDescription, NULL, &mDriverConstantBufferVS); + UNUSED_ASSERTION_VARIABLE(result); + ASSERT(SUCCEEDED(result)); + + mDeviceContext->VSSetConstantBuffers(1, 1, &mDriverConstantBufferVS); + } + + if (!mDriverConstantBufferPS) + { + D3D11_BUFFER_DESC constantBufferDescription = {0}; + constantBufferDescription.ByteWidth = sizeof(dx_PixelConstants); + constantBufferDescription.Usage = D3D11_USAGE_DEFAULT; + constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + constantBufferDescription.CPUAccessFlags = 0; + constantBufferDescription.MiscFlags = 0; + constantBufferDescription.StructureByteStride = 0; + + HRESULT result = mDevice->CreateBuffer(&constantBufferDescription, NULL, &mDriverConstantBufferPS); + UNUSED_ASSERTION_VARIABLE(result); + ASSERT(SUCCEEDED(result)); + + mDeviceContext->PSSetConstantBuffers(1, 1, &mDriverConstantBufferPS); + } + + if (memcmp(&mVertexConstants, &mAppliedVertexConstants, sizeof(dx_VertexConstants)) != 0) + { + mDeviceContext->UpdateSubresource(mDriverConstantBufferVS, 0, NULL, &mVertexConstants, 16, 0); + memcpy(&mAppliedVertexConstants, &mVertexConstants, sizeof(dx_VertexConstants)); + } + + if (memcmp(&mPixelConstants, &mAppliedPixelConstants, sizeof(dx_PixelConstants)) != 0) + { + mDeviceContext->UpdateSubresource(mDriverConstantBufferPS, 0, NULL, &mPixelConstants, 16, 0); + memcpy(&mAppliedPixelConstants, &mPixelConstants, sizeof(dx_PixelConstants)); + } + + // needed for the point sprite geometry shader + if (mFeatureLevel >= D3D_FEATURE_LEVEL_10_0 && mCurrentGeometryConstantBuffer != mDriverConstantBufferPS) + { + mDeviceContext->GSSetConstantBuffers(0, 1, &mDriverConstantBufferPS); + mCurrentGeometryConstantBuffer = mDriverConstantBufferPS; + } +} + +void Renderer11::clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer) +{ + mClear->clearFramebuffer(clearParams, frameBuffer); + invalidateFramebufferSwizzles(frameBuffer); +} + +void Renderer11::markAllStateDirty() +{ + for (unsigned int rtIndex = 0; rtIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; rtIndex++) + { + mAppliedRenderTargetSerials[rtIndex] = 0; + } + mAppliedDepthbufferSerial = 0; + mAppliedStencilbufferSerial = 0; + mDepthStencilInitialized = false; + mRenderTargetDescInitialized = false; + + for (int i = 0; i < gl::IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS; i++) + { + mForceSetVertexSamplerStates[i] = true; + mCurVertexSRVs[i] = NULL; + } + for (int i = 0; i < gl::MAX_TEXTURE_IMAGE_UNITS; i++) + { + mForceSetPixelSamplerStates[i] = true; + mCurPixelSRVs[i] = NULL; + } + + mForceSetBlendState = true; + mForceSetRasterState = true; + mForceSetDepthStencilState = true; + mForceSetScissor = true; + mForceSetViewport = true; + + mAppliedIB = NULL; + mAppliedIBFormat = DXGI_FORMAT_UNKNOWN; + mAppliedIBOffset = 0; + + mAppliedVertexShader = NULL; + mAppliedGeometryShader = NULL; + mCurPointGeometryShader = NULL; + mAppliedPixelShader = NULL; + + for (size_t i = 0; i < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++) + { + mAppliedTFBuffers[i] = NULL; + mAppliedTFOffsets[i] = 0; + } + + memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants)); + memset(&mAppliedPixelConstants, 0, sizeof(dx_PixelConstants)); + + mInputLayoutCache.markDirty(); + + for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; i++) + { + mCurrentConstantBufferVS[i] = -1; + mCurrentConstantBufferPS[i] = -1; + } + + mCurrentVertexConstantBuffer = NULL; + mCurrentPixelConstantBuffer = NULL; + mCurrentGeometryConstantBuffer = NULL; + + mCurrentPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + +void Renderer11::releaseDeviceResources() +{ + mStateCache.clear(); + mInputLayoutCache.clear(); + + SafeDelete(mVertexDataManager); + SafeDelete(mIndexDataManager); + SafeDelete(mLineLoopIB); + SafeDelete(mTriangleFanIB); + SafeDelete(mBlit); + SafeDelete(mClear); + SafeDelete(mPixelTransfer); + + SafeRelease(mDriverConstantBufferVS); + SafeRelease(mDriverConstantBufferPS); + SafeRelease(mSyncQuery); +} + +void Renderer11::notifyDeviceLost() +{ + mDeviceLost = true; + mDisplay->notifyDeviceLost(); +} + +bool Renderer11::isDeviceLost() +{ + return mDeviceLost; +} + +// set notify to true to broadcast a message to all contexts of the device loss +bool Renderer11::testDeviceLost(bool notify) +{ + 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; + if (notify) + { + notifyDeviceLost(); + } + } + + 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; + } + + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, +#if !defined(ANGLE_ENABLE_D3D9) + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, +#endif + }; + + ID3D11Device* dummyDevice; + D3D_FEATURE_LEVEL dummyFeatureLevel; + ID3D11DeviceContext* dummyContext; + + HRESULT result = D3D11CreateDevice(NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + #if defined(_DEBUG) + D3D11_CREATE_DEVICE_DEBUG, + #else + 0, + #endif + featureLevels, + ArraySize(featureLevels), + D3D11_SDK_VERSION, + &dummyDevice, + &dummyFeatureLevel, + &dummyContext); + + if (!mDevice || FAILED(result)) + { + return false; + } + + SafeRelease(dummyContext); + SafeRelease(dummyDevice); + + return true; +} + +void Renderer11::release() +{ + releaseDeviceResources(); + + SafeRelease(mDxgiFactory); + SafeRelease(mDxgiAdapter); + + if (mDeviceContext) + { + mDeviceContext->ClearState(); + mDeviceContext->Flush(); + SafeRelease(mDeviceContext); + } + + SafeRelease(mDevice); + + if (mD3d11Module) + { + FreeLibrary(mD3d11Module); + mD3d11Module = NULL; + } + + if (mDxgiModule) + { + FreeLibrary(mDxgiModule); + mDxgiModule = NULL; + } + + mCompiler.release(); +} + +bool Renderer11::resetDevice() +{ + // recreate everything + release(); + EGLint result = initialize(); + + if (result != EGL_SUCCESS) + { + ERR("Could not reinitialize D3D11 device: %08X", result); + return false; + } + + mDeviceLost = false; + + return true; +} + +DWORD Renderer11::getAdapterVendor() const +{ + return mAdapterDescription.VendorId; +} + +std::string Renderer11::getRendererDescription() const +{ + std::ostringstream rendererString; + + rendererString << mDescription; + rendererString << " Direct3D11"; + + rendererString << " vs_" << getMajorShaderModel() << "_" << getMinorShaderModel(); + rendererString << " ps_" << getMajorShaderModel() << "_" << getMinorShaderModel(); + + return rendererString.str(); +} + +GUID Renderer11::getAdapterIdentifier() const +{ + // Use the adapter LUID as our adapter ID + // This number is local to a machine is only guaranteed to be unique between restarts + META_ASSERT(sizeof(LUID) <= sizeof(GUID)); + GUID adapterId = {0}; + memcpy(&adapterId, &mAdapterDescription.AdapterLuid, sizeof(LUID)); + return adapterId; +} + +unsigned int Renderer11::getMaxVertexTextureImageUnits() const +{ + META_ASSERT(MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 <= gl::IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS); + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + return MAX_TEXTURE_IMAGE_UNITS_VTF_SM4; + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +unsigned int Renderer11::getMaxCombinedTextureImageUnits() const +{ + return gl::MAX_TEXTURE_IMAGE_UNITS + getMaxVertexTextureImageUnits(); +} + +unsigned int Renderer11::getReservedVertexUniformVectors() const +{ + return 0; // Driver uniforms are stored in a separate constant buffer +} + +unsigned int Renderer11::getReservedFragmentUniformVectors() const +{ + return 0; // Driver uniforms are stored in a separate constant buffer +} + +unsigned int Renderer11::getMaxVertexUniformVectors() const +{ + META_ASSERT(MAX_VERTEX_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT); + ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_9_1); + return MAX_VERTEX_UNIFORM_VECTORS_D3D11; +} + +unsigned int Renderer11::getMaxFragmentUniformVectors() const +{ + META_ASSERT(MAX_FRAGMENT_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT); + ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_9_1); + return MAX_FRAGMENT_UNIFORM_VECTORS_D3D11; +} + +unsigned int Renderer11::getMaxVaryingVectors() const +{ + META_ASSERT(gl::IMPLEMENTATION_MAX_VARYING_VECTORS == D3D11_VS_OUTPUT_REGISTER_COUNT); + META_ASSERT(D3D11_VS_OUTPUT_REGISTER_COUNT <= D3D11_PS_INPUT_REGISTER_COUNT); + META_ASSERT(D3D10_VS_OUTPUT_REGISTER_COUNT <= D3D10_PS_INPUT_REGISTER_COUNT); + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return D3D11_VS_OUTPUT_REGISTER_COUNT - getReservedVaryings(); + case D3D_FEATURE_LEVEL_10_1: + return D3D10_1_VS_OUTPUT_REGISTER_COUNT - getReservedVaryings(); + case D3D_FEATURE_LEVEL_10_0: + return D3D10_VS_OUTPUT_REGISTER_COUNT - getReservedVaryings(); + case D3D_FEATURE_LEVEL_9_3: + return 10 - getReservedVaryings(); + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 8 - getReservedVaryings(); + default: UNREACHABLE(); + return 0; + } +} + +unsigned int Renderer11::getMaxVertexShaderUniformBuffers() const +{ + META_ASSERT(gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS >= D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT && + gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS >= D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); + + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - getReservedVertexUniformBuffers(); + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + return D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - getReservedVertexUniformBuffers(); + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +unsigned int Renderer11::getMaxFragmentShaderUniformBuffers() const +{ + META_ASSERT(gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS >= D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT && + gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS >= D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); + + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - getReservedFragmentUniformBuffers(); + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + return D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - getReservedFragmentUniformBuffers(); + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +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; +} + +unsigned int Renderer11::getReservedVaryings() const +{ + // We potentially reserve varyings for gl_Position, dx_Position, gl_FragCoord and gl_PointSize + return 4; +} + + +unsigned int Renderer11::getMaxTransformFeedbackBuffers() const +{ + META_ASSERT(gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS >= D3D11_SO_BUFFER_SLOT_COUNT && + gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS >= D3D10_SO_BUFFER_SLOT_COUNT); + + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return D3D11_SO_BUFFER_SLOT_COUNT; + case D3D_FEATURE_LEVEL_10_1: + return D3D10_1_SO_BUFFER_SLOT_COUNT; + case D3D_FEATURE_LEVEL_10_0: + return D3D10_SO_BUFFER_SLOT_COUNT; + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +unsigned int Renderer11::getMaxTransformFeedbackSeparateComponents() const +{ + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return getMaxTransformFeedbackInterleavedComponents() / getMaxTransformFeedbackBuffers(); + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + // D3D 10 and 10.1 only allow one output per output slot if an output slot other than zero + // is used. + return 4; + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +unsigned int Renderer11::getMaxTransformFeedbackInterleavedComponents() const +{ + return (getMaxVaryingVectors() * 4); +} + +unsigned int Renderer11::getMaxUniformBufferSize() const +{ + // Each component is a 4-element vector of 4-byte units (floats) + const unsigned int bytesPerComponent = 4 * sizeof(float); + + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + return D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * bytesPerComponent; + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + return D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * bytesPerComponent; + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 0; + default: UNREACHABLE(); + return 0; + } +} + +bool Renderer11::getShareHandleSupport() const +{ + // We only currently support share handles with BGRA surfaces, because + // chrome needs BGRA. Once chrome fixes this, we should always support them. + // PIX doesn't seem to support using share handles, so disable them. + return getRendererExtensions().textureFormatBGRA8888 && !gl::perfActive(); +} + +bool Renderer11::getPostSubBufferSupport() const +{ + // D3D11 does not support present with dirty rectangles until D3D11.1 and DXGI 1.2. + return false; +} + +int Renderer11::getMaxRecommendedElementsIndices() const +{ + META_ASSERT(D3D11_REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP == 32); + META_ASSERT(D3D10_REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP == 32); + + // D3D11 allows up to 2^32 elements, but we report max signed int for convenience. + return std::numeric_limits<GLint>::max(); +} + +int Renderer11::getMaxRecommendedElementsVertices() const +{ + META_ASSERT(D3D11_REQ_DRAW_VERTEX_COUNT_2_TO_EXP == 32); + META_ASSERT(D3D10_REQ_DRAW_VERTEX_COUNT_2_TO_EXP == 32); + + // D3D11 allows up to 2^32 elements, but we report max signed int for convenience. + return std::numeric_limits<GLint>::max(); +} + +bool Renderer11::getSRGBTextureSupport() const +{ + return true; +} + +int Renderer11::getMajorShaderModel() const +{ + switch (mFeatureLevel) + { + 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: + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return D3D10_SHADER_MAJOR_VERSION; // 4 + default: UNREACHABLE(); return 0; + } +} + +int Renderer11::getMinorShaderModel() const +{ + switch (mFeatureLevel) + { + 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: + case D3D_FEATURE_LEVEL_9_3: + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: return D3D10_SHADER_MINOR_VERSION; // 0 + default: UNREACHABLE(); return 0; + } +} + +int Renderer11::getMinSwapInterval() const +{ + return 0; +} + +int Renderer11::getMaxSwapInterval() const +{ + return 4; +} + +int Renderer11::getMaxSupportedSamples() const +{ + return mMaxSupportedSamples; +} + +GLsizei Renderer11::getMaxSupportedFormatSamples(GLenum internalFormat) const +{ + DXGI_FORMAT format = gl_d3d11::GetRenderableFormat(internalFormat); + MultisampleSupportMap::const_iterator iter = mMultisampleSupportMap.find(format); + return (iter != mMultisampleSupportMap.end()) ? iter->second.maxSupportedSamples : 0; +} + +GLsizei Renderer11::getNumSampleCounts(GLenum internalFormat) const +{ + unsigned int numCounts = 0; + + // D3D11 supports multisampling for signed and unsigned format, but ES 3.0 does not + GLenum componentType = gl::GetComponentType(internalFormat); + if (componentType != GL_INT && componentType != GL_UNSIGNED_INT) + { + DXGI_FORMAT format = gl_d3d11::GetRenderableFormat(internalFormat); + MultisampleSupportMap::const_iterator iter = mMultisampleSupportMap.find(format); + + if (iter != mMultisampleSupportMap.end()) + { + const MultisampleSupportInfo& info = iter->second; + for (int i = 0; i < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; i++) + { + if (info.qualityLevels[i] > 0) + { + numCounts++; + } + } + } + } + + return numCounts; +} + +void Renderer11::getSampleCounts(GLenum internalFormat, GLsizei bufSize, GLint *params) const +{ + // D3D11 supports multisampling for signed and unsigned format, but ES 3.0 does not + GLenum componentType = gl::GetComponentType(internalFormat); + if (componentType == GL_INT || componentType == GL_UNSIGNED_INT) + { + return; + } + + DXGI_FORMAT format = gl_d3d11::GetRenderableFormat(internalFormat); + MultisampleSupportMap::const_iterator iter = mMultisampleSupportMap.find(format); + + if (iter != mMultisampleSupportMap.end()) + { + const MultisampleSupportInfo& info = iter->second; + int bufPos = 0; + for (int i = D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT - 1; i >= 0 && bufPos < bufSize; i--) + { + if (info.qualityLevels[i] > 0) + { + params[bufPos++] = i + 1; + } + } + } +} + +int Renderer11::getNearestSupportedSamples(DXGI_FORMAT format, unsigned int requested) const +{ + if (requested == 0) + { + return 0; + } + + MultisampleSupportMap::const_iterator iter = mMultisampleSupportMap.find(format); + if (iter != mMultisampleSupportMap.end()) + { + const MultisampleSupportInfo& info = iter->second; + for (unsigned int i = requested - 1; i < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; i++) + { + if (info.qualityLevels[i] > 0) + { + return i + 1; + } + } + } + + return -1; +} + +bool Renderer11::copyToRenderTarget(TextureStorageInterface2D *dest, TextureStorageInterface2D *source) +{ + if (source && dest) + { + TextureStorage11_2D *source11 = TextureStorage11_2D::makeTextureStorage11_2D(source->getStorageInstance()); + TextureStorage11_2D *dest11 = TextureStorage11_2D::makeTextureStorage11_2D(dest->getStorageInstance()); + + mDeviceContext->CopyResource(dest11->getResource(), source11->getResource()); + + dest11->invalidateSwizzleCache(); + + return true; + } + + return false; +} + +bool Renderer11::copyToRenderTarget(TextureStorageInterfaceCube *dest, TextureStorageInterfaceCube *source) +{ + if (source && dest) + { + TextureStorage11_Cube *source11 = TextureStorage11_Cube::makeTextureStorage11_Cube(source->getStorageInstance()); + TextureStorage11_Cube *dest11 = TextureStorage11_Cube::makeTextureStorage11_Cube(dest->getStorageInstance()); + + mDeviceContext->CopyResource(dest11->getResource(), source11->getResource()); + + dest11->invalidateSwizzleCache(); + + return true; + } + + return false; +} + +bool Renderer11::copyToRenderTarget(TextureStorageInterface3D *dest, TextureStorageInterface3D *source) +{ + if (source && dest) + { + TextureStorage11_3D *source11 = TextureStorage11_3D::makeTextureStorage11_3D(source->getStorageInstance()); + TextureStorage11_3D *dest11 = TextureStorage11_3D::makeTextureStorage11_3D(dest->getStorageInstance()); + + mDeviceContext->CopyResource(dest11->getResource(), source11->getResource()); + + dest11->invalidateSwizzleCache(); + + return true; + } + + return false; +} + +bool Renderer11::copyToRenderTarget(TextureStorageInterface2DArray *dest, TextureStorageInterface2DArray *source) +{ + if (source && dest) + { + TextureStorage11_2DArray *source11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(source->getStorageInstance()); + TextureStorage11_2DArray *dest11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(dest->getStorageInstance()); + + mDeviceContext->CopyResource(dest11->getResource(), source11->getResource()); + + dest11->invalidateSwizzleCache(); + + return true; + } + + return false; +} + +bool Renderer11::copyImage(gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, + GLint xoffset, GLint yoffset, TextureStorageInterface2D *storage, GLint level) +{ + gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); + if (!colorbuffer) + { + ERR("Failed to retrieve the color buffer from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *sourceRenderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (!sourceRenderTarget) + { + ERR("Failed to retrieve the render target from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11ShaderResourceView *source = sourceRenderTarget->getShaderResourceView(); + if (!source) + { + ERR("Failed to retrieve the render target view from the render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + TextureStorage11_2D *storage11 = TextureStorage11_2D::makeTextureStorage11_2D(storage->getStorageInstance()); + if (!storage11) + { + ERR("Failed to retrieve the texture storage from the destination."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *destRenderTarget = RenderTarget11::makeRenderTarget11(storage11->getRenderTarget(level)); + if (!destRenderTarget) + { + ERR("Failed to retrieve the render target from the destination storage."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11RenderTargetView *dest = destRenderTarget->getRenderTargetView(); + if (!dest) + { + ERR("Failed to retrieve the render target view from the destination render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + + gl::Box destArea(xoffset, yoffset, 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 + bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST); + + storage11->invalidateSwizzleCacheLevel(level); + + return ret; +} + +bool Renderer11::copyImage(gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, + GLint xoffset, GLint yoffset, TextureStorageInterfaceCube *storage, GLenum target, GLint level) +{ + gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); + if (!colorbuffer) + { + ERR("Failed to retrieve the color buffer from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *sourceRenderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (!sourceRenderTarget) + { + ERR("Failed to retrieve the render target from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11ShaderResourceView *source = sourceRenderTarget->getShaderResourceView(); + if (!source) + { + ERR("Failed to retrieve the render target view from the render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + TextureStorage11_Cube *storage11 = TextureStorage11_Cube::makeTextureStorage11_Cube(storage->getStorageInstance()); + if (!storage11) + { + ERR("Failed to retrieve the texture storage from the destination."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *destRenderTarget = RenderTarget11::makeRenderTarget11(storage11->getRenderTargetFace(target, level)); + if (!destRenderTarget) + { + ERR("Failed to retrieve the render target from the destination storage."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11RenderTargetView *dest = destRenderTarget->getRenderTargetView(); + if (!dest) + { + ERR("Failed to retrieve the render target view from the destination render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + + gl::Box destArea(xoffset, yoffset, 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 + bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST); + + storage11->invalidateSwizzleCacheLevel(level); + + return ret; +} + +bool Renderer11::copyImage(gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, + GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface3D *storage, GLint level) +{ + gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); + if (!colorbuffer) + { + ERR("Failed to retrieve the color buffer from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *sourceRenderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (!sourceRenderTarget) + { + ERR("Failed to retrieve the render target from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11ShaderResourceView *source = sourceRenderTarget->getShaderResourceView(); + if (!source) + { + ERR("Failed to retrieve the render target view from the render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + TextureStorage11_3D *storage11 = TextureStorage11_3D::makeTextureStorage11_3D(storage->getStorageInstance()); + if (!storage11) + { + ERR("Failed to retrieve the texture storage from the destination."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *destRenderTarget = RenderTarget11::makeRenderTarget11(storage11->getRenderTargetLayer(level, zOffset)); + if (!destRenderTarget) + { + ERR("Failed to retrieve the render target from the destination storage."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11RenderTargetView *dest = destRenderTarget->getRenderTargetView(); + if (!dest) + { + ERR("Failed to retrieve the render target view from the destination render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + + gl::Box destArea(xoffset, yoffset, 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 + bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST); + + storage11->invalidateSwizzleCacheLevel(level); + + return ret; +} + +bool Renderer11::copyImage(gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat, + GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface2DArray *storage, GLint level) +{ + gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); + if (!colorbuffer) + { + ERR("Failed to retrieve the color buffer from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *sourceRenderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (!sourceRenderTarget) + { + ERR("Failed to retrieve the render target from the frame buffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11ShaderResourceView *source = sourceRenderTarget->getShaderResourceView(); + if (!source) + { + ERR("Failed to retrieve the render target view from the render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + TextureStorage11_2DArray *storage11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(storage->getStorageInstance()); + if (!storage11) + { + SafeRelease(source); + ERR("Failed to retrieve the texture storage from the destination."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget11 *destRenderTarget = RenderTarget11::makeRenderTarget11(storage11->getRenderTargetLayer(level, zOffset)); + if (!destRenderTarget) + { + SafeRelease(source); + ERR("Failed to retrieve the render target from the destination storage."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11RenderTargetView *dest = destRenderTarget->getRenderTargetView(); + if (!dest) + { + ERR("Failed to retrieve the render target view from the destination render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1); + gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1); + + gl::Box destArea(xoffset, yoffset, 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 + bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, + destFormat, GL_NEAREST); + + storage11->invalidateSwizzleCacheLevel(level); + + return ret; +} + +void Renderer11::unapplyRenderTargets() +{ + setOneTimeRenderTarget(NULL); +} + +void Renderer11::setOneTimeRenderTarget(ID3D11RenderTargetView *renderTargetView) +{ + ID3D11RenderTargetView *rtvArray[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS] = {NULL}; + + rtvArray[0] = renderTargetView; + + mDeviceContext->OMSetRenderTargets(getRendererCaps().maxDrawBuffers, rtvArray, NULL); + + // Do not preserve the serial for this one-time-use render target + for (unsigned int rtIndex = 0; rtIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; rtIndex++) + { + mAppliedRenderTargetSerials[rtIndex] = 0; + } +} + +RenderTarget *Renderer11::createRenderTarget(SwapChain *swapChain, bool depth) +{ + SwapChain11 *swapChain11 = SwapChain11::makeSwapChain11(swapChain); + RenderTarget11 *renderTarget = NULL; + + if (depth) + { + // Note: depth stencil may be NULL for 0 sized surfaces + renderTarget = new RenderTarget11(this, swapChain11->getDepthStencil(), + swapChain11->getDepthStencilTexture(), + swapChain11->getDepthStencilShaderResource(), + swapChain11->getWidth(), swapChain11->getHeight(), 1); + } + else + { + // Note: render target may be NULL for 0 sized surfaces + renderTarget = new RenderTarget11(this, swapChain11->getRenderTarget(), + swapChain11->getOffscreenTexture(), + swapChain11->getRenderTargetShaderResource(), + swapChain11->getWidth(), swapChain11->getHeight(), 1); + } + return renderTarget; +} + +RenderTarget *Renderer11::createRenderTarget(int width, int height, GLenum format, GLsizei samples) +{ + RenderTarget11 *renderTarget = new RenderTarget11(this, width, height, format, samples); + return renderTarget; +} + +ShaderExecutable *Renderer11::loadExecutable(const void *function, size_t length, rx::ShaderType type, + const std::vector<gl::LinkedVarying> &transformFeedbackVaryings, + bool separatedOutputBuffers) +{ + ShaderExecutable11 *executable = NULL; + HRESULT result; + + switch (type) + { + case rx::SHADER_VERTEX: + { + ID3D11VertexShader *vertexShader = NULL; + ID3D11GeometryShader *streamOutShader = NULL; + + result = mDevice->CreateVertexShader(function, length, NULL, &vertexShader); + ASSERT(SUCCEEDED(result)); + + if (transformFeedbackVaryings.size() > 0) + { + std::vector<D3D11_SO_DECLARATION_ENTRY> soDeclaration; + for (size_t i = 0; i < transformFeedbackVaryings.size(); i++) + { + const gl::LinkedVarying &varying = transformFeedbackVaryings[i]; + GLenum transposedType = gl::TransposeMatrixType(varying.type); + + for (size_t j = 0; j < varying.semanticIndexCount; j++) + { + D3D11_SO_DECLARATION_ENTRY entry = { 0 }; + entry.Stream = 0; + entry.SemanticName = varying.semanticName.c_str(); + entry.SemanticIndex = varying.semanticIndex + j; + entry.StartComponent = 0; + entry.ComponentCount = gl::VariableColumnCount(transposedType); + entry.OutputSlot = (separatedOutputBuffers ? i : 0); + soDeclaration.push_back(entry); + } + } + + result = mDevice->CreateGeometryShaderWithStreamOutput(function, length, soDeclaration.data(), soDeclaration.size(), + NULL, 0, 0, NULL, &streamOutShader); + ASSERT(SUCCEEDED(result)); + } + + if (vertexShader) + { + executable = new ShaderExecutable11(function, length, vertexShader, streamOutShader); + } + } + break; + case rx::SHADER_PIXEL: + { + ID3D11PixelShader *pixelShader = NULL; + + result = mDevice->CreatePixelShader(function, length, NULL, &pixelShader); + ASSERT(SUCCEEDED(result)); + + if (pixelShader) + { + executable = new ShaderExecutable11(function, length, pixelShader); + } + } + break; + case rx::SHADER_GEOMETRY: + { + ID3D11GeometryShader *geometryShader = NULL; + + result = mDevice->CreateGeometryShader(function, length, NULL, &geometryShader); + ASSERT(SUCCEEDED(result)); + + if (geometryShader) + { + executable = new ShaderExecutable11(function, length, geometryShader); + } + } + break; + default: + UNREACHABLE(); + break; + } + + return executable; +} + +ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, rx::ShaderType type, + const std::vector<gl::LinkedVarying> &transformFeedbackVaryings, + bool separatedOutputBuffers, D3DWorkaroundType workaround) +{ + const char *profileType = NULL; + switch (type) + { + case rx::SHADER_VERTEX: + profileType = "vs"; + break; + case rx::SHADER_PIXEL: + profileType = "ps"; + break; + case rx::SHADER_GEOMETRY: + profileType = "gs"; + break; + default: + UNREACHABLE(); + return NULL; + } + + const char *profileVersion = NULL; + switch (mFeatureLevel) + { + case D3D_FEATURE_LEVEL_11_0: + profileVersion = "5_0"; + break; + case D3D_FEATURE_LEVEL_10_1: + profileVersion = "4_1"; + break; + case D3D_FEATURE_LEVEL_10_0: + profileVersion = "4_0"; + break; + case D3D_FEATURE_LEVEL_9_3: + profileVersion = "4_0_level_9_3"; + break; + case D3D_FEATURE_LEVEL_9_2: + profileVersion = "4_0_level_9_2"; + break; + case D3D_FEATURE_LEVEL_9_1: + profileVersion = "4_0_level_9_1"; + break; + default: + UNREACHABLE(); + return NULL; + } + + char profile[32]; + snprintf(profile, ArraySize(profile), "%s_%s", profileType, profileVersion); + + UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL0; + + if (gl::perfActive()) + { +#ifndef NDEBUG + flags = D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + flags |= D3DCOMPILE_DEBUG; + + std::string sourcePath = getTempPath(); + std::string sourceText = std::string("#line 2 \"") + sourcePath + std::string("\"\n\n") + std::string(shaderHLSL); + writeFile(sourcePath.c_str(), sourceText.c_str(), sourceText.size()); + } + + // 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. + const UINT extraFlags[] = + { + flags, + flags | D3DCOMPILE_SKIP_VALIDATION, + flags | D3DCOMPILE_SKIP_OPTIMIZATION + }; + + const static char *extraFlagNames[] = + { + "default", + "skip validation", + "skip optimization" + }; + + int attempts = ArraySize(extraFlags); + + ID3DBlob *binary = (ID3DBlob*)mCompiler.compileToBinary(infoLog, shaderHLSL, profile, extraFlags, extraFlagNames, attempts); + if (!binary) + { + return NULL; + } + + ShaderExecutable *executable = loadExecutable((DWORD *)binary->GetBufferPointer(), binary->GetBufferSize(), type, + transformFeedbackVaryings, separatedOutputBuffers); + SafeRelease(binary); + + return executable; +} + +rx::UniformStorage *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() +{ + return new Buffer11(this); +} + +VertexArrayImpl *Renderer11::createVertexArray() +{ + return new VertexArray11(this); +} + +QueryImpl *Renderer11::createQuery(GLenum type) +{ + return new Query11(this, type); +} + +FenceImpl *Renderer11::createFence() +{ + return new Fence11(this); +} + +bool Renderer11::supportsFastCopyBufferToTexture(GLenum internalFormat) const +{ + ASSERT(getRendererExtensions().pixelBufferObject); + + // sRGB formats do not work with D3D11 buffer SRVs + if (gl::GetColorEncoding(internalFormat) == GL_SRGB) + { + return false; + } + + // We cannot support direct copies to non-color-renderable formats + if (gl_d3d11::GetRTVFormat(internalFormat) != DXGI_FORMAT_UNKNOWN) + { + return false; + } + + // We skip all 3-channel formats since sometimes format support is missing + if (gl::GetComponentCount(internalFormat) == 3) + { + return false; + } + + // We don't support formats which we can't represent without conversion + if (getNativeTextureFormat(internalFormat) != internalFormat) + { + return false; + } + + return true; +} + +bool Renderer11::fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTarget *destRenderTarget, + GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea) +{ + ASSERT(supportsFastCopyBufferToTexture(destinationFormat)); + return mPixelTransfer->copyBufferToTexture(unpack, offset, destRenderTarget, destinationFormat, sourcePixelsType, destArea); +} + +bool Renderer11::getRenderTargetResource(gl::FramebufferAttachment *colorbuffer, unsigned int *subresourceIndex, ID3D11Texture2D **resource) +{ + ASSERT(colorbuffer != NULL); + + RenderTarget11 *renderTarget = RenderTarget11::makeRenderTarget11(colorbuffer->getRenderTarget()); + if (renderTarget) + { + *subresourceIndex = renderTarget->getSubresourceIndex(); + + ID3D11RenderTargetView *colorBufferRTV = renderTarget->getRenderTargetView(); + if (colorBufferRTV) + { + ID3D11Resource *textureResource = NULL; + colorBufferRTV->GetResource(&textureResource); + + if (textureResource) + { + HRESULT result = textureResource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)resource); + SafeRelease(textureResource); + + if (SUCCEEDED(result)) + { + return true; + } + else + { + ERR("Failed to extract the ID3D11Texture2D from the render target resource, " + "HRESULT: 0x%X.", result); + } + } + } + } + + return false; +} + +bool Renderer11::blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect, + const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter) +{ + if (blitRenderTarget) + { + gl::FramebufferAttachment *readBuffer = readTarget->getReadColorbuffer(); + + if (!readBuffer) + { + ERR("Failed to retrieve the read buffer from the read framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget *readRenderTarget = readBuffer->getRenderTarget(); + + for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++) + { + if (drawTarget->isEnabledColorAttachment(colorAttachment)) + { + gl::FramebufferAttachment *drawBuffer = drawTarget->getColorbuffer(colorAttachment); + + if (!drawBuffer) + { + ERR("Failed to retrieve the draw buffer from the draw framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget *drawRenderTarget = drawBuffer->getRenderTarget(); + + if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter, scissor, + blitRenderTarget, false, false)) + { + return false; + } + } + } + } + + if (blitDepth || blitStencil) + { + gl::FramebufferAttachment *readBuffer = readTarget->getDepthOrStencilbuffer(); + gl::FramebufferAttachment *drawBuffer = drawTarget->getDepthOrStencilbuffer(); + + if (!readBuffer) + { + ERR("Failed to retrieve the read depth-stencil buffer from the read framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + if (!drawBuffer) + { + ERR("Failed to retrieve the draw depth-stencil buffer from the draw framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + RenderTarget *readRenderTarget = readBuffer->getDepthStencil(); + RenderTarget *drawRenderTarget = drawBuffer->getDepthStencil(); + + if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter, scissor, + false, blitDepth, blitStencil)) + { + return false; + } + } + + invalidateFramebufferSwizzles(drawTarget); + + return true; +} + +void Renderer11::readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, GLuint outputPitch, const gl::PixelPackState &pack, void* pixels) +{ + ID3D11Texture2D *colorBufferTexture = NULL; + unsigned int subresourceIndex = 0; + + gl::FramebufferAttachment *colorbuffer = framebuffer->getReadColorbuffer(); + + if (colorbuffer && getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture)) + { + gl::Rectangle area; + area.x = x; + area.y = y; + area.width = width; + area.height = height; + + if (pack.pixelBuffer.get() != NULL) + { + rx::Buffer11 *packBufferStorage = Buffer11::makeBuffer11(pack.pixelBuffer.get()->getImplementation()); + PackPixelsParams packParams(area, format, type, outputPitch, pack, reinterpret_cast<ptrdiff_t>(pixels)); + packBufferStorage->packPixels(colorBufferTexture, subresourceIndex, packParams); + } + else + { + readTextureData(colorBufferTexture, subresourceIndex, area, format, type, outputPitch, pack, pixels); + } + + SafeRelease(colorBufferTexture); + } +} + +Image *Renderer11::createImage() +{ + return new Image11(); +} + +void Renderer11::generateMipmap(Image *dest, Image *src) +{ + Image11 *dest11 = Image11::makeImage11(dest); + Image11 *src11 = Image11::makeImage11(src); + Image11::generateMipmap(dest11, src11); +} + +TextureStorage *Renderer11::createTextureStorage2D(SwapChain *swapChain) +{ + SwapChain11 *swapChain11 = SwapChain11::makeSwapChain11(swapChain); + return new TextureStorage11_2D(this, swapChain11); +} + +TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels) +{ + return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels); +} + +TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels) +{ + return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels); +} + +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); +} + +Texture2DImpl *Renderer11::createTexture2D() +{ + return new TextureD3D_2D(this); +} + +TextureCubeImpl *Renderer11::createTextureCube() +{ + return new TextureD3D_Cube(this); +} + +Texture3DImpl *Renderer11::createTexture3D() +{ + return new TextureD3D_3D(this); +} + +Texture2DArrayImpl *Renderer11::createTexture2DArray() +{ + return new TextureD3D_2DArray(this); +} + +void Renderer11::readTextureData(ID3D11Texture2D *texture, unsigned int subResource, const gl::Rectangle &area, GLenum format, + GLenum type, GLuint outputPitch, const gl::PixelPackState &pack, void *pixels) +{ + ASSERT(area.width >= 0); + ASSERT(area.height >= 0); + + D3D11_TEXTURE2D_DESC textureDesc; + texture->GetDesc(&textureDesc); + + // 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(area.x, 0, static_cast<int>(textureDesc.Width)); + safeArea.y = gl::clamp(area.y, 0, static_cast<int>(textureDesc.Height)); + safeArea.width = gl::clamp(area.width + std::min(area.x, 0), 0, + static_cast<int>(textureDesc.Width) - safeArea.x); + safeArea.height = gl::clamp(area.height + std::min(area.y, 0), 0, + static_cast<int>(textureDesc.Height) - safeArea.y); + + ASSERT(safeArea.x >= 0 && safeArea.y >= 0); + ASSERT(safeArea.x + safeArea.width <= static_cast<int>(textureDesc.Width)); + ASSERT(safeArea.y + safeArea.height <= static_cast<int>(textureDesc.Height)); + + if (safeArea.width == 0 || safeArea.height == 0) + { + // no work to do + return; + } + + D3D11_TEXTURE2D_DESC stagingDesc; + stagingDesc.Width = safeArea.width; + stagingDesc.Height = safeArea.height; + stagingDesc.MipLevels = 1; + stagingDesc.ArraySize = 1; + stagingDesc.Format = textureDesc.Format; + stagingDesc.SampleDesc.Count = 1; + stagingDesc.SampleDesc.Quality = 0; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + ID3D11Texture2D* stagingTex = NULL; + HRESULT result = mDevice->CreateTexture2D(&stagingDesc, NULL, &stagingTex); + if (FAILED(result)) + { + ERR("Failed to create staging texture for readPixels, HRESULT: 0x%X.", result); + return; + } + + ID3D11Texture2D* srcTex = NULL; + 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 = D3D11_USAGE_DEFAULT; + resolveDesc.BindFlags = 0; + resolveDesc.CPUAccessFlags = 0; + resolveDesc.MiscFlags = 0; + + result = mDevice->CreateTexture2D(&resolveDesc, NULL, &srcTex); + if (FAILED(result)) + { + ERR("Failed to create resolve texture for readPixels, HRESULT: 0x%X.", result); + SafeRelease(stagingTex); + return; + } + + mDeviceContext->ResolveSubresource(srcTex, 0, texture, subResource, textureDesc.Format); + subResource = 0; + } + else + { + srcTex = texture; + srcTex->AddRef(); + } + + 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); + srcBox.front = 0; + srcBox.back = 1; + + mDeviceContext->CopySubresourceRegion(stagingTex, 0, 0, 0, 0, srcTex, subResource, &srcBox); + + SafeRelease(srcTex); + + PackPixelsParams packParams(safeArea, format, type, outputPitch, pack, 0); + packPixels(stagingTex, packParams, pixels); + + SafeRelease(stagingTex); +} + +void Renderer11::packPixels(ID3D11Texture2D *readTexture, const PackPixelsParams ¶ms, void *pixelsOut) +{ + D3D11_TEXTURE2D_DESC textureDesc; + readTexture->GetDesc(&textureDesc); + + D3D11_MAPPED_SUBRESOURCE mapping; + HRESULT hr = mDeviceContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &mapping); + UNUSED_ASSERTION_VARIABLE(hr); + ASSERT(SUCCEEDED(hr)); + + unsigned char *source; + int inputPitch; + if (params.pack.reverseRowOrder) + { + source = static_cast<unsigned char*>(mapping.pData) + mapping.RowPitch * (params.area.height - 1); + inputPitch = -static_cast<int>(mapping.RowPitch); + } + else + { + source = static_cast<unsigned char*>(mapping.pData); + inputPitch = static_cast<int>(mapping.RowPitch); + } + + GLenum sourceInternalFormat = d3d11_gl::GetInternalFormat(textureDesc.Format); + GLenum sourceFormat = gl::GetFormat(sourceInternalFormat); + GLenum sourceType = gl::GetType(sourceInternalFormat); + + GLuint sourcePixelSize = gl::GetPixelBytes(sourceInternalFormat); + + if (sourceFormat == params.format && sourceType == params.type) + { + unsigned char *dest = static_cast<unsigned char*>(pixelsOut) + params.offset; + for (int y = 0; y < params.area.height; y++) + { + memcpy(dest + y * params.outputPitch, source + y * inputPitch, params.area.width * sourcePixelSize); + } + } + else + { + GLenum destInternalFormat = gl::GetSizedInternalFormat(params.format, params.type); + GLuint destPixelSize = gl::GetPixelBytes(destInternalFormat); + + ColorCopyFunction fastCopyFunc = d3d11::GetFastCopyFunction(textureDesc.Format, params.format, params.type); + 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++) + { + void *dest = static_cast<unsigned char*>(pixelsOut) + params.offset + y * params.outputPitch + x * destPixelSize; + void *src = static_cast<unsigned char*>(source) + y * inputPitch + x * sourcePixelSize; + + fastCopyFunc(src, dest); + } + } + } + else + { + ColorReadFunction readFunc = d3d11::GetColorReadFunction(textureDesc.Format); + ColorWriteFunction writeFunc = gl::GetColorWriteFunction(params.format, params.type); + + unsigned char temp[16]; // Maximum size of any Color<T> type used. + META_ASSERT(sizeof(temp) >= sizeof(gl::ColorF) && + sizeof(temp) >= sizeof(gl::ColorUI) && + sizeof(temp) >= sizeof(gl::ColorI)); + + for (int y = 0; y < params.area.height; y++) + { + for (int x = 0; x < params.area.width; x++) + { + void *dest = static_cast<unsigned char*>(pixelsOut) + params.offset + y * params.outputPitch + x * destPixelSize; + void *src = static_cast<unsigned char*>(source) + y * inputPitch + x * sourcePixelSize; + + // readFunc and writeFunc will be using the same type of color, CopyTexImage + // will not allow the copy otherwise. + readFunc(src, temp); + writeFunc(temp, dest); + } + } + } + } + + mDeviceContext->Unmap(readTexture, 0); +} + +bool Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget, + RenderTarget *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)); + + bool result = true; + + RenderTarget11 *drawRenderTarget11 = RenderTarget11::makeRenderTarget11(drawRenderTarget); + if (!drawRenderTarget) + { + ERR("Failed to retrieve the draw render target from the draw framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + ID3D11Resource *drawTexture = drawRenderTarget11->getTexture(); + unsigned int drawSubresource = drawRenderTarget11->getSubresourceIndex(); + ID3D11RenderTargetView *drawRTV = drawRenderTarget11->getRenderTargetView(); + ID3D11DepthStencilView *drawDSV = drawRenderTarget11->getDepthStencilView(); + + RenderTarget11 *readRenderTarget11 = RenderTarget11::makeRenderTarget11(readRenderTarget); + if (!readRenderTarget) + { + ERR("Failed to retrieve the read render target from the read framebuffer."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + 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, false); + } + } + } + else + { + readTexture = readRenderTarget11->getTexture(); + readTexture->AddRef(); + readSubresource = readRenderTarget11->getSubresourceIndex(); + readSRV = readRenderTarget11->getShaderResourceView(); + readSRV->AddRef(); + } + + if (!readTexture || !readSRV) + { + SafeRelease(readTexture); + SafeRelease(readSRV); + ERR("Failed to retrieve the read render target view from the read render target."); + return gl::error(GL_OUT_OF_MEMORY, false); + } + + gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1); + gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1); + + bool scissorNeeded = scissor && gl::ClipRectangle(drawRect, *scissor, NULL); + + bool wholeBufferCopy = !scissorNeeded && + 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 hasDepth = gl::GetDepthBits(drawRenderTarget11->getActualFormat()) > 0; + bool hasStencil = gl::GetStencilBits(drawRenderTarget11->getActualFormat()) > 0; + bool partialDSBlit = (hasDepth && depthBlit) != (hasStencil && stencilBlit); + + if (readRenderTarget11->getActualFormat() == drawRenderTarget->getActualFormat() && + !stretchRequired && !outOfBounds && !flipRequired && !partialDSBlit && + (!(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 = true; + } + 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 + { + GLenum format = gl::GetFormat(drawRenderTarget->getInternalFormat()); + result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize, + scissor, format, filter); + } + } + + SafeRelease(readTexture); + SafeRelease(readSRV); + + return result; +} + +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; + } +} + +void Renderer11::invalidateFBOAttachmentSwizzles(gl::FramebufferAttachment *attachment, int mipLevel) +{ + ASSERT(attachment->isTexture()); + TextureStorage *texStorage = attachment->getTextureStorage(); + if (texStorage) + { + TextureStorage11 *texStorage11 = TextureStorage11::makeTextureStorage11(texStorage); + if (!texStorage11) + { + ERR("texture storage pointer unexpectedly null."); + return; + } + + texStorage11->invalidateSwizzleCacheLevel(mipLevel); + } +} + +void Renderer11::invalidateFramebufferSwizzles(gl::Framebuffer *framebuffer) +{ + for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++) + { + gl::FramebufferAttachment *attachment = framebuffer->getColorbuffer(colorAttachment); + if (attachment && attachment->isTexture()) + { + invalidateFBOAttachmentSwizzles(attachment, attachment->mipLevel()); + } + } + + gl::FramebufferAttachment *depthAttachment = framebuffer->getDepthbuffer(); + if (depthAttachment && depthAttachment->isTexture()) + { + invalidateFBOAttachmentSwizzles(depthAttachment, depthAttachment->mipLevel()); + } + + gl::FramebufferAttachment *stencilAttachment = framebuffer->getStencilbuffer(); + if (stencilAttachment && stencilAttachment->isTexture()) + { + invalidateFBOAttachmentSwizzles(stencilAttachment, stencilAttachment->mipLevel()); + } +} + +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; +} + +GLenum Renderer11::getNativeTextureFormat(GLenum internalFormat) const +{ + return d3d11_gl::GetInternalFormat(gl_d3d11::GetTexFormat(internalFormat)); +} + +rx::VertexConversionType Renderer11::getVertexConversionType(const gl::VertexFormat &vertexFormat) const +{ + return gl_d3d11::GetVertexConversionType(vertexFormat); +} + +GLenum Renderer11::getVertexComponentType(const gl::VertexFormat &vertexFormat) const +{ + return d3d11::GetComponentType(gl_d3d11::GetNativeVertexFormat(vertexFormat)); +} + +Renderer11::MultisampleSupportInfo Renderer11::getMultisampleSupportInfo(DXGI_FORMAT format) +{ + MultisampleSupportInfo supportInfo = { 0 }; + + UINT formatSupport; + HRESULT result; + + result = mDevice->CheckFormatSupport(format, &formatSupport); + if (SUCCEEDED(result) && (formatSupport & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET)) + { + for (unsigned int i = 1; i <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; i++) + { + result = mDevice->CheckMultisampleQualityLevels(format, i, &supportInfo.qualityLevels[i - 1]); + if (SUCCEEDED(result) && supportInfo.qualityLevels[i - 1] > 0) + { + supportInfo.maxSupportedSamples = std::max(supportInfo.maxSupportedSamples, i); + } + else + { + supportInfo.qualityLevels[i - 1] = 0; + } + } + } + + return supportInfo; +} + +void Renderer11::generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, gl::Extensions *outExtensions) const +{ + d3d11_gl::GenerateCaps(mDevice, outCaps, outTextureCaps, outExtensions); +} + +} |