| /* |
| * Copyright 2014 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. |
| */ |
| |
| #include "cobalt/renderer/rasterizer/skia/hardware_image.h" |
| |
| #include "base/bind.h" |
| #include "base/debug/trace_event.h" |
| #include "cobalt/renderer/backend/egl/texture.h" |
| #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h" |
| #include "cobalt/renderer/rasterizer/skia/gl_format_conversions.h" |
| #include "third_party/skia/include/gpu/SkGrPixelRef.h" |
| |
| namespace cobalt { |
| namespace renderer { |
| namespace rasterizer { |
| namespace skia { |
| |
| GrTexture* WrapCobaltTextureWithSkiaTexture( |
| GrContext* gr_context, backend::TextureEGL* cobalt_texture) { |
| // Setup a Skia texture descriptor to describe the texture we wish to have |
| // wrapped within a Skia GrTexture. |
| GrBackendTextureDesc desc; |
| desc.fFlags = kNone_GrBackendTextureFlag; |
| desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
| desc.fWidth = cobalt_texture->GetSize().width(); |
| desc.fHeight = cobalt_texture->GetSize().height(); |
| desc.fConfig = ConvertGLFormatToGr(cobalt_texture->GetFormat()); |
| desc.fSampleCnt = 0; |
| |
| desc.fTextureHandle = |
| static_cast<GrBackendObject>(cobalt_texture->GetPlatformHandle()); |
| |
| return gr_context->wrapBackendTexture(desc); |
| } |
| |
| HardwareImageData::HardwareImageData( |
| scoped_ptr<backend::TextureDataEGL> texture_data, |
| render_tree::PixelFormat pixel_format, |
| render_tree::AlphaFormat alpha_format) |
| : texture_data_(texture_data.Pass()), |
| descriptor_(texture_data_->GetSize(), pixel_format, alpha_format, |
| texture_data_->GetPitchInBytes()) {} |
| |
| const render_tree::ImageDataDescriptor& HardwareImageData::GetDescriptor() |
| const { |
| return descriptor_; |
| } |
| |
| uint8_t* HardwareImageData::GetMemory() { return texture_data_->GetMemory(); } |
| |
| scoped_ptr<backend::TextureDataEGL> HardwareImageData::PassTextureData() { |
| return texture_data_.Pass(); |
| } |
| |
| HardwareRawImageMemory::HardwareRawImageMemory( |
| scoped_ptr<backend::RawTextureMemoryEGL> raw_texture_memory) |
| : raw_texture_memory_(raw_texture_memory.Pass()) {} |
| |
| size_t HardwareRawImageMemory::GetSizeInBytes() const { |
| return raw_texture_memory_->GetSizeInBytes(); |
| } |
| |
| uint8_t* HardwareRawImageMemory::GetMemory() { |
| return raw_texture_memory_->GetMemory(); |
| } |
| |
| scoped_ptr<backend::RawTextureMemoryEGL> |
| HardwareRawImageMemory::PassRawTextureMemory() { |
| return raw_texture_memory_.Pass(); |
| } |
| |
| // This will store the given pixel data in a GrTexture and the function |
| // GetBitmap(), overridden from SkiaImage, will return a reference to a SkBitmap |
| // object that refers to the image's GrTexture. This object should only be |
| // constructed, destructed and used from the same rasterizer thread. |
| class HardwareFrontendImage::HardwareBackendImage { |
| public: |
| HardwareBackendImage(scoped_ptr<HardwareImageData> image_data, |
| backend::GraphicsContextEGL* cobalt_context, |
| GrContext* gr_context) { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareBackendImage::HardwareBackendImage()"); |
| scoped_ptr<backend::TextureEGL> texture = |
| cobalt_context->CreateTexture(image_data->PassTextureData()); |
| |
| CommonInitialize(texture.Pass(), gr_context); |
| } |
| |
| HardwareBackendImage(const scoped_refptr<backend::ConstRawTextureMemoryEGL>& |
| raw_texture_memory, |
| intptr_t offset, |
| const render_tree::ImageDataDescriptor& descriptor, |
| backend::GraphicsContextEGL* cobalt_context, |
| GrContext* gr_context) { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareBackendImage::HardwareBackendImage()"); |
| scoped_ptr<backend::TextureEGL> texture = |
| cobalt_context->CreateTextureFromRawMemory( |
| raw_texture_memory, offset, descriptor.size, |
| ConvertRenderTreeFormatToGL(descriptor.pixel_format), |
| descriptor.pitch_in_bytes); |
| |
| CommonInitialize(texture.Pass(), gr_context); |
| } |
| |
| ~HardwareBackendImage() { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareBackendImage::~HardwareBackendImage()"); |
| // This object should always be destroyed from the thread that it was |
| // constructed on. |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| // Initiate all texture initialization code here, which should be executed |
| // on the rasterizer thread. |
| void CommonInitialize(scoped_ptr<backend::TextureEGL> texture, |
| GrContext* gr_context) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareBackendImage::CommonInitialize()"); |
| |
| texture_ = texture.Pass(); |
| gr_texture_.reset( |
| WrapCobaltTextureWithSkiaTexture(gr_context, texture_.get())); |
| DCHECK(gr_texture_); |
| |
| // Prepare a member SkBitmap that refers to the newly created GrTexture and |
| // will be the object that Skia draw calls will reference when referring |
| // to this image. |
| bitmap_.setInfo(gr_texture_->info()); |
| bitmap_.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (bitmap_.info(), gr_texture_))) |
| ->unref(); |
| } |
| |
| const SkBitmap& GetBitmap() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return bitmap_; |
| } |
| |
| private: |
| // Keep a reference to the texture alive as long as this backend image |
| // exists. |
| scoped_ptr<backend::TextureEGL> texture_; |
| |
| base::ThreadChecker thread_checker_; |
| SkAutoTUnref<GrTexture> gr_texture_; |
| SkBitmap bitmap_; |
| }; |
| |
| HardwareFrontendImage::HardwareFrontendImage( |
| scoped_ptr<HardwareImageData> image_data, |
| backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context, |
| MessageLoop* rasterizer_message_loop) |
| : is_opaque_(image_data->GetDescriptor().alpha_format == |
| render_tree::kAlphaFormatOpaque), |
| size_(image_data->GetDescriptor().size), |
| rasterizer_message_loop_(rasterizer_message_loop) { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareFrontendImage::HardwareFrontendImage()"); |
| |
| initialize_backend_image_ = |
| base::Bind(&HardwareFrontendImage::InitializeBackendImageFromImageData, |
| base::Unretained(this), base::Passed(&image_data), |
| cobalt_context, gr_context); |
| } |
| |
| HardwareFrontendImage::HardwareFrontendImage( |
| const scoped_refptr<backend::ConstRawTextureMemoryEGL>& raw_texture_memory, |
| intptr_t offset, const render_tree::ImageDataDescriptor& descriptor, |
| backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context, |
| MessageLoop* rasterizer_message_loop) |
| : is_opaque_(descriptor.alpha_format == render_tree::kAlphaFormatOpaque), |
| size_(descriptor.size), |
| rasterizer_message_loop_(rasterizer_message_loop) { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareFrontendImage::HardwareFrontendImage()"); |
| initialize_backend_image_ = |
| base::Bind(&HardwareFrontendImage::InitializeBackendImageFromRawImageData, |
| base::Unretained(this), raw_texture_memory, offset, descriptor, |
| cobalt_context, gr_context); |
| } |
| |
| HardwareFrontendImage::~HardwareFrontendImage() { |
| TRACE_EVENT0("cobalt::renderer", |
| "HardwareFrontendImage::~HardwareFrontendImage()"); |
| // If we are destroying this image from a non-rasterizer thread, we still must |
| // ensure that the |backend_image_| is destroyed from the rasterizer thread, |
| // if |backend_image_| was ever constructed in the first place. |
| if (backend_image_ && rasterizer_message_loop_ && |
| rasterizer_message_loop_ != MessageLoop::current()) { |
| rasterizer_message_loop_->DeleteSoon(FROM_HERE, backend_image_.release()); |
| } // else let the scoped pointer clean it up immediately. |
| } |
| |
| const SkBitmap& HardwareFrontendImage::GetBitmap() const { |
| DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current()); |
| // Forward this call to the backend image. This method must be called from |
| // the rasterizer thread (e.g. during a render tree visitation). The backend |
| // image will check that this is being called from the correct thread. |
| return backend_image_->GetBitmap(); |
| } |
| |
| bool HardwareFrontendImage::EnsureInitialized() { |
| DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current()); |
| if (!initialize_backend_image_.is_null()) { |
| initialize_backend_image_.Run(); |
| initialize_backend_image_.Reset(); |
| return true; |
| } |
| return false; |
| } |
| |
| void HardwareFrontendImage::InitializeBackendImageFromImageData( |
| scoped_ptr<HardwareImageData> image_data, |
| backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context) { |
| DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current()); |
| backend_image_.reset( |
| new HardwareBackendImage(image_data.Pass(), cobalt_context, gr_context)); |
| } |
| |
| void HardwareFrontendImage::InitializeBackendImageFromRawImageData( |
| const scoped_refptr<backend::ConstRawTextureMemoryEGL>& raw_texture_memory, |
| intptr_t offset, const render_tree::ImageDataDescriptor& descriptor, |
| backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context) { |
| DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current()); |
| backend_image_.reset(new HardwareBackendImage( |
| raw_texture_memory, offset, descriptor, cobalt_context, gr_context)); |
| } |
| |
| HardwareMultiPlaneImage::HardwareMultiPlaneImage( |
| scoped_ptr<HardwareRawImageMemory> raw_image_memory, |
| const render_tree::MultiPlaneImageDataDescriptor& descriptor, |
| backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context, |
| MessageLoop* rasterizer_message_loop) |
| : size_(descriptor.GetPlaneDescriptor(0).size), |
| format_(descriptor.image_format()) { |
| scoped_refptr<backend::ConstRawTextureMemoryEGL> const_raw_texture_memory( |
| new backend::ConstRawTextureMemoryEGL( |
| raw_image_memory->PassRawTextureMemory())); |
| |
| // Construct a single plane image for each plane of this multi plane image. |
| for (int i = 0; i < descriptor.num_planes(); ++i) { |
| planes_[i] = new HardwareFrontendImage( |
| const_raw_texture_memory, descriptor.GetPlaneOffset(i), |
| descriptor.GetPlaneDescriptor(i), cobalt_context, gr_context, |
| rasterizer_message_loop); |
| } |
| } |
| |
| HardwareMultiPlaneImage::~HardwareMultiPlaneImage() {} |
| |
| bool HardwareMultiPlaneImage::EnsureInitialized() { |
| // A multi-plane image is not considered backend-initialized until all its |
| // single-plane images are backend-initialized, thus we ensure that all |
| // the component images are backend-initialized. |
| bool initialized = false; |
| for (int i = 0; i < render_tree::MultiPlaneImageDataDescriptor::kMaxPlanes; |
| ++i) { |
| if (planes_[i]) { |
| initialized |= planes_[i]->EnsureInitialized(); |
| } |
| } |
| return initialized; |
| } |
| |
| } // namespace skia |
| } // namespace rasterizer |
| } // namespace renderer |
| } // namespace cobalt |