| // |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // StateManager11.cpp: Defines a class for caching D3D11 state |
| |
| #include "libANGLE/renderer/d3d/d3d11/StateManager11.h" |
| |
| #if defined(STARBOARD) |
| #include "angle_hdr.h" |
| #endif // STARBOARD |
| #include "common/angleutils.h" |
| #include "common/bitset_utils.h" |
| #include "common/mathutil.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Query.h" |
| #include "libANGLE/VertexArray.h" |
| #include "libANGLE/renderer/d3d/DisplayD3D.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/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" |
| #include "libANGLE/renderer/d3d/d3d11/VertexBuffer11.h" |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| bool ImageIndexConflictsWithSRV(const gl::ImageIndex &index, D3D11_SHADER_RESOURCE_VIEW_DESC desc) |
| { |
| unsigned mipLevel = index.getLevelIndex(); |
| gl::TextureType textureType = index.getType(); |
| |
| switch (desc.ViewDimension) |
| { |
| case D3D11_SRV_DIMENSION_TEXTURE2D: |
| { |
| 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.getLevelIndex(); |
| unsigned mipMax = INT_MAX; |
| |
| return textureType == gl::TextureType::_2D && |
| gl::RangeUI(mipMin, mipMax) |
| .intersects(gl::RangeUI(desc.Texture2D.MostDetailedMip, maxSrvMip)); |
| } |
| |
| case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: |
| { |
| GLint layerIndex = index.getLayerIndex(); |
| |
| bool allLevels = (desc.Texture2DArray.MipLevels == std::numeric_limits<UINT>::max()); |
| unsigned int maxSrvMip = |
| desc.Texture2DArray.MipLevels + desc.Texture2DArray.MostDetailedMip; |
| maxSrvMip = allLevels ? INT_MAX : maxSrvMip; |
| |
| unsigned maxSlice = desc.Texture2DArray.FirstArraySlice + desc.Texture2DArray.ArraySize; |
| |
| // Cube maps can be mapped to Texture2DArray SRVs |
| return (textureType == gl::TextureType::_2DArray || |
| textureType == gl::TextureType::CubeMap) && |
| desc.Texture2DArray.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip && |
| desc.Texture2DArray.FirstArraySlice <= static_cast<UINT>(layerIndex) && |
| static_cast<UINT>(layerIndex) < maxSlice; |
| } |
| |
| case D3D11_SRV_DIMENSION_TEXTURECUBE: |
| { |
| 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 textureType == gl::TextureType::CubeMap && |
| desc.TextureCube.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; |
| } |
| |
| case D3D11_SRV_DIMENSION_TEXTURE3D: |
| { |
| 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 textureType == gl::TextureType::_3D && |
| desc.Texture3D.MostDetailedMip <= mipLevel && mipLevel < maxSrvMip; |
| } |
| default: |
| // We only handle the cases corresponding to valid image indexes |
| UNIMPLEMENTED(); |
| } |
| |
| return false; |
| } |
| |
| bool ImageIndexConflictsWithUAV(const gl::ImageIndex &index, D3D11_UNORDERED_ACCESS_VIEW_DESC desc) |
| { |
| unsigned mipLevel = index.getLevelIndex(); |
| gl::TextureType textureType = index.getType(); |
| |
| switch (desc.ViewDimension) |
| { |
| case D3D11_UAV_DIMENSION_TEXTURE2D: |
| { |
| return textureType == gl::TextureType::_2D && mipLevel == desc.Texture2D.MipSlice; |
| } |
| |
| case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: |
| { |
| GLint layerIndex = index.getLayerIndex(); |
| unsigned mipSlice = desc.Texture2DArray.MipSlice; |
| unsigned firstArraySlice = desc.Texture2DArray.FirstArraySlice; |
| unsigned lastArraySlice = firstArraySlice + desc.Texture2DArray.ArraySize; |
| |
| return (textureType == gl::TextureType::_2DArray || |
| textureType == gl::TextureType::CubeMap) && |
| (mipLevel == mipSlice && gl::RangeUI(firstArraySlice, lastArraySlice) |
| .contains(static_cast<UINT>(layerIndex))); |
| } |
| |
| case D3D11_UAV_DIMENSION_TEXTURE3D: |
| { |
| GLint layerIndex = index.getLayerIndex(); |
| unsigned mipSlice = desc.Texture3D.MipSlice; |
| unsigned firstWSlice = desc.Texture3D.FirstWSlice; |
| unsigned lastWSlice = firstWSlice + desc.Texture3D.WSize; |
| |
| return textureType == gl::TextureType::_3D && |
| (mipLevel == mipSlice && |
| gl::RangeUI(firstWSlice, lastWSlice).contains(static_cast<UINT>(layerIndex))); |
| } |
| default: |
| return false; |
| } |
| } |
| |
| // Does *not* increment the resource ref count!! |
| ID3D11Resource *GetViewResource(ID3D11View *view) |
| { |
| ID3D11Resource *resource = nullptr; |
| ASSERT(view); |
| view->GetResource(&resource); |
| resource->Release(); |
| return resource; |
| } |
| |
| int GetWrapBits(GLenum wrap) |
| { |
| switch (wrap) |
| { |
| case GL_CLAMP_TO_EDGE: |
| return 0x0; |
| case GL_REPEAT: |
| return 0x1; |
| case GL_MIRRORED_REPEAT: |
| return 0x2; |
| case GL_CLAMP_TO_BORDER: |
| 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 ProgramD3D &programD3D, |
| const std::vector<TranslatedAttribute> &vertexArrayAttribs, |
| const std::vector<TranslatedAttribute> ¤tValueAttribs, |
| AttribIndexArray *sortedD3DSemanticsOut, |
| std::vector<const TranslatedAttribute *> *sortedAttributesOut) |
| { |
| sortedAttributesOut->clear(); |
| |
| const AttribIndexArray &locationToSemantic = programD3D.getAttribLocationToD3DSemantics(); |
| |
| for (auto locationIndex : programD3D.getState().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); |
| } |
| |
| size_t GetReservedBufferCount(bool usesPointSpriteEmulation) |
| { |
| return usesPointSpriteEmulation ? 1 : 0; |
| } |
| |
| bool CullsEverything(const gl::State &glState) |
| { |
| return (glState.getRasterizerState().cullFace && |
| glState.getRasterizerState().cullMode == gl::CullFaceMode::FrontAndBack); |
| } |
| } // anonymous namespace |
| |
| // StateManager11::ViewCache Implementation. |
| template <typename ViewType, typename DescType> |
| StateManager11::ViewCache<ViewType, DescType>::ViewCache() : mHighestUsedView(0) |
| { |
| } |
| |
| template <typename ViewType, typename DescType> |
| StateManager11::ViewCache<ViewType, DescType>::~ViewCache() |
| { |
| } |
| |
| template <typename ViewType, typename DescType> |
| void StateManager11::ViewCache<ViewType, DescType>::update(size_t resourceIndex, ViewType *view) |
| { |
| ASSERT(resourceIndex < mCurrentViews.size()); |
| ViewRecord<DescType> *record = &mCurrentViews[resourceIndex]; |
| |
| record->view = reinterpret_cast<uintptr_t>(view); |
| if (view) |
| { |
| record->resource = reinterpret_cast<uintptr_t>(GetViewResource(view)); |
| view->GetDesc(&record->desc); |
| mHighestUsedView = std::max(resourceIndex + 1, mHighestUsedView); |
| } |
| else |
| { |
| record->resource = 0; |
| |
| if (resourceIndex + 1 == mHighestUsedView) |
| { |
| do |
| { |
| --mHighestUsedView; |
| } while (mHighestUsedView > 0 && mCurrentViews[mHighestUsedView].view == 0); |
| } |
| } |
| } |
| |
| template <typename ViewType, typename DescType> |
| void StateManager11::ViewCache<ViewType, DescType>::clear() |
| { |
| if (mCurrentViews.empty()) |
| { |
| return; |
| } |
| |
| memset(&mCurrentViews[0], 0, sizeof(ViewRecord<DescType>) * mCurrentViews.size()); |
| mHighestUsedView = 0; |
| } |
| |
| StateManager11::SRVCache *StateManager11::getSRVCache(gl::ShaderType shaderType) |
| { |
| ASSERT(shaderType != gl::ShaderType::InvalidEnum); |
| return &mCurShaderSRVs[shaderType]; |
| } |
| |
| // ShaderConstants11 implementation |
| ShaderConstants11::ShaderConstants11() : mNumActiveShaderSamplers({}) |
| { |
| mShaderConstantsDirty.set(); |
| } |
| |
| ShaderConstants11::~ShaderConstants11() {} |
| |
| void ShaderConstants11::init(const gl::Caps &caps) |
| { |
| for (gl::ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| mShaderSamplerMetadata[shaderType].resize(caps.maxShaderTextureImageUnits[shaderType]); |
| mShaderReadonlyImageMetadata[shaderType].resize(caps.maxShaderImageUniforms[shaderType]); |
| mShaderImageMetadata[shaderType].resize(caps.maxShaderImageUniforms[shaderType]); |
| } |
| } |
| |
| size_t ShaderConstants11::GetShaderConstantsStructSize(gl::ShaderType shaderType) |
| { |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| return sizeof(Vertex); |
| case gl::ShaderType::Fragment: |
| return sizeof(Pixel); |
| case gl::ShaderType::Compute: |
| return sizeof(Compute); |
| |
| // TODO(jiawei.shao@intel.com): return geometry shader constant struct size |
| case gl::ShaderType::Geometry: |
| return 0u; |
| |
| default: |
| UNREACHABLE(); |
| return 0u; |
| } |
| } |
| |
| size_t ShaderConstants11::getRequiredBufferSize(gl::ShaderType shaderType) const |
| { |
| ASSERT(shaderType != gl::ShaderType::InvalidEnum); |
| return GetShaderConstantsStructSize(shaderType) + |
| mShaderSamplerMetadata[shaderType].size() * sizeof(SamplerMetadata) + |
| mShaderImageMetadata[shaderType].size() * sizeof(ImageMetadata) + |
| mShaderReadonlyImageMetadata[shaderType].size() * sizeof(ImageMetadata); |
| } |
| |
| void ShaderConstants11::markDirty() |
| { |
| mShaderConstantsDirty.set(); |
| mNumActiveShaderSamplers.fill(0); |
| } |
| |
| bool ShaderConstants11::updateSamplerMetadata(SamplerMetadata *data, |
| const gl::Texture &texture, |
| const gl::SamplerState &samplerState) |
| { |
| bool dirty = false; |
| unsigned int baseLevel = texture.getTextureState().getEffectiveBaseLevel(); |
| gl::TextureTarget target = (texture.getType() == gl::TextureType::CubeMap) |
| ? gl::kCubeMapTextureTargetMin |
| : gl::NonCubeTextureTypeToTarget(texture.getType()); |
| GLenum sizedFormat = texture.getFormat(target, 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 two 4-integer |
| // vectors. |
| GLenum wrapS = samplerState.getWrapS(); |
| GLenum wrapT = samplerState.getWrapT(); |
| GLenum wrapR = samplerState.getWrapR(); |
| int wrapModes = GetWrapBits(wrapS) | (GetWrapBits(wrapT) << 2) | (GetWrapBits(wrapR) << 4); |
| if (data->wrapModes != wrapModes) |
| { |
| data->wrapModes = wrapModes; |
| dirty = true; |
| } |
| |
| const angle::ColorGeneric &borderColor(samplerState.getBorderColor()); |
| constexpr int kBlack[4] = {}; |
| const void *const intBorderColor = (borderColor.type == angle::ColorGeneric::Type::Float) |
| ? kBlack |
| : borderColor.colorI.data(); |
| ASSERT(static_cast<const void *>(borderColor.colorI.data()) == |
| static_cast<const void *>(borderColor.colorUI.data())); |
| if (memcmp(data->intBorderColor, intBorderColor, sizeof(data->intBorderColor)) != 0) |
| { |
| memcpy(data->intBorderColor, intBorderColor, sizeof(data->intBorderColor)); |
| dirty = true; |
| } |
| } |
| |
| return dirty; |
| } |
| |
| bool ShaderConstants11::updateImageMetadata(ImageMetadata *data, const gl::ImageUnit &imageUnit) |
| { |
| bool dirty = false; |
| |
| if (data->layer != static_cast<int>(imageUnit.layer)) |
| { |
| data->layer = static_cast<int>(imageUnit.layer); |
| dirty = true; |
| } |
| |
| if (data->level != static_cast<unsigned int>(imageUnit.level)) |
| { |
| data->level = static_cast<unsigned int>(imageUnit.level); |
| 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; |
| mShaderConstantsDirty.set(gl::ShaderType::Compute); |
| } |
| |
| void ShaderConstants11::setMultiviewWriteToViewportIndex(GLfloat index) |
| { |
| mVertex.multiviewWriteToViewportIndex = index; |
| mPixel.multiviewWriteToViewportIndex = index; |
| mShaderConstantsDirty.set(gl::ShaderType::Vertex); |
| mShaderConstantsDirty.set(gl::ShaderType::Fragment); |
| } |
| |
| void ShaderConstants11::onViewportChange(const gl::Rectangle &glViewport, |
| const D3D11_VIEWPORT &dxViewport, |
| bool is9_3, |
| bool presentPathFast) |
| { |
| mShaderConstantsDirty.set(gl::ShaderType::Vertex); |
| mShaderConstantsDirty.set(gl::ShaderType::Fragment); |
| |
| // 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]; |
| } |
| |
| // Update the ShaderConstants with a new first vertex and return whether the update dirties them. |
| ANGLE_INLINE bool ShaderConstants11::onFirstVertexChange(GLint firstVertex, GLint baseVertex) |
| { |
| uint32_t newFirstVertex = static_cast<uint32_t>(firstVertex + baseVertex); |
| |
| bool firstVertexDirty = (mVertex.firstVertex != newFirstVertex); |
| if (firstVertexDirty) |
| { |
| mVertex.firstVertex = newFirstVertex; |
| mShaderConstantsDirty.set(gl::ShaderType::Vertex); |
| } |
| return firstVertexDirty; |
| } |
| |
| void ShaderConstants11::onSamplerChange(gl::ShaderType shaderType, |
| unsigned int samplerIndex, |
| const gl::Texture &texture, |
| const gl::SamplerState &samplerState) |
| { |
| ASSERT(shaderType != gl::ShaderType::InvalidEnum); |
| if (updateSamplerMetadata(&mShaderSamplerMetadata[shaderType][samplerIndex], texture, |
| samplerState)) |
| { |
| mNumActiveShaderSamplers[shaderType] = 0; |
| } |
| } |
| |
| void ShaderConstants11::onImageChange(gl::ShaderType shaderType, |
| unsigned int imageIndex, |
| const gl::ImageUnit &imageUnit) |
| { |
| ASSERT(shaderType != gl::ShaderType::InvalidEnum); |
| if (imageUnit.access == GL_READ_ONLY) |
| { |
| if (updateImageMetadata(&mShaderReadonlyImageMetadata[shaderType][imageIndex], imageUnit)) |
| { |
| mNumActiveShaderReadonlyImages[shaderType] = 0; |
| } |
| } |
| else |
| { |
| if (updateImageMetadata(&mShaderImageMetadata[shaderType][imageIndex], imageUnit)) |
| { |
| mNumActiveShaderImages[shaderType] = 0; |
| } |
| } |
| } |
| |
| angle::Result ShaderConstants11::updateBuffer(const gl::Context *context, |
| Renderer11 *renderer, |
| gl::ShaderType shaderType, |
| const ProgramD3D &programD3D, |
| const d3d11::Buffer &driverConstantBuffer) |
| { |
| // Re-upload the sampler meta-data if the current program uses more samplers |
| // than we previously uploaded. |
| const int numSamplers = programD3D.getUsedSamplerRange(shaderType).length(); |
| const int numReadonlyImages = programD3D.getUsedImageRange(shaderType, true).length(); |
| const int numImages = programD3D.getUsedImageRange(shaderType, false).length(); |
| |
| const bool dirty = mShaderConstantsDirty[shaderType] || |
| (mNumActiveShaderSamplers[shaderType] < numSamplers) || |
| (mNumActiveShaderReadonlyImages[shaderType] < numReadonlyImages) || |
| (mNumActiveShaderImages[shaderType] < numImages); |
| |
| const size_t dataSize = GetShaderConstantsStructSize(shaderType); |
| const uint8_t *samplerData = |
| reinterpret_cast<const uint8_t *>(mShaderSamplerMetadata[shaderType].data()); |
| const size_t samplerDataSize = sizeof(SamplerMetadata) * numSamplers; |
| const uint8_t *readonlyImageData = |
| reinterpret_cast<const uint8_t *>(mShaderReadonlyImageMetadata[shaderType].data()); |
| const size_t readonlyImageDataSize = sizeof(ImageMetadata) * numReadonlyImages; |
| const uint8_t *imageData = |
| reinterpret_cast<const uint8_t *>(mShaderImageMetadata[shaderType].data()); |
| const size_t imageDataSize = sizeof(ImageMetadata) * numImages; |
| |
| mNumActiveShaderSamplers[shaderType] = numSamplers; |
| mNumActiveShaderReadonlyImages[shaderType] = numReadonlyImages; |
| mNumActiveShaderImages[shaderType] = numImages; |
| mShaderConstantsDirty.set(shaderType, false); |
| |
| const uint8_t *data = nullptr; |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| data = reinterpret_cast<const uint8_t *>(&mVertex); |
| break; |
| case gl::ShaderType::Fragment: |
| data = reinterpret_cast<const uint8_t *>(&mPixel); |
| break; |
| case gl::ShaderType::Compute: |
| data = reinterpret_cast<const uint8_t *>(&mCompute); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| ASSERT(driverConstantBuffer.valid()); |
| |
| if (!dirty) |
| { |
| return angle::Result::Continue; |
| } |
| |
| // Previous buffer contents are discarded, so we need to refresh the whole buffer. |
| D3D11_MAPPED_SUBRESOURCE mapping = {}; |
| ANGLE_TRY(renderer->mapResource(context, driverConstantBuffer.get(), 0, D3D11_MAP_WRITE_DISCARD, |
| 0, &mapping)); |
| |
| memcpy(mapping.pData, data, dataSize); |
| memcpy(static_cast<uint8_t *>(mapping.pData) + dataSize, samplerData, |
| sizeof(SamplerMetadata) * numSamplers); |
| |
| memcpy(static_cast<uint8_t *>(mapping.pData) + dataSize + samplerDataSize, readonlyImageData, |
| readonlyImageDataSize); |
| memcpy( |
| static_cast<uint8_t *>(mapping.pData) + dataSize + samplerDataSize + readonlyImageDataSize, |
| imageData, imageDataSize); |
| renderer->getDeviceContext()->Unmap(driverConstantBuffer.get(), 0); |
| |
| return angle::Result::Continue; |
| } |
| |
| StateManager11::StateManager11(Renderer11 *renderer) |
| : mRenderer(renderer), |
| mInternalDirtyBits(), |
| mCurBlendColor(0, 0, 0, 0), |
| mCurSampleMask(0), |
| mCurStencilRef(0), |
| mCurStencilBackRef(0), |
| mCurStencilSize(0), |
| mCurScissorEnabled(false), |
| mCurScissorRect(), |
| mCurViewport(), |
| mCurNear(0.0f), |
| mCurFar(0.0f), |
| mViewportBounds(), |
| mRenderTargetIsDirty(true), |
| mCurPresentPathFastEnabled(false), |
| mCurPresentPathFastColorBufferHeight(0), |
| mDirtyCurrentValueAttribs(), |
| mCurrentValueAttribs(), |
| mCurrentInputLayout(), |
| mDirtyVertexBufferRange(gl::MAX_VERTEX_ATTRIBS, 0), |
| mCurrentPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED), |
| mLastAppliedDrawMode(gl::PrimitiveMode::InvalidEnum), |
| mCullEverything(false), |
| mDirtySwizzles(false), |
| mAppliedIB(nullptr), |
| mAppliedIBFormat(DXGI_FORMAT_UNKNOWN), |
| mAppliedIBOffset(0), |
| mIndexBufferIsDirty(false), |
| mVertexDataManager(renderer), |
| mIndexDataManager(renderer), |
| mIsMultiviewEnabled(false), |
| mEmptySerial(mRenderer->generateSerial()), |
| mProgramD3D(nullptr), |
| mVertexArray11(nullptr), |
| mFramebuffer11(nullptr) |
| { |
| mCurBlendState.blend = false; |
| mCurBlendState.sourceBlendRGB = GL_ONE; |
| mCurBlendState.destBlendRGB = GL_ZERO; |
| mCurBlendState.sourceBlendAlpha = GL_ONE; |
| mCurBlendState.destBlendAlpha = GL_ZERO; |
| mCurBlendState.blendEquationRGB = GL_FUNC_ADD; |
| mCurBlendState.blendEquationAlpha = GL_FUNC_ADD; |
| mCurBlendState.colorMaskRed = true; |
| mCurBlendState.colorMaskBlue = true; |
| mCurBlendState.colorMaskGreen = true; |
| mCurBlendState.colorMaskAlpha = true; |
| mCurBlendState.sampleAlphaToCoverage = false; |
| mCurBlendState.dither = false; |
| |
| mCurDepthStencilState.depthTest = false; |
| mCurDepthStencilState.depthFunc = GL_LESS; |
| mCurDepthStencilState.depthMask = true; |
| mCurDepthStencilState.stencilTest = false; |
| mCurDepthStencilState.stencilMask = true; |
| mCurDepthStencilState.stencilFail = GL_KEEP; |
| mCurDepthStencilState.stencilPassDepthFail = GL_KEEP; |
| mCurDepthStencilState.stencilPassDepthPass = GL_KEEP; |
| mCurDepthStencilState.stencilWritemask = static_cast<GLuint>(-1); |
| mCurDepthStencilState.stencilBackFunc = GL_ALWAYS; |
| mCurDepthStencilState.stencilBackMask = static_cast<GLuint>(-1); |
| mCurDepthStencilState.stencilBackFail = GL_KEEP; |
| mCurDepthStencilState.stencilBackPassDepthFail = GL_KEEP; |
| mCurDepthStencilState.stencilBackPassDepthPass = GL_KEEP; |
| mCurDepthStencilState.stencilBackWritemask = static_cast<GLuint>(-1); |
| |
| mCurRasterState.rasterizerDiscard = false; |
| mCurRasterState.cullFace = false; |
| 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 except DIRTY_BIT_COMPUTE_SRVUAV_STATE and |
| // DIRTY_BIT_GRAPHICS_SRVUAV_STATE. |
| mInternalDirtyBits.set(); |
| mInternalDirtyBits.reset(DIRTY_BIT_GRAPHICS_SRVUAV_STATE); |
| mInternalDirtyBits.reset(DIRTY_BIT_COMPUTE_SRVUAV_STATE); |
| |
| mGraphicsDirtyBitsMask.set(); |
| mGraphicsDirtyBitsMask.reset(DIRTY_BIT_COMPUTE_SRVUAV_STATE); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_PROGRAM_UNIFORMS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_DRIVER_UNIFORMS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_PROGRAM_ATOMIC_COUNTER_BUFFERS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_PROGRAM_SHADER_STORAGE_BUFFERS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_SHADERS); |
| mComputeDirtyBitsMask.set(DIRTY_BIT_COMPUTE_SRVUAV_STATE); |
| |
| // 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() {} |
| |
| template <typename SRVType> |
| void StateManager11::setShaderResourceInternal(gl::ShaderType shaderType, |
| UINT resourceSlot, |
| const SRVType *srv) |
| { |
| auto *currentSRVs = getSRVCache(shaderType); |
| ASSERT(static_cast<size_t>(resourceSlot) < currentSRVs->size()); |
| const ViewRecord<D3D11_SHADER_RESOURCE_VIEW_DESC> &record = (*currentSRVs)[resourceSlot]; |
| |
| if (record.view != reinterpret_cast<uintptr_t>(srv)) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| ID3D11ShaderResourceView *srvPtr = srv ? srv->get() : nullptr; |
| if (srvPtr) |
| { |
| uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(srvPtr)); |
| unsetConflictingUAVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Compute, |
| resource, nullptr); |
| } |
| |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| deviceContext->VSSetShaderResources(resourceSlot, 1, &srvPtr); |
| break; |
| case gl::ShaderType::Fragment: |
| deviceContext->PSSetShaderResources(resourceSlot, 1, &srvPtr); |
| break; |
| case gl::ShaderType::Compute: |
| { |
| if (srvPtr) |
| { |
| uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(srvPtr)); |
| unsetConflictingRTVs(resource); |
| } |
| deviceContext->CSSetShaderResources(resourceSlot, 1, &srvPtr); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| |
| currentSRVs->update(resourceSlot, srvPtr); |
| } |
| } |
| |
| template <typename UAVType> |
| void StateManager11::setUnorderedAccessViewInternal(gl::ShaderType shaderType, |
| UINT resourceSlot, |
| const UAVType *uav) |
| { |
| ASSERT(shaderType == gl::ShaderType::Compute); |
| ASSERT(static_cast<size_t>(resourceSlot) < mCurComputeUAVs.size()); |
| const ViewRecord<D3D11_UNORDERED_ACCESS_VIEW_DESC> &record = mCurComputeUAVs[resourceSlot]; |
| |
| if (record.view != reinterpret_cast<uintptr_t>(uav)) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| ID3D11UnorderedAccessView *uavPtr = uav ? uav->get() : nullptr; |
| // We need to make sure that resource being set to UnorderedAccessView slot |resourceSlot| |
| // is not bound on SRV. |
| if (uavPtr) |
| { |
| uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(uavPtr)); |
| unsetConflictingSRVs(gl::PipelineType::ComputePipeline, gl::ShaderType::Vertex, |
| resource, nullptr, false); |
| unsetConflictingSRVs(gl::PipelineType::ComputePipeline, gl::ShaderType::Fragment, |
| resource, nullptr, false); |
| unsetConflictingSRVs(gl::PipelineType::ComputePipeline, gl::ShaderType::Compute, |
| resource, nullptr, false); |
| } |
| deviceContext->CSSetUnorderedAccessViews(resourceSlot, 1, &uavPtr, nullptr); |
| |
| mCurComputeUAVs.update(resourceSlot, uavPtr); |
| } |
| } |
| |
| void StateManager11::updateStencilSizeIfChanged(bool depthStencilInitialized, |
| unsigned int stencilSize) |
| { |
| if (!depthStencilInitialized || stencilSize != mCurStencilSize) |
| { |
| mCurStencilSize = stencilSize; |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| } |
| |
| void StateManager11::checkPresentPath(const gl::Context *context) |
| { |
| if (!mRenderer->presentPathFastEnabled()) |
| return; |
| |
| const auto *framebuffer = context->getState().getDrawFramebuffer(); |
| const auto *firstColorAttachment = framebuffer->getFirstColorAttachment(); |
| const bool presentPathFastActive = UsePresentPathFast(mRenderer, firstColorAttachment); |
| |
| const int colorBufferHeight = firstColorAttachment ? firstColorAttachment->getSize().height : 0; |
| |
| if ((mCurPresentPathFastEnabled != presentPathFastActive) || |
| (presentPathFastActive && (colorBufferHeight != mCurPresentPathFastColorBufferHeight))) |
| { |
| mCurPresentPathFastEnabled = presentPathFastActive; |
| mCurPresentPathFastColorBufferHeight = colorBufferHeight; |
| |
| // 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); |
| } |
| } |
| |
| angle::Result StateManager11::updateStateForCompute(const gl::Context *context, |
| GLuint numGroupsX, |
| GLuint numGroupsY, |
| GLuint numGroupsZ) |
| { |
| mShaderConstants.setComputeWorkGroups(numGroupsX, numGroupsY, numGroupsZ); |
| |
| if (mProgramD3D->updateSamplerMapping() == ProgramD3D::SamplerMapping::WasDirty) |
| { |
| invalidateTexturesAndSamplers(); |
| } |
| |
| if (mDirtySwizzles) |
| { |
| ANGLE_TRY(generateSwizzlesForShader(context, gl::ShaderType::Compute)); |
| mDirtySwizzles = false; |
| } |
| |
| if (mProgramD3D->anyShaderUniformsDirty()) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_UNIFORMS); |
| } |
| |
| auto dirtyBitsCopy = mInternalDirtyBits & mComputeDirtyBitsMask; |
| mInternalDirtyBits &= ~mComputeDirtyBitsMask; |
| |
| for (auto iter = dirtyBitsCopy.begin(), end = dirtyBitsCopy.end(); iter != end; ++iter) |
| { |
| switch (*iter) |
| { |
| case DIRTY_BIT_COMPUTE_SRVUAV_STATE: |
| // Avoid to call syncTexturesForCompute function two times. |
| iter.resetLaterBit(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| ANGLE_TRY(syncTexturesForCompute(context)); |
| break; |
| case DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE: |
| ANGLE_TRY(syncTexturesForCompute(context)); |
| break; |
| case DIRTY_BIT_PROGRAM_UNIFORMS: |
| case DIRTY_BIT_DRIVER_UNIFORMS: |
| ANGLE_TRY(applyComputeUniforms(context, mProgramD3D)); |
| break; |
| case DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS: |
| ANGLE_TRY(syncUniformBuffers(context)); |
| break; |
| case DIRTY_BIT_PROGRAM_ATOMIC_COUNTER_BUFFERS: |
| ANGLE_TRY(syncAtomicCounterBuffers(context)); |
| break; |
| case DIRTY_BIT_PROGRAM_SHADER_STORAGE_BUFFERS: |
| ANGLE_TRY(syncShaderStorageBuffers(context)); |
| break; |
| case DIRTY_BIT_SHADERS: |
| ANGLE_TRY(syncProgramForCompute(context)); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits) |
| { |
| if (!dirtyBits.any()) |
| { |
| return; |
| } |
| |
| const gl::State &state = context->getState(); |
| |
| for (size_t dirtyBit : dirtyBits) |
| { |
| switch (dirtyBit) |
| { |
| case gl::State::DIRTY_BIT_BLEND_EQUATIONS: |
| { |
| const gl::BlendState &blendState = state.getBlendState(); |
| if (blendState.blendEquationRGB != mCurBlendState.blendEquationRGB || |
| blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_BLEND_FUNCS: |
| { |
| const gl::BlendState &blendState = state.getBlendState(); |
| if (blendState.sourceBlendRGB != mCurBlendState.sourceBlendRGB || |
| blendState.destBlendRGB != mCurBlendState.destBlendRGB || |
| blendState.sourceBlendAlpha != mCurBlendState.sourceBlendAlpha || |
| blendState.destBlendAlpha != mCurBlendState.destBlendAlpha) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_BLEND_ENABLED: |
| if (state.getBlendState().blend != mCurBlendState.blend) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: |
| if (state.getBlendState().sampleAlphaToCoverage != |
| mCurBlendState.sampleAlphaToCoverage) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_DITHER_ENABLED: |
| if (state.getBlendState().dither != mCurBlendState.dither) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_COLOR_MASK: |
| { |
| const gl::BlendState &blendState = state.getBlendState(); |
| if (blendState.colorMaskRed != mCurBlendState.colorMaskRed || |
| blendState.colorMaskGreen != mCurBlendState.colorMaskGreen || |
| blendState.colorMaskBlue != mCurBlendState.colorMaskBlue || |
| blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_BLEND_COLOR: |
| if (state.getBlendColor() != mCurBlendColor) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| break; |
| // Depth and stencil redundant state changes are guarded in the |
| // frontend so for related cases here just set the dirty bit. |
| case gl::State::DIRTY_BIT_DEPTH_MASK: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_DEPTH_FUNC: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_OPS_BACK: |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| break; |
| |
| case gl::State::DIRTY_BIT_CULL_FACE_ENABLED: |
| if (state.getRasterizerState().cullFace != mCurRasterState.cullFace) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| } |
| break; |
| case gl::State::DIRTY_BIT_CULL_FACE: |
| if (state.getRasterizerState().cullMode != mCurRasterState.cullMode) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| } |
| break; |
| case gl::State::DIRTY_BIT_FRONT_FACE: |
| if (state.getRasterizerState().frontFace != mCurRasterState.frontFace) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| } |
| break; |
| case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: |
| if (state.getRasterizerState().polygonOffsetFill != |
| mCurRasterState.polygonOffsetFill) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_POLYGON_OFFSET: |
| { |
| const gl::RasterizerState &rasterState = state.getRasterizerState(); |
| if (rasterState.polygonOffsetFactor != mCurRasterState.polygonOffsetFactor || |
| rasterState.polygonOffsetUnits != mCurRasterState.polygonOffsetUnits) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED: |
| if (state.getRasterizerState().rasterizerDiscard != |
| mCurRasterState.rasterizerDiscard) |
| { |
| 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) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED: |
| if (state.isScissorTestEnabled() != mCurScissorEnabled) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); |
| // Rasterizer state update needs mCurScissorsEnabled and updates when it changes |
| mInternalDirtyBits.set(DIRTY_BIT_RASTERIZER_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_DEPTH_RANGE: |
| invalidateViewport(context); |
| break; |
| case gl::State::DIRTY_BIT_VIEWPORT: |
| if (state.getViewport() != mCurViewport) |
| { |
| invalidateViewport(context); |
| } |
| break; |
| case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: |
| invalidateRenderTarget(); |
| if (mIsMultiviewEnabled) |
| { |
| handleMultiviewDrawFramebufferChange(context); |
| } |
| mFramebuffer11 = GetImplAs<Framebuffer11>(state.getDrawFramebuffer()); |
| 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. |
| invalidateIndexBuffer(); |
| mVertexArray11 = GetImplAs<VertexArray11>(state.getVertexArray()); |
| break; |
| case gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS: |
| invalidateProgramUniformBuffers(); |
| break; |
| case gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING: |
| invalidateProgramAtomicCounterBuffers(); |
| break; |
| case gl::State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING: |
| invalidateProgramShaderStorageBuffers(); |
| break; |
| case gl::State::DIRTY_BIT_TEXTURE_BINDINGS: |
| invalidateTexturesAndSamplers(); |
| break; |
| case gl::State::DIRTY_BIT_SAMPLER_BINDINGS: |
| invalidateTexturesAndSamplers(); |
| break; |
| case gl::State::DIRTY_BIT_IMAGE_BINDINGS: |
| // TODO(jie.a.chen@intel.com): More fine-grained update. |
| // Currently images are updated together with textures and samplers. It would be |
| // better to update them separately. |
| // http://anglebug.com/2814 |
| invalidateTexturesAndSamplers(); |
| break; |
| case gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING: |
| invalidateTransformFeedback(); |
| break; |
| case gl::State::DIRTY_BIT_PROGRAM_BINDING: |
| mProgramD3D = GetImplAs<ProgramD3D>(state.getProgram()); |
| break; |
| case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: |
| { |
| invalidateShaders(); |
| invalidateTexturesAndSamplers(); |
| invalidateProgramUniforms(); |
| invalidateProgramUniformBuffers(); |
| invalidateProgramAtomicCounterBuffers(); |
| invalidateProgramShaderStorageBuffers(); |
| invalidateDriverUniforms(); |
| const gl::Program *program = state.getProgram(); |
| if (!program || !program->hasLinkedShaderStage(gl::ShaderType::Compute)) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| invalidateVertexBuffer(); |
| invalidateRenderTarget(); |
| // If OVR_multiview2 is enabled, the attribute divisor has to be updated for |
| // each binding. When using compute, there could be no vertex array. |
| if (mIsMultiviewEnabled && mVertexArray11) |
| { |
| ASSERT(mProgramD3D); |
| ASSERT(mVertexArray11 == GetImplAs<VertexArray11>(state.getVertexArray())); |
| const gl::ProgramState &programState = mProgramD3D->getState(); |
| int numViews = |
| programState.usesMultiview() ? programState.getNumViews() : 1; |
| mVertexArray11->markAllAttributeDivisorsForAdjustment(numViews); |
| } |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_CURRENT_VALUES: |
| { |
| for (auto attribIndex : state.getAndResetDirtyCurrentValues()) |
| { |
| invalidateCurrentValueAttrib(attribIndex); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_PROVOKING_VERTEX: |
| invalidateShaders(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // TODO(jmadill): Input layout and vertex buffer state. |
| } |
| |
| void StateManager11::handleMultiviewDrawFramebufferChange(const gl::Context *context) |
| { |
| const auto &glState = context->getState(); |
| const gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer(); |
| ASSERT(drawFramebuffer != nullptr); |
| |
| if (drawFramebuffer->isMultiview()) |
| { |
| // 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); |
| } |
| } |
| |
| angle::Result StateManager11::syncBlendState(const gl::Context *context, |
| const gl::BlendState &blendState, |
| const gl::ColorF &blendColor, |
| unsigned int sampleMask) |
| { |
| const d3d11::BlendState *dxBlendState = nullptr; |
| const d3d11::BlendStateKey &key = |
| RenderStateCache::GetBlendStateKey(context, mFramebuffer11, blendState); |
| |
| ANGLE_TRY(mRenderer->getBlendState(context, key, &dxBlendState)); |
| |
| ASSERT(dxBlendState != nullptr); |
| |
| float blendColors[4] = {0.0f}; |
| if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && |
| blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && |
| blendState.destBlendRGB != GL_CONSTANT_ALPHA && |
| blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) |
| { |
| blendColors[0] = blendColor.red; |
| blendColors[1] = blendColor.green; |
| blendColors[2] = blendColor.blue; |
| blendColors[3] = blendColor.alpha; |
| } |
| else |
| { |
| blendColors[0] = blendColor.alpha; |
| blendColors[1] = blendColor.alpha; |
| blendColors[2] = blendColor.alpha; |
| blendColors[3] = blendColor.alpha; |
| } |
| |
| mRenderer->getDeviceContext()->OMSetBlendState(dxBlendState->get(), blendColors, sampleMask); |
| |
| mCurBlendState = blendState; |
| mCurBlendColor = blendColor; |
| mCurSampleMask = sampleMask; |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncDepthStencilState(const gl::Context *context) |
| { |
| const gl::State &glState = context->getState(); |
| |
| mCurDepthStencilState = glState.getDepthStencilState(); |
| mCurStencilRef = glState.getStencilRef(); |
| mCurStencilBackRef = glState.getStencilBackRef(); |
| |
| // get the maximum size of the stencil ref |
| unsigned int maxStencil = 0; |
| if (mCurDepthStencilState.stencilTest && mCurStencilSize > 0) |
| { |
| maxStencil = (1 << mCurStencilSize) - 1; |
| } |
| ASSERT((mCurDepthStencilState.stencilWritemask & maxStencil) == |
| (mCurDepthStencilState.stencilBackWritemask & maxStencil)); |
| ASSERT(gl::clamp(mCurStencilRef, 0, static_cast<int>(maxStencil)) == |
| gl::clamp(mCurStencilBackRef, 0, static_cast<int>(maxStencil))); |
| ASSERT((mCurDepthStencilState.stencilMask & maxStencil) == |
| (mCurDepthStencilState.stencilBackMask & maxStencil)); |
| |
| gl::DepthStencilState modifiedGLState = glState.getDepthStencilState(); |
| |
| ASSERT(mCurDisableDepth.valid() && mCurDisableStencil.valid()); |
| |
| if (mCurDisableDepth.value()) |
| { |
| modifiedGLState.depthTest = false; |
| modifiedGLState.depthMask = false; |
| } |
| |
| if (mCurDisableStencil.value()) |
| { |
| modifiedGLState.stencilTest = false; |
| } |
| if (!modifiedGLState.stencilTest) |
| { |
| modifiedGLState.stencilWritemask = 0; |
| modifiedGLState.stencilBackWritemask = 0; |
| } |
| |
| // If STENCIL_TEST is disabled in glState, stencil testing and writing should be disabled. |
| // Verify that's true in the modifiedGLState so it is propagated to d3dState. |
| ASSERT(glState.getDepthStencilState().stencilTest || |
| (!modifiedGLState.stencilTest && modifiedGLState.stencilWritemask == 0 && |
| modifiedGLState.stencilBackWritemask == 0)); |
| |
| const d3d11::DepthStencilState *d3dState = nullptr; |
| ANGLE_TRY(mRenderer->getDepthStencilState(context, modifiedGLState, &d3dState)); |
| ASSERT(d3dState); |
| |
| // Max D3D11 stencil reference value is 0xFF, |
| // corresponding to the max 8 bits in a stencil buffer |
| // GL specifies we should clamp the ref value to the |
| // nearest bit depth when doing stencil ops |
| static_assert(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF, |
| "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 = static_cast<UINT>(gl::clamp(mCurStencilRef, 0, 0xFF)); |
| |
| mRenderer->getDeviceContext()->OMSetDepthStencilState(d3dState->get(), dxStencilRef); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncRasterizerState(const gl::Context *context, |
| gl::PrimitiveMode mode) |
| { |
| // TODO: Remove pointDrawMode and multiSample from gl::RasterizerState. |
| gl::RasterizerState rasterState = context->getState().getRasterizerState(); |
| rasterState.pointDrawMode = (mode == gl::PrimitiveMode::Points); |
| rasterState.multiSample = mCurRasterState.multiSample; |
| |
| ID3D11RasterizerState *dxRasterState = nullptr; |
| |
| if (mCurPresentPathFastEnabled) |
| { |
| gl::RasterizerState modifiedRasterState = rasterState; |
| |
| // If prseent path fast is active then we need invert the front face state. |
| // This ensures that both gl_FrontFacing is correct, and front/back culling |
| // is performed correctly. |
| if (modifiedRasterState.frontFace == GL_CCW) |
| { |
| modifiedRasterState.frontFace = GL_CW; |
| } |
| else |
| { |
| ASSERT(modifiedRasterState.frontFace == GL_CW); |
| modifiedRasterState.frontFace = GL_CCW; |
| } |
| |
| ANGLE_TRY(mRenderer->getRasterizerState(context, modifiedRasterState, mCurScissorEnabled, |
| &dxRasterState)); |
| } |
| else |
| { |
| ANGLE_TRY(mRenderer->getRasterizerState(context, rasterState, mCurScissorEnabled, |
| &dxRasterState)); |
| } |
| |
| mRenderer->getDeviceContext()->RSSetState(dxRasterState); |
| |
| mCurRasterState = rasterState; |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::syncScissorRectangle(const gl::Rectangle &scissor, bool enabled) |
| { |
| int modifiedScissorY = scissor.y; |
| if (mCurPresentPathFastEnabled) |
| { |
| modifiedScissorY = mCurPresentPathFastColorBufferHeight - scissor.height - scissor.y; |
| } |
| |
| if (enabled) |
| { |
| D3D11_RECT rect; |
| int x = scissor.x; |
| int y = modifiedScissorY; |
| 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(1, &rect); |
| } |
| |
| mCurScissorRect = scissor; |
| mCurScissorEnabled = enabled; |
| } |
| |
| void StateManager11::syncViewport(const gl::Context *context) |
| { |
| const auto &glState = context->getState(); |
| gl::Framebuffer *framebuffer = glState.getDrawFramebuffer(); |
| float actualZNear = gl::clamp01(glState.getNearPlane()); |
| float actualZFar = gl::clamp01(glState.getFarPlane()); |
| |
| const auto &caps = context->getCaps(); |
| int dxMaxViewportBoundsX = caps.maxViewportWidth; |
| int dxMaxViewportBoundsY = caps.maxViewportHeight; |
| int dxMinViewportBoundsX = -dxMaxViewportBoundsX; |
| int dxMinViewportBoundsY = -dxMaxViewportBoundsY; |
| |
| 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); |
| dxMaxViewportBoundsY = static_cast<int>(mViewportBounds.height); |
| dxMinViewportBoundsX = 0; |
| dxMinViewportBoundsY = 0; |
| } |
| |
| const auto &viewport = glState.getViewport(); |
| |
| int dxViewportTopLeftX = 0; |
| int dxViewportTopLeftY = 0; |
| int dxViewportWidth = 0; |
| int dxViewportHeight = 0; |
| |
| dxViewportTopLeftX = gl::clamp(viewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX); |
| dxViewportTopLeftY = gl::clamp(viewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY); |
| dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); |
| dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); |
| |
| D3D11_VIEWPORT dxViewport; |
| 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); |
| } |
| |
| // 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); |
| |
| mCurViewport = viewport; |
| mCurNear = actualZNear; |
| mCurFar = actualZFar; |
| |
| 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) |
| { |
| ASSERT(mRenderTargetIsDirty); |
| ASSERT(context); |
| |
| 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->getState().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, gl::AttachmentSampleType::Emulated) != 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) |
| { |
| 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); |
| } |
| } |
| } |
| } |
| |
| void StateManager11::invalidateBoundViews() |
| { |
| for (SRVCache &curShaderSRV : mCurShaderSRVs) |
| { |
| curShaderSRV.clear(); |
| } |
| |
| invalidateRenderTarget(); |
| } |
| |
| void StateManager11::invalidateVertexBuffer() |
| { |
| unsigned int limit = std::min<unsigned int>(mRenderer->getNativeCaps().maxVertexAttributes, |
| gl::MAX_VERTEX_ATTRIBS); |
| mDirtyVertexBufferRange = gl::RangeUI(0, limit); |
| invalidateInputLayout(); |
| invalidateShaders(); |
| mInternalDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_ATTRIBS); |
| } |
| |
| void StateManager11::invalidateViewport(const gl::Context *context) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_VIEWPORT_STATE); |
| |
| // Viewport affects the driver constants. |
| invalidateDriverUniforms(); |
| } |
| |
| void StateManager11::invalidateTexturesAndSamplers() |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| invalidateSwizzles(); |
| |
| // Texture state affects the driver uniforms (base level, etc). |
| invalidateDriverUniforms(); |
| } |
| |
| void StateManager11::invalidateSwizzles() |
| { |
| mDirtySwizzles = true; |
| } |
| |
| 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::invalidateProgramAtomicCounterBuffers() |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_ATOMIC_COUNTER_BUFFERS); |
| } |
| |
| void StateManager11::invalidateProgramShaderStorageBuffers() |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_SHADER_STORAGE_BUFFERS); |
| } |
| |
| void StateManager11::invalidateConstantBuffer(unsigned int slot) |
| { |
| if (slot == d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER) |
| { |
| invalidateDriverUniforms(); |
| } |
| else if (slot == d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK) |
| { |
| invalidateProgramUniforms(); |
| } |
| else |
| { |
| invalidateProgramUniformBuffers(); |
| } |
| } |
| |
| void StateManager11::invalidateShaders() |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_SHADERS); |
| } |
| |
| void StateManager11::invalidateTransformFeedback() |
| { |
| // Transform feedback affects the stream-out geometry shader. |
| invalidateShaders(); |
| mInternalDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK); |
| // syncPrimitiveTopology checks the transform feedback state. |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| } |
| |
| void StateManager11::invalidateInputLayout() |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS_AND_INPUT_LAYOUT); |
| } |
| |
| void StateManager11::invalidateIndexBuffer() |
| { |
| mIndexBufferIsDirty = true; |
| } |
| |
| void StateManager11::setRenderTarget(ID3D11RenderTargetView *rtv, ID3D11DepthStencilView *dsv) |
| { |
| if (rtv) |
| { |
| unsetConflictingView(gl::PipelineType::GraphicsPipeline, rtv, true); |
| } |
| |
| if (dsv) |
| { |
| unsetConflictingView(gl::PipelineType::GraphicsPipeline, dsv, true); |
| } |
| |
| mRenderer->getDeviceContext()->OMSetRenderTargets(1, &rtv, dsv); |
| mCurRTVs.clear(); |
| mCurRTVs.update(0, rtv); |
| mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); |
| } |
| |
| void StateManager11::setRenderTargets(ID3D11RenderTargetView **rtvs, |
| UINT numRTVs, |
| ID3D11DepthStencilView *dsv) |
| { |
| for (UINT rtvIndex = 0; rtvIndex < numRTVs; ++rtvIndex) |
| { |
| unsetConflictingView(gl::PipelineType::GraphicsPipeline, rtvs[rtvIndex], true); |
| } |
| |
| if (dsv) |
| { |
| unsetConflictingView(gl::PipelineType::GraphicsPipeline, dsv, true); |
| } |
| |
| mRenderer->getDeviceContext()->OMSetRenderTargets(numRTVs, (numRTVs > 0) ? rtvs : nullptr, dsv); |
| mCurRTVs.clear(); |
| for (UINT i = 0; i < numRTVs; i++) |
| { |
| mCurRTVs.update(i, rtvs[i]); |
| } |
| mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); |
| } |
| |
| void StateManager11::onBeginQuery(Query11 *query) |
| { |
| mCurrentQueries.insert(query); |
| } |
| |
| void StateManager11::onDeleteQueryObject(Query11 *query) |
| { |
| mCurrentQueries.erase(query); |
| } |
| |
| angle::Result StateManager11::onMakeCurrent(const gl::Context *context) |
| { |
| ANGLE_TRY(ensureInitialized(context)); |
| |
| const gl::State &state = context->getState(); |
| |
| Context11 *context11 = GetImplAs<Context11>(context); |
| |
| for (Query11 *query : mCurrentQueries) |
| { |
| ANGLE_TRY(query->pause(context11)); |
| } |
| mCurrentQueries.clear(); |
| |
| for (gl::QueryType type : angle::AllEnums<gl::QueryType>()) |
| { |
| gl::Query *query = state.getActiveQuery(type); |
| if (query != nullptr) |
| { |
| Query11 *query11 = GetImplAs<Query11>(query); |
| ANGLE_TRY(query11->resume(context11)); |
| mCurrentQueries.insert(query11); |
| } |
| } |
| |
| // Reset the cache objects. |
| mProgramD3D = nullptr; |
| mVertexArray11 = nullptr; |
| mFramebuffer11 = nullptr; |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::unsetConflictingView(gl::PipelineType pipeline, |
| ID3D11View *view, |
| bool isRenderTarget) |
| { |
| uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(view)); |
| |
| unsetConflictingSRVs(pipeline, gl::ShaderType::Vertex, resource, nullptr, isRenderTarget); |
| unsetConflictingSRVs(pipeline, gl::ShaderType::Fragment, resource, nullptr, isRenderTarget); |
| unsetConflictingSRVs(pipeline, gl::ShaderType::Compute, resource, nullptr, isRenderTarget); |
| unsetConflictingUAVs(pipeline, gl::ShaderType::Compute, resource, nullptr); |
| } |
| |
| void StateManager11::unsetConflictingSRVs(gl::PipelineType pipeline, |
| gl::ShaderType shaderType, |
| uintptr_t resource, |
| const gl::ImageIndex *index, |
| bool isRenderTarget) |
| { |
| auto *currentSRVs = getSRVCache(shaderType); |
| gl::PipelineType conflictPipeline = gl::GetPipelineType(shaderType); |
| bool foundOne = false; |
| size_t count = std::min(currentSRVs->size(), currentSRVs->highestUsed()); |
| for (size_t resourceIndex = 0; resourceIndex < count; ++resourceIndex) |
| { |
| auto &record = (*currentSRVs)[resourceIndex]; |
| |
| if (record.view && record.resource == resource && |
| (!index || ImageIndexConflictsWithSRV(*index, record.desc))) |
| { |
| setShaderResourceInternal<d3d11::ShaderResourceView>( |
| shaderType, static_cast<UINT>(resourceIndex), nullptr); |
| foundOne = true; |
| } |
| } |
| |
| if (foundOne && (pipeline != conflictPipeline || isRenderTarget)) |
| { |
| switch (conflictPipeline) |
| { |
| case gl::PipelineType::GraphicsPipeline: |
| mInternalDirtyBits.set(DIRTY_BIT_GRAPHICS_SRVUAV_STATE); |
| break; |
| case gl::PipelineType::ComputePipeline: |
| mInternalDirtyBits.set(DIRTY_BIT_COMPUTE_SRVUAV_STATE); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| } |
| |
| void StateManager11::unsetConflictingUAVs(gl::PipelineType pipeline, |
| gl::ShaderType shaderType, |
| uintptr_t resource, |
| const gl::ImageIndex *index) |
| { |
| ASSERT(shaderType == gl::ShaderType::Compute); |
| bool foundOne = false; |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| size_t count = std::min(mCurComputeUAVs.size(), mCurComputeUAVs.highestUsed()); |
| for (size_t resourceIndex = 0; resourceIndex < count; ++resourceIndex) |
| { |
| auto &record = mCurComputeUAVs[resourceIndex]; |
| |
| if (record.view && record.resource == resource && |
| (!index || ImageIndexConflictsWithUAV(*index, record.desc))) |
| { |
| deviceContext->CSSetUnorderedAccessViews(static_cast<UINT>(resourceIndex), 1, |
| &mNullUAVs[0], nullptr); |
| mCurComputeUAVs.update(resourceIndex, nullptr); |
| foundOne = true; |
| } |
| } |
| |
| if (foundOne && pipeline == gl::PipelineType::GraphicsPipeline) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_COMPUTE_SRVUAV_STATE); |
| } |
| } |
| |
| void StateManager11::unsetConflictingRTVs(uintptr_t resource) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| size_t count = std::min(mCurRTVs.size(), mCurRTVs.highestUsed()); |
| for (size_t resourceIndex = 0; resourceIndex < count; ++resourceIndex) |
| { |
| auto &record = mCurRTVs[resourceIndex]; |
| |
| if (record.view && record.resource == resource) |
| { |
| deviceContext->OMSetRenderTargets(0, nullptr, nullptr); |
| mCurRTVs.clear(); |
| mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); |
| return; |
| } |
| } |
| } |
| |
| 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::PipelineType::GraphicsPipeline, gl::ShaderType::Vertex, |
| resourcePtr, &index, false); |
| unsetConflictingSRVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Fragment, |
| resourcePtr, &index, false); |
| unsetConflictingSRVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Compute, |
| resourcePtr, &index, false); |
| unsetConflictingUAVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Compute, |
| resourcePtr, &index); |
| } |
| else if (attachment.type() == GL_FRAMEBUFFER_DEFAULT) |
| { |
| uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource); |
| unsetConflictingSRVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Vertex, |
| resourcePtr, nullptr, false); |
| unsetConflictingSRVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Fragment, |
| resourcePtr, nullptr, false); |
| unsetConflictingSRVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Compute, |
| resourcePtr, nullptr, false); |
| unsetConflictingUAVs(gl::PipelineType::GraphicsPipeline, gl::ShaderType::Compute, |
| resourcePtr, nullptr); |
| } |
| } |
| |
| angle::Result StateManager11::ensureInitialized(const gl::Context *context) |
| { |
| Renderer11 *renderer = GetImplAs<Context11>(context)->getRenderer(); |
| |
| const gl::Caps &caps = renderer->getNativeCaps(); |
| const gl::Extensions &extensions = renderer->getNativeExtensions(); |
| |
| for (gl::ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| const GLuint maxShaderTextureImageUnits = |
| static_cast<GLuint>(caps.maxShaderTextureImageUnits[shaderType]); |
| |
| mCurShaderSRVs[shaderType].initialize(maxShaderTextureImageUnits); |
| mForceSetShaderSamplerStates[shaderType].resize(maxShaderTextureImageUnits, true); |
| mCurShaderSamplerStates[shaderType].resize(maxShaderTextureImageUnits); |
| } |
| mCurRTVs.initialize(caps.maxColorAttachments); |
| mCurComputeUAVs.initialize(caps.maxImageUnits); |
| |
| // Initialize cached NULL SRV block |
| mNullSRVs.resize(caps.maxShaderTextureImageUnits[gl::ShaderType::Fragment], nullptr); |
| |
| mNullUAVs.resize(caps.maxImageUnits, nullptr); |
| |
| mCurrentValueAttribs.resize(caps.maxVertexAttributes); |
| |
| mShaderConstants.init(caps); |
| |
| mIsMultiviewEnabled = extensions.multiview || extensions.multiview2; |
| |
| ANGLE_TRY(mVertexDataManager.initialize(context)); |
| |
| mCurrentAttributes.reserve(gl::MAX_VERTEX_ATTRIBS); |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::deinitialize() |
| { |
| mCurrentValueAttribs.clear(); |
| mInputLayoutCache.clear(); |
| mVertexDataManager.deinitialize(); |
| mIndexDataManager.deinitialize(); |
| |
| for (d3d11::Buffer &ShaderDriverConstantBuffer : mShaderDriverConstantBuffers) |
| { |
| ShaderDriverConstantBuffer.reset(); |
| } |
| |
| mPointSpriteVertexBuffer.reset(); |
| mPointSpriteIndexBuffer.reset(); |
| } |
| |
| // Applies the render target surface, depth stencil surface, viewport rectangle and |
| // scissor rectangle to the renderer |
| angle::Result StateManager11::syncFramebuffer(const gl::Context *context) |
| { |
| // 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 (mFramebuffer11->getState().isDefault()) |
| { |
| RenderTarget11 *firstRT = mFramebuffer11->getFirstRenderTarget(); |
| const gl::Extents &size = firstRT->getExtents(); |
| if (size.empty()) |
| { |
| return angle::Result::Continue; |
| } |
| } |
| |
| RTVArray framebufferRTVs = {{}}; |
| const auto &colorRTs = mFramebuffer11->getCachedColorRenderTargets(); |
| |
| size_t appliedRTIndex = 0; |
| bool skipInactiveRTs = mRenderer->getFeatures().mrtPerfWorkaround.enabled; |
| const auto &drawStates = mFramebuffer11->getState().getDrawBufferStates(); |
| gl::DrawBufferMask activeProgramOutputs = mProgramD3D->getState().getActiveOutputVariables(); |
| UINT maxExistingRT = 0; |
| const auto &colorAttachments = mFramebuffer11->getState().getColorAttachments(); |
| |
| for (size_t rtIndex = 0; rtIndex < colorRTs.size(); ++rtIndex) |
| { |
| const RenderTarget11 *renderTarget = colorRTs[rtIndex]; |
| |
| // Skip inactive rendertargets if the workaround is enabled. |
| if (skipInactiveRTs && |
| (!renderTarget || drawStates[rtIndex] == GL_NONE || !activeProgramOutputs[rtIndex])) |
| { |
| continue; |
| } |
| |
| if (renderTarget) |
| { |
| framebufferRTVs[appliedRTIndex] = renderTarget->getRenderTargetView().get(); |
| ASSERT(framebufferRTVs[appliedRTIndex]); |
| maxExistingRT = static_cast<UINT>(appliedRTIndex) + 1; |
| |
| // Unset conflicting texture SRVs |
| const gl::FramebufferAttachment &attachment = colorAttachments[rtIndex]; |
| ASSERT(attachment.isAttached()); |
| unsetConflictingAttachmentResources(attachment, renderTarget->getTexture().get()); |
| } |
| |
| appliedRTIndex++; |
| } |
| |
| // Get the depth stencil buffers |
| ID3D11DepthStencilView *framebufferDSV = nullptr; |
| const auto *depthStencilRenderTarget = mFramebuffer11->getCachedDepthStencilRenderTarget(); |
| if (depthStencilRenderTarget) |
| { |
| framebufferDSV = depthStencilRenderTarget->getDepthStencilView().get(); |
| ASSERT(framebufferDSV); |
| |
| // Unset conflicting texture SRVs |
| const gl::FramebufferAttachment *attachment = |
| mFramebuffer11->getState().getDepthOrStencilAttachment(); |
| ASSERT(attachment); |
| unsetConflictingAttachmentResources(*attachment, |
| depthStencilRenderTarget->getTexture().get()); |
| } |
| |
| ASSERT(maxExistingRT <= static_cast<UINT>(context->getCaps().maxDrawBuffers)); |
| |
| // Apply the render target and depth stencil |
| mRenderer->getDeviceContext()->OMSetRenderTargets(maxExistingRT, framebufferRTVs.data(), |
| framebufferDSV); |
| mCurRTVs.clear(); |
| for (UINT i = 0; i < maxExistingRT; i++) |
| { |
| mCurRTVs.update(i, framebufferRTVs[i]); |
| } |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::invalidateCurrentValueAttrib(size_t attribIndex) |
| { |
| mDirtyCurrentValueAttribs.set(attribIndex); |
| mInternalDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_ATTRIBS); |
| invalidateInputLayout(); |
| invalidateShaders(); |
| } |
| |
| angle::Result StateManager11::syncCurrentValueAttribs( |
| const gl::Context *context, |
| const std::vector<gl::VertexAttribCurrentValueData> ¤tValues) |
| { |
| const auto &activeAttribsMask = mProgramD3D->getState().getActiveAttribLocationsMask(); |
| const auto &dirtyActiveAttribs = (activeAttribsMask & mDirtyCurrentValueAttribs); |
| |
| if (!dirtyActiveAttribs.any()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| const auto &vertexAttributes = mVertexArray11->getState().getVertexAttributes(); |
| const auto &vertexBindings = mVertexArray11->getState().getVertexBindings(); |
| mDirtyCurrentValueAttribs = (mDirtyCurrentValueAttribs & ~dirtyActiveAttribs); |
| |
| for (auto attribIndex : dirtyActiveAttribs) |
| { |
| if (vertexAttributes[attribIndex].enabled) |
| continue; |
| |
| const auto *attrib = &vertexAttributes[attribIndex]; |
| const auto ¤tValue = currentValues[attribIndex]; |
| TranslatedAttribute *currentValueAttrib = &mCurrentValueAttribs[attribIndex]; |
| currentValueAttrib->currentValueType = currentValue.Type; |
| currentValueAttrib->attribute = attrib; |
| currentValueAttrib->binding = &vertexBindings[attrib->bindingIndex]; |
| |
| mDirtyVertexBufferRange.extend(static_cast<unsigned int>(attribIndex)); |
| |
| ANGLE_TRY(mVertexDataManager.storeCurrentValue(context, currentValue, currentValueAttrib, |
| static_cast<size_t>(attribIndex))); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::setInputLayout(const d3d11::InputLayout *inputLayout) |
| { |
| if (setInputLayoutInternal(inputLayout)) |
| { |
| invalidateInputLayout(); |
| } |
| } |
| |
| bool StateManager11::setInputLayoutInternal(const d3d11::InputLayout *inputLayout) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| if (inputLayout == nullptr) |
| { |
| if (!mCurrentInputLayout.empty()) |
| { |
| deviceContext->IASetInputLayout(nullptr); |
| mCurrentInputLayout.clear(); |
| return true; |
| } |
| } |
| else if (inputLayout->getSerial() != mCurrentInputLayout) |
| { |
| deviceContext->IASetInputLayout(inputLayout->get()); |
| mCurrentInputLayout = inputLayout->getSerial(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool StateManager11::queueVertexBufferChange(size_t bufferIndex, |
| ID3D11Buffer *buffer, |
| UINT stride, |
| UINT offset) |
| { |
| if (buffer != mCurrentVertexBuffers[bufferIndex] || |
| stride != mCurrentVertexStrides[bufferIndex] || |
| offset != mCurrentVertexOffsets[bufferIndex]) |
| { |
| mDirtyVertexBufferRange.extend(static_cast<unsigned int>(bufferIndex)); |
| |
| mCurrentVertexBuffers[bufferIndex] = buffer; |
| mCurrentVertexStrides[bufferIndex] = stride; |
| mCurrentVertexOffsets[bufferIndex] = offset; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| 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)) |
| { |
| invalidateInputLayout(); |
| applyVertexBufferChanges(); |
| } |
| } |
| |
| angle::Result StateManager11::updateState(const gl::Context *context, |
| gl::PrimitiveMode mode, |
| GLint firstVertex, |
| GLsizei vertexOrIndexCount, |
| gl::DrawElementsType indexTypeOrInvalid, |
| const void *indices, |
| GLsizei instanceCount, |
| GLint baseVertex) |
| { |
| const gl::State &glState = context->getState(); |
| |
| // TODO(jmadill): Use dirty bits. |
| if (mRenderTargetIsDirty) |
| { |
| processFramebufferInvalidation(context); |
| mRenderTargetIsDirty = false; |
| } |
| |
| // TODO(jmadill): Use dirty bits. |
| if (mProgramD3D->updateSamplerMapping() == ProgramD3D::SamplerMapping::WasDirty) |
| { |
| invalidateTexturesAndSamplers(); |
| } |
| |
| // TODO(jmadill): Use dirty bits. |
| if (mProgramD3D->anyShaderUniformsDirty()) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PROGRAM_UNIFORMS); |
| } |
| |
| // Swizzling can cause internal state changes with blit shaders. |
| if (mDirtySwizzles) |
| { |
| ANGLE_TRY(generateSwizzles(context)); |
| mDirtySwizzles = false; |
| } |
| |
| ANGLE_TRY(mFramebuffer11->markAttachmentsDirty(context)); |
| |
| // TODO(jiawei.shao@intel.com): This can be recomputed only on framebuffer or multisample mask |
| // state changes. |
| RenderTarget11 *firstRT = mFramebuffer11->getFirstRenderTarget(); |
| int samples = (firstRT ? firstRT->getSamples() : 0); |
| unsigned int sampleMask = GetBlendSampleMask(glState, samples); |
| if (sampleMask != mCurSampleMask) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE); |
| } |
| |
| ANGLE_TRY(mVertexArray11->syncStateForDraw(context, firstVertex, vertexOrIndexCount, |
| indexTypeOrInvalid, indices, instanceCount, |
| baseVertex)); |
| |
| // Changes in the draw call can affect the vertex buffer translations. |
| if (!mLastFirstVertex.valid() || mLastFirstVertex.value() != firstVertex) |
| { |
| mLastFirstVertex = firstVertex; |
| invalidateInputLayout(); |
| } |
| |
| // The ShaderConstants only need to be updated when the program uses vertexID |
| if (mProgramD3D->usesVertexID()) |
| { |
| if (mShaderConstants.onFirstVertexChange(firstVertex, baseVertex)) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS); |
| } |
| } |
| |
| if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum) |
| { |
| ANGLE_TRY(applyIndexBuffer(context, vertexOrIndexCount, indexTypeOrInvalid, indices)); |
| } |
| |
| if (mLastAppliedDrawMode != mode) |
| { |
| mLastAppliedDrawMode = mode; |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| |
| bool pointDrawMode = (mode == gl::PrimitiveMode::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(); |
| } |
| } |
| |
| auto dirtyBitsCopy = mInternalDirtyBits & mGraphicsDirtyBitsMask; |
| mInternalDirtyBits &= ~mGraphicsDirtyBitsMask; |
| |
| for (auto iter = dirtyBitsCopy.begin(), end = dirtyBitsCopy.end(); iter != end; ++iter) |
| { |
| switch (*iter) |
| { |
| case DIRTY_BIT_RENDER_TARGET: |
| ANGLE_TRY(syncFramebuffer(context)); |
| 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, mode)); |
| break; |
| case DIRTY_BIT_BLEND_STATE: |
| ANGLE_TRY(syncBlendState(context, glState.getBlendState(), glState.getBlendColor(), |
| sampleMask)); |
| break; |
| case DIRTY_BIT_DEPTH_STENCIL_STATE: |
| ANGLE_TRY(syncDepthStencilState(context)); |
| break; |
| case DIRTY_BIT_GRAPHICS_SRVUAV_STATE: |
| iter.resetLaterBit(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| ANGLE_TRY(syncTextures(context)); |
| 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(context)); |
| break; |
| case DIRTY_BIT_DRIVER_UNIFORMS: |
| // This must happen after viewport sync; the viewport affects builtin uniforms. |
| ANGLE_TRY(applyDriverUniforms(context)); |
| break; |
| case DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS: |
| ANGLE_TRY(syncUniformBuffers(context)); |
| break; |
| case DIRTY_BIT_PROGRAM_ATOMIC_COUNTER_BUFFERS: |
| // TODO(jie.a.chen@intel.com): http://anglebug.com/1729 |
| break; |
| case DIRTY_BIT_PROGRAM_SHADER_STORAGE_BUFFERS: |
| // TODO(jie.a.chen@intel.com): http://anglebug.com/1951 |
| break; |
| case DIRTY_BIT_SHADERS: |
| ANGLE_TRY(syncProgram(context, mode)); |
| break; |
| case DIRTY_BIT_CURRENT_VALUE_ATTRIBS: |
| ANGLE_TRY(syncCurrentValueAttribs(context, glState.getVertexAttribCurrentValues())); |
| break; |
| case DIRTY_BIT_TRANSFORM_FEEDBACK: |
| ANGLE_TRY(syncTransformFeedbackBuffers(context)); |
| break; |
| case DIRTY_BIT_VERTEX_BUFFERS_AND_INPUT_LAYOUT: |
| ANGLE_TRY(syncVertexBuffersAndInputLayout(context, mode, firstVertex, |
| vertexOrIndexCount, indexTypeOrInvalid, |
| instanceCount)); |
| break; |
| case DIRTY_BIT_PRIMITIVE_TOPOLOGY: |
| syncPrimitiveTopology(glState, mode); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| // Check that we haven't set any dirty bits in the flushing of the dirty bits loop, except |
| // DIRTY_BIT_COMPUTE_SRVUAV_STATE dirty bit. |
| ASSERT((mInternalDirtyBits & mGraphicsDirtyBitsMask).none()); |
| |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::setShaderResourceShared(gl::ShaderType 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::ShaderType 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 (setPrimitiveTopologyInternal(primitiveTopology)) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| } |
| } |
| |
| bool StateManager11::setPrimitiveTopologyInternal(D3D11_PRIMITIVE_TOPOLOGY primitiveTopology) |
| { |
| if (primitiveTopology != mCurrentPrimitiveTopology) |
| { |
| mRenderer->getDeviceContext()->IASetPrimitiveTopology(primitiveTopology); |
| mCurrentPrimitiveTopology = primitiveTopology; |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| 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 != mAppliedShaders[gl::ShaderType::Vertex]) |
| { |
| ID3D11VertexShader *appliedShader = shader ? shader->get() : nullptr; |
| mRenderer->getDeviceContext()->VSSetShader(appliedShader, nullptr, 0); |
| mAppliedShaders[gl::ShaderType::Vertex] = serial; |
| invalidateShaders(); |
| } |
| } |
| |
| void StateManager11::setGeometryShader(const d3d11::GeometryShader *shader) |
| { |
| ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); |
| |
| if (serial != mAppliedShaders[gl::ShaderType::Geometry]) |
| { |
| ID3D11GeometryShader *appliedShader = shader ? shader->get() : nullptr; |
| mRenderer->getDeviceContext()->GSSetShader(appliedShader, nullptr, 0); |
| mAppliedShaders[gl::ShaderType::Geometry] = serial; |
| invalidateShaders(); |
| } |
| } |
| |
| void StateManager11::setPixelShader(const d3d11::PixelShader *shader) |
| { |
| ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); |
| |
| if (serial != mAppliedShaders[gl::ShaderType::Fragment]) |
| { |
| ID3D11PixelShader *appliedShader = shader ? shader->get() : nullptr; |
| mRenderer->getDeviceContext()->PSSetShader(appliedShader, nullptr, 0); |
| mAppliedShaders[gl::ShaderType::Fragment] = serial; |
| invalidateShaders(); |
| } |
| } |
| |
| void StateManager11::setComputeShader(const d3d11::ComputeShader *shader) |
| { |
| ResourceSerial serial = shader ? shader->getSerial() : ResourceSerial(0); |
| |
| if (serial != mAppliedShaders[gl::ShaderType::Compute]) |
| { |
| ID3D11ComputeShader *appliedShader = shader ? shader->get() : nullptr; |
| mRenderer->getDeviceContext()->CSSetShader(appliedShader, nullptr, 0); |
| mAppliedShaders[gl::ShaderType::Compute] = serial; |
| invalidateShaders(); |
| } |
| } |
| |
| 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::ShaderType::Fragment, 0, &srv); |
| deviceContext->PSSetSamplers(0, 1, samplerState.getPointer()); |
| |
| mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| mForceSetShaderSamplerStates[gl::ShaderType::Fragment][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); |
| } |
| |
| angle::Result StateManager11::syncTextures(const gl::Context *context) |
| { |
| ANGLE_TRY(applyTexturesForSRVs(context, gl::ShaderType::Vertex)); |
| ANGLE_TRY(applyTexturesForSRVs(context, gl::ShaderType::Fragment)); |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Geometry)) |
| { |
| ANGLE_TRY(applyTexturesForSRVs(context, gl::ShaderType::Geometry)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::setSamplerState(const gl::Context *context, |
| gl::ShaderType 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(); |
| |
| ASSERT(index < mRenderer->getNativeCaps().maxShaderTextureImageUnits[type]); |
| |
| if (mForceSetShaderSamplerStates[type][index] || |
| memcmp(&samplerState, &mCurShaderSamplerStates[type][index], sizeof(gl::SamplerState)) != 0) |
| { |
| ID3D11SamplerState *dxSamplerState = nullptr; |
| ANGLE_TRY(mRenderer->getSamplerState(context, samplerState, &dxSamplerState)); |
| |
| ASSERT(dxSamplerState != nullptr); |
| |
| switch (type) |
| { |
| case gl::ShaderType::Vertex: |
| deviceContext->VSSetSamplers(index, 1, &dxSamplerState); |
| break; |
| case gl::ShaderType::Fragment: |
| deviceContext->PSSetSamplers(index, 1, &dxSamplerState); |
| break; |
| case gl::ShaderType::Compute: |
| deviceContext->CSSetSamplers(index, 1, &dxSamplerState); |
| break; |
| case gl::ShaderType::Geometry: |
| deviceContext->GSSetSamplers(index, 1, &dxSamplerState); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| mCurShaderSamplerStates[type][index] = samplerState; |
| } |
| |
| mForceSetShaderSamplerStates[type][index] = false; |
| |
| // 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/CSSetSamplers/GSSetSamplers. |
| mShaderConstants.onSamplerChange(type, index, *texture, samplerState); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::setTextureForSampler(const gl::Context *context, |
| gl::ShaderType type, |
| int index, |
| gl::Texture *texture, |
| const gl::SamplerState &sampler) |
| { |
| 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->getSRVForSampler(context, texture->getTextureState(), sampler, &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::ShaderType::Fragment && |
| index < mRenderer->getNativeCaps().maxShaderTextureImageUnits[gl::ShaderType::Fragment]) || |
| (type == gl::ShaderType::Vertex && |
| index < mRenderer->getNativeCaps().maxShaderTextureImageUnits[gl::ShaderType::Vertex]) || |
| (type == gl::ShaderType::Compute && |
| index < mRenderer->getNativeCaps().maxShaderTextureImageUnits[gl::ShaderType::Compute])); |
| |
| setShaderResourceInternal(type, index, textureSRV); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::setImageState(const gl::Context *context, |
| gl::ShaderType type, |
| int index, |
| const gl::ImageUnit &imageUnit) |
| { |
| ASSERT(index < mRenderer->getNativeCaps().maxShaderImageUniforms[type]); |
| |
| mShaderConstants.onImageChange(type, index, imageUnit); |
| |
| return angle::Result::Continue; |
| } |
| |
| // 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. |
| angle::Result StateManager11::applyTexturesForSRVs(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| const auto &glState = context->getState(); |
| const auto &caps = context->getCaps(); |
| |
| ASSERT(!mProgramD3D->isSamplerMappingDirty()); |
| |
| // TODO(jmadill): Use the Program's sampler bindings. |
| const gl::ActiveTexturePointerArray &completeTextures = glState.getActiveTexturesCache(); |
| |
| const gl::RangeUI samplerRange = mProgramD3D->getUsedSamplerRange(shaderType); |
| for (unsigned int samplerIndex = samplerRange.low(); samplerIndex < samplerRange.high(); |
| samplerIndex++) |
| { |
| GLint textureUnit = mProgramD3D->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( |
| setTextureForSampler(context, shaderType, samplerIndex, texture, samplerState)); |
| } |
| else |
| { |
| gl::TextureType textureType = |
| mProgramD3D->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(setTextureForSampler(context, shaderType, samplerIndex, incompleteTexture, |
| incompleteTexture->getSamplerState())); |
| } |
| } |
| |
| const gl::RangeUI readonlyImageRange = mProgramD3D->getUsedImageRange(shaderType, true); |
| for (unsigned int readonlyImageIndex = readonlyImageRange.low(); |
| readonlyImageIndex < readonlyImageRange.high(); readonlyImageIndex++) |
| { |
| GLint imageUnitIndex = |
| mProgramD3D->getImageMapping(shaderType, readonlyImageIndex, true, caps); |
| ASSERT(imageUnitIndex != -1); |
| const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex); |
| if (!imageUnit.layered) |
| { |
| ANGLE_TRY(setImageState(context, gl::ShaderType::Compute, |
| readonlyImageIndex - readonlyImageRange.low(), imageUnit)); |
| invalidateProgramUniforms(); |
| } |
| ANGLE_TRY(setTextureForImage(context, shaderType, readonlyImageIndex, true, imageUnit)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyTexturesForUAVs(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| ASSERT(shaderType == gl::ShaderType::Compute); |
| const auto &glState = context->getState(); |
| const auto &caps = context->getCaps(); |
| |
| const gl::RangeUI imageRange = mProgramD3D->getUsedImageRange(shaderType, false); |
| for (unsigned int imageIndex = imageRange.low(); imageIndex < imageRange.high(); imageIndex++) |
| { |
| GLint imageUnitIndex = mProgramD3D->getImageMapping(shaderType, imageIndex, false, caps); |
| ASSERT(imageUnitIndex != -1); |
| const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex); |
| if (!imageUnit.layered) |
| { |
| ANGLE_TRY(setImageState(context, gl::ShaderType::Compute, imageIndex - imageRange.low(), |
| imageUnit)); |
| invalidateProgramUniforms(); |
| } |
| ANGLE_TRY(setTextureForImage(context, shaderType, imageIndex, false, imageUnit)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncTexturesForCompute(const gl::Context *context) |
| { |
| ANGLE_TRY(applyTexturesForUAVs(context, gl::ShaderType::Compute)); |
| ANGLE_TRY(applyTexturesForSRVs(context, gl::ShaderType::Compute)); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::setTextureForImage(const gl::Context *context, |
| gl::ShaderType type, |
| int index, |
| bool readonly, |
| const gl::ImageUnit &imageUnit) |
| { |
| TextureD3D *textureImpl = nullptr; |
| if (!imageUnit.texture.get()) |
| { |
| // The texture is used in shader. However, there is no resource binding to it. We |
| // should clear the corresponding UAV/SRV in case the previous view type is a buffer not a |
| // texture. Otherwise, below error will be reported. The Unordered Access View dimension |
| // declared in the shader code (TEXTURE2D) does not match the view type bound to slot 0 |
| // of the Compute Shader unit (BUFFER). |
| if (readonly) |
| { |
| setShaderResourceInternal<d3d11::ShaderResourceView>(type, static_cast<UINT>(index), |
| nullptr); |
| } |
| else |
| { |
| setUnorderedAccessViewInternal<d3d11::UnorderedAccessView>( |
| type, static_cast<UINT>(index), nullptr); |
| } |
| return angle::Result::Continue; |
| } |
| |
| textureImpl = GetImplAs<TextureD3D>(imageUnit.texture.get()); |
| TextureStorage *texStorage = nullptr; |
| ANGLE_TRY(textureImpl->getNativeTexture(context, &texStorage)); |
| // Texture should be complete and have a storage |
| ASSERT(texStorage); |
| TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage); |
| |
| if (readonly) |
| { |
| const d3d11::SharedSRV *textureSRV = nullptr; |
| ANGLE_TRY(storage11->getSRVForImage(context, imageUnit, &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()); |
| ASSERT((index < mRenderer->getNativeCaps().maxImageUnits)); |
| setShaderResourceInternal(type, index, textureSRV); |
| } |
| else |
| { |
| const d3d11::SharedUAV *textureUAV = nullptr; |
| ANGLE_TRY(storage11->getUAVForImage(context, imageUnit, &textureUAV)); |
| // If we get an invalid UAV here, something went wrong in the texture class and we're |
| // unexpectedly missing the unordered access view. |
| ASSERT(textureUAV->valid()); |
| ASSERT((index < mRenderer->getNativeCaps().maxImageUnits)); |
| setUnorderedAccessViewInternal(type, index, textureUAV); |
| } |
| |
| textureImpl->resetDirty(); |
| return angle::Result::Continue; |
| } |
| |
| #if defined(STARBOARD) |
| namespace |
| { |
| angle::FormatID GetTextureFormatId(const gl::Context *context, ProgramD3D *programD3D) |
| { |
| gl::ShaderType type = gl::ShaderType::Fragment; |
| const gl::State &glState = context->getState(); |
| gl::RangeUI samplerRange = programD3D->getUsedSamplerRange(type); |
| |
| for (unsigned int i = samplerRange.low(); i < samplerRange.high(); i++) |
| { |
| gl::TextureType 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); |
| rx::TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); |
| ASSERT(textureD3D); |
| TextureStorage *texStorage = nullptr; |
| textureD3D->getNativeTexture(context, & texStorage); |
| if (texStorage) |
| { |
| return GetAs<TextureStorage11_2D>(texStorage)->getFormat().id; |
| } |
| } |
| } |
| return angle::FormatID::NONE; |
| } |
| } // namespace |
| #endif // STARBOARD |
| |
| // 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. |
| angle::Result StateManager11::syncProgram(const gl::Context *context, gl::PrimitiveMode drawMode) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_TRY(context11->triggerDrawCallProgramRecompilation(context, drawMode)); |
| |
| const auto &glState = context->getState(); |
| |
| mProgramD3D->updateCachedInputLayout(mVertexArray11->getCurrentStateSerial(), glState); |
| |
| // Binaries must be compiled before the sync. |
| ASSERT(mProgramD3D->hasVertexExecutableForCachedInputLayout()); |
| ASSERT(mProgramD3D->hasGeometryExecutableForPrimitiveType(glState, drawMode)); |
| ASSERT(mProgramD3D->hasPixelExecutableForCachedOutputLayout()); |
| |
| ShaderExecutableD3D *vertexExe = nullptr; |
| ANGLE_TRY(mProgramD3D->getVertexExecutableForCachedInputLayout(context11, &vertexExe, nullptr)); |
| |
| ShaderExecutableD3D *pixelExe = nullptr; |
| #if defined(STARBOARD) |
| // While 10-bit HDR video is playing we run the pixel shader to apply color space for all UI |
| // elements conversion from 8-bit to 10-bit for all draw calls that do not involve the HDR video |
| // texture (look at spec ITU - R BT .2100 - 2(07 / 2018) for BT709 to BT2020 transform). This |
| // conversion is applicable only once when we draw to the display - drawFramebuffer->id() is 0. |
| const gl::Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); |
| if (IsHdrAngleModeEnabled() && drawFramebuffer->id().value == 0) |
| { |
| if (GetTextureFormatId(context, mProgramD3D) == angle::FormatID::R10G10B10A2_UNORM || |
| GetTextureFormatId(context, mProgramD3D) == angle::FormatID::R16_UNORM) |
| { |
| ANGLE_TRY(mProgramD3D->getPixelExecutableForCachedOutputLayout(context11, &pixelExe, |
| nullptr)); |
| } |
| else |
| { |
| ANGLE_TRY(mProgramD3D->getPixelExecutableForCachedHdrOutputLayout(context11, &pixelExe, |
| nullptr)); |
| } |
| } |
| else |
| { |
| ANGLE_TRY( |
| mProgramD3D->getPixelExecutableForCachedOutputLayout(context11, &pixelExe, nullptr)); |
| } |
| #else |
| ANGLE_TRY(mProgramD3D->getPixelExecutableForCachedOutputLayout(context11, &pixelExe, nullptr)); |
| #endif // STARBOARD |
| |
| ShaderExecutableD3D *geometryExe = nullptr; |
| ANGLE_TRY(mProgramD3D->getGeometryExecutableForPrimitiveType(context11, glState, 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 angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncProgramForCompute(const gl::Context *context) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_TRY(context11->triggerDispatchCallProgramRecompilation(context)); |
| |
| mProgramD3D->updateCachedComputeImage2DBindLayout(context); |
| |
| // Binaries must be compiled before the sync. |
| ASSERT(mProgramD3D->hasComputeExecutableForCachedImage2DBindLayout()); |
| |
| ShaderExecutableD3D *computeExe = nullptr; |
| ANGLE_TRY( |
| mProgramD3D->getComputeExecutableForImage2DBindLayout(context11, &computeExe, nullptr)); |
| |
| const d3d11::ComputeShader *computeShader = |
| (computeExe ? &GetAs<ShaderExecutable11>(computeExe)->getComputeShader() : nullptr); |
| setComputeShader(computeShader); |
| // Explicitly clear the shaders dirty bit. |
| mInternalDirtyBits.reset(DIRTY_BIT_SHADERS); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncVertexBuffersAndInputLayout( |
| const gl::Context *context, |
| gl::PrimitiveMode mode, |
| GLint firstVertex, |
| GLsizei vertexOrIndexCount, |
| gl::DrawElementsType indexTypeOrInvalid, |
| GLsizei instanceCount) |
| { |
| const auto &vertexArrayAttribs = mVertexArray11->getTranslatedAttribs(); |
| |
| // Sort the attributes according to ensure we re-use similar input layouts. |
| AttribIndexArray sortedSemanticIndices; |
| SortAttributesByLayout(*mProgramD3D, vertexArrayAttribs, mCurrentValueAttribs, |
| &sortedSemanticIndices, &mCurrentAttributes); |
| |
| D3D_FEATURE_LEVEL 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()) |
| { |
| 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. |
| const gl::State &state = context->getState(); |
| const d3d11::InputLayout *inputLayout = nullptr; |
| ANGLE_TRY(mInputLayoutCache.getInputLayout(GetImplAs<Context11>(context), state, |
| mCurrentAttributes, sortedSemanticIndices, mode, |
| vertexOrIndexCount, instanceCount, &inputLayout)); |
| setInputLayoutInternal(inputLayout); |
| |
| // Update the applied vertex buffers. |
| ANGLE_TRY(applyVertexBuffers(context, mode, indexTypeOrInvalid, firstVertex)); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyVertexBuffers(const gl::Context *context, |
| gl::PrimitiveMode mode, |
| gl::DrawElementsType indexTypeOrInvalid, |
| GLint firstVertex) |
| { |
| bool programUsesInstancedPointSprites = |
| mProgramD3D->usesPointSize() && mProgramD3D->usesInstancedPointSpriteEmulation(); |
| bool instancedPointSpritesActive = |
| programUsesInstancedPointSprites && (mode == gl::PrimitiveMode::Points); |
| |
| // Note that if we use instance emulation, we reserve the first buffer slot. |
| size_t reservedBuffers = GetReservedBufferCount(programUsesInstancedPointSprites); |
| |
| for (size_t attribIndex = 0; attribIndex < (gl::MAX_VERTEX_ATTRIBS - reservedBuffers); |
| ++attribIndex) |
| { |
| ID3D11Buffer *buffer = nullptr; |
| UINT vertexStride = 0; |
| UINT vertexOffset = 0; |
| |
| if (attribIndex < mCurrentAttributes.size()) |
| { |
| const TranslatedAttribute &attrib = *mCurrentAttributes[attribIndex]; |
| Buffer11 *bufferStorage = attrib.storage ? GetAs<Buffer11>(attrib.storage) : nullptr; |
| |
| // If indexed pointsprite emulation is active, then we need to take a less efficent code |
| // path. Emulated indexed pointsprite rendering requires that the vertex buffers match |
| // exactly to the indices passed by the caller. This could expand or shrink the vertex |
| // buffer depending on the number of points indicated by the index list or how many |
| // duplicates are found on the index list. |
| if (bufferStorage == nullptr) |
| { |
| ASSERT(attrib.vertexBuffer.get()); |
| buffer = GetAs<VertexBuffer11>(attrib.vertexBuffer.get())->getBuffer().get(); |
| } |
| else if (instancedPointSpritesActive && |
| indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum) |
| { |
| ASSERT(mVertexArray11->isCachedIndexInfoValid()); |
| TranslatedIndexData indexInfo = mVertexArray11->getCachedIndexInfo(); |
| if (indexInfo.srcIndexData.srcBuffer != nullptr) |
| { |
| const uint8_t *bufferData = nullptr; |
| ANGLE_TRY(indexInfo.srcIndexData.srcBuffer->getData(context, &bufferData)); |
| ASSERT(bufferData != nullptr); |
| |
| ptrdiff_t offset = |
| reinterpret_cast<ptrdiff_t>(indexInfo.srcIndexData.srcIndices); |
| indexInfo.srcIndexData.srcBuffer = nullptr; |
| indexInfo.srcIndexData.srcIndices = bufferData + offset; |
| } |
| |
| ANGLE_TRY(bufferStorage->getEmulatedIndexedBuffer(context, &indexInfo.srcIndexData, |
| attrib, firstVertex, &buffer)); |
| |
| mVertexArray11->updateCachedIndexInfo(indexInfo); |
| } |
| else |
| { |
| ANGLE_TRY(bufferStorage->getBuffer( |
| context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, &buffer)); |
| } |
| |
| vertexStride = attrib.stride; |
| ANGLE_TRY(attrib.computeOffset(context, firstVertex, &vertexOffset)); |
| } |
| |
| size_t bufferIndex = reservedBuffers + attribIndex; |
| |
| queueVertexBufferChange(bufferIndex, buffer, vertexStride, vertexOffset); |
| } |
| |
| Context11 *context11 = GetImplAs<Context11>(context); |
| |
| // Instanced PointSprite emulation requires two additional ID3D11Buffers. A vertex buffer needs |
| // to be created and added to the list of current buffers, strides and offsets collections. |
| // This buffer contains the vertices for a single PointSprite quad. |
| // An index buffer also needs to be created and applied because rendering instanced data on |
| // D3D11 FL9_3 requires DrawIndexedInstanced() to be used. Shaders that contain gl_PointSize and |
| // used without the GL_POINTS rendering mode require a vertex buffer because some drivers cannot |
| // handle missing vertex data and will TDR the system. |
| if (programUsesInstancedPointSprites) |
| { |
| constexpr UINT kPointSpriteVertexStride = sizeof(float) * 5; |
| |
| if (!mPointSpriteVertexBuffer.valid()) |
| { |
| static constexpr float kPointSpriteVertices[] = { |
| // Position | TexCoord |
| -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, /* v0 */ |
| -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, /* v1 */ |
| 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, /* v2 */ |
| 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, /* v3 */ |
| -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, /* v4 */ |
| 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, /* v5 */ |
| }; |
| |
| D3D11_SUBRESOURCE_DATA vertexBufferData = {kPointSpriteVertices, 0, 0}; |
| D3D11_BUFFER_DESC vertexBufferDesc; |
| vertexBufferDesc.ByteWidth = sizeof(kPointSpriteVertices); |
| vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; |
| vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; |
| vertexBufferDesc.CPUAccessFlags = 0; |
| vertexBufferDesc.MiscFlags = 0; |
| vertexBufferDesc.StructureByteStride = 0; |
| |
| ANGLE_TRY(mRenderer->allocateResource(context11, vertexBufferDesc, &vertexBufferData, |
| &mPointSpriteVertexBuffer)); |
| } |
| |
| // Set the stride to 0 if GL_POINTS mode is not being used to instruct the driver to avoid |
| // indexing into the vertex buffer. |
| UINT stride = instancedPointSpritesActive ? kPointSpriteVertexStride : 0; |
| queueVertexBufferChange(0, mPointSpriteVertexBuffer.get(), stride, 0); |
| |
| if (!mPointSpriteIndexBuffer.valid()) |
| { |
| // Create an index buffer and set it for pointsprite rendering |
| static constexpr unsigned short kPointSpriteIndices[] = { |
| 0, 1, 2, 3, 4, 5, |
| }; |
| |
| D3D11_SUBRESOURCE_DATA indexBufferData = {kPointSpriteIndices, 0, 0}; |
| D3D11_BUFFER_DESC indexBufferDesc; |
| indexBufferDesc.ByteWidth = sizeof(kPointSpriteIndices); |
| indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; |
| indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; |
| indexBufferDesc.CPUAccessFlags = 0; |
| indexBufferDesc.MiscFlags = 0; |
| indexBufferDesc.StructureByteStride = 0; |
| |
| ANGLE_TRY(mRenderer->allocateResource(context11, indexBufferDesc, &indexBufferData, |
| &mPointSpriteIndexBuffer)); |
| } |
| |
| if (instancedPointSpritesActive) |
| { |
| // The index buffer is applied here because Instanced PointSprite emulation uses the a |
| // non-indexed rendering path in ANGLE (DrawArrays). This means that applyIndexBuffer() |
| // on the renderer will not be called and setting this buffer here ensures that the |
| // rendering path will contain the correct index buffers. |
| syncIndexBuffer(mPointSpriteIndexBuffer.get(), DXGI_FORMAT_R16_UINT, 0); |
| } |
| } |
| |
| applyVertexBufferChanges(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyIndexBuffer(const gl::Context *context, |
| GLsizei indexCount, |
| gl::DrawElementsType indexType, |
| const void *indices) |
| { |
| if (!mIndexBufferIsDirty) |
| { |
| // No streaming or index buffer application necessary. |
| return angle::Result::Continue; |
| } |
| |
| gl::DrawElementsType destElementType = mVertexArray11->getCachedDestinationIndexType(); |
| gl::Buffer *elementArrayBuffer = mVertexArray11->getState().getElementArrayBuffer(); |
| |
| TranslatedIndexData indexInfo; |
| ANGLE_TRY(mIndexDataManager.prepareIndexData(context, indexType, destElementType, indexCount, |
| elementArrayBuffer, indices, &indexInfo)); |
| |
| ID3D11Buffer *buffer = nullptr; |
| DXGI_FORMAT bufferFormat = (indexInfo.indexType == gl::DrawElementsType::UnsignedInt) |
| ? DXGI_FORMAT_R32_UINT |
| : DXGI_FORMAT_R16_UINT; |
| |
| if (indexInfo.storage) |
| { |
| Buffer11 *storage = GetAs<Buffer11>(indexInfo.storage); |
| ANGLE_TRY(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; |
| |
| mVertexArray11->updateCachedIndexInfo(indexInfo); |
| return angle::Result::Continue; |
| } |
| |
| void StateManager11::setIndexBuffer(ID3D11Buffer *buffer, |
| DXGI_FORMAT indexFormat, |
| unsigned int offset) |
| { |
| if (syncIndexBuffer(buffer, indexFormat, offset)) |
| { |
| invalidateIndexBuffer(); |
| } |
| } |
| |
| 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. |
| angle::Result StateManager11::updateVertexOffsetsForPointSpritesEmulation( |
| const gl::Context *context, |
| GLint startVertex, |
| GLsizei emulatedInstanceId) |
| { |
| size_t reservedBuffers = GetReservedBufferCount(true); |
| for (size_t attribIndex = 0; attribIndex < mCurrentAttributes.size(); ++attribIndex) |
| { |
| const auto &attrib = *mCurrentAttributes[attribIndex]; |
| size_t bufferIndex = reservedBuffers + attribIndex; |
| |
| if (attrib.divisor > 0) |
| { |
| unsigned int offset = 0; |
| ANGLE_TRY(attrib.computeOffset(context, startVertex, &offset)); |
| offset += (attrib.stride * (emulatedInstanceId / attrib.divisor)); |
| if (offset != mCurrentVertexOffsets[bufferIndex]) |
| { |
| invalidateInputLayout(); |
| mDirtyVertexBufferRange.extend(static_cast<unsigned int>(bufferIndex)); |
| mCurrentVertexOffsets[bufferIndex] = offset; |
| } |
| } |
| } |
| |
| applyVertexBufferChanges(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::generateSwizzle(const gl::Context *context, gl::Texture *texture) |
| { |
| if (!texture) |
| { |
| return angle::Result::Continue; |
| } |
| |
| 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 angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::generateSwizzlesForShader(const gl::Context *context, |
| gl::ShaderType type) |
| { |
| const gl::State &glState = context->getState(); |
| const gl::RangeUI samplerRange = mProgramD3D->getUsedSamplerRange(type); |
| |
| for (unsigned int i = samplerRange.low(); i < samplerRange.high(); i++) |
| { |
| gl::TextureType textureType = mProgramD3D->getSamplerTextureType(type, i); |
| GLint textureUnit = mProgramD3D->getSamplerMapping(type, i, context->getCaps()); |
| if (textureUnit != -1) |
| { |
| gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType); |
| ASSERT(texture); |
| if (texture->getTextureState().swizzleRequired()) |
| { |
| ANGLE_TRY(generateSwizzle(context, texture)); |
| } |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::generateSwizzles(const gl::Context *context) |
| { |
| ANGLE_TRY(generateSwizzlesForShader(context, gl::ShaderType::Vertex)); |
| ANGLE_TRY(generateSwizzlesForShader(context, gl::ShaderType::Fragment)); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyUniformsForShader(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| UniformStorage11 *shaderUniformStorage = |
| GetAs<UniformStorage11>(mProgramD3D->getShaderUniformStorage(shaderType)); |
| ASSERT(shaderUniformStorage); |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| |
| const d3d11::Buffer *shaderConstantBuffer = nullptr; |
| ANGLE_TRY(shaderUniformStorage->getConstantBuffer(context, mRenderer, &shaderConstantBuffer)); |
| |
| if (shaderUniformStorage->size() > 0 && mProgramD3D->areShaderUniformsDirty(shaderType)) |
| { |
| UpdateUniformBuffer(deviceContext, shaderUniformStorage, shaderConstantBuffer); |
| } |
| |
| unsigned int slot = d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DEFAULT_UNIFORM_BLOCK; |
| |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| if (mCurrentConstantBufferVS[slot] != shaderConstantBuffer->getSerial()) |
| { |
| deviceContext->VSSetConstantBuffers(slot, 1, shaderConstantBuffer->getPointer()); |
| mCurrentConstantBufferVS[slot] = shaderConstantBuffer->getSerial(); |
| mCurrentConstantBufferVSOffset[slot] = 0; |
| mCurrentConstantBufferVSSize[slot] = 0; |
| } |
| break; |
| |
| case gl::ShaderType::Fragment: |
| if (mCurrentConstantBufferPS[slot] != shaderConstantBuffer->getSerial()) |
| { |
| deviceContext->PSSetConstantBuffers(slot, 1, shaderConstantBuffer->getPointer()); |
| mCurrentConstantBufferPS[slot] = shaderConstantBuffer->getSerial(); |
| mCurrentConstantBufferPSOffset[slot] = 0; |
| mCurrentConstantBufferPSSize[slot] = 0; |
| } |
| break; |
| |
| // TODO(jiawei.shao@intel.com): apply geometry shader uniforms |
| case gl::ShaderType::Geometry: |
| UNIMPLEMENTED(); |
| break; |
| |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyUniforms(const gl::Context *context) |
| { |
| ANGLE_TRY(applyUniformsForShader(context, gl::ShaderType::Vertex)); |
| ANGLE_TRY(applyUniformsForShader(context, gl::ShaderType::Fragment)); |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Geometry)) |
| { |
| ANGLE_TRY(applyUniformsForShader(context, gl::ShaderType::Geometry)); |
| } |
| |
| mProgramD3D->markUniformsClean(); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyDriverUniformsForShader(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| |
| d3d11::Buffer &shaderDriverConstantBuffer = mShaderDriverConstantBuffers[shaderType]; |
| if (!shaderDriverConstantBuffer.valid()) |
| { |
| size_t requiredSize = mShaderConstants.getRequiredBufferSize(shaderType); |
| |
| D3D11_BUFFER_DESC constantBufferDescription = {}; |
| d3d11::InitConstantBufferDesc(&constantBufferDescription, requiredSize); |
| ANGLE_TRY(mRenderer->allocateResource( |
| GetImplAs<Context11>(context), constantBufferDescription, &shaderDriverConstantBuffer)); |
| |
| ID3D11Buffer *driverConstants = shaderDriverConstantBuffer.get(); |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| deviceContext->VSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &driverConstants); |
| break; |
| |
| case gl::ShaderType::Fragment: |
| deviceContext->PSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &driverConstants); |
| break; |
| |
| case gl::ShaderType::Geometry: |
| deviceContext->GSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &driverConstants); |
| break; |
| |
| default: |
| UNREACHABLE(); |
| return angle::Result::Continue; |
| } |
| } |
| |
| // 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(context, mRenderer, shaderType, *mProgramD3D, |
| shaderDriverConstantBuffer)); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyDriverUniforms(const gl::Context *context) |
| { |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| |
| ANGLE_TRY(applyDriverUniformsForShader(context, gl::ShaderType::Vertex)); |
| ANGLE_TRY(applyDriverUniformsForShader(context, gl::ShaderType::Fragment)); |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Geometry)) |
| { |
| ANGLE_TRY(applyDriverUniformsForShader(context, gl::ShaderType::Geometry)); |
| } |
| |
| // 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()) |
| { |
| d3d11::Buffer &driverConstantBufferPS = |
| mShaderDriverConstantBuffers[gl::ShaderType::Fragment]; |
| if (mCurrentGeometryConstantBuffer != driverConstantBufferPS.getSerial()) |
| { |
| ASSERT(driverConstantBufferPS.valid()); |
| deviceContext->GSSetConstantBuffers(0, 1, driverConstantBufferPS.getPointer()); |
| mCurrentGeometryConstantBuffer = driverConstantBufferPS.getSerial(); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::applyComputeUniforms(const gl::Context *context, |
| ProgramD3D *programD3D) |
| { |
| UniformStorage11 *computeUniformStorage = |
| GetAs<UniformStorage11>(programD3D->getShaderUniformStorage(gl::ShaderType::Compute)); |
| ASSERT(computeUniformStorage); |
| |
| const d3d11::Buffer *constantBuffer = nullptr; |
| ANGLE_TRY(computeUniformStorage->getConstantBuffer(context, mRenderer, &constantBuffer)); |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| |
| if (computeUniformStorage->size() > 0 && |
| programD3D->areShaderUniformsDirty(gl::ShaderType::Compute)) |
| { |
| 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 (!mShaderDriverConstantBuffers[gl::ShaderType::Compute].valid()) |
| { |
| size_t requiredSize = mShaderConstants.getRequiredBufferSize(gl::ShaderType::Compute); |
| |
| D3D11_BUFFER_DESC constantBufferDescription = {}; |
| d3d11::InitConstantBufferDesc(&constantBufferDescription, requiredSize); |
| ANGLE_TRY( |
| mRenderer->allocateResource(GetImplAs<Context11>(context), constantBufferDescription, |
| &mShaderDriverConstantBuffers[gl::ShaderType::Compute])); |
| ID3D11Buffer *buffer = mShaderDriverConstantBuffers[gl::ShaderType::Compute].get(); |
| deviceContext->CSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1, |
| &buffer); |
| } |
| |
| ANGLE_TRY(mShaderConstants.updateBuffer(context, mRenderer, gl::ShaderType::Compute, |
| *programD3D, |
| mShaderDriverConstantBuffers[gl::ShaderType::Compute])); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncUniformBuffersForShader(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| gl::ShaderMap<unsigned int> shaderReservedUBOs = mRenderer->getReservedShaderUniformBuffers(); |
| |
| const auto &glState = context->getState(); |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported(); |
| |
| const auto &shaderUniformBuffers = mProgramD3D->getShaderUniformBufferCache(shaderType); |
| const unsigned int reservedUBOs = shaderReservedUBOs[shaderType]; |
| |
| for (size_t bufferIndex = 0; bufferIndex < shaderUniformBuffers.size(); ++bufferIndex) |
| { |
| const GLint binding = shaderUniformBuffers[bufferIndex]; |
| if (binding == -1) |
| { |
| continue; |
| } |
| |
| const auto &uniformBuffer = glState.getIndexedUniformBuffer(binding); |
| const GLintptr uniformBufferOffset = uniformBuffer.getOffset(); |
| const 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); |
| |
| const unsigned int appliedIndex = reservedUBOs + static_cast<unsigned int>(bufferIndex); |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| { |
| if (mCurrentConstantBufferVS[bufferIndex] == constantBuffer->getSerial() && |
| mCurrentConstantBufferVSOffset[bufferIndex] == uniformBufferOffset && |
| mCurrentConstantBufferVSSize[bufferIndex] == uniformBufferSize) |
| { |
| continue; |
| } |
| |
| 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; |
| break; |
| } |
| |
| case gl::ShaderType::Fragment: |
| { |
| if (mCurrentConstantBufferPS[bufferIndex] == constantBuffer->getSerial() && |
| mCurrentConstantBufferPSOffset[bufferIndex] == uniformBufferOffset && |
| mCurrentConstantBufferPSSize[bufferIndex] == uniformBufferSize) |
| { |
| continue; |
| } |
| |
| 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; |
| break; |
| } |
| |
| case gl::ShaderType::Compute: |
| { |
| if (mCurrentConstantBufferCS[bufferIndex] == constantBuffer->getSerial() && |
| mCurrentConstantBufferCSOffset[bufferIndex] == uniformBufferOffset && |
| mCurrentConstantBufferCSSize[bufferIndex] == uniformBufferSize) |
| { |
| continue; |
| } |
| |
| if (firstConstant != 0 && uniformBufferSize != 0) |
| { |
| deviceContext1->CSSetConstantBuffers1(appliedIndex, 1, |
| constantBuffer->getPointer(), |
| &firstConstant, &numConstants); |
| } |
| else |
| { |
| deviceContext->CSSetConstantBuffers(appliedIndex, 1, |
| constantBuffer->getPointer()); |
| } |
| |
| mCurrentConstantBufferCS[appliedIndex] = constantBuffer->getSerial(); |
| mCurrentConstantBufferCSOffset[appliedIndex] = uniformBufferOffset; |
| mCurrentConstantBufferCSSize[appliedIndex] = uniformBufferSize; |
| break; |
| } |
| |
| // TODO(jiawei.shao@intel.com): update geometry shader uniform buffers. |
| case gl::ShaderType::Geometry: |
| UNIMPLEMENTED(); |
| break; |
| |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncShaderStorageBuffersForShader(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| const gl::State &glState = context->getState(); |
| const gl::Program *program = glState.getProgram(); |
| angle::FixedVector<Buffer11 *, gl::IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS> |
| previouslyBound; |
| for (size_t blockIndex = 0; blockIndex < program->getActiveShaderStorageBlockCount(); |
| blockIndex++) |
| { |
| GLuint binding = program->getShaderStorageBlockBinding(static_cast<GLuint>(blockIndex)); |
| const unsigned int registerIndex = mProgramD3D->getShaderStorageBufferRegisterIndex( |
| static_cast<GLuint>(blockIndex), shaderType); |
| // It means this block is active but not statically used. |
| if (registerIndex == GL_INVALID_INDEX) |
| { |
| continue; |
| } |
| const auto &shaderStorageBuffer = glState.getIndexedShaderStorageBuffer(binding); |
| if (shaderStorageBuffer.get() == nullptr) |
| { |
| // We didn't see a driver error like atomic buffer did. But theoretically, the same |
| // thing should be done. |
| setUnorderedAccessViewInternal<d3d11::UnorderedAccessView>(shaderType, registerIndex, |
| nullptr); |
| continue; |
| } |
| |
| Buffer11 *bufferStorage = GetImplAs<Buffer11>(shaderStorageBuffer.get()); |
| if (std::find(previouslyBound.begin(), previouslyBound.end(), bufferStorage) != |
| previouslyBound.end()) |
| { |
| // D3D11 doesn't support binding a buffer multiple times |
| // http://anglebug.com/3032 |
| ERR() << "Writing to multiple blocks on the same buffer is not allowed."; |
| return angle::Result::Stop; |
| } |
| previouslyBound.push_back(bufferStorage); |
| |
| d3d11::UnorderedAccessView *uavPtr = nullptr; |
| GLsizeiptr viewSize = 0; |
| // Bindings only have a valid size if bound using glBindBufferRange |
| if (shaderStorageBuffer.getSize() > 0) |
| { |
| viewSize = shaderStorageBuffer.getSize(); |
| } |
| // We use the buffer size for glBindBufferBase |
| else |
| { |
| viewSize = bufferStorage->getSize(); |
| } |
| ANGLE_TRY(bufferStorage->getRawUAVRange(context, shaderStorageBuffer.getOffset(), viewSize, |
| &uavPtr)); |
| |
| switch (shaderType) |
| { |
| case gl::ShaderType::Compute: |
| { |
| setUnorderedAccessViewInternal(shaderType, registerIndex, uavPtr); |
| break; |
| } |
| |
| case gl::ShaderType::Vertex: |
| case gl::ShaderType::Fragment: |
| case gl::ShaderType::Geometry: |
| UNIMPLEMENTED(); |
| break; |
| |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncUniformBuffers(const gl::Context *context) |
| { |
| gl::ShaderMap<unsigned int> shaderReservedUBOs = mRenderer->getReservedShaderUniformBuffers(); |
| mProgramD3D->updateUniformBufferCache(context->getCaps(), shaderReservedUBOs); |
| |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Compute)) |
| { |
| ANGLE_TRY(syncUniformBuffersForShader(context, gl::ShaderType::Compute)); |
| } |
| else |
| { |
| ANGLE_TRY(syncUniformBuffersForShader(context, gl::ShaderType::Vertex)); |
| ANGLE_TRY(syncUniformBuffersForShader(context, gl::ShaderType::Fragment)); |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Geometry)) |
| { |
| ANGLE_TRY(syncUniformBuffersForShader(context, gl::ShaderType::Geometry)); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncAtomicCounterBuffers(const gl::Context *context) |
| { |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Compute)) |
| { |
| ANGLE_TRY(syncAtomicCounterBuffersForShader(context, gl::ShaderType::Compute)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncAtomicCounterBuffersForShader(const gl::Context *context, |
| gl::ShaderType shaderType) |
| { |
| const gl::State &glState = context->getState(); |
| const gl::Program *program = glState.getProgram(); |
| for (const auto &atomicCounterBuffer : program->getState().getAtomicCounterBuffers()) |
| { |
| GLuint binding = atomicCounterBuffer.binding; |
| const auto &buffer = glState.getIndexedAtomicCounterBuffer(binding); |
| const unsigned int registerIndex = |
| mProgramD3D->getAtomicCounterBufferRegisterIndex(binding, shaderType); |
| ASSERT(registerIndex != GL_INVALID_INDEX); |
| if (buffer.get() == nullptr) |
| { |
| // The atomic counter is used in shader. However, there is no buffer binding to it. We |
| // should clear the corresponding UAV in case the previous view type is a texture not a |
| // buffer. Otherwise, below error will be reported. The Unordered Access View dimension |
| // declared in the shader code (BUFFER) does not match the view type bound to slot 0 |
| // of the Compute Shader unit (TEXTURE2D). |
| setUnorderedAccessViewInternal<d3d11::UnorderedAccessView>(shaderType, registerIndex, |
| nullptr); |
| continue; |
| } |
| |
| Buffer11 *bufferStorage = GetImplAs<Buffer11>(buffer.get()); |
| // TODO(enrico.galli@intel.com): Check to make sure that we aren't binding the same buffer |
| // multiple times, as this is unsupported by D3D11. http://anglebug.com/3141 |
| |
| // Bindings only have a valid size if bound using glBindBufferRange. Therefore, we use the |
| // buffer size for glBindBufferBase |
| GLsizeiptr viewSize = (buffer.getSize() > 0) ? buffer.getSize() : bufferStorage->getSize(); |
| d3d11::UnorderedAccessView *uavPtr = nullptr; |
| ANGLE_TRY(bufferStorage->getRawUAVRange(context, buffer.getOffset(), viewSize, &uavPtr)); |
| |
| if (shaderType == gl::ShaderType::Compute) |
| { |
| setUnorderedAccessViewInternal(shaderType, registerIndex, uavPtr); |
| } |
| else |
| { |
| // Atomic Shaders on non-compute shaders are currently unimplemented |
| // http://anglebug.com/1729 |
| UNIMPLEMENTED(); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncShaderStorageBuffers(const gl::Context *context) |
| { |
| if (mProgramD3D->hasShaderStage(gl::ShaderType::Compute)) |
| { |
| ANGLE_TRY(syncShaderStorageBuffersForShader(context, gl::ShaderType::Compute)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncTransformFeedbackBuffers(const gl::Context *context) |
| { |
| const auto &glState = context->getState(); |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| |
| // If transform feedback is not active, unbind all buffers |
| if (!glState.isTransformFeedbackActiveUnpaused()) |
| { |
| if (mAppliedTFSerial != mEmptySerial) |
| { |
| deviceContext->SOSetTargets(0, nullptr, nullptr); |
| mAppliedTFSerial = mEmptySerial; |
| } |
| return angle::Result::Continue; |
| } |
| |
| gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback(); |
| TransformFeedback11 *tf11 = GetImplAs<TransformFeedback11>(transformFeedback); |
| if (mAppliedTFSerial == tf11->getSerial() && !tf11->isDirty()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| const std::vector<ID3D11Buffer *> *soBuffers = nullptr; |
| ANGLE_TRY(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 angle::Result::Continue; |
| } |
| |
| void StateManager11::syncPrimitiveTopology(const gl::State &glState, |
| gl::PrimitiveMode currentDrawMode) |
| { |
| D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; |
| // Don't cull everything by default, this also resets if we were previously culling |
| mCullEverything = false; |
| |
| switch (currentDrawMode) |
| { |
| case gl::PrimitiveMode::Points: |
| { |
| bool usesPointSize = mProgramD3D->usesPointSize(); |
| |
| // ProgramBinary assumes non-point rendering if gl_PointSize isn't written, |
| // which affects varying interpolation. Since the value of gl_PointSize is |
| // undefined when not written, just skip drawing to avoid unexpected results. |
| if (!usesPointSize && !glState.isTransformFeedbackActiveUnpaused()) |
| { |
| // Notify developers of risking undefined behavior. |
| WARN() << "Point rendering without writing to gl_PointSize."; |
| mCullEverything = true; |
| return; |
| } |
| |
| // If instanced pointsprites are enabled and the shader uses gl_PointSize, the topology |
| // must be D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST. |
| if (usesPointSize && mRenderer->getFeatures().useInstancedPointSpriteEmulation.enabled) |
| { |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; |
| } |
| else |
| { |
| primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; |
| } |
| break; |
| } |
| case gl::PrimitiveMode::Lines: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; |
| break; |
| case gl::PrimitiveMode::LineLoop: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; |
| break; |
| case gl::PrimitiveMode::LineStrip: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; |
| break; |
| case gl::PrimitiveMode::Triangles: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; |
| mCullEverything = CullsEverything(glState); |
| break; |
| case gl::PrimitiveMode::TriangleStrip: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; |
| mCullEverything = CullsEverything(glState); |
| break; |
| // emulate fans via rewriting index buffer |
| case gl::PrimitiveMode::TriangleFan: |
| primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; |
| mCullEverything = CullsEverything(glState); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| setPrimitiveTopologyInternal(primitiveTopology); |
| } |
| |
| } // namespace rx |