Commit 4c19a8a8 by Jamie Madill Committed by Commit Bot

D3D11: Update cached dynamically recompiled programs.

This change makes it so that when we need to recompile a program on a draw call, we also update the cache. It also streamlines the internal queries of the dynamic vertex and fragment shaders such that we only update the input and output signatures a single time per draw. This should also facilitate dirty bit implementations for the D3D11 back- end. BUG=angleproject:2116 Change-Id: Iccb0501b700bc894f40a8c68d7f297ff0c8f46bd Reviewed-on: https://chromium-review.googlesource.com/531798Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent b6664925
......@@ -1837,14 +1837,14 @@ void Context::texParameteriv(GLenum target, GLenum pname, const GLint *params)
void Context::drawArrays(GLenum mode, GLint first, GLsizei count)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawArrays(this, mode, first, count));
MarkTransformFeedbackBufferUsage(mGLState.getCurrentTransformFeedback());
}
void Context::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawArraysInstanced(this, mode, first, count, instanceCount));
MarkTransformFeedbackBufferUsage(mGLState.getCurrentTransformFeedback());
......@@ -1852,7 +1852,7 @@ void Context::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsiz
void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawElements(this, mode, count, type, indices));
}
......@@ -1862,7 +1862,7 @@ void Context::drawElementsInstanced(GLenum mode,
const void *indices,
GLsizei instances)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawElementsInstanced(this, mode, count, type, indices, instances));
}
......@@ -1874,20 +1874,20 @@ void Context::drawRangeElements(GLenum mode,
GLenum type,
const void *indices)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawRangeElements(this, mode, start, end, count, type, indices));
}
void Context::drawArraysIndirect(GLenum mode, const void *indirect)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawArraysIndirect(this, mode, indirect));
}
void Context::drawElementsIndirect(GLenum mode, GLenum type, const void *indirect)
{
ANGLE_CONTEXT_TRY(prepareForDraw());
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawElementsIndirect(this, mode, type, indirect));
}
......@@ -2170,7 +2170,7 @@ GLint Context::getProgramResourceLocation(GLuint program,
return QueryProgramResourceLocation(programObject, programInterface, name);
}
void Context::handleError(const Error &error)
Error Context::handleError(const Error &error)
{
if (error.isError())
{
......@@ -2188,6 +2188,8 @@ void Context::handleError(const Error &error)
GL_DEBUG_SEVERITY_HIGH, error.getMessage());
}
}
return error;
}
// Get one of the recorded errors and clear its flag, if any.
......@@ -2820,10 +2822,18 @@ void Context::initWorkarounds()
mWorkarounds.loseContextOnOutOfMemory = (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT);
}
Error Context::prepareForDraw()
Error Context::prepareForDraw(GLenum drawMode)
{
syncRendererState();
return NoError();
InfoLog infoLog;
Error err = mImplementation->triggerDrawCallProgramRecompilation(this, &infoLog,
mMemoryProgramCache, drawMode);
if (err.isError())
{
WARN() << "Dynamic recompilation error log: " << infoLog.str();
}
return err;
}
void Context::syncRendererState()
......
......@@ -779,7 +779,8 @@ class Context final : public ValidationContext
void *binary);
void programBinary(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);
void handleError(const Error &error) override;
// Returns the error.
Error handleError(const Error &error) override;
GLenum getError();
void markContextLost();
......@@ -832,7 +833,7 @@ class Context final : public ValidationContext
egl::Surface *getCurrentReadSurface() const { return mCurrentSurface; }
private:
Error prepareForDraw();
Error prepareForDraw(GLenum drawMode);
void syncRendererState();
void syncRendererState(const State::DirtyBits &bitMask, const State::DirtyObjects &objectMask);
void syncStateForReadPixels();
......
......@@ -100,7 +100,7 @@ class ValidationContext : angle::NonCopyable
bool skipValidation);
virtual ~ValidationContext() {}
virtual void handleError(const Error &error) = 0;
virtual Error handleError(const Error &error) = 0;
const ContextState &getContextState() const { return mState; }
GLint getClientMajorVersion() const { return mState.getClientMajorVersion(); }
......
......@@ -585,6 +585,13 @@ void MemoryProgramCache::putProgram(const ProgramHash &programHash,
put(programHash, std::move(binaryProgram));
}
void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
{
gl::ProgramHash programHash;
ComputeHash(context, program, &programHash);
putProgram(programHash, context, program);
}
void MemoryProgramCache::putBinary(const ProgramHash &programHash,
const uint8_t *binary,
size_t length)
......
......@@ -82,6 +82,9 @@ class MemoryProgramCache final : angle::NonCopyable
// Helper method that serializes a program.
void putProgram(const ProgramHash &programHash, const Context *context, const Program *program);
// Same as putProgram but computes the hash.
void updateProgram(const Context *context, const Program *program);
// Store a binary directly.
void putBinary(const ProgramHash &programHash, const uint8_t *binary, size_t length);
......
......@@ -18,6 +18,7 @@
namespace gl
{
class MemoryProgramCache;
class Path;
struct Workarounds;
}
......@@ -155,6 +156,18 @@ class ContextImpl : public GLImplFactory
GLuint numGroupsY,
GLuint numGroupsZ) = 0;
// This does not correspond to a GL API, but matches a common GL driver behaviour where
// draw call states can trigger dynamic shader recompilation. We pass the Program cache
// handle as a mutable pointer to this Impl method to both trigger dynamic recompilations
// and to allow the back-end to store the refreshed shaders in the cache.
virtual gl::Error triggerDrawCallProgramRecompilation(const gl::Context *context,
gl::InfoLog *infoLog,
gl::MemoryProgramCache *memoryCache,
GLenum drawMode)
{
return gl::NoError();
}
const gl::ContextState &getContextState() { return mState; }
int getClientMajorVersion() const { return mState.getClientMajorVersion(); }
int getClientMinorVersion() const { return mState.getClientMinorVersion(); }
......
......@@ -165,19 +165,14 @@ class ProgramD3D : public ProgramImpl
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
gl::Error getPixelExecutableForFramebuffer(const gl::Context *context,
const gl::Framebuffer *fbo,
ShaderExecutableD3D **outExectuable);
gl::Error getPixelExecutableForOutputLayout(const std::vector<GLenum> &outputLayout,
ShaderExecutableD3D **outExectuable,
gl::InfoLog *infoLog);
gl::Error getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout,
ShaderExecutableD3D **outExectuable,
gl::InfoLog *infoLog);
gl::Error getVertexExecutableForCachedInputLayout(ShaderExecutableD3D **outExectuable,
gl::InfoLog *infoLog);
gl::Error getGeometryExecutableForPrimitiveType(const gl::ContextState &data,
GLenum drawMode,
ShaderExecutableD3D **outExecutable,
gl::InfoLog *infoLog);
gl::Error getPixelExecutableForCachedOutputLayout(ShaderExecutableD3D **outExectuable,
gl::InfoLog *infoLog);
gl::Error getComputeExecutable(ShaderExecutableD3D **outExecutable);
gl::LinkResult link(const gl::Context *context,
const gl::VaryingPacking &packing,
......@@ -261,10 +256,15 @@ class ProgramD3D : public ProgramImpl
}
void updateCachedInputLayout(Serial associatedSerial, const gl::State &state);
const gl::InputLayout &getCachedInputLayout() const { return mCachedInputLayout; }
void updateCachedOutputLayout(const gl::Context *context, const gl::Framebuffer *framebuffer);
bool isSamplerMappingDirty() { return mDirtySamplerMapping; }
// Checks if we need to recompile certain shaders.
bool hasVertexExecutableForCachedInputLayout();
bool hasGeometryExecutableForPrimitiveType(GLenum drawMode);
bool hasPixelExecutableForCachedOutputLayout();
private:
// These forward-declared tasks are used for multi-thread shader compiles.
class GetExecutableTask;
......@@ -381,6 +381,9 @@ class ProgramD3D : public ProgramImpl
void initUniformBlockInfo(const gl::Context *context, gl::Shader *shader);
size_t getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock);
void updateCachedInputLayoutFromShader(const gl::Context *context);
void updateCachedOutputLayoutFromShader();
RendererD3D *mRenderer;
DynamicHLSL *mDynamicHLSL;
......@@ -417,8 +420,8 @@ class ProgramD3D : public ProgramImpl
GLuint mUsedComputeSamplerRange;
bool mDirtySamplerMapping;
// Cache for getPixelExecutableForFramebuffer
std::vector<GLenum> mPixelShaderOutputFormatCache;
// Cache for pixel shader output layout to save reallocations.
std::vector<GLenum> mPixelShaderOutputLayoutCache;
AttribIndexArray mAttribLocationToD3DSemantic;
......@@ -441,6 +444,6 @@ class ProgramD3D : public ProgramImpl
Serial mCurrentVertexArrayStateSerial;
};
}
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_PROGRAMD3D_H_
......@@ -10,6 +10,8 @@
#include "libANGLE/renderer/d3d/d3d11/Context11.h"
#include "common/string_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/MemoryProgramCache.h"
#include "libANGLE/renderer/d3d/CompilerD3D.h"
#include "libANGLE/renderer/d3d/ProgramD3D.h"
#include "libANGLE/renderer/d3d/RenderbufferD3D.h"
......@@ -294,4 +296,58 @@ gl::Error Context11::dispatchCompute(const gl::Context *context,
return mRenderer->dispatchCompute(context, numGroupsX, numGroupsY, numGroupsZ);
}
gl::Error Context11::triggerDrawCallProgramRecompilation(const gl::Context *context,
gl::InfoLog *infoLog,
gl::MemoryProgramCache *memoryCache,
GLenum drawMode)
{
const auto &glState = context->getGLState();
const auto *va11 = GetImplAs<VertexArray11>(glState.getVertexArray());
const auto *drawFBO = glState.getDrawFramebuffer();
gl::Program *program = glState.getProgram();
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
programD3D->updateCachedInputLayout(va11->getCurrentStateSerial(), glState);
programD3D->updateCachedOutputLayout(context, drawFBO);
bool recompileVS = !programD3D->hasVertexExecutableForCachedInputLayout();
bool recompileGS = !programD3D->hasGeometryExecutableForPrimitiveType(drawMode);
bool recompilePS = !programD3D->hasPixelExecutableForCachedOutputLayout();
if (!recompileVS && !recompileGS && !recompilePS)
{
return gl::NoError();
}
// Load the compiler if necessary and recompile the programs.
ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized());
if (recompileVS)
{
ShaderExecutableD3D *vertexExe = nullptr;
ANGLE_TRY(programD3D->getVertexExecutableForCachedInputLayout(&vertexExe, infoLog));
}
if (recompileGS)
{
ShaderExecutableD3D *geometryExe = nullptr;
ANGLE_TRY(programD3D->getGeometryExecutableForPrimitiveType(
context->getContextState(), drawMode, &geometryExe, infoLog));
}
if (recompilePS)
{
ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(&pixelExe, infoLog));
}
// Refresh the program cache entry.
if (memoryCache)
{
memoryCache->updateProgram(context, program);
}
return gl::NoError();
}
} // namespace rx
......@@ -135,6 +135,11 @@ class Context11 : public ContextImpl
GLuint numGroupsY,
GLuint numGroupsZ) override;
gl::Error triggerDrawCallProgramRecompilation(const gl::Context *context,
gl::InfoLog *infoLog,
gl::MemoryProgramCache *memoryCache,
GLenum drawMode) override;
private:
Renderer11 *mRenderer;
};
......
......@@ -35,19 +35,6 @@ size_t GetReservedBufferCount(bool usesPointSpriteEmulation)
return usesPointSpriteEmulation ? 1 : 0;
}
gl::InputLayout GetInputLayout(const std::vector<const TranslatedAttribute *> &translatedAttributes)
{
gl::InputLayout inputLayout(translatedAttributes.size(), gl::VERTEX_FORMAT_INVALID);
for (size_t attributeIndex = 0; attributeIndex < translatedAttributes.size(); ++attributeIndex)
{
const TranslatedAttribute *translatedAttribute = translatedAttributes[attributeIndex];
inputLayout[attributeIndex] = gl::GetVertexFormatType(
*translatedAttribute->attribute, translatedAttribute->currentValueType);
}
return inputLayout;
}
GLenum GetGLSLAttributeType(const std::vector<sh::Attribute> &shaderAttributes, size_t index)
{
// Count matrices differently
......@@ -547,10 +534,8 @@ gl::Error InputLayoutCache::createInputLayout(Renderer11 *renderer,
inputElementCount++;
}
const gl::InputLayout &shaderInputLayout = GetInputLayout(mCurrentAttributes);
ShaderExecutableD3D *shader = nullptr;
ANGLE_TRY(programD3D->getVertexExecutableForInputLayout(shaderInputLayout, &shader, nullptr));
ANGLE_TRY(programD3D->getVertexExecutableForCachedInputLayout(&shader, nullptr));
ShaderExecutableD3D *shader11 = GetAs<ShaderExecutable11>(shader);
......
......@@ -1709,8 +1709,7 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
}
rx::ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForFramebuffer(
context, glState.getDrawFramebuffer(), &pixelExe));
ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(&pixelExe, nullptr));
// Skip the draw call if rasterizer discard is enabled (or no fragment shader).
if (!pixelExe || glState.getRasterizerState().rasterizerDiscard)
......
......@@ -1737,23 +1737,22 @@ gl::Error StateManager11::setTexture(const gl::Context *context,
gl::Error StateManager11::syncProgram(const gl::Context *context, GLenum drawMode)
{
// This method is called single-threaded.
ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized());
const auto &glState = context->getGLState();
const auto *va11 = GetImplAs<VertexArray11>(glState.getVertexArray());
const auto *va11 = GetImplAs<VertexArray11>(glState.getVertexArray());
auto *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
programD3D->updateCachedInputLayout(va11->getCurrentStateSerial(), glState);
const auto &inputLayout = programD3D->getCachedInputLayout();
// Binaries must be compiled before the sync.
ASSERT(programD3D->hasVertexExecutableForCachedInputLayout());
ASSERT(programD3D->hasGeometryExecutableForPrimitiveType(drawMode));
ASSERT(programD3D->hasPixelExecutableForCachedOutputLayout());
ShaderExecutableD3D *vertexExe = nullptr;
ANGLE_TRY(programD3D->getVertexExecutableForInputLayout(inputLayout, &vertexExe, nullptr));
ANGLE_TRY(programD3D->getVertexExecutableForCachedInputLayout(&vertexExe, nullptr));
const gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer();
ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForFramebuffer(context, drawFramebuffer, &pixelExe));
ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(&pixelExe, nullptr));
ShaderExecutableD3D *geometryExe = nullptr;
ANGLE_TRY(programD3D->getGeometryExecutableForPrimitiveType(context->getContextState(),
......
......@@ -1796,14 +1796,14 @@ gl::Error Renderer9::applyShaders(const gl::Context *context, GLenum drawMode)
VertexArray9 *vao = GetImplAs<VertexArray9>(state.getVertexArray());
programD3D->updateCachedInputLayout(vao->getCurrentStateSerial(), state);
const auto &inputLayout = programD3D->getCachedInputLayout();
ShaderExecutableD3D *vertexExe = nullptr;
ANGLE_TRY(programD3D->getVertexExecutableForInputLayout(inputLayout, &vertexExe, nullptr));
ANGLE_TRY(programD3D->getVertexExecutableForCachedInputLayout(&vertexExe, nullptr));
const gl::Framebuffer *drawFramebuffer = state.getDrawFramebuffer();
ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForFramebuffer(context, drawFramebuffer, &pixelExe));
programD3D->updateCachedOutputLayout(context, drawFramebuffer);
ShaderExecutableD3D *pixelExe = nullptr;
ANGLE_TRY(programD3D->getPixelExecutableForCachedOutputLayout(&pixelExe, nullptr));
IDirect3DVertexShader9 *vertexShader =
(vertexExe ? GetAs<ShaderExecutable9>(vertexExe)->getVertexShader() : nullptr);
......
......@@ -52,7 +52,7 @@ class MockValidationContext : public ValidationContext
{
}
MOCK_METHOD1(handleError, void(const Error &));
MOCK_METHOD1(handleError, Error(const Error &));
};
// Test that ANGLE generates an INVALID_OPERATION when validating index data that uses a value
......
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