mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-01-14 06:51:08 +00:00
1186 lines
40 KiB
C++
1186 lines
40 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "DeviceGraphics.h"
|
|
#include "VertexBuffer.h"
|
|
#include "IndexBuffer.h"
|
|
#include "FrameBuffer.h"
|
|
#include "GraphicsHandle.h"
|
|
#include "Texture2D.h"
|
|
#include "RenderTarget.h"
|
|
#include "Program.h"
|
|
#include "GFXUtils.h"
|
|
|
|
#include "platform/CCPlatformConfig.h"
|
|
#include "base/CCGLUtils.h"
|
|
|
|
RENDERER_BEGIN
|
|
|
|
static_assert(sizeof(int) == sizeof(GLint), "ERROR: GLint isn't equal to int!");
|
|
static_assert(sizeof(float) == sizeof(GLfloat), "ERROR: GLfloat isn't equal to float!");
|
|
|
|
namespace
|
|
{
|
|
void attach(GLenum location, const RenderTarget* target)
|
|
{
|
|
if (nullptr != dynamic_cast<const Texture2D*>(target))
|
|
{
|
|
GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, location, GL_TEXTURE_2D, target->getHandle(), 0));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, location, GL_RENDERBUFFER, target->getHandle()));
|
|
}
|
|
}
|
|
|
|
static DeviceGraphics* __instance = nullptr;
|
|
} // namespace {
|
|
|
|
void DeviceGraphics::destroy() {
|
|
if (__instance) {
|
|
__instance->release();
|
|
delete __instance;
|
|
__instance = nullptr;
|
|
}
|
|
}
|
|
|
|
DeviceGraphics* DeviceGraphics::getInstance()
|
|
{
|
|
if (__instance == nullptr) {
|
|
__instance = new (std::nothrow) DeviceGraphics();
|
|
__instance->retain();
|
|
}
|
|
return __instance;
|
|
}
|
|
|
|
void DeviceGraphics::setFrameBuffer(const FrameBuffer* fb)
|
|
{
|
|
if (fb == _frameBuffer)
|
|
return;
|
|
|
|
// should reset view port rect when switch frame buffer
|
|
_vx = _vy = _vw = _vh = 0;
|
|
|
|
RENDERER_SAFE_RELEASE(_frameBuffer);
|
|
_frameBuffer = const_cast<FrameBuffer*>(fb);
|
|
RENDERER_SAFE_RETAIN(_frameBuffer);
|
|
|
|
if (nullptr == fb)
|
|
{
|
|
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, _defaultFbo));
|
|
return;
|
|
}
|
|
|
|
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fb->getHandle()));
|
|
|
|
int i = 0;
|
|
const auto& colorBuffers = fb->getColorBuffers();
|
|
for (const auto& colorBuffer : colorBuffers)
|
|
attach(GL_COLOR_ATTACHMENT0 + i, colorBuffer);
|
|
for (i = static_cast<int>(colorBuffers.size()); i < _caps.maxColorAttatchments; ++i)
|
|
{
|
|
GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0));
|
|
}
|
|
if (0 == colorBuffers.size())
|
|
{
|
|
// If not draw buffer is needed, should invoke this line explicitly, or it will cause
|
|
// GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER and GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER error.
|
|
// https://stackoverflow.com/questions/28313782/porting-opengl-es-framebuffer-to-opengl
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
|
|
glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
#endif
|
|
}
|
|
|
|
if (_frameBuffer->getDepthBuffer())
|
|
attach(GL_DEPTH_ATTACHMENT, _frameBuffer->getDepthBuffer());
|
|
|
|
if (_frameBuffer->getStencilBuffer())
|
|
attach(GL_STENCIL_ATTACHMENT, _frameBuffer->getStencilBuffer());
|
|
|
|
// if (_frameBuffer->getDepthStencilBuffer())
|
|
// attach(GL_DEPTH_STENCIL_ATTACHMENT, _frameBuffer->getDepthStencilBuffer());
|
|
|
|
auto result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (GL_FRAMEBUFFER_COMPLETE != result)
|
|
RENDERER_LOGE("Framebuffer status error: 0x%x", result);
|
|
}
|
|
|
|
void DeviceGraphics::setViewport(int x, int y, int w, int h)
|
|
{
|
|
if (_vx != x ||
|
|
_vy != y ||
|
|
_vw != w ||
|
|
_vh != h)
|
|
{
|
|
_vx = x;
|
|
_vy = y;
|
|
_vw = w;
|
|
_vh = h;
|
|
GL_CHECK(ccViewport(_vx, _vy, _vw, _vh));
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::setScissor(int x, int y, int w, int h)
|
|
{
|
|
if (_sx != x ||
|
|
_sy != y ||
|
|
_sw != w ||
|
|
_sh != h)
|
|
{
|
|
_sx = x;
|
|
_sy = y;
|
|
_sw = w;
|
|
_sh = h;
|
|
ccScissor(_sx, _sy, _sw, _sh);
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::clear(uint8_t flags, Color4F *color, double depth, int32_t stencil)
|
|
{
|
|
GLbitfield mask = 0;
|
|
if (flags & ClearFlag::COLOR)
|
|
{
|
|
mask |= GL_COLOR_BUFFER_BIT;
|
|
GL_CHECK(glClearColor(color->r, color->g, color->b, color->a));
|
|
}
|
|
|
|
if (flags & ClearFlag::DEPTH)
|
|
{
|
|
mask |= GL_DEPTH_BUFFER_BIT;
|
|
GL_CHECK(glClearDepth(depth));
|
|
|
|
GL_CHECK(glEnable(GL_DEPTH_TEST));
|
|
GL_CHECK(glDepthMask(GL_TRUE));
|
|
GL_CHECK(glDepthFunc(GL_ALWAYS));
|
|
}
|
|
|
|
if (flags & ClearFlag::STENCIL)
|
|
{
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|
GL_CHECK(glClearStencil(stencil));
|
|
}
|
|
|
|
GL_CHECK(glClear(mask));
|
|
|
|
// Restore depth related state.
|
|
if (flags & ClearFlag::DEPTH)
|
|
{
|
|
if (!_currentState->depthTest)
|
|
{
|
|
GL_CHECK(glDisable(GL_DEPTH_TEST));
|
|
}
|
|
else
|
|
{
|
|
if (!_currentState->depthWrite)
|
|
{
|
|
GL_CHECK(glDepthMask(GL_FALSE));
|
|
}
|
|
if (_currentState->depthFunc != DepthFunc::ALWAYS)
|
|
{
|
|
GL_CHECK(glDepthFunc(static_cast<GLenum>(_currentState->depthFunc)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::enableBlend()
|
|
{
|
|
_nextState->blend = true;
|
|
}
|
|
|
|
void DeviceGraphics::enableDepthTest()
|
|
{
|
|
_nextState->depthTest = true;
|
|
}
|
|
|
|
void DeviceGraphics::enableDepthWrite()
|
|
{
|
|
_nextState->depthWrite = true;
|
|
}
|
|
|
|
void DeviceGraphics::enableStencilTest()
|
|
{
|
|
_nextState->stencilTest = true;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilFunc(StencilFunc func, int ref, unsigned int mask)
|
|
{
|
|
_nextState->stencilSeparation = false;
|
|
_nextState->stencilFuncFront = _nextState->stencilFuncBack = func;
|
|
_nextState->stencilRefFront = _nextState->stencilRefBack = ref;
|
|
_nextState->stencilMaskFront = _nextState->stencilMaskBack = mask;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilFuncFront(StencilFunc func, int ref, unsigned int mask)
|
|
{
|
|
_nextState->stencilSeparation = true;
|
|
_nextState->stencilFuncFront = func;
|
|
_nextState->stencilRefFront = ref;
|
|
_nextState->stencilMaskFront = mask;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilFuncBack(StencilFunc func, int ref, unsigned int mask)
|
|
{
|
|
_nextState->stencilSeparation = true;
|
|
_nextState->stencilFuncBack = func;
|
|
_nextState->stencilRefBack = ref;
|
|
_nextState->stencilMaskBack = mask;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilOp(StencilOp failOp, StencilOp zFailOp, StencilOp zPassOp, unsigned int writeMask)
|
|
{
|
|
_nextState->stencilFailOpFront = _nextState->stencilFailOpBack = failOp;
|
|
_nextState->stencilZFailOpFront = _nextState->stencilZFailOpBack = zFailOp;
|
|
_nextState->stencilZPassOpFront = _nextState->stencilZPassOpBack = zPassOp;
|
|
_nextState->stencilWriteMaskFront = _nextState->stencilWriteMaskBack = writeMask;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilOpFront(StencilOp failOp, StencilOp zFailOp, StencilOp zPassOp, unsigned int writeMask)
|
|
{
|
|
_nextState->stencilSeparation = true;
|
|
_nextState->stencilFailOpFront = failOp;
|
|
_nextState->stencilZFailOpFront = zFailOp;
|
|
_nextState->stencilZPassOpFront = zPassOp;
|
|
_nextState->stencilWriteMaskFront = writeMask;
|
|
}
|
|
|
|
void DeviceGraphics::setStencilOpBack(StencilOp failOp, StencilOp zFailOp, StencilOp zPassOp, unsigned int writeMask)
|
|
{
|
|
_nextState->stencilSeparation = true;
|
|
_nextState->stencilFailOpBack = failOp;
|
|
_nextState->stencilZFailOpBack = zFailOp;
|
|
_nextState->stencilZPassOpBack = zPassOp;
|
|
_nextState->stencilWriteMaskBack = writeMask;
|
|
}
|
|
|
|
void DeviceGraphics::setDepthFunc(DepthFunc func)
|
|
{
|
|
_nextState->depthFunc = func;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendColor(uint32_t rgba)
|
|
{
|
|
_nextState->blendColor = rgba;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
|
{
|
|
_nextState->blendColor = (r << 24) | (g << 16) | (b << 8) | a;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendFunc(BlendFactor src, BlendFactor dst)
|
|
{
|
|
_nextState->blendSeparation = false;
|
|
_nextState->blendSrc = src;
|
|
_nextState->blendDst = dst;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendFuncSeparate(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha)
|
|
{
|
|
_nextState->blendSeparation = true;
|
|
_nextState->blendSrc = srcRGB;
|
|
_nextState->blendDst = dstRGB;
|
|
_nextState->blendSrcAlpha = srcAlpha;
|
|
_nextState->blendDstAlpha = dstAlpha;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendEquation(BlendOp mode)
|
|
{
|
|
_nextState->blendSeparation = false;
|
|
_nextState->blendEq = mode;
|
|
}
|
|
|
|
void DeviceGraphics::setBlendEquationSeparate(BlendOp modeRGB, BlendOp modeAlpha)
|
|
{
|
|
_nextState->blendSeparation = true;
|
|
_nextState->blendEq = modeRGB;
|
|
_nextState->blendAlphaEq = modeAlpha;
|
|
}
|
|
|
|
void DeviceGraphics::setCullMode(CullMode mode)
|
|
{
|
|
_nextState->cullMode = mode;
|
|
}
|
|
|
|
void DeviceGraphics::setVertexBuffer(int stream, VertexBuffer* buffer, int start /*= 0*/)
|
|
{
|
|
_nextState->setVertexBuffer(stream, buffer);
|
|
_nextState->setVertexBufferOffset(stream, start);
|
|
|
|
if (_nextState->maxStream < stream) {
|
|
_nextState->maxStream = stream;
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::setIndexBuffer(IndexBuffer *buffer)
|
|
{
|
|
_nextState->setIndexBuffer(buffer);
|
|
}
|
|
|
|
void DeviceGraphics::setProgram(Program *program)
|
|
{
|
|
_nextState->setProgram(program);
|
|
}
|
|
|
|
void DeviceGraphics::setTexture(size_t hashName, Texture* texture, int slot)
|
|
{
|
|
if (slot >= _caps.maxTextureUnits)
|
|
{
|
|
RENDERER_LOGW("Can not set texture %zu at stage %d, max texture exceed: %d",
|
|
hashName, slot, _caps.maxTextureUnits);
|
|
return;
|
|
}
|
|
|
|
_nextState->setTexture(slot, texture);
|
|
setUniformi(hashName, slot);
|
|
}
|
|
|
|
void DeviceGraphics::setTextureArray(size_t hashName, const std::vector<Texture*>& textures, const std::vector<int>& slots)
|
|
{
|
|
auto len = textures.size();
|
|
if (len >= _caps.maxTextureUnits)
|
|
{
|
|
RENDERER_LOGW("Can not set %d textures for %zu, max texture exceed: %d",
|
|
(int)len, hashName, _caps.maxTextureUnits);
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
auto slot = slots[i];
|
|
_nextState->setTexture(slot, textures[i]);
|
|
}
|
|
|
|
setUniformiv(hashName, slots.size(), slots.data(), slots.size());
|
|
}
|
|
|
|
void DeviceGraphics::setPrimitiveType(PrimitiveType type)
|
|
{
|
|
_nextState->primitiveType = type;
|
|
}
|
|
|
|
void DeviceGraphics::draw(size_t base, GLsizei count)
|
|
{
|
|
commitBlendStates();
|
|
commitDepthStates();
|
|
commitStencilStates();
|
|
commitCullMode();
|
|
commitVertexBuffer();
|
|
|
|
auto nextIndexBuffer = _nextState->getIndexBuffer();
|
|
if (_currentState->getIndexBuffer() != nextIndexBuffer)
|
|
{
|
|
GL_CHECK(ccBindBuffer(GL_ELEMENT_ARRAY_BUFFER, nextIndexBuffer ? nextIndexBuffer->getHandle() : 0));
|
|
}
|
|
|
|
//commit program
|
|
bool programDirty = false;
|
|
if (_currentState->getProgram() != _nextState->getProgram())
|
|
{
|
|
if (_nextState->getProgram()->isLinked())
|
|
{
|
|
GL_CHECK(glUseProgram(_nextState->getProgram()->getHandle()));
|
|
}
|
|
else
|
|
RENDERER_LOGW("Failed to use program: has not linked yet.");
|
|
|
|
programDirty = true;
|
|
}
|
|
|
|
commitTextures();
|
|
|
|
//commit uniforms
|
|
const auto& uniformsInfo = _nextState->getProgram()->getUniforms();
|
|
for (const auto& uniformInfo : uniformsInfo)
|
|
{
|
|
auto iter = _uniforms.find(uniformInfo.hashName);
|
|
if (_uniforms.end() == iter)
|
|
continue;
|
|
|
|
auto& uniform = iter->second;
|
|
if (!programDirty && !uniform.dirty)
|
|
continue;
|
|
|
|
uniform.dirty = false;
|
|
uniformInfo.setUniform(uniform.value, uniform.elementType, uniform.count);
|
|
}
|
|
|
|
// draw primitives
|
|
if (nextIndexBuffer)
|
|
{
|
|
GL_CHECK(glDrawElements(ENUM_CLASS_TO_GLENUM(_nextState->primitiveType),
|
|
count,
|
|
ENUM_CLASS_TO_GLENUM(nextIndexBuffer->getFormat()),
|
|
(GLvoid *)(base * nextIndexBuffer->getBytesPerIndex())));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glDrawArrays(ENUM_CLASS_TO_GLENUM(_nextState->primitiveType), (GLint)base, count));
|
|
}
|
|
|
|
_drawCalls++;
|
|
|
|
auto temp = _nextState;
|
|
_nextState = _currentState;
|
|
_currentState = temp;
|
|
|
|
_nextState->reset();
|
|
}
|
|
|
|
void DeviceGraphics::setUniform(size_t hashName, const void* v, size_t bytes, UniformElementType elementType, size_t uniformCount)
|
|
{
|
|
auto iter = _uniforms.find(hashName);
|
|
if (iter == _uniforms.end())
|
|
{
|
|
_uniforms[hashName] = Uniform(v, bytes, elementType, uniformCount);
|
|
}
|
|
else
|
|
{
|
|
auto& uniform = iter->second;
|
|
uniform.dirty = true;
|
|
uniform.setValue(v, bytes, uniformCount);
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::setUniformi(size_t hashName, int i1)
|
|
{
|
|
setUniform(hashName, &i1, sizeof(int), UniformElementType::INT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformi(size_t hashName, int i1, int i2)
|
|
{
|
|
int tempValue[] = {i1, i2};
|
|
setUniform(hashName, tempValue, 2 * sizeof(int), UniformElementType::INT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformi(size_t hashName, int i1, int i2, int i3)
|
|
{
|
|
int tempValue[] = {i1, i2, i3};
|
|
setUniform(hashName, tempValue, 3 * sizeof(int), UniformElementType::INT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformi(size_t hashName, int i1, int i2, int i3, int i4)
|
|
{
|
|
int tempValue[] = {i1, i2, i3, i4};
|
|
setUniform(hashName, tempValue, 4 * sizeof(int), UniformElementType::INT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformiv(size_t hashName, size_t elementCount, const int* value, size_t uniformCount)
|
|
{
|
|
setUniform(hashName, value, elementCount * sizeof(int), UniformElementType::INT, uniformCount);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformf(size_t hashName, float f1)
|
|
{
|
|
setUniform(hashName, &f1, sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformf(size_t hashName, float f1, float f2)
|
|
{
|
|
float tempValue[] = {f1, f2};
|
|
setUniform(hashName, tempValue, 2 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformf(size_t hashName, float f1, float f2, float f3)
|
|
{
|
|
float tempValue[] = {f1, f2, f3};
|
|
setUniform(hashName, tempValue, 3 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformf(size_t hashName, float f1, float f2, float f3, float f4)
|
|
{
|
|
float tempValue[] = {f1, f2, f3, f4};
|
|
setUniform(hashName, tempValue, 4 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformfv(size_t hashName, size_t elementCount, const float* value, size_t uniformCount)
|
|
{
|
|
setUniform(hashName, value, elementCount * sizeof(float), UniformElementType::FLOAT, uniformCount);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformVec2(size_t hashName, const cocos2d::Vec2& value)
|
|
{
|
|
setUniform(hashName, &value, sizeof(Vec2), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformVec3(size_t hashName, const cocos2d::Vec3& value)
|
|
{
|
|
setUniform(hashName, &value, sizeof(Vec3), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformVec4(size_t hashName, const cocos2d::Vec4& value)
|
|
{
|
|
setUniform(hashName, &value, sizeof(Vec4), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformMat2(size_t hashName, float* value)
|
|
{
|
|
setUniform(hashName, value, 4 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformMat3(size_t hashName, float* value)
|
|
{
|
|
setUniform(hashName, value, 9 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformMat4(size_t hashName, float* value)
|
|
{
|
|
setUniform(hashName, value, 16 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
void DeviceGraphics::setUniformMat4(size_t hashName, const cocos2d::Mat4& value)
|
|
{
|
|
setUniform(hashName, value.m, 16 * sizeof(float), UniformElementType::FLOAT);
|
|
}
|
|
|
|
//
|
|
// Priviate funcitons.
|
|
//
|
|
|
|
DeviceGraphics::DeviceGraphics()
|
|
: _vx(0)
|
|
, _vy(0)
|
|
, _vw(0)
|
|
, _vh(0)
|
|
, _sx(0)
|
|
, _sy(0)
|
|
, _sw(0)
|
|
, _sh(0)
|
|
, _frameBuffer(nullptr)
|
|
{
|
|
initCaps();
|
|
initStates();
|
|
|
|
_newAttributes.resize(_caps.maxVertexAttributes);
|
|
_enabledAtrributes.resize(_caps.maxVertexAttributes);
|
|
|
|
|
|
_currentState = new State();
|
|
_nextState = new State();
|
|
|
|
// Make sure _currentState and _nextState have enough sapce for textures.
|
|
_currentState->setTexture(_caps.maxTextureUnits, nullptr);
|
|
_nextState->setTexture(_caps.maxTextureUnits, nullptr);
|
|
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFbo);
|
|
}
|
|
|
|
DeviceGraphics::~DeviceGraphics()
|
|
{
|
|
RENDERER_SAFE_RELEASE(_frameBuffer);
|
|
|
|
delete _currentState;
|
|
delete _nextState;
|
|
|
|
_currentState = _nextState = nullptr;
|
|
}
|
|
|
|
void DeviceGraphics::initCaps()
|
|
{
|
|
GL_CHECK(glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &_caps.maxVextexTextures));
|
|
GL_CHECK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &_caps.maxVertexAttributes));
|
|
// Need to emulate MAX_FRAGMENT/VERTEX_UNIFORM_VECTORS and MAX_VARYING_VECTORS
|
|
// because desktop GL's corresponding queries return the number of components
|
|
// whereas GLES2 return the number of vectors (each vector has 4 components).
|
|
// Therefore, the value returned by desktop GL needs to be divided by 4.
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
|
GL_CHECK(glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &_caps.maxFragUniforms));
|
|
_caps.maxFragUniforms /= 4;
|
|
#else
|
|
GL_CHECK(glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &_caps.maxFragUniforms));
|
|
#endif
|
|
|
|
GL_CHECK(glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &_caps.maxTextureUnits));
|
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS ||CC_TARGET_PLATFORM == CC_PLATFORM_OPENHARMONY)
|
|
// IDEA: how to get these infomations
|
|
_caps.maxColorAttatchments = 1;
|
|
_caps.maxDrawBuffers = 1;
|
|
#else
|
|
GL_CHECK(glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &_caps.maxColorAttatchments));
|
|
GL_CHECK(glGetIntegerv(GL_MAX_DRAW_BUFFERS, &_caps.maxDrawBuffers));
|
|
#endif
|
|
|
|
RENDERER_LOGD("Device caps: maxVextexTextures: %d, maxFragUniforms: %d, maxTextureUints: %d, maxVertexAttributes: %d, maxDrawBuffers: %d, maxColorAttatchments: %d",
|
|
_caps.maxVextexTextures, _caps.maxFragUniforms, _caps.maxTextureUnits, _caps.maxVertexAttributes, _caps.maxDrawBuffers, _caps.maxColorAttatchments);
|
|
}
|
|
|
|
void DeviceGraphics::initStates()
|
|
{
|
|
GL_CHECK(glDisable(GL_BLEND));
|
|
GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO));
|
|
GL_CHECK(glBlendEquation(GL_FUNC_ADD));
|
|
GL_CHECK(glBlendColor(1, 1, 1, 1));
|
|
|
|
GL_CHECK(glColorMask(true, true, true, true));
|
|
|
|
GL_CHECK(glEnable(GL_CULL_FACE));
|
|
GL_CHECK(glCullFace(GL_BACK));
|
|
|
|
GL_CHECK(glDisable(GL_DEPTH_TEST));
|
|
GL_CHECK(glDepthFunc(GL_LESS));
|
|
GL_CHECK(glDepthMask(GL_FALSE));
|
|
GL_CHECK(glDisable(GL_POLYGON_OFFSET_FILL));
|
|
GL_CHECK(glDepthRange(0, 1));
|
|
|
|
GL_CHECK(glDisable(GL_STENCIL_TEST));
|
|
GL_CHECK(glStencilFunc(GL_ALWAYS, 0, 0xff));
|
|
GL_CHECK(glStencilMask(0xff));
|
|
GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
|
|
|
GL_CHECK(glClearDepth(1));
|
|
GL_CHECK(glClearColor(0, 0, 0, 0));
|
|
GL_CHECK(glClearStencil(0));
|
|
|
|
GL_CHECK(glDisable(GL_SCISSOR_TEST));
|
|
}
|
|
|
|
void DeviceGraphics::restoreTexture(uint32_t index)
|
|
{
|
|
auto texture = _currentState->getTexture(index);
|
|
if (texture)
|
|
{
|
|
GL_CHECK(glBindTexture(texture->getTarget(), texture->getHandle()));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::restoreIndexBuffer()
|
|
{
|
|
auto ib = _currentState->getIndexBuffer();
|
|
GL_CHECK(ccBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib ? ib->getHandle(): 0));
|
|
}
|
|
|
|
void DeviceGraphics::commitBlendStates()
|
|
{
|
|
if (_currentState->blend != _nextState->blend)
|
|
{
|
|
if (!_nextState->blend)
|
|
{
|
|
glDisable(GL_BLEND);
|
|
return;
|
|
}
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
if (_nextState->blendSrc == BlendFactor::CONSTANT_COLOR ||
|
|
_nextState->blendSrc == BlendFactor::ONE_MINUS_CONSTANT_COLOR ||
|
|
_nextState->blendDst == BlendFactor::CONSTANT_COLOR ||
|
|
_nextState->blendDst == BlendFactor::ONE_MINUS_CONSTANT_COLOR)
|
|
{
|
|
GL_CHECK(glBlendColor((_nextState->blendColor >> 24) / 255.f,
|
|
(_nextState->blendColor >> 16 & 0xff) / 255.f,
|
|
(_nextState->blendColor >> 8 & 0xff) / 255.f,
|
|
(_nextState->blendColor & 0xff) / 255.f));
|
|
|
|
}
|
|
|
|
if (_nextState->blendSeparation)
|
|
{
|
|
GL_CHECK(glBlendFuncSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendSrcAlpha),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDstAlpha)));
|
|
GL_CHECK(glBlendEquationSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendEq),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendAlphaEq)));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glBlendFunc(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst)));
|
|
GL_CHECK(glBlendEquation(ENUM_CLASS_TO_GLENUM(_nextState->blendEq)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_nextState->blend == false)
|
|
return;
|
|
|
|
if (_currentState->blendColor != _nextState->blendColor)
|
|
glBlendColor((_nextState->blendColor >> 24) / 255.f,
|
|
(_nextState->blendColor >> 16 & 0xff) / 255.f,
|
|
(_nextState->blendColor >> 8 & 0xff) / 255.f,
|
|
(_nextState->blendColor & 0xff) / 255.f);
|
|
|
|
if (_currentState->blendSeparation != _nextState->blendSeparation)
|
|
{
|
|
if (_nextState->blendSeparation)
|
|
{
|
|
GL_CHECK(glBlendFuncSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendSrcAlpha),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDstAlpha)));
|
|
GL_CHECK(glBlendEquationSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendEq),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendAlphaEq)));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glBlendFunc(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst)));
|
|
GL_CHECK(glBlendEquation(ENUM_CLASS_TO_GLENUM(_nextState->blendEq)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_nextState->blendSeparation)
|
|
{
|
|
if (_currentState->blendSrc != _nextState->blendSrc ||
|
|
_currentState->blendDst != _nextState->blendDst ||
|
|
_currentState->blendSrcAlpha != _nextState->blendSrcAlpha ||
|
|
_currentState->blendDstAlpha != _nextState->blendDstAlpha)
|
|
{
|
|
GL_CHECK(glBlendFuncSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendSrcAlpha),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDstAlpha)));
|
|
}
|
|
if (_currentState->blendEq != _nextState->blendEq ||
|
|
_currentState->blendAlphaEq != _nextState->blendAlphaEq)
|
|
{
|
|
GL_CHECK(glBlendEquationSeparate(ENUM_CLASS_TO_GLENUM(_nextState->blendEq),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendAlphaEq)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_currentState->blendSrc != _nextState->blendSrc ||
|
|
_currentState->blendDst != _nextState->blendDst)
|
|
{
|
|
GL_CHECK(glBlendFunc(ENUM_CLASS_TO_GLENUM(_nextState->blendSrc),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->blendDst)));
|
|
}
|
|
|
|
if (_currentState->blendEq != _nextState->blendEq)
|
|
{
|
|
GL_CHECK(glBlendEquation(ENUM_CLASS_TO_GLENUM(_nextState->blendEq)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::commitDepthStates()
|
|
{
|
|
if (_currentState->depthTest != _nextState->depthTest)
|
|
{
|
|
if (!_nextState->depthTest)
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
return;
|
|
}
|
|
|
|
GL_CHECK(glEnable(GL_DEPTH_TEST));
|
|
GL_CHECK(glDepthFunc(ENUM_CLASS_TO_GLENUM(_nextState->depthFunc)));
|
|
GL_CHECK(glDepthMask(_nextState->depthWrite ? GL_TRUE : GL_FALSE));
|
|
|
|
return;
|
|
}
|
|
|
|
if (_currentState->depthWrite != _nextState->depthWrite)
|
|
{
|
|
GL_CHECK(glDepthMask(_nextState->depthWrite ? GL_TRUE : GL_FALSE));
|
|
}
|
|
|
|
if (!_nextState->depthTest)
|
|
{
|
|
if (_nextState->depthWrite)
|
|
{
|
|
_nextState->depthTest = true;
|
|
_nextState->depthFunc = DepthFunc::ALWAYS;
|
|
|
|
GL_CHECK(glEnable(GL_DEPTH_TEST));
|
|
GL_CHECK(glDepthFunc(ENUM_CLASS_TO_GLENUM(_nextState->depthFunc)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_currentState->depthFunc != _nextState->depthFunc)
|
|
{
|
|
GL_CHECK(glDepthFunc(ENUM_CLASS_TO_GLENUM(_nextState->depthFunc)));
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::commitStencilStates()
|
|
{
|
|
if (_currentState->stencilTest != _nextState->stencilTest)
|
|
{
|
|
if (_nextState->stencilTest)
|
|
{
|
|
glEnable(GL_STENCIL_TEST);
|
|
}
|
|
|
|
if (_nextState->stencilSeparation)
|
|
{
|
|
GL_CHECK(glStencilFuncSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
GL_CHECK(glStencilMaskSeparate(GL_FRONT, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
GL_CHECK(glStencilOpSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
GL_CHECK(glStencilFuncSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskBack)));
|
|
GL_CHECK(glStencilMaskSeparate(GL_BACK, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskBack)));
|
|
GL_CHECK(glStencilOpSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpBack)));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glStencilFunc(ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
GL_CHECK(glStencilMask(ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
GL_CHECK(glStencilOp(ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
}
|
|
|
|
if (!_nextState->stencilTest)
|
|
{
|
|
glDisable(GL_STENCIL_TEST);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!_nextState->stencilTest)
|
|
return;
|
|
|
|
if (_currentState->stencilSeparation != _nextState->stencilSeparation)
|
|
{
|
|
if (_nextState->stencilSeparation)
|
|
{
|
|
// front
|
|
GL_CHECK(glStencilFuncSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
GL_CHECK(glStencilMaskSeparate(GL_FRONT, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
GL_CHECK(glStencilOpSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
|
|
// back
|
|
GL_CHECK(glStencilFuncSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskBack)));
|
|
GL_CHECK(glStencilMaskSeparate(GL_BACK, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskBack)));
|
|
GL_CHECK(glStencilOpSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpBack)));
|
|
}
|
|
else
|
|
{
|
|
GL_CHECK(glStencilFunc(ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
GL_CHECK(glStencilMask(ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
GL_CHECK(glStencilOp(ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_nextState->stencilSeparation)
|
|
{
|
|
// font
|
|
if (_currentState->stencilFuncFront != _nextState->stencilFuncFront ||
|
|
_currentState->stencilRefFront != _nextState->stencilRefFront ||
|
|
_currentState->stencilMaskFront != _nextState->stencilMaskFront)
|
|
{
|
|
GL_CHECK(glStencilFuncSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
}
|
|
if (_currentState->stencilWriteMaskFront != _nextState->stencilWriteMaskFront)
|
|
{
|
|
GL_CHECK(glStencilMaskSeparate(GL_FRONT, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
}
|
|
if (_currentState->stencilFailOpFront != _nextState->stencilFailOpFront ||
|
|
_currentState->stencilZFailOpFront != _nextState->stencilZFailOpFront ||
|
|
_currentState->stencilZPassOpFront != _nextState->stencilZPassOpFront)
|
|
{
|
|
GL_CHECK(glStencilOpSeparate(GL_FRONT,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
}
|
|
|
|
// back
|
|
if (_currentState->stencilFuncBack != _nextState->stencilFuncBack ||
|
|
_currentState->stencilRefBack != _nextState->stencilRefBack ||
|
|
_currentState->stencilMaskBack != _nextState->stencilMaskBack)
|
|
{
|
|
GL_CHECK(glStencilFuncSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskBack)));
|
|
}
|
|
if (_currentState->stencilWriteMaskBack != _nextState->stencilWriteMaskBack)
|
|
GL_CHECK(glStencilMaskSeparate(GL_BACK, ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskBack)));
|
|
if (_currentState->stencilFailOpBack != _nextState->stencilFailOpBack ||
|
|
_currentState->stencilZFailOpBack != _nextState->stencilZFailOpBack ||
|
|
_currentState->stencilZPassOpBack != _nextState->stencilZPassOpBack)
|
|
{
|
|
GL_CHECK(glStencilOpSeparate(GL_BACK,
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpBack),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpBack)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_currentState->stencilFuncFront != _nextState->stencilFuncFront ||
|
|
_currentState->stencilRefFront != _nextState->stencilRefFront ||
|
|
_currentState->stencilMaskFront != _nextState->stencilMaskFront)
|
|
{
|
|
GL_CHECK(glStencilFunc(ENUM_CLASS_TO_GLENUM(_nextState->stencilFuncFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilRefFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilMaskFront)));
|
|
}
|
|
|
|
if (_currentState->stencilWriteMaskFront != _nextState->stencilWriteMaskFront)
|
|
{
|
|
GL_CHECK(glStencilMask(ENUM_CLASS_TO_GLENUM(_nextState->stencilWriteMaskFront)));
|
|
}
|
|
|
|
if (_currentState->stencilFailOpFront != _nextState->stencilFailOpFront ||
|
|
_currentState->stencilZFailOpFront != _nextState->stencilZFailOpFront ||
|
|
_currentState->stencilZPassOpFront != _nextState->stencilZPassOpFront)
|
|
{
|
|
GL_CHECK(glStencilOp(ENUM_CLASS_TO_GLENUM(_nextState->stencilFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZFailOpFront),
|
|
ENUM_CLASS_TO_GLENUM(_nextState->stencilZPassOpFront)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::commitCullMode()
|
|
{
|
|
if (_currentState->cullMode == _nextState->cullMode)
|
|
return;
|
|
|
|
if (_nextState->cullMode == CullMode::NONE)
|
|
{
|
|
GL_CHECK(glDisable(GL_CULL_FACE));
|
|
return;
|
|
}
|
|
|
|
GL_CHECK(glEnable(GL_CULL_FACE));
|
|
GL_CHECK(glCullFace(ENUM_CLASS_TO_GLENUM(_nextState->cullMode)));
|
|
}
|
|
void DeviceGraphics::commitVertexBuffer()
|
|
{
|
|
if (-1 == _nextState->maxStream)
|
|
{
|
|
RENDERER_LOGW("VertexBuffer not assigned, please call setVertexBuffer before every draw.");
|
|
return;
|
|
}
|
|
|
|
bool attrsDirty = false;
|
|
if (_currentState->maxStream != _nextState->maxStream)
|
|
attrsDirty = true;
|
|
else if (_currentState->getProgram() != _nextState->getProgram())
|
|
attrsDirty = true;
|
|
else
|
|
{
|
|
for (int i = 0; i < _nextState->maxStream + 1; ++i)
|
|
{
|
|
if (_currentState->getVertexBuffer(i) != _nextState->getVertexBuffer(i) ||
|
|
_currentState->getVertexBufferOffset(i) != _nextState->getVertexBufferOffset(i))
|
|
{
|
|
attrsDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (attrsDirty)
|
|
{
|
|
for (int i = 0; i < _caps.maxVertexAttributes; ++i)
|
|
_newAttributes[i] = 0;
|
|
|
|
for (int i = 0; i < _nextState->maxStream + 1; ++i)
|
|
{
|
|
auto vb = _nextState->getVertexBuffer(i);
|
|
if (!vb)
|
|
continue;
|
|
|
|
GL_CHECK(ccBindBuffer(GL_ARRAY_BUFFER, vb->getHandle()));
|
|
|
|
auto vboffset = _nextState->getVertexBufferOffset(i);
|
|
const auto& attributes = _nextState->getProgram()->getAttributes();
|
|
auto usedAttriLen = attributes.size();
|
|
for (int j = 0; j < usedAttriLen; ++j)
|
|
{
|
|
const auto& attr = attributes[j];
|
|
const auto* el = vb->getFormat().getElement(attr.hashName);
|
|
if (!el || !el->isValid())
|
|
{
|
|
RENDERER_LOGW("Can not find vertex attribute: %s", attr.name.c_str());
|
|
continue;
|
|
}
|
|
|
|
if (0 == _enabledAtrributes[attr.location])
|
|
{
|
|
GL_CHECK(ccEnableVertexAttribArray(attr.location));
|
|
_enabledAtrributes[attr.location] = 1;
|
|
}
|
|
_newAttributes[attr.location] = 1;
|
|
|
|
// glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
|
|
GL_CHECK(ccVertexAttribPointer(attr.location,
|
|
el->num,
|
|
ENUM_CLASS_TO_GLENUM(el->type),
|
|
el->normalize,
|
|
el->stride,
|
|
(GLvoid*)(el->offset + vboffset * el->stride)));
|
|
}
|
|
}
|
|
|
|
// Disable unused attributes.
|
|
for (int i = 0; i < _caps.maxVertexAttributes; ++i)
|
|
{
|
|
if (_enabledAtrributes[i] != _newAttributes[i])
|
|
{
|
|
GL_CHECK(ccDisableVertexAttribArray(i));
|
|
_enabledAtrributes[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceGraphics::commitTextures()
|
|
{
|
|
const auto& curTextureUnits = _currentState->getTextureUnits();
|
|
int curTextureSize = static_cast<int>(curTextureUnits.size());
|
|
const auto& nextTextureUnits = _nextState->getTextureUnits();
|
|
int capacity = static_cast<int>(nextTextureUnits.size());
|
|
for (int i = 0; i < capacity; ++i)
|
|
{
|
|
if (i >= curTextureSize || curTextureUnits[i] != nextTextureUnits[i])
|
|
{
|
|
auto texture = nextTextureUnits[i];
|
|
if (texture)
|
|
{
|
|
GL_CHECK(glActiveTexture(GL_TEXTURE0 + i));
|
|
GL_CHECK(glBindTexture(texture->getTarget(),
|
|
texture->getHandle()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Uniform
|
|
//
|
|
DeviceGraphics::Uniform::Uniform()
|
|
: dirty(true)
|
|
, elementType(UniformElementType::FLOAT)
|
|
{}
|
|
|
|
DeviceGraphics::Uniform::Uniform(const void* v, size_t bytes, UniformElementType elementType_, size_t count)
|
|
: dirty(true)
|
|
, elementType(elementType_)
|
|
{
|
|
setValue(v, bytes, count);
|
|
}
|
|
|
|
DeviceGraphics::Uniform::Uniform(Uniform&& h)
|
|
{
|
|
if (this == &h)
|
|
return;
|
|
|
|
if (value != nullptr)
|
|
{
|
|
free(value);
|
|
}
|
|
value = h.value;
|
|
count = h.count;
|
|
h.value = nullptr;
|
|
|
|
dirty = h.dirty;
|
|
elementType = h.elementType;
|
|
}
|
|
|
|
DeviceGraphics::Uniform::~Uniform()
|
|
{
|
|
if (value)
|
|
{
|
|
free(value);
|
|
value = nullptr;
|
|
}
|
|
}
|
|
|
|
DeviceGraphics::Uniform& DeviceGraphics::Uniform::operator=(Uniform&& h)
|
|
{
|
|
if (this == &h)
|
|
return *this;
|
|
|
|
dirty = h.dirty;
|
|
|
|
if (value != nullptr)
|
|
{
|
|
free(value);
|
|
}
|
|
value = h.value;
|
|
count = h.count;
|
|
h.value = nullptr;
|
|
elementType = h.elementType;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void DeviceGraphics::Uniform::setValue(const void* v, size_t valueBytes, size_t uniformCount)
|
|
{
|
|
if (bytes != valueBytes || !value) {
|
|
if (value)
|
|
free(value);
|
|
value = malloc(valueBytes);
|
|
|
|
bytes = valueBytes;
|
|
count = uniformCount;
|
|
}
|
|
|
|
memcpy(value, v, valueBytes);
|
|
}
|
|
|
|
RENDERER_END
|