diff options
Diffstat (limited to 'content/browser/renderer_host/accelerated_surface_container_mac.cc')
-rw-r--r-- | content/browser/renderer_host/accelerated_surface_container_mac.cc | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/content/browser/renderer_host/accelerated_surface_container_mac.cc b/content/browser/renderer_host/accelerated_surface_container_mac.cc new file mode 100644 index 0000000..a661c92 --- /dev/null +++ b/content/browser/renderer_host/accelerated_surface_container_mac.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/accelerated_surface_container_mac.h" + +#include "app/surface/io_surface_support_mac.h" +#include "base/logging.h" +#include "content/browser/renderer_host/accelerated_surface_container_manager_mac.h" +#include "webkit/plugins/npapi/webplugin.h" + +AcceleratedSurfaceContainerMac::AcceleratedSurfaceContainerMac( + AcceleratedSurfaceContainerManagerMac* manager, + bool opaque) + : manager_(manager), + opaque_(opaque), + surface_id_(0), + width_(0), + height_(0), + texture_(0), + texture_needs_upload_(true), + texture_pending_deletion_(0), + visible_(false), + was_painted_to_(false) { +} + +AcceleratedSurfaceContainerMac::~AcceleratedSurfaceContainerMac() { +} + +void AcceleratedSurfaceContainerMac::SetSizeAndIOSurface( + int32 width, + int32 height, + uint64 io_surface_identifier) { + // Ignore |io_surface_identifier|: The surface hasn't been painted to and + // only contains garbage data. Update the surface in |set_was_painted_to()| + // instead. + width_ = width; + height_ = height; +} + +void AcceleratedSurfaceContainerMac::SetSizeAndTransportDIB( + int32 width, + int32 height, + TransportDIB::Handle transport_dib) { + if (TransportDIB::is_valid(transport_dib)) { + transport_dib_.reset(TransportDIB::Map(transport_dib)); + EnqueueTextureForDeletion(); + width_ = width; + height_ = height; + } +} + +void AcceleratedSurfaceContainerMac::SetGeometry( + const webkit::npapi::WebPluginGeometry& geom) { + visible_ = geom.visible; + if (geom.rects_valid) + clip_rect_ = geom.clip_rect; +} + +void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) { + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); + GLenum target = GL_TEXTURE_RECTANGLE_ARB; + if (texture_pending_deletion_) { + // Clean up an old texture object. This is essentially a pre-emptive + // cleanup, as the resources will be released when the OpenGL context + // associated with our containing NSView is destroyed. However, if we + // resize a plugin often, we might generate a lot of textures, so we + // should try to eagerly reclaim their resources. Note also that the + // OpenGL context must be current when performing the deletion, and it + // seems risky to make the OpenGL context current at an arbitrary point + // in time, which is why the deletion does not occur in the container's + // destructor. + glDeleteTextures(1, &texture_pending_deletion_); + texture_pending_deletion_ = 0; + } + if (!texture_) { + if ((io_surface_support && !surface_.get()) || + (!io_surface_support && !transport_dib_.get())) + return; + glGenTextures(1, &texture_); + glBindTexture(target, texture_); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + if (io_surface_support) { + texture_needs_upload_ = true; + } else { + // Reserve space on the card for the actual texture upload, done with the + // glTexSubImage2D() call, below. + glTexImage2D(target, + 0, // mipmap level 0 + GL_RGBA, // internal format + width_, + height_, + 0, // no border + GL_BGRA, // The GPU plugin read BGRA pixels + GL_UNSIGNED_INT_8_8_8_8_REV, + NULL); // No data, this call just reserves room. + } + } + + // When using an IOSurface, the texture does not need to be repeatedly + // uploaded, just when we've been told we have to. + if (io_surface_support && texture_needs_upload_) { + DCHECK(surface_.get()); + glBindTexture(target, texture_); + // Don't think we need to identify a plane. + GLuint plane = 0; + io_surface_support->CGLTexImageIOSurface2D(context, + target, + GL_RGBA, + surface_width_, + surface_height_, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + surface_.get(), + plane); + texture_needs_upload_ = false; + } + // If using TransportDIBs, the texture needs to be uploaded every frame. + if (transport_dib_.get() != NULL) { + void* pixel_memory = transport_dib_->memory(); + if (pixel_memory) { + glBindTexture(target, texture_); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Needed for NPOT textures. + glTexSubImage2D(target, + 0, // mipmap level 0 + 0, // x-offset + 0, // y-offset + width_, + height_, + GL_BGRA, // The GPU plugin gave us BGRA pixels + GL_UNSIGNED_INT_8_8_8_8_REV, + pixel_memory); + } + } + + if (texture_) { + int texture_width = io_surface_support ? surface_width_ : width_; + int texture_height = io_surface_support ? surface_height_ : height_; + + // TODO(kbr): convert this to use only OpenGL ES 2.0 functionality. + + // TODO(kbr): may need to pay attention to cutout rects. + int clipX = clip_rect_.x(); + int clipY = clip_rect_.y(); + int clipWidth = clip_rect_.width(); + int clipHeight = clip_rect_.height(); + + if (clipX + clipWidth > texture_width) + clipWidth = texture_width - clipX; + if (clipY + clipHeight > texture_height) + clipHeight = texture_height - clipY; + + if (opaque_) { + // Pepper 3D's output is currently considered opaque even if the + // program draws pixels with alpha less than 1. In order to have + // this effect, we need to drop the alpha channel of the input, + // replacing it with alpha = 1. + + // First fill the rectangle with alpha=1. + glColorMask(false, false, false, true); + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(0, 0, 0); + glVertex3f(clipWidth, 0, 0); + glVertex3f(0, clipHeight, 0); + glVertex3f(clipWidth, clipHeight, 0); + glEnd(); + + // Now draw the color channels from the incoming texture. + glColorMask(true, true, true, false); + // This call shouldn't be necessary -- we are using the GL_REPLACE + // texture environment mode -- but it appears to be. + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } else { + glColorMask(true, true, true, true); + } + + // Draw the color channels from the incoming texture. + glBindTexture(target, texture_); + glEnable(target); + glBegin(GL_TRIANGLE_STRIP); + + glTexCoord2f(clipX, texture_height - clipY); + glVertex3f(0, 0, 0); + + glTexCoord2f(clipX + clipWidth, texture_height - clipY); + glVertex3f(clipWidth, 0, 0); + + glTexCoord2f(clipX, texture_height - clipY - clipHeight); + glVertex3f(0, clipHeight, 0); + + glTexCoord2f(clipX + clipWidth, texture_height - clipY - clipHeight); + glVertex3f(clipWidth, clipHeight, 0); + + glEnd(); + glDisable(target); + } +} + +bool AcceleratedSurfaceContainerMac::ShouldBeVisible() const { + return visible_ && was_painted_to_ && !clip_rect_.IsEmpty(); +} + +void AcceleratedSurfaceContainerMac::set_was_painted_to(uint64 surface_id) { + if (surface_id && (!surface_ || surface_id != surface_id_)) { + // Keep the surface that was most recently painted to around. + if (IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize()) { + CFTypeRef surface = io_surface_support->IOSurfaceLookup( + static_cast<uint32>(surface_id)); + // Can fail if IOSurface with that ID was already released by the + // gpu process or the plugin process. We will get a |set_was_painted_to()| + // message with a new surface soon in that case. + if (surface) { + surface_.reset(surface); + surface_id_ = surface_id; + surface_width_ = io_surface_support->IOSurfaceGetWidth(surface_); + surface_height_ = io_surface_support->IOSurfaceGetHeight(surface_); + EnqueueTextureForDeletion(); + } + } + } + was_painted_to_ = true; +} + +void AcceleratedSurfaceContainerMac::EnqueueTextureForDeletion() { + if (texture_) { + DCHECK(texture_pending_deletion_ == 0); + texture_pending_deletion_ = texture_; + texture_ = 0; + } +} + |