| // 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_ = static_cast<int64>(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)); |
| CHECK(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) |