diff options
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp | 2797 |
1 files changed, 2416 insertions, 381 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp index aa34fd4de8..e9902d3f14 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp @@ -8,11 +8,22 @@ #include "libANGLE/renderer/d3d/d3d11/StateManager11.h" -#include "common/BitSetIterator.h" +#include "common/bitset_utils.h" #include "common/utilities.h" +#include "libANGLE/Context.h" +#include "libANGLE/Query.h" +#include "libANGLE/VertexArray.h" +#include "libANGLE/renderer/d3d/TextureD3D.h" +#include "libANGLE/renderer/d3d/d3d11/Buffer11.h" +#include "libANGLE/renderer/d3d/d3d11/Context11.h" #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h" -#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" +#include "libANGLE/renderer/d3d/d3d11/IndexBuffer11.h" #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" +#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" +#include "libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h" +#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h" +#include "libANGLE/renderer/d3d/d3d11/TransformFeedback11.h" +#include "libANGLE/renderer/d3d/d3d11/VertexArray11.h" namespace rx { @@ -22,15 +33,16 @@ namespace bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOURCE_VIEW_DESC desc) { unsigned mipLevel = index.mipIndex; - unsigned layerIndex = index.layerIndex; + GLint layerIndex = index.layerIndex; GLenum type = index.type; switch (desc.ViewDimension) { case D3D11_SRV_DIMENSION_TEXTURE2D: { - unsigned maxSrvMip = desc.Texture2D.MipLevels + desc.Texture2D.MostDetailedMip; - maxSrvMip = (desc.Texture2D.MipLevels == -1) ? INT_MAX : maxSrvMip; + bool allLevels = (desc.Texture2D.MipLevels == std::numeric_limits<UINT>::max()); + unsigned int maxSrvMip = desc.Texture2D.MipLevels + desc.Texture2D.MostDetailedMip; + maxSrvMip = allLevels ? INT_MAX : maxSrvMip; unsigned mipMin = index.mipIndex; unsigned mipMax = (layerIndex == -1) ? INT_MAX : layerIndex; @@ -42,22 +54,25 @@ bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOUR case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: { - unsigned maxSrvMip = + bool allLevels = (desc.Texture2DArray.MipLevels == std::numeric_limits<UINT>::max()); + unsigned int maxSrvMip = desc.Texture2DArray.MipLevels + desc.Texture2DArray.MostDetailedMip; - maxSrvMip = (desc.Texture2DArray.MipLevels == -1) ? INT_MAX : maxSrvMip; + maxSrvMip = allLevels ? INT_MAX : maxSrvMip; unsigned maxSlice = desc.Texture2DArray.FirstArraySlice + desc.Texture2DArray.ArraySize; // Cube maps can be mapped to Texture2DArray SRVs return (type == GL_TEXTURE_2D_ARRAY || gl::IsCubeMapTextureTarget(type)) && desc.Texture2DArray.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip && - desc.Texture2DArray.FirstArraySlice <= layerIndex && layerIndex < maxSlice; + desc.Texture2DArray.FirstArraySlice <= static_cast<UINT>(layerIndex) && + static_cast<UINT>(layerIndex) < maxSlice; } case D3D11_SRV_DIMENSION_TEXTURECUBE: { - unsigned maxSrvMip = desc.TextureCube.MipLevels + desc.TextureCube.MostDetailedMip; - maxSrvMip = (desc.TextureCube.MipLevels == -1) ? INT_MAX : maxSrvMip; + bool allLevels = (desc.TextureCube.MipLevels == std::numeric_limits<UINT>::max()); + unsigned int maxSrvMip = desc.TextureCube.MipLevels + desc.TextureCube.MostDetailedMip; + maxSrvMip = allLevels ? INT_MAX : maxSrvMip; return gl::IsCubeMapTextureTarget(type) && desc.TextureCube.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; @@ -65,8 +80,9 @@ bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOUR case D3D11_SRV_DIMENSION_TEXTURE3D: { - unsigned maxSrvMip = desc.Texture3D.MipLevels + desc.Texture3D.MostDetailedMip; - maxSrvMip = (desc.Texture3D.MipLevels == -1) ? INT_MAX : maxSrvMip; + bool allLevels = (desc.Texture3D.MipLevels == std::numeric_limits<UINT>::max()); + unsigned int maxSrvMip = desc.Texture3D.MipLevels + desc.Texture3D.MostDetailedMip; + maxSrvMip = allLevels ? INT_MAX : maxSrvMip; return type == GL_TEXTURE_3D && desc.Texture3D.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; @@ -82,15 +98,97 @@ bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOUR // Does *not* increment the resource ref count!! ID3D11Resource *GetViewResource(ID3D11View *view) { - ID3D11Resource *resource = NULL; + ID3D11Resource *resource = nullptr; ASSERT(view); view->GetResource(&resource); resource->Release(); return resource; } +int GetWrapBits(GLenum wrap) +{ + switch (wrap) + { + case GL_CLAMP_TO_EDGE: + return 0x1; + case GL_REPEAT: + return 0x2; + case GL_MIRRORED_REPEAT: + return 0x3; + default: + UNREACHABLE(); + return 0; + } +} + +Optional<size_t> FindFirstNonInstanced( + const std::vector<const TranslatedAttribute *> ¤tAttributes) +{ + for (size_t index = 0; index < currentAttributes.size(); ++index) + { + if (currentAttributes[index]->divisor == 0) + { + return Optional<size_t>(index); + } + } + + return Optional<size_t>::Invalid(); +} + +void SortAttributesByLayout(const gl::Program *program, + const std::vector<TranslatedAttribute> &vertexArrayAttribs, + const std::vector<TranslatedAttribute> ¤tValueAttribs, + AttribIndexArray *sortedD3DSemanticsOut, + std::vector<const TranslatedAttribute *> *sortedAttributesOut) +{ + sortedAttributesOut->clear(); + + const auto &locationToSemantic = + GetImplAs<ProgramD3D>(program)->getAttribLocationToD3DSemantics(); + + for (auto locationIndex : program->getActiveAttribLocationsMask()) + { + int d3dSemantic = locationToSemantic[locationIndex]; + if (sortedAttributesOut->size() <= static_cast<size_t>(d3dSemantic)) + { + sortedAttributesOut->resize(d3dSemantic + 1); + } + + (*sortedD3DSemanticsOut)[d3dSemantic] = d3dSemantic; + + const auto *arrayAttrib = &vertexArrayAttribs[locationIndex]; + if (arrayAttrib->attribute && arrayAttrib->attribute->enabled) + { + (*sortedAttributesOut)[d3dSemantic] = arrayAttrib; + } + else + { + ASSERT(currentValueAttribs[locationIndex].attribute); + (*sortedAttributesOut)[d3dSemantic] = ¤tValueAttribs[locationIndex]; + } + } +} + +void UpdateUniformBuffer(ID3D11DeviceContext *deviceContext, + UniformStorage11 *storage, + const d3d11::Buffer *buffer) +{ + deviceContext->UpdateSubresource(buffer->get(), 0, nullptr, storage->getDataPointer(0, 0), 0, + 0); +} + } // anonymous namespace +// StateManager11::SRVCache Implementation. + +StateManager11::SRVCache::SRVCache() : mHighestUsedSRV(0) +{ +} + +StateManager11::SRVCache::~SRVCache() +{ +} + void StateManager11::SRVCache::update(size_t resourceIndex, ID3D11ShaderResourceView *srv) { ASSERT(resourceIndex < mCurrentSRVs.size()); @@ -128,27 +226,348 @@ void StateManager11::SRVCache::clear() mHighestUsedSRV = 0; } +// ShaderConstants11 implementation + +ShaderConstants11::ShaderConstants11() + : mVertexDirty(true), + mPixelDirty(true), + mComputeDirty(true), + mSamplerMetadataVSDirty(true), + mSamplerMetadataPSDirty(true), + mSamplerMetadataCSDirty(true) +{ +} + +ShaderConstants11::~ShaderConstants11() +{ +} + +void ShaderConstants11::init(const gl::Caps &caps) +{ + mSamplerMetadataVS.resize(caps.maxVertexTextureImageUnits); + mSamplerMetadataPS.resize(caps.maxTextureImageUnits); + mSamplerMetadataCS.resize(caps.maxComputeTextureImageUnits); +} + +size_t ShaderConstants11::getRequiredBufferSize(gl::SamplerType samplerType) const +{ + switch (samplerType) + { + case gl::SAMPLER_VERTEX: + return sizeof(Vertex) + mSamplerMetadataVS.size() * sizeof(SamplerMetadata); + case gl::SAMPLER_PIXEL: + return sizeof(Pixel) + mSamplerMetadataPS.size() * sizeof(SamplerMetadata); + case gl::SAMPLER_COMPUTE: + return sizeof(Compute) + mSamplerMetadataCS.size() * sizeof(SamplerMetadata); + default: + UNREACHABLE(); + return 0; + } +} + +void ShaderConstants11::markDirty() +{ + mVertexDirty = true; + mPixelDirty = true; + mComputeDirty = true; + mSamplerMetadataVSDirty = true; + mSamplerMetadataPSDirty = true; + mSamplerMetadataCSDirty = true; +} + +bool ShaderConstants11::updateSamplerMetadata(SamplerMetadata *data, const gl::Texture &texture) +{ + bool dirty = false; + unsigned int baseLevel = texture.getTextureState().getEffectiveBaseLevel(); + GLenum sizedFormat = + texture.getFormat(texture.getTarget(), baseLevel).info->sizedInternalFormat; + if (data->baseLevel != static_cast<int>(baseLevel)) + { + data->baseLevel = static_cast<int>(baseLevel); + dirty = true; + } + + // Some metadata is needed only for integer textures. We avoid updating the constant buffer + // unnecessarily by changing the data only in case the texture is an integer texture and + // the values have changed. + bool needIntegerTextureMetadata = false; + // internalFormatBits == 0 means a 32-bit texture in the case of integer textures. + int internalFormatBits = 0; + switch (sizedFormat) + { + case GL_RGBA32I: + case GL_RGBA32UI: + case GL_RGB32I: + case GL_RGB32UI: + case GL_RG32I: + case GL_RG32UI: + case GL_R32I: + case GL_R32UI: + needIntegerTextureMetadata = true; + break; + case GL_RGBA16I: + case GL_RGBA16UI: + case GL_RGB16I: + case GL_RGB16UI: + case GL_RG16I: + case GL_RG16UI: + case GL_R16I: + case GL_R16UI: + needIntegerTextureMetadata = true; + internalFormatBits = 16; + break; + case GL_RGBA8I: + case GL_RGBA8UI: + case GL_RGB8I: + case GL_RGB8UI: + case GL_RG8I: + case GL_RG8UI: + case GL_R8I: + case GL_R8UI: + needIntegerTextureMetadata = true; + internalFormatBits = 8; + break; + case GL_RGB10_A2UI: + needIntegerTextureMetadata = true; + internalFormatBits = 10; + break; + default: + break; + } + if (needIntegerTextureMetadata) + { + if (data->internalFormatBits != internalFormatBits) + { + data->internalFormatBits = internalFormatBits; + dirty = true; + } + // Pack the wrap values into one integer so we can fit all the metadata in one 4-integer + // vector. + GLenum wrapS = texture.getWrapS(); + GLenum wrapT = texture.getWrapT(); + GLenum wrapR = texture.getWrapR(); + int wrapModes = GetWrapBits(wrapS) | (GetWrapBits(wrapT) << 2) | (GetWrapBits(wrapR) << 4); + if (data->wrapModes != wrapModes) + { + data->wrapModes = wrapModes; + dirty = true; + } + } + + return dirty; +} + +void ShaderConstants11::setComputeWorkGroups(GLuint numGroupsX, + GLuint numGroupsY, + GLuint numGroupsZ) +{ + mCompute.numWorkGroups[0] = numGroupsX; + mCompute.numWorkGroups[1] = numGroupsY; + mCompute.numWorkGroups[2] = numGroupsZ; + mComputeDirty = true; +} + +void ShaderConstants11::setMultiviewWriteToViewportIndex(GLfloat index) +{ + mVertex.multiviewWriteToViewportIndex = index; + mVertexDirty = true; + mPixel.multiviewWriteToViewportIndex = index; + mPixelDirty = true; +} + +void ShaderConstants11::onViewportChange(const gl::Rectangle &glViewport, + const D3D11_VIEWPORT &dxViewport, + bool is9_3, + bool presentPathFast) +{ + mVertexDirty = true; + mPixelDirty = true; + + // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders + // using viewAdjust (like the D3D9 renderer). + if (is9_3) + { + mVertex.viewAdjust[0] = static_cast<float>((glViewport.width - dxViewport.Width) + + 2 * (glViewport.x - dxViewport.TopLeftX)) / + dxViewport.Width; + mVertex.viewAdjust[1] = static_cast<float>((glViewport.height - dxViewport.Height) + + 2 * (glViewport.y - dxViewport.TopLeftY)) / + dxViewport.Height; + mVertex.viewAdjust[2] = static_cast<float>(glViewport.width) / dxViewport.Width; + mVertex.viewAdjust[3] = static_cast<float>(glViewport.height) / dxViewport.Height; + } + + mPixel.viewCoords[0] = glViewport.width * 0.5f; + mPixel.viewCoords[1] = glViewport.height * 0.5f; + mPixel.viewCoords[2] = glViewport.x + (glViewport.width * 0.5f); + mPixel.viewCoords[3] = glViewport.y + (glViewport.height * 0.5f); + + // Instanced pointsprite emulation requires ViewCoords to be defined in the + // the vertex shader. + mVertex.viewCoords[0] = mPixel.viewCoords[0]; + mVertex.viewCoords[1] = mPixel.viewCoords[1]; + mVertex.viewCoords[2] = mPixel.viewCoords[2]; + mVertex.viewCoords[3] = mPixel.viewCoords[3]; + + const float zNear = dxViewport.MinDepth; + const float zFar = dxViewport.MaxDepth; + + mPixel.depthFront[0] = (zFar - zNear) * 0.5f; + mPixel.depthFront[1] = (zNear + zFar) * 0.5f; + + mVertex.depthRange[0] = zNear; + mVertex.depthRange[1] = zFar; + mVertex.depthRange[2] = zFar - zNear; + + mPixel.depthRange[0] = zNear; + mPixel.depthRange[1] = zFar; + mPixel.depthRange[2] = zFar - zNear; + + mPixel.viewScale[0] = 1.0f; + mPixel.viewScale[1] = presentPathFast ? 1.0f : -1.0f; + // Updates to the multiviewWriteToViewportIndex member are to be handled whenever the draw + // framebuffer's layout is changed. + + mVertex.viewScale[0] = mPixel.viewScale[0]; + mVertex.viewScale[1] = mPixel.viewScale[1]; +} + +void ShaderConstants11::onSamplerChange(gl::SamplerType samplerType, + unsigned int samplerIndex, + const gl::Texture &texture) +{ + switch (samplerType) + { + case gl::SAMPLER_VERTEX: + if (updateSamplerMetadata(&mSamplerMetadataVS[samplerIndex], texture)) + { + mSamplerMetadataVSDirty = true; + } + break; + case gl::SAMPLER_PIXEL: + if (updateSamplerMetadata(&mSamplerMetadataPS[samplerIndex], texture)) + { + mSamplerMetadataPSDirty = true; + } + break; + case gl::SAMPLER_COMPUTE: + if (updateSamplerMetadata(&mSamplerMetadataCS[samplerIndex], texture)) + { + mSamplerMetadataCSDirty = true; + } + break; + default: + UNREACHABLE(); + break; + } +} + +gl::Error ShaderConstants11::updateBuffer(ID3D11DeviceContext *deviceContext, + gl::SamplerType samplerType, + const ProgramD3D &programD3D, + const d3d11::Buffer &driverConstantBuffer) +{ + bool dirty = false; + size_t dataSize = 0; + const uint8_t *data = nullptr; + const uint8_t *samplerData = nullptr; + + switch (samplerType) + { + case gl::SAMPLER_VERTEX: + dirty = mVertexDirty || mSamplerMetadataVSDirty; + dataSize = sizeof(Vertex); + data = reinterpret_cast<const uint8_t *>(&mVertex); + samplerData = reinterpret_cast<const uint8_t *>(mSamplerMetadataVS.data()); + mVertexDirty = false; + mSamplerMetadataVSDirty = false; + break; + case gl::SAMPLER_PIXEL: + dirty = mPixelDirty || mSamplerMetadataPSDirty; + dataSize = sizeof(Pixel); + data = reinterpret_cast<const uint8_t *>(&mPixel); + samplerData = reinterpret_cast<const uint8_t *>(mSamplerMetadataPS.data()); + mPixelDirty = false; + mSamplerMetadataPSDirty = false; + break; + case gl::SAMPLER_COMPUTE: + dirty = mComputeDirty || mSamplerMetadataCSDirty; + dataSize = sizeof(Compute); + data = reinterpret_cast<const uint8_t *>(&mCompute); + samplerData = reinterpret_cast<const uint8_t *>(mSamplerMetadataCS.data()); + mComputeDirty = false; + mSamplerMetadataCSDirty = false; + break; + default: + UNREACHABLE(); + break; + } + + ASSERT(driverConstantBuffer.valid()); + + if (!dirty) + { + return gl::NoError(); + } + + // Previous buffer contents are discarded, so we need to refresh the whole buffer. + D3D11_MAPPED_SUBRESOURCE mapping = {0}; + HRESULT result = + deviceContext->Map(driverConstantBuffer.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapping); + + if (FAILED(result)) + { + return gl::OutOfMemory() << "Internal error mapping constant buffer: " << gl::FmtHR(result); + } + + size_t samplerDataBytes = sizeof(SamplerMetadata) * programD3D.getUsedSamplerRange(samplerType); + + memcpy(mapping.pData, data, dataSize); + memcpy(reinterpret_cast<uint8_t *>(mapping.pData) + dataSize, samplerData, samplerDataBytes); + + deviceContext->Unmap(driverConstantBuffer.get(), 0); + + return gl::NoError(); +} + +static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, + GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED_EXT, + GL_COMMANDS_COMPLETED_CHROMIUM}; + StateManager11::StateManager11(Renderer11 *renderer) : mRenderer(renderer), - mBlendStateIsDirty(false), + mInternalDirtyBits(), mCurBlendColor(0, 0, 0, 0), mCurSampleMask(0), - mDepthStencilStateIsDirty(false), mCurStencilRef(0), mCurStencilBackRef(0), mCurStencilSize(0), - mRasterizerStateIsDirty(false), - mScissorStateIsDirty(false), mCurScissorEnabled(false), mCurScissorRect(), - mViewportStateIsDirty(false), mCurViewport(), mCurNear(0.0f), mCurFar(0.0f), mViewportBounds(), + mRenderTargetIsDirty(true), mCurPresentPathFastEnabled(false), mCurPresentPathFastColorBufferHeight(0), - mAppliedDSV(angle::DirtyPointer) + mDirtyCurrentValueAttribs(), + mCurrentValueAttribs(), + mCurrentInputLayout(), + mInputLayoutIsDirty(false), + mVertexAttribsNeedTranslation(false), + mDirtyVertexBufferRange(gl::MAX_VERTEX_ATTRIBS, 0), + mCurrentPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED), + mDirtySwizzles(false), + mAppliedIB(nullptr), + mAppliedIBFormat(DXGI_FORMAT_UNKNOWN), + mAppliedIBOffset(0), + mIndexBufferIsDirty(false), + mVertexDataManager(renderer), + mIndexDataManager(renderer), + mIsMultiviewEnabled(false), + mEmptySerial(mRenderer->generateSerial()), + mIsTransformFeedbackCurrentlyActiveUnpaused(false) { mCurBlendState.blend = false; mCurBlendState.sourceBlendRGB = GL_ONE; @@ -182,64 +601,127 @@ StateManager11::StateManager11(Renderer11 *renderer) mCurRasterState.rasterizerDiscard = false; mCurRasterState.cullFace = false; - mCurRasterState.cullMode = GL_BACK; + mCurRasterState.cullMode = gl::CullFaceMode::Back; mCurRasterState.frontFace = GL_CCW; mCurRasterState.polygonOffsetFill = false; mCurRasterState.polygonOffsetFactor = 0.0f; mCurRasterState.polygonOffsetUnits = 0.0f; mCurRasterState.pointDrawMode = false; mCurRasterState.multiSample = false; + + // Start with all internal dirty bits set. + mInternalDirtyBits.set(); + + // Initially all current value attributes must be updated on first use. + mDirtyCurrentValueAttribs.set(); + + mCurrentVertexBuffers.fill(nullptr); + mCurrentVertexStrides.fill(std::numeric_limits<UINT>::max()); + mCurrentVertexOffsets.fill(std::numeric_limits<UINT>::max()); } StateManager11::~StateManager11() { } -void StateManager11::updateStencilSizeIfChanged(bool depthStencilInitialized, - unsigned int stencilSize) +template <typename SRVType> +void StateManager11::setShaderResourceInternal(gl::SamplerType shaderType, + UINT resourceSlot, + const SRVType *srv) { - if (!depthStencilInitialized || stencilSize != mCurStencilSize) + auto ¤tSRVs = (shaderType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + + ASSERT(static_cast<size_t>(resourceSlot) < currentSRVs.size()); + const SRVRecord &record = currentSRVs[resourceSlot]; + + if (record.srv != reinterpret_cast<uintptr_t>(srv)) { - mCurStencilSize = stencilSize; - mDepthStencilStateIsDirty = true; + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + ID3D11ShaderResourceView *srvPtr = srv ? srv->get() : nullptr; + if (shaderType == gl::SAMPLER_VERTEX) + { + deviceContext->VSSetShaderResources(resourceSlot, 1, &srvPtr); + } + else + { + deviceContext->PSSetShaderResources(resourceSlot, 1, &srvPtr); + } + + currentSRVs.update(resourceSlot, srvPtr); } } -void StateManager11::setViewportBounds(const int width, const int height) +void StateManager11::updateStencilSizeIfChanged(bool depthStencilInitialized, + unsigned int stencilSize) { - if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3 && - (mViewportBounds.width != width || mViewportBounds.height != height)) + if (!depthStencilInitialized || stencilSize != mCurStencilSize) { - mViewportBounds = gl::Extents(width, height, 1); - mViewportStateIsDirty = true; + mCurStencilSize = stencilSize; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } } -void StateManager11::updatePresentPath(bool presentPathFastActive, - const gl::FramebufferAttachment *framebufferAttachment) +void StateManager11::checkPresentPath(const gl::Context *context) { - const int colorBufferHeight = - framebufferAttachment ? framebufferAttachment->getSize().height : 0; + if (!mRenderer->presentPathFastEnabled()) + return; + + const auto *framebuffer = context->getGLState().getDrawFramebuffer(); + const auto *firstColorAttachment = framebuffer->getFirstColorbuffer(); + const bool presentPathFastActive = UsePresentPathFast(mRenderer, firstColorAttachment); + + const int colorBufferHeight = firstColorAttachment ? firstColorAttachment->getSize().height : 0; if ((mCurPresentPathFastEnabled != presentPathFastActive) || (presentPathFastActive && (colorBufferHeight != mCurPresentPathFastColorBufferHeight))) { mCurPresentPathFastEnabled = presentPathFastActive; mCurPresentPathFastColorBufferHeight = colorBufferHeight; - mViewportStateIsDirty = true; // Viewport may need to be vertically inverted - mScissorStateIsDirty = true; // Scissor rect may need to be vertically inverted - mRasterizerStateIsDirty = true; // Cull Mode may need to be inverted + + // Scissor rect may need to be vertically inverted + mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); + + // Cull Mode may need to be inverted + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); + + // Viewport may need to be vertically inverted + invalidateViewport(context); } } -void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) +gl::Error StateManager11::updateStateForCompute(const gl::Context *context, + GLuint numGroupsX, + GLuint numGroupsY, + GLuint numGroupsZ) +{ + mShaderConstants.setComputeWorkGroups(numGroupsX, numGroupsY, numGroupsZ); + + // TODO(jmadill): Use dirty bits. + const auto &glState = context->getGLState(); + auto *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + programD3D->updateSamplerMapping(); + + // TODO(jmadill): Use dirty bits. + ANGLE_TRY(generateSwizzlesForShader(context, gl::SAMPLER_COMPUTE)); + + // TODO(jmadill): More complete implementation. + ANGLE_TRY(syncTextures(context)); + + // TODO(Xinghua): applyUniformBuffers for compute shader. + + return gl::NoError(); +} + +void StateManager11::syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits) { if (!dirtyBits.any()) { return; } - for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits)) + const auto &state = context->getGLState(); + + for (auto dirtyBit : dirtyBits) { switch (dirtyBit) { @@ -249,7 +731,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit if (blendState.blendEquationRGB != mCurBlendState.blendEquationRGB || blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; } @@ -261,27 +743,27 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit blendState.sourceBlendAlpha != mCurBlendState.sourceBlendAlpha || blendState.destBlendAlpha != mCurBlendState.destBlendAlpha) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; } case gl::State::DIRTY_BIT_BLEND_ENABLED: if (state.getBlendState().blend != mCurBlendState.blend) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: if (state.getBlendState().sampleAlphaToCoverage != mCurBlendState.sampleAlphaToCoverage) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; case gl::State::DIRTY_BIT_DITHER_ENABLED: if (state.getBlendState().dither != mCurBlendState.dither) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; case gl::State::DIRTY_BIT_COLOR_MASK: @@ -292,38 +774,38 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit blendState.colorMaskBlue != mCurBlendState.colorMaskBlue || blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; } case gl::State::DIRTY_BIT_BLEND_COLOR: if (state.getBlendColor() != mCurBlendColor) { - mBlendStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); } break; case gl::State::DIRTY_BIT_DEPTH_MASK: if (state.getDepthStencilState().depthMask != mCurDepthStencilState.depthMask) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: if (state.getDepthStencilState().depthTest != mCurDepthStencilState.depthTest) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_DEPTH_FUNC: if (state.getDepthStencilState().depthFunc != mCurDepthStencilState.depthFunc) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: if (state.getDepthStencilState().stencilTest != mCurDepthStencilState.stencilTest) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: @@ -333,7 +815,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit depthStencil.stencilMask != mCurDepthStencilState.stencilMask || state.getStencilRef() != mCurStencilRef) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; } @@ -344,7 +826,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit depthStencil.stencilBackMask != mCurDepthStencilState.stencilBackMask || state.getStencilBackRef() != mCurStencilBackRef) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; } @@ -352,14 +834,14 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit if (state.getDepthStencilState().stencilWritemask != mCurDepthStencilState.stencilWritemask) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: if (state.getDepthStencilState().stencilBackWritemask != mCurDepthStencilState.stencilBackWritemask) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: @@ -370,7 +852,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit mCurDepthStencilState.stencilPassDepthFail || depthStencil.stencilPassDepthPass != mCurDepthStencilState.stencilPassDepthPass) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; } @@ -383,33 +865,33 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit depthStencil.stencilBackPassDepthPass != mCurDepthStencilState.stencilBackPassDepthPass) { - mDepthStencilStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); } break; } case gl::State::DIRTY_BIT_CULL_FACE_ENABLED: if (state.getRasterizerState().cullFace != mCurRasterState.cullFace) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; case gl::State::DIRTY_BIT_CULL_FACE: if (state.getRasterizerState().cullMode != mCurRasterState.cullMode) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; case gl::State::DIRTY_BIT_FRONT_FACE: if (state.getRasterizerState().frontFace != mCurRasterState.frontFace) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: if (state.getRasterizerState().polygonOffsetFill != mCurRasterState.polygonOffsetFill) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; case gl::State::DIRTY_BIT_POLYGON_OFFSET: @@ -418,7 +900,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit if (rasterState.polygonOffsetFactor != mCurRasterState.polygonOffsetFactor || rasterState.polygonOffsetUnits != mCurRasterState.polygonOffsetUnits) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; } @@ -426,58 +908,150 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit if (state.getRasterizerState().rasterizerDiscard != mCurRasterState.rasterizerDiscard) { - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); + + // Enabling/disabling rasterizer discard affects the pixel shader. + invalidateShaders(); } break; case gl::State::DIRTY_BIT_SCISSOR: if (state.getScissor() != mCurScissorRect) { - mScissorStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); } break; case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED: if (state.isScissorTestEnabled() != mCurScissorEnabled) { - mScissorStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); // Rasterizer state update needs mCurScissorsEnabled and updates when it changes - mRasterizerStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); } break; case gl::State::DIRTY_BIT_DEPTH_RANGE: if (state.getNearPlane() != mCurNear || state.getFarPlane() != mCurFar) { - mViewportStateIsDirty = true; + invalidateViewport(context); } break; case gl::State::DIRTY_BIT_VIEWPORT: if (state.getViewport() != mCurViewport) { - mViewportStateIsDirty = true; + invalidateViewport(context); + } + break; + case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: + invalidateRenderTarget(); + if (mIsMultiviewEnabled) + { + handleMultiviewDrawFramebufferChange(context); } break; + case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: + invalidateVertexBuffer(); + // Force invalidate the current value attributes, since the VertexArray11 keeps an + // internal cache of TranslatedAttributes, and they CurrentValue attributes are + // owned by the StateManager11/Context. + mDirtyCurrentValueAttribs.set(); + // Invalidate the cached index buffer. + mIndexBufferIsDirty = true; + break; + case gl::State::DIRTY_BIT_TEXTURE_BINDINGS: + invalidateTexturesAndSamplers(); + break; + case gl::State::DIRTY_BIT_SAMPLER_BINDINGS: + invalidateTexturesAndSamplers(); + break; + case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: + { + mInternalDirtyBits.set(DIRTY_BIT_SHADERS); + invalidateVertexBuffer(); + invalidateRenderTarget(); + invalidateTexturesAndSamplers(); + invalidateProgramUniforms(); + invalidateProgramUniformBuffers(); + gl::VertexArray *vao = state.getVertexArray(); + if (mIsMultiviewEnabled && vao != nullptr) + { + // If ANGLE_multiview is enabled, the attribute divisor has to be updated for + // each binding. + VertexArray11 *vao11 = GetImplAs<VertexArray11>(vao); + const gl::Program *program = state.getProgram(); + int numViews = 1; + if (program != nullptr && program->usesMultiview()) + { + numViews = program->getNumViews(); + } + vao11->markAllAttributeDivisorsForAdjustment(numViews); + } + break; + } + case gl::State::DIRTY_BIT_CURRENT_VALUES: + { + for (auto attribIndex : state.getAndResetDirtyCurrentValues()) + { + invalidateCurrentValueAttrib(attribIndex); + } + } default: break; } } + + // TODO(jmadill): Input layout and vertex buffer state. } -gl::Error StateManager11::setBlendState(const gl::Framebuffer *framebuffer, - const gl::BlendState &blendState, - const gl::ColorF &blendColor, - unsigned int sampleMask) +void StateManager11::handleMultiviewDrawFramebufferChange(const gl::Context *context) { - if (!mBlendStateIsDirty && sampleMask == mCurSampleMask) + const auto &glState = context->getGLState(); + const gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer(); + ASSERT(drawFramebuffer != nullptr); + + // Update viewport offsets. + const std::vector<gl::Offset> *attachmentViewportOffsets = + drawFramebuffer->getViewportOffsets(); + const std::vector<gl::Offset> &viewportOffsets = + attachmentViewportOffsets != nullptr + ? *attachmentViewportOffsets + : gl::FramebufferAttachment::GetDefaultViewportOffsetVector(); + if (mViewportOffsets != viewportOffsets) { - return gl::Error(GL_NO_ERROR); - } + mViewportOffsets = viewportOffsets; - ID3D11BlendState *dxBlendState = nullptr; - gl::Error error = - mRenderer->getStateCache().getBlendState(framebuffer, blendState, &dxBlendState); - if (error.isError()) + // Because new viewport offsets are to be applied, we have to mark the internal viewport and + // scissor state as dirty. + invalidateViewport(context); + mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); + } + switch (drawFramebuffer->getMultiviewLayout()) { - return error; + case GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE: + mShaderConstants.setMultiviewWriteToViewportIndex(1.0f); + break; + case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE: + // Because the base view index is applied as an offset to the 2D texture array when the + // RTV is created, we just have to pass a boolean to select which code path is to be + // used. + mShaderConstants.setMultiviewWriteToViewportIndex(0.0f); + break; + default: + // There is no need to update the value in the constant buffer if the active framebuffer + // object does not have a multiview layout. + break; } +} + +gl::Error StateManager11::syncBlendState(const gl::Context *context, + const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, + unsigned int sampleMask) +{ + const d3d11::BlendState *dxBlendState = nullptr; + const d3d11::BlendStateKey &key = + RenderStateCache::GetBlendStateKey(context, framebuffer, blendState); + + ANGLE_TRY(mRenderer->getBlendState(key, &dxBlendState)); ASSERT(dxBlendState != nullptr); @@ -500,62 +1074,53 @@ gl::Error StateManager11::setBlendState(const gl::Framebuffer *framebuffer, blendColors[3] = blendColor.alpha; } - mRenderer->getDeviceContext()->OMSetBlendState(dxBlendState, blendColors, sampleMask); + mRenderer->getDeviceContext()->OMSetBlendState(dxBlendState->get(), blendColors, sampleMask); mCurBlendState = blendState; mCurBlendColor = blendColor; mCurSampleMask = sampleMask; - mBlendStateIsDirty = false; - - return error; + return gl::NoError(); } -gl::Error StateManager11::setDepthStencilState(const gl::State &glState) +gl::Error StateManager11::syncDepthStencilState(const gl::State &glState) { - const auto &fbo = *glState.getDrawFramebuffer(); - - // Disable the depth test/depth write if we are using a stencil-only attachment. - // This is because ANGLE emulates stencil-only with D24S8 on D3D11 - we should neither read - // nor write to the unused depth part of this emulated texture. - bool disableDepth = (!fbo.hasDepth() && fbo.hasStencil()); - - // Similarly we disable the stencil portion of the DS attachment if the app only binds depth. - bool disableStencil = (fbo.hasDepth() && !fbo.hasStencil()); + mCurDepthStencilState = glState.getDepthStencilState(); + mCurStencilRef = glState.getStencilRef(); + mCurStencilBackRef = glState.getStencilBackRef(); - // CurDisableDepth/Stencil are reset automatically after we call forceSetDepthStencilState. - if (!mDepthStencilStateIsDirty && mCurDisableDepth.valid() && - disableDepth == mCurDisableDepth.value() && mCurDisableStencil.valid() && - disableStencil == mCurDisableStencil.value()) + // get the maximum size of the stencil ref + unsigned int maxStencil = 0; + if (mCurDepthStencilState.stencilTest && mCurStencilSize > 0) { - return gl::Error(GL_NO_ERROR); + maxStencil = (1 << mCurStencilSize) - 1; } + ASSERT((mCurDepthStencilState.stencilWritemask & maxStencil) == + (mCurDepthStencilState.stencilBackWritemask & maxStencil)); + ASSERT(mCurStencilRef == mCurStencilBackRef); + ASSERT((mCurDepthStencilState.stencilMask & maxStencil) == + (mCurDepthStencilState.stencilBackMask & maxStencil)); - const auto &depthStencilState = glState.getDepthStencilState(); - int stencilRef = glState.getStencilRef(); - int stencilBackRef = glState.getStencilBackRef(); + gl::DepthStencilState modifiedGLState = glState.getDepthStencilState(); - // get the maximum size of the stencil ref - unsigned int maxStencil = 0; - if (depthStencilState.stencilTest && mCurStencilSize > 0) + ASSERT(mCurDisableDepth.valid() && mCurDisableStencil.valid()); + + if (mCurDisableDepth.value()) { - maxStencil = (1 << mCurStencilSize) - 1; + modifiedGLState.depthTest = false; + modifiedGLState.depthMask = false; } - ASSERT((depthStencilState.stencilWritemask & maxStencil) == - (depthStencilState.stencilBackWritemask & maxStencil)); - ASSERT(stencilRef == stencilBackRef); - ASSERT((depthStencilState.stencilMask & maxStencil) == - (depthStencilState.stencilBackMask & maxStencil)); - ID3D11DepthStencilState *dxDepthStencilState = NULL; - gl::Error error = mRenderer->getStateCache().getDepthStencilState( - depthStencilState, disableDepth, disableStencil, &dxDepthStencilState); - if (error.isError()) + if (mCurDisableStencil.value()) { - return error; + modifiedGLState.stencilWritemask = 0; + modifiedGLState.stencilBackWritemask = 0; + modifiedGLState.stencilTest = false; } - ASSERT(dxDepthStencilState); + const d3d11::DepthStencilState *d3dState = nullptr; + ANGLE_TRY(mRenderer->getDepthStencilState(modifiedGLState, &d3dState)); + ASSERT(d3dState); // Max D3D11 stencil reference value is 0xFF, // corresponding to the max 8 bits in a stencil buffer @@ -565,30 +1130,21 @@ gl::Error StateManager11::setDepthStencilState(const gl::State &glState) "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK"); static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF, "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK"); - UINT dxStencilRef = std::min<UINT>(stencilRef, 0xFFu); + UINT dxStencilRef = std::min<UINT>(mCurStencilRef, 0xFFu); - mRenderer->getDeviceContext()->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef); + mRenderer->getDeviceContext()->OMSetDepthStencilState(d3dState->get(), dxStencilRef); - mCurDepthStencilState = depthStencilState; - mCurStencilRef = stencilRef; - mCurStencilBackRef = stencilBackRef; - mCurDisableDepth = disableDepth; - mCurDisableStencil = disableStencil; - - mDepthStencilStateIsDirty = false; - - return gl::Error(GL_NO_ERROR); + return gl::NoError(); } -gl::Error StateManager11::setRasterizerState(const gl::RasterizerState &rasterState) +gl::Error StateManager11::syncRasterizerState(const gl::Context *context, bool pointDrawMode) { - if (!mRasterizerStateIsDirty) - { - return gl::Error(GL_NO_ERROR); - } + // TODO: Remove pointDrawMode and multiSample from gl::RasterizerState. + gl::RasterizerState rasterState = context->getGLState().getRasterizerState(); + rasterState.pointDrawMode = pointDrawMode; + rasterState.multiSample = mCurRasterState.multiSample; ID3D11RasterizerState *dxRasterState = nullptr; - gl::Error error(GL_NO_ERROR); if (mCurPresentPathFastEnabled) { @@ -607,33 +1163,23 @@ gl::Error StateManager11::setRasterizerState(const gl::RasterizerState &rasterSt modifiedRasterState.frontFace = GL_CCW; } - error = mRenderer->getStateCache().getRasterizerState(modifiedRasterState, - mCurScissorEnabled, &dxRasterState); + ANGLE_TRY( + mRenderer->getRasterizerState(modifiedRasterState, mCurScissorEnabled, &dxRasterState)); } else { - error = mRenderer->getStateCache().getRasterizerState(rasterState, mCurScissorEnabled, - &dxRasterState); - } - - if (error.isError()) - { - return error; + ANGLE_TRY(mRenderer->getRasterizerState(rasterState, mCurScissorEnabled, &dxRasterState)); } mRenderer->getDeviceContext()->RSSetState(dxRasterState); - mCurRasterState = rasterState; - mRasterizerStateIsDirty = false; + mCurRasterState = rasterState; - return error; + return gl::NoError(); } -void StateManager11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled) +void StateManager11::syncScissorRectangle(const gl::Rectangle &scissor, bool enabled) { - if (!mScissorStateIsDirty) - return; - int modifiedScissorY = scissor.y; if (mCurPresentPathFastEnabled) { @@ -642,37 +1188,41 @@ void StateManager11::setScissorRectangle(const gl::Rectangle &scissor, bool enab if (enabled) { - D3D11_RECT rect; - rect.left = std::max(0, scissor.x); - rect.top = std::max(0, modifiedScissorY); - rect.right = scissor.x + std::max(0, scissor.width); - rect.bottom = modifiedScissorY + std::max(0, scissor.height); - - mRenderer->getDeviceContext()->RSSetScissorRects(1, &rect); + std::array<D3D11_RECT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> rectangles; + const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size()); + for (UINT i = 0u; i < numRectangles; ++i) + { + D3D11_RECT &rect = rectangles[i]; + int x = scissor.x + mViewportOffsets[i].x; + int y = modifiedScissorY + mViewportOffsets[i].y; + rect.left = std::max(0, x); + rect.top = std::max(0, y); + rect.right = x + std::max(0, scissor.width); + rect.bottom = y + std::max(0, scissor.height); + } + mRenderer->getDeviceContext()->RSSetScissorRects(numRectangles, rectangles.data()); } mCurScissorRect = scissor; mCurScissorEnabled = enabled; - mScissorStateIsDirty = false; } -void StateManager11::setViewport(const gl::Caps *caps, - const gl::Rectangle &viewport, - float zNear, - float zFar) +void StateManager11::syncViewport(const gl::Context *context) { - if (!mViewportStateIsDirty) - return; - - float actualZNear = gl::clamp01(zNear); - float actualZFar = gl::clamp01(zFar); - - int dxMaxViewportBoundsX = static_cast<int>(caps->maxViewportWidth); - int dxMaxViewportBoundsY = static_cast<int>(caps->maxViewportHeight); + const auto &glState = context->getGLState(); + gl::Framebuffer *framebuffer = glState.getDrawFramebuffer(); + float actualZNear = gl::clamp01(glState.getNearPlane()); + float actualZFar = gl::clamp01(glState.getFarPlane()); + + const auto &caps = context->getCaps(); + int dxMaxViewportBoundsX = static_cast<int>(caps.maxViewportWidth); + int dxMaxViewportBoundsY = static_cast<int>(caps.maxViewportHeight); int dxMinViewportBoundsX = -dxMaxViewportBoundsX; int dxMinViewportBoundsY = -dxMaxViewportBoundsY; - if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) + bool is9_3 = mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3; + + if (is9_3) { // Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget. dxMaxViewportBoundsX = static_cast<int>(mViewportBounds.width); @@ -681,173 +1231,295 @@ void StateManager11::setViewport(const gl::Caps *caps, dxMinViewportBoundsY = 0; } - int dxViewportTopLeftX = gl::clamp(viewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX); - int dxViewportTopLeftY = gl::clamp(viewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY); - int dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); - int dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); + const auto &viewport = glState.getViewport(); + std::array<D3D11_VIEWPORT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> dxViewports; + const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size()); - D3D11_VIEWPORT dxViewport; - dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX); + int dxViewportTopLeftX = 0; + int dxViewportTopLeftY = 0; + int dxViewportWidth = 0; + int dxViewportHeight = 0; - if (mCurPresentPathFastEnabled) - { - // When present path fast is active and we're rendering to framebuffer 0, we must invert - // the viewport in Y-axis. - // NOTE: We delay the inversion until right before the call to RSSetViewports, and leave - // dxViewportTopLeftY unchanged. This allows us to calculate viewAdjust below using the - // unaltered dxViewportTopLeftY value. - dxViewport.TopLeftY = static_cast<float>(mCurPresentPathFastColorBufferHeight - - dxViewportTopLeftY - dxViewportHeight); - } - else + for (UINT i = 0u; i < numRectangles; ++i) { - dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY); - } + dxViewportTopLeftX = gl::clamp(viewport.x + mViewportOffsets[i].x, dxMinViewportBoundsX, + dxMaxViewportBoundsX); + dxViewportTopLeftY = gl::clamp(viewport.y + mViewportOffsets[i].y, dxMinViewportBoundsY, + dxMaxViewportBoundsY); + dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); + dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); + + D3D11_VIEWPORT &dxViewport = dxViewports[i]; + dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX); + if (mCurPresentPathFastEnabled) + { + // When present path fast is active and we're rendering to framebuffer 0, we must invert + // the viewport in Y-axis. + // NOTE: We delay the inversion until right before the call to RSSetViewports, and leave + // dxViewportTopLeftY unchanged. This allows us to calculate viewAdjust below using the + // unaltered dxViewportTopLeftY value. + dxViewport.TopLeftY = static_cast<float>(mCurPresentPathFastColorBufferHeight - + dxViewportTopLeftY - dxViewportHeight); + } + else + { + dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY); + } - dxViewport.Width = static_cast<float>(dxViewportWidth); - dxViewport.Height = static_cast<float>(dxViewportHeight); - dxViewport.MinDepth = actualZNear; - dxViewport.MaxDepth = actualZFar; + // The es 3.1 spec section 9.2 states that, "If there are no attachments, rendering + // will be limited to a rectangle having a lower left of (0, 0) and an upper right of + // (width, height), where width and height are the framebuffer object's default width + // and height." See http://anglebug.com/1594 + // If the Framebuffer has no color attachment and the default width or height is smaller + // than the current viewport, use the smaller of the two sizes. + // If framebuffer default width or height is 0, the params should not set. + if (!framebuffer->getFirstNonNullAttachment() && + (framebuffer->getDefaultWidth() || framebuffer->getDefaultHeight())) + { + dxViewport.Width = + static_cast<GLfloat>(std::min(viewport.width, framebuffer->getDefaultWidth())); + dxViewport.Height = + static_cast<GLfloat>(std::min(viewport.height, framebuffer->getDefaultHeight())); + } + else + { + dxViewport.Width = static_cast<float>(dxViewportWidth); + dxViewport.Height = static_cast<float>(dxViewportHeight); + } + dxViewport.MinDepth = actualZNear; + dxViewport.MaxDepth = actualZFar; + } - mRenderer->getDeviceContext()->RSSetViewports(1, &dxViewport); + mRenderer->getDeviceContext()->RSSetViewports(numRectangles, dxViewports.data()); mCurViewport = viewport; mCurNear = actualZNear; mCurFar = actualZFar; - // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders - // using viewAdjust (like the D3D9 renderer). + const D3D11_VIEWPORT adjustViewport = {static_cast<FLOAT>(dxViewportTopLeftX), + static_cast<FLOAT>(dxViewportTopLeftY), + static_cast<FLOAT>(dxViewportWidth), + static_cast<FLOAT>(dxViewportHeight), + actualZNear, + actualZFar}; + mShaderConstants.onViewportChange(viewport, adjustViewport, is9_3, mCurPresentPathFastEnabled); +} + +void StateManager11::invalidateRenderTarget() +{ + mRenderTargetIsDirty = true; +} + +void StateManager11::processFramebufferInvalidation(const gl::Context *context) +{ + if (!mRenderTargetIsDirty) + { + return; + } + + ASSERT(context); + + mRenderTargetIsDirty = false; + mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); + + // The pixel shader is dependent on the output layout. + invalidateShaders(); + + // The D3D11 blend state is heavily dependent on the current render target. + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); + + gl::Framebuffer *fbo = context->getGLState().getDrawFramebuffer(); + ASSERT(fbo); + + // Disable the depth test/depth write if we are using a stencil-only attachment. + // This is because ANGLE emulates stencil-only with D24S8 on D3D11 - we should neither read + // nor write to the unused depth part of this emulated texture. + bool disableDepth = (!fbo->hasDepth() && fbo->hasStencil()); + + // Similarly we disable the stencil portion of the DS attachment if the app only binds depth. + bool disableStencil = (fbo->hasDepth() && !fbo->hasStencil()); + + if (!mCurDisableDepth.valid() || disableDepth != mCurDisableDepth.value() || + !mCurDisableStencil.valid() || disableStencil != mCurDisableStencil.value()) + { + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); + mCurDisableDepth = disableDepth; + mCurDisableStencil = disableStencil; + } + + bool multiSample = (fbo->getCachedSamples(context) != 0); + if (multiSample != mCurRasterState.multiSample) + { + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); + mCurRasterState.multiSample = multiSample; + } + + checkPresentPath(context); + if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) { - mVertexConstants.viewAdjust[0] = static_cast<float>((viewport.width - dxViewportWidth) + - 2 * (viewport.x - dxViewportTopLeftX)) / - dxViewport.Width; - mVertexConstants.viewAdjust[1] = static_cast<float>((viewport.height - dxViewportHeight) + - 2 * (viewport.y - dxViewportTopLeftY)) / - dxViewport.Height; - mVertexConstants.viewAdjust[2] = static_cast<float>(viewport.width) / dxViewport.Width; - mVertexConstants.viewAdjust[3] = static_cast<float>(viewport.height) / dxViewport.Height; + const auto *firstAttachment = fbo->getFirstNonNullAttachment(); + if (firstAttachment) + { + const auto &size = firstAttachment->getSize(); + if (mViewportBounds.width != size.width || mViewportBounds.height != size.height) + { + mViewportBounds = gl::Extents(size.width, size.height, 1); + invalidateViewport(context); + } + } } +} - mPixelConstants.viewCoords[0] = viewport.width * 0.5f; - mPixelConstants.viewCoords[1] = viewport.height * 0.5f; - mPixelConstants.viewCoords[2] = viewport.x + (viewport.width * 0.5f); - mPixelConstants.viewCoords[3] = viewport.y + (viewport.height * 0.5f); +void StateManager11::invalidateBoundViews() +{ + mCurVertexSRVs.clear(); + mCurPixelSRVs.clear(); - // Instanced pointsprite emulation requires ViewCoords to be defined in the - // the vertex shader. - mVertexConstants.viewCoords[0] = mPixelConstants.viewCoords[0]; - mVertexConstants.viewCoords[1] = mPixelConstants.viewCoords[1]; - mVertexConstants.viewCoords[2] = mPixelConstants.viewCoords[2]; - mVertexConstants.viewCoords[3] = mPixelConstants.viewCoords[3]; + invalidateRenderTarget(); +} - mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f; - mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f; +void StateManager11::invalidateVertexBuffer() +{ + unsigned int limit = std::min<unsigned int>(mRenderer->getNativeCaps().maxVertexAttributes, + gl::MAX_VERTEX_ATTRIBS); + mDirtyVertexBufferRange = gl::RangeUI(0, limit); + mInputLayoutIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_ATTRIBS); + invalidateVertexAttributeTranslation(); +} - mVertexConstants.depthRange[0] = actualZNear; - mVertexConstants.depthRange[1] = actualZFar; - mVertexConstants.depthRange[2] = actualZFar - actualZNear; +void StateManager11::invalidateViewport(const gl::Context *context) +{ + mInternalDirtyBits.set(DIRTY_BIT_VIEWPORT_STATE); - mPixelConstants.depthRange[0] = actualZNear; - mPixelConstants.depthRange[1] = actualZFar; - mPixelConstants.depthRange[2] = actualZFar - actualZNear; + // Viewport affects the driver constants. + invalidateDriverUniforms(); +} - mPixelConstants.viewScale[0] = 1.0f; - mPixelConstants.viewScale[1] = mCurPresentPathFastEnabled ? 1.0f : -1.0f; - mPixelConstants.viewScale[2] = 1.0f; - mPixelConstants.viewScale[3] = 1.0f; +void StateManager11::invalidateTexturesAndSamplers() +{ + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); + invalidateSwizzles(); - mVertexConstants.viewScale[0] = mPixelConstants.viewScale[0]; - mVertexConstants.viewScale[1] = mPixelConstants.viewScale[1]; - mVertexConstants.viewScale[2] = mPixelConstants.viewScale[2]; - mVertexConstants.viewScale[3] = mPixelConstants.viewScale[3]; + // Texture state affects the driver uniforms (base level, etc). + invalidateDriverUniforms(); +} - mViewportStateIsDirty = false; +void StateManager11::invalidateSwizzles() +{ + mDirtySwizzles = true; } -void StateManager11::invalidateRenderTarget() +void StateManager11::invalidateProgramUniforms() +{ + mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_UNIFORMS); +} + +void StateManager11::invalidateDriverUniforms() +{ + mInternalDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS); +} + +void StateManager11::invalidateProgramUniformBuffers() +{ + mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS); +} + +void StateManager11::invalidateConstantBuffer(unsigned int slot) { - for (auto &appliedRTV : mAppliedRTVs) + if (slot == d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER) { - appliedRTV = angle::DirtyPointer; + invalidateDriverUniforms(); + } + else if (slot == d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK) + { + invalidateProgramUniforms(); + } + else + { + invalidateProgramUniformBuffers(); } - mAppliedDSV = angle::DirtyPointer; } -void StateManager11::invalidateEverything() +void StateManager11::invalidateShaders() { - mBlendStateIsDirty = true; - mDepthStencilStateIsDirty = true; - mRasterizerStateIsDirty = true; - mScissorStateIsDirty = true; - mViewportStateIsDirty = true; + mInternalDirtyBits.set(DIRTY_BIT_SHADERS); +} - // We reset the current SRV data because it might not be in sync with D3D's state - // anymore. For example when a currently used SRV is used as an RTV, D3D silently - // remove it from its state. - mCurVertexSRVs.clear(); - mCurPixelSRVs.clear(); +void StateManager11::setRenderTarget(ID3D11RenderTargetView *rtv, ID3D11DepthStencilView *dsv) +{ + if ((rtv && unsetConflictingView(rtv)) || (dsv && unsetConflictingView(dsv))) + { + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); + } - invalidateRenderTarget(); + mRenderer->getDeviceContext()->OMSetRenderTargets(1, &rtv, dsv); + mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); } -bool StateManager11::setRenderTargets(const RenderTargetArray &renderTargets, - ID3D11DepthStencilView *depthStencil) +void StateManager11::setRenderTargets(ID3D11RenderTargetView **rtvs, + UINT numRTVs, + ID3D11DepthStencilView *dsv) { - // TODO(jmadill): Use context caps? - UINT drawBuffers = mRenderer->getRendererCaps().maxDrawBuffers; + bool anyDirty = false; - // Apply the render target and depth stencil - size_t arraySize = sizeof(uintptr_t) * drawBuffers; - if (memcmp(renderTargets.data(), mAppliedRTVs.data(), arraySize) == 0 && - reinterpret_cast<uintptr_t>(depthStencil) == mAppliedDSV) + for (UINT rtvIndex = 0; rtvIndex < numRTVs; ++rtvIndex) { - return false; + anyDirty = anyDirty || unsetConflictingView(rtvs[rtvIndex]); } - // The D3D11 blend state is heavily dependent on the current render target. - mBlendStateIsDirty = true; + if (dsv) + { + anyDirty = anyDirty || unsetConflictingView(dsv); + } - for (UINT rtIndex = 0; rtIndex < drawBuffers; rtIndex++) + if (anyDirty) { - mAppliedRTVs[rtIndex] = reinterpret_cast<uintptr_t>(renderTargets[rtIndex]); + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); } - mAppliedDSV = reinterpret_cast<uintptr_t>(depthStencil); - mRenderer->getDeviceContext()->OMSetRenderTargets(drawBuffers, renderTargets.data(), - depthStencil); - return true; + mRenderer->getDeviceContext()->OMSetRenderTargets(numRTVs, (numRTVs > 0) ? rtvs : nullptr, dsv); + mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); } -void StateManager11::setRenderTarget(ID3D11RenderTargetView *renderTarget, - ID3D11DepthStencilView *depthStencil) +void StateManager11::invalidateVertexAttributeTranslation() { - mRenderer->getDeviceContext()->OMSetRenderTargets(1, &renderTarget, depthStencil); + mVertexAttribsNeedTranslation = true; } -void StateManager11::setShaderResource(gl::SamplerType shaderType, - UINT resourceSlot, - ID3D11ShaderResourceView *srv) +void StateManager11::onBeginQuery(Query11 *query) { - auto ¤tSRVs = (shaderType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + mCurrentQueries.insert(query); +} - ASSERT(static_cast<size_t>(resourceSlot) < currentSRVs.size()); - const SRVRecord &record = currentSRVs[resourceSlot]; +void StateManager11::onDeleteQueryObject(Query11 *query) +{ + mCurrentQueries.erase(query); +} - if (record.srv != reinterpret_cast<uintptr_t>(srv)) +gl::Error StateManager11::onMakeCurrent(const gl::Context *context) +{ + const gl::State &state = context->getGLState(); + + for (Query11 *query : mCurrentQueries) { - auto deviceContext = mRenderer->getDeviceContext(); - if (shaderType == gl::SAMPLER_VERTEX) - { - deviceContext->VSSetShaderResources(resourceSlot, 1, &srv); - } - else + ANGLE_TRY(query->pause()); + } + mCurrentQueries.clear(); + + for (GLenum queryType : QueryTypes) + { + gl::Query *query = state.getActiveQuery(queryType); + if (query != nullptr) { - deviceContext->PSSetShaderResources(resourceSlot, 1, &srv); + Query11 *query11 = GetImplAs<Query11>(query); + ANGLE_TRY(query11->resume()); + mCurrentQueries.insert(query11); } - - currentSRVs.update(resourceSlot, srv); } + + return gl::NoError(); } gl::Error StateManager11::clearTextures(gl::SamplerType samplerType, @@ -856,185 +1528,1548 @@ gl::Error StateManager11::clearTextures(gl::SamplerType samplerType, { if (rangeStart == rangeEnd) { - return gl::Error(GL_NO_ERROR); + return gl::NoError(); } auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); - gl::Range<size_t> clearRange(rangeStart, rangeStart); - clearRange.extend(std::min(rangeEnd, currentSRVs.highestUsed())); - + gl::Range<size_t> clearRange(rangeStart, std::min(rangeEnd, currentSRVs.highestUsed())); if (clearRange.empty()) { - return gl::Error(GL_NO_ERROR); + return gl::NoError(); } - auto deviceContext = mRenderer->getDeviceContext(); + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); if (samplerType == gl::SAMPLER_VERTEX) { - deviceContext->VSSetShaderResources(static_cast<unsigned int>(rangeStart), - static_cast<unsigned int>(rangeEnd - rangeStart), + deviceContext->VSSetShaderResources(static_cast<unsigned int>(clearRange.low()), + static_cast<unsigned int>(clearRange.length()), &mNullSRVs[0]); } else { - deviceContext->PSSetShaderResources(static_cast<unsigned int>(rangeStart), - static_cast<unsigned int>(rangeEnd - rangeStart), + deviceContext->PSSetShaderResources(static_cast<unsigned int>(clearRange.low()), + static_cast<unsigned int>(clearRange.length()), &mNullSRVs[0]); } - for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; ++samplerIndex) + for (size_t samplerIndex : clearRange) { currentSRVs.update(samplerIndex, nullptr); } - return gl::Error(GL_NO_ERROR); + return gl::NoError(); } -void StateManager11::unsetConflictingSRVs(gl::SamplerType samplerType, +bool StateManager11::unsetConflictingView(ID3D11View *view) +{ + uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(view)); + return unsetConflictingSRVs(gl::SAMPLER_VERTEX, resource, nullptr) || + unsetConflictingSRVs(gl::SAMPLER_PIXEL, resource, nullptr); +} + +bool StateManager11::unsetConflictingSRVs(gl::SamplerType samplerType, uintptr_t resource, - const gl::ImageIndex &index) + const gl::ImageIndex *index) { auto ¤tSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs); + bool foundOne = false; + for (size_t resourceIndex = 0; resourceIndex < currentSRVs.size(); ++resourceIndex) { auto &record = currentSRVs[resourceIndex]; if (record.srv && record.resource == resource && - ImageIndexConflictsWithSRV(index, record.desc)) + (!index || ImageIndexConflictsWithSRV(*index, record.desc))) { - setShaderResource(samplerType, static_cast<UINT>(resourceIndex), NULL); + setShaderResourceInternal<d3d11::ShaderResourceView>( + samplerType, static_cast<UINT>(resourceIndex), nullptr); + foundOne = true; } } + + return foundOne; +} + +void StateManager11::unsetConflictingAttachmentResources( + const gl::FramebufferAttachment *attachment, + ID3D11Resource *resource) +{ + // Unbind render target SRVs from the shader here to prevent D3D11 warnings. + if (attachment->type() == GL_TEXTURE) + { + uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource); + const gl::ImageIndex &index = attachment->getTextureImageIndex(); + // The index doesn't need to be corrected for the small compressed texture workaround + // because a rendertarget is never compressed. + unsetConflictingSRVs(gl::SAMPLER_VERTEX, resourcePtr, &index); + unsetConflictingSRVs(gl::SAMPLER_PIXEL, resourcePtr, &index); + } + else if (attachment->type() == GL_FRAMEBUFFER_DEFAULT) + { + uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource); + unsetConflictingSRVs(gl::SAMPLER_VERTEX, resourcePtr, nullptr); + unsetConflictingSRVs(gl::SAMPLER_PIXEL, resourcePtr, nullptr); + } } -void StateManager11::initialize(const gl::Caps &caps) +gl::Error StateManager11::initialize(const gl::Caps &caps, const gl::Extensions &extensions) { mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits); mCurPixelSRVs.initialize(caps.maxTextureImageUnits); // Initialize cached NULL SRV block mNullSRVs.resize(caps.maxTextureImageUnits, nullptr); + + mCurrentValueAttribs.resize(caps.maxVertexAttributes); + + mForceSetVertexSamplerStates.resize(caps.maxVertexTextureImageUnits, true); + mForceSetPixelSamplerStates.resize(caps.maxTextureImageUnits, true); + mForceSetComputeSamplerStates.resize(caps.maxComputeTextureImageUnits, true); + + mCurVertexSamplerStates.resize(caps.maxVertexTextureImageUnits); + mCurPixelSamplerStates.resize(caps.maxTextureImageUnits); + mCurComputeSamplerStates.resize(caps.maxComputeTextureImageUnits); + + mShaderConstants.init(caps); + + mIsMultiviewEnabled = extensions.multiview; + mViewportOffsets.resize(1u); + + ANGLE_TRY(mVertexDataManager.initialize()); + + mCurrentAttributes.reserve(gl::MAX_VERTEX_ATTRIBS); + + return gl::NoError(); +} + +void StateManager11::deinitialize() +{ + mCurrentValueAttribs.clear(); + mInputLayoutCache.clear(); + mVertexDataManager.deinitialize(); + mIndexDataManager.deinitialize(); + + mDriverConstantBufferVS.reset(); + mDriverConstantBufferPS.reset(); + mDriverConstantBufferCS.reset(); } -gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer) +gl::Error StateManager11::syncFramebuffer(const gl::Context *context, 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; - DXGI_FORMAT renderTargetFormat = DXGI_FORMAT_UNKNOWN; - RenderTargetArray framebufferRTVs; - bool missingColorRenderTarget = true; + Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer); + + // Applies the render target surface, depth stencil surface, viewport rectangle and + // scissor rectangle to the renderer + ASSERT(framebuffer && !framebuffer->hasAnyDirtyBit() && framebuffer->cachedComplete()); + + // 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 (framebuffer->id() == 0) + { + ASSERT(!framebuffer11->hasAnyInternalDirtyBit()); + const gl::Extents &size = framebuffer->getFirstColorbuffer()->getSize(); + if (size.width == 0 || size.height == 0) + { + return gl::NoError(); + } + } + + RTVArray framebufferRTVs = {{}}; - framebufferRTVs.fill(nullptr); + const auto &colorRTs = framebuffer11->getCachedColorRenderTargets(); - const Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer); - const gl::AttachmentList &colorbuffers = framebuffer11->getColorAttachmentsForRender(); + size_t appliedRTIndex = 0; + bool skipInactiveRTs = mRenderer->getWorkarounds().mrtPerfWorkaround; + const auto &drawStates = framebuffer->getDrawBufferStates(); + gl::DrawBufferMask activeProgramOutputs = + context->getContextState().getState().getProgram()->getActiveOutputVariables(); + UINT maxExistingRT = 0; - for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) + for (size_t rtIndex = 0; rtIndex < colorRTs.size(); ++rtIndex) { - const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment]; + const RenderTarget11 *renderTarget = colorRTs[rtIndex]; - if (colorbuffer) + // Skip inactive rendertargets if the workaround is enabled. + if (skipInactiveRTs && + (!renderTarget || drawStates[rtIndex] == GL_NONE || !activeProgramOutputs[rtIndex])) { - // the draw buffer must be either "none", "back" for the default buffer or the same - // index as this color (in order) - - // 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. - const gl::Extents &size = colorbuffer->getSize(); - if (size.width == 0 || size.height == 0) - { - return gl::Error(GL_NO_ERROR); - } + continue; + } - // Extract the render target dimensions and view - RenderTarget11 *renderTarget = NULL; - gl::Error error = colorbuffer->getRenderTarget(&renderTarget); - if (error.isError()) - { - return error; - } - ASSERT(renderTarget); + if (renderTarget) + { + framebufferRTVs[appliedRTIndex] = renderTarget->getRenderTargetView().get(); + ASSERT(framebufferRTVs[appliedRTIndex]); + maxExistingRT = static_cast<UINT>(appliedRTIndex) + 1; + + // Unset conflicting texture SRVs + const auto *attachment = framebuffer->getColorbuffer(rtIndex); + ASSERT(attachment); + unsetConflictingAttachmentResources(attachment, renderTarget->getTexture().get()); + } + + appliedRTIndex++; + } + + // Get the depth stencil buffers + ID3D11DepthStencilView *framebufferDSV = nullptr; + const auto *depthStencilRenderTarget = framebuffer11->getCachedDepthStencilRenderTarget(); + if (depthStencilRenderTarget) + { + framebufferDSV = depthStencilRenderTarget->getDepthStencilView().get(); + ASSERT(framebufferDSV); + + // Unset conflicting texture SRVs + const auto *attachment = framebuffer->getDepthOrStencilbuffer(); + ASSERT(attachment); + unsetConflictingAttachmentResources(attachment, + depthStencilRenderTarget->getTexture().get()); + } + + // TODO(jmadill): Use context caps? + ASSERT(maxExistingRT <= static_cast<UINT>(mRenderer->getNativeCaps().maxDrawBuffers)); + + // Apply the render target and depth stencil + mRenderer->getDeviceContext()->OMSetRenderTargets(maxExistingRT, framebufferRTVs.data(), + framebufferDSV); + + return gl::NoError(); +} + +void StateManager11::invalidateCurrentValueAttrib(size_t attribIndex) +{ + mDirtyCurrentValueAttribs.set(attribIndex); + mInternalDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_ATTRIBS); +} + +gl::Error StateManager11::syncCurrentValueAttribs(const gl::State &glState) +{ + const auto &activeAttribsMask = glState.getProgram()->getActiveAttribLocationsMask(); + const auto &dirtyActiveAttribs = (activeAttribsMask & mDirtyCurrentValueAttribs); + + if (!dirtyActiveAttribs.any()) + { + return gl::NoError(); + } + + const auto &vertexAttributes = glState.getVertexArray()->getVertexAttributes(); + const auto &vertexBindings = glState.getVertexArray()->getVertexBindings(); + mDirtyCurrentValueAttribs = (mDirtyCurrentValueAttribs & ~dirtyActiveAttribs); + + for (auto attribIndex : dirtyActiveAttribs) + { + if (vertexAttributes[attribIndex].enabled) + continue; + + const auto *attrib = &vertexAttributes[attribIndex]; + const auto ¤tValue = glState.getVertexAttribCurrentValue(attribIndex); + TranslatedAttribute *currentValueAttrib = &mCurrentValueAttribs[attribIndex]; + currentValueAttrib->currentValueType = currentValue.Type; + currentValueAttrib->attribute = attrib; + currentValueAttrib->binding = &vertexBindings[attrib->bindingIndex]; + + mDirtyVertexBufferRange.extend(static_cast<unsigned int>(attribIndex)); + mInputLayoutIsDirty = true; + + ANGLE_TRY(mVertexDataManager.storeCurrentValue(currentValue, currentValueAttrib, + static_cast<size_t>(attribIndex))); + } + + return gl::NoError(); +} - framebufferRTVs[colorAttachment] = renderTarget->getRenderTargetView(); - ASSERT(framebufferRTVs[colorAttachment]); +void StateManager11::setInputLayout(const d3d11::InputLayout *inputLayout) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + if (inputLayout == nullptr) + { + if (!mCurrentInputLayout.empty()) + { + deviceContext->IASetInputLayout(nullptr); + mCurrentInputLayout.clear(); + mInputLayoutIsDirty = true; + } + } + else if (inputLayout->getSerial() != mCurrentInputLayout) + { + deviceContext->IASetInputLayout(inputLayout->get()); + mCurrentInputLayout = inputLayout->getSerial(); + mInputLayoutIsDirty = true; + } +} + +bool StateManager11::queueVertexBufferChange(size_t bufferIndex, + ID3D11Buffer *buffer, + UINT stride, + UINT offset) +{ + if (buffer != mCurrentVertexBuffers[bufferIndex] || + stride != mCurrentVertexStrides[bufferIndex] || + offset != mCurrentVertexOffsets[bufferIndex]) + { + mInputLayoutIsDirty = true; + mDirtyVertexBufferRange.extend(static_cast<unsigned int>(bufferIndex)); + + mCurrentVertexBuffers[bufferIndex] = buffer; + mCurrentVertexStrides[bufferIndex] = stride; + mCurrentVertexOffsets[bufferIndex] = offset; + return true; + } + + return false; +} + +bool StateManager11::queueVertexOffsetChange(size_t bufferIndex, UINT offsetOnly) +{ + if (offsetOnly != mCurrentVertexOffsets[bufferIndex]) + { + mInputLayoutIsDirty = true; + mDirtyVertexBufferRange.extend(static_cast<unsigned int>(bufferIndex)); + mCurrentVertexOffsets[bufferIndex] = offsetOnly; + return true; + } + return false; +} - if (missingColorRenderTarget) +void StateManager11::applyVertexBufferChanges() +{ + if (mDirtyVertexBufferRange.empty()) + { + return; + } + + ASSERT(mDirtyVertexBufferRange.high() <= gl::MAX_VERTEX_ATTRIBS); + + UINT start = static_cast<UINT>(mDirtyVertexBufferRange.low()); + + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + deviceContext->IASetVertexBuffers(start, static_cast<UINT>(mDirtyVertexBufferRange.length()), + &mCurrentVertexBuffers[start], &mCurrentVertexStrides[start], + &mCurrentVertexOffsets[start]); + + mDirtyVertexBufferRange = gl::RangeUI(gl::MAX_VERTEX_ATTRIBS, 0); +} + +void StateManager11::setSingleVertexBuffer(const d3d11::Buffer *buffer, UINT stride, UINT offset) +{ + ID3D11Buffer *native = buffer ? buffer->get() : nullptr; + if (queueVertexBufferChange(0, native, stride, offset)) + { + applyVertexBufferChanges(); + } +} + +gl::Error StateManager11::updateState(const gl::Context *context, GLenum drawMode) +{ + const auto &glState = context->getGLState(); + auto *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + + // TODO(jmadill): Use dirty bits. + processFramebufferInvalidation(context); + + // TODO(jmadill): Use dirty bits. + if (programD3D->updateSamplerMapping() == ProgramD3D::SamplerMapping::WasDirty) + { + invalidateTexturesAndSamplers(); + } + + // TODO(jmadill): Use dirty bits. + if (programD3D->areVertexUniformsDirty() || programD3D->areFragmentUniformsDirty()) + { + mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_UNIFORMS); + } + + // Transform feedback affects the stream-out geometry shader. + // TODO(jmadill): Use dirty bits. + if (glState.isTransformFeedbackActiveUnpaused() != mIsTransformFeedbackCurrentlyActiveUnpaused) + { + mIsTransformFeedbackCurrentlyActiveUnpaused = glState.isTransformFeedbackActiveUnpaused(); + invalidateShaders(); + } + + // Swizzling can cause internal state changes with blit shaders. + if (mDirtySwizzles) + { + ANGLE_TRY(generateSwizzles(context)); + mDirtySwizzles = false; + } + + gl::Framebuffer *framebuffer = glState.getDrawFramebuffer(); + Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer); + ANGLE_TRY(framebuffer11->markAttachmentsDirty(context)); + + if (framebuffer11->hasAnyInternalDirtyBit()) + { + ASSERT(framebuffer->id() != 0); + framebuffer11->syncInternalState(context); + } + + bool pointDrawMode = (drawMode == GL_POINTS); + if (pointDrawMode != mCurRasterState.pointDrawMode) + { + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); + + // Changing from points to not points (or vice-versa) affects the geometry shader. + invalidateShaders(); + } + + // TODO(jiawei.shao@intel.com): This can be recomputed only on framebuffer or multisample mask + // state changes. + RenderTarget11 *firstRT = framebuffer11->getFirstRenderTarget(); + int samples = (firstRT ? firstRT->getSamples() : 0); + unsigned int sampleMask = GetBlendSampleMask(glState, samples); + if (sampleMask != mCurSampleMask) + { + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); + } + + // Changing the vertex attribute state can affect the vertex shader. + gl::VertexArray *vao = glState.getVertexArray(); + VertexArray11 *vao11 = GetImplAs<VertexArray11>(vao); + if (vao11->flushAttribUpdates(context)) + { + mInternalDirtyBits.set(DIRTY_BIT_SHADERS); + } + + auto dirtyBitsCopy = mInternalDirtyBits; + mInternalDirtyBits.reset(); + + for (auto dirtyBit : dirtyBitsCopy) + { + switch (dirtyBit) + { + case DIRTY_BIT_RENDER_TARGET: + ANGLE_TRY(syncFramebuffer(context, framebuffer)); + break; + case DIRTY_BIT_VIEWPORT_STATE: + syncViewport(context); + break; + case DIRTY_BIT_SCISSOR_STATE: + syncScissorRectangle(glState.getScissor(), glState.isScissorTestEnabled()); + break; + case DIRTY_BIT_RASTERIZER_STATE: + ANGLE_TRY(syncRasterizerState(context, pointDrawMode)); + break; + case DIRTY_BIT_BLEND_STATE: + ANGLE_TRY(syncBlendState(context, framebuffer, glState.getBlendState(), + glState.getBlendColor(), sampleMask)); + break; + case DIRTY_BIT_DEPTH_STENCIL_STATE: + ANGLE_TRY(syncDepthStencilState(glState)); + break; + case DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE: + // TODO(jmadill): More fine-grained update. + ANGLE_TRY(syncTextures(context)); + break; + case DIRTY_BIT_PROGRAM_UNIFORMS: + ANGLE_TRY(applyUniforms(programD3D)); + break; + case DIRTY_BIT_DRIVER_UNIFORMS: + // This must happen after viewport sync; the viewport affects builtin uniforms. + ANGLE_TRY(applyDriverUniforms(*programD3D)); + break; + case DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS: + ANGLE_TRY(syncUniformBuffers(context, programD3D)); + break; + case DIRTY_BIT_SHADERS: + ANGLE_TRY(syncProgram(context, drawMode)); + break; + case DIRTY_BIT_CURRENT_VALUE_ATTRIBS: + ANGLE_TRY(syncCurrentValueAttribs(glState)); + break; + default: + UNREACHABLE(); + break; + } + } + + ANGLE_TRY(syncTransformFeedbackBuffers(context)); + + // Check that we haven't set any dirty bits in the flushing of the dirty bits loop. + ASSERT(mInternalDirtyBits.none()); + + return gl::NoError(); +} + +void StateManager11::setShaderResourceShared(gl::SamplerType shaderType, + UINT resourceSlot, + const d3d11::SharedSRV *srv) +{ + setShaderResourceInternal(shaderType, resourceSlot, srv); + + // TODO(jmadill): Narrower dirty region. + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); +} + +void StateManager11::setShaderResource(gl::SamplerType shaderType, + UINT resourceSlot, + const d3d11::ShaderResourceView *srv) +{ + setShaderResourceInternal(shaderType, resourceSlot, srv); + + // TODO(jmadill): Narrower dirty region. + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); +} + +void StateManager11::setPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY primitiveTopology) +{ + if (primitiveTopology != mCurrentPrimitiveTopology) + { + mRenderer->getDeviceContext()->IASetPrimitiveTopology(primitiveTopology); + mCurrentPrimitiveTopology = primitiveTopology; + } +} + +void StateManager11::setDrawShaders(const d3d11::VertexShader *vertexShader, + const d3d11::GeometryShader *geometryShader, + const d3d11::PixelShader *pixelShader) +{ + setVertexShader(vertexShader); + setGeometryShader(geometryShader); + setPixelShader(pixelShader); +} + +void StateManager11::setVertexShader(const d3d11::VertexShader *shader) +{ + ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); + + if (serial != mAppliedVertexShader) + { + ID3D11VertexShader *appliedShader = shader ? shader->get() : nullptr; + mRenderer->getDeviceContext()->VSSetShader(appliedShader, nullptr, 0); + mAppliedVertexShader = serial; + invalidateShaders(); + } +} + +void StateManager11::setGeometryShader(const d3d11::GeometryShader *shader) +{ + ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); + + if (serial != mAppliedGeometryShader) + { + ID3D11GeometryShader *appliedShader = shader ? shader->get() : nullptr; + mRenderer->getDeviceContext()->GSSetShader(appliedShader, nullptr, 0); + mAppliedGeometryShader = serial; + invalidateShaders(); + } +} + +void StateManager11::setPixelShader(const d3d11::PixelShader *shader) +{ + ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); + + if (serial != mAppliedPixelShader) + { + ID3D11PixelShader *appliedShader = shader ? shader->get() : nullptr; + mRenderer->getDeviceContext()->PSSetShader(appliedShader, nullptr, 0); + mAppliedPixelShader = serial; + invalidateShaders(); + } +} + +void StateManager11::setComputeShader(const d3d11::ComputeShader *shader) +{ + ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); + + if (serial != mAppliedComputeShader) + { + ID3D11ComputeShader *appliedShader = shader ? shader->get() : nullptr; + mRenderer->getDeviceContext()->CSSetShader(appliedShader, nullptr, 0); + mAppliedComputeShader = serial; + // TODO(jmadill): Dirty bits for compute. + } +} + +void StateManager11::setVertexConstantBuffer(unsigned int slot, const d3d11::Buffer *buffer) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + auto ¤tSerial = mCurrentConstantBufferVS[slot]; + + mCurrentConstantBufferVSOffset[slot] = 0; + mCurrentConstantBufferVSSize[slot] = 0; + + if (buffer) + { + if (currentSerial != buffer->getSerial()) + { + deviceContext->VSSetConstantBuffers(slot, 1, buffer->getPointer()); + currentSerial = buffer->getSerial(); + invalidateConstantBuffer(slot); + } + } + else + { + if (!currentSerial.empty()) + { + ID3D11Buffer *nullBuffer = nullptr; + deviceContext->VSSetConstantBuffers(slot, 1, &nullBuffer); + currentSerial.clear(); + invalidateConstantBuffer(slot); + } + } +} + +void StateManager11::setPixelConstantBuffer(unsigned int slot, const d3d11::Buffer *buffer) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + auto ¤tSerial = mCurrentConstantBufferPS[slot]; + + mCurrentConstantBufferPSOffset[slot] = 0; + mCurrentConstantBufferPSSize[slot] = 0; + + if (buffer) + { + if (currentSerial != buffer->getSerial()) + { + deviceContext->PSSetConstantBuffers(slot, 1, buffer->getPointer()); + currentSerial = buffer->getSerial(); + invalidateConstantBuffer(slot); + } + } + else + { + if (!currentSerial.empty()) + { + ID3D11Buffer *nullBuffer = nullptr; + deviceContext->PSSetConstantBuffers(slot, 1, &nullBuffer); + currentSerial.clear(); + invalidateConstantBuffer(slot); + } + } +} + +void StateManager11::setDepthStencilState(const d3d11::DepthStencilState *depthStencilState, + UINT stencilRef) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + if (depthStencilState) + { + deviceContext->OMSetDepthStencilState(depthStencilState->get(), stencilRef); + } + else + { + deviceContext->OMSetDepthStencilState(nullptr, stencilRef); + } + + mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); +} + +void StateManager11::setSimpleBlendState(const d3d11::BlendState *blendState) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + if (blendState) + { + deviceContext->OMSetBlendState(blendState->get(), nullptr, 0xFFFFFFFF); + } + else + { + deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF); + } + + mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); +} + +void StateManager11::setRasterizerState(const d3d11::RasterizerState *rasterizerState) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + if (rasterizerState) + { + deviceContext->RSSetState(rasterizerState->get()); + } + else + { + deviceContext->RSSetState(nullptr); + } + + mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); +} + +void StateManager11::setSimpleViewport(const gl::Extents &extents) +{ + setSimpleViewport(extents.width, extents.height); +} + +void StateManager11::setSimpleViewport(int width, int height) +{ + D3D11_VIEWPORT viewport; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = static_cast<FLOAT>(width); + viewport.Height = static_cast<FLOAT>(height); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + mRenderer->getDeviceContext()->RSSetViewports(1, &viewport); + mInternalDirtyBits.set(DIRTY_BIT_VIEWPORT_STATE); +} + +void StateManager11::setSimplePixelTextureAndSampler(const d3d11::SharedSRV &srv, + const d3d11::SamplerState &samplerState) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + setShaderResourceInternal(gl::SAMPLER_PIXEL, 0, &srv); + deviceContext->PSSetSamplers(0, 1, samplerState.getPointer()); + + mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); + mForceSetPixelSamplerStates[0] = true; +} + +void StateManager11::setSimpleScissorRect(const gl::Rectangle &glRect) +{ + D3D11_RECT scissorRect; + scissorRect.left = glRect.x; + scissorRect.right = glRect.x + glRect.width; + scissorRect.top = glRect.y; + scissorRect.bottom = glRect.y + glRect.height; + setScissorRectD3D(scissorRect); +} + +void StateManager11::setScissorRectD3D(const D3D11_RECT &d3dRect) +{ + mRenderer->getDeviceContext()->RSSetScissorRects(1, &d3dRect); + mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); +} + +// For each Direct3D sampler of either the pixel or vertex stage, +// looks up the corresponding OpenGL texture image unit and texture type, +// and sets the texture and its addressing/filtering state (or NULL when inactive). +// Sampler mapping needs to be up-to-date on the program object before this is called. +gl::Error StateManager11::applyTextures(const gl::Context *context, gl::SamplerType shaderType) +{ + const auto &glState = context->getGLState(); + const auto &caps = context->getCaps(); + ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + + ASSERT(!programD3D->isSamplerMappingDirty()); + + // TODO(jmadill): Use the Program's sampler bindings. + const auto &completeTextures = glState.getCompleteTextureCache(); + + unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType); + for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++) + { + GLint textureUnit = programD3D->getSamplerMapping(shaderType, samplerIndex, caps); + ASSERT(textureUnit != -1); + gl::Texture *texture = completeTextures[textureUnit]; + + // A nullptr texture indicates incomplete. + if (texture) + { + gl::Sampler *samplerObject = glState.getSampler(textureUnit); + + const gl::SamplerState &samplerState = + samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState(); + + ANGLE_TRY(setSamplerState(context, shaderType, samplerIndex, texture, samplerState)); + ANGLE_TRY(setTexture(context, shaderType, samplerIndex, texture)); + } + else + { + GLenum textureType = programD3D->getSamplerTextureType(shaderType, samplerIndex); + + // Texture is not sampler complete or it is in use by the framebuffer. Bind the + // incomplete texture. + gl::Texture *incompleteTexture = nullptr; + ANGLE_TRY(mRenderer->getIncompleteTexture(context, textureType, &incompleteTexture)); + ANGLE_TRY(setSamplerState(context, shaderType, samplerIndex, incompleteTexture, + incompleteTexture->getSamplerState())); + ANGLE_TRY(setTexture(context, shaderType, samplerIndex, incompleteTexture)); + } + } + + // Set all the remaining textures to NULL + size_t samplerCount = (shaderType == gl::SAMPLER_PIXEL) ? caps.maxTextureImageUnits + : caps.maxVertexTextureImageUnits; + ANGLE_TRY(clearTextures(shaderType, samplerRange, samplerCount)); + + return gl::NoError(); +} + +gl::Error StateManager11::syncTextures(const gl::Context *context) +{ + ANGLE_TRY(applyTextures(context, gl::SAMPLER_VERTEX)); + ANGLE_TRY(applyTextures(context, gl::SAMPLER_PIXEL)); + return gl::NoError(); +} + +gl::Error StateManager11::setSamplerState(const gl::Context *context, + gl::SamplerType type, + int index, + gl::Texture *texture, + const gl::SamplerState &samplerState) +{ +#if !defined(NDEBUG) + // Storage should exist, texture should be complete. Only verified in Debug. + TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); + TextureStorage *storage = nullptr; + ANGLE_TRY(textureD3D->getNativeTexture(context, &storage)); + ASSERT(storage); +#endif // !defined(NDEBUG) + + auto *deviceContext = mRenderer->getDeviceContext(); + + if (type == gl::SAMPLER_PIXEL) + { + ASSERT(static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxTextureImageUnits); + + if (mForceSetPixelSamplerStates[index] || + memcmp(&samplerState, &mCurPixelSamplerStates[index], sizeof(gl::SamplerState)) != 0) + { + ID3D11SamplerState *dxSamplerState = nullptr; + ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState)); + + ASSERT(dxSamplerState != nullptr); + deviceContext->PSSetSamplers(index, 1, &dxSamplerState); + + mCurPixelSamplerStates[index] = samplerState; + } + + mForceSetPixelSamplerStates[index] = false; + } + else if (type == gl::SAMPLER_VERTEX) + { + ASSERT(static_cast<unsigned int>(index) < + mRenderer->getNativeCaps().maxVertexTextureImageUnits); + + if (mForceSetVertexSamplerStates[index] || + memcmp(&samplerState, &mCurVertexSamplerStates[index], sizeof(gl::SamplerState)) != 0) + { + ID3D11SamplerState *dxSamplerState = nullptr; + ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState)); + + ASSERT(dxSamplerState != nullptr); + deviceContext->VSSetSamplers(index, 1, &dxSamplerState); + + mCurVertexSamplerStates[index] = samplerState; + } + + mForceSetVertexSamplerStates[index] = false; + } + else if (type == gl::SAMPLER_COMPUTE) + { + ASSERT(static_cast<unsigned int>(index) < + mRenderer->getNativeCaps().maxComputeTextureImageUnits); + + if (mForceSetComputeSamplerStates[index] || + memcmp(&samplerState, &mCurComputeSamplerStates[index], sizeof(gl::SamplerState)) != 0) + { + ID3D11SamplerState *dxSamplerState = nullptr; + ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState)); + + ASSERT(dxSamplerState != nullptr); + deviceContext->CSSetSamplers(index, 1, &dxSamplerState); + + mCurComputeSamplerStates[index] = samplerState; + } + + mForceSetComputeSamplerStates[index] = false; + } + else + UNREACHABLE(); + + // Sampler metadata that's passed to shaders in uniforms is stored separately from rest of the + // sampler state since having it in contiguous memory makes it possible to memcpy to a constant + // buffer, and it doesn't affect the state set by PSSetSamplers/VSSetSamplers. + mShaderConstants.onSamplerChange(type, index, *texture); + + return gl::NoError(); +} + +gl::Error StateManager11::setTexture(const gl::Context *context, + gl::SamplerType type, + int index, + gl::Texture *texture) +{ + const d3d11::SharedSRV *textureSRV = nullptr; + + if (texture) + { + TextureD3D *textureImpl = GetImplAs<TextureD3D>(texture); + + TextureStorage *texStorage = nullptr; + ANGLE_TRY(textureImpl->getNativeTexture(context, &texStorage)); + + // Texture should be complete and have a storage + ASSERT(texStorage); + + TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage); + + ANGLE_TRY(storage11->getSRV(context, texture->getTextureState(), &textureSRV)); + + // If we get an invalid SRV here, something went wrong in the texture class and we're + // unexpectedly missing the shader resource view. + ASSERT(textureSRV->valid()); + + textureImpl->resetDirty(); + } + + ASSERT( + (type == gl::SAMPLER_PIXEL && + static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxTextureImageUnits) || + (type == gl::SAMPLER_VERTEX && + static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxVertexTextureImageUnits)); + + setShaderResourceInternal(type, index, textureSRV); + return gl::NoError(); +} + +// Things that affect a program's dirtyness: +// 1. Directly changing the program executable -> triggered in StateManager11::syncState. +// 2. The vertex attribute layout -> triggered in VertexArray11::syncState/signal. +// 3. The fragment shader's rendertargets -> triggered in Framebuffer11::syncState/signal. +// 4. Enabling/disabling rasterizer discard. -> triggered in StateManager11::syncState. +// 5. Enabling/disabling transform feedback. -> checked in StateManager11::updateState. +// 6. An internal shader was used. -> triggered in StateManager11::set*Shader. +// 7. Drawing with/without point sprites. -> checked in StateManager11::updateState. +// TODO(jmadill): Use dirty bits for transform feedback. +gl::Error StateManager11::syncProgram(const gl::Context *context, GLenum drawMode) +{ + Context11 *context11 = GetImplAs<Context11>(context); + ANGLE_TRY(context11->triggerDrawCallProgramRecompilation(context, drawMode)); + + const auto &glState = context->getGLState(); + const auto *va11 = GetImplAs<VertexArray11>(glState.getVertexArray()); + auto *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + + programD3D->updateCachedInputLayout(va11->getCurrentStateSerial(), glState); + + // Binaries must be compiled before the sync. + ASSERT(programD3D->hasVertexExecutableForCachedInputLayout()); + ASSERT(programD3D->hasGeometryExecutableForPrimitiveType(drawMode)); + ASSERT(programD3D->hasPixelExecutableForCachedOutputLayout()); + + ShaderExecutableD3D *vertexExe = nullptr; + ANGLE_TRY(programD3D->getVertexExecutableForCachedInputLayout(&vertexExe, nullptr)); + + ShaderExecutableD3D *pixelExe = nullptr; + ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(&pixelExe, nullptr)); + + ShaderExecutableD3D *geometryExe = nullptr; + ANGLE_TRY(programD3D->getGeometryExecutableForPrimitiveType(context, drawMode, &geometryExe, + nullptr)); + + const d3d11::VertexShader *vertexShader = + (vertexExe ? &GetAs<ShaderExecutable11>(vertexExe)->getVertexShader() : nullptr); + + // Skip pixel shader if we're doing rasterizer discard. + const d3d11::PixelShader *pixelShader = nullptr; + if (!glState.getRasterizerState().rasterizerDiscard) + { + pixelShader = (pixelExe ? &GetAs<ShaderExecutable11>(pixelExe)->getPixelShader() : nullptr); + } + + const d3d11::GeometryShader *geometryShader = nullptr; + if (glState.isTransformFeedbackActiveUnpaused()) + { + geometryShader = + (vertexExe ? &GetAs<ShaderExecutable11>(vertexExe)->getStreamOutShader() : nullptr); + } + else + { + geometryShader = + (geometryExe ? &GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader() : nullptr); + } + + setDrawShaders(vertexShader, geometryShader, pixelShader); + + // Explicitly clear the shaders dirty bit. + mInternalDirtyBits.reset(DIRTY_BIT_SHADERS); + + return gl::NoError(); +} + +gl::Error StateManager11::applyVertexBuffer(const gl::Context *context, + GLenum mode, + const DrawCallVertexParams &vertexParams, + bool isIndexedRendering) +{ + const auto &state = context->getGLState(); + const gl::VertexArray *vertexArray = state.getVertexArray(); + VertexArray11 *vertexArray11 = GetImplAs<VertexArray11>(vertexArray); + + if (mVertexAttribsNeedTranslation) + { + ANGLE_TRY(vertexArray11->updateDirtyAndDynamicAttribs(context, &mVertexDataManager, + vertexParams)); + mInputLayoutIsDirty = true; + + // Determine if we need to update attribs on the next draw. + mVertexAttribsNeedTranslation = (vertexArray11->hasActiveDynamicAttrib(context)); + } + + if (!mLastFirstVertex.valid() || mLastFirstVertex.value() != vertexParams.firstVertex()) + { + mLastFirstVertex = vertexParams.firstVertex(); + mInputLayoutIsDirty = true; + } + + if (!mInputLayoutIsDirty) + { + return gl::NoError(); + } + + const auto &vertexArrayAttribs = vertexArray11->getTranslatedAttribs(); + gl::Program *program = state.getProgram(); + + // Sort the attributes according to ensure we re-use similar input layouts. + AttribIndexArray sortedSemanticIndices; + SortAttributesByLayout(program, vertexArrayAttribs, mCurrentValueAttribs, + &sortedSemanticIndices, &mCurrentAttributes); + + auto featureLevel = mRenderer->getRenderer11DeviceCaps().featureLevel; + + // If we are using FL 9_3, make sure the first attribute is not instanced + if (featureLevel <= D3D_FEATURE_LEVEL_9_3 && !mCurrentAttributes.empty()) + { + if (mCurrentAttributes[0]->divisor > 0) + { + Optional<size_t> firstNonInstancedIndex = FindFirstNonInstanced(mCurrentAttributes); + if (firstNonInstancedIndex.valid()) { - renderTargetWidth = renderTarget->getWidth(); - renderTargetHeight = renderTarget->getHeight(); - renderTargetFormat = renderTarget->getDXGIFormat(); - missingColorRenderTarget = false; + size_t index = firstNonInstancedIndex.value(); + std::swap(mCurrentAttributes[0], mCurrentAttributes[index]); + std::swap(sortedSemanticIndices[0], sortedSemanticIndices[index]); } + } + } + + // Update the applied input layout by querying the cache. + ANGLE_TRY(mInputLayoutCache.updateInputLayout(mRenderer, state, mCurrentAttributes, mode, + sortedSemanticIndices, vertexParams)); + + // Update the applied vertex buffers. + ANGLE_TRY(mInputLayoutCache.applyVertexBuffers(context, mCurrentAttributes, mode, + vertexParams.firstVertex(), isIndexedRendering)); + + // InputLayoutCache::applyVertexBuffers calls through to the Bufer11 to get the native vertex + // buffer (ID3D11Buffer *). Because we allocate these buffers lazily, this will trigger + // allocation. This in turn will signal that the buffer is dirty. Since we just resolved the + // dirty-ness in VertexArray11::updateDirtyAndDynamicAttribs, this can make us do a needless + // update on the second draw call. + // Hence we clear the flags here, after we've applied vertex data, since we know everything + // is clean. This is a bit of a hack. + vertexArray11->clearDirtyAndPromoteDynamicAttribs(context, vertexParams); + + mInputLayoutIsDirty = false; + return gl::NoError(); +} + +gl::Error StateManager11::applyIndexBuffer(const gl::Context *context, + const void *indices, + GLsizei count, + GLenum type, + const gl::HasIndexRange &lazyIndexRange, + bool usePrimitiveRestartWorkaround) +{ + const auto &glState = context->getGLState(); + gl::VertexArray *vao = glState.getVertexArray(); + VertexArray11 *vao11 = GetImplAs<VertexArray11>(vao); + + GLenum destElementType = + GetIndexTranslationDestType(type, lazyIndexRange, usePrimitiveRestartWorkaround); + + if (!vao11->updateElementArrayStorage(context, type, destElementType, indices) && + !mIndexBufferIsDirty) + { + // No streaming or index buffer application necessary. + return gl::NoError(); + } + + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + + TranslatedIndexData *indexInfo = vao11->getCachedIndexInfo(); + ANGLE_TRY(mIndexDataManager.prepareIndexData(context, type, destElementType, count, + elementArrayBuffer, indices, indexInfo)); + + ID3D11Buffer *buffer = nullptr; + DXGI_FORMAT bufferFormat = + (indexInfo->indexType == GL_UNSIGNED_INT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT; + + if (indexInfo->storage) + { + Buffer11 *storage = GetAs<Buffer11>(indexInfo->storage); + ANGLE_TRY_RESULT(storage->getBuffer(context, BUFFER_USAGE_INDEX), buffer); + } + else + { + IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(indexInfo->indexBuffer); + buffer = indexBuffer->getBuffer().get(); + } + + // Track dirty indices in the index range cache. + indexInfo->srcIndexData.srcIndicesChanged = + syncIndexBuffer(buffer, bufferFormat, indexInfo->startOffset); + + mIndexBufferIsDirty = false; + + vao11->setCachedIndexInfoValid(); + return gl::NoError(); +} + +void StateManager11::setIndexBuffer(ID3D11Buffer *buffer, + DXGI_FORMAT indexFormat, + unsigned int offset) +{ + if (syncIndexBuffer(buffer, indexFormat, offset)) + { + mIndexBufferIsDirty = true; + } +} - // Unbind render target SRVs from the shader here to prevent D3D11 warnings. - if (colorbuffer->type() == GL_TEXTURE) +bool StateManager11::syncIndexBuffer(ID3D11Buffer *buffer, + DXGI_FORMAT indexFormat, + unsigned int offset) +{ + if (buffer != mAppliedIB || indexFormat != mAppliedIBFormat || offset != mAppliedIBOffset) + { + mRenderer->getDeviceContext()->IASetIndexBuffer(buffer, indexFormat, offset); + + mAppliedIB = buffer; + mAppliedIBFormat = indexFormat; + mAppliedIBOffset = offset; + return true; + } + + return false; +} + +// Vertex buffer is invalidated outside this function. +gl::Error StateManager11::updateVertexOffsetsForPointSpritesEmulation(GLint startVertex, + GLsizei emulatedInstanceId) +{ + return mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation( + mRenderer, mCurrentAttributes, startVertex, emulatedInstanceId); +} + +gl::Error StateManager11::generateSwizzle(const gl::Context *context, gl::Texture *texture) +{ + if (!texture) + { + return gl::NoError(); + } + + TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); + ASSERT(textureD3D); + + TextureStorage *texStorage = nullptr; + ANGLE_TRY(textureD3D->getNativeTexture(context, &texStorage)); + + if (texStorage) + { + TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage); + const gl::TextureState &textureState = texture->getTextureState(); + ANGLE_TRY(storage11->generateSwizzles(context, textureState.getSwizzleState())); + } + + return gl::NoError(); +} + +gl::Error StateManager11::generateSwizzlesForShader(const gl::Context *context, + gl::SamplerType type) +{ + const auto &glState = context->getGLState(); + ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram()); + + unsigned int samplerRange = programD3D->getUsedSamplerRange(type); + + for (unsigned int i = 0; i < samplerRange; i++) + { + GLenum textureType = programD3D->getSamplerTextureType(type, i); + GLint textureUnit = programD3D->getSamplerMapping(type, i, context->getCaps()); + if (textureUnit != -1) + { + gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType); + ASSERT(texture); + if (texture->getTextureState().swizzleRequired()) { - uintptr_t rtResource = - reinterpret_cast<uintptr_t>(GetViewResource(framebufferRTVs[colorAttachment])); - const gl::ImageIndex &index = colorbuffer->getTextureImageIndex(); - // The index doesn't need to be corrected for the small compressed texture - // workaround - // because a rendertarget is never compressed. - unsetConflictingSRVs(gl::SAMPLER_VERTEX, rtResource, index); - unsetConflictingSRVs(gl::SAMPLER_PIXEL, rtResource, index); + ANGLE_TRY(generateSwizzle(context, texture)); } } } - // Get the depth stencil buffers - ID3D11DepthStencilView *framebufferDSV = NULL; - const gl::FramebufferAttachment *depthStencil = framebuffer->getDepthOrStencilbuffer(); - if (depthStencil) + return gl::NoError(); +} + +gl::Error StateManager11::generateSwizzles(const gl::Context *context) +{ + ANGLE_TRY(generateSwizzlesForShader(context, gl::SAMPLER_VERTEX)); + ANGLE_TRY(generateSwizzlesForShader(context, gl::SAMPLER_PIXEL)); + return gl::NoError(); +} + +gl::Error StateManager11::applyUniforms(ProgramD3D *programD3D) +{ + UniformStorage11 *vertexUniformStorage = + GetAs<UniformStorage11>(&programD3D->getVertexUniformStorage()); + UniformStorage11 *fragmentUniformStorage = + GetAs<UniformStorage11>(&programD3D->getFragmentUniformStorage()); + ASSERT(vertexUniformStorage); + ASSERT(fragmentUniformStorage); + + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + const d3d11::Buffer *vertexConstantBuffer = nullptr; + ANGLE_TRY(vertexUniformStorage->getConstantBuffer(mRenderer, &vertexConstantBuffer)); + const d3d11::Buffer *pixelConstantBuffer = nullptr; + ANGLE_TRY(fragmentUniformStorage->getConstantBuffer(mRenderer, &pixelConstantBuffer)); + + if (vertexUniformStorage->size() > 0 && programD3D->areVertexUniformsDirty()) + { + UpdateUniformBuffer(deviceContext, vertexUniformStorage, vertexConstantBuffer); + } + + if (fragmentUniformStorage->size() > 0 && programD3D->areFragmentUniformsDirty()) + { + UpdateUniformBuffer(deviceContext, fragmentUniformStorage, pixelConstantBuffer); + } + + unsigned int slot = d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK; + + if (mCurrentConstantBufferVS[slot] != vertexConstantBuffer->getSerial()) + { + deviceContext->VSSetConstantBuffers(slot, 1, vertexConstantBuffer->getPointer()); + mCurrentConstantBufferVS[slot] = vertexConstantBuffer->getSerial(); + mCurrentConstantBufferVSOffset[slot] = 0; + mCurrentConstantBufferVSSize[slot] = 0; + } + + if (mCurrentConstantBufferPS[slot] != pixelConstantBuffer->getSerial()) + { + deviceContext->PSSetConstantBuffers(slot, 1, pixelConstantBuffer->getPointer()); + mCurrentConstantBufferPS[slot] = pixelConstantBuffer->getSerial(); + mCurrentConstantBufferPSOffset[slot] = 0; + mCurrentConstantBufferPSSize[slot] = 0; + } + + programD3D->markUniformsClean(); + + return gl::NoError(); +} + +gl::Error StateManager11::applyDriverUniforms(const ProgramD3D &programD3D) +{ + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + if (!mDriverConstantBufferVS.valid()) + { + size_t requiredSize = mShaderConstants.getRequiredBufferSize(gl::SAMPLER_VERTEX); + + D3D11_BUFFER_DESC constantBufferDescription = {0}; + d3d11::InitConstantBufferDesc(&constantBufferDescription, requiredSize); + ANGLE_TRY(mRenderer->allocateResource(constantBufferDescription, &mDriverConstantBufferVS)); + + ID3D11Buffer *driverVSConstants = mDriverConstantBufferVS.get(); + deviceContext->VSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, + &driverVSConstants); + } + + if (!mDriverConstantBufferPS.valid()) + { + size_t requiredSize = mShaderConstants.getRequiredBufferSize(gl::SAMPLER_PIXEL); + + D3D11_BUFFER_DESC constantBufferDescription = {0}; + d3d11::InitConstantBufferDesc(&constantBufferDescription, requiredSize); + ANGLE_TRY(mRenderer->allocateResource(constantBufferDescription, &mDriverConstantBufferPS)); + + ID3D11Buffer *driverVSConstants = mDriverConstantBufferPS.get(); + deviceContext->PSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, + &driverVSConstants); + } + + // Sampler metadata and driver constants need to coexist in the same constant buffer to conserve + // constant buffer slots. We update both in the constant buffer if needed. + ANGLE_TRY(mShaderConstants.updateBuffer(deviceContext, gl::SAMPLER_VERTEX, programD3D, + mDriverConstantBufferVS)); + ANGLE_TRY(mShaderConstants.updateBuffer(deviceContext, gl::SAMPLER_PIXEL, programD3D, + mDriverConstantBufferPS)); + + // needed for the point sprite geometry shader + // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it for ES3. + if (mRenderer->isES3Capable()) { - RenderTarget11 *depthStencilRenderTarget = NULL; - gl::Error error = depthStencil->getRenderTarget(&depthStencilRenderTarget); - if (error.isError()) + if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS.getSerial()) { - return error; + ASSERT(mDriverConstantBufferPS.valid()); + deviceContext->GSSetConstantBuffers(0, 1, mDriverConstantBufferPS.getPointer()); + mCurrentGeometryConstantBuffer = mDriverConstantBufferPS.getSerial(); } - ASSERT(depthStencilRenderTarget); + } - framebufferDSV = depthStencilRenderTarget->getDepthStencilView(); - ASSERT(framebufferDSV); + return gl::NoError(); +} + +gl::Error StateManager11::applyComputeUniforms(ProgramD3D *programD3D) +{ + UniformStorage11 *computeUniformStorage = + GetAs<UniformStorage11>(&programD3D->getComputeUniformStorage()); + ASSERT(computeUniformStorage); + + const d3d11::Buffer *constantBuffer = nullptr; + ANGLE_TRY(computeUniformStorage->getConstantBuffer(mRenderer, &constantBuffer)); + + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + if (computeUniformStorage->size() > 0 && programD3D->areComputeUniformsDirty()) + { + UpdateUniformBuffer(deviceContext, computeUniformStorage, constantBuffer); + programD3D->markUniformsClean(); + } + + if (mCurrentComputeConstantBuffer != constantBuffer->getSerial()) + { + deviceContext->CSSetConstantBuffers( + d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK, 1, + constantBuffer->getPointer()); + mCurrentComputeConstantBuffer = constantBuffer->getSerial(); + } + + if (!mDriverConstantBufferCS.valid()) + { + size_t requiredSize = mShaderConstants.getRequiredBufferSize(gl::SAMPLER_COMPUTE); + + D3D11_BUFFER_DESC constantBufferDescription = {0}; + d3d11::InitConstantBufferDesc(&constantBufferDescription, requiredSize); + ANGLE_TRY(mRenderer->allocateResource(constantBufferDescription, &mDriverConstantBufferCS)); + ID3D11Buffer *buffer = mDriverConstantBufferCS.get(); + deviceContext->CSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, + &buffer); + } + + ANGLE_TRY(mShaderConstants.updateBuffer(deviceContext, gl::SAMPLER_COMPUTE, *programD3D, + mDriverConstantBufferCS)); + + return gl::NoError(); +} + +gl::Error StateManager11::syncUniformBuffers(const gl::Context *context, ProgramD3D *programD3D) +{ + unsigned int reservedVertex = mRenderer->getReservedVertexUniformBuffers(); + unsigned int reservedFragment = mRenderer->getReservedFragmentUniformBuffers(); + + programD3D->updateUniformBufferCache(context->getCaps(), reservedVertex, reservedFragment); + + const auto &vertexUniformBuffers = programD3D->getVertexUniformBufferCache(); + const auto &fragmentUniformBuffers = programD3D->getFragmentUniformBufferCache(); + const auto &glState = context->getGLState(); + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported(); + + for (size_t bufferIndex = 0; bufferIndex < vertexUniformBuffers.size(); bufferIndex++) + { + GLint binding = vertexUniformBuffers[bufferIndex]; + + if (binding == -1) + { + continue; + } + + const auto &uniformBuffer = glState.getIndexedUniformBuffer(binding); + GLintptr uniformBufferOffset = uniformBuffer.getOffset(); + GLsizeiptr uniformBufferSize = uniformBuffer.getSize(); + + if (uniformBuffer.get() == nullptr) + { + continue; + } + + Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get()); + const d3d11::Buffer *constantBuffer = nullptr; + UINT firstConstant = 0; + UINT numConstants = 0; + + ANGLE_TRY(bufferStorage->getConstantBufferRange(context, uniformBufferOffset, + uniformBufferSize, &constantBuffer, + &firstConstant, &numConstants)); + + ASSERT(constantBuffer); + + if (mCurrentConstantBufferVS[bufferIndex] == constantBuffer->getSerial() && + mCurrentConstantBufferVSOffset[bufferIndex] == uniformBufferOffset && + mCurrentConstantBufferVSSize[bufferIndex] == uniformBufferSize) + { + continue; + } + + unsigned int appliedIndex = reservedVertex + static_cast<unsigned int>(bufferIndex); + + if (firstConstant != 0 && uniformBufferSize != 0) + { + ASSERT(numConstants != 0); + deviceContext1->VSSetConstantBuffers1(appliedIndex, 1, constantBuffer->getPointer(), + &firstConstant, &numConstants); + } + else + { + deviceContext->VSSetConstantBuffers(appliedIndex, 1, constantBuffer->getPointer()); + } + + mCurrentConstantBufferVS[appliedIndex] = constantBuffer->getSerial(); + mCurrentConstantBufferVSOffset[appliedIndex] = uniformBufferOffset; + mCurrentConstantBufferVSSize[appliedIndex] = uniformBufferSize; + } + + for (size_t bufferIndex = 0; bufferIndex < fragmentUniformBuffers.size(); bufferIndex++) + { + GLint binding = fragmentUniformBuffers[bufferIndex]; - // If there is no render buffer, the width, height and format values come from - // the depth stencil - if (missingColorRenderTarget) + if (binding == -1) { - renderTargetWidth = depthStencilRenderTarget->getWidth(); - renderTargetHeight = depthStencilRenderTarget->getHeight(); + continue; } - // Unbind render target SRVs from the shader here to prevent D3D11 warnings. - if (depthStencil->type() == GL_TEXTURE) + const auto &uniformBuffer = glState.getIndexedUniformBuffer(binding); + GLintptr uniformBufferOffset = uniformBuffer.getOffset(); + GLsizeiptr uniformBufferSize = uniformBuffer.getSize(); + + if (uniformBuffer.get() == nullptr) { - uintptr_t depthStencilResource = - reinterpret_cast<uintptr_t>(GetViewResource(framebufferDSV)); - const gl::ImageIndex &index = depthStencil->getTextureImageIndex(); - // The index doesn't need to be corrected for the small compressed texture workaround - // because a rendertarget is never compressed. - unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index); - unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index); + continue; } + + Buffer11 *bufferStorage = GetImplAs<Buffer11>(uniformBuffer.get()); + const d3d11::Buffer *constantBuffer = nullptr; + UINT firstConstant = 0; + UINT numConstants = 0; + + ANGLE_TRY(bufferStorage->getConstantBufferRange(context, uniformBufferOffset, + uniformBufferSize, &constantBuffer, + &firstConstant, &numConstants)); + + ASSERT(constantBuffer); + + if (mCurrentConstantBufferPS[bufferIndex] == constantBuffer->getSerial() && + mCurrentConstantBufferPSOffset[bufferIndex] == uniformBufferOffset && + mCurrentConstantBufferPSSize[bufferIndex] == uniformBufferSize) + { + continue; + } + + unsigned int appliedIndex = reservedFragment + static_cast<unsigned int>(bufferIndex); + + if (firstConstant != 0 && uniformBufferSize != 0) + { + deviceContext1->PSSetConstantBuffers1(appliedIndex, 1, constantBuffer->getPointer(), + &firstConstant, &numConstants); + } + else + { + deviceContext->PSSetConstantBuffers(appliedIndex, 1, constantBuffer->getPointer()); + } + + mCurrentConstantBufferPS[appliedIndex] = constantBuffer->getSerial(); + mCurrentConstantBufferPSOffset[appliedIndex] = uniformBufferOffset; + mCurrentConstantBufferPSSize[appliedIndex] = uniformBufferSize; } - if (setRenderTargets(framebufferRTVs, framebufferDSV)) + return gl::NoError(); +} + +gl::Error StateManager11::syncTransformFeedbackBuffers(const gl::Context *context) +{ + const auto &glState = context->getGLState(); + + ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); + + // If transform feedback is not active, unbind all buffers + if (!glState.isTransformFeedbackActiveUnpaused()) { - setViewportBounds(renderTargetWidth, renderTargetHeight); + if (mAppliedTFSerial != mEmptySerial) + { + deviceContext->SOSetTargets(0, nullptr, nullptr); + mAppliedTFSerial = mEmptySerial; + } + return gl::NoError(); } - gl::Error error = framebuffer11->invalidateSwizzles(); - if (error.isError()) + gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback(); + TransformFeedback11 *tf11 = GetImplAs<TransformFeedback11>(transformFeedback); + if (mAppliedTFSerial == tf11->getSerial() && !tf11->isDirty()) { - return error; + return gl::NoError(); } - return gl::Error(GL_NO_ERROR); + const std::vector<ID3D11Buffer *> *soBuffers = nullptr; + ANGLE_TRY_RESULT(tf11->getSOBuffers(context), soBuffers); + const std::vector<UINT> &soOffsets = tf11->getSOBufferOffsets(); + + deviceContext->SOSetTargets(tf11->getNumSOBuffers(), soBuffers->data(), soOffsets.data()); + + mAppliedTFSerial = tf11->getSerial(); + tf11->onApply(); + + return gl::NoError(); +} + +// DrawCallVertexParams implementation. +DrawCallVertexParams::DrawCallVertexParams(GLint firstVertex, + GLsizei vertexCount, + GLsizei instances) + : mHasIndexRange(nullptr), + mFirstVertex(firstVertex), + mVertexCount(vertexCount), + mInstances(instances), + mBaseVertex(0) +{ +} + +// Use when in a drawElements call. +DrawCallVertexParams::DrawCallVertexParams(bool firstVertexDefinitelyZero, + const gl::HasIndexRange &hasIndexRange, + GLint baseVertex, + GLsizei instances) + : mHasIndexRange(&hasIndexRange), + mFirstVertex(), + mVertexCount(0), + mInstances(instances), + mBaseVertex(baseVertex) +{ + if (firstVertexDefinitelyZero) + { + mFirstVertex = baseVertex; + } +} + +GLint DrawCallVertexParams::firstVertex() const +{ + if (!mFirstVertex.valid()) + { + ensureResolved(); + ASSERT(mFirstVertex.valid()); + } + return mFirstVertex.value(); +} + +GLsizei DrawCallVertexParams::vertexCount() const +{ + ensureResolved(); + return mVertexCount; +} + +GLsizei DrawCallVertexParams::instances() const +{ + return mInstances; +} + +void DrawCallVertexParams::ensureResolved() const +{ + if (mHasIndexRange) + { + ASSERT(!mFirstVertex.valid() || mFirstVertex == mBaseVertex); + + // Resolve the index range now if we need to. + const auto &indexRange = mHasIndexRange->getIndexRange().value(); + mFirstVertex = mBaseVertex + static_cast<GLint>(indexRange.start); + mVertexCount = static_cast<GLsizei>(indexRange.vertexCount()); + mHasIndexRange = nullptr; + } } } // namespace rx |