// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // Context.inl.h: Defines inline functions of gl::Context class // Has to be included after libANGLE/Context.h when using one // of the defined functions #ifndef LIBANGLE_CONTEXT_INL_H_ #define LIBANGLE_CONTEXT_INL_H_ #include "libANGLE/Context.h" #include "libANGLE/GLES1Renderer.h" #include "libANGLE/renderer/ContextImpl.h" #define ANGLE_HANDLE_ERR(X) \ (void)(X); \ return; #define ANGLE_CONTEXT_TRY(EXPR) ANGLE_TRY_TEMPLATE(EXPR, static_cast(0), ANGLE_HANDLE_ERR) namespace gl { constexpr angle::PackedEnumMap kMinimumPrimitiveCounts = {{ {PrimitiveMode::Points, 1}, {PrimitiveMode::Lines, 2}, {PrimitiveMode::LineLoop, 2}, {PrimitiveMode::LineStrip, 2}, {PrimitiveMode::Triangles, 3}, {PrimitiveMode::TriangleStrip, 3}, {PrimitiveMode::TriangleFan, 3}, {PrimitiveMode::LinesAdjacency, 2}, {PrimitiveMode::LineStripAdjacency, 2}, {PrimitiveMode::TrianglesAdjacency, 3}, {PrimitiveMode::TriangleStripAdjacency, 3}, }}; // All bits except |DIRTY_BIT_READ_FRAMEBUFFER_BINDING| because |mDrawDirtyObjects| does not contain // |DIRTY_OBJECT_READ_FRAMEBUFFER|, to avoid synchronizing with invalid read framebuffer state. constexpr state::DirtyBits kDrawDirtyBits = ~state::DirtyBits{state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING}; constexpr state::ExtendedDirtyBits kDrawExtendedDirtyBits = state::ExtendedDirtyBits().set(); ANGLE_INLINE void MarkTransformFeedbackBufferUsage(const Context *context, GLsizei count, GLsizei instanceCount) { if (context->getStateCache().isTransformFeedbackActiveUnpaused()) { TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback(); transformFeedback->onVerticesDrawn(context, count, instanceCount); } } ANGLE_INLINE void MarkShaderStorageUsage(const Context *context) { for (size_t index : context->getStateCache().getActiveShaderStorageBufferIndices()) { Buffer *buffer = context->getState().getIndexedShaderStorageBuffer(index).get(); if (buffer) { buffer->onDataChanged(context); } } for (size_t index : context->getStateCache().getActiveImageUnitIndices()) { const ImageUnit &imageUnit = context->getState().getImageUnit(index); const Texture *texture = imageUnit.texture.get(); if (texture) { texture->onStateChange(angle::SubjectMessage::ContentsChanged); } } } // Return true if the draw is a no-op, else return false. // If there is no active program for the vertex or fragment shader stages, the results of vertex // and fragment shader execution will respectively be undefined. However, this is not // an error. ANGLE will treat this as a no-op. // A no-op draw occurs if the count of vertices is less than the minimum required to // have a valid primitive for this mode (0 for points, 0-1 for lines, 0-2 for tris). ANGLE_INLINE bool Context::noopDrawProgram() const { // Make sure any pending link is done before checking whether draw is allowed. mState.ensureNoPendingLink(this); // No-op when there is no active vertex shader return !mStateCache.getCanDraw(); } ANGLE_INLINE bool Context::noopDraw(PrimitiveMode mode, GLsizei count) const { if (ANGLE_UNLIKELY(count < kMinimumPrimitiveCounts[mode])) { return true; } return noopDrawProgram(); } ANGLE_INLINE bool Context::noopDrawInstanced(PrimitiveMode mode, GLsizei count, GLsizei instanceCount) const { if (ANGLE_UNLIKELY(instanceCount < 1)) { return true; } return noopDraw(mode, count); } ANGLE_INLINE bool Context::noopMultiDraw(GLsizei drawcount) const { if (ANGLE_UNLIKELY(drawcount < 1)) { return true; } return noopDrawProgram(); } ANGLE_INLINE angle::Result Context::syncDirtyBits(const state::DirtyBits bitMask, const state::ExtendedDirtyBits extendedBitMask, Command command) { const state::DirtyBits dirtyBits = (mState.getDirtyBits() & bitMask); const state::ExtendedDirtyBits extendedDirtyBits = (mState.getExtendedDirtyBits() & extendedBitMask); ANGLE_TRY(mImplementation->syncState(this, dirtyBits, bitMask, extendedDirtyBits, extendedBitMask, command)); mState.clearDirtyBits(dirtyBits); mState.clearExtendedDirtyBits(extendedDirtyBits); return angle::Result::Continue; } ANGLE_INLINE angle::Result Context::syncDirtyObjects(const state::DirtyObjects &objectMask, Command command) { return mState.syncDirtyObjects(this, objectMask, command); } ANGLE_INLINE angle::Result Context::prepareForDraw(PrimitiveMode mode) { if (mGLES1Renderer) { ANGLE_TRY(mGLES1Renderer->prepareForDraw(mode, this, &mState, getMutableGLES1State())); } ANGLE_TRY(syncDirtyObjects(mDrawDirtyObjects, Command::Draw)); ASSERT(!isRobustResourceInitEnabled() || !mState.getDrawFramebuffer()->hasResourceThatNeedsInit()); return syncDirtyBits(kDrawDirtyBits, kDrawExtendedDirtyBits, Command::Draw); } ANGLE_INLINE void Context::drawArrays(PrimitiveMode mode, GLint first, GLsizei count) { // No-op if count draws no primitives for given mode if (noopDraw(mode, count)) { ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); return; } ANGLE_CONTEXT_TRY(prepareForDraw(mode)); ANGLE_CONTEXT_TRY(mImplementation->drawArrays(this, mode, first, count)); MarkTransformFeedbackBufferUsage(this, count, 1); } ANGLE_INLINE void Context::drawElements(PrimitiveMode mode, GLsizei count, DrawElementsType type, const void *indices) { // No-op if count draws no primitives for given mode if (noopDraw(mode, count)) { ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); return; } ANGLE_CONTEXT_TRY(prepareForDraw(mode)); ANGLE_CONTEXT_TRY(mImplementation->drawElements(this, mode, count, type, indices)); } ANGLE_INLINE void Context::bindBuffer(BufferBinding target, BufferID buffer) { Buffer *bufferObject = mState.mBufferManager->checkBufferAllocation(mImplementation.get(), buffer); // If there is a shared context, buffer may have been modified by other context. bindBuffer // supposedly should pick up the changes other contexts have made and update current vertex // array. if (bufferObject != nullptr && isSharedContext()) { VertexArrayBufferBindingMask bindingMask = bufferObject->getVertexArrayBinding(this); if (bindingMask.any()) { ASSERT(mState.mVertexArray != nullptr); // Update vertex array only if buffer is attached to current vertex array. mState.mVertexArray->onSharedBufferBind(this, bufferObject, bindingMask); mState.setObjectDirty(GL_VERTEX_ARRAY); mPrivateStateCache.onVertexArrayBufferContentsChange(); } } // Early return if rebinding the same buffer if (bufferObject == mState.getTargetBuffer(target)) { return; } mState.setBufferBinding(this, target, bufferObject); mPrivateStateCache.onBufferBindingChange(); if (bufferObject && isWebGL()) { bufferObject->onBind(this, target); } } ANGLE_INLINE void Context::uniform1f(UniformLocation location, GLfloat x) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform1fv(location, 1, &x); } ANGLE_INLINE void Context::uniform1fv(UniformLocation location, GLsizei count, const GLfloat *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform1fv(location, count, v); } ANGLE_INLINE void Context::setUniform1iImpl(Program *program, UniformLocation location, GLsizei count, const GLint *v) { program->getExecutable().setUniform1iv(this, location, count, v); } ANGLE_INLINE void Context::uniform1i(UniformLocation location, GLint x) { Program *program = getActiveLinkedProgram(); setUniform1iImpl(program, location, 1, &x); } ANGLE_INLINE void Context::uniform1iv(UniformLocation location, GLsizei count, const GLint *v) { Program *program = getActiveLinkedProgram(); setUniform1iImpl(program, location, count, v); } ANGLE_INLINE void Context::uniform2f(UniformLocation location, GLfloat x, GLfloat y) { GLfloat xy[2] = {x, y}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform2fv(location, 1, xy); } ANGLE_INLINE void Context::uniform2fv(UniformLocation location, GLsizei count, const GLfloat *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform2fv(location, count, v); } ANGLE_INLINE void Context::uniform2i(UniformLocation location, GLint x, GLint y) { GLint xy[2] = {x, y}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform2iv(location, 1, xy); } ANGLE_INLINE void Context::uniform2iv(UniformLocation location, GLsizei count, const GLint *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform2iv(location, count, v); } ANGLE_INLINE void Context::uniform3f(UniformLocation location, GLfloat x, GLfloat y, GLfloat z) { GLfloat xyz[3] = {x, y, z}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform3fv(location, 1, xyz); } ANGLE_INLINE void Context::uniform3fv(UniformLocation location, GLsizei count, const GLfloat *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform3fv(location, count, v); } ANGLE_INLINE void Context::uniform3i(UniformLocation location, GLint x, GLint y, GLint z) { GLint xyz[3] = {x, y, z}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform3iv(location, 1, xyz); } ANGLE_INLINE void Context::uniform3iv(UniformLocation location, GLsizei count, const GLint *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform3iv(location, count, v); } ANGLE_INLINE void Context::uniform4f(UniformLocation location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { GLfloat xyzw[4] = {x, y, z, w}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform4fv(location, 1, xyzw); } ANGLE_INLINE void Context::uniform4fv(UniformLocation location, GLsizei count, const GLfloat *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform4fv(location, count, v); } ANGLE_INLINE void Context::uniform4i(UniformLocation location, GLint x, GLint y, GLint z, GLint w) { GLint xyzw[4] = {x, y, z, w}; Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform4iv(location, 1, xyzw); } ANGLE_INLINE void Context::uniform4iv(UniformLocation location, GLsizei count, const GLint *v) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform4iv(location, count, v); } ANGLE_INLINE void Context::uniform1ui(UniformLocation location, GLuint v0) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform1uiv(location, 1, &v0); } ANGLE_INLINE void Context::uniform2ui(UniformLocation location, GLuint v0, GLuint v1) { Program *program = getActiveLinkedProgram(); const GLuint xy[] = {v0, v1}; program->getExecutable().setUniform2uiv(location, 1, xy); } ANGLE_INLINE void Context::uniform3ui(UniformLocation location, GLuint v0, GLuint v1, GLuint v2) { Program *program = getActiveLinkedProgram(); const GLuint xyz[] = {v0, v1, v2}; program->getExecutable().setUniform3uiv(location, 1, xyz); } ANGLE_INLINE void Context::uniform4ui(UniformLocation location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { Program *program = getActiveLinkedProgram(); const GLuint xyzw[] = {v0, v1, v2, v3}; program->getExecutable().setUniform4uiv(location, 1, xyzw); } ANGLE_INLINE void Context::uniform1uiv(UniformLocation location, GLsizei count, const GLuint *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform1uiv(location, count, value); } ANGLE_INLINE void Context::uniform2uiv(UniformLocation location, GLsizei count, const GLuint *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform2uiv(location, count, value); } ANGLE_INLINE void Context::uniform3uiv(UniformLocation location, GLsizei count, const GLuint *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform3uiv(location, count, value); } ANGLE_INLINE void Context::uniform4uiv(UniformLocation location, GLsizei count, const GLuint *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniform4uiv(location, count, value); } ANGLE_INLINE void Context::uniformMatrix2x3fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix2x3fv(location, count, transpose, value); } ANGLE_INLINE void Context::uniformMatrix3x2fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix3x2fv(location, count, transpose, value); } ANGLE_INLINE void Context::uniformMatrix2x4fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix2x4fv(location, count, transpose, value); } ANGLE_INLINE void Context::uniformMatrix4x2fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix4x2fv(location, count, transpose, value); } ANGLE_INLINE void Context::uniformMatrix3x4fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix3x4fv(location, count, transpose, value); } ANGLE_INLINE void Context::uniformMatrix4x3fv(UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { Program *program = getActiveLinkedProgram(); program->getExecutable().setUniformMatrix4x3fv(location, count, transpose, value); } ANGLE_INLINE void Context::vertexAttribPointer(GLuint index, GLint size, VertexAttribType type, GLboolean normalized, GLsizei stride, const void *ptr) { bool vertexAttribDirty = false; mState.setVertexAttribPointer(this, index, mState.getTargetBuffer(BufferBinding::Array), size, type, normalized != GL_FALSE, stride, ptr, &vertexAttribDirty); if (vertexAttribDirty) { mPrivateStateCache.onVertexArrayStateChange(); } } } // namespace gl #endif // LIBANGLE_CONTEXT_INL_H_