Commit a7d12dc7 by Jamie Madill Committed by Commit Bot

Store uniform block bindings in program binaries.

This affects all back-ends - we weren't saving this. Note that bindings can only be set after program linking. The spec is fairly clear in that any programs saved must be loadable and runnable under the same set of state, which would include block bindings. Also add validation for zero binary formats in GetProgramBinary. Also add a workaround for AMD where the block bindings were not applied properly after link, similarly to our original bug. This CL also includes a few fixups for GLProgram (raii). BUG=angleproject:1637 Change-Id: Iae068eb4e1e4c763aa9f9332c033e38708026c8f Reviewed-on: https://chromium-review.googlesource.com/418393Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent e1faacb1
...@@ -818,6 +818,14 @@ Error Program::loadBinary(const Context *context, ...@@ -818,6 +818,14 @@ Error Program::loadBinary(const Context *context,
mState.mUniformBlocks.push_back(uniformBlock); mState.mUniformBlocks.push_back(uniformBlock);
} }
for (GLuint bindingIndex = 0; bindingIndex < mState.mUniformBlockBindings.size();
++bindingIndex)
{
stream.readInt(&mState.mUniformBlockBindings[bindingIndex]);
mState.mActiveUniformBlockBindings.set(bindingIndex,
mState.mUniformBlockBindings[bindingIndex] != 0);
}
unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>(); unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
ASSERT(mState.mTransformFeedbackVaryingVars.empty()); ASSERT(mState.mTransformFeedbackVaryingVars.empty());
for (unsigned int transformFeedbackVaryingIndex = 0; for (unsigned int transformFeedbackVaryingIndex = 0;
...@@ -848,7 +856,7 @@ Error Program::loadBinary(const Context *context, ...@@ -848,7 +856,7 @@ Error Program::loadBinary(const Context *context,
stream.readInt(&mSamplerUniformRange.start); stream.readInt(&mSamplerUniformRange.start);
stream.readInt(&mSamplerUniformRange.end); stream.readInt(&mSamplerUniformRange.end);
ANGLE_TRY_RESULT(mProgram->load(mInfoLog, &stream), mLinked); ANGLE_TRY_RESULT(mProgram->load(context->getImplementation(), mInfoLog, &stream), mLinked);
return NoError(); return NoError();
#endif // #if ANGLE_PROGRAM_BINARY_LOAD == ANGLE_ENABLED #endif // #if ANGLE_PROGRAM_BINARY_LOAD == ANGLE_ENABLED
...@@ -936,6 +944,11 @@ Error Program::saveBinary(const Context *context, ...@@ -936,6 +944,11 @@ Error Program::saveBinary(const Context *context,
} }
} }
for (GLuint binding : mState.mUniformBlockBindings)
{
stream.writeInt(binding);
}
stream.writeInt(mState.mTransformFeedbackVaryingVars.size()); stream.writeInt(mState.mTransformFeedbackVaryingVars.size());
for (const sh::Varying &varying : mState.mTransformFeedbackVaryingVars) for (const sh::Varying &varying : mState.mTransformFeedbackVaryingVars)
{ {
...@@ -1685,6 +1698,7 @@ const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const ...@@ -1685,6 +1698,7 @@ const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const
void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding) void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
{ {
mState.mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding; mState.mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding;
mState.mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlockBinding != 0);
mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding); mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <GLSLANG/ShaderVars.h> #include <GLSLANG/ShaderVars.h>
#include <array>
#include <map> #include <map>
#include <set> #include <set>
#include <sstream> #include <sstream>
...@@ -211,7 +212,7 @@ class ProgramState final : angle::NonCopyable ...@@ -211,7 +212,7 @@ class ProgramState final : angle::NonCopyable
std::vector<sh::Varying> mTransformFeedbackVaryingVars; std::vector<sh::Varying> mTransformFeedbackVaryingVars;
GLenum mTransformFeedbackBufferMode; GLenum mTransformFeedbackBufferMode;
GLuint mUniformBlockBindings[IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS]; std::array<GLuint, IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS> mUniformBlockBindings;
UniformBlockBindingMask mActiveUniformBlockBindings; UniformBlockBindingMask mActiveUniformBlockBindings;
std::vector<sh::Attribute> mAttributes; std::vector<sh::Attribute> mAttributes;
......
...@@ -26,13 +26,17 @@ namespace rx ...@@ -26,13 +26,17 @@ namespace rx
{ {
using LinkResult = gl::ErrorOrResult<bool>; using LinkResult = gl::ErrorOrResult<bool>;
class ContextImpl;
class ProgramImpl : angle::NonCopyable class ProgramImpl : angle::NonCopyable
{ {
public: public:
ProgramImpl(const gl::ProgramState &state) : mState(state) {} ProgramImpl(const gl::ProgramState &state) : mState(state) {}
virtual ~ProgramImpl() {} virtual ~ProgramImpl() {}
virtual LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) = 0; virtual LinkResult load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream) = 0;
virtual gl::Error save(gl::BinaryOutputStream *stream) = 0; virtual gl::Error save(gl::BinaryOutputStream *stream) = 0;
virtual void setBinaryRetrievableHint(bool retrievable) = 0; virtual void setBinaryRetrievableHint(bool retrievable) = 0;
......
...@@ -23,7 +23,7 @@ class MockProgramImpl : public rx::ProgramImpl ...@@ -23,7 +23,7 @@ class MockProgramImpl : public rx::ProgramImpl
MockProgramImpl() : ProgramImpl(gl::ProgramState()) {} MockProgramImpl() : ProgramImpl(gl::ProgramState()) {}
virtual ~MockProgramImpl() { destroy(); } virtual ~MockProgramImpl() { destroy(); }
MOCK_METHOD2(load, LinkResult(gl::InfoLog &, gl::BinaryInputStream *)); MOCK_METHOD3(load, LinkResult(const ContextImpl *, gl::InfoLog &, gl::BinaryInputStream *));
MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *)); MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *));
MOCK_METHOD1(setBinaryRetrievableHint, void(bool)); MOCK_METHOD1(setBinaryRetrievableHint, void(bool));
......
...@@ -783,8 +783,12 @@ void ProgramD3D::updateSamplerMapping() ...@@ -783,8 +783,12 @@ void ProgramD3D::updateSamplerMapping()
} }
} }
LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) LinkResult ProgramD3D::load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream)
{ {
// TODO(jmadill): Use Renderer from contextImpl.
reset(); reset();
DeviceIdentifier binaryDeviceIdentifier = {0}; DeviceIdentifier binaryDeviceIdentifier = {0};
......
...@@ -151,7 +151,9 @@ class ProgramD3D : public ProgramImpl ...@@ -151,7 +151,9 @@ class ProgramD3D : public ProgramImpl
bool usesGeometryShader(GLenum drawMode) const; bool usesGeometryShader(GLenum drawMode) const;
bool usesInstancedPointSpriteEmulation() const; bool usesInstancedPointSpriteEmulation() const;
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; LinkResult load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream) override;
gl::Error save(gl::BinaryOutputStream *stream) override; gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
......
...@@ -369,7 +369,7 @@ StateManagerGL *ContextGL::getStateManager() ...@@ -369,7 +369,7 @@ StateManagerGL *ContextGL::getStateManager()
return mRenderer->getStateManager(); return mRenderer->getStateManager();
} }
const WorkaroundsGL &ContextGL::getWorkaroundsGL() const WorkaroundsGL &ContextGL::getWorkaroundsGL() const
{ {
return mRenderer->getWorkarounds(); return mRenderer->getWorkarounds();
} }
......
...@@ -172,7 +172,7 @@ class ContextGL : public ContextImpl ...@@ -172,7 +172,7 @@ class ContextGL : public ContextImpl
// Handle helpers // Handle helpers
const FunctionsGL *getFunctions() const; const FunctionsGL *getFunctions() const;
StateManagerGL *getStateManager(); StateManagerGL *getStateManager();
const WorkaroundsGL &getWorkaroundsGL(); const WorkaroundsGL &getWorkaroundsGL() const;
private: private:
RendererGL *mRenderer; RendererGL *mRenderer;
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
#include "libANGLE/renderer/gl/ProgramGL.h" #include "libANGLE/renderer/gl/ProgramGL.h"
#include "common/BitSetIterator.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/string_utils.h" #include "common/string_utils.h"
#include "common/utilities.h" #include "common/utilities.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/ShaderGL.h" #include "libANGLE/renderer/gl/ShaderGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h"
...@@ -46,7 +48,9 @@ ProgramGL::~ProgramGL() ...@@ -46,7 +48,9 @@ ProgramGL::~ProgramGL()
mProgramID = 0; mProgramID = 0;
} }
LinkResult ProgramGL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) LinkResult ProgramGL::load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream)
{ {
preLink(); preLink();
...@@ -67,6 +71,16 @@ LinkResult ProgramGL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) ...@@ -67,6 +71,16 @@ LinkResult ProgramGL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
postLink(); postLink();
// Re-apply UBO bindings to work around driver bugs.
const WorkaroundsGL &workaroundsGL = GetAs<ContextGL>(contextImpl)->getWorkaroundsGL();
if (workaroundsGL.reapplyUBOBindingsAfterLoadingBinaryProgram)
{
for (GLuint bindingIndex : angle::IterateBitSet(mState.getActiveUniformBlockBindingsMask()))
{
setUniformBlockBinding(bindingIndex, mState.getUniformBlockBinding(bindingIndex));
}
}
return true; return true;
} }
......
...@@ -37,7 +37,9 @@ class ProgramGL : public ProgramImpl ...@@ -37,7 +37,9 @@ class ProgramGL : public ProgramImpl
bool enablePathRendering); bool enablePathRendering);
~ProgramGL() override; ~ProgramGL() override;
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; LinkResult load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream) override;
gl::Error save(gl::BinaryOutputStream *stream) override; gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
......
...@@ -14,25 +14,6 @@ namespace rx ...@@ -14,25 +14,6 @@ namespace rx
struct WorkaroundsGL struct WorkaroundsGL
{ {
WorkaroundsGL()
: avoid1BitAlphaTextureFormats(false),
rgba4IsNotSupportedForColorRendering(false),
doesSRGBClearsOnLinearFramebufferAttachments(false),
doWhileGLSLCausesGPUHang(false),
finishDoesNotCauseQueriesToBeAvailable(false),
alwaysCallUseProgramAfterLink(false),
unpackOverlappingRowsSeparatelyUnpackBuffer(false),
emulateAbsIntFunction(false),
addAndTrueToLoopCondition(false),
emulateIsnanFloat(false),
useUnusedBlocksWithStandardOrSharedLayout(false),
dontRemoveInvariantForFragmentInput(false),
removeInvariantAndCentroidForESSL3(false),
rewriteFloatUnaryMinusOperator(false),
emulateAtan2Float(false)
{
}
// When writing a float to a normalized integer framebuffer, desktop OpenGL is allowed to write // When writing a float to a normalized integer framebuffer, desktop OpenGL is allowed to write
// one of the two closest normalized integer representations (although round to nearest is // one of the two closest normalized integer representations (although round to nearest is
// preferred) (see section 2.3.5.2 of the GL 4.5 core specification). OpenGL ES requires that // preferred) (see section 2.3.5.2 of the GL 4.5 core specification). OpenGL ES requires that
...@@ -40,18 +21,18 @@ struct WorkaroundsGL ...@@ -40,18 +21,18 @@ struct WorkaroundsGL
// section 2.1.2 of the OpenGL ES 2.0.25 spec). This issue only shows up on Intel and AMD // section 2.1.2 of the OpenGL ES 2.0.25 spec). This issue only shows up on Intel and AMD
// drivers on framebuffer formats that have 1-bit alpha, work around this by using higher // drivers on framebuffer formats that have 1-bit alpha, work around this by using higher
// precision formats instead. // precision formats instead.
bool avoid1BitAlphaTextureFormats; bool avoid1BitAlphaTextureFormats = false;
// On some older Intel drivers, GL_RGBA4 is not color renderable, glCheckFramebufferStatus // On some older Intel drivers, GL_RGBA4 is not color renderable, glCheckFramebufferStatus
// returns GL_FRAMEBUFFER_UNSUPPORTED. Work around this by using a known color-renderable // returns GL_FRAMEBUFFER_UNSUPPORTED. Work around this by using a known color-renderable
// format. // format.
bool rgba4IsNotSupportedForColorRendering; bool rgba4IsNotSupportedForColorRendering = false;
// When clearing a framebuffer on Intel or AMD drivers, when GL_FRAMEBUFFER_SRGB is enabled, the // When clearing a framebuffer on Intel or AMD drivers, when GL_FRAMEBUFFER_SRGB is enabled, the
// driver clears to the linearized clear color despite the framebuffer not supporting SRGB // driver clears to the linearized clear color despite the framebuffer not supporting SRGB
// blending. It only seems to do this when the framebuffer has only linear attachments, mixed // blending. It only seems to do this when the framebuffer has only linear attachments, mixed
// attachments appear to get the correct clear color. // attachments appear to get the correct clear color.
bool doesSRGBClearsOnLinearFramebufferAttachments; bool doesSRGBClearsOnLinearFramebufferAttachments = false;
// On Mac some GLSL constructs involving do-while loops cause GPU hangs, such as the following: // On Mac some GLSL constructs involving do-while loops cause GPU hangs, such as the following:
// int i = 1; // int i = 1;
...@@ -60,34 +41,34 @@ struct WorkaroundsGL ...@@ -60,34 +41,34 @@ struct WorkaroundsGL
// continue; // continue;
// } while (i > 0) // } while (i > 0)
// Work around this by rewriting the do-while to use another GLSL construct (block + while) // Work around this by rewriting the do-while to use another GLSL construct (block + while)
bool doWhileGLSLCausesGPUHang; bool doWhileGLSLCausesGPUHang = false;
// Calling glFinish doesn't cause all queries to report that the result is available on some // Calling glFinish doesn't cause all queries to report that the result is available on some
// (NVIDIA) drivers. It was found that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS before the finish // (NVIDIA) drivers. It was found that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS before the finish
// causes it to fully finish. // causes it to fully finish.
bool finishDoesNotCauseQueriesToBeAvailable; bool finishDoesNotCauseQueriesToBeAvailable = false;
// Always call useProgram after a successful link to avoid a driver bug. // Always call useProgram after a successful link to avoid a driver bug.
// This workaround is meant to reproduce the use_current_program_after_successful_link // This workaround is meant to reproduce the use_current_program_after_successful_link
// workaround in Chromium (http://crbug.com/110263). It has been shown that this workaround is // workaround in Chromium (http://crbug.com/110263). It has been shown that this workaround is
// not necessary for MacOSX 10.9 and higher (http://crrev.com/39eb535b). // not necessary for MacOSX 10.9 and higher (http://crrev.com/39eb535b).
bool alwaysCallUseProgramAfterLink; bool alwaysCallUseProgramAfterLink = false;
// In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row. // In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row.
bool unpackOverlappingRowsSeparatelyUnpackBuffer; bool unpackOverlappingRowsSeparatelyUnpackBuffer = false;
// In the case of packing to a pixel pack buffer, pack overlapping rows row by row. // In the case of packing to a pixel pack buffer, pack overlapping rows row by row.
bool packOverlappingRowsSeparatelyPackBuffer; bool packOverlappingRowsSeparatelyPackBuffer = false;
// During initialization, assign the current vertex attributes to the spec-mandated defaults. // During initialization, assign the current vertex attributes to the spec-mandated defaults.
bool initializeCurrentVertexAttributes; bool initializeCurrentVertexAttributes = false;
// abs(i) where i is an integer returns unexpected result on Intel Mac. // abs(i) where i is an integer returns unexpected result on Intel Mac.
// Emulate abs(i) with i * sign(i). // Emulate abs(i) with i * sign(i).
bool emulateAbsIntFunction; bool emulateAbsIntFunction = false;
// On Intel Mac, calculation of loop conditions in for and while loop has bug. // On Intel Mac, calculation of loop conditions in for and while loop has bug.
// Add "&& true" to the end of the condition expression to work around the bug. // Add "&& true" to the end of the condition expression to work around the bug.
bool addAndTrueToLoopCondition; bool addAndTrueToLoopCondition = false;
// When uploading textures from an unpack buffer, some drivers count an extra row padding when // When uploading textures from an unpack buffer, some drivers count an extra row padding when
// checking if the pixel unpack buffer is big enough. Tracking bug: http://anglebug.com/1512 // checking if the pixel unpack buffer is big enough. Tracking bug: http://anglebug.com/1512
...@@ -101,37 +82,43 @@ struct WorkaroundsGL ...@@ -101,37 +82,43 @@ struct WorkaroundsGL
// +-------A--B // +-------A--B
// The last pixel read will be A, but the driver will think it is B, causing it to generate an // The last pixel read will be A, but the driver will think it is B, causing it to generate an
// error when the pixel buffer is just big enough. // error when the pixel buffer is just big enough.
bool unpackLastRowSeparatelyForPaddingInclusion; bool unpackLastRowSeparatelyForPaddingInclusion = false;
// Equivalent workaround when uploading data from a pixel pack buffer. // Equivalent workaround when uploading data from a pixel pack buffer.
bool packLastRowSeparatelyForPaddingInclusion; bool packLastRowSeparatelyForPaddingInclusion = false;
// On some Intel drivers, using isnan() on highp float will get wrong answer. To work around // On some Intel drivers, using isnan() on highp float will get wrong answer. To work around
// this bug, we use an expression to emulate function isnan(). // this bug, we use an expression to emulate function isnan().
// Tracking bug: http://crbug.com/650547 // Tracking bug: http://crbug.com/650547
bool emulateIsnanFloat; bool emulateIsnanFloat = false;
// On Mac with OpenGL version 4.1, unused std140 or shared uniform blocks will be // On Mac with OpenGL version 4.1, unused std140 or shared uniform blocks will be
// treated as inactive which is not consistent with WebGL2.0 spec. Reference all members in a // treated as inactive which is not consistent with WebGL2.0 spec. Reference all members in a
// unused std140 or shared uniform block at the beginning of main to work around it. // unused std140 or shared uniform block at the beginning of main to work around it.
bool useUnusedBlocksWithStandardOrSharedLayout; bool useUnusedBlocksWithStandardOrSharedLayout = false;
// This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20 // This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20
// on AMD. // on AMD.
bool dontRemoveInvariantForFragmentInput; bool dontRemoveInvariantForFragmentInput = false;
// This flag is used to fix spec difference between GLSL 4.1 or lower and ESSL3. // This flag is used to fix spec difference between GLSL 4.1 or lower and ESSL3.
bool removeInvariantAndCentroidForESSL3; bool removeInvariantAndCentroidForESSL3 = false;
// On Intel Mac OSX 10.11 driver, using "-float" will get wrong answer. Use "0.0 - float" to // On Intel Mac OSX 10.11 driver, using "-float" will get wrong answer. Use "0.0 - float" to
// replace "-float". // replace "-float".
// Tracking bug: http://crbug.com/308366 // Tracking bug: http://crbug.com/308366
bool rewriteFloatUnaryMinusOperator; bool rewriteFloatUnaryMinusOperator = false;
// On NVIDIA drivers, atan(y, x) may return a wrong answer. // On NVIDIA drivers, atan(y, x) may return a wrong answer.
// Tracking bug: http://crbug.com/672380 // Tracking bug: http://crbug.com/672380
bool emulateAtan2Float; bool emulateAtan2Float = false;
// Some drivers seem to forget about UBO bindings when loading program binaries. Work around
// this by re-applying the bindings after the program binary is loaded.
// This only seems to affect AMD OpenGL drivers.
// http://anglebug.com/1660
bool reapplyUBOBindingsAfterLoadingBinaryProgram = false;
}; };
} } // namespace rx
#endif // LIBANGLE_RENDERER_GL_WORKAROUNDSGL_H_ #endif // LIBANGLE_RENDERER_GL_WORKAROUNDSGL_H_
...@@ -957,10 +957,12 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround ...@@ -957,10 +957,12 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
// TODO(oetuaho): Make this specific to the affected driver versions. Versions that came after // TODO(oetuaho): Make this specific to the affected driver versions. Versions that came after
// 364 are known to be affected, at least up to 375. // 364 are known to be affected, at least up to 375.
workarounds->emulateAtan2Float = IsNvidia(vendor); workarounds->emulateAtan2Float = IsNvidia(vendor);
}
workarounds->reapplyUBOBindingsAfterLoadingBinaryProgram = IsAMD(vendor);
} }
} // namespace nativegl_gl
namespace nativegl namespace nativegl
{ {
bool SupportsFenceSync(const FunctionsGL *functions) bool SupportsFenceSync(const FunctionsGL *functions)
......
...@@ -22,7 +22,9 @@ ProgramNULL::~ProgramNULL() ...@@ -22,7 +22,9 @@ ProgramNULL::~ProgramNULL()
{ {
} }
LinkResult ProgramNULL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) LinkResult ProgramNULL::load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream)
{ {
return true; return true;
} }
......
...@@ -21,7 +21,9 @@ class ProgramNULL : public ProgramImpl ...@@ -21,7 +21,9 @@ class ProgramNULL : public ProgramImpl
ProgramNULL(const gl::ProgramState &state); ProgramNULL(const gl::ProgramState &state);
~ProgramNULL() override; ~ProgramNULL() override;
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; LinkResult load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream) override;
gl::Error save(gl::BinaryOutputStream *stream) override; gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
......
...@@ -22,7 +22,9 @@ ProgramVk::~ProgramVk() ...@@ -22,7 +22,9 @@ ProgramVk::~ProgramVk()
{ {
} }
LinkResult ProgramVk::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) LinkResult ProgramVk::load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream)
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
......
...@@ -21,7 +21,9 @@ class ProgramVk : public ProgramImpl ...@@ -21,7 +21,9 @@ class ProgramVk : public ProgramImpl
ProgramVk(const gl::ProgramState &state); ProgramVk(const gl::ProgramState &state);
~ProgramVk() override; ~ProgramVk() override;
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override; LinkResult load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream) override;
gl::Error save(gl::BinaryOutputStream *stream) override; gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
......
...@@ -4003,6 +4003,12 @@ bool ValidateGetProgramBinaryBase(Context *context, ...@@ -4003,6 +4003,12 @@ bool ValidateGetProgramBinaryBase(Context *context,
return false; return false;
} }
if (context->getCaps().programBinaryFormats.empty())
{
context->handleError(Error(GL_INVALID_OPERATION, "No program binary formats supported."));
return false;
}
return true; return true;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "EGLWindow.h" #include "EGLWindow.h"
#include "OSWindow.h" #include "OSWindow.h"
#include "test_utils/angle_test_configs.h" #include "test_utils/angle_test_configs.h"
#include "test_utils/gl_raii.h"
using namespace angle; using namespace angle;
...@@ -222,6 +223,93 @@ ANGLE_INSTANTIATE_TEST(ProgramBinaryTest, ...@@ -222,6 +223,93 @@ ANGLE_INSTANTIATE_TEST(ProgramBinaryTest,
ES2_OPENGL(), ES2_OPENGL(),
ES3_OPENGL()); ES3_OPENGL());
class ProgramBinaryES3Test : public ANGLETest
{
};
// Tests that saving and loading a program perserves uniform block binding info.
TEST_P(ProgramBinaryES3Test, UniformBlockBinding)
{
// We can't run the test if no program binary formats are supported.
GLint binaryFormatCount = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &binaryFormatCount);
if (binaryFormatCount == 0)
{
std::cout << "Test skipped because no program binary formats available." << std::endl;
return;
}
const std::string &vertexShader =
"#version 300 es\n"
"uniform block {\n"
" float f;\n"
"};\n"
"in vec4 position;\n"
"out vec4 color;\n"
"void main() {\n"
" gl_Position = position;\n"
" color = vec4(f, f, f, 1);\n"
"}";
const std::string &fragmentShader =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 color;\n"
"out vec4 colorOut;\n"
"void main() {\n"
" colorOut = color;\n"
"}";
// Init and draw with the program.
ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
float fData[4] = {1.0f, 1.0f, 1.0f, 1.0f};
GLuint bindIndex = 2;
GLBuffer ubo;
glBindBuffer(GL_UNIFORM_BUFFER, ubo.get());
glBufferData(GL_UNIFORM_BUFFER, sizeof(fData), &fData, GL_STATIC_DRAW);
glBindBufferRange(GL_UNIFORM_BUFFER, bindIndex, ubo.get(), 0, sizeof(fData));
GLint blockIndex = glGetUniformBlockIndex(program.get(), "block");
ASSERT_NE(-1, blockIndex);
glUniformBlockBinding(program.get(), blockIndex, bindIndex);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
drawQuad(program.get(), "position", 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
// Read back the binary.
GLint programLength = 0;
glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
ASSERT_GL_NO_ERROR();
GLsizei readLength = 0;
GLenum binaryFormat = GL_NONE;
std::vector<uint8_t> binary(programLength);
glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
// Load a new program with the binary and draw.
ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
drawQuad(binaryProgram.get(), "position", 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
}
ANGLE_INSTANTIATE_TEST(ProgramBinaryES3Test, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
class ProgramBinaryTransformFeedbackTest : public ANGLETest class ProgramBinaryTransformFeedbackTest : public ANGLETest
{ {
protected: protected:
......
...@@ -48,47 +48,65 @@ using GLFramebuffer = GLWrapper<glGenFramebuffers, glDeleteFramebuffers>; ...@@ -48,47 +48,65 @@ using GLFramebuffer = GLWrapper<glGenFramebuffers, glDeleteFramebuffers>;
using GLRenderbuffer = GLWrapper<glGenRenderbuffers, glDeleteRenderbuffers>; using GLRenderbuffer = GLWrapper<glGenRenderbuffers, glDeleteRenderbuffers>;
using GLSampler = GLWrapper<glGenSamplers, glDeleteSamplers>; using GLSampler = GLWrapper<glGenSamplers, glDeleteSamplers>;
// Don't use GLProgram directly, use ANGLE_GL_PROGRAM.
namespace priv
{
class GLProgram class GLProgram
{ {
public: public:
GLProgram(const std::string &vertexShader, const std::string &fragmentShader) GLProgram() : mHandle(0) {}
: mHandle(0), mVertexShader(vertexShader), mFragmentShader(fragmentShader)
~GLProgram() { glDeleteProgram(mHandle); }
void makeCompute(const std::string &computeShader)
{ {
mHandle = CompileComputeProgram(computeShader);
} }
GLProgram(const std::string &computeShader) : mHandle(0), mComputeShader(computeShader) {} void makeRaster(const std::string &vertexShader, const std::string &fragmentShader)
{
mHandle = CompileProgram(vertexShader, fragmentShader);
}
~GLProgram() { glDeleteProgram(mHandle); } void makeBinaryOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
mHandle = LoadBinaryProgramOES(binary, binaryFormat);
}
void makeBinaryES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
mHandle = LoadBinaryProgramES3(binary, binaryFormat);
}
GLuint get() GLuint get()
{ {
if (mHandle == 0) ASSERT(mHandle != 0);
{
if (!mComputeShader.empty())
{
mHandle = CompileComputeProgram(mComputeShader);
}
else
{
mHandle = CompileProgram(mVertexShader, mFragmentShader);
}
}
return mHandle; return mHandle;
} }
private: private:
GLuint mHandle; GLuint mHandle;
const std::string mVertexShader;
const std::string mFragmentShader;
const std::string mComputeShader;
}; };
} // namespace priv
#define ANGLE_GL_PROGRAM(name, vertex, fragment) \ #define ANGLE_GL_PROGRAM(name, vertex, fragment) \
GLProgram name(vertex, fragment); \ priv::GLProgram name; \
name.makeRaster(vertex, fragment); \
ASSERT_NE(0u, name.get()); ASSERT_NE(0u, name.get());
#define ANGLE_GL_COMPUTE_PROGRAM(name, compute) \ #define ANGLE_GL_COMPUTE_PROGRAM(name, compute) \
GLProgram name(compute); \ priv::GLProgram name; \
name.makeCompute(compute); \
ASSERT_NE(0u, name.get());
#define ANGLE_GL_BINARY_OES_PROGRAM(name, binary, binaryFormat) \
priv::GLProgram name; \
name.makeBinaryOES(binary, binaryFormat); \
ASSERT_NE(0u, name.get());
#define ANGLE_GL_BINARY_ES3_PROGRAM(name, binary, binaryFormat) \
priv::GLProgram name; \
name.makeBinaryES3(binary, binaryFormat); \
ASSERT_NE(0u, name.get()); ASSERT_NE(0u, name.get());
} // namespace angle } // namespace angle
......
...@@ -79,6 +79,9 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath) ...@@ -79,6 +79,9 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages) GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
{ {
if (glGetError() != GL_NO_ERROR)
return 0;
GLint linkStatus; GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == 0) if (linkStatus == 0)
...@@ -189,3 +192,17 @@ GLuint CompileComputeProgram(const std::string &csSource, bool outputErrorMessag ...@@ -189,3 +192,17 @@ GLuint CompileComputeProgram(const std::string &csSource, bool outputErrorMessag
return CheckLinkStatusAndReturnProgram(program, outputErrorMessages); return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
} }
GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
GLuint program = glCreateProgram();
glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
return CheckLinkStatusAndReturnProgram(program, true);
}
GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
GLuint program = glCreateProgram();
glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
return CheckLinkStatusAndReturnProgram(program, true);
}
...@@ -33,4 +33,7 @@ ANGLE_EXPORT GLuint CompileProgramFromFiles(const std::string &vsPath, const std ...@@ -33,4 +33,7 @@ ANGLE_EXPORT GLuint CompileProgramFromFiles(const std::string &vsPath, const std
ANGLE_EXPORT GLuint CompileComputeProgram(const std::string &csSource, ANGLE_EXPORT GLuint CompileComputeProgram(const std::string &csSource,
bool outputErrorMessages = true); bool outputErrorMessages = true);
ANGLE_EXPORT GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat);
ANGLE_EXPORT GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat);
#endif // SAMPLE_UTIL_SHADER_UTILS_H #endif // SAMPLE_UTIL_SHADER_UTILS_H
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