Commit 401345e4 by Jamie Madill Committed by Commit Bot

D3D11: Move more state into StateManager11.

This moves the input layout cache and vertex and index data managers and related info into the state manager. This makes it easier to manage the state application with regards to dirty bits. Also updates the dirty current value handling in StateManager11. BUG=angleproject:1156 BUG=angleproject:2052 Change-Id: I8de968a1f8416363aa1c49d9e9da129942d21275 Reviewed-on: https://chromium-review.googlesource.com/616783Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 3cb4894c
...@@ -128,15 +128,19 @@ gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, ...@@ -128,15 +128,19 @@ gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer,
IndexDataManager::IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass) IndexDataManager::IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass)
: mFactory(factory), : mFactory(factory),
mRendererClass(rendererClass), mRendererClass(rendererClass),
mStreamingBufferShort(nullptr), mStreamingBufferShort(),
mStreamingBufferInt(nullptr) mStreamingBufferInt()
{ {
} }
IndexDataManager::~IndexDataManager() IndexDataManager::~IndexDataManager()
{ {
SafeDelete(mStreamingBufferShort); }
SafeDelete(mStreamingBufferInt);
void IndexDataManager::deinitialize()
{
mStreamingBufferShort.reset();
mStreamingBufferInt.reset();
} }
// static // static
...@@ -376,41 +380,19 @@ gl::Error IndexDataManager::getStreamingIndexBuffer(GLenum destinationIndexType, ...@@ -376,41 +380,19 @@ gl::Error IndexDataManager::getStreamingIndexBuffer(GLenum destinationIndexType,
IndexBufferInterface **outBuffer) IndexBufferInterface **outBuffer)
{ {
ASSERT(outBuffer); ASSERT(outBuffer);
if (destinationIndexType == GL_UNSIGNED_INT) ASSERT(destinationIndexType == GL_UNSIGNED_SHORT || destinationIndexType == GL_UNSIGNED_INT);
{
if (!mStreamingBufferInt)
{
mStreamingBufferInt = new StreamingIndexBufferInterface(mFactory);
gl::Error error =
mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT);
if (error.isError())
{
SafeDelete(mStreamingBufferInt);
return error;
}
}
*outBuffer = mStreamingBufferInt; auto &streamingBuffer =
return gl::NoError(); (destinationIndexType == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
}
else
{
ASSERT(destinationIndexType == GL_UNSIGNED_SHORT);
if (!mStreamingBufferShort) if (!streamingBuffer)
{
mStreamingBufferShort = new StreamingIndexBufferInterface(mFactory);
gl::Error error = mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE,
GL_UNSIGNED_SHORT);
if (error.isError())
{ {
SafeDelete(mStreamingBufferShort); StreamingBuffer newBuffer(new StreamingIndexBufferInterface(mFactory));
return error; ANGLE_TRY(newBuffer->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, destinationIndexType));
} streamingBuffer = std::move(newBuffer);
} }
*outBuffer = mStreamingBufferShort; *outBuffer = streamingBuffer.get();
return gl::NoError(); return gl::NoError();
}
}
} }
} // namespace rx
...@@ -68,6 +68,8 @@ class IndexDataManager : angle::NonCopyable ...@@ -68,6 +68,8 @@ class IndexDataManager : angle::NonCopyable
explicit IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass); explicit IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass);
virtual ~IndexDataManager(); virtual ~IndexDataManager();
void deinitialize();
static bool UsePrimitiveRestartWorkaround(bool primitiveRestartFixedIndexEnabled, static bool UsePrimitiveRestartWorkaround(bool primitiveRestartFixedIndexEnabled,
GLenum type, GLenum type,
RendererClass rendererClass); RendererClass rendererClass);
...@@ -91,11 +93,13 @@ class IndexDataManager : angle::NonCopyable ...@@ -91,11 +93,13 @@ class IndexDataManager : angle::NonCopyable
gl::Error getStreamingIndexBuffer(GLenum destinationIndexType, gl::Error getStreamingIndexBuffer(GLenum destinationIndexType,
IndexBufferInterface **outBuffer); IndexBufferInterface **outBuffer);
using StreamingBuffer = std::unique_ptr<StreamingIndexBufferInterface>;
BufferFactoryD3D *const mFactory; BufferFactoryD3D *const mFactory;
RendererClass mRendererClass; RendererClass mRendererClass;
StreamingIndexBufferInterface *mStreamingBufferShort; std::unique_ptr<StreamingIndexBufferInterface> mStreamingBufferShort;
StreamingIndexBufferInterface *mStreamingBufferInt; std::unique_ptr<StreamingIndexBufferInterface> mStreamingBufferInt;
}; };
} } // namespace rx
#endif // LIBANGLE_INDEXDATAMANAGER_H_ #endif // LIBANGLE_INDEXDATAMANAGER_H_
...@@ -113,6 +113,7 @@ bool DirectStoragePossible(const gl::VertexAttribute &attrib, const gl::VertexBi ...@@ -113,6 +113,7 @@ bool DirectStoragePossible(const gl::VertexAttribute &attrib, const gl::VertexBi
TranslatedAttribute::TranslatedAttribute() TranslatedAttribute::TranslatedAttribute()
: active(false), : active(false),
attribute(nullptr), attribute(nullptr),
binding(nullptr),
currentValueType(GL_NONE), currentValueType(GL_NONE),
baseOffset(0), baseOffset(0),
usesFirstVertexOffset(false), usesFirstVertexOffset(false),
...@@ -176,9 +177,7 @@ VertexStorageType ClassifyAttributeStorage(const gl::VertexAttribute &attrib, ...@@ -176,9 +177,7 @@ VertexStorageType ClassifyAttributeStorage(const gl::VertexAttribute &attrib,
} }
} }
VertexDataManager::CurrentValueState::CurrentValueState() VertexDataManager::CurrentValueState::CurrentValueState() : buffer(), offset(0)
: buffer(nullptr),
offset(0)
{ {
data.FloatValues[0] = std::numeric_limits<float>::quiet_NaN(); data.FloatValues[0] = std::numeric_limits<float>::quiet_NaN();
data.FloatValues[1] = std::numeric_limits<float>::quiet_NaN(); data.FloatValues[1] = std::numeric_limits<float>::quiet_NaN();
...@@ -189,26 +188,33 @@ VertexDataManager::CurrentValueState::CurrentValueState() ...@@ -189,26 +188,33 @@ VertexDataManager::CurrentValueState::CurrentValueState()
VertexDataManager::CurrentValueState::~CurrentValueState() VertexDataManager::CurrentValueState::~CurrentValueState()
{ {
SafeDelete(buffer);
} }
VertexDataManager::VertexDataManager(BufferFactoryD3D *factory) VertexDataManager::VertexDataManager(BufferFactoryD3D *factory)
: mFactory(factory), : mFactory(factory), mStreamingBuffer(), mCurrentValueCache(gl::MAX_VERTEX_ATTRIBS)
mStreamingBuffer(nullptr),
// TODO(jmadill): use context caps
mCurrentValueCache(gl::MAX_VERTEX_ATTRIBS)
{ {
mStreamingBuffer = new StreamingVertexBufferInterface(factory, INITIAL_STREAM_BUFFER_SIZE); }
VertexDataManager::~VertexDataManager()
{
}
gl::Error VertexDataManager::initialize()
{
mStreamingBuffer.reset(
new StreamingVertexBufferInterface(mFactory, INITIAL_STREAM_BUFFER_SIZE));
if (!mStreamingBuffer) if (!mStreamingBuffer)
{ {
ERR() << "Failed to allocate the streaming vertex buffer."; return gl::OutOfMemory() << "Failed to allocate the streaming vertex buffer.";
} }
return gl::NoError();
} }
VertexDataManager::~VertexDataManager() void VertexDataManager::deinitialize()
{ {
SafeDelete(mStreamingBuffer); mStreamingBuffer.reset();
mCurrentValueCache.clear();
} }
gl::Error VertexDataManager::prepareVertexData(const gl::State &state, gl::Error VertexDataManager::prepareVertexData(const gl::State &state,
...@@ -398,7 +404,7 @@ gl::Error VertexDataManager::storeDynamicAttribs( ...@@ -398,7 +404,7 @@ gl::Error VertexDataManager::storeDynamicAttribs(
}; };
// Will trigger unmapping on return. // Will trigger unmapping on return.
StreamingBufferUnmapper localUnmapper(mStreamingBuffer); StreamingBufferUnmapper localUnmapper(mStreamingBuffer.get());
// Reserve the required space for the dynamic buffers. // Reserve the required space for the dynamic buffers.
for (auto attribIndex : dynamicAttribsMask) for (auto attribIndex : dynamicAttribsMask)
...@@ -522,11 +528,11 @@ gl::Error VertexDataManager::storeCurrentValue(const gl::VertexAttribCurrentValu ...@@ -522,11 +528,11 @@ gl::Error VertexDataManager::storeCurrentValue(const gl::VertexAttribCurrentValu
size_t attribIndex) size_t attribIndex)
{ {
CurrentValueState *cachedState = &mCurrentValueCache[attribIndex]; CurrentValueState *cachedState = &mCurrentValueCache[attribIndex];
auto *&buffer = cachedState->buffer; auto &buffer = cachedState->buffer;
if (!buffer) if (!buffer)
{ {
buffer = new StreamingVertexBufferInterface(mFactory, CONSTANT_VERTEX_BUFFER_SIZE); buffer.reset(new StreamingVertexBufferInterface(mFactory, CONSTANT_VERTEX_BUFFER_SIZE));
} }
if (cachedState->data != currentValue) if (cachedState->data != currentValue)
......
...@@ -87,6 +87,9 @@ class VertexDataManager : angle::NonCopyable ...@@ -87,6 +87,9 @@ class VertexDataManager : angle::NonCopyable
VertexDataManager(BufferFactoryD3D *factory); VertexDataManager(BufferFactoryD3D *factory);
virtual ~VertexDataManager(); virtual ~VertexDataManager();
gl::Error initialize();
void deinitialize();
gl::Error prepareVertexData(const gl::State &state, gl::Error prepareVertexData(const gl::State &state,
GLint start, GLint start,
GLsizei count, GLsizei count,
...@@ -118,7 +121,7 @@ class VertexDataManager : angle::NonCopyable ...@@ -118,7 +121,7 @@ class VertexDataManager : angle::NonCopyable
CurrentValueState(); CurrentValueState();
~CurrentValueState(); ~CurrentValueState();
StreamingVertexBufferInterface *buffer; std::unique_ptr<StreamingVertexBufferInterface> buffer;
gl::VertexAttribCurrentValueData data; gl::VertexAttribCurrentValueData data;
size_t offset; size_t offset;
}; };
...@@ -134,7 +137,7 @@ class VertexDataManager : angle::NonCopyable ...@@ -134,7 +137,7 @@ class VertexDataManager : angle::NonCopyable
BufferFactoryD3D *const mFactory; BufferFactoryD3D *const mFactory;
StreamingVertexBufferInterface *mStreamingBuffer; std::unique_ptr<StreamingVertexBufferInterface> mStreamingBuffer;
std::vector<CurrentValueState> mCurrentValueCache; std::vector<CurrentValueState> mCurrentValueCache;
gl::AttributesMask mDynamicAttribsMaskCache; gl::AttributesMask mDynamicAttribsMaskCache;
}; };
......
...@@ -403,12 +403,8 @@ Renderer11::Renderer11(egl::Display *display) ...@@ -403,12 +403,8 @@ Renderer11::Renderer11(egl::Display *display)
mScratchMemoryBuffer(ScratchMemoryBufferLifetime), mScratchMemoryBuffer(ScratchMemoryBufferLifetime),
mAnnotator(nullptr) mAnnotator(nullptr)
{ {
mVertexDataManager = nullptr;
mIndexDataManager = nullptr;
mLineLoopIB = nullptr; mLineLoopIB = nullptr;
mTriangleFanIB = nullptr; mTriangleFanIB = nullptr;
mAppliedIBChanged = false;
mBlit = nullptr; mBlit = nullptr;
mPixelTransfer = nullptr; mPixelTransfer = nullptr;
...@@ -683,7 +679,7 @@ egl::Error Renderer11::initialize() ...@@ -683,7 +679,7 @@ egl::Error Renderer11::initialize()
mDebug = d3d11::DynamicCastComObject<ID3D11Debug>(mDevice); mDebug = d3d11::DynamicCastComObject<ID3D11Debug>(mDevice);
#endif #endif
initializeDevice(); ANGLE_TRY(initializeDevice());
return egl::NoError(); return egl::NoError();
} }
...@@ -790,7 +786,7 @@ egl::Error Renderer11::initializeD3DDevice() ...@@ -790,7 +786,7 @@ egl::Error Renderer11::initializeD3DDevice()
// do any one-time device initialization // do any one-time device initialization
// NOTE: this is also needed after a device lost/reset // NOTE: this is also needed after a device lost/reset
// to reset the scene status and ensure the default states are reset. // to reset the scene status and ensure the default states are reset.
void Renderer11::initializeDevice() egl::Error Renderer11::initializeDevice()
{ {
SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDeviceMS"); SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDeviceMS");
TRACE_EVENT0("gpu.angle", "Renderer11::initializeDevice"); TRACE_EVENT0("gpu.angle", "Renderer11::initializeDevice");
...@@ -799,10 +795,6 @@ void Renderer11::initializeDevice() ...@@ -799,10 +795,6 @@ void Renderer11::initializeDevice()
mStateCache.clear(); mStateCache.clear();
ASSERT(!mVertexDataManager && !mIndexDataManager);
mVertexDataManager = new VertexDataManager(this);
mIndexDataManager = new IndexDataManager(this, getRendererClass());
ASSERT(!mBlit); ASSERT(!mBlit);
mBlit = new Blit11(this); mBlit = new Blit11(this);
...@@ -827,7 +819,10 @@ void Renderer11::initializeDevice() ...@@ -827,7 +819,10 @@ void Renderer11::initializeDevice()
const gl::Caps &rendererCaps = getNativeCaps(); const gl::Caps &rendererCaps = getNativeCaps();
mStateManager.initialize(rendererCaps); if (mStateManager.initialize(rendererCaps).isError())
{
return egl::EglBadAlloc() << "Error initializing state manager.";
}
// No context is available here, use the proxy context in the display. // No context is available here, use the proxy context in the display.
markAllStateDirty(mDisplay->getProxyContext()); markAllStateDirty(mDisplay->getProxyContext());
...@@ -848,6 +843,8 @@ void Renderer11::initializeDevice() ...@@ -848,6 +843,8 @@ void Renderer11::initializeDevice()
ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.D3D11FeatureLevel", angleFeatureLevel, ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.D3D11FeatureLevel", angleFeatureLevel,
NUM_ANGLE_FEATURE_LEVELS); NUM_ANGLE_FEATURE_LEVELS);
return egl::NoError();
} }
void Renderer11::populateRenderer11DeviceCaps() void Renderer11::populateRenderer11DeviceCaps()
...@@ -1585,94 +1582,6 @@ bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSi ...@@ -1585,94 +1582,6 @@ bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSi
return count >= minCount; return count >= minCount;
} }
gl::Error Renderer11::applyVertexBuffer(const gl::Context *context,
GLenum mode,
GLint first,
GLsizei count,
GLsizei instances,
TranslatedIndexData *indexInfo)
{
const auto &state = context->getGLState();
const auto &vertexArray = state.getVertexArray();
auto *vertexArray11 = GetImplAs<VertexArray11>(vertexArray);
ANGLE_TRY(vertexArray11->updateDirtyAndDynamicAttribs(context, mVertexDataManager, first, count,
instances));
ANGLE_TRY(mStateManager.updateCurrentValueAttribs(state, mVertexDataManager));
// If index information is passed, mark it with the current changed status.
if (indexInfo)
{
indexInfo->srcIndexData.srcIndicesChanged = mAppliedIBChanged;
}
GLsizei numIndicesPerInstance = 0;
if (instances > 0)
{
numIndicesPerInstance = count;
}
const auto &vertexArrayAttribs = vertexArray11->getTranslatedAttribs();
const auto &currentValueAttribs = mStateManager.getCurrentValueAttribs();
ANGLE_TRY(mInputLayoutCache.applyVertexBuffers(this, state, vertexArrayAttribs,
currentValueAttribs, mode, first, indexInfo,
numIndicesPerInstance));
// InputLayoutCache::applyVertexBuffers calls through to the Bufer11 to get the native vertex
// buffer (ID3D11Buffer *). Because we allocate these buffers lazily, this will trigger
// allocation. This in turn will signal that the buffer is dirty. Since we just resolved the
// dirty-ness in VertexArray11::updateDirtyAndDynamicAttribs, this can make us do a needless
// update on the second draw call.
// Hence we clear the flags here, after we've applied vertex data, since we know everything
// is clean. This is a bit of a hack.
vertexArray11->clearDirtyAndPromoteDynamicAttribs(state, count);
return gl::NoError();
}
gl::Error Renderer11::applyIndexBuffer(const gl::ContextState &data,
const void *indices,
GLsizei count,
GLenum mode,
GLenum type,
TranslatedIndexData *indexInfo)
{
const auto &glState = data.getState();
gl::VertexArray *vao = glState.getVertexArray();
gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get();
ANGLE_TRY(mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices,
indexInfo, glState.isPrimitiveRestartEnabled()));
ID3D11Buffer *buffer = nullptr;
DXGI_FORMAT bufferFormat =
(indexInfo->indexType == GL_UNSIGNED_INT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
if (indexInfo->storage)
{
Buffer11 *storage = GetAs<Buffer11>(indexInfo->storage);
ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDEX), buffer);
}
else
{
IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(indexInfo->indexBuffer);
buffer = indexBuffer->getBuffer().get();
}
mAppliedIBChanged = false;
if (buffer != mAppliedIB || bufferFormat != mAppliedIBFormat ||
indexInfo->startOffset != mAppliedIBOffset)
{
mDeviceContext->IASetIndexBuffer(buffer, bufferFormat, indexInfo->startOffset);
mAppliedIB = buffer;
mAppliedIBFormat = bufferFormat;
mAppliedIBOffset = indexInfo->startOffset;
mAppliedIBChanged = true;
}
return gl::NoError();
}
gl::Error Renderer11::applyTransformFeedbackBuffers(const gl::ContextState &data) gl::Error Renderer11::applyTransformFeedbackBuffers(const gl::ContextState &data)
{ {
const auto &state = data.getState(); const auto &state = data.getState();
...@@ -1779,24 +1688,11 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1779,24 +1688,11 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
bool useInstancedPointSpriteEmulation = bool useInstancedPointSpriteEmulation =
programD3D->usesPointSize() && getWorkarounds().useInstancedPointSpriteEmulation; programD3D->usesPointSize() && getWorkarounds().useInstancedPointSpriteEmulation;
if (instances > 0) if (mode != GL_POINTS || !useInstancedPointSpriteEmulation)
{
if (mode == GL_POINTS && useInstancedPointSpriteEmulation)
{ {
// If pointsprite emulation is used with glDrawArraysInstanced then we need to take a if (instances == 0)
// less efficent code path.
// Instanced rendering of emulated pointsprites requires a loop to draw each batch of
// points. An offset into the instanced data buffer is calculated and applied on each
// iteration to ensure all instances are rendered correctly.
// Each instance being rendered requires the inputlayout cache to reapply buffers and
// offsets.
for (GLsizei i = 0; i < instances; i++)
{ {
ANGLE_TRY(mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation( mDeviceContext->Draw(count, 0);
this, startVertex, i));
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
}
} }
else else
{ {
...@@ -1808,13 +1704,21 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1808,13 +1704,21 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
// If the shader is writing to gl_PointSize, then pointsprites are being rendered. // If the shader is writing to gl_PointSize, then pointsprites are being rendered.
// Emulating instanced point sprites for FL9_3 requires the topology to be // Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
if (mode == GL_POINTS && useInstancedPointSpriteEmulation) if (instances == 0)
{ {
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
return gl::NoError();
} }
else
// If pointsprite emulation is used with glDrawArraysInstanced then we need to take a less
// efficent code path. Instanced rendering of emulated pointsprites requires a loop to draw each
// batch of points. An offset into the instanced data buffer is calculated and applied on each
// iteration to ensure all instances are rendered correctly. Each instance being rendered
// requires the inputlayout cache to reapply buffers and offsets.
for (GLsizei i = 0; i < instances; i++)
{ {
mDeviceContext->Draw(count, 0); ANGLE_TRY(mStateManager.updateVertexOffsetsForPointSpritesEmulation(startVertex, i));
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -1831,8 +1735,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1831,8 +1735,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
if (!DrawCallNeedsTranslation(context, mode, type)) if (!DrawCallNeedsTranslation(context, mode, type))
{ {
ANGLE_TRY(applyIndexBuffer(data, nullptr, 0, mode, type, &indexInfo)); ANGLE_TRY(mStateManager.applyIndexBuffer(data, nullptr, 0, type, &indexInfo));
ANGLE_TRY(applyVertexBuffer(context, mode, 0, 0, 0, &indexInfo)); ANGLE_TRY(mStateManager.applyVertexBuffer(context, mode, 0, 0, 0, &indexInfo));
const gl::Type &typeInfo = gl::GetTypeInfo(type); const gl::Type &typeInfo = gl::GetTypeInfo(type);
unsigned int startIndexLocation = unsigned int startIndexLocation =
static_cast<unsigned int>(reinterpret_cast<const uintptr_t>(indices)) / typeInfo.bytes; static_cast<unsigned int>(reinterpret_cast<const uintptr_t>(indices)) / typeInfo.bytes;
...@@ -1851,9 +1755,10 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1851,9 +1755,10 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
context->getParams<gl::HasIndexRange>().getIndexRange().value(); context->getParams<gl::HasIndexRange>().getIndexRange().value();
indexInfo.indexRange = indexRange; indexInfo.indexRange = indexRange;
ANGLE_TRY(applyIndexBuffer(data, indices, count, mode, type, &indexInfo)); ANGLE_TRY(mStateManager.applyIndexBuffer(data, indices, count, type, &indexInfo));
size_t vertexCount = indexInfo.indexRange.vertexCount(); size_t vertexCount = indexInfo.indexRange.vertexCount();
ANGLE_TRY(applyVertexBuffer(context, mode, static_cast<GLsizei>(indexInfo.indexRange.start), ANGLE_TRY(mStateManager.applyVertexBuffer(
context, mode, static_cast<GLsizei>(indexInfo.indexRange.start),
static_cast<GLsizei>(vertexCount), instances, &indexInfo)); static_cast<GLsizei>(vertexCount), instances, &indexInfo));
int startVertex = static_cast<int>(indexInfo.indexRange.start); int startVertex = static_cast<int>(indexInfo.indexRange.start);
...@@ -1870,25 +1775,12 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1870,25 +1775,12 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
} }
const ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.getState().getProgram()); const ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.getState().getProgram());
if (instances > 0)
{
if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation())
{
// If pointsprite emulation is used with glDrawElementsInstanced then we need to take a
// less efficent code path.
// Instanced rendering of emulated pointsprites requires a loop to draw each batch of
// points. An offset into the instanced data buffer is calculated and applied on each
// iteration to ensure all instances are rendered correctly.
GLsizei elementsToRender = static_cast<GLsizei>(indexInfo.indexRange.vertexCount());
// Each instance being rendered requires the inputlayout cache to reapply buffers and if (mode != GL_POINTS || !programD3D->usesInstancedPointSpriteEmulation())
// offsets.
for (GLsizei i = 0; i < instances; i++)
{ {
ANGLE_TRY(mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation( if (instances == 0)
this, startVertex, i)); {
mDeviceContext->DrawIndexedInstanced(6, elementsToRender, 0, 0, 0); mDeviceContext->DrawIndexed(count, 0, baseVertex);
}
} }
else else
{ {
...@@ -1900,21 +1792,30 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1900,21 +1792,30 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
// If the shader is writing to gl_PointSize, then pointsprites are being rendered. // If the shader is writing to gl_PointSize, then pointsprites are being rendered.
// Emulating instanced point sprites for FL9_3 requires the topology to be // Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation())
{
// The count parameter passed to drawElements represents the total number of instances
// to be rendered. Each instance is referenced by the bound index buffer from the
// the caller.
// //
// Indexed pointsprite emulation replicates data for duplicate entries found // The count parameter passed to drawElements represents the total number of instances to be
// in the index buffer. // rendered. Each instance is referenced by the bound index buffer from the the caller.
// This is not an efficent rendering mechanism and is only used on downlevel renderers //
// Indexed pointsprite emulation replicates data for duplicate entries found in the index
// buffer. This is not an efficent rendering mechanism and is only used on downlevel renderers
// that do not support geometry shaders. // that do not support geometry shaders.
if (instances == 0)
{
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
return gl::NoError();
} }
else
// If pointsprite emulation is used with glDrawElementsInstanced then we need to take a less
// efficent code path. Instanced rendering of emulated pointsprites requires a loop to draw each
// batch of points. An offset into the instanced data buffer is calculated and applied on each
// iteration to ensure all instances are rendered correctly.
GLsizei elementsToRender = static_cast<GLsizei>(indexInfo.indexRange.vertexCount());
// Each instance being rendered requires the inputlayout cache to reapply buffers and offsets.
for (GLsizei i = 0; i < instances; i++)
{ {
mDeviceContext->DrawIndexed(count, 0, baseVertex); ANGLE_TRY(mStateManager.updateVertexOffsetsForPointSpritesEmulation(startVertex, i));
mDeviceContext->DrawIndexedInstanced(6, elementsToRender, 0, 0, 0);
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -1937,7 +1838,7 @@ gl::Error Renderer11::drawArraysIndirectImpl(const gl::Context *context, ...@@ -1937,7 +1838,7 @@ gl::Error Renderer11::drawArraysIndirectImpl(const gl::Context *context,
if (!DrawCallNeedsTranslation(context, mode, GL_NONE)) if (!DrawCallNeedsTranslation(context, mode, GL_NONE))
{ {
applyVertexBuffer(context, mode, 0, 0, 0, nullptr); mStateManager.applyVertexBuffer(context, mode, 0, 0, 0, nullptr);
ID3D11Buffer *buffer = nullptr; ID3D11Buffer *buffer = nullptr;
ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDIRECT), buffer); ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDIRECT), buffer);
mDeviceContext->DrawInstancedIndirect(buffer, static_cast<unsigned int>(offset)); mDeviceContext->DrawInstancedIndirect(buffer, static_cast<unsigned int>(offset));
...@@ -1953,7 +1854,7 @@ gl::Error Renderer11::drawArraysIndirectImpl(const gl::Context *context, ...@@ -1953,7 +1854,7 @@ gl::Error Renderer11::drawArraysIndirectImpl(const gl::Context *context,
GLuint instances = args->instanceCount; GLuint instances = args->instanceCount;
GLuint first = args->first; GLuint first = args->first;
ANGLE_TRY(applyVertexBuffer(context, mode, first, count, instances, nullptr)); ANGLE_TRY(mStateManager.applyVertexBuffer(context, mode, first, count, instances, nullptr));
if (mode == GL_LINE_LOOP) if (mode == GL_LINE_LOOP)
{ {
...@@ -1988,8 +1889,8 @@ gl::Error Renderer11::drawElementsIndirectImpl(const gl::Context *context, ...@@ -1988,8 +1889,8 @@ gl::Error Renderer11::drawElementsIndirectImpl(const gl::Context *context,
TranslatedIndexData indexInfo; TranslatedIndexData indexInfo;
if (!DrawCallNeedsTranslation(context, mode, type)) if (!DrawCallNeedsTranslation(context, mode, type))
{ {
ANGLE_TRY(applyIndexBuffer(contextState, nullptr, 0, mode, type, &indexInfo)); ANGLE_TRY(mStateManager.applyIndexBuffer(contextState, nullptr, 0, type, &indexInfo));
ANGLE_TRY(applyVertexBuffer(context, mode, 0, 0, 0, &indexInfo)); ANGLE_TRY(mStateManager.applyVertexBuffer(context, mode, 0, 0, 0, &indexInfo));
ID3D11Buffer *buffer = nullptr; ID3D11Buffer *buffer = nullptr;
ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDIRECT), buffer); ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDIRECT), buffer);
mDeviceContext->DrawIndexedInstancedIndirect(buffer, static_cast<unsigned int>(offset)); mDeviceContext->DrawIndexedInstancedIndirect(buffer, static_cast<unsigned int>(offset));
...@@ -2017,9 +1918,10 @@ gl::Error Renderer11::drawElementsIndirectImpl(const gl::Context *context, ...@@ -2017,9 +1918,10 @@ gl::Error Renderer11::drawElementsIndirectImpl(const gl::Context *context,
glState.isPrimitiveRestartEnabled(), &indexRange)); glState.isPrimitiveRestartEnabled(), &indexRange));
indexInfo.indexRange = indexRange; indexInfo.indexRange = indexRange;
ANGLE_TRY(applyIndexBuffer(contextState, indices, count, mode, type, &indexInfo)); ANGLE_TRY(mStateManager.applyIndexBuffer(contextState, indices, count, type, &indexInfo));
size_t vertexCount = indexRange.vertexCount(); size_t vertexCount = indexRange.vertexCount();
ANGLE_TRY(applyVertexBuffer(context, mode, static_cast<GLsizei>(indexRange.start) + baseVertex, ANGLE_TRY(mStateManager.applyVertexBuffer(
context, mode, static_cast<GLsizei>(indexRange.start) + baseVertex,
static_cast<GLsizei>(vertexCount), instances, &indexInfo)); static_cast<GLsizei>(vertexCount), instances, &indexInfo));
int baseVertexLocation = -static_cast<int>(indexRange.start); int baseVertexLocation = -static_cast<int>(indexRange.start);
...@@ -2105,14 +2007,7 @@ gl::Error Renderer11::drawLineLoop(const gl::ContextState &data, ...@@ -2105,14 +2007,7 @@ gl::Error Renderer11::drawLineLoop(const gl::ContextState &data,
const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer(); const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer();
DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat();
if (mAppliedIB != d3dIndexBuffer.get() || mAppliedIBFormat != indexFormat || mStateManager.setIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset, false);
mAppliedIBOffset != offset)
{
mDeviceContext->IASetIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset);
mAppliedIB = d3dIndexBuffer.get();
mAppliedIBFormat = indexFormat;
mAppliedIBOffset = offset;
}
UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size());
...@@ -2194,14 +2089,7 @@ gl::Error Renderer11::drawTriangleFan(const gl::ContextState &data, ...@@ -2194,14 +2089,7 @@ gl::Error Renderer11::drawTriangleFan(const gl::ContextState &data,
const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer(); const d3d11::Buffer &d3dIndexBuffer = indexBuffer->getBuffer();
DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat();
if (mAppliedIB != d3dIndexBuffer.get() || mAppliedIBFormat != indexFormat || mStateManager.setIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset, false);
mAppliedIBOffset != offset)
{
mDeviceContext->IASetIndexBuffer(d3dIndexBuffer.get(), indexFormat, offset);
mAppliedIB = d3dIndexBuffer.get();
mAppliedIBFormat = indexFormat;
mAppliedIBOffset = offset;
}
UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size()); UINT indexCount = static_cast<UINT>(mScratchIndexDataBuffer.size());
...@@ -2436,10 +2324,6 @@ void Renderer11::markAllStateDirty(const gl::Context *context) ...@@ -2436,10 +2324,6 @@ void Renderer11::markAllStateDirty(const gl::Context *context)
mStateManager.invalidateEverything(context); mStateManager.invalidateEverything(context);
mAppliedIB = nullptr;
mAppliedIBFormat = DXGI_FORMAT_UNKNOWN;
mAppliedIBOffset = 0;
mAppliedTFObject = angle::DirtyPointer; mAppliedTFObject = angle::DirtyPointer;
memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants11)); memset(&mAppliedVertexConstants, 0, sizeof(dx_VertexConstants11));
...@@ -2465,10 +2349,7 @@ void Renderer11::releaseDeviceResources() ...@@ -2465,10 +2349,7 @@ void Renderer11::releaseDeviceResources()
{ {
mStateManager.deinitialize(); mStateManager.deinitialize();
mStateCache.clear(); mStateCache.clear();
mInputLayoutCache.clear();
SafeDelete(mVertexDataManager);
SafeDelete(mIndexDataManager);
SafeDelete(mLineLoopIB); SafeDelete(mLineLoopIB);
SafeDelete(mTriangleFanIB); SafeDelete(mTriangleFanIB);
SafeDelete(mBlit); SafeDelete(mBlit);
...@@ -4318,7 +4199,7 @@ gl::Error Renderer11::genericDrawArrays(const gl::Context *context, ...@@ -4318,7 +4199,7 @@ gl::Error Renderer11::genericDrawArrays(const gl::Context *context,
ANGLE_TRY(mStateManager.updateState(context, mode)); ANGLE_TRY(mStateManager.updateState(context, mode));
ANGLE_TRY(applyTransformFeedbackBuffers(data)); ANGLE_TRY(applyTransformFeedbackBuffers(data));
ANGLE_TRY(applyVertexBuffer(context, mode, first, count, instances, nullptr)); ANGLE_TRY(mStateManager.applyVertexBuffer(context, mode, first, count, instances, nullptr));
ANGLE_TRY(programD3D->applyUniformBuffers(data)); ANGLE_TRY(programD3D->applyUniformBuffers(data));
if (!skipDraw(data, mode)) if (!skipDraw(data, mode))
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h" #include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h"
#include "libANGLE/renderer/d3d/d3d11/InputLayoutCache.h"
#include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h"
#include "libANGLE/renderer/d3d/d3d11/ResourceManager11.h" #include "libANGLE/renderer/d3d/d3d11/ResourceManager11.h"
#include "libANGLE/renderer/d3d/d3d11/StateManager11.h" #include "libANGLE/renderer/d3d/d3d11/StateManager11.h"
...@@ -146,18 +145,6 @@ class Renderer11 : public RendererD3D ...@@ -146,18 +145,6 @@ class Renderer11 : public RendererD3D
gl::Error applyUniforms(const ProgramD3D &programD3D, gl::Error applyUniforms(const ProgramD3D &programD3D,
GLenum drawMode, GLenum drawMode,
const std::vector<D3DUniform *> &uniformArray) override; const std::vector<D3DUniform *> &uniformArray) override;
gl::Error applyVertexBuffer(const gl::Context *context,
GLenum mode,
GLint first,
GLsizei count,
GLsizei instances,
TranslatedIndexData *indexInfo);
gl::Error applyIndexBuffer(const gl::ContextState &data,
const void *indices,
GLsizei count,
GLenum mode,
GLenum type,
TranslatedIndexData *indexInfo);
gl::Error applyTransformFeedbackBuffers(const gl::ContextState &data); gl::Error applyTransformFeedbackBuffers(const gl::ContextState &data);
// lost device // lost device
...@@ -384,7 +371,6 @@ class Renderer11 : public RendererD3D ...@@ -384,7 +371,6 @@ class Renderer11 : public RendererD3D
const Renderer11DeviceCaps &getRenderer11DeviceCaps() const { return mRenderer11DeviceCaps; }; const Renderer11DeviceCaps &getRenderer11DeviceCaps() const { return mRenderer11DeviceCaps; };
RendererClass getRendererClass() const override { return RENDERER_D3D11; } RendererClass getRendererClass() const override { return RENDERER_D3D11; }
InputLayoutCache *getInputLayoutCache() { return &mInputLayoutCache; }
StateManager11 *getStateManager() { return &mStateManager; } StateManager11 *getStateManager() { return &mStateManager; }
void onSwap(); void onSwap();
...@@ -542,7 +528,7 @@ class Renderer11 : public RendererD3D ...@@ -542,7 +528,7 @@ class Renderer11 : public RendererD3D
const gl::TextureCaps &depthStencilBufferFormatCaps) const; const gl::TextureCaps &depthStencilBufferFormatCaps) const;
egl::Error initializeD3DDevice(); egl::Error initializeD3DDevice();
void initializeDevice(); egl::Error initializeDevice();
void releaseDeviceResources(); void releaseDeviceResources();
void release(); void release();
...@@ -563,12 +549,6 @@ class Renderer11 : public RendererD3D ...@@ -563,12 +549,6 @@ class Renderer11 : public RendererD3D
StateManager11 mStateManager; StateManager11 mStateManager;
// Currently applied index buffer
ID3D11Buffer *mAppliedIB;
DXGI_FORMAT mAppliedIBFormat;
unsigned int mAppliedIBOffset;
bool mAppliedIBChanged;
// Currently applied transform feedback buffers // Currently applied transform feedback buffers
uintptr_t mAppliedTFObject; uintptr_t mAppliedTFObject;
...@@ -592,11 +572,6 @@ class Renderer11 : public RendererD3D ...@@ -592,11 +572,6 @@ class Renderer11 : public RendererD3D
uintptr_t mCurrentGeometryConstantBuffer; uintptr_t mCurrentGeometryConstantBuffer;
// Vertex, index and input layouts
VertexDataManager *mVertexDataManager;
IndexDataManager *mIndexDataManager;
InputLayoutCache mInputLayoutCache;
StreamingIndexBufferInterface *mLineLoopIB; StreamingIndexBufferInterface *mLineLoopIB;
StreamingIndexBufferInterface *mTriangleFanIB; StreamingIndexBufferInterface *mTriangleFanIB;
......
...@@ -14,7 +14,9 @@ ...@@ -14,7 +14,9 @@
#include "libANGLE/Query.h" #include "libANGLE/Query.h"
#include "libANGLE/VertexArray.h" #include "libANGLE/VertexArray.h"
#include "libANGLE/renderer/d3d/TextureD3D.h" #include "libANGLE/renderer/d3d/TextureD3D.h"
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.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/RenderTarget11.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h" #include "libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h"
...@@ -287,7 +289,13 @@ StateManager11::StateManager11(Renderer11 *renderer) ...@@ -287,7 +289,13 @@ StateManager11::StateManager11(Renderer11 *renderer)
mCurrentInputLayout(), mCurrentInputLayout(),
mInputLayoutIsDirty(false), mInputLayoutIsDirty(false),
mDirtyVertexBufferRange(gl::MAX_VERTEX_ATTRIBS, 0), mDirtyVertexBufferRange(gl::MAX_VERTEX_ATTRIBS, 0),
mCurrentPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED) mCurrentPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED),
mAppliedIB(nullptr),
mAppliedIBFormat(DXGI_FORMAT_UNKNOWN),
mAppliedIBOffset(0),
mAppliedIBChanged(false),
mVertexDataManager(renderer),
mIndexDataManager(renderer, RENDERER_D3D11)
{ {
mCurBlendState.blend = false; mCurBlendState.blend = false;
mCurBlendState.sourceBlendRGB = GL_ONE; mCurBlendState.sourceBlendRGB = GL_ONE;
...@@ -996,6 +1004,10 @@ void StateManager11::invalidateEverything(const gl::Context *context) ...@@ -996,6 +1004,10 @@ void StateManager11::invalidateEverything(const gl::Context *context)
std::fill(mForceSetVertexSamplerStates.begin(), mForceSetVertexSamplerStates.end(), true); std::fill(mForceSetVertexSamplerStates.begin(), mForceSetVertexSamplerStates.end(), true);
std::fill(mForceSetPixelSamplerStates.begin(), mForceSetPixelSamplerStates.end(), true); std::fill(mForceSetPixelSamplerStates.begin(), mForceSetPixelSamplerStates.end(), true);
std::fill(mForceSetComputeSamplerStates.begin(), mForceSetComputeSamplerStates.end(), true); std::fill(mForceSetComputeSamplerStates.begin(), mForceSetComputeSamplerStates.end(), true);
mAppliedIB = nullptr;
mAppliedIBFormat = DXGI_FORMAT_UNKNOWN;
mAppliedIBOffset = 0;
} }
void StateManager11::invalidateVertexBuffer() void StateManager11::invalidateVertexBuffer()
...@@ -1155,7 +1167,7 @@ void StateManager11::unsetConflictingAttachmentResources( ...@@ -1155,7 +1167,7 @@ void StateManager11::unsetConflictingAttachmentResources(
} }
} }
void StateManager11::initialize(const gl::Caps &caps) gl::Error StateManager11::initialize(const gl::Caps &caps)
{ {
mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits); mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits);
mCurPixelSRVs.initialize(caps.maxTextureImageUnits); mCurPixelSRVs.initialize(caps.maxTextureImageUnits);
...@@ -1176,11 +1188,17 @@ void StateManager11::initialize(const gl::Caps &caps) ...@@ -1176,11 +1188,17 @@ void StateManager11::initialize(const gl::Caps &caps)
mSamplerMetadataVS.initData(caps.maxVertexTextureImageUnits); mSamplerMetadataVS.initData(caps.maxVertexTextureImageUnits);
mSamplerMetadataPS.initData(caps.maxTextureImageUnits); mSamplerMetadataPS.initData(caps.maxTextureImageUnits);
mSamplerMetadataCS.initData(caps.maxComputeTextureImageUnits); mSamplerMetadataCS.initData(caps.maxComputeTextureImageUnits);
ANGLE_TRY(mVertexDataManager.initialize());
return gl::NoError();
} }
void StateManager11::deinitialize() void StateManager11::deinitialize()
{ {
mCurrentValueAttribs.clear(); mCurrentValueAttribs.clear();
mInputLayoutCache.clear();
mVertexDataManager.deinitialize();
mIndexDataManager.deinitialize();
} }
gl::Error StateManager11::syncFramebuffer(const gl::Context *context, gl::Framebuffer *framebuffer) gl::Error StateManager11::syncFramebuffer(const gl::Context *context, gl::Framebuffer *framebuffer)
...@@ -1266,21 +1284,31 @@ gl::Error StateManager11::syncFramebuffer(const gl::Context *context, gl::Frameb ...@@ -1266,21 +1284,31 @@ gl::Error StateManager11::syncFramebuffer(const gl::Context *context, gl::Frameb
return gl::NoError(); return gl::NoError();
} }
gl::Error StateManager11::updateCurrentValueAttribs(const gl::State &state, void StateManager11::invalidateCurrentValueAttrib(size_t attribIndex)
VertexDataManager *vertexDataManager) {
mDirtyCurrentValueAttribs.set(attribIndex);
}
gl::Error StateManager11::syncCurrentValueAttribs(const gl::State &state)
{ {
const auto &activeAttribsMask = state.getProgram()->getActiveAttribLocationsMask(); const auto &activeAttribsMask = state.getProgram()->getActiveAttribLocationsMask();
const auto &dirtyActiveAttribs = (activeAttribsMask & mDirtyCurrentValueAttribs); const auto &dirtyActiveAttribs = (activeAttribsMask & mDirtyCurrentValueAttribs);
const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes(); const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes();
const auto &vertexBindings = state.getVertexArray()->getVertexBindings(); const auto &vertexBindings = state.getVertexArray()->getVertexBindings();
if (!dirtyActiveAttribs.any())
{
return gl::NoError();
}
invalidateVertexBuffer();
mDirtyCurrentValueAttribs = (mDirtyCurrentValueAttribs & ~dirtyActiveAttribs);
for (auto attribIndex : dirtyActiveAttribs) for (auto attribIndex : dirtyActiveAttribs)
{ {
if (vertexAttributes[attribIndex].enabled) if (vertexAttributes[attribIndex].enabled)
continue; continue;
mDirtyCurrentValueAttribs.reset(attribIndex);
const auto *attrib = &vertexAttributes[attribIndex]; const auto *attrib = &vertexAttributes[attribIndex];
const auto &currentValue = state.getVertexAttribCurrentValue(attribIndex); const auto &currentValue = state.getVertexAttribCurrentValue(attribIndex);
auto currentValueAttrib = &mCurrentValueAttribs[attribIndex]; auto currentValueAttrib = &mCurrentValueAttribs[attribIndex];
...@@ -1288,18 +1316,13 @@ gl::Error StateManager11::updateCurrentValueAttribs(const gl::State &state, ...@@ -1288,18 +1316,13 @@ gl::Error StateManager11::updateCurrentValueAttribs(const gl::State &state,
currentValueAttrib->attribute = attrib; currentValueAttrib->attribute = attrib;
currentValueAttrib->binding = &vertexBindings[attrib->bindingIndex]; currentValueAttrib->binding = &vertexBindings[attrib->bindingIndex];
ANGLE_TRY(vertexDataManager->storeCurrentValue(currentValue, currentValueAttrib, ANGLE_TRY(mVertexDataManager.storeCurrentValue(currentValue, currentValueAttrib,
static_cast<size_t>(attribIndex))); static_cast<size_t>(attribIndex)));
} }
return gl::NoError(); return gl::NoError();
} }
const std::vector<TranslatedAttribute> &StateManager11::getCurrentValueAttribs() const
{
return mCurrentValueAttribs;
}
void StateManager11::setInputLayout(const d3d11::InputLayout *inputLayout) void StateManager11::setInputLayout(const d3d11::InputLayout *inputLayout)
{ {
ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
...@@ -1784,4 +1807,108 @@ gl::Error StateManager11::syncProgram(const gl::Context *context, GLenum drawMod ...@@ -1784,4 +1807,108 @@ gl::Error StateManager11::syncProgram(const gl::Context *context, GLenum drawMod
return gl::NoError(); return gl::NoError();
} }
gl::Error StateManager11::applyVertexBuffer(const gl::Context *context,
GLenum mode,
GLint first,
GLsizei count,
GLsizei instances,
TranslatedIndexData *indexInfo)
{
const auto &state = context->getGLState();
const auto &vertexArray = state.getVertexArray();
auto *vertexArray11 = GetImplAs<VertexArray11>(vertexArray);
ANGLE_TRY(vertexArray11->updateDirtyAndDynamicAttribs(context, &mVertexDataManager, first,
count, instances));
ANGLE_TRY(syncCurrentValueAttribs(state));
// If index information is passed, mark it with the current changed status.
if (indexInfo)
{
indexInfo->srcIndexData.srcIndicesChanged = mAppliedIBChanged;
}
GLsizei numIndicesPerInstance = 0;
if (instances > 0)
{
numIndicesPerInstance = count;
}
const auto &vertexArrayAttribs = vertexArray11->getTranslatedAttribs();
ANGLE_TRY(mInputLayoutCache.applyVertexBuffers(mRenderer, state, vertexArrayAttribs,
mCurrentValueAttribs, mode, first, indexInfo,
numIndicesPerInstance));
// InputLayoutCache::applyVertexBuffers calls through to the Bufer11 to get the native vertex
// buffer (ID3D11Buffer *). Because we allocate these buffers lazily, this will trigger
// allocation. This in turn will signal that the buffer is dirty. Since we just resolved the
// dirty-ness in VertexArray11::updateDirtyAndDynamicAttribs, this can make us do a needless
// update on the second draw call.
// Hence we clear the flags here, after we've applied vertex data, since we know everything
// is clean. This is a bit of a hack.
vertexArray11->clearDirtyAndPromoteDynamicAttribs(state, count);
return gl::NoError();
}
gl::Error StateManager11::applyIndexBuffer(const gl::ContextState &data,
const void *indices,
GLsizei count,
GLenum type,
TranslatedIndexData *indexInfo)
{
const auto &glState = data.getState();
gl::VertexArray *vao = glState.getVertexArray();
gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get();
ANGLE_TRY(mIndexDataManager.prepareIndexData(type, count, elementArrayBuffer, indices,
indexInfo, glState.isPrimitiveRestartEnabled()));
ID3D11Buffer *buffer = nullptr;
DXGI_FORMAT bufferFormat =
(indexInfo->indexType == GL_UNSIGNED_INT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
if (indexInfo->storage)
{
Buffer11 *storage = GetAs<Buffer11>(indexInfo->storage);
ANGLE_TRY_RESULT(storage->getBuffer(BUFFER_USAGE_INDEX), buffer);
}
else
{
IndexBuffer11 *indexBuffer = GetAs<IndexBuffer11>(indexInfo->indexBuffer);
buffer = indexBuffer->getBuffer().get();
}
mAppliedIBChanged = false;
setIndexBuffer(buffer, bufferFormat, indexInfo->startOffset, true);
return gl::NoError();
}
void StateManager11::setIndexBuffer(ID3D11Buffer *buffer,
DXGI_FORMAT indexFormat,
unsigned int offset,
bool indicesChanged)
{
if (buffer != mAppliedIB || indexFormat != mAppliedIBFormat || offset != mAppliedIBOffset)
{
mRenderer->getDeviceContext()->IASetIndexBuffer(buffer, indexFormat, offset);
mAppliedIB = buffer;
mAppliedIBFormat = indexFormat;
mAppliedIBOffset = offset;
if (indicesChanged)
{
mAppliedIBChanged = true;
}
}
}
gl::Error StateManager11::updateVertexOffsetsForPointSpritesEmulation(GLint startVertex,
GLsizei emulatedInstanceId)
{
return mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(mRenderer, startVertex,
emulatedInstanceId);
}
} // namespace rx } // namespace rx
...@@ -11,13 +11,15 @@ ...@@ -11,13 +11,15 @@
#include <array> #include <array>
#include "libANGLE/angletypes.h"
#include "libANGLE/ContextState.h" #include "libANGLE/ContextState.h"
#include "libANGLE/State.h" #include "libANGLE/State.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/d3d/IndexDataManager.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/d3d11/InputLayoutCache.h"
#include "libANGLE/renderer/d3d/d3d11/Query11.h"
#include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
#include "libANGLE/renderer/d3d/d3d11/Query11.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
namespace rx namespace rx
{ {
...@@ -82,7 +84,7 @@ class StateManager11 final : angle::NonCopyable ...@@ -82,7 +84,7 @@ class StateManager11 final : angle::NonCopyable
StateManager11(Renderer11 *renderer); StateManager11(Renderer11 *renderer);
~StateManager11(); ~StateManager11();
void initialize(const gl::Caps &caps); gl::Error initialize(const gl::Caps &caps);
void deinitialize(); void deinitialize();
void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits); void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits);
...@@ -114,6 +116,9 @@ class StateManager11 final : angle::NonCopyable ...@@ -114,6 +116,9 @@ class StateManager11 final : angle::NonCopyable
void invalidateVertexBuffer(); void invalidateVertexBuffer();
void invalidateEverything(const gl::Context *context); void invalidateEverything(const gl::Context *context);
// Called from VertexArray11::updateVertexAttribStorage.
void invalidateCurrentValueAttrib(size_t attribIndex);
void setOneTimeRenderTarget(const gl::Context *context, void setOneTimeRenderTarget(const gl::Context *context,
ID3D11RenderTargetView *rtv, ID3D11RenderTargetView *rtv,
ID3D11DepthStencilView *dsv); ID3D11DepthStencilView *dsv);
...@@ -126,11 +131,6 @@ class StateManager11 final : angle::NonCopyable ...@@ -126,11 +131,6 @@ class StateManager11 final : angle::NonCopyable
void onDeleteQueryObject(Query11 *query); void onDeleteQueryObject(Query11 *query);
gl::Error onMakeCurrent(const gl::Context *context); gl::Error onMakeCurrent(const gl::Context *context);
gl::Error updateCurrentValueAttribs(const gl::State &state,
VertexDataManager *vertexDataManager);
const std::vector<TranslatedAttribute> &getCurrentValueAttribs() const;
void setInputLayout(const d3d11::InputLayout *inputLayout); void setInputLayout(const d3d11::InputLayout *inputLayout);
// TODO(jmadill): Migrate to d3d11::Buffer. // TODO(jmadill): Migrate to d3d11::Buffer.
...@@ -155,6 +155,31 @@ class StateManager11 final : angle::NonCopyable ...@@ -155,6 +155,31 @@ class StateManager11 final : angle::NonCopyable
void setPixelShader(const d3d11::PixelShader *shader); void setPixelShader(const d3d11::PixelShader *shader);
void setComputeShader(const d3d11::ComputeShader *shader); void setComputeShader(const d3d11::ComputeShader *shader);
// Not handled by an internal dirty bit because of the extra draw parameters.
gl::Error applyVertexBuffer(const gl::Context *context,
GLenum mode,
GLint first,
GLsizei count,
GLsizei instances,
TranslatedIndexData *indexInfo);
gl::Error applyIndexBuffer(const gl::ContextState &data,
const void *indices,
GLsizei count,
GLenum type,
TranslatedIndexData *indexInfo);
void setIndexBuffer(ID3D11Buffer *buffer,
DXGI_FORMAT indexFormat,
unsigned int offset,
bool indicesChanged);
gl::Error updateVertexOffsetsForPointSpritesEmulation(GLint startVertex,
GLsizei emulatedInstanceId);
// Only used in testing.
InputLayoutCache *getInputLayoutCache() { return &mInputLayoutCache; }
private: private:
void unsetConflictingSRVs(gl::SamplerType shaderType, void unsetConflictingSRVs(gl::SamplerType shaderType,
uintptr_t resource, uintptr_t resource,
...@@ -200,6 +225,8 @@ class StateManager11 final : angle::NonCopyable ...@@ -200,6 +225,8 @@ class StateManager11 final : angle::NonCopyable
// Faster than calling setTexture a jillion times // Faster than calling setTexture a jillion times
gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd); gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd);
gl::Error syncCurrentValueAttribs(const gl::State &state);
enum DirtyBitType enum DirtyBitType
{ {
DIRTY_BIT_RENDER_TARGET, DIRTY_BIT_RENDER_TARGET,
...@@ -334,6 +361,17 @@ class StateManager11 final : angle::NonCopyable ...@@ -334,6 +361,17 @@ class StateManager11 final : angle::NonCopyable
SamplerMetadata11 mSamplerMetadataVS; SamplerMetadata11 mSamplerMetadataVS;
SamplerMetadata11 mSamplerMetadataPS; SamplerMetadata11 mSamplerMetadataPS;
SamplerMetadata11 mSamplerMetadataCS; SamplerMetadata11 mSamplerMetadataCS;
// Currently applied index buffer
ID3D11Buffer *mAppliedIB;
DXGI_FORMAT mAppliedIBFormat;
unsigned int mAppliedIBOffset;
bool mAppliedIBChanged;
// Vertex, index and input layouts
VertexDataManager mVertexDataManager;
IndexDataManager mIndexDataManager;
InputLayoutCache mInputLayoutCache;
}; };
} // namespace rx } // namespace rx
......
...@@ -121,13 +121,20 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t ...@@ -121,13 +121,20 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t
Buffer11 *oldBuffer11 = oldBufferGL ? GetImplAs<Buffer11>(oldBufferGL) : nullptr; Buffer11 *oldBuffer11 = oldBufferGL ? GetImplAs<Buffer11>(oldBufferGL) : nullptr;
Buffer11 *newBuffer11 = newBufferGL ? GetImplAs<Buffer11>(newBufferGL) : nullptr; Buffer11 *newBuffer11 = newBufferGL ? GetImplAs<Buffer11>(newBufferGL) : nullptr;
StateManager11 *stateManager = GetImplAs<Context11>(context)->getRenderer()->getStateManager();
if (oldBuffer11 != newBuffer11 || oldStorageType != newStorageType) if (oldBuffer11 != newBuffer11 || oldStorageType != newStorageType)
{ {
// Note that for static callbacks, promotion to a static buffer from a dynamic buffer means
// we need to tag dynamic buffers with static callbacks.
OnBufferDataDirtyChannel *newChannel = nullptr; OnBufferDataDirtyChannel *newChannel = nullptr;
if (newBuffer11 != nullptr)
if (newStorageType == VertexStorageType::CURRENT_VALUE)
{
stateManager->invalidateCurrentValueAttrib(attribIndex);
}
else if (newBuffer11 != nullptr)
{ {
// Note that for static callbacks, promotion to a static buffer from a dynamic buffer
// means we need to tag dynamic buffers with static callbacks.
switch (newStorageType) switch (newStorageType)
{ {
case VertexStorageType::DIRECT: case VertexStorageType::DIRECT:
...@@ -138,9 +145,11 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t ...@@ -138,9 +145,11 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t
newChannel = newBuffer11->getStaticBroadcastChannel(); newChannel = newBuffer11->getStaticBroadcastChannel();
break; break;
default: default:
UNREACHABLE();
break; break;
} }
} }
mOnBufferDataDirty[attribIndex].bind(newChannel); mOnBufferDataDirty[attribIndex].bind(newChannel);
mCurrentBuffers[attribIndex].set(context, binding.getBuffer().get()); mCurrentBuffers[attribIndex].set(context, binding.getBuffer().get());
} }
......
...@@ -339,7 +339,7 @@ egl::Error Renderer9::initialize() ...@@ -339,7 +339,7 @@ egl::Error Renderer9::initialize()
mAdapter, mDeviceType, currentDisplayMode.Format, mAdapter, mDeviceType, currentDisplayMode.Format,
D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F)); D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F));
initializeDevice(); ANGLE_TRY(initializeDevice());
return egl::NoError(); return egl::NoError();
} }
...@@ -347,7 +347,7 @@ egl::Error Renderer9::initialize() ...@@ -347,7 +347,7 @@ egl::Error Renderer9::initialize()
// do any one-time device initialization // do any one-time device initialization
// NOTE: this is also needed after a device lost/reset // NOTE: this is also needed after a device lost/reset
// to reset the scene status and ensure the default states are reset. // to reset the scene status and ensure the default states are reset.
void Renderer9::initializeDevice() egl::Error Renderer9::initializeDevice()
{ {
// Permanent non-default states // Permanent non-default states
mDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE); mDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
...@@ -382,9 +382,16 @@ void Renderer9::initializeDevice() ...@@ -382,9 +382,16 @@ void Renderer9::initializeDevice()
mVertexDataManager = new VertexDataManager(this); mVertexDataManager = new VertexDataManager(this);
mIndexDataManager = new IndexDataManager(this, getRendererClass()); mIndexDataManager = new IndexDataManager(this, getRendererClass());
if (mVertexDataManager->initialize().isError())
{
return egl::EglBadAlloc() << "Error initializing VertexDataManager";
}
mTranslatedAttribCache.resize(getNativeCaps().maxVertexAttributes); mTranslatedAttribCache.resize(getNativeCaps().maxVertexAttributes);
mStateManager.initialize(); mStateManager.initialize();
return egl::NoError();
} }
D3DPRESENT_PARAMETERS Renderer9::getDefaultPresentParameters() D3DPRESENT_PARAMETERS Renderer9::getDefaultPresentParameters()
...@@ -2362,7 +2369,10 @@ bool Renderer9::resetDevice() ...@@ -2362,7 +2369,10 @@ bool Renderer9::resetDevice()
if (!removedDevice) if (!removedDevice)
{ {
// reset device defaults // reset device defaults
initializeDevice(); if (initializeDevice().isError())
{
return false;
}
} }
return true; return true;
......
...@@ -456,7 +456,7 @@ class Renderer9 : public RendererD3D ...@@ -456,7 +456,7 @@ class Renderer9 : public RendererD3D
HMODULE mD3d9Module; HMODULE mD3d9Module;
void initializeDevice(); egl::Error initializeDevice();
D3DPRESENT_PARAMETERS getDefaultPresentParameters(); D3DPRESENT_PARAMETERS getDefaultPresentParameters();
void releaseDeviceResources(); void releaseDeviceResources();
......
...@@ -68,7 +68,7 @@ TEST_P(D3D11InputLayoutCacheTest, StressTest) ...@@ -68,7 +68,7 @@ TEST_P(D3D11InputLayoutCacheTest, StressTest)
gl::Context *context = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext()); gl::Context *context = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext());
rx::Context11 *context11 = rx::GetImplAs<rx::Context11>(context); rx::Context11 *context11 = rx::GetImplAs<rx::Context11>(context);
rx::Renderer11 *renderer11 = context11->getRenderer(); rx::Renderer11 *renderer11 = context11->getRenderer();
rx::InputLayoutCache *inputLayoutCache = renderer11->getInputLayoutCache(); rx::InputLayoutCache *inputLayoutCache = renderer11->getStateManager()->getInputLayoutCache();
// Clamp the cache size to something tiny // Clamp the cache size to something tiny
inputLayoutCache->setCacheSize(4); inputLayoutCache->setCacheSize(4);
......
...@@ -860,6 +860,60 @@ TEST_P(VertexAttributeTest, DrawArraysWithDisabledAttribute) ...@@ -860,6 +860,60 @@ TEST_P(VertexAttributeTest, DrawArraysWithDisabledAttribute)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Test based on WebGL Test attribs/gl-disabled-vertex-attrib.html
TEST_P(VertexAttributeTest, DisabledAttribArrays)
{
// Known failure on Retina MBP: http://crbug.com/635081
ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA());
const std::string vsSource =
"attribute vec4 a_position;\n"
"attribute vec4 a_color;\n"
"varying vec4 v_color;\n"
"bool isCorrectColor(vec4 v) {\n"
" return v.x == 0.0 && v.y == 0.0 && v.z == 0.0 && v.w == 1.0;\n"
"}"
"void main() {\n"
" gl_Position = a_position;\n"
" v_color = isCorrectColor(a_color) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n"
"}";
const std::string fsSource =
"varying mediump vec4 v_color;\n"
"void main() {\n"
" gl_FragColor = v_color;\n"
"}";
GLint maxVertexAttribs = 0;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
for (GLint colorIndex = 0; colorIndex < maxVertexAttribs; ++colorIndex)
{
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
ASSERT_NE(0u, vs);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
ASSERT_NE(0u, fs);
GLuint program = glCreateProgram();
glBindAttribLocation(program, colorIndex, "a_color");
glAttachShader(program, vs);
glDeleteShader(vs);
glAttachShader(program, fs);
glDeleteShader(fs);
ASSERT_TRUE(LinkAttachedProgram(program));
drawQuad(program, "a_position", 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
glDeleteProgram(program);
}
}
class VertexAttributeTestES31 : public VertexAttributeTestES3 class VertexAttributeTestES31 : public VertexAttributeTestES3
{ {
protected: protected:
......
...@@ -208,3 +208,9 @@ GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFor ...@@ -208,3 +208,9 @@ GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFor
glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size())); glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
return CheckLinkStatusAndReturnProgram(program, true); return CheckLinkStatusAndReturnProgram(program, true);
} }
bool LinkAttachedProgram(GLuint program)
{
glLinkProgram(program);
return (CheckLinkStatusAndReturnProgram(program, true) != 0);
}
...@@ -32,6 +32,7 @@ ANGLE_EXPORT GLuint CompileProgram(const std::string &vsSource, const std::strin ...@@ -32,6 +32,7 @@ ANGLE_EXPORT GLuint CompileProgram(const std::string &vsSource, const std::strin
ANGLE_EXPORT GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath); ANGLE_EXPORT GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath);
ANGLE_EXPORT GLuint CompileComputeProgram(const std::string &csSource, ANGLE_EXPORT GLuint CompileComputeProgram(const std::string &csSource,
bool outputErrorMessages = true); bool outputErrorMessages = true);
ANGLE_EXPORT bool LinkAttachedProgram(GLuint program);
ANGLE_EXPORT GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat); ANGLE_EXPORT GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat);
ANGLE_EXPORT GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat); ANGLE_EXPORT GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment