#include "gles-graphics-render-pass.h"
#include "gles-graphics-render-target.h"
+#include <map>
+
namespace Dali::Graphics::GLES
{
struct Context::Impl
~Impl() = default;
/**
+ * Binds (and creates) VAO
+ *
+ * VAO is fixed per program so it has to be created only once assuming
+ * that VertexInputState has been set correctly for the pipeline.
+ *
+ */
+ void BindProgramVAO(GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
+ {
+ auto& gl = *mController.GetGL();
+ auto iter = mProgramVAOMap.find(program);
+ if(iter != mProgramVAOMap.end())
+ {
+ if(mProgramVAOCurrentState != iter->second)
+ {
+ mProgramVAOCurrentState = iter->second;
+ gl.BindVertexArray(iter->second);
+ }
+ return;
+ }
+
+ uint32_t vao;
+ gl.GenVertexArrays(1, &vao);
+ gl.BindVertexArray(vao);
+ mProgramVAOMap[program] = vao;
+ for(const auto& attr : vertexInputState.attributes)
+ {
+ gl.EnableVertexAttribArray(attr.location);
+ }
+
+ mProgramVAOCurrentState = vao;
+ }
+
+ /**
* Sets the initial GL state.
*/
void InitializeGlState()
const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
const GLES::RenderPass* mCurrentRenderPass{nullptr};
- GLStateCache mGlStateCache{}; ///< GL status cache
+ // Each context must have own VAOs as they cannot be shared
+ std::map<GLES::ProgramImpl*, uint32_t> mProgramVAOMap; ///< GL program-VAO map
+ uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO
+ GLStateCache mGlStateCache{}; ///< GL status cache
bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
};
{
auto& gl = *mImpl->mController.GetGL();
+ static const bool hasGLES3(mImpl->mController.GetGLESVersion() >= GLESVersion::GLES_30);
+
+ // early out if neither current nor new pipelines are set
+ // this behaviour may be valid so no assert
+ if(!mImpl->mCurrentPipeline && !mImpl->mNewPipeline)
+ {
+ return;
+ }
+
// Execute states if pipeline is changed
- const auto& currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
- const auto& newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+ const auto currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
+
+ // case when new pipeline has been set
+ const GLES::Program* newProgram = nullptr;
- if(mImpl->mCurrentPipeline != mImpl->mNewPipeline)
+ if(mImpl->mNewPipeline)
+ {
+ newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+ }
+
+ if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
{
if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
{
// Bind textures
// Map binding# to sampler location
- const auto& reflection = newProgram->GetReflection();
+ const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection();
const auto& samplers = reflection.GetSamplers();
for(const auto& binding : mImpl->mCurrentTextureBindings)
{
}
// for each attribute bind vertices
- const auto& pipelineState = mImpl->mNewPipeline->GetCreateInfo();
- const auto& vi = pipelineState.vertexInputState;
- for(const auto& attr : vi->attributes)
+
+ const auto& pipelineState = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo();
+ const auto& vertexInputState = pipelineState.vertexInputState;
+
+ if(hasGLES3)
+ {
+ mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
+ }
+
+ for(const auto& attr : vertexInputState->attributes)
{
// Enable location
- mImpl->SetVertexAttributeLocation(attr.location, true);
+ if(!hasGLES3)
+ {
+ mImpl->SetVertexAttributeLocation(attr.location, true);
+ }
const auto& bufferSlot = mImpl->mCurrentVertexBufferBindings[attr.binding];
- const auto& bufferBinding = vi->bufferBindings[attr.binding];
+ const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
}
// Resolve topology
- const auto& ia = mImpl->mNewPipeline->GetCreateInfo().inputAssemblyState;
+ const auto& ia = pipelineState.inputAssemblyState;
// Bind uniforms
mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
mImpl->mGlStateCache.DepthBufferWriteEnabled(),
mImpl->mGlStateCache.StencilBufferWriteEnabled());
- mImpl->FlushVertexAttributeLocations();
+ // For GLES3+ we use VAO, for GLES2 internal cache
+ if(!hasGLES3)
+ {
+ mImpl->FlushVertexAttributeLocations();
+ }
gl.DrawArrays(GLESTopology(ia->topology),
drawCall.draw.firstVertex,
mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
mImpl->mGlStateCache.DepthBufferWriteEnabled(),
mImpl->mGlStateCache.StencilBufferWriteEnabled());
- mImpl->FlushVertexAttributeLocations();
+
+ // For GLES3+ we use VAO, for GLES2 internal cache
+ if(!hasGLES3)
+ {
+ mImpl->FlushVertexAttributeLocations();
+ }
auto indexBufferFormat = GLIndexFormat(binding.format).format;
gl.DrawElements(GLESTopology(ia->topology),
}
}
-void Context::BindTextures(const std::vector<Graphics::TextureBinding>& bindings)
+void Context::BindTextures(const Graphics::TextureBinding* bindings, uint32_t count)
{
// for each texture allocate slot
- for(const auto& binding : bindings)
+ for(auto i = 0u; i < count; ++i)
{
+ auto& binding = bindings[i];
+
// Resize binding array if needed
if(mImpl->mCurrentTextureBindings.size() <= binding.binding)
{
}
}
-void Context::BindVertexBuffers(const std::vector<GLES::VertexBufferBindingDescriptor>& bindings)
+void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindings, uint32_t count)
{
- if(bindings.size() > mImpl->mCurrentVertexBufferBindings.size())
+ if(count > mImpl->mCurrentVertexBufferBindings.size())
{
- mImpl->mCurrentVertexBufferBindings.resize(bindings.size());
+ mImpl->mCurrentVertexBufferBindings.resize(count);
}
// Copy only set slots
- std::copy_if(bindings.begin(), bindings.end(), mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) {
+ std::copy_if(bindings, bindings + count, mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) {
return (nullptr != item.buffer);
});
}
mImpl->mNewPipeline = &newPipeline->GetPipeline();
}
-void Context::BindUniformBuffers(const std::vector<UniformBufferBindingDescriptor>& uboBindings,
- const UniformBufferBindingDescriptor& standaloneBindings)
+void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindings,
+ uint32_t uboCount,
+ const UniformBufferBindingDescriptor& standaloneBindings)
{
if(standaloneBindings.buffer)
{
mImpl->mCurrentStandaloneUBOBinding = standaloneBindings;
}
- if(uboBindings.size() >= mImpl->mCurrentUBOBindings.size())
+ if(uboCount >= mImpl->mCurrentUBOBindings.size())
{
- mImpl->mCurrentUBOBindings.resize(uboBindings.size() + 1);
+ mImpl->mCurrentUBOBindings.resize(uboCount + 1);
}
- auto it = uboBindings.begin();
- for(auto i = 0u; i < uboBindings.size(); ++i)
+ auto it = uboBindings;
+ for(auto i = 0u; i < uboCount; ++i)
{
if(it->buffer)
{
void Context::ResolveStandaloneUniforms()
{
- auto& gl = *mImpl->mController.GetGL();
-
// Find reflection for program
const auto program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+ const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
- const auto& reflection = program->GetReflection();
-
- auto extraInfos = reflection.GetStandaloneUniformExtraInfo();
-
- const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
-
- for(const auto& info : extraInfos)
- {
- auto type = GLTypeConversion(info.type).type;
- auto offset = info.offset;
- switch(type)
- {
- case GLType::FLOAT_VEC2:
- {
- gl.Uniform2fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::FLOAT_VEC3:
- {
- gl.Uniform3fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::FLOAT_VEC4:
- {
- gl.Uniform4fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::INT_VEC2:
- {
- gl.Uniform2iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
- break;
- }
- case GLType::INT_VEC3:
- {
- gl.Uniform3iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
- break;
- }
- case GLType::INT_VEC4:
- {
- gl.Uniform4iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
- break;
- }
- case GLType::BOOL:
- {
- // not supported by DALi
- break;
- }
- case GLType::BOOL_VEC2:
- {
- // not supported by DALi
- break;
- }
- case GLType::BOOL_VEC3:
- {
- // not supported by DALi
- break;
- }
- case GLType::BOOL_VEC4:
- {
- // not supported by DALi
- break;
- }
- case GLType::FLOAT:
- {
- gl.Uniform1fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::FLOAT_MAT2:
- {
- gl.UniformMatrix2fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::FLOAT_MAT3:
- {
- gl.UniformMatrix3fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::FLOAT_MAT4:
- {
- gl.UniformMatrix4fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
- break;
- }
- case GLType::SAMPLER_2D:
- {
- break;
- }
- case GLType::SAMPLER_CUBE:
- {
- break;
- }
- default:
- {
- }
- }
- }
+ // Update program uniforms
+ program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
}
void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
// Something goes wrong here if Alpha mask is GL_TRUE
ColorMask(true);
+ const auto clearValues = renderPassBegin.clearValues.Ptr();
+
if(!mImpl->mGlStateCache.mClearColorSet ||
- mImpl->mGlStateCache.mClearColor.r != renderPassBegin.clearValues[0].color.r ||
- mImpl->mGlStateCache.mClearColor.g != renderPassBegin.clearValues[0].color.g ||
- mImpl->mGlStateCache.mClearColor.b != renderPassBegin.clearValues[0].color.b ||
- mImpl->mGlStateCache.mClearColor.a != renderPassBegin.clearValues[0].color.a)
+ mImpl->mGlStateCache.mClearColor.r != clearValues[0].color.r ||
+ mImpl->mGlStateCache.mClearColor.g != clearValues[0].color.g ||
+ mImpl->mGlStateCache.mClearColor.b != clearValues[0].color.b ||
+ mImpl->mGlStateCache.mClearColor.a != clearValues[0].color.a)
{
- gl.ClearColor(renderPassBegin.clearValues[0].color.r,
- renderPassBegin.clearValues[0].color.g,
- renderPassBegin.clearValues[0].color.b,
- renderPassBegin.clearValues[0].color.a);
+ gl.ClearColor(clearValues[0].color.r,
+ clearValues[0].color.g,
+ clearValues[0].color.b,
+ clearValues[0].color.a);
mImpl->mGlStateCache.mClearColorSet = true;
- mImpl->mGlStateCache.mClearColor = Vector4(renderPassBegin.clearValues[0].color.r,
- renderPassBegin.clearValues[0].color.g,
- renderPassBegin.clearValues[0].color.b,
- renderPassBegin.clearValues[0].color.a);
+ mImpl->mGlStateCache.mClearColor = Vector4(clearValues[0].color.r,
+ clearValues[0].color.g,
+ clearValues[0].color.b,
+ clearValues[0].color.a);
}
}
}
}
+void Context::InvalidateDepthStencilBuffers()
+{
+ auto& gl = *mImpl->mController.GetGL();
+
+ GLenum attachments[] = {GL_DEPTH, GL_STENCIL};
+ gl.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
+}
+
void Context::SetScissorTestEnabled(bool scissorEnabled)
{
if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)