// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/android/xr_renderer.h"
#include "base/check_op.h"
#include "device/vr/vr_gl_util.h"
namespace device {
namespace {
static constexpr float kQuadVertices[8] = {
-0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
};
static constexpr GLushort kQuadIndices[6] = {0, 1, 2, 1, 3, 2};
// clang-format off
static constexpr char const* kVertexShader = SHADER(
precision mediump float;
uniform mat4 u_UvTransform;
attribute vec4 a_Position;
varying vec2 v_TexCoordinate;
void main() {
// The quad vertex coordinate range is [-0.5, 0.5]. Transform to [0, 1],
// then apply the supplied affine transform matrix to get the final UV.
float xposition = a_Position[0] + 0.5;
float yposition = a_Position[1] + 0.5;
vec4 uv_in = vec4(xposition, yposition, 0.0, 1.0);
vec4 uv_out = u_UvTransform * uv_in;
v_TexCoordinate = vec2(uv_out.x, uv_out.y);
gl_Position = vec4(a_Position.xyz * 2.0, 1.0);
}
);
static constexpr char const* kFragmentShaderExternal = OEIE_SHADER(
precision highp float;
uniform samplerExternalOES u_Texture;
varying vec2 v_TexCoordinate;
void main() {
gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
}
);
static constexpr char const* kFragmentShader2D = OEIE_SHADER(
precision highp float;
uniform sampler2D u_Texture;
varying vec2 v_TexCoordinate;
void main() {
gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
}
);
// clang-format on
} // namespace
XrRenderer::Program XrRenderer::CreateProgram(const std::string& vertex,
const std::string& fragment) {
std::string error;
GLuint vertex_shader_handle =
vr::CompileShader(GL_VERTEX_SHADER, vertex, error);
// TODO(crbug.com/41403313): fail gracefully if shaders don't compile.
CHECK(vertex_shader_handle) << error << "\nvertex_src\n" << vertex;
GLuint fragment_shader_handle =
vr::CompileShader(GL_FRAGMENT_SHADER, fragment, error);
CHECK(fragment_shader_handle) << error << "\nfragment_src\n" << fragment;
Program result;
result.program_handle_ = vr::CreateAndLinkProgram(
vertex_shader_handle, fragment_shader_handle, error);
CHECK(result.program_handle_) << error;
// Once the program is linked the shader objects are no longer needed
glDeleteShader(vertex_shader_handle);
glDeleteShader(fragment_shader_handle);
result.position_handle_ =
glGetAttribLocation(result.program_handle_, "a_Position");
result.texture_handle_ =
glGetUniformLocation(result.program_handle_, "u_Texture");
result.uv_transform_ =
glGetUniformLocation(result.program_handle_, "u_UvTransform");
return result;
}
XrRenderer::XrRenderer() {
program_external_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
program_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
}
void XrRenderer::Draw(const LocalTexture& texture,
const float (&uv_transform)[16]) {
if (!vertex_buffer_ || !index_buffer_) {
GLuint buffers[2];
glGenBuffersARB(2, buffers);
vertex_buffer_ = buffers[0];
index_buffer_ = buffers[1];
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
glBufferData(GL_ARRAY_BUFFER, std::size(kQuadVertices) * sizeof(float),
kQuadVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
std::size(kQuadIndices) * sizeof(GLushort), kQuadIndices,
GL_STATIC_DRAW);
}
CHECK(texture.target == GL_TEXTURE_EXTERNAL_OES ||
texture.target == GL_TEXTURE_2D);
const Program& program = texture.target == GL_TEXTURE_EXTERNAL_OES
? program_external_
: program_2d_;
glUseProgram(program.program_handle_);
// Bind vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
// Set up position attribute.
glVertexAttribPointer(program.position_handle_, 2, GL_FLOAT, false, 0,
nullptr);
glEnableVertexAttribArray(program.position_handle_);
// Bind texture. This is not necessarily a 1:1 pixel copy since the
// size is modified by framebufferScaleFactor and requestViewportScale,
// so use GL_LINEAR.
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture.target, texture.id);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glUniform1i(program.texture_handle_, 0);
glUniformMatrix4fv(program.uv_transform_, 1, GL_FALSE, &uv_transform[0]);
// Blit texture to buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
glDrawElements(GL_TRIANGLES, std::size(kQuadIndices), GL_UNSIGNED_SHORT, 0);
glDisableVertexAttribArray(program.position_handle_);
}
// Note that we don't explicitly delete gl objects here, they're deleted
// automatically when we call ShutdownGL, and deleting them here leads to
// segfaults.
XrRenderer::~XrRenderer() = default;
} // namespace device