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,
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>();
ASSERT(mState.mTransformFeedbackVaryingVars.empty());
for (unsigned int transformFeedbackVaryingIndex = 0;
......@@ -848,7 +856,7 @@ Error Program::loadBinary(const Context *context,
stream.readInt(&mSamplerUniformRange.start);
stream.readInt(&mSamplerUniformRange.end);
ANGLE_TRY_RESULT(mProgram->load(mInfoLog, &stream), mLinked);
ANGLE_TRY_RESULT(mProgram->load(context->getImplementation(), mInfoLog, &stream), mLinked);
return NoError();
#endif // #if ANGLE_PROGRAM_BINARY_LOAD == ANGLE_ENABLED
......@@ -936,6 +944,11 @@ Error Program::saveBinary(const Context *context,
}
}
for (GLuint binding : mState.mUniformBlockBindings)
{
stream.writeInt(binding);
}
stream.writeInt(mState.mTransformFeedbackVaryingVars.size());
for (const sh::Varying &varying : mState.mTransformFeedbackVaryingVars)
{
......@@ -1685,6 +1698,7 @@ const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const
void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
{
mState.mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding;
mState.mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlockBinding != 0);
mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
}
......
......@@ -13,6 +13,7 @@
#include <GLES2/gl2.h>
#include <GLSLANG/ShaderVars.h>
#include <array>
#include <map>
#include <set>
#include <sstream>
......@@ -211,7 +212,7 @@ class ProgramState final : angle::NonCopyable
std::vector<sh::Varying> mTransformFeedbackVaryingVars;
GLenum mTransformFeedbackBufferMode;
GLuint mUniformBlockBindings[IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS];
std::array<GLuint, IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS> mUniformBlockBindings;
UniformBlockBindingMask mActiveUniformBlockBindings;
std::vector<sh::Attribute> mAttributes;
......
......@@ -26,13 +26,17 @@ namespace rx
{
using LinkResult = gl::ErrorOrResult<bool>;
class ContextImpl;
class ProgramImpl : angle::NonCopyable
{
public:
ProgramImpl(const gl::ProgramState &state) : mState(state) {}
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 void setBinaryRetrievableHint(bool retrievable) = 0;
......
......@@ -23,7 +23,7 @@ class MockProgramImpl : public rx::ProgramImpl
MockProgramImpl() : ProgramImpl(gl::ProgramState()) {}
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(setBinaryRetrievableHint, void(bool));
......
......@@ -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();
DeviceIdentifier binaryDeviceIdentifier = {0};
......
......@@ -151,7 +151,9 @@ class ProgramD3D : public ProgramImpl
bool usesGeometryShader(GLenum drawMode) 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;
void setBinaryRetrievableHint(bool retrievable) override;
......
......@@ -369,7 +369,7 @@ StateManagerGL *ContextGL::getStateManager()
return mRenderer->getStateManager();
}
const WorkaroundsGL &ContextGL::getWorkaroundsGL()
const WorkaroundsGL &ContextGL::getWorkaroundsGL() const
{
return mRenderer->getWorkarounds();
}
......
......@@ -172,7 +172,7 @@ class ContextGL : public ContextImpl
// Handle helpers
const FunctionsGL *getFunctions() const;
StateManagerGL *getStateManager();
const WorkaroundsGL &getWorkaroundsGL();
const WorkaroundsGL &getWorkaroundsGL() const;
private:
RendererGL *mRenderer;
......
......@@ -8,10 +8,12 @@
#include "libANGLE/renderer/gl/ProgramGL.h"
#include "common/BitSetIterator.h"
#include "common/angleutils.h"
#include "common/debug.h"
#include "common/string_utils.h"
#include "common/utilities.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/ShaderGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h"
......@@ -46,7 +48,9 @@ ProgramGL::~ProgramGL()
mProgramID = 0;
}
LinkResult ProgramGL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
LinkResult ProgramGL::load(const ContextImpl *contextImpl,
gl::InfoLog &infoLog,
gl::BinaryInputStream *stream)
{
preLink();
......@@ -67,6 +71,16 @@ LinkResult ProgramGL::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
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;
}
......
......@@ -37,7 +37,9 @@ class ProgramGL : public ProgramImpl
bool enablePathRendering);
~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;
void setBinaryRetrievableHint(bool retrievable) override;
......
......@@ -14,25 +14,6 @@ namespace rx
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
// 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
......@@ -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
// drivers on framebuffer formats that have 1-bit alpha, work around this by using higher
// precision formats instead.
bool avoid1BitAlphaTextureFormats;
bool avoid1BitAlphaTextureFormats = false;
// 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
// format.
bool rgba4IsNotSupportedForColorRendering;
bool rgba4IsNotSupportedForColorRendering = false;
// 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
// blending. It only seems to do this when the framebuffer has only linear attachments, mixed
// 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:
// int i = 1;
......@@ -60,34 +41,34 @@ struct WorkaroundsGL
// continue;
// } while (i > 0)
// 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
// (NVIDIA) drivers. It was found that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS before the finish
// causes it to fully finish.
bool finishDoesNotCauseQueriesToBeAvailable;
bool finishDoesNotCauseQueriesToBeAvailable = false;
// 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
// 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).
bool alwaysCallUseProgramAfterLink;
bool alwaysCallUseProgramAfterLink = false;
// 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.
bool packOverlappingRowsSeparatelyPackBuffer;
bool packOverlappingRowsSeparatelyPackBuffer = false;
// 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.
// 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.
// 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
// checking if the pixel unpack buffer is big enough. Tracking bug: http://anglebug.com/1512
......@@ -101,37 +82,43 @@ struct WorkaroundsGL
// +-------A--B
// 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.
bool unpackLastRowSeparatelyForPaddingInclusion;
bool unpackLastRowSeparatelyForPaddingInclusion = false;
// 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
// this bug, we use an expression to emulate function isnan().
// 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
// 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.
bool useUnusedBlocksWithStandardOrSharedLayout;
bool useUnusedBlocksWithStandardOrSharedLayout = false;
// This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20
// on AMD.
bool dontRemoveInvariantForFragmentInput;
bool dontRemoveInvariantForFragmentInput = false;
// 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
// replace "-float".
// Tracking bug: http://crbug.com/308366
bool rewriteFloatUnaryMinusOperator;
bool rewriteFloatUnaryMinusOperator = false;
// On NVIDIA drivers, atan(y, x) may return a wrong answer.
// 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_
......@@ -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
// 364 are known to be affected, at least up to 375.
workarounds->emulateAtan2Float = IsNvidia(vendor);
}
workarounds->reapplyUBOBindingsAfterLoadingBinaryProgram = IsAMD(vendor);
}
} // namespace nativegl_gl
namespace nativegl
{
bool SupportsFenceSync(const FunctionsGL *functions)
......
......@@ -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;
}
......
......@@ -21,7 +21,9 @@ class ProgramNULL : public ProgramImpl
ProgramNULL(const gl::ProgramState &state);
~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;
void setBinaryRetrievableHint(bool retrievable) override;
......
......@@ -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();
return gl::Error(GL_INVALID_OPERATION);
......
......@@ -21,7 +21,9 @@ class ProgramVk : public ProgramImpl
ProgramVk(const gl::ProgramState &state);
~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;
void setBinaryRetrievableHint(bool retrievable) override;
......
......@@ -4003,6 +4003,12 @@ bool ValidateGetProgramBinaryBase(Context *context,
return false;
}
if (context->getCaps().programBinaryFormats.empty())
{
context->handleError(Error(GL_INVALID_OPERATION, "No program binary formats supported."));
return false;
}
return true;
}
......
......@@ -12,6 +12,7 @@
#include "EGLWindow.h"
#include "OSWindow.h"
#include "test_utils/angle_test_configs.h"
#include "test_utils/gl_raii.h"
using namespace angle;
......@@ -222,6 +223,93 @@ ANGLE_INSTANTIATE_TEST(ProgramBinaryTest,
ES2_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
{
protected:
......
......@@ -48,47 +48,65 @@ using GLFramebuffer = GLWrapper<glGenFramebuffers, glDeleteFramebuffers>;
using GLRenderbuffer = GLWrapper<glGenRenderbuffers, glDeleteRenderbuffers>;
using GLSampler = GLWrapper<glGenSamplers, glDeleteSamplers>;
// Don't use GLProgram directly, use ANGLE_GL_PROGRAM.
namespace priv
{
class GLProgram
{
public:
GLProgram(const std::string &vertexShader, const std::string &fragmentShader)
: mHandle(0), mVertexShader(vertexShader), mFragmentShader(fragmentShader)
{
}
GLProgram(const std::string &computeShader) : mHandle(0), mComputeShader(computeShader) {}
GLProgram() : mHandle(0) {}
~GLProgram() { glDeleteProgram(mHandle); }
GLuint get()
void makeCompute(const std::string &computeShader)
{
if (mHandle == 0)
{
if (!mComputeShader.empty())
mHandle = CompileComputeProgram(computeShader);
}
void makeRaster(const std::string &vertexShader, const std::string &fragmentShader)
{
mHandle = CompileComputeProgram(mComputeShader);
mHandle = CompileProgram(vertexShader, fragmentShader);
}
else
void makeBinaryOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
mHandle = CompileProgram(mVertexShader, mFragmentShader);
mHandle = LoadBinaryProgramOES(binary, binaryFormat);
}
void makeBinaryES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
mHandle = LoadBinaryProgramES3(binary, binaryFormat);
}
GLuint get()
{
ASSERT(mHandle != 0);
return mHandle;
}
private:
GLuint mHandle;
const std::string mVertexShader;
const std::string mFragmentShader;
const std::string mComputeShader;
};
} // namespace priv
#define ANGLE_GL_PROGRAM(name, vertex, fragment) \
GLProgram name(vertex, fragment); \
priv::GLProgram name; \
name.makeRaster(vertex, fragment); \
ASSERT_NE(0u, name.get());
#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());
} // namespace angle
......
......@@ -79,6 +79,9 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
{
if (glGetError() != GL_NO_ERROR)
return 0;
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == 0)
......@@ -189,3 +192,17 @@ GLuint CompileComputeProgram(const std::string &csSource, bool outputErrorMessag
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
ANGLE_EXPORT GLuint CompileComputeProgram(const std::string &csSource,
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
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