blob: edc0e40191f16cbebafbe934664247b4e9aa0b99 [file] [log] [blame]
/*
* Copyright 2015 Google Inc. 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.
*/
#if defined(GLES3_SUPPORTED)
#include "cobalt/renderer/backend/egl/texture_data_pbo.h"
#include <GLES2/gl2ext.h>
#include "base/bind.h"
#include "base/logging.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/utils.h"
namespace cobalt {
namespace renderer {
namespace backend {
TextureDataPBO::TextureDataPBO(ResourceContext* resource_context,
const math::Size& size, GLenum format)
: resource_context_(resource_context), size_(size), format_(format) {
data_size_ = GetPitchInBytes() * size_.height();
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&TextureDataPBO::InitAndMapPBO, base::Unretained(this)));
}
void TextureDataPBO::InitAndMapPBO() {
resource_context_->AssertWithinResourceContext();
// Use the resource context to allocate a GL pixel unpack buffer with the
// specified size.
GL_CALL(glGenBuffers(1, &pixel_buffer_));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, data_size_, 0, GL_STATIC_DRAW));
// Map the GL pixel buffer data to CPU addressable memory. We pass the flags
// MAP_INVALIDATE_BUFFER_BIT | MAP_UNSYNCHRONIZED_BIT to tell GL that it
// we don't care about previous data in the buffer and it shouldn't try to
// synchronize existing draw calls with it, both things which should be
// implied anyways since this is a brand new buffer, but we specify just in
// case.
mapped_data_ = static_cast<GLubyte*>(glMapBufferRange(
GL_PIXEL_UNPACK_BUFFER, 0, data_size_, GL_MAP_WRITE_BIT |
GL_MAP_INVALIDATE_BUFFER_BIT |
GL_MAP_UNSYNCHRONIZED_BIT));
DCHECK(mapped_data_);
DCHECK_EQ(GL_NO_ERROR, glGetError());
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
}
TextureDataPBO::~TextureDataPBO() {
// mapped_data_ will be non-null if ConvertToTexture() was never called.
if (mapped_data_) {
// In the case that we still have valid mapped_data_, we must release it,
// and we do so on the resource context.
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&TextureDataPBO::UnmapAndDeletePBO, base::Unretained(this)));
}
}
void TextureDataPBO::UnmapAndDeletePBO() {
resource_context_->AssertWithinResourceContext();
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
GL_CALL(glDeleteBuffers(1, &pixel_buffer_));
}
GLuint TextureDataPBO::ConvertToTexture(GraphicsContextEGL* graphics_context,
bool bgra_supported) {
DCHECK(mapped_data_) << "ConvertToTexture() should only ever be called once.";
// Since we wish to create a texture within the specified graphics context,
// we do the texture creation work using that context.
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context);
// Our texture data remains mapped until this method is called, so we begin
// by unmapping it.
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
// Setup our texture.
GLuint texture_handle;
GL_CALL(glGenTextures(1, &texture_handle));
GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_handle));
SetupInitialTextureParameters();
// Determine the texture's GL format.
if (format_ == GL_BGRA_EXT) {
DCHECK(bgra_supported);
}
// Create the texture. Since a GL_PIXEL_UNPACK_BUFFER is currently bound,
// the "data" field passed to glTexImage2D will refer to an offset into that
// buffer.
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH,
GetPitchInBytes() / BytesPerPixelForGLFormat(format_)));
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, format_, size_.width(), size_.height(),
0, format_, GL_UNSIGNED_BYTE, 0));
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
// The buffer is now referenced by the texture, and we no longer need to
// explicitly reference it.
GL_CALL(glDeleteBuffers(1, &pixel_buffer_));
mapped_data_ = NULL;
return texture_handle;
}
RawTextureMemoryPBO::RawTextureMemoryPBO(ResourceContext* resource_context,
size_t size_in_bytes, size_t alignment)
: size_in_bytes_(size_in_bytes),
alignment_(alignment),
resource_context_(resource_context) {
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&RawTextureMemoryPBO::InitAndMapPBO, base::Unretained(this)));
}
void RawTextureMemoryPBO::InitAndMapPBO() {
resource_context_->AssertWithinResourceContext();
int size_to_allocate = size_in_bytes_ + alignment_;
// Allocate a GL pixel unpack buffer with the specified size, leaving enough
// room such that we can adjust for alignment.
GL_CALL(glGenBuffers(1, &pixel_buffer_));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, size_to_allocate, 0,
GL_STATIC_DRAW));
// Map the GL pixel buffer data to CPU addressable memory. We pass the flags
// MAP_INVALIDATE_BUFFER_BIT | MAP_UNSYNCHRONIZED_BIT to tell GL that it
// we don't care about previous data in the buffer and it shouldn't try to
// synchronize existing draw calls with it, both things which should be
// implied anyways since this is a brand new buffer, but we specify just in
// case.
mapped_data_ = static_cast<GLubyte*>(
glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, size_to_allocate,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT |
GL_MAP_UNSYNCHRONIZED_BIT));
DCHECK_EQ(GL_NO_ERROR, glGetError());
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
// Finally, determine the offset within |mapped_data_| necessary to acheive
// the desired alignment.
intptr_t alignment_remainder =
reinterpret_cast<size_t>(mapped_data_) % alignment_;
alignment_offset_ =
alignment_remainder == 0 ? 0 : alignment_ - alignment_remainder;
}
RawTextureMemoryPBO::~RawTextureMemoryPBO() {
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&RawTextureMemoryPBO::DestroyPBO, base::Unretained(this)));
}
void RawTextureMemoryPBO::DestroyPBO() {
resource_context_->AssertWithinResourceContext();
if (mapped_data_) {
// mapped_data_ will be non-null if we never call MakeConst() on it, in
// which case we should unmap it before continuing.
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
mapped_data_ = NULL;
}
// Clear our reference to the specified pixel buffer.
GL_CALL(glDeleteBuffers(1, &pixel_buffer_));
}
void RawTextureMemoryPBO::MakeConst() {
DCHECK(mapped_data_) << "MakeConst() should only ever be called once.";
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&RawTextureMemoryPBO::UnmapPBO, base::Unretained(this)));
}
void RawTextureMemoryPBO::UnmapPBO() {
resource_context_->AssertWithinResourceContext();
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
GL_CALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
mapped_data_ = NULL;
}
GLuint RawTextureMemoryPBO::CreateTexture(GraphicsContextEGL* graphics_context,
intptr_t offset,
const math::Size& size, GLenum format,
int pitch_in_bytes,
bool bgra_supported) const {
DCHECK(!mapped_data_)
<< "MakeConst() should be called before calling CreateTexture().";
GLuint texture_handle;
// Since we wish to create a texture within the specified graphics context,
// we do the texture creation work using that context.
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context);
// Our texture data remains mapped until this method is called, so we begin
// by unmapping it.
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_));
// Setup our texture.
GL_CALL(glGenTextures(1, &texture_handle));
GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_handle));
SetupInitialTextureParameters();
// Determine the texture's GL format.
if (format == GL_BGRA_EXT) {
DCHECK(bgra_supported);
}
// Create the texture. Since a GL_PIXEL_UNPACK_BUFFER is currently bound,
// the "data" field passed to glTexImage2D will refer to an offset into that
// buffer.
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH,
pitch_in_bytes / BytesPerPixelForGLFormat(format)));
GL_CALL(
glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0,
format, GL_UNSIGNED_BYTE,
reinterpret_cast<const void*>(alignment_offset_ + offset)));
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
return texture_handle;
}
} // namespace backend
} // namespace renderer
} // namespace cobalt
#endif // defined(GLES3_SUPPORTED)