blob: bf053c5e5eac3698535a71ba399c20059fbb685e [file] [log] [blame]
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/program_binding.h"
#include "base/debug/trace_event.h"
#include "cc/geometry_binding.h"
#include "cc/gl_renderer.h" // For the GLC() macro.
#include "third_party/khronos/GLES2/gl2.h"
#include <public/WebGraphicsContext3D.h>
using WebKit::WebGraphicsContext3D;
namespace cc {
ProgramBindingBase::ProgramBindingBase()
: m_program(0)
, m_vertexShaderId(0)
, m_fragmentShaderId(0)
, m_initialized(false)
{
}
ProgramBindingBase::~ProgramBindingBase()
{
// If you hit these asserts, you initialized but forgot to call cleanup().
DCHECK(!m_program);
DCHECK(!m_vertexShaderId);
DCHECK(!m_fragmentShaderId);
DCHECK(!m_initialized);
}
void ProgramBindingBase::init(WebGraphicsContext3D* context, const std::string& vertexShader, const std::string& fragmentShader)
{
TRACE_EVENT0("cc", "ProgramBindingBase::init");
m_vertexShaderId = loadShader(context, GL_VERTEX_SHADER, vertexShader);
if (!m_vertexShaderId) {
if (!IsContextLost(context))
LOG(ERROR) << "Failed to create vertex shader";
return;
}
m_fragmentShaderId = loadShader(context, GL_FRAGMENT_SHADER, fragmentShader);
if (!m_fragmentShaderId) {
GLC(context, context->deleteShader(m_vertexShaderId));
m_vertexShaderId = 0;
if (!IsContextLost(context))
LOG(ERROR) << "Failed to create fragment shader";
return;
}
m_program = createShaderProgram(context, m_vertexShaderId, m_fragmentShaderId);
DCHECK(m_program || IsContextLost(context));
}
void ProgramBindingBase::link(WebGraphicsContext3D* context)
{
GLC(context, context->linkProgram(m_program));
cleanupShaders(context);
#ifndef NDEBUG
int linked = 0;
GLC(context, context->getProgramiv(m_program, GL_LINK_STATUS, &linked));
if (!linked) {
if (!IsContextLost(context))
LOG(ERROR) << "Failed to link shader program";
GLC(context, context->deleteProgram(m_program));
}
#endif
}
void ProgramBindingBase::cleanup(WebGraphicsContext3D* context)
{
m_initialized = false;
if (!m_program)
return;
DCHECK(context);
GLC(context, context->deleteProgram(m_program));
m_program = 0;
cleanupShaders(context);
}
unsigned ProgramBindingBase::loadShader(WebGraphicsContext3D* context, unsigned type, const std::string& shaderSource)
{
unsigned shader = context->createShader(type);
if (!shader)
return 0;
GLC(context, context->shaderSource(shader, shaderSource.data()));
GLC(context, context->compileShader(shader));
#ifndef NDEBUG
int compiled = 0;
GLC(context, context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled));
if (!compiled) {
GLC(context, context->deleteShader(shader));
return 0;
}
#endif
return shader;
}
unsigned ProgramBindingBase::createShaderProgram(WebGraphicsContext3D* context, unsigned vertexShader, unsigned fragmentShader)
{
unsigned programObject = context->createProgram();
if (!programObject) {
if (!IsContextLost(context))
LOG(ERROR) << "Failed to create shader program";
return 0;
}
GLC(context, context->attachShader(programObject, vertexShader));
GLC(context, context->attachShader(programObject, fragmentShader));
// Bind the common attrib locations.
GLC(context, context->bindAttribLocation(programObject, GeometryBinding::positionAttribLocation(), "a_position"));
GLC(context, context->bindAttribLocation(programObject, GeometryBinding::texCoordAttribLocation(), "a_texCoord"));
return programObject;
}
void ProgramBindingBase::cleanupShaders(WebGraphicsContext3D* context)
{
if (m_vertexShaderId) {
GLC(context, context->deleteShader(m_vertexShaderId));
m_vertexShaderId = 0;
}
if (m_fragmentShaderId) {
GLC(context, context->deleteShader(m_fragmentShaderId));
m_fragmentShaderId = 0;
}
}
bool ProgramBindingBase::IsContextLost(WebGraphicsContext3D* context) {
return (context->getGraphicsResetStatusARB() != GL_NO_ERROR);
}
} // namespace cc