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