blob: f8096b0608d41aaf09c81f5880d24f9237f7885d [file] [log] [blame]
/*
* 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)
: 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)
: 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();
}
void HardwareFrontendImage::EnsureInitialized() {
DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current());
if (!initialize_backend_image_.is_null()) {
initialize_backend_image_.Run();
initialize_backend_image_.Reset();
}
}
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() {}
void 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.
for (int i = 0; i < render_tree::MultiPlaneImageDataDescriptor::kMaxPlanes;
++i) {
if (planes_[i]) {
planes_[i]->EnsureInitialized();
}
}
}
} // namespace skia
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt