| // |
| // Copyright (c) 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" |
| |
| #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; |
| } |
| |
| // 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] = {0}; |
| 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; |
| } |
| |
| 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]; |
| } |
| |
| 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 = {0}; |
| 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. |
| mInternalDirtyBits.set(); |
| |
| 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); |
| |
| // 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; |
| 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: |
| 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 && unsetConflictingView(uavPtr)) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| } |
| 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->getFirstColorbuffer(); |
| const bool presentPathFastActive = UsePresentPathFast(mRenderer, firstColorAttachment); |
| |
| const int colorBufferHeight = firstColorAttachment ? firstColorAttachment->getSize().height : 0; |
| |
| if ((mCurPresentPathFastEnabled != presentPathFastActive) || |
| (presentPathFastActive && (colorBufferHeight != mCurPresentPathFastColorBufferHeight))) |
| { |
| mCurPresentPathFastEnabled = presentPathFastActive; |
| mCurPresentPathFastColorBufferHeight = colorBufferHeight; |
| |
| // 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 dirtyBit : dirtyBitsCopy) |
| { |
| switch (dirtyBit) |
| { |
| 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; |
| case gl::State::DIRTY_BIT_DEPTH_MASK: |
| if (state.getDepthStencilState().depthMask != mCurDepthStencilState.depthMask) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: |
| if (state.getDepthStencilState().depthTest != mCurDepthStencilState.depthTest) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_DEPTH_FUNC: |
| if (state.getDepthStencilState().depthFunc != mCurDepthStencilState.depthFunc) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: |
| if (state.getDepthStencilState().stencilTest != mCurDepthStencilState.stencilTest) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: |
| { |
| const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); |
| if (depthStencil.stencilFunc != mCurDepthStencilState.stencilFunc || |
| depthStencil.stencilMask != mCurDepthStencilState.stencilMask || |
| state.getStencilRef() != mCurStencilRef) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK: |
| { |
| const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); |
| if (depthStencil.stencilBackFunc != mCurDepthStencilState.stencilBackFunc || |
| depthStencil.stencilBackMask != mCurDepthStencilState.stencilBackMask || |
| state.getStencilBackRef() != mCurStencilBackRef) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: |
| if (state.getDepthStencilState().stencilWritemask != |
| mCurDepthStencilState.stencilWritemask) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: |
| if (state.getDepthStencilState().stencilBackWritemask != |
| mCurDepthStencilState.stencilBackWritemask) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: |
| { |
| const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); |
| if (depthStencil.stencilFail != mCurDepthStencilState.stencilFail || |
| depthStencil.stencilPassDepthFail != |
| mCurDepthStencilState.stencilPassDepthFail || |
| depthStencil.stencilPassDepthPass != mCurDepthStencilState.stencilPassDepthPass) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_STATE); |
| } |
| break; |
| } |
| case gl::State::DIRTY_BIT_STENCIL_OPS_BACK: |
| { |
| const gl::DepthStencilState &depthStencil = state.getDepthStencilState(); |
| if (depthStencil.stencilBackFail != mCurDepthStencilState.stencilBackFail || |
| depthStencil.stencilBackPassDepthFail != |
| mCurDepthStencilState.stencilBackPassDepthFail || |
| depthStencil.stencilBackPassDepthPass != |
| mCurDepthStencilState.stencilBackPassDepthPass) |
| { |
| 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: |
| if (state.getNearPlane() != mCurNear || state.getFarPlane() != mCurFar) |
| { |
| 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: |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_PRIMITIVE_TOPOLOGY); |
| invalidateShaders(); |
| invalidateVertexBuffer(); |
| invalidateRenderTarget(); |
| invalidateTexturesAndSamplers(); |
| invalidateProgramUniforms(); |
| invalidateProgramUniformBuffers(); |
| invalidateProgramAtomicCounterBuffers(); |
| invalidateProgramShaderStorageBuffers(); |
| invalidateDriverUniforms(); |
| // If ANGLE_multiview 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); |
| 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); |
| |
| // Update viewport offsets. |
| const std::vector<gl::Offset> *attachmentViewportOffsets = |
| drawFramebuffer->getViewportOffsets(); |
| const std::vector<gl::Offset> &viewportOffsets = |
| attachmentViewportOffsets != nullptr |
| ? *attachmentViewportOffsets |
| : gl::FramebufferAttachment::GetDefaultViewportOffsetVector(); |
| if (mViewportOffsets != viewportOffsets) |
| { |
| mViewportOffsets = viewportOffsets; |
| |
| // Because new viewport offsets are to be applied, we have to mark the internal viewport and |
| // scissor state as dirty. |
| invalidateViewport(context); |
| mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE); |
| } |
| switch (drawFramebuffer->getMultiviewLayout()) |
| { |
| case GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE: |
| mShaderConstants.setMultiviewWriteToViewportIndex(1.0f); |
| break; |
| case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE: |
| // Because the base view index is applied as an offset to the 2D texture array when the |
| // RTV is created, we just have to pass a boolean to select which code path is to be |
| // used. |
| mShaderConstants.setMultiviewWriteToViewportIndex(0.0f); |
| break; |
| default: |
| // There is no need to update the value in the constant buffer if the active framebuffer |
| // object does not have a multiview layout. |
| break; |
| } |
| } |
| |
| 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) |
| { |
| std::array<D3D11_RECT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> rectangles; |
| const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size()); |
| for (UINT i = 0u; i < numRectangles; ++i) |
| { |
| D3D11_RECT &rect = rectangles[i]; |
| int x = scissor.x + mViewportOffsets[i].x; |
| int y = modifiedScissorY + mViewportOffsets[i].y; |
| rect.left = std::max(0, x); |
| rect.top = std::max(0, y); |
| rect.right = x + std::max(0, scissor.width); |
| rect.bottom = y + std::max(0, scissor.height); |
| } |
| mRenderer->getDeviceContext()->RSSetScissorRects(numRectangles, rectangles.data()); |
| } |
| |
| mCurScissorRect = scissor; |
| mCurScissorEnabled = enabled; |
| } |
| |
| 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 = static_cast<int>(caps.maxViewportWidth); |
| int dxMaxViewportBoundsY = static_cast<int>(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(); |
| std::array<D3D11_VIEWPORT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> dxViewports; |
| const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size()); |
| |
| int dxViewportTopLeftX = 0; |
| int dxViewportTopLeftY = 0; |
| int dxViewportWidth = 0; |
| int dxViewportHeight = 0; |
| |
| for (UINT i = 0u; i < numRectangles; ++i) |
| { |
| dxViewportTopLeftX = gl::clamp(viewport.x + mViewportOffsets[i].x, dxMinViewportBoundsX, |
| dxMaxViewportBoundsX); |
| dxViewportTopLeftY = gl::clamp(viewport.y + mViewportOffsets[i].y, dxMinViewportBoundsY, |
| dxMaxViewportBoundsY); |
| dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX); |
| dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY); |
| |
| D3D11_VIEWPORT &dxViewport = dxViewports[i]; |
| dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX); |
| if (mCurPresentPathFastEnabled) |
| { |
| // When present path fast is active and we're rendering to framebuffer 0, we must invert |
| // the viewport in Y-axis. |
| // NOTE: We delay the inversion until right before the call to RSSetViewports, and leave |
| // dxViewportTopLeftY unchanged. This allows us to calculate viewAdjust below using the |
| // unaltered dxViewportTopLeftY value. |
| dxViewport.TopLeftY = static_cast<float>(mCurPresentPathFastColorBufferHeight - |
| dxViewportTopLeftY - dxViewportHeight); |
| } |
| else |
| { |
| dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY); |
| } |
| |
| // 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(numRectangles, dxViewports.data()); |
| |
| 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) != 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(rtv)) || (dsv && unsetConflictingView(dsv))) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| } |
| |
| mRenderer->getDeviceContext()->OMSetRenderTargets(1, &rtv, dsv); |
| mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET); |
| } |
| |
| void StateManager11::setRenderTargets(ID3D11RenderTargetView **rtvs, |
| UINT numRTVs, |
| ID3D11DepthStencilView *dsv) |
| { |
| bool anyDirty = false; |
| |
| for (UINT rtvIndex = 0; rtvIndex < numRTVs; ++rtvIndex) |
| { |
| anyDirty = anyDirty || unsetConflictingView(rtvs[rtvIndex]); |
| } |
| |
| if (dsv) |
| { |
| anyDirty = anyDirty || unsetConflictingView(dsv); |
| } |
| |
| if (anyDirty) |
| { |
| mInternalDirtyBits.set(DIRTY_BIT_TEXTURE_AND_SAMPLER_STATE); |
| } |
| |
| mRenderer->getDeviceContext()->OMSetRenderTargets(numRTVs, (numRTVs > 0) ? rtvs : nullptr, dsv); |
| 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); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::clearSRVs(gl::ShaderType shaderType, |
| size_t rangeStart, |
| size_t rangeEnd) |
| { |
| if (rangeStart == rangeEnd) |
| { |
| return angle::Result::Continue; |
| } |
| |
| auto *currentSRVs = getSRVCache(shaderType); |
| gl::Range<size_t> clearRange(rangeStart, std::min(rangeEnd, currentSRVs->highestUsed())); |
| if (clearRange.empty()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| switch (shaderType) |
| { |
| case gl::ShaderType::Vertex: |
| deviceContext->VSSetShaderResources(static_cast<unsigned int>(clearRange.low()), |
| static_cast<unsigned int>(clearRange.length()), |
| &mNullSRVs[0]); |
| break; |
| case gl::ShaderType::Fragment: |
| deviceContext->PSSetShaderResources(static_cast<unsigned int>(clearRange.low()), |
| static_cast<unsigned int>(clearRange.length()), |
| &mNullSRVs[0]); |
| break; |
| case gl::ShaderType::Compute: |
| deviceContext->CSSetShaderResources(static_cast<unsigned int>(clearRange.low()), |
| static_cast<unsigned int>(clearRange.length()), |
| &mNullSRVs[0]); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| for (size_t samplerIndex : clearRange) |
| { |
| currentSRVs->update(samplerIndex, nullptr); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::clearUAVs(gl::ShaderType shaderType, |
| size_t rangeStart, |
| size_t rangeEnd) |
| { |
| ASSERT(shaderType == gl::ShaderType::Compute); |
| if (rangeStart == rangeEnd) |
| { |
| return angle::Result::Continue; |
| } |
| |
| gl::Range<size_t> clearRange(rangeStart, std::min(rangeEnd, mCurComputeUAVs.highestUsed())); |
| if (clearRange.empty()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| deviceContext->CSSetUnorderedAccessViews(static_cast<unsigned int>(clearRange.low()), |
| static_cast<unsigned int>(clearRange.length()), |
| &mNullUAVs[0], nullptr); |
| |
| for (size_t index : clearRange) |
| { |
| mCurComputeUAVs.update(index, nullptr); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| bool StateManager11::unsetConflictingView(ID3D11View *view) |
| { |
| uintptr_t resource = reinterpret_cast<uintptr_t>(GetViewResource(view)); |
| return unsetConflictingSRVs(gl::ShaderType::Vertex, resource, nullptr) || |
| unsetConflictingSRVs(gl::ShaderType::Fragment, resource, nullptr) || |
| unsetConflictingSRVs(gl::ShaderType::Compute, resource, nullptr); |
| } |
| |
| bool StateManager11::unsetConflictingSRVs(gl::ShaderType shaderType, |
| uintptr_t resource, |
| const gl::ImageIndex *index) |
| { |
| auto *currentSRVs = getSRVCache(shaderType); |
| |
| bool foundOne = false; |
| |
| for (size_t resourceIndex = 0; resourceIndex < currentSRVs->size(); ++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; |
| } |
| } |
| |
| return foundOne; |
| } |
| |
| void StateManager11::unsetConflictingAttachmentResources( |
| const gl::FramebufferAttachment &attachment, |
| ID3D11Resource *resource) |
| { |
| // Unbind render target SRVs from the shader here to prevent D3D11 warnings. |
| if (attachment.type() == GL_TEXTURE) |
| { |
| uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource); |
| const gl::ImageIndex &index = attachment.getTextureImageIndex(); |
| // The index doesn't need to be corrected for the small compressed texture workaround |
| // because a rendertarget is never compressed. |
| unsetConflictingSRVs(gl::ShaderType::Vertex, resourcePtr, &index); |
| unsetConflictingSRVs(gl::ShaderType::Fragment, resourcePtr, &index); |
| } |
| else if (attachment.type() == GL_FRAMEBUFFER_DEFAULT) |
| { |
| uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource); |
| unsetConflictingSRVs(gl::ShaderType::Vertex, resourcePtr, nullptr); |
| unsetConflictingSRVs(gl::ShaderType::Fragment, 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 = caps.maxShaderTextureImageUnits[shaderType]; |
| |
| mCurShaderSRVs[shaderType].initialize(maxShaderTextureImageUnits); |
| mForceSetShaderSamplerStates[shaderType].resize(maxShaderTextureImageUnits, true); |
| mCurShaderSamplerStates[shaderType].resize(maxShaderTextureImageUnits); |
| } |
| |
| 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; |
| mViewportOffsets.resize(1u); |
| |
| 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().id() == 0) |
| { |
| 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->getWorkarounds().mrtPerfWorkaround; |
| 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); |
| |
| 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(); |
| } |
| |
| 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; |
| mInternalDirtyBits.reset(); |
| |
| for (auto dirtyBit : dirtyBitsCopy) |
| { |
| switch (dirtyBit) |
| { |
| 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_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. |
| ASSERT(mInternalDirtyBits.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(static_cast<unsigned int>(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 && |
| static_cast<unsigned int>(index) < |
| mRenderer->getNativeCaps().maxShaderTextureImageUnits[gl::ShaderType::Fragment]) || |
| (type == gl::ShaderType::Vertex && |
| static_cast<unsigned int>(index) < |
| mRenderer->getNativeCaps().maxShaderTextureImageUnits[gl::ShaderType::Vertex]) || |
| (type == gl::ShaderType::Compute && |
| static_cast<unsigned int>(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(static_cast<unsigned int>(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)); |
| } |
| |
| size_t samplerCount = caps.maxShaderTextureImageUnits[shaderType]; |
| size_t readonlyImageCount = |
| context->getClientVersion() >= gl::Version(3, 1) ? caps.maxImageUnits : 0; |
| |
| // Samplers and readonly images share the SRVs here, their range is |
| // [0, max(samplerRange.high(), readonlyImageRange.high()). |
| ANGLE_TRY(clearSRVs(shaderType, std::max(samplerRange.high(), readonlyImageRange.high()), |
| samplerCount + readonlyImageCount)); |
| |
| 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)); |
| } |
| |
| size_t imageCount = caps.maxImageUnits; |
| ANGLE_TRY(clearUAVs(shaderType, imageRange.high(), imageCount)); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result StateManager11::syncTexturesForCompute(const gl::Context *context) |
| { |
| // applyTexturesForUAVs must be earlier than applyTexturesForSRVs since we need to do clearUVAs |
| // before set resources to SRVs. Otherwise, it will report the following error: |
| // ID3D11DeviceContext::CSSetShaderResources: Resource being set to CS shader resource slot 0 is |
| // still bound on output! Forcing to NULL. |
| 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()) |
| { |
| 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((static_cast<unsigned int>(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((static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxImageUnits)); |
| setUnorderedAccessViewInternal(type, index, textureUAV); |
| } |
| |
| textureImpl->resetDirty(); |
| return angle::Result::Continue; |
| } |
| |
| // 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; |
| ANGLE_TRY(mProgramD3D->getPixelExecutableForCachedOutputLayout(context11, &pixelExe, nullptr)); |
| |
| 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( |