diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp b/gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp new file mode 100644 index 0000000000..4ca3d47189 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/GLES1Renderer.cpp @@ -0,0 +1,1197 @@ +// +// 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. +// + +// GLES1Renderer.cpp: Implements the GLES1Renderer renderer. + +#include "libANGLE/GLES1Renderer.h" + +#include <string.h> +#include <iterator> +#include <sstream> +#include <vector> + +#include "common/hash_utils.h" +#include "libANGLE/Context.h" +#include "libANGLE/Context.inl.h" +#include "libANGLE/Program.h" +#include "libANGLE/ResourceManager.h" +#include "libANGLE/Shader.h" +#include "libANGLE/State.h" +#include "libANGLE/renderer/ContextImpl.h" + +namespace +{ +#include "libANGLE/GLES1Shaders.inc" + +uint32_t GetLogicOpUniform(const gl::FramebufferAttachment *color, gl::LogicalOperation logicOp) +{ + const uint32_t red = color->getRedSize(); + const uint32_t green = color->getGreenSize(); + const uint32_t blue = color->getBlueSize(); + const uint32_t alpha = color->getAlphaSize(); + + ASSERT(red <= 8 && green <= 8 && blue <= 8 && alpha <= 8); + + return red | green << 4 | blue << 8 | alpha << 12 | static_cast<uint32_t>(logicOp) << 16; +} +} // anonymous namespace + +namespace gl +{ +GLES1ShaderState::GLES1ShaderState() = default; +GLES1ShaderState::~GLES1ShaderState() = default; +GLES1ShaderState::GLES1ShaderState(const GLES1ShaderState &other) +{ + memcpy(this, &other, sizeof(GLES1ShaderState)); +} + +bool operator==(const GLES1ShaderState &a, const GLES1ShaderState &b) +{ + return memcmp(&a, &b, sizeof(GLES1ShaderState)) == 0; +} +bool operator!=(const GLES1ShaderState &a, const GLES1ShaderState &b) +{ + return !(a == b); +} + +size_t GLES1ShaderState::hash() const +{ + return angle::ComputeGenericHash(*this); +} + +GLES1Renderer::GLES1Renderer() : mRendererProgramInitialized(false) {} + +void GLES1Renderer::onDestroy(Context *context, State *state) +{ + if (mRendererProgramInitialized) + { + (void)state->setProgram(context, 0); + + for (const auto &iter : mUberShaderState) + { + const GLES1UberShaderState &UberShaderState = iter.second; + mShaderPrograms->deleteProgram(context, {UberShaderState.programState.program}); + } + mShaderPrograms->release(context); + mShaderPrograms = nullptr; + mRendererProgramInitialized = false; + } +} + +GLES1Renderer::~GLES1Renderer() = default; + +angle::Result GLES1Renderer::prepareForDraw(PrimitiveMode mode, Context *context, State *glState) +{ + GLES1State &gles1State = glState->gles1(); + + GLES1ShaderState::BoolTexArray &tex2DEnables = mShaderState.tex2DEnables; + GLES1ShaderState::BoolTexArray &texCubeEnables = mShaderState.texCubeEnables; + GLES1ShaderState::IntTexArray &tex2DFormats = mShaderState.tex2DFormats; + + for (int i = 0; i < kTexUnitCount; i++) + { + // GL_OES_cube_map allows only one of TEXTURE_2D / TEXTURE_CUBE_MAP + // to be enabled per unit, thankfully. From the extension text: + // + // -- Section 3.8.10 "Texture Application" + // + // Replace the beginning sentences of the first paragraph (page 138) + // with: + // + // "Texturing is enabled or disabled using the generic Enable + // and Disable commands, respectively, with the symbolic constants + // TEXTURE_2D or TEXTURE_CUBE_MAP_OES to enable the two-dimensional or cube + // map texturing respectively. If the cube map texture and the two- + // dimensional texture are enabled, then cube map texturing is used. If + // texturing is disabled, a rasterized fragment is passed on unaltered to the + // next stage of the GL (although its texture coordinates may be discarded). + // Otherwise, a texture value is found according to the parameter values of + // the currently bound texture image of the appropriate dimensionality. + + texCubeEnables[i] = gles1State.isTextureTargetEnabled(i, TextureType::CubeMap); + tex2DEnables[i] = + !texCubeEnables[i] && gles1State.isTextureTargetEnabled(i, TextureType::_2D); + + Texture *curr2DTexture = glState->getSamplerTexture(i, TextureType::_2D); + if (curr2DTexture) + { + tex2DFormats[i] = gl::GetUnsizedFormat( + curr2DTexture->getFormat(TextureTarget::_2D, 0).info->internalFormat); + } + + Texture *currCubeTexture = glState->getSamplerTexture(i, TextureType::CubeMap); + + // > If texturing is enabled for a texture unit at the time a primitive is rasterized, if + // > TEXTURE MIN FILTER is one that requires a mipmap, and if the texture image bound to the + // > enabled texture target is not complete, then it is as if texture mapping were disabled + // > for that texture unit. + if (tex2DEnables[i] && curr2DTexture && IsMipmapFiltered(curr2DTexture->getMinFilter())) + { + tex2DEnables[i] = curr2DTexture->isMipmapComplete(); + } + if (texCubeEnables[i] && currCubeTexture && + IsMipmapFiltered(currCubeTexture->getMinFilter())) + { + texCubeEnables[i] = curr2DTexture->isMipmapComplete(); + } + } + + GLES1ShaderState::IntTexArray &texEnvModes = mShaderState.texEnvModes; + GLES1ShaderState::IntTexArray &texCombineRgbs = mShaderState.texCombineRgbs; + GLES1ShaderState::IntTexArray &texCombineAlphas = mShaderState.texCombineAlphas; + GLES1ShaderState::IntTexArray &texCombineSrc0Rgbs = mShaderState.texCombineSrc0Rgbs; + GLES1ShaderState::IntTexArray &texCombineSrc0Alphas = mShaderState.texCombineSrc0Alphas; + GLES1ShaderState::IntTexArray &texCombineSrc1Rgbs = mShaderState.texCombineSrc1Rgbs; + GLES1ShaderState::IntTexArray &texCombineSrc1Alphas = mShaderState.texCombineSrc1Alphas; + GLES1ShaderState::IntTexArray &texCombineSrc2Rgbs = mShaderState.texCombineSrc2Rgbs; + GLES1ShaderState::IntTexArray &texCombineSrc2Alphas = mShaderState.texCombineSrc2Alphas; + GLES1ShaderState::IntTexArray &texCombineOp0Rgbs = mShaderState.texCombineOp0Rgbs; + GLES1ShaderState::IntTexArray &texCombineOp0Alphas = mShaderState.texCombineOp0Alphas; + GLES1ShaderState::IntTexArray &texCombineOp1Rgbs = mShaderState.texCombineOp1Rgbs; + GLES1ShaderState::IntTexArray &texCombineOp1Alphas = mShaderState.texCombineOp1Alphas; + GLES1ShaderState::IntTexArray &texCombineOp2Rgbs = mShaderState.texCombineOp2Rgbs; + GLES1ShaderState::IntTexArray &texCombineOp2Alphas = mShaderState.texCombineOp2Alphas; + + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_TEXTURE_ENVIRONMENT)) + { + for (int i = 0; i < kTexUnitCount; i++) + { + const auto &env = gles1State.textureEnvironment(i); + texEnvModes[i] = ToGLenum(env.mode); + texCombineRgbs[i] = ToGLenum(env.combineRgb); + texCombineAlphas[i] = ToGLenum(env.combineAlpha); + texCombineSrc0Rgbs[i] = ToGLenum(env.src0Rgb); + texCombineSrc0Alphas[i] = ToGLenum(env.src0Alpha); + texCombineSrc1Rgbs[i] = ToGLenum(env.src1Rgb); + texCombineSrc1Alphas[i] = ToGLenum(env.src1Alpha); + texCombineSrc2Rgbs[i] = ToGLenum(env.src2Rgb); + texCombineSrc2Alphas[i] = ToGLenum(env.src2Alpha); + texCombineOp0Rgbs[i] = ToGLenum(env.op0Rgb); + texCombineOp0Alphas[i] = ToGLenum(env.op0Alpha); + texCombineOp1Rgbs[i] = ToGLenum(env.op1Rgb); + texCombineOp1Alphas[i] = ToGLenum(env.op1Alpha); + texCombineOp2Rgbs[i] = ToGLenum(env.op2Rgb); + texCombineOp2Alphas[i] = ToGLenum(env.op2Alpha); + } + } + + bool enableClipPlanes = false; + GLES1ShaderState::BoolClipPlaneArray &clipPlaneEnables = mShaderState.clipPlaneEnables; + for (int i = 0; i < kClipPlaneCount; i++) + { + clipPlaneEnables[i] = glState->getEnableFeature(GL_CLIP_PLANE0 + i); + enableClipPlanes = enableClipPlanes || clipPlaneEnables[i]; + } + + mShaderState.mGLES1StateEnabled[GLES1StateEnables::ClipPlanes] = enableClipPlanes; + mShaderState.mGLES1StateEnabled[GLES1StateEnables::DrawTexture] = mDrawTextureEnabled; + mShaderState.mGLES1StateEnabled[GLES1StateEnables::PointRasterization] = + mode == PrimitiveMode::Points; + mShaderState.mGLES1StateEnabled[GLES1StateEnables::ShadeModelFlat] = + gles1State.mShadeModel == ShadingModel::Flat; + mShaderState.mGLES1StateEnabled[GLES1StateEnables::AlphaTest] = + glState->getEnableFeature(GL_ALPHA_TEST); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::Lighting] = + glState->getEnableFeature(GL_LIGHTING); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::RescaleNormal] = + glState->getEnableFeature(GL_RESCALE_NORMAL); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::Normalize] = + glState->getEnableFeature(GL_NORMALIZE); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::Fog] = glState->getEnableFeature(GL_FOG); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::PointSprite] = + glState->getEnableFeature(GL_POINT_SPRITE_OES); + mShaderState.mGLES1StateEnabled[GLES1StateEnables::ColorMaterial] = + glState->getEnableFeature(GL_COLOR_MATERIAL); + + // TODO (lfy@google.com): Implement two-sided lighting model (lightModel.twoSided) + mShaderState.mGLES1StateEnabled[GLES1StateEnables::LightModelTwoSided] = false; + + GLES1ShaderState::BoolTexArray &pointSpriteCoordReplaces = + mShaderState.pointSpriteCoordReplaces; + for (int i = 0; i < kTexUnitCount; i++) + { + const auto &env = gles1State.textureEnvironment(i); + pointSpriteCoordReplaces[i] = env.pointSpriteCoordReplace; + } + + GLES1ShaderState::BoolLightArray &lightEnables = mShaderState.lightEnables; + for (int i = 0; i < kLightCount; i++) + { + const auto &light = gles1State.mLights[i]; + lightEnables[i] = light.enabled; + } + + mShaderState.alphaTestFunc = gles1State.mAlphaTestFunc; + mShaderState.fogMode = gles1State.fogParameters().mode; + + const bool hasLogicOpANGLE = context->getExtensions().logicOpANGLE; + const bool hasFramebufferFetch = context->getExtensions().shaderFramebufferFetchEXT || + context->getExtensions().shaderFramebufferFetchNonCoherentEXT; + + if (!hasLogicOpANGLE && hasFramebufferFetch) + { + mShaderState.mGLES1StateEnabled[GLES1StateEnables::LogicOpThroughFramebufferFetch] = + gles1State.mLogicOpEnabled; + } + + // All the states set before this spot affect ubershader creation + + ANGLE_TRY(initializeRendererProgram(context, glState)); + + GLES1UberShaderState UberShaderState = getUberShaderState(); + + const GLES1ProgramState &programState = UberShaderState.programState; + GLES1UniformBuffers &uniformBuffers = UberShaderState.uniformBuffers; + + Program *programObject = getProgram(programState.program); + + // If anything is dirty in gles1 or the common parts of gles1/2, just redo these parts + // completely for now. + + // Feature enables + + // Texture unit enables and format info + std::array<Vec4Uniform, kTexUnitCount> texCropRects; + Vec4Uniform *cropRectBuffer = texCropRects.data(); + for (int i = 0; i < kTexUnitCount; i++) + { + Texture *curr2DTexture = glState->getSamplerTexture(i, TextureType::_2D); + if (curr2DTexture) + { + const gl::Rectangle &cropRect = curr2DTexture->getCrop(); + + GLfloat textureWidth = + static_cast<GLfloat>(curr2DTexture->getWidth(TextureTarget::_2D, 0)); + GLfloat textureHeight = + static_cast<GLfloat>(curr2DTexture->getHeight(TextureTarget::_2D, 0)); + + if (textureWidth > 0.0f && textureHeight > 0.0f) + { + cropRectBuffer[i][0] = cropRect.x / textureWidth; + cropRectBuffer[i][1] = cropRect.y / textureHeight; + cropRectBuffer[i][2] = cropRect.width / textureWidth; + cropRectBuffer[i][3] = cropRect.height / textureHeight; + } + } + } + setUniform4fv(programObject, programState.drawTextureNormalizedCropRectLoc, kTexUnitCount, + reinterpret_cast<GLfloat *>(cropRectBuffer)); + + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_LOGIC_OP) && hasLogicOpANGLE) + { + context->setLogicOpEnabled(gles1State.mLogicOpEnabled); + context->setLogicOp(gles1State.mLogicOp); + } + else if (hasFramebufferFetch) + { + const Framebuffer *drawFramebuffer = glState->getDrawFramebuffer(); + const FramebufferAttachment *colorAttachment = drawFramebuffer->getColorAttachment(0); + + if (gles1State.mLogicOpEnabled) + { + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_LOGIC_OP)) + { + // Set up uniform value for logic op + setUniform1ui(programObject, programState.logicOpLoc, + GetLogicOpUniform(colorAttachment, gles1State.mLogicOp)); + } + + // Issue a framebuffer fetch barrier if non-coherent + if (!context->getExtensions().shaderFramebufferFetchEXT) + { + context->framebufferFetchBarrier(); + } + } + } + + // Client state / current vector enables + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_CLIENT_STATE_ENABLE) || + gles1State.isDirty(GLES1State::DIRTY_GLES1_CURRENT_VECTOR)) + { + if (!gles1State.isClientStateEnabled(ClientVertexArrayType::Normal)) + { + const angle::Vector3 normal = gles1State.getCurrentNormal(); + context->vertexAttrib3f(kNormalAttribIndex, normal.x(), normal.y(), normal.z()); + } + + if (!gles1State.isClientStateEnabled(ClientVertexArrayType::Color)) + { + const ColorF color = gles1State.getCurrentColor(); + context->vertexAttrib4f(kColorAttribIndex, color.red, color.green, color.blue, + color.alpha); + } + + if (!gles1State.isClientStateEnabled(ClientVertexArrayType::PointSize)) + { + GLfloat pointSize = gles1State.mPointParameters.pointSize; + context->vertexAttrib1f(kPointSizeAttribIndex, pointSize); + } + + for (int i = 0; i < kTexUnitCount; i++) + { + if (!gles1State.mTexCoordArrayEnabled[i]) + { + const TextureCoordF texcoord = gles1State.getCurrentTextureCoords(i); + context->vertexAttrib4f(kTextureCoordAttribIndexBase + i, texcoord.s, texcoord.t, + texcoord.r, texcoord.q); + } + } + } + + // Matrices + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_MATRICES)) + { + angle::Mat4 proj = gles1State.mProjectionMatrices.back(); + setUniformMatrix4fv(programObject, programState.projMatrixLoc, 1, GL_FALSE, proj.data()); + + angle::Mat4 modelview = gles1State.mModelviewMatrices.back(); + setUniformMatrix4fv(programObject, programState.modelviewMatrixLoc, 1, GL_FALSE, + modelview.data()); + + angle::Mat4 modelviewInvTr = modelview.transpose().inverse(); + setUniformMatrix4fv(programObject, programState.modelviewInvTrLoc, 1, GL_FALSE, + modelviewInvTr.data()); + + Mat4Uniform *textureMatrixBuffer = uniformBuffers.textureMatrices.data(); + + for (int i = 0; i < kTexUnitCount; i++) + { + angle::Mat4 textureMatrix = gles1State.mTextureMatrices[i].back(); + memcpy(textureMatrixBuffer + i, textureMatrix.data(), sizeof(Mat4Uniform)); + } + + setUniformMatrix4fv(programObject, programState.textureMatrixLoc, kTexUnitCount, GL_FALSE, + reinterpret_cast<float *>(uniformBuffers.textureMatrices.data())); + } + + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_TEXTURE_ENVIRONMENT)) + { + for (int i = 0; i < kTexUnitCount; i++) + { + const auto &env = gles1State.textureEnvironment(i); + + uniformBuffers.texEnvColors[i][0] = env.color.red; + uniformBuffers.texEnvColors[i][1] = env.color.green; + uniformBuffers.texEnvColors[i][2] = env.color.blue; + uniformBuffers.texEnvColors[i][3] = env.color.alpha; + + uniformBuffers.texEnvRgbScales[i] = env.rgbScale; + uniformBuffers.texEnvAlphaScales[i] = env.alphaScale; + } + + setUniform4fv(programObject, programState.textureEnvColorLoc, kTexUnitCount, + reinterpret_cast<float *>(uniformBuffers.texEnvColors.data())); + setUniform1fv(programObject, programState.rgbScaleLoc, kTexUnitCount, + uniformBuffers.texEnvRgbScales.data()); + setUniform1fv(programObject, programState.alphaScaleLoc, kTexUnitCount, + uniformBuffers.texEnvAlphaScales.data()); + } + + // Alpha test + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_ALPHA_TEST)) + { + setUniform1f(programObject, programState.alphaTestRefLoc, gles1State.mAlphaTestRef); + } + + // Shading, materials, and lighting + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_MATERIAL)) + { + const auto &material = gles1State.mMaterial; + + setUniform4fv(programObject, programState.materialAmbientLoc, 1, material.ambient.data()); + setUniform4fv(programObject, programState.materialDiffuseLoc, 1, material.diffuse.data()); + setUniform4fv(programObject, programState.materialSpecularLoc, 1, material.specular.data()); + setUniform4fv(programObject, programState.materialEmissiveLoc, 1, material.emissive.data()); + setUniform1f(programObject, programState.materialSpecularExponentLoc, + material.specularExponent); + } + + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_LIGHTS)) + { + const auto &lightModel = gles1State.mLightModel; + + setUniform4fv(programObject, programState.lightModelSceneAmbientLoc, 1, + lightModel.color.data()); + + for (int i = 0; i < kLightCount; i++) + { + const auto &light = gles1State.mLights[i]; + memcpy(uniformBuffers.lightAmbients.data() + i, light.ambient.data(), + sizeof(Vec4Uniform)); + memcpy(uniformBuffers.lightDiffuses.data() + i, light.diffuse.data(), + sizeof(Vec4Uniform)); + memcpy(uniformBuffers.lightSpeculars.data() + i, light.specular.data(), + sizeof(Vec4Uniform)); + memcpy(uniformBuffers.lightPositions.data() + i, light.position.data(), + sizeof(Vec4Uniform)); + memcpy(uniformBuffers.lightDirections.data() + i, light.direction.data(), + sizeof(Vec3Uniform)); + uniformBuffers.spotlightExponents[i] = light.spotlightExponent; + uniformBuffers.spotlightCutoffAngles[i] = light.spotlightCutoffAngle; + uniformBuffers.attenuationConsts[i] = light.attenuationConst; + uniformBuffers.attenuationLinears[i] = light.attenuationLinear; + uniformBuffers.attenuationQuadratics[i] = light.attenuationQuadratic; + } + + setUniform4fv(programObject, programState.lightAmbientsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.lightAmbients.data())); + setUniform4fv(programObject, programState.lightDiffusesLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.lightDiffuses.data())); + setUniform4fv(programObject, programState.lightSpecularsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.lightSpeculars.data())); + setUniform4fv(programObject, programState.lightPositionsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.lightPositions.data())); + setUniform3fv(programObject, programState.lightDirectionsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.lightDirections.data())); + setUniform1fv(programObject, programState.lightSpotlightExponentsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.spotlightExponents.data())); + setUniform1fv(programObject, programState.lightSpotlightCutoffAnglesLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.spotlightCutoffAngles.data())); + setUniform1fv(programObject, programState.lightAttenuationConstsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.attenuationConsts.data())); + setUniform1fv(programObject, programState.lightAttenuationLinearsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.attenuationLinears.data())); + setUniform1fv(programObject, programState.lightAttenuationQuadraticsLoc, kLightCount, + reinterpret_cast<float *>(uniformBuffers.attenuationQuadratics.data())); + } + + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_FOG)) + { + const FogParameters &fog = gles1State.fogParameters(); + setUniform1f(programObject, programState.fogDensityLoc, fog.density); + setUniform1f(programObject, programState.fogStartLoc, fog.start); + setUniform1f(programObject, programState.fogEndLoc, fog.end); + setUniform4fv(programObject, programState.fogColorLoc, 1, fog.color.data()); + } + + // Clip planes + if (gles1State.isDirty(GLES1State::DIRTY_GLES1_CLIP_PLANES)) + { + for (int i = 0; i < kClipPlaneCount; i++) + { + gles1State.getClipPlane( + i, reinterpret_cast<float *>(uniformBuffers.clipPlanes.data() + i)); + } + + setUniform4fv(programObject, programState.clipPlanesLoc, kClipPlaneCount, + reinterpret_cast<float *>(uniformBuffers.clipPlanes.data())); + } + + // Point rasterization + { + const PointParameters &pointParams = gles1State.mPointParameters; + + setUniform1f(programObject, programState.pointSizeMinLoc, pointParams.pointSizeMin); + setUniform1f(programObject, programState.pointSizeMaxLoc, pointParams.pointSizeMax); + setUniform3fv(programObject, programState.pointDistanceAttenuationLoc, 1, + pointParams.pointDistanceAttenuation.data()); + } + + // Draw texture + { + setUniform4fv(programObject, programState.drawTextureCoordsLoc, 1, mDrawTextureCoords); + setUniform2fv(programObject, programState.drawTextureDimsLoc, 1, mDrawTextureDims); + } + + gles1State.clearDirty(); + + // None of those are changes in sampler, so there is no need to set the GL_PROGRAM dirty. + // Otherwise, put the dirtying here. + + return angle::Result::Continue; +} + +// static +int GLES1Renderer::VertexArrayIndex(ClientVertexArrayType type, const GLES1State &gles1) +{ + switch (type) + { + case ClientVertexArrayType::Vertex: + return kVertexAttribIndex; + case ClientVertexArrayType::Normal: + return kNormalAttribIndex; + case ClientVertexArrayType::Color: + return kColorAttribIndex; + case ClientVertexArrayType::PointSize: + return kPointSizeAttribIndex; + case ClientVertexArrayType::TextureCoord: + return kTextureCoordAttribIndexBase + gles1.getClientTextureUnit(); + default: + UNREACHABLE(); + return 0; + } +} + +// static +ClientVertexArrayType GLES1Renderer::VertexArrayType(int attribIndex) +{ + switch (attribIndex) + { + case kVertexAttribIndex: + return ClientVertexArrayType::Vertex; + case kNormalAttribIndex: + return ClientVertexArrayType::Normal; + case kColorAttribIndex: + return ClientVertexArrayType::Color; + case kPointSizeAttribIndex: + return ClientVertexArrayType::PointSize; + default: + if (attribIndex < kTextureCoordAttribIndexBase + kTexUnitCount) + { + return ClientVertexArrayType::TextureCoord; + } + UNREACHABLE(); + return ClientVertexArrayType::InvalidEnum; + } +} + +// static +int GLES1Renderer::TexCoordArrayIndex(unsigned int unit) +{ + return kTextureCoordAttribIndexBase + unit; +} + +void GLES1Renderer::drawTexture(Context *context, + State *glState, + float x, + float y, + float z, + float width, + float height) +{ + + // get viewport + const gl::Rectangle &viewport = glState->getViewport(); + + // Translate from viewport to NDC for feeding the shader. + // Recenter, rescale. (e.g., [0, 0, 1080, 1920] -> [-1, -1, 1, 1]) + float xNdc = scaleScreenCoordinateToNdc(x, static_cast<GLfloat>(viewport.width)); + float yNdc = scaleScreenCoordinateToNdc(y, static_cast<GLfloat>(viewport.height)); + float wNdc = scaleScreenDimensionToNdc(width, static_cast<GLfloat>(viewport.width)); + float hNdc = scaleScreenDimensionToNdc(height, static_cast<GLfloat>(viewport.height)); + + float zNdc = 2.0f * clamp(z, 0.0f, 1.0f) - 1.0f; + + mDrawTextureCoords[0] = xNdc; + mDrawTextureCoords[1] = yNdc; + mDrawTextureCoords[2] = zNdc; + + mDrawTextureDims[0] = wNdc; + mDrawTextureDims[1] = hNdc; + + mDrawTextureEnabled = true; + + AttributesMask prevAttributesMask = glState->gles1().getVertexArraysAttributeMask(); + + setAttributesEnabled(context, glState, AttributesMask()); + + glState->gles1().setAllDirty(); + + context->drawArrays(PrimitiveMode::Triangles, 0, 6); + + setAttributesEnabled(context, glState, prevAttributesMask); + + mDrawTextureEnabled = false; +} + +Shader *GLES1Renderer::getShader(ShaderProgramID handle) const +{ + return mShaderPrograms->getShader(handle); +} + +Program *GLES1Renderer::getProgram(ShaderProgramID handle) const +{ + return mShaderPrograms->getProgram(handle); +} + +angle::Result GLES1Renderer::compileShader(Context *context, + ShaderType shaderType, + const char *src, + ShaderProgramID *shaderOut) +{ + rx::ContextImpl *implementation = context->getImplementation(); + const Limitations &limitations = implementation->getNativeLimitations(); + + ShaderProgramID shader = mShaderPrograms->createShader(implementation, limitations, shaderType); + + Shader *shaderObject = getShader(shader); + ANGLE_CHECK(context, shaderObject, "Missing shader object", GL_INVALID_OPERATION); + + shaderObject->setSource(1, &src, nullptr); + shaderObject->compile(context); + + *shaderOut = shader; + + if (!shaderObject->isCompiled(context)) + { + GLint infoLogLength = shaderObject->getInfoLogLength(context); + std::vector<char> infoLog(infoLogLength, 0); + shaderObject->getInfoLog(context, infoLogLength - 1, nullptr, infoLog.data()); + + ERR() << "Internal GLES 1 shader compile failed. Info log: " << infoLog.data(); + ANGLE_CHECK(context, false, "GLES1Renderer shader compile failed.", GL_INVALID_OPERATION); + return angle::Result::Stop; + } + + return angle::Result::Continue; +} + +angle::Result GLES1Renderer::linkProgram(Context *context, + State *glState, + ShaderProgramID vertexShader, + ShaderProgramID fragmentShader, + const angle::HashMap<GLint, std::string> &attribLocs, + ShaderProgramID *programOut) +{ + ShaderProgramID program = mShaderPrograms->createProgram(context->getImplementation()); + + Program *programObject = getProgram(program); + ANGLE_CHECK(context, programObject, "Missing program object", GL_INVALID_OPERATION); + + *programOut = program; + + programObject->attachShader(getShader(vertexShader)); + programObject->attachShader(getShader(fragmentShader)); + + for (auto it : attribLocs) + { + GLint index = it.first; + const std::string &name = it.second; + programObject->bindAttributeLocation(index, name.c_str()); + } + + ANGLE_TRY(programObject->link(context)); + programObject->resolveLink(context); + + ANGLE_TRY(glState->onProgramExecutableChange(context, programObject)); + + if (!programObject->isLinked()) + { + GLint infoLogLength = programObject->getExecutable().getInfoLogLength(); + std::vector<char> infoLog(infoLogLength, 0); + programObject->getExecutable().getInfoLog(infoLogLength - 1, nullptr, infoLog.data()); + + ERR() << "Internal GLES 1 shader link failed. Info log: " << infoLog.data(); + ANGLE_CHECK(context, false, "GLES1Renderer program link failed.", GL_INVALID_OPERATION); + return angle::Result::Stop; + } + + programObject->detachShader(context, getShader(vertexShader)); + programObject->detachShader(context, getShader(fragmentShader)); + + return angle::Result::Continue; +} + +const char *GLES1Renderer::getShaderBool(GLES1StateEnables state) +{ + if (mShaderState.mGLES1StateEnabled[state]) + { + return "true"; + } + else + { + return "false"; + } +} + +void GLES1Renderer::addShaderDefine(std::stringstream &outStream, + GLES1StateEnables state, + const char *enableString) +{ + outStream << "\n"; + outStream << "#define " << enableString << " " << getShaderBool(state); +} + +void GLES1Renderer::addShaderInt(std::stringstream &outStream, const char *name, int value) +{ + outStream << "\n"; + outStream << "const int " << name << " = " << value << ";"; +} + +void GLES1Renderer::addShaderIntTexArray(std::stringstream &outStream, + const char *texString, + GLES1ShaderState::IntTexArray &texState) +{ + outStream << "\n"; + outStream << "const int " << texString << "[kMaxTexUnits] = int[kMaxTexUnits]("; + for (int i = 0; i < kTexUnitCount; i++) + { + if (i != 0) + { + outStream << ", "; + } + outStream << texState[i]; + } + outStream << ");"; +} + +void GLES1Renderer::addShaderBoolTexArray(std::stringstream &outStream, + const char *name, + GLES1ShaderState::BoolTexArray &value) +{ + outStream << std::boolalpha; + outStream << "\n"; + outStream << "bool " << name << "[kMaxTexUnits] = bool[kMaxTexUnits]("; + for (int i = 0; i < kTexUnitCount; i++) + { + if (i != 0) + { + outStream << ", "; + } + outStream << value[i]; + } + outStream << ");"; +} + +void GLES1Renderer::addShaderBoolLightArray(std::stringstream &outStream, + const char *name, + GLES1ShaderState::BoolLightArray &value) +{ + outStream << std::boolalpha; + outStream << "\n"; + outStream << "bool " << name << "[kMaxLights] = bool[kMaxLights]("; + for (int i = 0; i < kLightCount; i++) + { + if (i != 0) + { + outStream << ", "; + } + outStream << value[i]; + } + outStream << ");"; +} + +void GLES1Renderer::addShaderBoolClipPlaneArray(std::stringstream &outStream, + const char *name, + GLES1ShaderState::BoolClipPlaneArray &value) +{ + outStream << std::boolalpha; + outStream << "\n"; + outStream << "bool " << name << "[kMaxClipPlanes] = bool[kMaxClipPlanes]("; + for (int i = 0; i < kClipPlaneCount; i++) + { + if (i != 0) + { + outStream << ", "; + } + outStream << value[i]; + } + outStream << ");"; +} + +void GLES1Renderer::addVertexShaderDefs(std::stringstream &outStream) +{ + addShaderDefine(outStream, GLES1StateEnables::Lighting, "enable_lighting"); + addShaderDefine(outStream, GLES1StateEnables::ColorMaterial, "enable_color_material"); + addShaderDefine(outStream, GLES1StateEnables::DrawTexture, "enable_draw_texture"); + addShaderDefine(outStream, GLES1StateEnables::PointRasterization, "point_rasterization"); + addShaderDefine(outStream, GLES1StateEnables::RescaleNormal, "enable_rescale_normal"); + addShaderDefine(outStream, GLES1StateEnables::Normalize, "enable_normalize"); + addShaderDefine(outStream, GLES1StateEnables::LightModelTwoSided, "light_model_two_sided"); + + // bool light_enables[kMaxLights] = bool[kMaxLights](...); + addShaderBoolLightArray(outStream, "light_enables", mShaderState.lightEnables); +} + +void GLES1Renderer::addFragmentShaderDefs(std::stringstream &outStream) +{ + addShaderDefine(outStream, GLES1StateEnables::Fog, "enable_fog"); + addShaderDefine(outStream, GLES1StateEnables::ClipPlanes, "enable_clip_planes"); + addShaderDefine(outStream, GLES1StateEnables::DrawTexture, "enable_draw_texture"); + addShaderDefine(outStream, GLES1StateEnables::PointRasterization, "point_rasterization"); + addShaderDefine(outStream, GLES1StateEnables::PointSprite, "point_sprite_enabled"); + addShaderDefine(outStream, GLES1StateEnables::AlphaTest, "enable_alpha_test"); + addShaderDefine(outStream, GLES1StateEnables::ShadeModelFlat, "shade_model_flat"); + + // bool enable_texture_2d[kMaxTexUnits] = bool[kMaxTexUnits](...); + addShaderBoolTexArray(outStream, "enable_texture_2d", mShaderState.tex2DEnables); + + // bool enable_texture_cube_map[kMaxTexUnits] = bool[kMaxTexUnits](...); + addShaderBoolTexArray(outStream, "enable_texture_cube_map", mShaderState.texCubeEnables); + + // int texture_format[kMaxTexUnits] = int[kMaxTexUnits](...); + addShaderIntTexArray(outStream, "texture_format", mShaderState.tex2DFormats); + + // bool point_sprite_coord_replace[kMaxTexUnits] = bool[kMaxTexUnits](...); + addShaderBoolTexArray(outStream, "point_sprite_coord_replace", + mShaderState.pointSpriteCoordReplaces); + + // bool clip_plane_enables[kMaxClipPlanes] = bool[kMaxClipPlanes](...); + addShaderBoolClipPlaneArray(outStream, "clip_plane_enables", mShaderState.clipPlaneEnables); + + // int texture_format[kMaxTexUnits] = int[kMaxTexUnits](...); + addShaderIntTexArray(outStream, "texture_env_mode", mShaderState.texEnvModes); + + // int combine_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "combine_rgb", mShaderState.texCombineRgbs); + + // int combine_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "combine_alpha", mShaderState.texCombineAlphas); + + // int src0_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src0_rgb", mShaderState.texCombineSrc0Rgbs); + + // int src0_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src0_alpha", mShaderState.texCombineSrc0Alphas); + + // int src1_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src1_rgb", mShaderState.texCombineSrc1Rgbs); + + // int src1_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src1_alpha", mShaderState.texCombineSrc1Alphas); + + // int src2_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src2_rgb", mShaderState.texCombineSrc2Rgbs); + + // int src2_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "src2_alpha", mShaderState.texCombineSrc2Alphas); + + // int op0_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op0_rgb", mShaderState.texCombineOp0Rgbs); + + // int op0_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op0_alpha", mShaderState.texCombineOp0Alphas); + + // int op1_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op1_rgb", mShaderState.texCombineOp1Rgbs); + + // int op1_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op1_alpha", mShaderState.texCombineOp1Alphas); + + // int op2_rgb[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op2_rgb", mShaderState.texCombineOp2Rgbs); + + // int op2_alpha[kMaxTexUnits]; + addShaderIntTexArray(outStream, "op2_alpha", mShaderState.texCombineOp2Alphas); + + // int alpha_func; + addShaderInt(outStream, "alpha_func", ToGLenum(mShaderState.alphaTestFunc)); + + // int fog_mode; + addShaderInt(outStream, "fog_mode", ToGLenum(mShaderState.fogMode)); +} + +angle::Result GLES1Renderer::initializeRendererProgram(Context *context, State *glState) +{ + // See if we have the shader for this combination of states + if (mUberShaderState.find(mShaderState) != mUberShaderState.end()) + { + Program *programObject = getProgram(getUberShaderState().programState.program); + + // If this is different than the current program, we need to sync everything + // TODO: This could be optimized to only dirty state that differs between the two programs + if (glState->getProgram()->id() != programObject->id()) + { + glState->gles1().setAllDirty(); + } + + ANGLE_TRY(glState->setProgram(context, programObject)); + return angle::Result::Continue; + } + + if (!mRendererProgramInitialized) + { + mShaderPrograms = new ShaderProgramManager(); + } + + // If we get here, we don't have a shader for this state, need to create it + GLES1ProgramState &programState = mUberShaderState[mShaderState].programState; + + ShaderProgramID vertexShader; + ShaderProgramID fragmentShader; + + std::stringstream GLES1DrawVShaderStateDefs; + addVertexShaderDefs(GLES1DrawVShaderStateDefs); + + std::stringstream vertexStream; + vertexStream << kGLES1DrawVShaderHeader; + vertexStream << GLES1DrawVShaderStateDefs.str(); + vertexStream << kGLES1DrawVShader; + + ANGLE_TRY( + compileShader(context, ShaderType::Vertex, vertexStream.str().c_str(), &vertexShader)); + + std::stringstream GLES1DrawFShaderStateDefs; + addFragmentShaderDefs(GLES1DrawFShaderStateDefs); + + std::stringstream fragmentStream; + fragmentStream << kGLES1DrawFShaderVersion; + if (mShaderState.mGLES1StateEnabled[GLES1StateEnables::LogicOpThroughFramebufferFetch]) + { + if (context->getExtensions().shaderFramebufferFetchEXT) + { + fragmentStream << "#extension GL_EXT_shader_framebuffer_fetch : require\n"; + } + else + { + fragmentStream << "#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n"; + } + } + fragmentStream << kGLES1DrawFShaderHeader; + fragmentStream << GLES1DrawFShaderStateDefs.str(); + fragmentStream << kGLES1DrawFShaderUniformDefs; + if (mShaderState.mGLES1StateEnabled[GLES1StateEnables::LogicOpThroughFramebufferFetch]) + { + if (context->getExtensions().shaderFramebufferFetchEXT) + { + fragmentStream << kGLES1DrawFShaderFramebufferFetchOutputDef; + } + else + { + fragmentStream << kGLES1DrawFShaderFramebufferFetchNonCoherentOutputDef; + } + fragmentStream << kGLES1DrawFShaderLogicOpFramebufferFetchEnabled; + } + else + { + fragmentStream << kGLES1DrawFShaderOutputDef; + fragmentStream << kGLES1DrawFShaderLogicOpFramebufferFetchDisabled; + } + fragmentStream << kGLES1DrawFShaderFunctions; + fragmentStream << kGLES1DrawFShaderMultitexturing; + fragmentStream << kGLES1DrawFShaderMain; + + ANGLE_TRY(compileShader(context, ShaderType::Fragment, fragmentStream.str().c_str(), + &fragmentShader)); + + angle::HashMap<GLint, std::string> attribLocs; + + attribLocs[(GLint)kVertexAttribIndex] = "pos"; + attribLocs[(GLint)kNormalAttribIndex] = "normal"; + attribLocs[(GLint)kColorAttribIndex] = "color"; + attribLocs[(GLint)kPointSizeAttribIndex] = "pointsize"; + + for (int i = 0; i < kTexUnitCount; i++) + { + std::stringstream ss; + ss << "texcoord" << i; + attribLocs[kTextureCoordAttribIndexBase + i] = ss.str(); + } + + ANGLE_TRY(linkProgram(context, glState, vertexShader, fragmentShader, attribLocs, + &programState.program)); + + mShaderPrograms->deleteShader(context, vertexShader); + mShaderPrograms->deleteShader(context, fragmentShader); + + Program *programObject = getProgram(programState.program); + + programState.projMatrixLoc = programObject->getUniformLocation("projection"); + programState.modelviewMatrixLoc = programObject->getUniformLocation("modelview"); + programState.textureMatrixLoc = programObject->getUniformLocation("texture_matrix"); + programState.modelviewInvTrLoc = programObject->getUniformLocation("modelview_invtr"); + + for (int i = 0; i < kTexUnitCount; i++) + { + std::stringstream ss2d; + std::stringstream sscube; + + ss2d << "tex_sampler" << i; + sscube << "tex_cube_sampler" << i; + + programState.tex2DSamplerLocs[i] = programObject->getUniformLocation(ss2d.str().c_str()); + programState.texCubeSamplerLocs[i] = + programObject->getUniformLocation(sscube.str().c_str()); + } + + programState.textureEnvColorLoc = programObject->getUniformLocation("texture_env_color"); + programState.rgbScaleLoc = programObject->getUniformLocation("texture_env_rgb_scale"); + programState.alphaScaleLoc = programObject->getUniformLocation("texture_env_alpha_scale"); + + programState.alphaTestRefLoc = programObject->getUniformLocation("alpha_test_ref"); + + programState.materialAmbientLoc = programObject->getUniformLocation("material_ambient"); + programState.materialDiffuseLoc = programObject->getUniformLocation("material_diffuse"); + programState.materialSpecularLoc = programObject->getUniformLocation("material_specular"); + programState.materialEmissiveLoc = programObject->getUniformLocation("material_emissive"); + programState.materialSpecularExponentLoc = + programObject->getUniformLocation("material_specular_exponent"); + + programState.lightModelSceneAmbientLoc = + programObject->getUniformLocation("light_model_scene_ambient"); + + programState.lightAmbientsLoc = programObject->getUniformLocation("light_ambients"); + programState.lightDiffusesLoc = programObject->getUniformLocation("light_diffuses"); + programState.lightSpecularsLoc = programObject->getUniformLocation("light_speculars"); + programState.lightPositionsLoc = programObject->getUniformLocation("light_positions"); + programState.lightDirectionsLoc = programObject->getUniformLocation("light_directions"); + programState.lightSpotlightExponentsLoc = + programObject->getUniformLocation("light_spotlight_exponents"); + programState.lightSpotlightCutoffAnglesLoc = + programObject->getUniformLocation("light_spotlight_cutoff_angles"); + programState.lightAttenuationConstsLoc = + programObject->getUniformLocation("light_attenuation_consts"); + programState.lightAttenuationLinearsLoc = + programObject->getUniformLocation("light_attenuation_linears"); + programState.lightAttenuationQuadraticsLoc = + programObject->getUniformLocation("light_attenuation_quadratics"); + + programState.fogDensityLoc = programObject->getUniformLocation("fog_density"); + programState.fogStartLoc = programObject->getUniformLocation("fog_start"); + programState.fogEndLoc = programObject->getUniformLocation("fog_end"); + programState.fogColorLoc = programObject->getUniformLocation("fog_color"); + + programState.clipPlanesLoc = programObject->getUniformLocation("clip_planes"); + + programState.logicOpLoc = programObject->getUniformLocation("logic_op"); + + programState.pointSizeMinLoc = programObject->getUniformLocation("point_size_min"); + programState.pointSizeMaxLoc = programObject->getUniformLocation("point_size_max"); + programState.pointDistanceAttenuationLoc = + programObject->getUniformLocation("point_distance_attenuation"); + + programState.drawTextureCoordsLoc = programObject->getUniformLocation("draw_texture_coords"); + programState.drawTextureDimsLoc = programObject->getUniformLocation("draw_texture_dims"); + programState.drawTextureNormalizedCropRectLoc = + programObject->getUniformLocation("draw_texture_normalized_crop_rect"); + + ANGLE_TRY(glState->setProgram(context, programObject)); + + for (int i = 0; i < kTexUnitCount; i++) + { + setUniform1i(context, programObject, programState.tex2DSamplerLocs[i], i); + setUniform1i(context, programObject, programState.texCubeSamplerLocs[i], i + kTexUnitCount); + } + glState->setObjectDirty(GL_PROGRAM); + + // We just created a new program, we need to sync everything + glState->gles1().setAllDirty(); + + mRendererProgramInitialized = true; + return angle::Result::Continue; +} + +void GLES1Renderer::setUniform1i(Context *context, + Program *programObject, + UniformLocation location, + GLint value) +{ + if (location.value == -1) + return; + programObject->setUniform1iv(context, location, 1, &value); +} + +void GLES1Renderer::setUniform1ui(Program *programObject, UniformLocation location, GLuint value) +{ + if (location.value == -1) + return; + programObject->setUniform1uiv(location, 1, &value); +} + +void GLES1Renderer::setUniform1iv(Context *context, + Program *programObject, + UniformLocation location, + GLint count, + const GLint *value) +{ + if (location.value == -1) + return; + programObject->setUniform1iv(context, location, count, value); +} + +void GLES1Renderer::setUniformMatrix4fv(Program *programObject, + UniformLocation location, + GLint count, + GLboolean transpose, + const GLfloat *value) +{ + if (location.value == -1) + return; + programObject->setUniformMatrix4fv(location, count, transpose, value); +} + +void GLES1Renderer::setUniform4fv(Program *programObject, + UniformLocation location, + GLint count, + const GLfloat *value) +{ + if (location.value == -1) + return; + programObject->setUniform4fv(location, count, value); +} + +void GLES1Renderer::setUniform3fv(Program *programObject, + UniformLocation location, + GLint count, + const GLfloat *value) +{ + if (location.value == -1) + return; + programObject->setUniform3fv(location, count, value); +} + +void GLES1Renderer::setUniform2fv(Program *programObject, + UniformLocation location, + GLint count, + const GLfloat *value) +{ + if (location.value == -1) + return; + programObject->setUniform2fv(location, count, value); +} + +void GLES1Renderer::setUniform1f(Program *programObject, UniformLocation location, GLfloat value) +{ + if (location.value == -1) + return; + programObject->setUniform1fv(location, 1, &value); +} + +void GLES1Renderer::setUniform1fv(Program *programObject, + UniformLocation location, + GLint count, + const GLfloat *value) +{ + if (location.value == -1) + return; + programObject->setUniform1fv(location, count, value); +} + +void GLES1Renderer::setAttributesEnabled(Context *context, State *glState, AttributesMask mask) +{ + GLES1State &gles1 = glState->gles1(); + + ClientVertexArrayType nonTexcoordArrays[] = { + ClientVertexArrayType::Vertex, + ClientVertexArrayType::Normal, + ClientVertexArrayType::Color, + ClientVertexArrayType::PointSize, + }; + + for (const ClientVertexArrayType attrib : nonTexcoordArrays) + { + int index = VertexArrayIndex(attrib, glState->gles1()); + + if (mask.test(index)) + { + gles1.setClientStateEnabled(attrib, true); + context->enableVertexAttribArray(index); + } + else + { + gles1.setClientStateEnabled(attrib, false); + context->disableVertexAttribArray(index); + } + } + + for (unsigned int i = 0; i < kTexUnitCount; i++) + { + int index = TexCoordArrayIndex(i); + + if (mask.test(index)) + { + gles1.setTexCoordArrayEnabled(i, true); + context->enableVertexAttribArray(index); + } + else + { + gles1.setTexCoordArrayEnabled(i, false); + context->disableVertexAttribArray(index); + } + } +} + +} // namespace gl |