blob: a06295d5f8c8c466ffa25499bfd45ddf57bb8a98 [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/blittergles/blit_shader_program.h"
#include <GLES2/gl2.h>
#include "starboard/blitter.h"
#include "starboard/shared/blittergles/blitter_context.h"
#include "starboard/shared/blittergles/blitter_internal.h"
#include "starboard/shared/blittergles/blitter_surface.h"
#include "starboard/shared/blittergles/shader_program.h"
#include "starboard/shared/gles/gl_call.h"
namespace starboard {
namespace shared {
namespace blittergles {
namespace {
// Location of the blit shader attribute "a_blit_position."
static const int kBlitPositionAttribute = 0;
// Location of the blit shader attribute "a_tex_coord."
static const int kTexCoordAttribute = 1;
// When applied to a color vector, this 4x4 identity matrix will not affect it:
// kIdentityMatrix * [r g b a]^T = [r g b a]^T.
const float kIdentityMatrix[16] = {1, 0, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1};
// When applied to a color vector, this 4x4 matrix will fill rgb values with the
// alpha value: kAlphaMatrix * [r g b a]^T = [a a a a]^T. Note GL matrices are
// read in column-major order.
const float kAlphaMatrix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
} // namespace
BlitShaderProgram::BlitShaderProgram() {
const char* vertex_shader_source =
"attribute vec2 a_blit_position;"
"attribute vec2 a_tex_coord;"
"varying vec2 v_tex_coord;"
"void main() {"
" gl_Position = vec4(a_blit_position.x, a_blit_position.y, 0, 1);"
" v_tex_coord = a_tex_coord;"
"}";
const char* fragment_shader_source =
"precision mediump float;"
"uniform sampler2D tex;"
"uniform mat4 u_color_matrix;"
"uniform vec4 u_tex_coord_clamp;"
"varying vec2 v_tex_coord;"
"void main() {"
" vec2 coords = clamp(v_tex_coord, u_tex_coord_clamp.xy, "
" u_tex_coord_clamp.zw);"
" gl_FragColor = u_color_matrix * texture2D(tex, coords);"
"}";
InitializeShaders(vertex_shader_source, fragment_shader_source);
GL_CALL(glBindAttribLocation(GetProgramHandle(), kBlitPositionAttribute,
"a_blit_position"));
GL_CALL(glBindAttribLocation(GetProgramHandle(), kTexCoordAttribute,
"a_tex_coord"));
int link_status;
GL_CALL(glLinkProgram(GetProgramHandle()));
GL_CALL(glGetProgramiv(GetProgramHandle(), GL_LINK_STATUS, &link_status));
SB_CHECK(link_status);
clamp_uniform_ =
glGetUniformLocation(GetProgramHandle(), "u_tex_coord_clamp");
color_matrix_uniform_ =
glGetUniformLocation(GetProgramHandle(), "u_color_matrix");
SB_CHECK(clamp_uniform_ != -1);
SB_CHECK(color_matrix_uniform_ != -1);
}
bool BlitShaderProgram::Draw(SbBlitterContext context,
SbBlitterSurface surface,
SbBlitterRect src_rect,
SbBlitterRect dst_rect) const {
GL_CALL(glUseProgram(GetProgramHandle()));
float dst_vertices[8], src_vertices[8];
SetTexCoords(src_rect, surface->info.width, surface->info.height,
src_vertices);
SetNDC(dst_rect, context->current_render_target->width,
context->current_render_target->height, dst_vertices);
// Clamp so fragment shader does not sample beyond edges of texture.
const float kTexelInset = 0.499f;
float texel_clamps[] = {
src_vertices[0] + kTexelInset / surface->info.width, // min u
src_vertices[1] + kTexelInset / surface->info.height, // min v
src_vertices[4] - kTexelInset / surface->info.width, // max u
src_vertices[3] - kTexelInset / surface->info.height}; // max v
GL_CALL(glVertexAttribPointer(kBlitPositionAttribute, 2, GL_FLOAT, GL_FALSE,
0, dst_vertices));
GL_CALL(glVertexAttribPointer(kTexCoordAttribute, 2, GL_FLOAT, GL_FALSE, 0,
src_vertices));
GL_CALL(glEnableVertexAttribArray(kBlitPositionAttribute));
GL_CALL(glEnableVertexAttribArray(kTexCoordAttribute));
GL_CALL(glUniform4f(clamp_uniform_, texel_clamps[0], texel_clamps[1],
texel_clamps[2], texel_clamps[3]));
const float* transform_mat;
const float kAlphaBlitMatrix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, context->current_rgba[0],
context->current_rgba[1], context->current_rgba[2],
context->current_rgba[3]};
const float kBlitMatrix[16] = {context->current_rgba[0], 0, 0, 0, 0,
context->current_rgba[1], 0, 0, 0, 0,
context->current_rgba[2], 0, 0, 0, 0,
context->current_rgba[3]};
if (surface->info.format == kSbBlitterSurfaceFormatA8) {
transform_mat =
context->modulate_blits_with_color ? kAlphaBlitMatrix : kAlphaMatrix;
} else {
transform_mat =
context->modulate_blits_with_color ? kBlitMatrix : kIdentityMatrix;
}
GL_CALL(
glUniformMatrix4fv(color_matrix_uniform_, 1, GL_FALSE, transform_mat));
GL_CALL(glActiveTexture(GL_TEXTURE0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, surface->color_texture_handle));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
bool success = glGetError() == GL_NO_ERROR;
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
GL_CALL(glDisableVertexAttribArray(kTexCoordAttribute));
GL_CALL(glDisableVertexAttribArray(kBlitPositionAttribute));
GL_CALL(glUseProgram(0));
return success;
}
} // namespace blittergles
} // namespace shared
} // namespace starboard