mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
801 lines
52 KiB
C++
801 lines
52 KiB
C++
// MobileGL - MobileGL/MG_State/GLState/Core.h
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#pragma once
|
|
#include <Includes.h>
|
|
#include "ErrorState/Error.h"
|
|
#include "BufferState/BufferState.h"
|
|
#include "MG_State/GLState/RenderbufferState/RenderbufferObject.h"
|
|
#include "MG_State/GLState/RenderbufferState/RenderbufferState.h"
|
|
#include "RenderState/RenderState.h"
|
|
#include "ProgramState/ProgramState.h"
|
|
#include "ProgramState/ProgramPipelineObject.h"
|
|
#include "SamplerState/SamplerState.h"
|
|
#include "ShareGroupState.h"
|
|
#include "TextureState/TextureState.h"
|
|
#include "FramebufferState/FramebufferState.h"
|
|
#include "VertexArrayState/VertexArrayState.h"
|
|
#include "RenderbufferState/RenderbufferState.h"
|
|
|
|
namespace MobileGL::MG_Util::ShaderTranspiler {
|
|
struct CompileEnv;
|
|
}
|
|
|
|
namespace MobileGL {
|
|
namespace MG_State {
|
|
void Init();
|
|
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// THE SERVER SESSION GENERATION. A split client advances it each time a session with a
|
|
// server comes up; every GLContext records the value it was built under. A device loss
|
|
// ends a session, and once a fresh one replaces it every context built before is lost:
|
|
// its objects exist only on the dead session's server. 0 is "no session yet" (a monolith
|
|
// run, or a context built before the first bring-up), which no later session ends.
|
|
Uint64 CurrentWireEpoch();
|
|
void AdvanceWireEpoch();
|
|
#endif
|
|
|
|
namespace GLState {
|
|
// MGPAttribValue::ValueClass' encoding (MG_Pipe/MGPipeTypes.h documents the order
|
|
// "Float | Int | Uint | Double"). It lives here rather than in MG_Pipe because the
|
|
// FRONTEND is the only thing that knows which of the three views below a value was
|
|
// written through - the other two are numeric conversions of it - and MG_Pipe has
|
|
// no enum for the field yet. If package A introduces one, this becomes its alias.
|
|
inline constexpr Uint32 kVertexAttribValueClassFloat = 0;
|
|
inline constexpr Uint32 kVertexAttribValueClassInt = 1;
|
|
inline constexpr Uint32 kVertexAttribValueClassUint = 2;
|
|
|
|
struct CurrentVertexAttributeValue {
|
|
Array<Float, 4> floatValue{0.f, 0.f, 0.f, 1.f};
|
|
Array<Int32, 4> intValue{0, 0, 0, 1};
|
|
Array<Uint32, 4> uintValue{0u, 0u, 0u, 1u};
|
|
// Three scalar arrays and NOTHING ELSE. MG_Backend/MGPipe/PipeInputs.cpp
|
|
// compares this storage with one memcmp and asserts that size, so a fourth
|
|
// member here is a build break in a file P2 package B does not own. The
|
|
// written-class discriminator set_vertex_attrib_defaults needs therefore
|
|
// lives beside the array on GLContext, not inside the value.
|
|
};
|
|
|
|
// Which of the three views above a shader input of a given GLSL type consumes.
|
|
enum class VertexAttribBaseType { Unsupported, Float, Int, Uint };
|
|
|
|
struct VertexAttribTypeInfo {
|
|
VertexAttribBaseType baseType = VertexAttribBaseType::Unsupported;
|
|
Uint componentCount = 0;
|
|
};
|
|
|
|
// Maps a shader vertex-input type (GL_FLOAT_VEC3, GL_INT_VEC2, ...) onto the current-value
|
|
// view that feeds it. Shared by every backend so that "a disabled array reads the current
|
|
// value" resolves identically regardless of which backend is active; each backend only
|
|
// translates the result into its own API call.
|
|
VertexAttribTypeInfo ClassifyVertexAttribType(GLenum glType);
|
|
|
|
// One indexed capture binding of a transform feedback object, as the by-name queries
|
|
// report it. An empty Buffer means the binding point is unbound.
|
|
struct NamedTransformFeedbackBinding {
|
|
SharedPtr<BufferObject> Buffer;
|
|
Range1D Range{};
|
|
Bool HasExplicitRange = false;
|
|
};
|
|
|
|
class GLContext {
|
|
public:
|
|
// A context with a share group of its own. Created through EGL, a context
|
|
// either starts a new group or joins the one its share context belongs to;
|
|
// this constructor is the standalone shape the tests and the process default
|
|
// context use.
|
|
GLContext();
|
|
explicit GLContext(SharedPtr<ShareGroupState> shareGroup);
|
|
|
|
// The GL objects this context shares with every context that shares with it.
|
|
const SharedPtr<ShareGroupState>& GetShareGroup() const { return m_shareGroup; }
|
|
|
|
// Installs the process-wide default framebuffer (GL name 0) into this context:
|
|
// its table entry and the Draw/Read bindings the context starts with.
|
|
void InstallDefaultFramebuffer(const SharedPtr<FramebufferObject>& framebuffer);
|
|
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// The server session this context was built against (CurrentWireEpoch() at its
|
|
// construction). Its objects live on that session's server and nowhere else, so
|
|
// once a later session has replaced it - after a device loss - the context is
|
|
// lost for good: nothing it holds may reach the new session.
|
|
Uint64 WireEpoch() const { return m_wireEpoch; }
|
|
Bool IsFromEndedWireSession() const;
|
|
// GL_CONTEXT_LOST is reported by glGetError once per lost context, then the
|
|
// error queue is clear again (a caller draining errors in a loop terminates).
|
|
Bool TakeContextLostReport() {
|
|
if (m_contextLostReported) return false;
|
|
m_contextLostReported = true;
|
|
return true;
|
|
}
|
|
// True the first time glGetGraphicsResetStatus reports this context's reset (the
|
|
// one time it names the cause in the log).
|
|
Bool TakeResetStatusReport() {
|
|
if (m_resetStatusReported) return false;
|
|
m_resetStatusReported = true;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Error
|
|
void RecordError(ErrorCode code, UniquePtr<ErrorInfo> info);
|
|
Bool HasGLError() const;
|
|
Optional<const Error*> PeekNonGLError() const;
|
|
Optional<UniquePtr<Error>> PopNonGLError();
|
|
Bool HasNonGLError() const;
|
|
Optional<const Error*> PeekGLError() const;
|
|
Optional<UniquePtr<Error>> PopGLError();
|
|
void ClearErrors();
|
|
|
|
// Buffer
|
|
void GenBufferNames(Uint number, Vector<Uint>& buffers);
|
|
const SharedPtr<BufferObject>& GetBufferObject(Uint index);
|
|
BindingSlot<BufferObject>& GetBufferBindingSlot(BufferTarget target);
|
|
BindingSlotRange1D<BufferObject>& GetBufferBindingPoint(BufferTarget target, Uint index);
|
|
constexpr SizeT GetBufferBindingPointCount(BufferTarget target) const {
|
|
return m_bufferState.GetBindingPointCount(target);
|
|
}
|
|
void TouchBufferBindingPoint(BufferTarget target, Uint index) {
|
|
m_bufferState.TouchBindPoint(target, index);
|
|
}
|
|
SizeT GetTouchedBufferBindingPointCount(BufferTarget target) const {
|
|
return m_bufferState.GetTouchedBindPointCount(target);
|
|
}
|
|
const SharedPtr<BufferObject>& CreateBufferObject(Uint index);
|
|
void MarkBufferObjectForDeletion(Uint index);
|
|
Bool ValidateBufferName(Uint index) const;
|
|
Bool ValidateBufferObject(Uint index) const;
|
|
|
|
// VertexArray
|
|
void GenVertexArrayNames(Uint number, Vector<Uint>& vertexArrays);
|
|
const SharedPtr<VertexArrayObject>& GetVertexArrayObject(Uint index);
|
|
void BindVertexArray(Uint index);
|
|
const SharedPtr<VertexArrayObject>& CreateVertexArrayObject(Uint index);
|
|
void MarkVertexArrayForDeletion(Uint index);
|
|
Bool ValidateVertexArrayName(Uint index) const;
|
|
Bool ValidateVertexArrayObject(Uint index) const;
|
|
const SharedPtr<VertexArrayObject>& GetBoundVertexArray();
|
|
void SetCurrentVertexAttributeFloat(Uint index, const Array<Float, 4>& value);
|
|
void SetCurrentVertexAttributeInt(Uint index, const Array<Int32, 4>& value);
|
|
void SetCurrentVertexAttributeUint(Uint index, const Array<Uint32, 4>& value);
|
|
const CurrentVertexAttributeValue& GetCurrentVertexAttribute(Uint index) const;
|
|
|
|
// Texture
|
|
void GenTextureNames(Uint number, Vector<Uint>& textures);
|
|
const SharedPtr<ITextureObject>& GetTextureObject(Uint index);
|
|
// Per-target default texture object (name 0); see TextureState::GetDefaultTextureObject.
|
|
const SharedPtr<ITextureObject>& GetDefaultTextureObject(TextureTarget target) const;
|
|
const SharedPtr<ITextureObject>& CreateTextureObject(Uint index, TextureTarget target);
|
|
// See TextureState::CreateTextureViewObject (glTextureView, GL 4.6 core 8.18).
|
|
const SharedPtr<ITextureObject>& CreateTextureViewObject(Uint index, TextureTarget target,
|
|
const SharedPtr<ITextureObject>& storageOwner,
|
|
Uint minLevel, Uint numLevels, Uint minLayer,
|
|
Uint numLayers);
|
|
void MarkTextureObjectForDeletion(Uint index);
|
|
TextureUnit& GetTextureUnitObject(Int unit);
|
|
ImageTextureBinding& GetImageTextureBinding(Int unit);
|
|
const ImageTextureBinding& GetImageTextureBinding(Int unit) const;
|
|
void NoteTextureUnitTouched(Int unit, Bool bindingChanged = true) {
|
|
m_textureState.NoteUnitTouched(unit, bindingChanged);
|
|
}
|
|
Int GetMaxTouchedTextureUnit() const { return m_textureState.GetMaxTouchedUnit(); }
|
|
// The IMAGE-unit high-water mark, which is a different namespace from the
|
|
// texture-unit one above - see TextureState::NoteImageUnitTouched for why it is a
|
|
// second mark and why it only grows. Push builds only, so the pull build's
|
|
// GLContext is untouched (G1).
|
|
void NoteImageUnitTouched(Int unit) { m_textureState.NoteImageUnitTouched(unit); }
|
|
Int GetMaxTouchedImageUnit() const { return m_textureState.GetMaxTouchedImageUnit(); }
|
|
// Monotonic counter bumped whenever a texture bind/unbind/delete changes which
|
|
// texture is bound at a unit; lets a backend skip re-resolving an unchanged
|
|
// per-draw sampled-texture set.
|
|
Uint64 GetTextureBindGeneration() const { return m_textureState.GetTextureBindGeneration(); }
|
|
void BumpTextureBindGeneration() { m_textureState.BumpTextureBindGeneration(); }
|
|
// Monotonic counter bumped whenever a texture's shape or a sampler object's
|
|
// parameters change, i.e. whenever a bound texture's mipmap-completeness (and so
|
|
// whether a backend binds it at all) can have flipped without any bind moving;
|
|
// see TextureState::GetSamplingResolutionGeneration.
|
|
Uint64 GetSamplingResolutionGeneration() const {
|
|
return m_textureState.GetSamplingResolutionGeneration();
|
|
}
|
|
void BumpSamplingResolutionGeneration() { m_textureState.BumpSamplingResolutionGeneration(); }
|
|
// Never-reused id of this context, for backend memos keyed on the two counters
|
|
// above: both restart at 0 in a new context, and a recreated context can land on
|
|
// the old heap address. See TextureState::GetContextId.
|
|
Uint64 GetTextureContextId() const { return m_textureState.GetContextId(); }
|
|
Bool ValidateTextureName(Uint index) const;
|
|
Bool ValidateTextureObject(Uint index) const;
|
|
Int GetActiveTextureUnit() const;
|
|
void SetActiveTextureUnit(Int unit);
|
|
|
|
// Program
|
|
Uint CreateProgram();
|
|
Uint CreateShader(ShaderStage stage);
|
|
void MarkProgramForDeletion(Uint index);
|
|
void MarkShaderForDeletion(Uint index);
|
|
// Frees a deletion-flagged shader's name once it lost its last GL-visible
|
|
// attachment (call after glDetachShader).
|
|
void ReleaseShaderNameIfOrphaned(Uint index);
|
|
Bool ValidateProgramName(Uint index) const;
|
|
Bool ValidateShaderName(Uint index) const;
|
|
const SharedPtr<ProgramObject>& GetProgramObject(Uint index);
|
|
const SharedPtr<ShaderObject>& GetShaderObject(Uint index);
|
|
// Settles every compile and link this context still owns; see
|
|
// ProgramState::JoinAllPendingWork. Called by glMaxShaderCompilerThreadsKHR(0).
|
|
void JoinAllPendingShaderWork();
|
|
// P1 stage 6: the per-context index of adoptable compile nodes, for its
|
|
// adoption counter. Diagnostics and tests only - no GL entry point reads it.
|
|
ShaderCompileAdoptionMap& GetShaderCompileAdoptionMap() {
|
|
return Group().Programs().GetShaderCompileAdoptionMap();
|
|
}
|
|
void UseProgram(Uint program);
|
|
const SharedPtr<ProgramObject>& GetCurrentProgram();
|
|
// What a DRAW executes: the program in use, or - when there is none - the bound
|
|
// pipeline's GRAPHICS stages composited into one program. A pipeline's compute
|
|
// stage is never part of that composite; ask GetProgramForDispatch for it.
|
|
const SharedPtr<ProgramObject>& GetProgramForDraw();
|
|
// What a DISPATCH executes: the program in use, or - when there is none - the
|
|
// bound pipeline's compute stage program itself. GL's compute stage is a whole
|
|
// program on its own (GL 4.6 core 7.4: it may not be linked with any other
|
|
// stage), so there is nothing to composite and no composite to cache.
|
|
const SharedPtr<ProgramObject>& GetProgramForDispatch();
|
|
// What glUniform* addresses: the program in use, or the bound pipeline's
|
|
// active program (GL 4.6 core 7.6.1).
|
|
const SharedPtr<ProgramObject>& GetProgramForUniform();
|
|
|
|
// Program pipeline (GL_ARB_separate_shader_objects, GL 4.6 core 7.4). Like queries
|
|
// and transform feedbacks, glGenProgramPipelines only RESERVES a name - the object
|
|
// appears on first USE (any of bind, UseProgramStages, ActiveShaderProgram,
|
|
// ValidateProgramPipeline) - while glCreateProgramPipelines makes it immediately.
|
|
void GenProgramPipelineNames(Uint number, Vector<Uint>& pipelines);
|
|
void CreateProgramPipelineObject(Uint index);
|
|
Bool ValidateProgramPipelineName(Uint index) const;
|
|
Bool IsProgramPipelineObject(Uint index) const;
|
|
void BindProgramPipelineObject(Uint index);
|
|
// Materializes a reserved name; returns null for 0 or a name that is not a live
|
|
// GenProgramPipelines name.
|
|
const SharedPtr<ProgramPipelineObject>& MaterializeProgramPipelineObject(Uint index);
|
|
void MarkProgramPipelineForDeletion(Uint index);
|
|
const SharedPtr<ProgramPipelineObject>& GetProgramPipelineObject(Uint index) const;
|
|
Uint GetBoundProgramPipelineName() const { return m_boundProgramPipeline; }
|
|
const SharedPtr<ProgramPipelineObject>& GetBoundProgramPipeline() const;
|
|
|
|
// ---- the aggregate generations (P2 brief D4) ----
|
|
//
|
|
// The bump points sit on OBJECTS - a VertexArrayObject, a TextureObject, a
|
|
// BufferObject - which have no back-pointer to the state container that owns
|
|
// them, so MGP_NOTE_AGGREGATE goes through MGPipeNoteAggregate, which finds
|
|
// the live context and lands here. This facade is the whole reason the
|
|
// objects need no back-pointer, and it is push-only so the pull build's
|
|
// GLContext is byte-identical (G1).
|
|
void NoteVaoAttributeChanged() { m_vertexArrayState.NoteAttributeChanged(); }
|
|
Uint64 GetAnyVaoAttributeGeneration() const {
|
|
return m_vertexArrayState.GetAnyAttributeGeneration();
|
|
}
|
|
void NoteFramebufferAttachmentChanged() { m_framebufferState.NoteAttachmentChanged(); }
|
|
Uint64 GetAnyFramebufferAttachmentGeneration() const {
|
|
return m_framebufferState.GetAnyAttachmentGeneration();
|
|
}
|
|
void NoteTextureContentChanged() { m_textureState.NoteTextureContentChanged(); }
|
|
Uint64 GetAnyTextureContentGeneration() const {
|
|
return m_textureState.GetAnyTextureContentGeneration();
|
|
}
|
|
void NoteTextureParamsChanged() { m_textureState.NoteTextureParamsChanged(); }
|
|
Uint64 GetAnyTextureParamsGeneration() const {
|
|
return m_textureState.GetAnyTextureParamsGeneration();
|
|
}
|
|
void NoteBufferChanged() { m_bufferState.NoteBufferChanged(); }
|
|
Uint64 GetAnyBufferChangeGeneration() const {
|
|
return m_bufferState.GetAnyBufferChangeGeneration();
|
|
}
|
|
// P5e (sb): the per-target INDEXED BINDING POINT generation, which is what dirty
|
|
// bits 15/16/17 shutter on now. See BufferState::NoteBindPointChanged for why
|
|
// the content aggregate above could not answer the question.
|
|
void NoteBufferBindPointChanged(BufferTarget target) {
|
|
m_bufferState.NoteBindPointChanged(target);
|
|
}
|
|
Uint64 GetBufferBindPointGeneration(BufferTarget target) const {
|
|
return m_bufferState.GetBindPointGeneration(target);
|
|
}
|
|
// The sixth aggregate lives here rather than on a state container because
|
|
// the values it guards do too (m_currentVertexAttributes).
|
|
void NoteVertexAttribDefaultChanged() { ++m_anyVertexAttribDefaultGeneration; }
|
|
Uint64 GetAnyVertexAttribDefaultGeneration() const {
|
|
return m_anyVertexAttribDefaultGeneration;
|
|
}
|
|
|
|
// Which of the three views of m_currentVertexAttributes[index] the last
|
|
// glVertexAttrib* write filled DIRECTLY. The other two are NUMERIC
|
|
// conversions of it (SetCurrentVertexAttribute* below), not bit copies, so
|
|
// four words on a wire are not the value unless the class travels with them:
|
|
// glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in
|
|
// floatValue. set_vertex_attrib_defaults carries this as MGPAttribValue's
|
|
// ValueClass AND, since P5c rv (CONTRACT-P5C.md §5.3), all three views
|
|
// verbatim - the applier writes each view from its own array rather than
|
|
// redoing the conversion.
|
|
//
|
|
// It is kept BESIDE the array rather than inside CurrentVertexAttributeValue
|
|
// because that struct is mirrored into PipeInputs and compared there by a
|
|
// memcmp whose size assertion (MG_Backend/MGPipe/PipeInputs.cpp) is a file
|
|
// this package does not own - and because it need not be mirrored: the class
|
|
// only decides how to REBUILD the three views, so two writes that leave the
|
|
// three views identical rebuild identically whichever class they carried.
|
|
Uint32 GetCurrentVertexAttributeClass(Uint index) const {
|
|
return index < m_currentVertexAttributeClasses.size()
|
|
? m_currentVertexAttributeClasses[index]
|
|
: kVertexAttribValueClassFloat;
|
|
}
|
|
|
|
// RenderState
|
|
Uint GetRenderStateParametersVersion() const;
|
|
// Only the pipeline-relevant subset - see RenderState::m_pipelineStateVersion.
|
|
Uint GetPipelineStateVersion() const;
|
|
const RenderStateParameters& GetRenderStateParameters() const;
|
|
void SetViewport(IntVec4 viewport); // x, y, width, height; writes ALL viewports
|
|
IntVec4 GetViewport() const; // x, y, width, height; viewport 0, rounded
|
|
void SetViewportIndexed(Uint index, FloatVec4 viewport);
|
|
const FloatVec4& GetViewportIndexed(Uint index) const;
|
|
void SetLineWidth(Float width);
|
|
Float GetLineWidth() const;
|
|
void SetPointSize(Float size);
|
|
Float GetPointSize() const;
|
|
void SetPatchVertices(Uint vertices);
|
|
Uint GetPatchVertices() const;
|
|
void SetPatchDefaultOuterLevel(const FloatVec4& levels);
|
|
const FloatVec4& GetPatchDefaultOuterLevel() const;
|
|
void SetPatchDefaultInnerLevel(const FloatVec2& levels);
|
|
const FloatVec2& GetPatchDefaultInnerLevel() const;
|
|
void SetPolygonOffset(Float factor, Float units);
|
|
void SetPolygonOffsetClamped(Float factor, Float units, Float clamp);
|
|
Float GetPolygonOffsetFactor() const;
|
|
Float GetPolygonOffsetUnits() const;
|
|
Float GetPolygonOffsetClamp() const;
|
|
void SetClipControl(GLenum origin, GLenum depth);
|
|
GLenum GetClipOrigin() const;
|
|
GLenum GetClipDepthMode() const;
|
|
void SetHint(GLenum target, GLenum mode);
|
|
GLenum GetHint(GLenum target) const;
|
|
void SetPointFadeThresholdSize(Float size);
|
|
Float GetPointFadeThresholdSize() const;
|
|
void SetPointSpriteCoordOrigin(GLenum origin);
|
|
GLenum GetPointSpriteCoordOrigin() const;
|
|
void SetClampReadColor(GLenum clamp);
|
|
GLenum GetClampReadColor() const;
|
|
void SetPolygonMode(GLenum front, GLenum back);
|
|
GLenum GetPolygonModeFront() const;
|
|
GLenum GetPolygonModeBack() const;
|
|
void SetPrimitiveRestartIndex(Uint32 index);
|
|
Uint32 GetPrimitiveRestartIndex() const;
|
|
void SetCapability(CapabilityInput cap, Bool enabled);
|
|
Bool IsCapabilityEnabled(CapabilityInput cap) const;
|
|
void SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled);
|
|
Bool IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const;
|
|
void SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha);
|
|
void GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
|
|
BlendFactor& dstAlpha) const;
|
|
void SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha,
|
|
BlendFactor dstAlpha);
|
|
void GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
|
|
BlendFactor& dstAlpha) const;
|
|
void SetBlendEquation(BlendEquation color, BlendEquation alpha);
|
|
void GetBlendEquation(BlendEquation& color, BlendEquation& alpha) const;
|
|
void SetBlendEquationIndexed(Uint index, BlendEquation color, BlendEquation alpha);
|
|
void GetBlendEquationIndexed(Uint index, BlendEquation& color, BlendEquation& alpha) const;
|
|
void SetLogicOp(LogicOperation logicOp);
|
|
LogicOperation GetLogicOp() const;
|
|
void SetDepthFunc(DepthTestFunc func);
|
|
DepthTestFunc GetDepthFunc() const;
|
|
void SetDepthMask(Bool flag);
|
|
Bool GetDepthMask() const;
|
|
void SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask);
|
|
void SetStencilMask(StencilFace face, Uint32 mask);
|
|
void SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail,
|
|
StencilOperation depthPass);
|
|
const StencilFaceState& GetStencilState(StencilFace face) const;
|
|
void SetColorMask(BoolVec4 mask);
|
|
BoolVec4 GetColorMask() const;
|
|
void SetColorMaskIndexed(Uint index, BoolVec4 mask);
|
|
BoolVec4 GetColorMaskIndexed(Uint index) const;
|
|
void SetClearColor(FloatVec4 color);
|
|
const FloatVec4& GetClearColor() const;
|
|
void SetClearDepth(Float depth);
|
|
Float GetClearDepth() const;
|
|
void SetClearStencil(Int stencil);
|
|
Uint32 GetClearStencil() const;
|
|
void SetBlendColor(FloatVec4 color);
|
|
const FloatVec4& GetBlendColor() const;
|
|
void SetDepthRange(FloatVec2 range); // writes ALL viewports' depth ranges
|
|
const FloatVec2& GetDepthRange() const;
|
|
void SetDepthRangeIndexed(Uint index, FloatVec2 range);
|
|
const FloatVec2& GetDepthRangeIndexed(Uint index) const;
|
|
void SetSampleCoverage(Float value, Bool invert);
|
|
Float GetSampleCoverageValue() const;
|
|
Bool GetSampleCoverageInvert() const;
|
|
void SetSampleMaskValue(Uint32 mask);
|
|
Uint32 GetSampleMaskValue() const;
|
|
void SetMinSampleShadingValue(Float value);
|
|
Float GetMinSampleShadingValue() const;
|
|
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
|
Int GetPixelStoreParam(PixelStoreParam param) const;
|
|
PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const;
|
|
void SetCullFaceMode(CullFaceMode mode);
|
|
CullFaceMode GetCullFaceMode() const;
|
|
void SetFrontFaceMode(FrontFaceMode mode);
|
|
FrontFaceMode GetFrontFaceMode() const;
|
|
void SetProvokingVertexMode(ProvokingVertexMode mode);
|
|
ProvokingVertexMode GetProvokingVertexMode() const;
|
|
void SetScissorBox(IntVec4 box); // x, y, width, height; writes ALL rectangles
|
|
const IntVec4& GetScissorBox() const; // x, y, width, height; rectangle 0
|
|
void SetScissorBoxIndexed(Uint index, IntVec4 box);
|
|
const IntVec4& GetScissorBoxIndexed(Uint index) const;
|
|
|
|
// Transform feedback. The fields below are the state of the transform
|
|
// feedback object currently bound to GL_TRANSFORM_FEEDBACK; see the object
|
|
// block further down for how a bind swaps them.
|
|
void BeginTransformFeedback(GLenum primitiveMode, const SharedPtr<ProgramObject>& program) {
|
|
m_transformFeedbackActive = true;
|
|
m_transformFeedbackPaused = false;
|
|
m_transformFeedbackPrimitiveMode = primitiveMode;
|
|
m_transformFeedbackProgram = program;
|
|
m_transformFeedbackGeneration = ++m_transformFeedbackNextGeneration;
|
|
m_transformFeedbackCapturedVertices = 0;
|
|
m_transformFeedbackInputPrimitives = 0;
|
|
}
|
|
void EndTransformFeedback() {
|
|
m_transformFeedbackActive = false;
|
|
m_transformFeedbackPaused = false;
|
|
m_transformFeedbackProgram.reset();
|
|
// What glDrawTransformFeedback on this object replays from now on.
|
|
auto& object = m_transformFeedbackObjects[m_boundTransformFeedback];
|
|
object.recordedVertices = m_transformFeedbackCapturedVertices;
|
|
object.hasCompletedSpan = true;
|
|
}
|
|
Bool IsTransformFeedbackActive() const { return m_transformFeedbackActive; }
|
|
Bool IsTransformFeedbackPaused() const { return m_transformFeedbackPaused; }
|
|
void SetTransformFeedbackPaused(Bool paused) { m_transformFeedbackPaused = paused; }
|
|
GLenum GetTransformFeedbackPrimitiveMode() const { return m_transformFeedbackPrimitiveMode; }
|
|
const SharedPtr<ProgramObject>& GetTransformFeedbackProgram() const {
|
|
return m_transformFeedbackProgram;
|
|
}
|
|
// Bumped on every BeginTransformFeedback; the backend uses it to
|
|
// distinguish "resume appending" from "fresh capture".
|
|
Uint64 GetTransformFeedbackGeneration() const { return m_transformFeedbackGeneration; }
|
|
// CPU-side primitive accounting for the transform feedback queries:
|
|
// every captured draw adds its primitive count (draws without a
|
|
// geometry stage write exactly what they generate).
|
|
void AddTransformFeedbackPrimitives(Uint64 primitives) {
|
|
m_transformFeedbackPrimitiveCounter += primitives;
|
|
}
|
|
Uint64 GetTransformFeedbackPrimitiveCounter() const { return m_transformFeedbackPrimitiveCounter; }
|
|
// Primitives a draw assembled while the capture was paused. GL counts those in
|
|
// PRIMITIVES_GENERATED, but a backend that answers the query with its own
|
|
// transform feedback counter cannot see them - nothing was being captured.
|
|
void AddTransformFeedbackPausedPrimitives(Uint64 primitives) {
|
|
m_transformFeedbackPausedPrimitiveCounter += primitives;
|
|
m_transformFeedbackGeneratedPrimitiveCounter += primitives;
|
|
}
|
|
Uint64 GetTransformFeedbackPausedPrimitiveCounter() const {
|
|
return m_transformFeedbackPausedPrimitiveCounter;
|
|
}
|
|
// Vertices already captured since BeginTransformFeedback (drives the
|
|
// buffer-capacity clamp on the primitives-written accounting).
|
|
void AddTransformFeedbackCapturedVertices(Uint64 vertices) {
|
|
m_transformFeedbackCapturedVertices += vertices;
|
|
}
|
|
Uint64 GetTransformFeedbackCapturedVertices() const { return m_transformFeedbackCapturedVertices; }
|
|
// Raw assembled input primitives fed to the capture stage since Begin
|
|
// (pre-clamp; drives the GS strip capture-order fixup at EndTF).
|
|
void AddTransformFeedbackInputPrimitives(Uint64 primitives) {
|
|
m_transformFeedbackInputPrimitives += primitives;
|
|
m_transformFeedbackGeneratedPrimitiveCounter += primitives;
|
|
}
|
|
Uint64 GetTransformFeedbackInputPrimitives() const { return m_transformFeedbackInputPrimitives; }
|
|
// What a GL_PRIMITIVES_GENERATED query counts over its span: every primitive the
|
|
// capture stage assembled, including the ones a paused span discarded (those are
|
|
// generated but never written). Kept as its own running total rather than derived
|
|
// from the input counter above, which BeginTransformFeedback resets per span while
|
|
// a query may cover several of them.
|
|
Uint64 GetTransformFeedbackGeneratedCounter() const {
|
|
return m_transformFeedbackGeneratedPrimitiveCounter;
|
|
}
|
|
// Capture draws whose written-primitive count the CPU accounting reproduced
|
|
// exactly, and the subset it could not: a program with a geometry stage amplifies
|
|
// by whatever the shader emits, which only the driver's own counter knows. The
|
|
// transform feedback queries diff both over their span to decide whether the CPU
|
|
// delta may stand in for the backend's GPU result (GL_Query.cpp).
|
|
void AddTransformFeedbackAccountedCaptureDraw() { ++m_transformFeedbackAccountedCaptureDraws; }
|
|
Uint64 GetTransformFeedbackAccountedCaptureDraws() const {
|
|
return m_transformFeedbackAccountedCaptureDraws;
|
|
}
|
|
void AddTransformFeedbackGeometryCaptureDraw() { ++m_transformFeedbackGeometryCaptureDraws; }
|
|
Uint64 GetTransformFeedbackGeometryCaptureDraws() const {
|
|
return m_transformFeedbackGeometryCaptureDraws;
|
|
}
|
|
|
|
// Conditional rendering (GL 4.6 core 10.9). `discard` is the verdict already
|
|
// resolved from the query object at glBeginConditionalRender - the predicate is
|
|
// read ONCE there, not per command, because GL specifies the block against the
|
|
// result available at Begin and re-reading it would let a query that is still
|
|
// being written change the answer mid-block.
|
|
void BeginConditionalRender(GLuint queryId, GLenum mode, Bool discard) {
|
|
m_conditionalRenderActive = true;
|
|
m_conditionalRenderQuery = queryId;
|
|
m_conditionalRenderMode = mode;
|
|
m_conditionalRenderDiscards = discard;
|
|
}
|
|
void EndConditionalRender() {
|
|
m_conditionalRenderActive = false;
|
|
m_conditionalRenderQuery = 0;
|
|
m_conditionalRenderMode = GL_NONE;
|
|
m_conditionalRenderDiscards = false;
|
|
}
|
|
Bool IsConditionalRenderActive() const { return m_conditionalRenderActive; }
|
|
GLuint GetConditionalRenderQuery() const { return m_conditionalRenderQuery; }
|
|
// Whether the commands GL 4.6 core 10.9 makes conditional are being discarded
|
|
// right now. False whenever no block is open, so a caller needs no second test.
|
|
Bool ConditionalRenderDiscardsCommands() const {
|
|
return m_conditionalRenderActive && m_conditionalRenderDiscards;
|
|
}
|
|
|
|
// Transform feedback objects (ARB_transform_feedback2 / GL 4.0 core).
|
|
// The capture state above and the indexed GL_TRANSFORM_FEEDBACK_BUFFER
|
|
// binding points are object state, but the context keeps exactly one live
|
|
// copy of both so that every existing reader - the backends' per-draw sync,
|
|
// the drawing and getter paths - needs no notion of which object owns them.
|
|
// A bind therefore saves the live copy into the outgoing object and restores
|
|
// the incoming one's. Object 0 is the default object and always exists.
|
|
static constexpr Uint MAX_TRANSFORM_FEEDBACK_BUFFERS = 4;
|
|
void GenTransformFeedbackNames(Uint number, Vector<Uint>& ids);
|
|
// A name glGenTransformFeedbacks handed out and glDeleteTransformFeedbacks
|
|
// has not taken back. Name 0 is always valid.
|
|
Bool ValidateTransformFeedbackName(Uint index) const;
|
|
// What glIsTransformFeedback reports: a generated name only becomes the name
|
|
// of an object once it has been bound at least once (GL 4.6 core 13.2.1).
|
|
Bool IsTransformFeedbackObject(Uint index) const;
|
|
void BindTransformFeedbackObject(Uint index);
|
|
void MarkTransformFeedbackObjectForDeletion(Uint index);
|
|
Uint GetBoundTransformFeedbackName() const { return m_boundTransformFeedback; }
|
|
// The bound object's never-reused identity, for a backend that keys a per-object
|
|
// resource on it. The NAME is not an identity: glGenTransformFeedbacks recycles a
|
|
// deleted one (LIFO), so a memo keyed on the name hands a brand-new object the dead
|
|
// one's slot. Cached rather than looked up on demand: the backend asks twice per
|
|
// captured draw, and an operator[] on m_transformFeedbackObjects would be an
|
|
// INSERT on the draw path - ska::flat_hash_map invalidates every reference into
|
|
// itself when it rehashes. The cache is refreshed by
|
|
// RestoreBoundTransformFeedbackState, which every bind (and the revert a delete
|
|
// performs) goes through, and seeded for the default object by the constructor.
|
|
// Never returns 0 - the counter starts at 1 so a zero-initialised memo slot cannot
|
|
// be mistaken for a live object.
|
|
Uint64 GetBoundTransformFeedbackLifetimeId() const { return m_boundTransformFeedbackLifetimeId; }
|
|
Uint64 GetTransformFeedbackLifetimeId(Uint index) const;
|
|
// Whether the object carrying this identity still has an OPEN capture span - one
|
|
// that glBeginTransformFeedback started and glEndTransformFeedback has not closed,
|
|
// paused or not. A backend that hands out a bounded set of per-object slots must
|
|
// never take one of these over: a paused span's counters are precisely what its
|
|
// resume reads, and GL only lets other objects capture WHILE it is paused, so the
|
|
// paused object is also the one that looks idle. An identity no live object
|
|
// carries any more (its object was deleted) answers false, which is what makes
|
|
// such a slot reclaimable.
|
|
Bool HasOpenTransformFeedbackSpan(Uint64 lifetimeId) const;
|
|
// Vertices the object captured in its last completed span; the vertex count
|
|
// glDrawTransformFeedback replays.
|
|
Uint64 GetTransformFeedbackRecordedVertices(Uint index) const;
|
|
// Whether the object has ever completed a capture span. glDrawTransformFeedback
|
|
// on an object that has not is INVALID_OPERATION, which a zero vertex count
|
|
// cannot express: an empty completed span is legal and draws nothing.
|
|
Bool HasTransformFeedbackCompletedSpan(Uint index) const;
|
|
|
|
// The by-name (direct state access) view. A named object that happens to be the
|
|
// bound one is answered from the live copy, since that is where its state actually
|
|
// is until a bind swaps it out.
|
|
void CreateTransformFeedbackObject(Uint index);
|
|
Bool IsNamedTransformFeedbackActive(Uint index) const;
|
|
Bool IsNamedTransformFeedbackPaused(Uint index) const;
|
|
NamedTransformFeedbackBinding GetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex) const;
|
|
void SetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex,
|
|
const SharedPtr<BufferObject>& buffer, Range1D range,
|
|
Bool hasExplicitRange);
|
|
|
|
// Framebuffer
|
|
void GenFramebufferNames(Uint number, Vector<Uint>& framebuffers);
|
|
const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// See FramebufferState::FindFramebufferObjectByLifetimeId - the named-blit
|
|
// consumer's handle-to-frontend resolution on a backend with no FBO twins.
|
|
SharedPtr<FramebufferObject> FindFramebufferObjectByLifetimeId(Uint64 lifetimeId) const;
|
|
#endif
|
|
BindingSlot<FramebufferObject>& GetFramebufferBindingSlot(FramebufferTarget target);
|
|
const SharedPtr<FramebufferObject>& CreateFramebufferObject(Uint index);
|
|
void MarkFramebufferObjectForDeletion(Uint index);
|
|
Bool ValidateFramebufferName(Uint index) const;
|
|
Bool ValidateFramebufferObject(Uint index) const;
|
|
|
|
// Sampler
|
|
void GenSamplerNames(Uint number, Vector<Uint>& samplers);
|
|
const SharedPtr<SamplerObject>& GetSamplerObject(Uint index);
|
|
const SharedPtr<SamplerObject>& CreateSamplerObject(Uint index);
|
|
void MarkSamplerObjectForDeletion(Uint index);
|
|
Bool ValidateSamplerName(Uint index) const;
|
|
Bool ValidateSamplerObject(Uint index) const;
|
|
|
|
// Renderbuffer
|
|
void GenRenderbufferNames(Uint number, Vector<Uint>& renderbuffers);
|
|
const SharedPtr<RenderbufferObject>& GetRenderbufferObject(Uint index);
|
|
BindingSlot<RenderbufferObject>& GetRenderbufferBindingSlot(RenderbufferTarget target);
|
|
const SharedPtr<RenderbufferObject>& CreateRenderbufferObject(Uint index);
|
|
void MarkRenderbufferObjectForDeletion(Uint index);
|
|
Bool ValidateRenderbufferName(Uint index) const;
|
|
Bool ValidateRenderbufferObject(Uint index) const;
|
|
|
|
// P1: the shader compile/link pipeline's snapshot of everything it reads from
|
|
// outside its own (stage, source) inputs. Captured lazily here because it
|
|
// cannot be captured in MG_State::Init() - that runs BEFORE MG_Backend::Init(),
|
|
// so there is no backend to query yet. Re-captured whenever the active backend
|
|
// object changes, which also rolls the fingerprint and therefore invalidates
|
|
// every P0b preprocess memo keyed against the old one. A backend whose dynamic
|
|
// capabilities become available without changing object identity must call
|
|
// InvalidateCompileEnv() after publishing them.
|
|
// GL thread only.
|
|
const SharedPtr<const MG_Util::ShaderTranspiler::CompileEnv>& GetCompileEnv();
|
|
void InvalidateCompileEnv();
|
|
|
|
private:
|
|
ShareGroupState& Group() const { return *m_shareGroup; }
|
|
|
|
// The GL objects shared with every context that shares with this one.
|
|
SharedPtr<ShareGroupState> m_shareGroup;
|
|
// State Components
|
|
ErrorState m_errorState;
|
|
BufferState m_bufferState;
|
|
VertexArrayState m_vertexArrayState;
|
|
Array<CurrentVertexAttributeValue, VertexArrayObject::MAX_VERTEX_ATTRIBS> m_currentVertexAttributes{};
|
|
Bool m_transformFeedbackActive = false;
|
|
Bool m_transformFeedbackPaused = false;
|
|
GLenum m_transformFeedbackPrimitiveMode = GL_POINTS;
|
|
SharedPtr<ProgramObject> m_transformFeedbackProgram;
|
|
Uint64 m_anyVertexAttribDefaultGeneration = 0;
|
|
// Parallel to m_currentVertexAttributes; see GetCurrentVertexAttributeClass.
|
|
Array<Uint32, VertexArrayObject::MAX_VERTEX_ATTRIBS> m_currentVertexAttributeClasses{};
|
|
Uint64 m_transformFeedbackGeneration = 0;
|
|
// Source of the per-span ids above; never rolls back with an object switch.
|
|
Uint64 m_transformFeedbackNextGeneration = 0;
|
|
// Not object state: the transform feedback queries snapshot it at BeginQuery
|
|
// and take the delta at EndQuery, which spans whatever objects were used.
|
|
Uint64 m_transformFeedbackPrimitiveCounter = 0;
|
|
Uint64 m_transformFeedbackPausedPrimitiveCounter = 0;
|
|
Uint64 m_transformFeedbackCapturedVertices = 0;
|
|
Uint64 m_transformFeedbackInputPrimitives = 0;
|
|
Uint64 m_transformFeedbackGeneratedPrimitiveCounter = 0;
|
|
Uint64 m_transformFeedbackAccountedCaptureDraws = 0;
|
|
Uint64 m_transformFeedbackGeometryCaptureDraws = 0;
|
|
|
|
// Conditional rendering. Context state, not object state: GL 4.6 core 10.9 allows
|
|
// exactly one block open at a time and no object owns it.
|
|
Bool m_conditionalRenderActive = false;
|
|
Bool m_conditionalRenderDiscards = false;
|
|
GLuint m_conditionalRenderQuery = 0;
|
|
GLenum m_conditionalRenderMode = GL_NONE;
|
|
|
|
// Process-wide, never-reused. See GetBoundTransformFeedbackLifetimeId(); same
|
|
// contract as BufferObject::AllocateLifetimeId().
|
|
static Uint64 AllocateTransformFeedbackLifetimeId();
|
|
// Everything a transform feedback object owns while it is NOT the bound one.
|
|
struct TransformFeedbackObjectState {
|
|
struct SavedBufferBinding {
|
|
SharedPtr<BufferObject> buffer;
|
|
Range1D range;
|
|
Bool hasExplicitRange = false;
|
|
};
|
|
Array<SavedBufferBinding, MAX_TRANSFORM_FEEDBACK_BUFFERS> bindings;
|
|
Bool active = false;
|
|
Bool paused = false;
|
|
GLenum primitiveMode = GL_POINTS;
|
|
SharedPtr<ProgramObject> program;
|
|
Uint64 generation = 0;
|
|
Uint64 capturedVertices = 0;
|
|
Uint64 inputPrimitives = 0;
|
|
Uint64 recordedVertices = 0;
|
|
Bool hasCompletedSpan = false;
|
|
Bool everBound = false;
|
|
// Assigned by the default member initialiser, so every way an object comes into
|
|
// being - operator[] materialisation, `= {}` in Gen/Create - gets a fresh one,
|
|
// and a recycled NAME never brings the dead object's id back with it.
|
|
Uint64 lifetimeId = AllocateTransformFeedbackLifetimeId();
|
|
};
|
|
void SaveBoundTransformFeedbackState();
|
|
void RestoreBoundTransformFeedbackState();
|
|
// operator[] materialises an entry with the default state on first touch, so
|
|
// the default object (name 0) needs no seeding here.
|
|
UnorderedMap<Uint, TransformFeedbackObjectState> m_transformFeedbackObjects;
|
|
IndexGenerator<Uint> m_transformFeedbackNames;
|
|
Uint m_boundTransformFeedback = 0;
|
|
// Mirror of m_transformFeedbackObjects[m_boundTransformFeedback].lifetimeId, so
|
|
// the per-draw read is a load rather than a hash lookup that could insert.
|
|
// Seeded by the constructor and rewritten by RestoreBoundTransformFeedbackState.
|
|
Uint64 m_boundTransformFeedbackLifetimeId = 0;
|
|
// Map membership is object EXISTENCE, which is not the same as the answer
|
|
// glIsProgramPipeline gives: any command that needs somewhere to put state
|
|
// materializes a reserved name, so the object can exist well before it is
|
|
// bound. ProgramPipelineObject::everBound carries the Is* answer.
|
|
UnorderedMap<Uint, SharedPtr<ProgramPipelineObject>> m_programPipelines;
|
|
IndexGenerator<Uint> m_programPipelineNames;
|
|
Uint m_boundProgramPipeline = 0;
|
|
TextureState m_textureState;
|
|
// The program this context has in use. Program STATE (the objects) belongs to
|
|
// the share group; which of them is current does not.
|
|
SharedPtr<ProgramObject> m_currentProgram;
|
|
RenderState m_renderState;
|
|
FramebufferState m_framebufferState;
|
|
RenderbufferState m_renderbufferState;
|
|
|
|
mutable SharedPtr<const MG_Util::ShaderTranspiler::CompileEnv> m_compileEnv;
|
|
// Identity of the backend object m_compileEnv was captured against; a plain
|
|
// pointer compare, never dereferenced.
|
|
const void* m_compileEnvBackend = nullptr;
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// See WireEpoch(); taken when the context is constructed.
|
|
Uint64 m_wireEpoch = CurrentWireEpoch();
|
|
Bool m_contextLostReported = false;
|
|
Bool m_resetStatusReported = false;
|
|
#endif
|
|
};
|
|
} // namespace GLState
|
|
|
|
// P14 S3 (docs/Disaggregated/design/11-state-ownership.md): the GL state the CALLING
|
|
// THREAD's GL entry points read and write. eglMakeCurrent points it at that EGL
|
|
// context's GLContext, and the EGL release edge puts it back to whatever it was, so a
|
|
// thread that never crossed an eglMakeCurrent keeps the process default MG_State::Init()
|
|
// gave it. NULL on a thread for which neither has happened, the same answer a process
|
|
// that never reached MG_State::Init() (the spawn server image) has always given.
|
|
extern thread_local SharedPtr<GLState::GLContext> pGLContext;
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// True on a thread whose last EGL release let go of a context lost with an ended server
|
|
// session, until it makes a context current again. The release hands such a thread the
|
|
// process default rather than the lost context's GL state (which would make the thread's
|
|
// TLS that state's last owner), so this is what keeps the thread answering as lost: an
|
|
// application that releases its context before asking glGetGraphicsResetStatus - Qt's
|
|
// render threads do - still learns that the context is gone and rebuilds it.
|
|
extern thread_local Bool tThreadReleasedLostContext;
|
|
#endif
|
|
|
|
// The fallback pGLContext starts at and an EGL release edge restores to: the process's
|
|
// one context for every thread that never bound an EGL context to itself, created on
|
|
// demand and owned here (leak-at-exit, like every other process global).
|
|
const SharedPtr<GLState::GLContext>& ProcessDefaultGLContext();
|
|
|
|
// Drops the process default context and this thread's binding to it, and the default
|
|
// framebuffer with them. Called on the teardown edge (the last eglTerminate, before
|
|
// MobileGL::Destroy()) so the objects MG_State::Init() built do not outlive
|
|
// glslang::FinalizeProcess() through a thread that kept its fallback binding.
|
|
void ReleaseProcessDefaultGLContext();
|
|
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// A fresh process default context, after a device loss has ended the session the old one
|
|
// was built against; the default framebuffer slot is emptied for MG_Impl to rebuild. The
|
|
// calling thread moves to the new context if it was on the old one. Other threads keep
|
|
// the old (lost) one until their next EGL release edge, which restores to this one.
|
|
// Returns the old one, so the caller decides where its last reference goes.
|
|
SharedPtr<GLState::GLContext> ReplaceProcessDefaultGLContext();
|
|
#endif
|
|
|
|
// P14 S3. GL framebuffer 0, built by MG_Impl once the backend is up (it needs texture
|
|
// objects, which need the backend's resource plumbing) and handed back here so every
|
|
// GLContext a later eglCreateContext builds starts with the same default framebuffer
|
|
// bound. Until the backend grows per-context native tuples there is exactly one such
|
|
// object, which is what the backend's own default-framebuffer identity tests assume.
|
|
void SetDefaultFramebuffer(SharedPtr<GLState::FramebufferObject> framebuffer);
|
|
const SharedPtr<GLState::FramebufferObject>& DefaultFramebuffer();
|
|
|
|
// True when relaxed GL semantics apply. Strict core rules are enforced only when the
|
|
// current EGL context explicitly requested a core profile (core bit in
|
|
// EGL_CONTEXT_OPENGL_PROFILE_MASK, or a >=3.1 version request without the compatibility
|
|
// bit) and MOBILEGL_RELAXED_SEMANTICS is off; no current context, legacy version
|
|
// requests, and the compatibility bit all relax.
|
|
Bool IsRelaxedSemanticsActive();
|
|
} // namespace MG_State
|
|
} // namespace MobileGL
|