summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/graphics/gpu
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:20:33 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:28:57 +0000
commitd17ea114e5ef69ad5d5d7413280a13e6428098aa (patch)
tree2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/platform/graphics/gpu
parent8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff)
downloadqtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/graphics/gpu')
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS13
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc1603
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h585
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc117
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc744
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h452
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc217
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc178
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc272
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc3260
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h293
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc173
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc266
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h100
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc626
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h164
25 files changed, 9640 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS
new file mode 100644
index 00000000000..adce483a8d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+mojo/public/cpp/bindings/binding.h",
+ "+mojo/public/cpp/system/platform_handle.h",
+ "+device/vr/public/mojom/vr_service.mojom-blink.h",
+ "+gpu/command_buffer/client/gles2_interface.h",
+ "+gpu/command_buffer/client/gles2_interface_stub.h",
+ "+gpu/command_buffer/common/capabilities.h",
+ "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
+ "+gpu/command_buffer/common/mailbox_holder.h",
+ "+gpu/config/gpu_driver_bug_workaround_type.h",
+ "+gpu/config/gpu_feature_info.h",
+ "+ui/gfx/gpu_fence.h",
+]
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS b/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS
new file mode 100644
index 00000000000..45ab8c73e8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS
@@ -0,0 +1,4 @@
+# The bulk of the files in this directory are related to WebGL, though
+# there are a couple used by accelerated 2D canvas as well.
+
+# COMPONENT: Blink>WebGL
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
new file mode 100644
index 00000000000..dded64ab9ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -0,0 +1,1603 @@
+/*
+ * Copyright (c) 2010, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "cc/layers/texture_layer.h"
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/bitmap_allocation.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+namespace {
+
+const float kResourceAdjustedRatio = 0.5;
+
+static bool g_should_fail_drawing_buffer_creation_for_testing = false;
+
+} // namespace
+
+scoped_refptr<DrawingBuffer> DrawingBuffer::Create(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ Client* client,
+ const IntSize& size,
+ bool premultiplied_alpha,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ PreserveDrawingBuffer preserve,
+ WebGLVersion webgl_version,
+ ChromiumImageUsage chromium_image_usage,
+ const CanvasColorParams& color_params) {
+ if (g_should_fail_drawing_buffer_creation_for_testing) {
+ g_should_fail_drawing_buffer_creation_for_testing = false;
+ return nullptr;
+ }
+
+ CheckedNumeric<int> data_size = color_params.BytesPerPixel();
+ data_size *= size.Width();
+ data_size *= size.Height();
+ if (!data_size.IsValid() ||
+ data_size.ValueOrDie() > v8::TypedArray::kMaxLength)
+ return nullptr;
+
+ DCHECK(context_provider);
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(context_provider->ContextGL());
+ if (!extensions_util->IsValid()) {
+ // This might be the first time we notice that the GL context is lost.
+ return nullptr;
+ }
+ DCHECK(extensions_util->SupportsExtension("GL_OES_packed_depth_stencil"));
+ extensions_util->EnsureExtensionEnabled("GL_OES_packed_depth_stencil");
+ bool multisample_supported =
+ want_antialiasing &&
+ (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample") ||
+ extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) &&
+ extensions_util->SupportsExtension("GL_OES_rgb8_rgba8");
+ if (multisample_supported) {
+ extensions_util->EnsureExtensionEnabled("GL_OES_rgb8_rgba8");
+ if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample")) {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_CHROMIUM_framebuffer_multisample");
+ } else {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_EXT_multisampled_render_to_texture");
+ }
+ }
+ bool discard_framebuffer_supported =
+ extensions_util->SupportsExtension("GL_EXT_discard_framebuffer");
+ if (discard_framebuffer_supported)
+ extensions_util->EnsureExtensionEnabled("GL_EXT_discard_framebuffer");
+
+ scoped_refptr<DrawingBuffer> drawing_buffer =
+ base::AdoptRef(new DrawingBuffer(
+ std::move(context_provider), using_gpu_compositing,
+ std::move(extensions_util), client, discard_framebuffer_supported,
+ want_alpha_channel, premultiplied_alpha, preserve, webgl_version,
+ want_depth_buffer, want_stencil_buffer, chromium_image_usage,
+ color_params));
+ if (!drawing_buffer->Initialize(size, multisample_supported)) {
+ drawing_buffer->BeginDestruction();
+ return scoped_refptr<DrawingBuffer>();
+ }
+ return drawing_buffer;
+}
+
+void DrawingBuffer::ForceNextDrawingBufferCreationToFail() {
+ g_should_fail_drawing_buffer_creation_for_testing = true;
+}
+
+DrawingBuffer::DrawingBuffer(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil> extensions_util,
+ Client* client,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool premultiplied_alpha,
+ PreserveDrawingBuffer preserve,
+ WebGLVersion webgl_version,
+ bool want_depth,
+ bool want_stencil,
+ ChromiumImageUsage chromium_image_usage,
+ const CanvasColorParams& color_params)
+ : client_(client),
+ preserve_drawing_buffer_(preserve),
+ webgl_version_(webgl_version),
+ context_provider_(std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider))),
+ gl_(this->ContextProvider()->ContextGL()),
+ extensions_util_(std::move(extensions_util)),
+ discard_framebuffer_supported_(discard_framebuffer_supported),
+ want_alpha_channel_(want_alpha_channel),
+ premultiplied_alpha_(premultiplied_alpha),
+ using_gpu_compositing_(using_gpu_compositing),
+ want_depth_(want_depth),
+ want_stencil_(want_stencil),
+ storage_color_space_(color_params.GetStorageGfxColorSpace()),
+ sampler_color_space_(color_params.GetSamplerGfxColorSpace()),
+ use_half_float_storage_(color_params.PixelFormat() ==
+ kF16CanvasPixelFormat),
+ chromium_image_usage_(chromium_image_usage) {
+ // Used by browser tests to detect the use of a DrawingBuffer.
+ TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation",
+ TRACE_EVENT_SCOPE_GLOBAL);
+}
+
+DrawingBuffer::~DrawingBuffer() {
+ DCHECK(destruction_in_progress_);
+ layer_.reset();
+ context_provider_.reset();
+}
+
+bool DrawingBuffer::MarkContentsChanged() {
+ if (contents_change_resolved_ || !contents_changed_) {
+ contents_change_resolved_ = false;
+ contents_changed_ = true;
+ return true;
+ }
+ return false;
+}
+
+void DrawingBuffer::ResetBuffersToAutoClear() {
+ GLuint buffers = GL_COLOR_BUFFER_BIT;
+ if (want_depth_)
+ buffers |= GL_DEPTH_BUFFER_BIT;
+ if (want_stencil_ || has_implicit_stencil_buffer_)
+ buffers |= GL_STENCIL_BUFFER_BIT;
+ SetBuffersToAutoClear(buffers);
+}
+
+void DrawingBuffer::SetBuffersToAutoClear(GLbitfield buffers) {
+ if (preserve_drawing_buffer_ == kDiscard) {
+ buffers_to_auto_clear_ = buffers;
+ } else {
+ DCHECK_EQ(0u, buffers_to_auto_clear_);
+ }
+}
+
+GLbitfield DrawingBuffer::GetBuffersToAutoClear() const {
+ return buffers_to_auto_clear_;
+}
+
+gpu::gles2::GLES2Interface* DrawingBuffer::ContextGL() {
+ return gl_;
+}
+
+WebGraphicsContext3DProvider* DrawingBuffer::ContextProvider() {
+ return context_provider_->ContextProvider();
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+DrawingBuffer::ContextProviderWeakPtr() {
+ return context_provider_->GetWeakPtr();
+}
+
+void DrawingBuffer::SetIsHidden(bool hidden) {
+ if (is_hidden_ == hidden)
+ return;
+ is_hidden_ = hidden;
+ if (is_hidden_)
+ recycled_color_buffer_queue_.clear();
+}
+
+void DrawingBuffer::SetFilterQuality(SkFilterQuality filter_quality) {
+ if (filter_quality_ != filter_quality) {
+ filter_quality_ = filter_quality;
+ if (layer_)
+ layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
+ }
+}
+
+bool DrawingBuffer::RequiresAlphaChannelToBePreserved() {
+ return client_->DrawingBufferClientIsBoundForDraw() &&
+ DefaultBufferRequiresAlphaChannelToBePreserved();
+}
+
+bool DrawingBuffer::DefaultBufferRequiresAlphaChannelToBePreserved() {
+ return !want_alpha_channel_ && have_alpha_channel_;
+}
+
+DrawingBuffer::RegisteredBitmap DrawingBuffer::CreateOrRecycleBitmap(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar) {
+ auto it = std::remove_if(recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
+ [this](const RegisteredBitmap& registered) {
+ return registered.bitmap->size() != size_;
+ });
+ recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
+
+ if (!recycled_bitmaps_.IsEmpty()) {
+ RegisteredBitmap recycled = std::move(recycled_bitmaps_.back());
+ recycled_bitmaps_.pop_back();
+ DCHECK(recycled.bitmap->size() == size_);
+ return recycled;
+ }
+
+ viz::SharedBitmapId id = viz::SharedBitmap::GenerateId();
+ std::unique_ptr<base::SharedMemory> shm =
+ viz::bitmap_allocation::AllocateMappedBitmap(size_, viz::RGBA_8888);
+ auto bitmap = base::MakeRefCounted<cc::CrossThreadSharedBitmap>(
+ id, std::move(shm), size_, viz::RGBA_8888);
+ RegisteredBitmap registered = {
+ bitmap, bitmap_registrar->RegisterSharedBitmapId(id, bitmap)};
+ return registered;
+}
+
+bool DrawingBuffer::PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ bool force_gpu_result = false;
+ return PrepareTransferableResourceInternal(
+ bitmap_registrar, out_resource, out_release_callback, force_gpu_result);
+}
+
+bool DrawingBuffer::PrepareTransferableResourceInternal(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback,
+ bool force_gpu_result) {
+ DCHECK(state_restorer_);
+ if (destruction_in_progress_) {
+ // It can be hit in the following sequence.
+ // 1. WebGL draws something.
+ // 2. The compositor begins the frame.
+ // 3. Javascript makes a context lost using WEBGL_lose_context extension.
+ // 4. Here.
+ return false;
+ }
+ DCHECK(!is_hidden_);
+ if (!contents_changed_)
+ return false;
+
+ // If the context is lost, we don't know if we should be producing GPU or
+ // software frames, until we get a new context, since the compositor will
+ // be trying to get a new context and may change modes.
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
+ return false;
+
+ TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox");
+
+ // Resolve the multisampled buffer into the texture attached to fbo_.
+ ResolveIfNeeded();
+
+ if (!using_gpu_compositing_ && !force_gpu_result) {
+ FinishPrepareTransferableResourceSoftware(bitmap_registrar, out_resource,
+ out_release_callback);
+ } else {
+ FinishPrepareTransferableResourceGpu(out_resource, out_release_callback);
+ }
+ return true;
+}
+
+void DrawingBuffer::FinishPrepareTransferableResourceSoftware(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ DCHECK(state_restorer_);
+ RegisteredBitmap registered = CreateOrRecycleBitmap(bitmap_registrar);
+
+ // Read the framebuffer into |bitmap|.
+ {
+ unsigned char* pixels = static_cast<unsigned char*>(
+ registered.bitmap->shared_memory()->memory());
+ DCHECK(pixels);
+ bool need_premultiply = want_alpha_channel_ && !premultiplied_alpha_;
+ WebGLImageConversion::AlphaOp op =
+ need_premultiply ? WebGLImageConversion::kAlphaDoPremultiply
+ : WebGLImageConversion::kAlphaDoNothing;
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ ReadBackFramebuffer(pixels, Size().Width(), Size().Height(), kReadbackSkia,
+ op);
+ }
+
+ *out_resource = viz::TransferableResource::MakeSoftware(
+ registered.bitmap->id(), /*sequence_number=*/0, size_, viz::RGBA_8888);
+ out_resource->color_space = storage_color_space_;
+
+ // This holds a ref on the DrawingBuffer that will keep it alive until the
+ // mailbox is released (and while the release callback is running). It also
+ // owns the SharedBitmap.
+ auto func = WTF::Bind(&DrawingBuffer::MailboxReleasedSoftware,
+ scoped_refptr<DrawingBuffer>(this),
+ WTF::Passed(std::move(registered)));
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+
+ ResetBuffersToAutoClear();
+}
+
+void DrawingBuffer::FinishPrepareTransferableResourceGpu(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ DCHECK(state_restorer_);
+ if (webgl_version_ > kWebGL1) {
+ state_restorer_->SetPixelUnpackBufferBindingDirty();
+ gl_->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+
+ if (premultiplied_alpha_false_texture_) {
+ // The rendering results are in this texture rather than the
+ // back_color_buffer_'s texture. Copy them in, multiplying the alpha channel
+ // into the color channels.
+ gl_->CopySubTextureCHROMIUM(premultiplied_alpha_false_texture_, 0,
+ texture_target_, back_color_buffer_->texture_id,
+ 0, 0, 0, 0, 0, size_.Width(), size_.Height(),
+ GL_FALSE, GL_TRUE, GL_FALSE);
+ }
+
+ // Specify the buffer that we will put in the mailbox.
+ scoped_refptr<ColorBuffer> color_buffer_for_mailbox;
+ if (preserve_drawing_buffer_ == kDiscard) {
+ // If we can discard the backbuffer, send the old backbuffer directly
+ // into the mailbox, and allocate (or recycle) a new backbuffer.
+ color_buffer_for_mailbox = back_color_buffer_;
+ back_color_buffer_ = CreateOrRecycleColorBuffer();
+ AttachColorBufferToReadFramebuffer();
+
+ // Explicitly specify that m_fbo (which is now bound to the just-allocated
+ // m_backColorBuffer) is not initialized, to save GPU memory bandwidth for
+ // tile-based GPU architectures.
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+ } else {
+ // If we can't discard the backbuffer, create (or recycle) a buffer to put
+ // in the mailbox, and copy backbuffer's contents there.
+ color_buffer_for_mailbox = CreateOrRecycleColorBuffer();
+ gl_->CopySubTextureCHROMIUM(
+ back_color_buffer_->texture_id, 0, texture_target_,
+ color_buffer_for_mailbox->texture_id, 0, 0, 0, 0, 0, size_.Width(),
+ size_.Height(), GL_FALSE, GL_FALSE, GL_FALSE);
+ }
+
+ // Put colorBufferForMailbox into its mailbox, and populate its
+ // produceSyncToken with that point.
+ {
+ gl_->ProduceTextureDirectCHROMIUM(color_buffer_for_mailbox->texture_id,
+ color_buffer_for_mailbox->mailbox.name);
+ // It's critical to order the execution of this context's work relative
+ // to other contexts, in particular the compositor. Previously this
+ // used to be a Flush, and there was a bug that we didn't flush before
+ // synchronizing with the composition, and on some platforms this caused
+ // incorrect rendering with complex WebGL content that wasn't always
+ // properly flushed to the driver. There is now a basic assumption that
+ // there are implicit flushes between contexts at the lowest level.
+ gl_->GenUnverifiedSyncTokenCHROMIUM(
+ color_buffer_for_mailbox->produce_sync_token.GetData());
+#if defined(OS_MACOSX)
+ // Needed for GPU back-pressure on macOS. Used to be in the middle
+ // of the commands above; try to move it to the bottom to allow
+ // them to be treated atomically.
+ gl_->DescheduleUntilFinishedCHROMIUM();
+#endif
+ }
+
+ // Populate the output mailbox and callback.
+ {
+ bool is_overlay_candidate = color_buffer_for_mailbox->image_id != 0;
+ *out_resource = viz::TransferableResource::MakeGLOverlay(
+ color_buffer_for_mailbox->mailbox, GL_LINEAR, texture_target_,
+ color_buffer_for_mailbox->produce_sync_token, gfx::Size(size_),
+ is_overlay_candidate);
+ out_resource->color_space = sampler_color_space_;
+ out_resource->format = viz::RGBA_8888;
+
+ // This holds a ref on the DrawingBuffer that will keep it alive until the
+ // mailbox is released (and while the release callback is running).
+ auto func =
+ WTF::Bind(&DrawingBuffer::MailboxReleasedGpu,
+ scoped_refptr<DrawingBuffer>(this), color_buffer_for_mailbox);
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ }
+
+ // Point |m_frontColorBuffer| to the buffer that we are now presenting.
+ front_color_buffer_ = color_buffer_for_mailbox;
+
+ contents_changed_ = false;
+ ResetBuffersToAutoClear();
+}
+
+void DrawingBuffer::MailboxReleasedGpu(scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ // If the mailbox has been returned by the compositor then it is no
+ // longer being presented, and so is no longer the front buffer.
+ if (color_buffer == front_color_buffer_)
+ front_color_buffer_ = nullptr;
+
+ // Update the SyncToken to ensure that we will wait for it even if we
+ // immediately destroy this buffer.
+ color_buffer->receive_sync_token = sync_token;
+
+ if (destruction_in_progress_ || color_buffer->size != size_ ||
+ gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lost_resource ||
+ is_hidden_) {
+ return;
+ }
+
+ // Creation of image backed mailboxes is very expensive, so be less
+ // aggressive about pruning them. Pruning is done in FIFO order.
+ size_t cache_limit = 1;
+ if (ShouldUseChromiumImage())
+ cache_limit = 4;
+ while (recycled_color_buffer_queue_.size() >= cache_limit)
+ recycled_color_buffer_queue_.TakeLast();
+
+ recycled_color_buffer_queue_.push_front(color_buffer);
+}
+
+void DrawingBuffer::MailboxReleasedSoftware(RegisteredBitmap registered,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ DCHECK(!sync_token.HasData()); // No sync tokens for software resources.
+ if (destruction_in_progress_ || lost_resource || is_hidden_ ||
+ registered.bitmap->size() != size_) {
+ // Just delete the RegisteredBitmap, which will free the memory and
+ // unregister it with the compositor.
+ return;
+ }
+
+ recycled_bitmaps_.push_back(std::move(registered));
+}
+
+scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ viz::TransferableResource transferable_resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ constexpr bool force_gpu_result = true;
+ if (!PrepareTransferableResourceInternal(nullptr, &transferable_resource,
+ &release_callback,
+ force_gpu_result)) {
+ // If we can't get a mailbox, return an transparent black ImageBitmap.
+ // The only situation in which this could happen is when two or more calls
+ // to transferToImageBitmap are made back-to-back, or when the context gets
+ // lost. We intentionally leave the transparent black image in legacy color
+ // space.
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
+ return StaticBitmapImage::Create(surface->makeImageSnapshot());
+ }
+
+ DCHECK_EQ(size_.Width(), transferable_resource.size.width());
+ DCHECK_EQ(size_.Height(), transferable_resource.size.height());
+
+ // Make our own textureId that is a reference on the same texture backing
+ // being used as the front buffer (which was returned from
+ // PrepareTransferableResourceInternal()). We do not need to wait on the sync
+ // token in |transferable_resource| since the mailbox was produced on the same
+ // |m_gl| context that we are using here. Similarly, the |release_callback|
+ // will run on the same context so we don't need to send a sync token for this
+ // consume action back to it.
+ // TODO(danakj): Instead of using PrepareTransferableResourceInternal(), we
+ // could just use the actual texture id and avoid needing to produce/consume a
+ // mailbox.
+ GLuint texture_id = gl_->CreateAndConsumeTextureCHROMIUM(
+ transferable_resource.mailbox_holder.mailbox.name);
+
+ if (out_release_callback) {
+ // Allow the consumer to release the resource when done using it, so it can
+ // be recycled.
+ *out_release_callback = std::move(release_callback);
+ } else {
+ // Return the mailbox but report that the resource is lost to prevent trying
+ // to use the backing for future frames. We keep it alive with our own
+ // reference to the backing via our |textureId|.
+ release_callback->Run(gpu::SyncToken(), true /* lost_resource */);
+ }
+
+ // We reuse the same mailbox name from above since our texture id was consumed
+ // from it.
+ const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
+ // Use the sync token generated after producing the mailbox. Waiting for this
+ // before trying to use the mailbox with some other context will ensure it is
+ // valid. We wouldn't need to wait for the consume done in this function
+ // because the texture id it generated would only be valid for the
+ // DrawingBuffer's context anyways.
+ const auto& sk_image_sync_token =
+ transferable_resource.mailbox_holder.sync_token;
+
+ // TODO(xidachen): Create a small pool of recycled textures from
+ // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them
+ // in DrawingBuffer.
+ return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ sk_image_mailbox, sk_image_sync_token, texture_id,
+ context_provider_->GetWeakPtr(), size_);
+}
+
+scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateOrRecycleColorBuffer() {
+ DCHECK(state_restorer_);
+ if (!recycled_color_buffer_queue_.IsEmpty()) {
+ scoped_refptr<ColorBuffer> recycled =
+ recycled_color_buffer_queue_.TakeLast();
+ if (recycled->receive_sync_token.HasData())
+ gl_->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
+ DCHECK(recycled->size == size_);
+ return recycled;
+ }
+ return CreateColorBuffer(size_);
+}
+
+DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer::
+ ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer* drawing_buffer,
+ bool is_user_draw_framebuffer_bound)
+ : drawing_buffer_(drawing_buffer) {
+ doing_work_ = drawing_buffer->SetupRGBEmulationForBlitFramebuffer(
+ is_user_draw_framebuffer_bound);
+}
+
+DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer::
+ ~ScopedRGBEmulationForBlitFramebuffer() {
+ if (doing_work_) {
+ drawing_buffer_->CleanupRGBEmulationForBlitFramebuffer();
+ }
+}
+
+DrawingBuffer::ColorBuffer::ColorBuffer(
+ DrawingBuffer* drawing_buffer,
+ const IntSize& size,
+ GLuint texture_id,
+ GLuint image_id,
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer)
+ : drawing_buffer(drawing_buffer),
+ size(size),
+ texture_id(texture_id),
+ image_id(image_id),
+ gpu_memory_buffer(std::move(gpu_memory_buffer)) {
+ drawing_buffer->ContextGL()->GenMailboxCHROMIUM(mailbox.name);
+}
+
+DrawingBuffer::ColorBuffer::~ColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->gl_;
+ GLenum texture_target = drawing_buffer->texture_target_;
+ if (receive_sync_token.HasData())
+ gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
+ if (image_id) {
+ gl->BindTexture(texture_target, texture_id);
+ gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
+ if (rgb_workaround_texture_id) {
+ gl->BindTexture(texture_target, rgb_workaround_texture_id);
+ gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
+ }
+ gl->DestroyImageCHROMIUM(image_id);
+ switch (texture_target) {
+ case GL_TEXTURE_2D:
+ // Restore the texture binding for GL_TEXTURE_2D, since the client will
+ // expect the previous state.
+ if (drawing_buffer->client_)
+ drawing_buffer->client_->DrawingBufferClientRestoreTexture2DBinding();
+ break;
+ case GC3D_TEXTURE_RECTANGLE_ARB:
+ // Rectangle textures aren't exposed to WebGL, so don't bother
+ // restoring this state (there is no meaningful way to restore it).
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ gpu_memory_buffer.reset();
+ }
+ gl->DeleteTextures(1, &texture_id);
+ if (rgb_workaround_texture_id) {
+ // Avoid deleting this texture if it was unused.
+ gl->DeleteTextures(1, &rgb_workaround_texture_id);
+ }
+}
+
+bool DrawingBuffer::Initialize(const IntSize& size, bool use_multisampling) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // Need to try to restore the context again later.
+ DLOG(ERROR) << "Cannot initialize with lost context.";
+ return false;
+ }
+
+ // Specifying a half-float backbuffer requires and implicitly enables
+ // half-float backbuffer extensions.
+ if (use_half_float_storage_) {
+ const char* color_buffer_extension = webgl_version_ > kWebGL1
+ ? "GL_EXT_color_buffer_float"
+ : "GL_EXT_color_buffer_half_float";
+ if (!extensions_util_->EnsureExtensionEnabled(color_buffer_extension)) {
+ DLOG(ERROR) << "Half-float color buffer support is absent.";
+ return false;
+ }
+ // Support for RGB half-float renderbuffers is absent from ES3. Do not
+ // attempt to expose them.
+ if (!want_alpha_channel_) {
+ DLOG(ERROR) << "RGB half-float renderbuffers are not supported.";
+ return false;
+ }
+ }
+
+ gl_->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
+
+ int max_sample_count = 0;
+ anti_aliasing_mode_ = kNone;
+ if (use_multisampling) {
+ gl_->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &max_sample_count);
+ anti_aliasing_mode_ = kMSAAExplicitResolve;
+ if (extensions_util_->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) {
+ anti_aliasing_mode_ = kMSAAImplicitResolve;
+ } else if (extensions_util_->SupportsExtension(
+ "GL_CHROMIUM_screen_space_antialiasing")) {
+ anti_aliasing_mode_ = kScreenSpaceAntialiasing;
+ }
+ }
+ // TODO(dshwang): Enable storage textures on all platforms. crbug.com/557848
+ // The Linux ATI bot fails
+ // WebglConformance.conformance_textures_misc_tex_image_webgl, so use storage
+ // textures only if ScreenSpaceAntialiasing is enabled, because
+ // ScreenSpaceAntialiasing is much faster with storage textures.
+ storage_texture_supported_ =
+ (webgl_version_ > kWebGL1 ||
+ extensions_util_->SupportsExtension("GL_EXT_texture_storage")) &&
+ anti_aliasing_mode_ == kScreenSpaceAntialiasing;
+ // Performance regreses by 30% in WebGL apps for AMD Stoney
+ // if sample count is 8x
+ if (ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::MAX_MSAA_SAMPLE_COUNT_4))
+ sample_count_ = std::min(4, max_sample_count);
+ else
+ sample_count_ = std::min(8, max_sample_count);
+
+ texture_target_ = GL_TEXTURE_2D;
+#if defined(OS_MACOSX)
+ if (ShouldUseChromiumImage()) {
+ // A CHROMIUM_image backed texture requires a specialized set of parameters
+ // on OSX.
+ texture_target_ = GC3D_TEXTURE_RECTANGLE_ARB;
+ }
+#endif
+
+ // Initialize the alpha allocation settings based on the features and
+ // workarounds in use.
+ if (want_alpha_channel_) {
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ } else {
+ allocate_alpha_channel_ = false;
+ have_alpha_channel_ = false;
+ if (ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_GL_RGB_FORMAT)) {
+ // This configuration will
+ // - allow invalid CopyTexImage to RGBA targets
+ // - fail valid FramebufferBlit from RGB targets
+ // https://crbug.com/776269
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ }
+ if (WantExplicitResolve() &&
+ ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_WEBGL_RGB_MULTISAMPLING_USAGE)) {
+ // This configuration avoids the above issues because
+ // - CopyTexImage is invalid from multisample renderbuffers
+ // - FramebufferBlit is invalid to multisample renderbuffers
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ }
+ if (ShouldUseChromiumImage() &&
+ ContextProvider()->GetCapabilities().chromium_image_rgb_emulation) {
+ // This configuration avoids the above issues by
+ // - extra command buffer validation for CopyTexImage
+ // - explicity re-binding as RGB for FramebufferBlit
+ allocate_alpha_channel_ = false;
+ have_alpha_channel_ = true;
+ }
+ }
+
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->GenFramebuffers(1, &fbo_);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ if (WantExplicitResolve()) {
+ gl_->GenFramebuffers(1, &multisample_fbo_);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ gl_->GenRenderbuffers(1, &multisample_renderbuffer_);
+ }
+ if (!ResizeFramebufferInternal(size)) {
+ DLOG(ERROR) << "Initialization failed to allocate backbuffer.";
+ return false;
+ }
+
+ if (depth_stencil_buffer_) {
+ DCHECK(WantDepthOrStencil());
+ has_implicit_stencil_buffer_ = !want_stencil_;
+ }
+
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // It's possible that the drawing buffer allocation provokes a context loss,
+ // so check again just in case. http://crbug.com/512302
+ DLOG(ERROR) << "Context lost during initialization.";
+ return false;
+ }
+
+ return true;
+}
+
+bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl,
+ GLenum dst_texture_target,
+ GLuint dst_texture,
+ bool premultiply_alpha,
+ bool flip_y,
+ const IntPoint& dst_texture_offset,
+ const IntRect& src_sub_rectangle,
+ SourceDrawingBuffer src_buffer) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ gpu::gles2::GLES2Interface* src_gl = gl_;
+
+ if (contents_changed_) {
+ ResolveIfNeeded();
+ src_gl->Flush();
+ }
+
+ if (!Extensions3DUtil::CanUseCopyTextureCHROMIUM(dst_texture_target))
+ return false;
+
+ // Contexts may be in a different share group. We must transfer the texture
+ // through a mailbox first.
+ gpu::Mailbox mailbox;
+ gpu::SyncToken produce_sync_token;
+ if (src_buffer == kFrontBuffer && front_color_buffer_) {
+ mailbox = front_color_buffer_->mailbox;
+ produce_sync_token = front_color_buffer_->produce_sync_token;
+ } else {
+ src_gl->GenMailboxCHROMIUM(mailbox.name);
+ if (premultiplied_alpha_false_texture_) {
+ // If this texture exists, then it holds the rendering results at this
+ // point, rather than back_color_buffer_. back_color_buffer_ receives the
+ // contents of this texture later, premultiplying alpha into the color
+ // channels.
+ src_gl->ProduceTextureDirectCHROMIUM(premultiplied_alpha_false_texture_,
+ mailbox.name);
+ } else {
+ src_gl->ProduceTextureDirectCHROMIUM(back_color_buffer_->texture_id,
+ mailbox.name);
+ }
+ src_gl->GenUnverifiedSyncTokenCHROMIUM(produce_sync_token.GetData());
+ }
+
+ if (!produce_sync_token.HasData()) {
+ // This should only happen if the context has been lost.
+ return false;
+ }
+
+ dst_gl->WaitSyncTokenCHROMIUM(produce_sync_token.GetConstData());
+ GLuint src_texture = dst_gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+
+ GLboolean unpack_premultiply_alpha_needed = GL_FALSE;
+ GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE;
+ if (want_alpha_channel_ && premultiplied_alpha_ && !premultiply_alpha)
+ unpack_unpremultiply_alpha_needed = GL_TRUE;
+ else if (want_alpha_channel_ && !premultiplied_alpha_ && premultiply_alpha)
+ unpack_premultiply_alpha_needed = GL_TRUE;
+
+ dst_gl->CopySubTextureCHROMIUM(
+ src_texture, 0, dst_texture_target, dst_texture, 0,
+ dst_texture_offset.X(), dst_texture_offset.Y(), src_sub_rectangle.X(),
+ src_sub_rectangle.Y(), src_sub_rectangle.Width(),
+ src_sub_rectangle.Height(), flip_y, unpack_premultiply_alpha_needed,
+ unpack_unpremultiply_alpha_needed);
+
+ dst_gl->DeleteTextures(1, &src_texture);
+
+ gpu::SyncToken sync_token;
+ dst_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ src_gl->WaitSyncTokenCHROMIUM(sync_token.GetData());
+
+ return true;
+}
+
+WebLayer* DrawingBuffer::PlatformLayer() {
+ if (!layer_) {
+ layer_ =
+ Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
+ this);
+
+ layer_->SetOpaque(!want_alpha_channel_);
+ layer_->SetBlendBackgroundColor(want_alpha_channel_);
+ // If premultiplied_alpha_false_texture_ exists, then premultiplied_alpha_
+ // has already been handled via CopySubTextureCHROMIUM -- the alpha channel
+ // has been multiplied into the color channels. In this case, or if
+ // premultiplied_alpha_ is true, then the layer should consider its contents
+ // to be premultiplied.
+ //
+ // The only situation where the layer should consider its contents
+ // un-premultiplied is when premultiplied_alpha_ is false, and
+ // premultiplied_alpha_false_texture_ does not exist.
+ DCHECK(!(premultiplied_alpha_ && premultiplied_alpha_false_texture_));
+ layer_->SetPremultipliedAlpha(premultiplied_alpha_ ||
+ premultiplied_alpha_false_texture_);
+ layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+ GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+ }
+
+ return layer_->Layer();
+}
+
+void DrawingBuffer::ClearPlatformLayer() {
+ if (layer_)
+ layer_->ClearTexture();
+
+ gl_->Flush();
+}
+
+void DrawingBuffer::BeginDestruction() {
+ DCHECK(!destruction_in_progress_);
+ destruction_in_progress_ = true;
+
+ ClearPlatformLayer();
+ recycled_color_buffer_queue_.clear();
+
+ // If the drawing buffer is being destroyed due to a real context loss these
+ // calls will be ineffective, but won't be harmful.
+ if (multisample_fbo_)
+ gl_->DeleteFramebuffers(1, &multisample_fbo_);
+
+ if (fbo_)
+ gl_->DeleteFramebuffers(1, &fbo_);
+
+ if (multisample_renderbuffer_)
+ gl_->DeleteRenderbuffers(1, &multisample_renderbuffer_);
+
+ if (depth_stencil_buffer_)
+ gl_->DeleteRenderbuffers(1, &depth_stencil_buffer_);
+
+ if (premultiplied_alpha_false_texture_)
+ gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+
+ size_ = IntSize();
+
+ back_color_buffer_ = nullptr;
+ front_color_buffer_ = nullptr;
+ multisample_renderbuffer_ = 0;
+ depth_stencil_buffer_ = 0;
+ premultiplied_alpha_false_texture_ = 0;
+ multisample_fbo_ = 0;
+ fbo_ = 0;
+
+ if (layer_)
+ GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+
+ client_ = nullptr;
+}
+
+bool DrawingBuffer::ResizeDefaultFramebuffer(const IntSize& size) {
+ DCHECK(state_restorer_);
+ // Recreate m_backColorBuffer.
+ back_color_buffer_ = CreateColorBuffer(size);
+
+ // Most OS compositors assume GpuMemoryBuffers contain premultiplied-alpha
+ // content. If the user created the context with premultipliedAlpha:false and
+ // GpuMemoryBuffers are being used, allocate a non-GMB texture which will hold
+ // the non-premultiplied rendering results. These will be copied into the GMB
+ // via CopySubTextureCHROMIUM, performing the premultiplication step then.
+ if (ShouldUseChromiumImage() && allocate_alpha_channel_ &&
+ !premultiplied_alpha_) {
+ state_restorer_->SetTextureBindingDirty();
+ // TODO(kbr): unify with code in CreateColorBuffer.
+ if (premultiplied_alpha_false_texture_) {
+ gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+ premultiplied_alpha_false_texture_ = 0;
+ }
+ gl_->GenTextures(1, &premultiplied_alpha_false_texture_);
+ // The command decoder forbids allocating "real" OpenGL textures with the
+ // GL_TEXTURE_RECTANGLE_ARB target. Allocate this temporary texture with
+ // type GL_TEXTURE_2D all the time. CopySubTextureCHROMIUM can handle
+ // copying between 2D and rectangular textures.
+ gl_->BindTexture(GL_TEXTURE_2D, premultiplied_alpha_false_texture_);
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format = GL_RGBA8;
+ if (use_half_float_storage_) {
+ internal_storage_format = GL_RGBA16F_EXT;
+ }
+ gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size.Width(), size.Height());
+ } else {
+ GLenum internal_format = GL_RGBA;
+ GLenum format = internal_format;
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (use_half_float_storage_) {
+ if (webgl_version_ > kWebGL1) {
+ internal_format = GL_RGBA16F;
+ data_type = GL_HALF_FLOAT;
+ } else {
+ internal_format = GL_RGBA;
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ }
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format, size.Width(),
+ size.Height(), 0, format, data_type, nullptr);
+ }
+ }
+
+ AttachColorBufferToReadFramebuffer();
+
+ if (WantExplicitResolve()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetRenderbufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_);
+ // Note that the multisample rendertarget will allocate an alpha channel
+ // based on |have_alpha_channel_|, not |allocate_alpha_channel_|, since it
+ // will resolve into the ColorBuffer.
+ GLenum internal_format = have_alpha_channel_ ? GL_RGBA8_OES : GL_RGB8_OES;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ internal_format = GL_RGBA16F_EXT;
+ }
+ gl_->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ internal_format, size.Width(),
+ size.Height());
+
+ if (gl_->GetError() == GL_OUT_OF_MEMORY)
+ return false;
+
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, multisample_renderbuffer_);
+ }
+
+ if (WantDepthOrStencil()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetRenderbufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER,
+ multisample_fbo_ ? multisample_fbo_ : fbo_);
+ if (!depth_stencil_buffer_)
+ gl_->GenRenderbuffers(1, &depth_stencil_buffer_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer_);
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl_->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size.Width(), size.Height());
+ } else if (anti_aliasing_mode_ == kMSAAExplicitResolve) {
+ gl_->RenderbufferStorageMultisampleCHROMIUM(
+ GL_RENDERBUFFER, sample_count_, GL_DEPTH24_STENCIL8_OES, size.Width(),
+ size.Height());
+ } else {
+ gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+ size.Width(), size.Height());
+ }
+ // For ES 2.0 contexts DEPTH_STENCIL is not available natively, so we
+ // emulate
+ // it at the command buffer level for WebGL contexts.
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+
+ if (WantExplicitResolve()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ if (gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ return false;
+ }
+
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ return gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
+}
+
+void DrawingBuffer::ClearFramebuffers(GLbitfield clear_mask) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ ClearFramebuffersInternal(clear_mask);
+}
+
+void DrawingBuffer::ClearFramebuffersInternal(GLbitfield clear_mask) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ // We will clear the multisample FBO, but we also need to clear the
+ // non-multisampled buffer.
+ if (multisample_fbo_) {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ }
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER,
+ multisample_fbo_ ? multisample_fbo_ : fbo_);
+ gl_->Clear(clear_mask);
+}
+
+IntSize DrawingBuffer::AdjustSize(const IntSize& desired_size,
+ const IntSize& cur_size,
+ int max_texture_size) {
+ IntSize adjusted_size = desired_size;
+
+ // Clamp if the desired size is greater than the maximum texture size for the
+ // device.
+ if (adjusted_size.Height() > max_texture_size)
+ adjusted_size.SetHeight(max_texture_size);
+
+ if (adjusted_size.Width() > max_texture_size)
+ adjusted_size.SetWidth(max_texture_size);
+
+ return adjusted_size;
+}
+
+bool DrawingBuffer::Resize(const IntSize& new_size) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ return ResizeFramebufferInternal(new_size);
+}
+
+bool DrawingBuffer::ResizeFramebufferInternal(const IntSize& new_size) {
+ DCHECK(state_restorer_);
+ DCHECK(!new_size.IsEmpty());
+ IntSize adjusted_size = AdjustSize(new_size, size_, max_texture_size_);
+ if (adjusted_size.IsEmpty())
+ return false;
+
+ if (adjusted_size != size_) {
+ do {
+ if (!ResizeDefaultFramebuffer(adjusted_size)) {
+ adjusted_size.Scale(kResourceAdjustedRatio);
+ continue;
+ }
+ break;
+ } while (!adjusted_size.IsEmpty());
+
+ size_ = adjusted_size;
+ // Free all mailboxes, because they are now of the wrong size. Only the
+ // first call in this loop has any effect.
+ recycled_color_buffer_queue_.clear();
+ recycled_bitmaps_.clear();
+
+ if (adjusted_size.IsEmpty())
+ return false;
+ }
+
+ state_restorer_->SetClearStateDirty();
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->ClearColor(0, 0, 0,
+ DefaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0);
+ gl_->ColorMask(true, true, true, true);
+
+ GLbitfield clear_mask = GL_COLOR_BUFFER_BIT;
+ if (!!depth_stencil_buffer_) {
+ gl_->ClearDepthf(1.0f);
+ clear_mask |= GL_DEPTH_BUFFER_BIT;
+ gl_->DepthMask(true);
+ }
+ if (!!depth_stencil_buffer_) {
+ gl_->ClearStencil(0);
+ clear_mask |= GL_STENCIL_BUFFER_BIT;
+ gl_->StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
+ }
+
+ ClearFramebuffersInternal(clear_mask);
+ return true;
+}
+
+void DrawingBuffer::ResolveAndBindForReadAndDraw() {
+ {
+ ScopedStateRestorer scoped_state_restorer(this);
+ ResolveIfNeeded();
+ }
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+}
+
+void DrawingBuffer::ResolveMultisampleFramebufferInternal() {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ if (WantExplicitResolve()) {
+ state_restorer_->SetClearStateDirty();
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, multisample_fbo_);
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, fbo_);
+ gl_->Disable(GL_SCISSOR_TEST);
+
+ int width = size_.Width();
+ int height = size_.Height();
+ // Use NEAREST, because there is no scale performed during the blit.
+ GLuint filter = GL_NEAREST;
+
+ gl_->BlitFramebufferCHROMIUM(0, 0, width, height, 0, 0, width, height,
+ GL_COLOR_BUFFER_BIT, filter);
+
+ // On old AMD GPUs on OS X, glColorMask doesn't work correctly for
+ // multisampled renderbuffers and the alpha channel can be overwritten.
+ // Clear the alpha channel of |m_fbo|.
+ if (DefaultBufferRequiresAlphaChannelToBePreserved() &&
+ ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_MULTISAMPLING_COLOR_MASK_USAGE)) {
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->ColorMask(false, false, false, true);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ }
+ }
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ if (anti_aliasing_mode_ == kScreenSpaceAntialiasing)
+ gl_->ApplyScreenSpaceAntialiasingCHROMIUM();
+}
+
+void DrawingBuffer::ResolveIfNeeded() {
+ if (anti_aliasing_mode_ != kNone && !contents_change_resolved_)
+ ResolveMultisampleFramebufferInternal();
+ contents_change_resolved_ = true;
+}
+
+void DrawingBuffer::RestoreFramebufferBindings() {
+ client_->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void DrawingBuffer::RestoreAllState() {
+ client_->DrawingBufferClientRestoreScissorTest();
+ client_->DrawingBufferClientRestoreMaskAndClearValues();
+ client_->DrawingBufferClientRestorePixelPackParameters();
+ client_->DrawingBufferClientRestoreTexture2DBinding();
+ client_->DrawingBufferClientRestoreRenderbufferBinding();
+ client_->DrawingBufferClientRestoreFramebufferBinding();
+ client_->DrawingBufferClientRestorePixelUnpackBufferBinding();
+ client_->DrawingBufferClientRestorePixelPackBufferBinding();
+}
+
+bool DrawingBuffer::Multisample() const {
+ return anti_aliasing_mode_ != kNone;
+}
+
+void DrawingBuffer::Bind(GLenum target) {
+ gl_->BindFramebuffer(target, WantExplicitResolve() ? multisample_fbo_ : fbo_);
+}
+
+scoped_refptr<Uint8Array> DrawingBuffer::PaintRenderingResultsToDataArray(
+ SourceDrawingBuffer source_buffer) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ int width = Size().Width();
+ int height = Size().Height();
+
+ CheckedNumeric<int> data_size = 4;
+ data_size *= width;
+ data_size *= height;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ data_size *= 2;
+ }
+ if (!data_size.IsValid())
+ return nullptr;
+
+ unsigned byte_length = width * height * 4;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ byte_length *= 2;
+ }
+ scoped_refptr<ArrayBuffer> dst_buffer =
+ ArrayBuffer::CreateOrNull(byte_length, 1);
+ if (!dst_buffer)
+ return nullptr;
+ scoped_refptr<Uint8Array> data_array =
+ Uint8Array::Create(std::move(dst_buffer), 0, byte_length);
+ if (!data_array)
+ return nullptr;
+
+ GLuint fbo = 0;
+ state_restorer_->SetFramebufferBindingDirty();
+ if (source_buffer == kFrontBuffer && front_color_buffer_) {
+ gl_->GenFramebuffers(1, &fbo);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, front_color_buffer_->texture_id,
+ 0);
+ } else {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ }
+
+ ReadBackFramebuffer(static_cast<unsigned char*>(data_array->Data()), width,
+ height, kReadbackRGBA,
+ WebGLImageConversion::kAlphaDoNothing);
+ FlipVertically(static_cast<uint8_t*>(data_array->Data()), width, height);
+
+ if (fbo) {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, 0, 0);
+ gl_->DeleteFramebuffers(1, &fbo);
+ }
+
+ return data_array;
+}
+
+void DrawingBuffer::ReadBackFramebuffer(unsigned char* pixels,
+ int width,
+ int height,
+ ReadbackOrder readback_order,
+ WebGLImageConversion::AlphaOp op) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetPixelPackParametersDirty();
+ gl_->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ if (webgl_version_ > kWebGL1) {
+ gl_->PixelStorei(GL_PACK_SKIP_ROWS, 0);
+ gl_->PixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ gl_->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+
+ state_restorer_->SetPixelPackBufferBindingDirty();
+ gl_->BindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+ }
+
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ if (webgl_version_ > kWebGL1)
+ data_type = GL_HALF_FLOAT;
+ else
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ gl_->ReadPixels(0, 0, width, height, GL_RGBA, data_type, pixels);
+
+ size_t buffer_size = 4 * width * height;
+ if (data_type != GL_UNSIGNED_BYTE)
+ buffer_size *= 2;
+ // For half float storage Skia order is RGBA, hence no swizzling is needed.
+ if (readback_order == kReadbackSkia && data_type == GL_UNSIGNED_BYTE) {
+#if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
+ // Swizzle red and blue channels to match SkBitmap's byte ordering.
+ // TODO(kbr): expose GL_BGRA as extension.
+ for (size_t i = 0; i < buffer_size; i += 4) {
+ std::swap(pixels[i], pixels[i + 2]);
+ }
+#endif
+ }
+
+ if (op == WebGLImageConversion::kAlphaDoPremultiply) {
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
+ SkColorSpace::MakeSRGBLinear().get());
+ SkColorSpaceXform::ColorFormat color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (data_type != GL_UNSIGNED_BYTE)
+ color_format = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+ xform->apply(color_format, pixels, color_format, pixels, width * height,
+ kPremul_SkAlphaType);
+ } else if (op != WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ }
+}
+
+void DrawingBuffer::FlipVertically(uint8_t* framebuffer,
+ int width,
+ int height) {
+ unsigned row_bytes = width * 4;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ row_bytes *= 2;
+ }
+ std::vector<uint8_t> scanline(row_bytes);
+ unsigned count = height / 2;
+ for (unsigned i = 0; i < count; i++) {
+ uint8_t* row_a = framebuffer + i * row_bytes;
+ uint8_t* row_b = framebuffer + (height - i - 1) * row_bytes;
+ memcpy(scanline.data(), row_b, row_bytes);
+ memcpy(row_b, row_a, row_bytes);
+ memcpy(row_a, scanline.data(), row_bytes);
+ }
+}
+
+scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateColorBuffer(
+ const IntSize& size) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetTextureBindingDirty();
+
+ // Select the parameters for the texture object. Allocate the backing
+ // GpuMemoryBuffer and GLImage, if one is going to be used.
+ GLuint image_id = 0;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
+ Platform::Current()->GetGpuMemoryBufferManager();
+ if (ShouldUseChromiumImage()) {
+ gfx::BufferFormat buffer_format;
+ GLenum gl_format = GL_NONE;
+ if (allocate_alpha_channel_) {
+ buffer_format = use_half_float_storage_ ? gfx::BufferFormat::RGBA_F16
+ : gfx::BufferFormat::RGBA_8888;
+ gl_format = GL_RGBA;
+ } else {
+ DCHECK(!use_half_float_storage_);
+ buffer_format = gfx::BufferFormat::RGBX_8888;
+ if (gpu::IsImageFromGpuMemoryBufferFormatSupported(
+ gfx::BufferFormat::BGRX_8888,
+ ContextProvider()->GetCapabilities()))
+ buffer_format = gfx::BufferFormat::BGRX_8888;
+ gl_format = GL_RGB;
+ }
+ gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
+ gfx::Size(size), buffer_format, gfx::BufferUsage::SCANOUT,
+ gpu::kNullSurfaceHandle);
+ if (gpu_memory_buffer) {
+ gpu_memory_buffer->SetColorSpace(storage_color_space_);
+ image_id =
+ gl_->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(),
+ size.Width(), size.Height(), gl_format);
+ if (!image_id)
+ gpu_memory_buffer.reset();
+ }
+ }
+
+ // Allocate the texture for this object.
+ GLuint texture_id = 0;
+ {
+ gl_->GenTextures(1, &texture_id);
+ gl_->BindTexture(texture_target_, texture_id);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ // If this is GpuMemoryBuffer-backed, then bind the texture to the
+ // GpuMemoryBuffer's GLImage. Otherwise, allocate ordinary texture storage.
+ if (image_id) {
+ gl_->BindTexImage2DCHROMIUM(texture_target_, image_id);
+ } else {
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format =
+ allocate_alpha_channel_ ? GL_RGBA8 : GL_RGB8;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ internal_storage_format = GL_RGBA16F_EXT;
+ }
+ gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size.Width(), size.Height());
+ } else {
+ GLenum internal_format = allocate_alpha_channel_ ? GL_RGBA : GL_RGB;
+ GLenum format = internal_format;
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ if (webgl_version_ > kWebGL1) {
+ internal_format = GL_RGBA16F;
+ data_type = GL_HALF_FLOAT;
+ } else {
+ internal_format = GL_RGBA;
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ }
+ gl_->TexImage2D(texture_target_, 0, internal_format, size.Width(),
+ size.Height(), 0, format, data_type, nullptr);
+ }
+ }
+
+ // Clear the alpha channel if this is RGB emulated.
+ if (image_id && !want_alpha_channel_ && have_alpha_channel_) {
+ GLuint fbo = 0;
+
+ state_restorer_->SetClearStateDirty();
+ gl_->GenFramebuffers(1, &fbo);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, texture_id, 0);
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->ColorMask(false, false, false, true);
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, 0, 0);
+ gl_->DeleteFramebuffers(1, &fbo);
+ }
+
+ return base::AdoptRef(new ColorBuffer(this, size, texture_id, image_id,
+ std::move(gpu_memory_buffer)));
+}
+
+void DrawingBuffer::AttachColorBufferToReadFramebuffer() {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetTextureBindingDirty();
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+
+ GLenum id = 0;
+ GLenum texture_target = 0;
+
+ if (premultiplied_alpha_false_texture_) {
+ id = premultiplied_alpha_false_texture_;
+ texture_target = GL_TEXTURE_2D;
+ } else {
+ id = back_color_buffer_->texture_id;
+ texture_target = texture_target_;
+ }
+
+ gl_->BindTexture(texture_target, id);
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl_->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, id, 0,
+ sample_count_);
+ } else {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target, id, 0);
+ }
+}
+
+bool DrawingBuffer::WantExplicitResolve() {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+}
+
+bool DrawingBuffer::WantDepthOrStencil() {
+ return want_depth_ || want_stencil_;
+}
+
+bool DrawingBuffer::SetupRGBEmulationForBlitFramebuffer(
+ bool is_user_draw_framebuffer_bound) {
+ // We only need to do this work if:
+ // - We are blitting to the default framebuffer
+ // - The user has selected alpha:false and antialias:false
+ // - We are using CHROMIUM_image with RGB emulation
+ // macOS is the only platform on which this is necessary.
+
+ if (is_user_draw_framebuffer_bound) {
+ return false;
+ }
+
+ if (anti_aliasing_mode_ != kNone)
+ return false;
+
+ bool has_emulated_rgb = !allocate_alpha_channel_ && have_alpha_channel_;
+ if (!has_emulated_rgb)
+ return false;
+
+ // If for some reason the back buffer doesn't exist or doesn't have a
+ // CHROMIUM_image, don't proceed with this workaround.
+ if (!back_color_buffer_ || !back_color_buffer_->image_id)
+ return false;
+
+ // Before allowing the BlitFramebuffer call to go through, it's necessary
+ // to swap out the RGBA texture that's bound to the CHROMIUM_image
+ // instance with an RGB texture. BlitFramebuffer requires the internal
+ // formats of the source and destination to match when doing a
+ // multisample resolve, and the best way to achieve this without adding
+ // more full-screen blits is to hook up a true RGB texture to the
+ // underlying IOSurface. Unfortunately, on macOS, this rendering path
+ // destroys the alpha channel and requires a fixup afterward, which is
+ // why it isn't used all the time.
+
+ GLuint rgb_texture = back_color_buffer_->rgb_workaround_texture_id;
+ DCHECK_EQ(texture_target_, GC3D_TEXTURE_RECTANGLE_ARB);
+ if (!rgb_texture) {
+ gl_->GenTextures(1, &rgb_texture);
+ gl_->BindTexture(texture_target_, rgb_texture);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ // Bind this texture to the CHROMIUM_image instance that the color
+ // buffer owns. This is an expensive operation, so it's important that
+ // the result be cached.
+ gl_->BindTexImage2DWithInternalformatCHROMIUM(texture_target_, GL_RGB,
+ back_color_buffer_->image_id);
+ back_color_buffer_->rgb_workaround_texture_id = rgb_texture;
+ }
+
+ gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
+ texture_target_, rgb_texture, 0);
+ return true;
+}
+
+void DrawingBuffer::CleanupRGBEmulationForBlitFramebuffer() {
+ // This will only be called if SetupRGBEmulationForBlitFramebuffer was.
+ // Put the framebuffer back the way it was, and clear the alpha channel.
+ DCHECK(back_color_buffer_);
+ DCHECK(back_color_buffer_->image_id);
+ gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
+ texture_target_, back_color_buffer_->texture_id, 0);
+ // Clear the alpha channel.
+ gl_->ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ DCHECK(client_);
+ client_->DrawingBufferClientRestoreScissorTest();
+ client_->DrawingBufferClientRestoreMaskAndClearValues();
+}
+
+DrawingBuffer::ScopedStateRestorer::ScopedStateRestorer(
+ DrawingBuffer* drawing_buffer)
+ : drawing_buffer_(drawing_buffer) {
+ // If this is a nested restorer, save the previous restorer.
+ previous_state_restorer_ = drawing_buffer->state_restorer_;
+ drawing_buffer_->state_restorer_ = this;
+}
+
+DrawingBuffer::ScopedStateRestorer::~ScopedStateRestorer() {
+ DCHECK_EQ(drawing_buffer_->state_restorer_, this);
+ drawing_buffer_->state_restorer_ = previous_state_restorer_;
+ Client* client = drawing_buffer_->client_;
+ if (!client)
+ return;
+
+ if (clear_state_dirty_) {
+ client->DrawingBufferClientRestoreScissorTest();
+ client->DrawingBufferClientRestoreMaskAndClearValues();
+ }
+ if (pixel_pack_parameters_dirty_)
+ client->DrawingBufferClientRestorePixelPackParameters();
+ if (texture_binding_dirty_)
+ client->DrawingBufferClientRestoreTexture2DBinding();
+ if (renderbuffer_binding_dirty_)
+ client->DrawingBufferClientRestoreRenderbufferBinding();
+ if (framebuffer_binding_dirty_)
+ client->DrawingBufferClientRestoreFramebufferBinding();
+ if (pixel_unpack_buffer_binding_dirty_)
+ client->DrawingBufferClientRestorePixelUnpackBufferBinding();
+ if (pixel_pack_buffer_binding_dirty_)
+ client->DrawingBufferClientRestorePixelPackBufferBinding();
+}
+
+bool DrawingBuffer::ShouldUseChromiumImage() {
+ return RuntimeEnabledFeatures::WebGLImageChromiumEnabled() &&
+ chromium_image_usage_ == kAllowChromiumImage &&
+ Platform::Current()->GetGpuMemoryBufferManager();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
new file mode 100644
index 00000000000..e19db7bcc26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2010, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
+
+#include <memory>
+
+#include "cc/layers/texture_layer_client.h"
+#include "cc/resources/cross_thread_shared_bitmap.h"
+#include "cc/resources/shared_bitmap_id_registrar.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types_3d.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/color_space.h"
+
+namespace gfx {
+class GpuMemoryBuffer;
+}
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace blink {
+class CanvasColorParams;
+class Extensions3DUtil;
+class StaticBitmapImage;
+class WebExternalTextureLayer;
+class WebGraphicsContext3DProvider;
+class WebGraphicsContext3DProviderWrapper;
+class WebLayer;
+
+// Manages a rendering target (framebuffer + attachment) for a canvas. Can
+// publish its rendering results to a WebLayer for compositing.
+class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
+ public RefCounted<DrawingBuffer> {
+ WTF_MAKE_NONCOPYABLE(DrawingBuffer);
+
+ public:
+ class Client {
+ public:
+ // Returns true if the DrawingBuffer is currently bound for draw.
+ virtual bool DrawingBufferClientIsBoundForDraw() = 0;
+ virtual void DrawingBufferClientRestoreScissorTest() = 0;
+ // Restores the mask and clear value for color, depth, and stencil buffers.
+ virtual void DrawingBufferClientRestoreMaskAndClearValues() = 0;
+ // Assume client knows the GL/WebGL version and restore necessary params
+ // accordingly.
+ virtual void DrawingBufferClientRestorePixelPackParameters() = 0;
+ // Restores the GL_TEXTURE_2D binding for the active texture unit only.
+ virtual void DrawingBufferClientRestoreTexture2DBinding() = 0;
+ virtual void DrawingBufferClientRestoreRenderbufferBinding() = 0;
+ virtual void DrawingBufferClientRestoreFramebufferBinding() = 0;
+ virtual void DrawingBufferClientRestorePixelUnpackBufferBinding() = 0;
+ virtual void DrawingBufferClientRestorePixelPackBufferBinding() = 0;
+ };
+
+ enum PreserveDrawingBuffer {
+ kPreserve,
+ kDiscard,
+ };
+ enum WebGLVersion {
+ kWebGL1,
+ kWebGL2,
+ };
+
+ enum ChromiumImageUsage {
+ kAllowChromiumImage,
+ kDisallowChromiumImage,
+ };
+
+ static scoped_refptr<DrawingBuffer> Create(
+ std::unique_ptr<WebGraphicsContext3DProvider>,
+ bool using_gpu_compositing,
+ Client*,
+ const IntSize&,
+ bool premultiplied_alpha,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ PreserveDrawingBuffer,
+ WebGLVersion,
+ ChromiumImageUsage,
+ const CanvasColorParams&);
+ static void ForceNextDrawingBufferCreationToFail();
+
+ ~DrawingBuffer() override;
+
+ // Destruction will be completed after all mailboxes are released.
+ void BeginDestruction();
+
+ // Issues a glClear() on all framebuffers associated with this DrawingBuffer.
+ void ClearFramebuffers(GLbitfield clear_mask);
+
+ // Indicates whether the DrawingBuffer internally allocated a packed
+ // depth-stencil renderbuffer in the situation where the end user only asked
+ // for a depth buffer. In this case, we need to upgrade clears of the depth
+ // buffer to clears of the depth and stencil buffers in order to avoid
+ // performance problems on some GPUs.
+ bool HasImplicitStencilBuffer() const { return has_implicit_stencil_buffer_; }
+ bool HasDepthBuffer() const { return !!depth_stencil_buffer_; }
+ bool HasStencilBuffer() const { return !!depth_stencil_buffer_; }
+
+ // Given the desired buffer size, provides the largest dimensions that will
+ // fit in the pixel budget.
+ static IntSize AdjustSize(const IntSize& desired_size,
+ const IntSize& cur_size,
+ int max_texture_size);
+
+ // Resizes (or allocates if necessary) all buffers attached to the default
+ // framebuffer. Returns whether the operation was successful.
+ bool Resize(const IntSize&);
+
+ // Bind the default framebuffer to |target|. |target| must be
+ // GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_DRAW_FRAMEBUFFER.
+ void Bind(GLenum target);
+ IntSize Size() const { return size_; }
+
+ // Resolves the multisample color buffer to the normal color buffer and leaves
+ // the resolved color buffer bound to GL_READ_FRAMEBUFFER and
+ // GL_DRAW_FRAMEBUFFER.
+ void ResolveAndBindForReadAndDraw();
+
+ bool Multisample() const;
+
+ bool DiscardFramebufferSupported() const {
+ return discard_framebuffer_supported_;
+ }
+
+ // Returns false if the contents had previously been marked as changed and
+ // have not yet been resolved.
+ bool MarkContentsChanged();
+
+ // Maintenance of auto-clearing of color/depth/stencil buffers. The
+ // Reset method is present to keep calling code simpler, so it
+ // doesn't have to know which buffers were allocated.
+ void ResetBuffersToAutoClear();
+ void SetBuffersToAutoClear(GLbitfield bitmask);
+ GLbitfield GetBuffersToAutoClear() const;
+
+ void SetIsHidden(bool);
+ void SetFilterQuality(SkFilterQuality);
+
+ // Whether the target for draw operations has format GL_RGBA, but is
+ // emulating format GL_RGB. When the target's storage is first
+ // allocated, its alpha channel must be cleared to 1. All future drawing
+ // operations must use a color mask with alpha=GL_FALSE.
+ bool RequiresAlphaChannelToBePreserved();
+
+ // Similar to requiresAlphaChannelToBePreserved(), but always targets the
+ // default framebuffer.
+ bool DefaultBufferRequiresAlphaChannelToBePreserved();
+
+ WebLayer* PlatformLayer();
+
+ gpu::gles2::GLES2Interface* ContextGL();
+ WebGraphicsContext3DProvider* ContextProvider();
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWeakPtr();
+ Client* client() { return client_; }
+ WebGLVersion webgl_version() const { return webgl_version_; }
+ bool destroyed() const { return destruction_in_progress_; }
+
+ // cc::TextureLayerClient implementation.
+ bool PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+ override;
+
+ // Returns a StaticBitmapImage backed by a texture containing the current
+ // contents of the front buffer. This is done without any pixel copies. The
+ // texture in the ImageBitmap is from the active ContextProvider on the
+ // DrawingBuffer.
+ // If out_release_callback is null, the image is discarded. If it is non-null
+ // the image must be recycled or discarded by calling *out_release_callback.
+ scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ bool CopyToPlatformTexture(gpu::gles2::GLES2Interface*,
+ GLenum dst_target,
+ GLuint dst_texture,
+ bool premultiply_alpha,
+ bool flip_y,
+ const IntPoint& dst_texture_offset,
+ const IntRect& src_sub_rectangle,
+ SourceDrawingBuffer);
+
+ scoped_refptr<Uint8Array> PaintRenderingResultsToDataArray(
+ SourceDrawingBuffer);
+
+ int SampleCount() const { return sample_count_; }
+ bool ExplicitResolveOfMultisampleData() const {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+ }
+
+ // Rebind the read and draw framebuffers that WebGL is expecting.
+ void RestoreFramebufferBindings();
+
+ // Restore all state that may have been dirtied by any call.
+ void RestoreAllState();
+
+ // This class helps implement correct semantics for BlitFramebuffer
+ // when the DrawingBuffer is using a CHROMIUM image for its backing
+ // store and RGB emulation is in use (basically, macOS only).
+ class PLATFORM_EXPORT ScopedRGBEmulationForBlitFramebuffer {
+ public:
+ ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer*,
+ bool is_user_draw_framebuffer_bound);
+ ~ScopedRGBEmulationForBlitFramebuffer();
+
+ private:
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+ bool doing_work_ = false;
+ };
+
+ protected: // For unittests
+ DrawingBuffer(std::unique_ptr<WebGraphicsContext3DProvider>,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil>,
+ Client*,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool premultiplied_alpha,
+ PreserveDrawingBuffer,
+ WebGLVersion,
+ bool wants_depth,
+ bool wants_stencil,
+ ChromiumImageUsage,
+ const CanvasColorParams&);
+
+ bool Initialize(const IntSize&, bool use_multisampling);
+
+ struct RegisteredBitmap {
+ scoped_refptr<cc::CrossThreadSharedBitmap> bitmap;
+ cc::SharedBitmapIdRegistration registration;
+
+ // Explicitly move-only.
+ RegisteredBitmap(RegisteredBitmap&&) = default;
+ RegisteredBitmap& operator=(RegisteredBitmap&&) = default;
+ };
+ // Shared memory bitmaps that were released by the compositor and can be used
+ // again by this DrawingBuffer.
+ Vector<RegisteredBitmap> recycled_bitmaps_;
+
+ private:
+ friend class ScopedRGBEmulationForBlitFramebuffer;
+ friend class ScopedStateRestorer;
+ friend class ColorBuffer;
+
+ // This structure should wrap all public entrypoints that may modify GL state.
+ // It will restore all state when it drops out of scope.
+ class ScopedStateRestorer {
+ public:
+ ScopedStateRestorer(DrawingBuffer*);
+ ~ScopedStateRestorer();
+
+ // Mark parts of the state that are dirty and need to be restored.
+ void SetClearStateDirty() { clear_state_dirty_ = true; }
+ void SetPixelPackParametersDirty() { pixel_pack_parameters_dirty_ = true; }
+ void SetTextureBindingDirty() { texture_binding_dirty_ = true; }
+ void SetRenderbufferBindingDirty() { renderbuffer_binding_dirty_ = true; }
+ void SetFramebufferBindingDirty() { framebuffer_binding_dirty_ = true; }
+ void SetPixelUnpackBufferBindingDirty() {
+ pixel_unpack_buffer_binding_dirty_ = true;
+ }
+ void SetPixelPackBufferBindingDirty() {
+ pixel_pack_buffer_binding_dirty_ = true;
+ }
+
+ private:
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+ // The previous state restorer, in case restorers are nested.
+ ScopedStateRestorer* previous_state_restorer_ = nullptr;
+ bool clear_state_dirty_ = false;
+ bool pixel_pack_parameters_dirty_ = false;
+ bool texture_binding_dirty_ = false;
+ bool renderbuffer_binding_dirty_ = false;
+ bool framebuffer_binding_dirty_ = false;
+ bool pixel_unpack_buffer_binding_dirty_ = false;
+ bool pixel_pack_buffer_binding_dirty_ = false;
+ };
+
+ struct ColorBuffer : public RefCounted<ColorBuffer> {
+ ColorBuffer(DrawingBuffer*,
+ const IntSize&,
+ GLuint texture_id,
+ GLuint image_id,
+ std::unique_ptr<gfx::GpuMemoryBuffer>);
+ ~ColorBuffer();
+
+ // The owning DrawingBuffer. Note that DrawingBuffer is explicitly destroyed
+ // by the beginDestruction method, which will eventually drain all of its
+ // ColorBuffers.
+ scoped_refptr<DrawingBuffer> drawing_buffer;
+ const IntSize size;
+ const GLuint texture_id = 0;
+ const GLuint image_id = 0;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+
+ // If we're emulating an RGB back buffer using an RGBA Chromium
+ // image (essentially macOS only), then when performing
+ // BlitFramebuffer calls, we have to swap in an RGB texture in
+ // place of the RGBA texture bound to the image. The reason is
+ // that BlitFramebuffer requires the internal formats of the
+ // source and destination to match (e.g. RGB8 on both sides).
+ // There are bugs in the semantics of RGB8 textures in this
+ // situation (the alpha channel is zeroed), requiring more fixups.
+ GLuint rgb_workaround_texture_id = 0;
+
+ // The mailbox used to send this buffer to the compositor.
+ gpu::Mailbox mailbox;
+
+ // The sync token for when this buffer was sent to the compositor.
+ gpu::SyncToken produce_sync_token;
+
+ // The sync token for when this buffer was received back from the
+ // compositor.
+ gpu::SyncToken receive_sync_token;
+
+ private:
+ WTF_MAKE_NONCOPYABLE(ColorBuffer);
+ };
+
+ // The same as clearFramebuffers(), but leaves GL state dirty.
+ void ClearFramebuffersInternal(GLbitfield clear_mask);
+
+ // The same as reset(), but leaves GL state dirty.
+ bool ResizeFramebufferInternal(const IntSize&);
+
+ // The same as resolveAndBindForReadAndDraw(), but leaves GL state dirty.
+ void ResolveMultisampleFramebufferInternal();
+
+ // Resolves m_multisampleFBO into m_fbo, if multisampling.
+ void ResolveIfNeeded();
+
+ bool PrepareTransferableResourceInternal(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback,
+ bool force_gpu_result);
+
+ // Helper functions to be called only by PrepareTransferableResourceInternal.
+ void FinishPrepareTransferableResourceGpu(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+ void FinishPrepareTransferableResourceSoftware(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ // Callbacks for mailboxes given to the compositor from
+ // FinishPrepareTransferableResource{Gpu,Software}.
+ void MailboxReleasedGpu(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+ void MailboxReleasedSoftware(RegisteredBitmap,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ // Attempts to allocator storage for, or resize all buffers. Returns whether
+ // the operation was successful.
+ bool ResizeDefaultFramebuffer(const IntSize&);
+
+ void ClearPlatformLayer();
+
+ RegisteredBitmap CreateOrRecycleBitmap(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar);
+
+ // Updates the current size of the buffer, ensuring that
+ // s_currentResourceUsePixels is updated.
+ void SetSize(const IntSize&);
+
+ // This is the order of bytes to use when doing a readback.
+ enum ReadbackOrder { kReadbackRGBA, kReadbackSkia };
+
+ // Helper function which does a readback from the currently-bound
+ // framebuffer into a buffer of a certain size with 4-byte pixels.
+ void ReadBackFramebuffer(unsigned char* pixels,
+ int width,
+ int height,
+ ReadbackOrder,
+ WebGLImageConversion::AlphaOp);
+
+ // Helper function to flip a bitmap vertically.
+ void FlipVertically(uint8_t* data, int width, int height);
+
+ // If RGB emulation is required, then the CHROMIUM image's alpha channel
+ // must be immediately cleared after it is bound to a texture. Nothing
+ // should be allowed to change the alpha channel after this.
+ void ClearChromiumImageAlpha(const ColorBuffer&);
+
+ // Tries to create a CHROMIUM_image backed texture if
+ // RuntimeEnabledFeatures::WebGLImageChromiumEnabled() is true. On failure,
+ // or if the flag is false, creates a default texture. Always returns a valid
+ // ColorBuffer.
+ scoped_refptr<ColorBuffer> CreateColorBuffer(const IntSize&);
+
+ // Creates or recycles a ColorBuffer of size |m_size|.
+ scoped_refptr<ColorBuffer> CreateOrRecycleColorBuffer();
+
+ // Attaches |m_backColorBuffer| to |m_fbo|, which is always the source for
+ // read operations.
+ void AttachColorBufferToReadFramebuffer();
+
+ // Whether the WebGL client desires an explicit resolve. This is
+ // implemented by forwarding all draw operations to a multisample
+ // renderbuffer, which is resolved before any read operations or swaps.
+ bool WantExplicitResolve();
+
+ // Whether the WebGL client wants a depth or stencil buffer.
+ bool WantDepthOrStencil();
+
+ // Helpers to ensure correct behavior of BlitFramebuffer when using
+ // an emulated RGB CHROMIUM_image back buffer.
+ bool SetupRGBEmulationForBlitFramebuffer(bool is_user_draw_framebuffer_bound);
+ void CleanupRGBEmulationForBlitFramebuffer();
+
+ // Weak, reset by beginDestruction.
+ Client* client_ = nullptr;
+
+ const PreserveDrawingBuffer preserve_drawing_buffer_;
+ const WebGLVersion webgl_version_;
+
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper> context_provider_;
+ // Lifetime is tied to the m_contextProvider.
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<Extensions3DUtil> extensions_util_;
+ IntSize size_ = {-1, -1};
+ const bool discard_framebuffer_supported_;
+ // Did the user request an alpha channel be allocated.
+ const bool want_alpha_channel_;
+ // Do we explicitly allocate an alpha channel in our ColorBuffer allocations.
+ // Note that this does not apply to |multisample_renderbuffer_|.
+ bool allocate_alpha_channel_ = false;
+ // Does our allocation have an alpha channel (potentially implicitly created).
+ // Note that this determines if |multisample_renderbuffer_| allocates an alpha
+ // channel.
+ bool have_alpha_channel_ = false;
+ const bool premultiplied_alpha_;
+ const bool using_gpu_compositing_;
+ bool has_implicit_stencil_buffer_ = false;
+ bool storage_texture_supported_ = false;
+
+ // The texture target (2D or RECTANGLE) for our allocations.
+ GLenum texture_target_ = 0;
+
+ // The current state restorer, which is used to track state dirtying. It is an
+ // error to dirty state shared with WebGL while there is no existing state
+ // restorer.
+ ScopedStateRestorer* state_restorer_ = nullptr;
+
+ // This is used when the user requests either a depth or stencil buffer.
+ GLuint depth_stencil_buffer_ = 0;
+
+ // When wantExplicitResolve() returns true, the target of all draw
+ // operations.
+ GLuint multisample_fbo_ = 0;
+
+ // The id of the renderbuffer storage for |m_multisampleFBO|.
+ GLuint multisample_renderbuffer_ = 0;
+
+ // If premultipliedAlpha:false is set during context creation, and a
+ // GpuMemoryBuffer is used for the DrawingBuffer's storage, then a separate,
+ // regular, OpenGL texture is allocated to hold either the rendering results
+ // (if antialias:false) or resolve results (if antialias:true). Then
+ // CopyTextureCHROMIUM is used to multiply the alpha channel into the color
+ // channels when copying into the GMB.
+ GLuint premultiplied_alpha_false_texture_ = 0;
+
+ // When wantExplicitResolve() returns false, the target of all draw and
+ // read operations. When wantExplicitResolve() returns true, the target of
+ // all read operations.
+ GLuint fbo_ = 0;
+
+ // The ColorBuffer that backs |m_fbo|.
+ scoped_refptr<ColorBuffer> back_color_buffer_;
+
+ // The ColorBuffer that was most recently presented to the compositor by
+ // PrepareTransferableResourceInternal.
+ scoped_refptr<ColorBuffer> front_color_buffer_;
+
+ // True if our contents have been modified since the last presentation of this
+ // buffer.
+ bool contents_changed_ = true;
+
+ // True if resolveIfNeeded() has been called since the last time
+ // markContentsChanged() had been called.
+ bool contents_change_resolved_ = false;
+
+ // A bitmask of GL buffer bits (GL_COLOR_BUFFER_BIT,
+ // GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT) which need to be
+ // auto-cleared.
+ GLbitfield buffers_to_auto_clear_ = 0;
+
+ // Whether the client wants a depth or stencil buffer.
+ const bool want_depth_;
+ const bool want_stencil_;
+
+ // The color space of this buffer's storage, and the color space in which
+ // shader samplers will read this buffer.
+ const gfx::ColorSpace storage_color_space_;
+ const gfx::ColorSpace sampler_color_space_;
+
+ enum AntialiasingMode {
+ kNone,
+ kMSAAImplicitResolve,
+ kMSAAExplicitResolve,
+ kScreenSpaceAntialiasing,
+ };
+
+ AntialiasingMode anti_aliasing_mode_ = kNone;
+
+ bool use_half_float_storage_ = false;
+
+ int max_texture_size_ = 0;
+ int sample_count_ = 0;
+ bool destruction_in_progress_ = false;
+ bool is_hidden_ = false;
+ SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
+
+ std::unique_ptr<WebExternalTextureLayer> layer_;
+
+ // Mailboxes that were released by the compositor can be used again by this
+ // DrawingBuffer.
+ Deque<scoped_refptr<ColorBuffer>> recycled_color_buffer_queue_;
+
+ // If the width and height of the Canvas's backing store don't
+ // match those that we were given in the most recent call to
+ // reshape(), then we need an intermediate bitmap to read back the
+ // frame buffer into. This seems to happen when CSS styles are
+ // used to resize the Canvas.
+ SkBitmap resizing_bitmap_;
+
+ // In the case of OffscreenCanvas, we do not want to enable the
+ // WebGLImageChromium flag, so we replace all the
+ // RuntimeEnabledFeatures::WebGLImageChromiumEnabled() call with
+ // shouldUseChromiumImage() calls, and set m_chromiumImageUsage to
+ // DisallowChromiumImage in the case of OffscreenCanvas.
+ ChromiumImageUsage chromium_image_usage_;
+ bool ShouldUseChromiumImage();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc
new file mode 100644
index 00000000000..c8e07ba0c7a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2016 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 "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include "cc/resources/shared_bitmap_id_registrar.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/command_buffer/client/gles2_interface_stub.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+
+// These unit tests are separate from DrawingBufferTests.cpp because they are
+// built as a part of webkit_unittests instead blink_platform_unittests. This is
+// because the software rendering mode has a dependency on the blink::Platform
+// interface for buffer allocations.
+
+namespace blink {
+namespace {
+
+class TestSharedBitmapIdRegistar : public cc::SharedBitmapIdRegistrar {
+ virtual cc::SharedBitmapIdRegistration RegisterSharedBitmapId(
+ const viz::SharedBitmapId& id,
+ scoped_refptr<cc::CrossThreadSharedBitmap> bitmap) {
+ return {};
+ }
+};
+
+class DrawingBufferSoftwareCompositingTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ bool gpu_compositing = false;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, kDisableMultisampling);
+ CHECK(drawing_buffer_);
+ }
+
+ scoped_refptr<DrawingBufferForTests> drawing_buffer_;
+ TestSharedBitmapIdRegistar test_shared_bitmap_id_registrar_;
+};
+
+TEST_F(DrawingBufferSoftwareCompositingTest, BitmapRecycling) {
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ drawing_buffer_->Resize(initial_size);
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback1); // create a bitmap.
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback1->Run(
+ gpu::SyncToken(),
+ false /* lostResource */); // release bitmap to the recycling queue
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback2); // recycle a bitmap.
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback2->Run(
+ gpu::SyncToken(),
+ false /* lostResource */); // release bitmap to the recycling queue
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+ drawing_buffer_->Resize(alternate_size);
+ drawing_buffer_->MarkContentsChanged();
+ // Regression test for crbug.com/647896 - Next line must not crash
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback3); // cause recycling queue to be purged due to resize
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferSoftwareCompositingTest, FramebufferBinding) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ GLint drawBinding = 0, readBinding = 0;
+
+ GLuint draw_framebuffer_binding = 0xbeef3;
+ GLuint read_framebuffer_binding = 0xbeef4;
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
+ gl_->SaveState();
+ drawing_buffer_->Resize(initial_size);
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource, &release_callback);
+ gl_->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawBinding);
+ gl_->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readBinding);
+ EXPECT_EQ(static_cast<GLint>(draw_framebuffer_binding), drawBinding);
+ EXPECT_EQ(static_cast<GLint>(read_framebuffer_binding), readBinding);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+ drawing_buffer_->BeginDestruction();
+}
+
+} // unnamed namespace
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
new file mode 100644
index 00000000000..e4171a1f293
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/client/gles2_interface_stub.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "v8/include/v8.h"
+
+using testing::Test;
+using testing::_;
+
+namespace blink {
+
+namespace {
+
+class FakePlatformSupport : public TestingPlatformSupport {
+ gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
+ return &test_gpu_memory_buffer_manager_;
+ }
+
+ private:
+ viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
+};
+
+} // anonymous namespace
+
+class DrawingBufferTest : public Test {
+ protected:
+ void SetUp() override { Init(kDisableMultisampling); }
+
+ void Init(UseMultisampling use_multisampling) {
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ bool gpu_compositing = true;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, use_multisampling);
+ CHECK(drawing_buffer_);
+ SetAndSaveRestoreState(false);
+ }
+
+ // Initialize GL state with unusual values, to verify that they are restored.
+ // The |invert| parameter will reverse all boolean parameters, so that all
+ // values are tested.
+ void SetAndSaveRestoreState(bool invert) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ GLboolean scissor_enabled = !invert;
+ GLfloat clear_color[4] = {0.1, 0.2, 0.3, 0.4};
+ GLfloat clear_depth = 0.8;
+ GLint clear_stencil = 37;
+ GLboolean color_mask[4] = {invert, !invert, !invert, invert};
+ GLboolean depth_mask = invert;
+ GLboolean stencil_mask = invert;
+ GLint pack_alignment = 7;
+ GLuint active_texture2d_binding = 0xbeef1;
+ GLuint renderbuffer_binding = 0xbeef2;
+ GLuint draw_framebuffer_binding = 0xbeef3;
+ GLuint read_framebuffer_binding = 0xbeef4;
+ GLuint pixel_unpack_buffer_binding = 0xbeef5;
+
+ if (scissor_enabled)
+ gl_->Enable(GL_SCISSOR_TEST);
+ else
+ gl_->Disable(GL_SCISSOR_TEST);
+
+ gl_->ClearColor(clear_color[0], clear_color[1], clear_color[2],
+ clear_color[3]);
+ gl_->ClearDepthf(clear_depth);
+ gl_->ClearStencil(clear_stencil);
+ gl_->ColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
+ gl_->DepthMask(depth_mask);
+ gl_->StencilMask(stencil_mask);
+ gl_->PixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
+ gl_->BindTexture(GL_TEXTURE_2D, active_texture2d_binding);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, renderbuffer_binding);
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
+ gl_->BindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_unpack_buffer_binding);
+
+ gl_->SaveState();
+ }
+
+ void VerifyStateWasRestored() {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ gl_->VerifyStateHasNotChangedSinceSave();
+ }
+
+ scoped_refptr<DrawingBufferForTests> drawing_buffer_;
+};
+
+class DrawingBufferTestMultisample : public DrawingBufferTest {
+ protected:
+ void SetUp() override { Init(kEnableMultisampling); }
+};
+
+TEST_F(DrawingBufferTestMultisample, verifyMultisampleResolve) {
+ // Initial state: already marked changed, multisampled
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->ExplicitResolveOfMultisampleData());
+
+ // Resolve the multisample buffer
+ drawing_buffer_->ResolveAndBindForReadAndDraw();
+
+ // After resolve, acknowledge new content
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ // No new content
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, VerifyResizingProperlyAffectsResources) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ VerifyStateWasRestored();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ // Produce one resource at size 100x100.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ VerifyStateWasRestored();
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+
+ // Resize to 100x50.
+ drawing_buffer_->Resize(alternate_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+
+ // Produce a resource at this size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+ VerifyStateWasRestored();
+
+ // Reset to initial size.
+ drawing_buffer_->Resize(initial_size);
+ VerifyStateWasRestored();
+ SetAndSaveRestoreState(true);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+
+ // Prepare another resource and verify that it's the correct size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ VerifyStateWasRestored();
+
+ // Prepare one final resource and verify that it's the correct size.
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ VerifyStateWasRestored();
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) {
+ bool live = true;
+ drawing_buffer_->live_ = &live;
+
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_STENCIL_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_DEPTH_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_COLOR_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+
+ drawing_buffer_->BeginDestruction();
+ ASSERT_EQ(live, true);
+
+ DrawingBufferForTests* raw_pointer = drawing_buffer_.get();
+ drawing_buffer_ = nullptr;
+ ASSERT_EQ(live, true);
+
+ EXPECT_FALSE(raw_pointer->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ ASSERT_EQ(live, true);
+
+ EXPECT_FALSE(raw_pointer->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ ASSERT_EQ(live, false);
+}
+
+TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) {
+ bool live = true;
+ drawing_buffer_->live_ = &live;
+
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+ VerifyStateWasRestored();
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), true /* lostResource */);
+ EXPECT_EQ(live, true);
+
+ drawing_buffer_->BeginDestruction();
+ EXPECT_EQ(live, true);
+
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_EQ(live, true);
+
+ DrawingBufferForTests* raw_ptr = drawing_buffer_.get();
+ drawing_buffer_ = nullptr;
+ EXPECT_EQ(live, true);
+
+ EXPECT_FALSE(raw_ptr->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), true /* lostResource */);
+ EXPECT_EQ(live, false);
+}
+
+TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) {
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+
+ // Release resources by specific order; 1, 3, 2.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+
+ // The first recycled resource must be 2. 1 and 3 were deleted by FIFO order
+ // because DrawingBuffer never keeps more than one resource.
+ viz::TransferableResource recycled_resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback1;
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &recycled_resource1, &recycled_release_callback1));
+ EXPECT_EQ(resource2.mailbox_holder.mailbox,
+ recycled_resource1.mailbox_holder.mailbox);
+
+ // The second recycled resource must be a new resource.
+ viz::TransferableResource recycled_resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback2;
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &recycled_resource2, &recycled_release_callback2));
+ EXPECT_NE(resource1.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+ EXPECT_NE(resource2.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+ EXPECT_NE(resource3.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+
+ recycled_release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ recycled_release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncTokenCorrectly) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ // PrepareTransferableResource() does not wait for any sync point.
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+
+ gpu::SyncToken wait_sync_token;
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer will wait for the sync point when recycling.
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ // m_drawingBuffer waits for the sync point when recycling in
+ // PrepareTransferableResource().
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+
+ drawing_buffer_->BeginDestruction();
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer waits for the sync point because the destruction is in
+ // progress.
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+}
+
+class DrawingBufferImageChromiumTest : public DrawingBufferTest,
+ private ScopedWebGLImageChromiumForTest {
+ public:
+ DrawingBufferImageChromiumTest() : ScopedWebGLImageChromiumForTest(true) {}
+
+ protected:
+ void SetUp() override {
+ platform_.reset(new ScopedTestingPlatformSupport<FakePlatformSupport>);
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ image_id0_ = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
+ bool gpu_compositing = true;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, kDisableMultisampling);
+ CHECK(drawing_buffer_);
+ SetAndSaveRestoreState(true);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ }
+
+ void TearDown() override {
+ platform_.reset();
+ }
+
+ GLuint image_id0_;
+ std::unique_ptr<ScopedTestingPlatformSupport<FakePlatformSupport>> platform_;
+};
+
+TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ GLuint image_id1 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id1)).Times(1);
+ // Produce one resource at size 100x100.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ GLuint image_id2 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id0_)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id0_)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id1)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id1)).Times(1);
+ // Resize to 100x50.
+ drawing_buffer_->Resize(alternate_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id3 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id3)).Times(1);
+ // Produce a resource at this size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(alternate_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id4 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id4)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id3)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id3)).Times(1);
+ // Reset to initial size.
+ drawing_buffer_->Resize(initial_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id5 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id5)).Times(1);
+ // Prepare another resource and verify that it's the correct size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ // Prepare one final resource and verify that it's the correct size.
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id5)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id5)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id4)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id4)).Times(1);
+ drawing_buffer_->BeginDestruction();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+}
+
+TEST_F(DrawingBufferImageChromiumTest, AllocationFailure) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ // Request a resource. An image should already be created. Everything works
+ // as expected.
+ EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(resource1.is_overlay_candidate);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ // Force image CHROMIUM creation failure. Request another resource. It should
+ // still be provided, but this time with allowOverlay = false.
+ gl_->SetCreateImageChromiumFail(true);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_FALSE(resource2.is_overlay_candidate);
+ VerifyStateWasRestored();
+
+ // Check that if image CHROMIUM starts working again, resources are
+ // correctly created with allowOverlay = true.
+ EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
+ gl_->SetCreateImageChromiumFail(false);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+ EXPECT_TRUE(resource3.is_overlay_candidate);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+
+ EXPECT_CALL(*gl_, DestroyImageMock(_)).Times(3);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(_)).Times(3);
+ drawing_buffer_->BeginDestruction();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+}
+
+class DepthStencilTrackingGLES2Interface
+ : public gpu::gles2::GLES2InterfaceStub {
+ public:
+ void FramebufferRenderbuffer(GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer) override {
+ switch (attachment) {
+ case GL_DEPTH_ATTACHMENT:
+ depth_attachment_ = renderbuffer;
+ break;
+ case GL_STENCIL_ATTACHMENT:
+ stencil_attachment_ = renderbuffer;
+ break;
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ depth_stencil_attachment_ = renderbuffer;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ GLenum CheckFramebufferStatus(GLenum target) override {
+ return GL_FRAMEBUFFER_COMPLETE;
+ }
+
+ void GetIntegerv(GLenum ptype, GLint* value) override {
+ switch (ptype) {
+ case GL_MAX_TEXTURE_SIZE:
+ *value = 1024;
+ return;
+ }
+ }
+
+ const GLubyte* GetString(GLenum type) override {
+ if (type == GL_EXTENSIONS)
+ return reinterpret_cast<const GLubyte*>("GL_OES_packed_depth_stencil");
+ return reinterpret_cast<const GLubyte*>("");
+ }
+
+ void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override {
+ for (GLsizei i = 0; i < n; ++i)
+ renderbuffers[i] = next_gen_renderbuffer_id_++;
+ }
+
+ GLuint StencilAttachment() const { return stencil_attachment_; }
+ GLuint DepthAttachment() const { return depth_attachment_; }
+ GLuint DepthStencilAttachment() const { return depth_stencil_attachment_; }
+ size_t NumAllocatedRenderBuffer() const {
+ return next_gen_renderbuffer_id_ - 1;
+ }
+
+ private:
+ GLuint next_gen_renderbuffer_id_ = 1;
+ GLuint depth_attachment_ = 0;
+ GLuint stencil_attachment_ = 0;
+ GLuint depth_stencil_attachment_ = 0;
+};
+
+struct DepthStencilTestCase {
+ DepthStencilTestCase(bool request_stencil,
+ bool request_depth,
+ int expected_render_buffers,
+ const char* const test_case_name)
+ : request_stencil(request_stencil),
+ request_depth(request_depth),
+ expected_render_buffers(expected_render_buffers),
+ test_case_name(test_case_name) {}
+
+ bool request_stencil;
+ bool request_depth;
+ size_t expected_render_buffers;
+ const char* const test_case_name;
+};
+
+// This tests that, when the packed depth+stencil extension is supported, and
+// either depth or stencil is requested, DrawingBuffer always allocates a single
+// packed renderbuffer and properly computes the actual context attributes as
+// defined by WebGL. We always allocate a packed buffer in this case since many
+// desktop OpenGL drivers that support this extension do not consider a
+// framebuffer with only a depth or a stencil buffer attached to be complete.
+TEST(DrawingBufferDepthStencilTest, packedDepthStencilSupported) {
+ DepthStencilTestCase cases[] = {
+ DepthStencilTestCase(false, false, 0, "neither"),
+ DepthStencilTestCase(true, false, 1, "stencil only"),
+ DepthStencilTestCase(false, true, 1, "depth only"),
+ DepthStencilTestCase(true, true, 1, "both"),
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
+ SCOPED_TRACE(cases[i].test_case_name);
+ auto gl = std::make_unique<DepthStencilTrackingGLES2Interface>();
+ DepthStencilTrackingGLES2Interface* tracking_gl = gl.get();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::kPreserve;
+
+ bool premultiplied_alpha = false;
+ bool want_alpha_channel = true;
+ bool want_depth_buffer = cases[i].request_depth;
+ bool want_stencil_buffer = cases[i].request_stencil;
+ bool want_antialiasing = false;
+ bool gpu_compositing = true;
+ scoped_refptr<DrawingBuffer> drawing_buffer = DrawingBuffer::Create(
+ std::move(provider), gpu_compositing, nullptr, IntSize(10, 10),
+ premultiplied_alpha, want_alpha_channel, want_depth_buffer,
+ want_stencil_buffer, want_antialiasing, preserve,
+ DrawingBuffer::kWebGL1, DrawingBuffer::kAllowChromiumImage,
+ CanvasColorParams());
+
+ // When we request a depth or a stencil buffer, we will get both.
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasDepthBuffer());
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasStencilBuffer());
+ EXPECT_EQ(cases[i].expected_render_buffers,
+ tracking_gl->NumAllocatedRenderBuffer());
+ if (cases[i].request_depth || cases[i].request_stencil) {
+ EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ } else {
+ EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ }
+
+ drawing_buffer->Resize(IntSize(10, 20));
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasDepthBuffer());
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasStencilBuffer());
+ EXPECT_EQ(cases[i].expected_render_buffers,
+ tracking_gl->NumAllocatedRenderBuffer());
+ if (cases[i].request_depth || cases[i].request_stencil) {
+ EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ } else {
+ EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ }
+
+ drawing_buffer->BeginDestruction();
+ }
+}
+
+TEST_F(DrawingBufferTest, VerifySetIsHiddenProperlyAffectsMailboxes) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+
+ gpu::SyncToken wait_sync_token;
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ drawing_buffer_->SetIsHidden(true);
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer deletes resource immediately when hidden.
+
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest,
+ VerifyTooBigDrawingBufferExceedingV8MaxSizeFailsToCreate) {
+ IntSize too_big_size(1, (v8::TypedArray::kMaxLength / 4) + 1);
+ bool gpu_compositing = true;
+ scoped_refptr<DrawingBuffer> too_big_drawing_buffer = DrawingBuffer::Create(
+ nullptr, gpu_compositing, nullptr, too_big_size, false, false, false,
+ false, false, DrawingBuffer::kDiscard, DrawingBuffer::kWebGL1,
+ DrawingBuffer::kAllowChromiumImage, CanvasColorParams());
+ EXPECT_EQ(too_big_drawing_buffer, nullptr);
+ drawing_buffer_->BeginDestruction();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
new file mode 100644
index 00000000000..308e30ab5a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
@@ -0,0 +1,452 @@
+// Copyright 2016 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
+
+#include "build/build_config.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+enum {
+ kInitialWidth = 100,
+ kInitialHeight = 100,
+ kAlternateHeight = 50,
+};
+
+enum UseMultisampling {
+ kDisableMultisampling,
+ kEnableMultisampling,
+};
+
+class WebGraphicsContext3DProviderForTests
+ : public WebGraphicsContext3DProvider {
+ public:
+ WebGraphicsContext3DProviderForTests(
+ std::unique_ptr<gpu::gles2::GLES2Interface> gl)
+ : gl_(std::move(gl)) {}
+
+ gpu::gles2::GLES2Interface* ContextGL() override { return gl_.get(); }
+ bool IsSoftwareRendering() const override { return false; }
+
+ // Not used by WebGL code.
+ GrContext* GetGrContext() override { return nullptr; }
+ bool BindToCurrentThread() override { return false; }
+ const gpu::Capabilities& GetCapabilities() const override {
+ return capabilities_;
+ }
+ const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
+ return gpu_feature_info_;
+ }
+ viz::GLHelper* GetGLHelper() override { return nullptr; }
+ void SetLostContextCallback(base::Closure) override {}
+ void SetErrorMessageCallback(
+ base::RepeatingCallback<void(const char*, int32_t id)>) {}
+ void SignalQuery(uint32_t, base::OnceClosure) override {}
+ cc::ImageDecodeCache* ImageDecodeCache() override { return nullptr; }
+
+ private:
+ std::unique_ptr<gpu::gles2::GLES2Interface> gl_;
+ gpu::Capabilities capabilities_;
+ gpu::GpuFeatureInfo gpu_feature_info_;
+};
+
+// The target to use when binding a texture to a Chromium image.
+GLenum ImageCHROMIUMTextureTarget() {
+#if defined(OS_MACOSX)
+ return GC3D_TEXTURE_RECTANGLE_ARB;
+#else
+ return GL_TEXTURE_2D;
+#endif
+}
+
+// The target to use when preparing a mailbox texture.
+GLenum DrawingBufferTextureTarget() {
+ if (RuntimeEnabledFeatures::WebGLImageChromiumEnabled())
+ return ImageCHROMIUMTextureTarget();
+ return GL_TEXTURE_2D;
+}
+
+class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
+ public DrawingBuffer::Client {
+ public:
+ // GLES2InterfaceStub implementation:
+ void BindTexture(GLenum target, GLuint texture) override {
+ if (target == GL_TEXTURE_2D)
+ state_.active_texture2d_binding = texture;
+ bound_textures_[target] = texture;
+ }
+
+ void BindFramebuffer(GLenum target, GLuint framebuffer) override {
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ state_.draw_framebuffer_binding = framebuffer;
+ state_.read_framebuffer_binding = framebuffer;
+ break;
+ case GL_DRAW_FRAMEBUFFER:
+ state_.draw_framebuffer_binding = framebuffer;
+ break;
+ case GL_READ_FRAMEBUFFER:
+ state_.read_framebuffer_binding = framebuffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void BindRenderbuffer(GLenum target, GLuint renderbuffer) override {
+ state_.renderbuffer_binding = renderbuffer;
+ }
+
+ void Enable(GLenum cap) {
+ if (cap == GL_SCISSOR_TEST)
+ state_.scissor_enabled = true;
+ }
+
+ void Disable(GLenum cap) {
+ if (cap == GL_SCISSOR_TEST)
+ state_.scissor_enabled = false;
+ }
+
+ void ClearColor(GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha) override {
+ state_.clear_color[0] = red;
+ state_.clear_color[1] = green;
+ state_.clear_color[2] = blue;
+ state_.clear_color[3] = alpha;
+ }
+
+ void ClearDepthf(GLfloat depth) override { state_.clear_depth = depth; }
+
+ void ClearStencil(GLint s) override { state_.clear_stencil = s; }
+
+ void ColorMask(GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha) override {
+ state_.color_mask[0] = red;
+ state_.color_mask[1] = green;
+ state_.color_mask[2] = blue;
+ state_.color_mask[3] = alpha;
+ }
+
+ void DepthMask(GLboolean flag) override { state_.depth_mask = flag; }
+
+ void StencilMask(GLuint mask) override { state_.stencil_mask = mask; }
+
+ void StencilMaskSeparate(GLenum face, GLuint mask) override {
+ if (face == GL_FRONT)
+ state_.stencil_mask = mask;
+ }
+
+ void PixelStorei(GLenum pname, GLint param) override {
+ if (pname == GL_PACK_ALIGNMENT)
+ state_.pack_alignment = param;
+ }
+
+ void BindBuffer(GLenum target, GLuint buffer) override {
+ switch (target) {
+ case GL_PIXEL_UNPACK_BUFFER:
+ state_.pixel_unpack_buffer_binding = buffer;
+ break;
+ case GL_PIXEL_PACK_BUFFER:
+ state_.pixel_pack_buffer_binding = buffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override {
+ memcpy(&most_recently_waited_sync_token_, sync_token,
+ sizeof(most_recently_waited_sync_token_));
+ }
+
+ GLenum CheckFramebufferStatus(GLenum target) override {
+ return GL_FRAMEBUFFER_COMPLETE;
+ }
+
+ void GetIntegerv(GLenum pname, GLint* value) override {
+ switch (pname) {
+ case GL_DRAW_FRAMEBUFFER_BINDING:
+ *value = state_.draw_framebuffer_binding;
+ break;
+ case GL_READ_FRAMEBUFFER_BINDING:
+ *value = state_.read_framebuffer_binding;
+ break;
+ case GL_MAX_TEXTURE_SIZE:
+ *value = 1024;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void GenMailboxCHROMIUM(GLbyte* mailbox) override {
+ ++current_mailbox_byte_;
+ memset(mailbox, current_mailbox_byte_, GL_MAILBOX_SIZE_CHROMIUM);
+ }
+
+ void ProduceTextureDirectCHROMIUM(GLuint texture,
+ const GLbyte* mailbox) override {
+ if (!create_image_chromium_fail_) {
+ ASSERT_TRUE(texture_sizes_.Contains(texture));
+ most_recently_produced_size_ = texture_sizes_.at(texture);
+ }
+ }
+
+ void TexImage2D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override {
+ if (target == GL_TEXTURE_2D && !level) {
+ texture_sizes_.Set(bound_textures_[target], IntSize(width, height));
+ }
+ }
+
+ GLuint CreateImageCHROMIUM(ClientBuffer buffer,
+ GLsizei width,
+ GLsizei height,
+ GLenum internalformat) override {
+ if (create_image_chromium_fail_)
+ return 0;
+ image_sizes_.Set(current_image_id_, IntSize(width, height));
+ return current_image_id_++;
+ }
+
+ MOCK_METHOD1(DestroyImageMock, void(GLuint imageId));
+ void DestroyImageCHROMIUM(GLuint image_id) {
+ image_sizes_.erase(image_id);
+ // No textures should be bound to this.
+ CHECK(image_to_texture_map_.find(image_id) == image_to_texture_map_.end());
+ image_sizes_.erase(image_id);
+ DestroyImageMock(image_id);
+ }
+
+ MOCK_METHOD1(BindTexImage2DMock, void(GLint imageId));
+ void BindTexImage2DCHROMIUM(GLenum target, GLint image_id) {
+ if (target == ImageCHROMIUMTextureTarget()) {
+ texture_sizes_.Set(bound_textures_[target],
+ image_sizes_.find(image_id)->value);
+ image_to_texture_map_.Set(image_id, bound_textures_[target]);
+ BindTexImage2DMock(image_id);
+ }
+ }
+
+ MOCK_METHOD1(ReleaseTexImage2DMock, void(GLint imageId));
+ void ReleaseTexImage2DCHROMIUM(GLenum target, GLint image_id) {
+ if (target == ImageCHROMIUMTextureTarget()) {
+ image_sizes_.Set(current_image_id_, IntSize());
+ image_to_texture_map_.erase(image_id);
+ ReleaseTexImage2DMock(image_id);
+ }
+ }
+
+ void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
+ static uint64_t unique_id = 1;
+ gpu::SyncToken source(
+ gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(unique_id++), 2);
+ memcpy(sync_token, &source, sizeof(source));
+ }
+
+ void GenTextures(GLsizei n, GLuint* textures) override {
+ static GLuint id = 1;
+ for (GLsizei i = 0; i < n; ++i)
+ textures[i] = id++;
+ }
+
+ // DrawingBuffer::Client implementation.
+ bool DrawingBufferClientIsBoundForDraw() override {
+ return !state_.draw_framebuffer_binding;
+ }
+ void DrawingBufferClientRestoreScissorTest() override {
+ state_.scissor_enabled = saved_state_.scissor_enabled;
+ }
+ void DrawingBufferClientRestoreMaskAndClearValues() override {
+ memcpy(state_.color_mask, saved_state_.color_mask,
+ sizeof(state_.color_mask));
+ state_.clear_depth = saved_state_.clear_depth;
+ state_.clear_stencil = saved_state_.clear_stencil;
+
+ memcpy(state_.clear_color, saved_state_.clear_color,
+ sizeof(state_.clear_color));
+ state_.depth_mask = saved_state_.depth_mask;
+ state_.stencil_mask = saved_state_.stencil_mask;
+ }
+ void DrawingBufferClientRestorePixelPackParameters() override {
+ // TODO(zmo): restore ES3 pack parameters?
+ state_.pack_alignment = saved_state_.pack_alignment;
+ }
+ void DrawingBufferClientRestoreTexture2DBinding() override {
+ state_.active_texture2d_binding = saved_state_.active_texture2d_binding;
+ }
+ void DrawingBufferClientRestoreRenderbufferBinding() override {
+ state_.renderbuffer_binding = saved_state_.renderbuffer_binding;
+ }
+ void DrawingBufferClientRestoreFramebufferBinding() override {
+ state_.draw_framebuffer_binding = saved_state_.draw_framebuffer_binding;
+ state_.read_framebuffer_binding = saved_state_.read_framebuffer_binding;
+ }
+ void DrawingBufferClientRestorePixelUnpackBufferBinding() override {
+ state_.pixel_unpack_buffer_binding =
+ saved_state_.pixel_unpack_buffer_binding;
+ }
+ void DrawingBufferClientRestorePixelPackBufferBinding() override {
+ state_.pixel_pack_buffer_binding = saved_state_.pixel_pack_buffer_binding;
+ }
+
+ // Testing methods.
+ gpu::SyncToken MostRecentlyWaitedSyncToken() const {
+ return most_recently_waited_sync_token_;
+ }
+ GLuint NextImageIdToBeCreated() const { return current_image_id_; }
+ IntSize MostRecentlyProducedSize() const {
+ return most_recently_produced_size_;
+ }
+
+ void SetCreateImageChromiumFail(bool fail) {
+ create_image_chromium_fail_ = fail;
+ }
+
+ // Saves current GL state for later verification.
+ void SaveState() { saved_state_ = state_; }
+ void VerifyStateHasNotChangedSinceSave() const {
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(state_.clear_color[0], saved_state_.clear_color[0]);
+ EXPECT_EQ(state_.color_mask[0], saved_state_.color_mask[0]);
+ }
+ EXPECT_EQ(state_.clear_depth, saved_state_.clear_depth);
+ EXPECT_EQ(state_.clear_stencil, saved_state_.clear_stencil);
+ EXPECT_EQ(state_.depth_mask, saved_state_.depth_mask);
+ EXPECT_EQ(state_.stencil_mask, saved_state_.stencil_mask);
+ EXPECT_EQ(state_.pack_alignment, saved_state_.pack_alignment);
+ EXPECT_EQ(state_.active_texture2d_binding,
+ saved_state_.active_texture2d_binding);
+ EXPECT_EQ(state_.renderbuffer_binding, saved_state_.renderbuffer_binding);
+ EXPECT_EQ(state_.draw_framebuffer_binding,
+ saved_state_.draw_framebuffer_binding);
+ EXPECT_EQ(state_.read_framebuffer_binding,
+ saved_state_.read_framebuffer_binding);
+ EXPECT_EQ(state_.pixel_unpack_buffer_binding,
+ saved_state_.pixel_unpack_buffer_binding);
+ EXPECT_EQ(state_.pixel_pack_buffer_binding,
+ saved_state_.pixel_pack_buffer_binding);
+ }
+
+ private:
+ std::map<GLenum, GLuint> bound_textures_;
+
+ // State tracked to verify that it is restored correctly.
+ struct State {
+ bool scissor_enabled = false;
+
+ GLfloat clear_color[4] = {0, 0, 0, 0};
+ GLfloat clear_depth = 0;
+ GLint clear_stencil = 0;
+
+ GLboolean color_mask[4] = {0, 0, 0, 0};
+ GLboolean depth_mask = 0;
+ GLuint stencil_mask = 0;
+
+ GLint pack_alignment = 4;
+
+ // The bound 2D texture for the active texture unit.
+ GLuint active_texture2d_binding = 0;
+ GLuint renderbuffer_binding = 0;
+ GLuint draw_framebuffer_binding = 0;
+ GLuint read_framebuffer_binding = 0;
+ GLuint pixel_unpack_buffer_binding = 0;
+ GLuint pixel_pack_buffer_binding = 0;
+ };
+ State state_;
+ State saved_state_;
+
+ gpu::SyncToken most_recently_waited_sync_token_;
+ GLbyte current_mailbox_byte_ = 0;
+ IntSize most_recently_produced_size_;
+ bool create_image_chromium_fail_ = false;
+ GLuint current_image_id_ = 1;
+ HashMap<GLuint, IntSize> texture_sizes_;
+ HashMap<GLuint, IntSize> image_sizes_;
+ HashMap<GLuint, GLuint> image_to_texture_map_;
+};
+
+class DrawingBufferForTests : public DrawingBuffer {
+ public:
+ static scoped_refptr<DrawingBufferForTests> Create(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ DrawingBuffer::Client* client,
+ const IntSize& size,
+ PreserveDrawingBuffer preserve,
+ UseMultisampling use_multisampling) {
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(context_provider->ContextGL());
+ scoped_refptr<DrawingBufferForTests> drawing_buffer =
+ base::AdoptRef(new DrawingBufferForTests(
+ std::move(context_provider), using_gpu_compositing,
+ std::move(extensions_util), client, preserve));
+ if (!drawing_buffer->Initialize(
+ size, use_multisampling != kDisableMultisampling)) {
+ drawing_buffer->BeginDestruction();
+ return nullptr;
+ }
+ return drawing_buffer;
+ }
+
+ DrawingBufferForTests(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil> extensions_util,
+ DrawingBuffer::Client* client,
+ PreserveDrawingBuffer preserve)
+ : DrawingBuffer(
+ std::move(context_provider),
+ using_gpu_compositing,
+ std::move(extensions_util),
+ client,
+ false /* discardFramebufferSupported */,
+ true /* wantAlphaChannel */,
+ true /* premultipliedAlpha */,
+ preserve,
+ kWebGL1,
+ false /* wantDepth */,
+ false /* wantStencil */,
+ DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
+ CanvasColorParams()),
+ live_(nullptr) {}
+
+ ~DrawingBufferForTests() override {
+ if (live_)
+ *live_ = false;
+ }
+
+ GLES2InterfaceForTests* ContextGLForTests() {
+ return static_cast<GLES2InterfaceForTests*>(ContextGL());
+ }
+
+ bool* live_;
+
+ int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
+};
+
+} // blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
new file mode 100644
index 00000000000..760ffa1f4a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 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 "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+namespace {
+
+void SplitStringHelper(const String& str, HashSet<String>& set) {
+ Vector<String> substrings;
+ str.Split(' ', substrings);
+ for (size_t i = 0; i < substrings.size(); ++i)
+ set.insert(substrings[i]);
+}
+
+} // anonymous namespace
+
+std::unique_ptr<Extensions3DUtil> Extensions3DUtil::Create(
+ gpu::gles2::GLES2Interface* gl) {
+ std::unique_ptr<Extensions3DUtil> out =
+ base::WrapUnique(new Extensions3DUtil(gl));
+ out->InitializeExtensions();
+ return out;
+}
+
+Extensions3DUtil::Extensions3DUtil(gpu::gles2::GLES2Interface* gl)
+ : gl_(gl), is_valid_(true) {}
+
+Extensions3DUtil::~Extensions3DUtil() = default;
+
+void Extensions3DUtil::InitializeExtensions() {
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // If the context is lost don't initialize the extension strings.
+ // This will cause supportsExtension, ensureExtensionEnabled, and
+ // isExtensionEnabled to always return false.
+ is_valid_ = false;
+ return;
+ }
+
+ String extensions_string(gl_->GetString(GL_EXTENSIONS));
+ SplitStringHelper(extensions_string, enabled_extensions_);
+
+ String requestable_extensions_string(gl_->GetRequestableExtensionsCHROMIUM());
+ SplitStringHelper(requestable_extensions_string, requestable_extensions_);
+}
+
+bool Extensions3DUtil::SupportsExtension(const String& name) {
+ return enabled_extensions_.Contains(name) ||
+ requestable_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::EnsureExtensionEnabled(const String& name) {
+ if (enabled_extensions_.Contains(name))
+ return true;
+
+ if (requestable_extensions_.Contains(name)) {
+ gl_->RequestExtensionCHROMIUM(name.Ascii().data());
+ enabled_extensions_.clear();
+ requestable_extensions_.clear();
+ InitializeExtensions();
+ }
+ return enabled_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::IsExtensionEnabled(const String& name) {
+ return enabled_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::CanUseCopyTextureCHROMIUM(GLenum dest_target) {
+ switch (dest_target) {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h
new file mode 100644
index 00000000000..f220a21eb4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT Extensions3DUtil final {
+ USING_FAST_MALLOC(Extensions3DUtil);
+ WTF_MAKE_NONCOPYABLE(Extensions3DUtil);
+
+ public:
+ // Creates a new Extensions3DUtil. If the passed GLES2Interface has been
+ // spontaneously lost, returns null.
+ static std::unique_ptr<Extensions3DUtil> Create(gpu::gles2::GLES2Interface*);
+ ~Extensions3DUtil();
+
+ bool IsValid() { return is_valid_; }
+
+ bool SupportsExtension(const String& name);
+ bool EnsureExtensionEnabled(const String& name);
+ bool IsExtensionEnabled(const String& name);
+
+ static bool CanUseCopyTextureCHROMIUM(GLenum dest_target);
+
+ private:
+ Extensions3DUtil(gpu::gles2::GLES2Interface*);
+ void InitializeExtensions();
+
+ gpu::gles2::GLES2Interface* gl_;
+ HashSet<String> enabled_extensions_;
+ HashSet<String> requestable_extensions_;
+ bool is_valid_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc
new file mode 100644
index 00000000000..0adc80a4a48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc
@@ -0,0 +1,83 @@
+// Copyright 2017 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 "third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace {
+
+struct GrTextureMailboxReleaseProcData {
+ GrTexture* gr_texture_;
+ base::WeakPtr<blink::WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper_;
+};
+
+void GrTextureMailboxReleaseProc(void* data) {
+ GrTextureMailboxReleaseProcData* release_proc_data =
+ static_cast<GrTextureMailboxReleaseProcData*>(data);
+
+ if (release_proc_data->context_provider_wrapper_) {
+ release_proc_data->context_provider_wrapper_->Utils()->RemoveCachedMailbox(
+ release_proc_data->gr_texture_);
+ }
+
+ delete release_proc_data;
+}
+
+} // unnamed namespace
+
+namespace blink {
+
+void GraphicsContext3DUtils::GetMailboxForSkImage(gpu::Mailbox& out_mailbox,
+ const sk_sp<SkImage>& image,
+ GLenum filter) {
+ // This object is owned by context_provider_wrapper_, so that weak ref
+ // should never be null.
+ DCHECK(context_provider_wrapper_);
+ DCHECK(image->isTextureBacked());
+ GrContext* gr = context_provider_wrapper_->ContextProvider()->GetGrContext();
+ gpu::gles2::GLES2Interface* gl =
+ context_provider_wrapper_->ContextProvider()->ContextGL();
+
+ DCHECK(gr);
+ DCHECK(gl);
+ GrTexture* gr_texture = image->getTexture();
+ DCHECK(gr == gr_texture->getContext());
+ auto it = cached_mailboxes_.find(gr_texture);
+ if (it != cached_mailboxes_.end()) {
+ out_mailbox = it->value;
+ } else {
+ gl->GenMailboxCHROMIUM(out_mailbox.name);
+
+ GrTextureMailboxReleaseProcData* release_proc_data =
+ new GrTextureMailboxReleaseProcData();
+ release_proc_data->gr_texture_ = gr_texture;
+ release_proc_data->context_provider_wrapper_ = context_provider_wrapper_;
+ gr_texture->setRelease(GrTextureMailboxReleaseProc, release_proc_data);
+ cached_mailboxes_.insert(gr_texture, out_mailbox);
+ }
+
+ GLuint texture_id =
+ skia::GrBackendObjectToGrGLTextureInfo(image->getTextureHandle(true))
+ ->fID;
+ gl->BindTexture(GL_TEXTURE_2D, texture_id);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->ProduceTextureDirectCHROMIUM(texture_id, out_mailbox.name);
+ image->getTexture()->textureParamsModified();
+}
+
+void GraphicsContext3DUtils::RemoveCachedMailbox(GrTexture* gr_texture) {
+ cached_mailboxes_.erase(gr_texture);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
new file mode 100644
index 00000000000..d88bef383ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_GRAPHICS_CONTEXT_3D_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_GRAPHICS_CONTEXT_3D_UTILS_H_
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrTexture.h"
+
+typedef unsigned int GLenum;
+
+namespace blink {
+
+class WebGraphicsContext3DProviderWrapper;
+
+class PLATFORM_EXPORT GraphicsContext3DUtils {
+ public:
+ // The constructor takes a weak ref to the wrapper because it internally
+ // it generates callbacks that may outlive the wrapper.
+ GraphicsContext3DUtils(base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper)
+ : context_provider_wrapper_(std::move(context_provider_wrapper)) {}
+
+ // Use this service to create a new mailbox or possibly obtain a pre-existing
+ // mailbox for a given texture. The caching of pre-existing mailboxes survives
+ // when the texture gets recycled by skia for creating a new SkSurface or
+ // SkImage with a pre-existing GrTexture backing.
+ void GetMailboxForSkImage(gpu::Mailbox&,
+ const sk_sp<SkImage>&,
+ GLenum filter);
+ void RemoveCachedMailbox(GrTexture*);
+
+ private:
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+ WTF::HashMap<GrTexture*, gpu::Mailbox> cached_mailboxes_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
new file mode 100644
index 00000000000..10f2d7faeda
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -0,0 +1,217 @@
+// Copyright 2017 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 "third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h"
+
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
+namespace blink {
+
+ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode)
+ : opacity_mode_(opacity_mode) {
+ layer_ = Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
+ this);
+ GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+ layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+ if (opacity_mode_ == kOpaque) {
+ layer_->SetOpaque(true);
+ layer_->SetBlendBackgroundColor(false);
+ }
+}
+
+ImageLayerBridge::~ImageLayerBridge() {
+ if (!disposed_)
+ Dispose();
+}
+
+void ImageLayerBridge::SetImage(scoped_refptr<StaticBitmapImage> image) {
+ if (disposed_)
+ return;
+
+ image_ = std::move(image);
+ if (image_) {
+ if (opacity_mode_ == kNonOpaque) {
+ layer_->SetOpaque(image_->CurrentFrameKnownToBeOpaque());
+ layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque());
+ }
+ }
+ if (!has_presented_since_last_set_image_ && image_ &&
+ image_->IsTextureBacked()) {
+ // If the layer bridge is not presenting, the GrContext may not be getting
+ // flushed regularly. The flush is normally triggered inside the
+ // m_image->EnsureMailbox() call of
+ // ImageLayerBridge::PrepareTransferableResource. To prevent a potential
+ // memory leak we must flush the GrContext here.
+ image_->PaintImageForCurrentFrame().GetSkImage()->getTextureHandle(
+ true); // GrContext flush.
+ }
+ has_presented_since_last_set_image_ = false;
+}
+
+void ImageLayerBridge::SetUV(const FloatPoint left_top,
+ const FloatPoint right_bottom) {
+ if (disposed_)
+ return;
+
+ layer_->SetUV(WebFloatPoint(left_top.X(), left_top.Y()),
+ WebFloatPoint(right_bottom.X(), right_bottom.Y()));
+}
+
+void ImageLayerBridge::Dispose() {
+ if (layer_) {
+ GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+ layer_->ClearTexture();
+ layer_.reset();
+ }
+ image_ = nullptr;
+ disposed_ = true;
+}
+
+bool ImageLayerBridge::PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ if (disposed_)
+ return false;
+
+ if (!image_)
+ return false;
+
+ if (has_presented_since_last_set_image_)
+ return false;
+
+ has_presented_since_last_set_image_ = true;
+
+ bool gpu_compositing = SharedGpuContext::IsGpuCompositingEnabled();
+ bool gpu_image = image_->IsTextureBacked();
+
+ // Expect software images for software compositing.
+ if (!gpu_compositing && gpu_image)
+ return false;
+
+ // If the texture comes from a software image then it does not need to be
+ // flipped.
+ layer_->SetFlipped(gpu_image);
+
+ scoped_refptr<StaticBitmapImage> image_for_compositor;
+
+ // Upload to a texture if the compositor is expecting one.
+ if (gpu_compositing && !image_->IsTextureBacked()) {
+ image_for_compositor =
+ image_->MakeAccelerated(SharedGpuContext::ContextProviderWrapper());
+ } else if (!gpu_compositing && image_->IsTextureBacked()) {
+ image_for_compositor = image_->MakeUnaccelerated();
+ } else {
+ image_for_compositor = image_;
+ }
+ DCHECK_EQ(image_for_compositor->IsTextureBacked(), gpu_compositing);
+
+ if (gpu_compositing) {
+ uint32_t filter =
+ filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
+ image_for_compositor->EnsureMailbox(kUnverifiedSyncToken, filter);
+ *out_resource = viz::TransferableResource::MakeGL(
+ image_for_compositor->GetMailbox(), filter, GL_TEXTURE_2D,
+ image_for_compositor->GetSyncToken());
+ auto func =
+ WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu,
+ WrapWeakPersistent(this), std::move(image_for_compositor));
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ } else {
+ std::unique_ptr<viz::SharedBitmap> bitmap =
+ CreateOrRecycleBitmap(image_for_compositor->Size());
+ if (!bitmap)
+ return false;
+
+ sk_sp<SkImage> sk_image =
+ image_for_compositor->PaintImageForCurrentFrame().GetSkImage();
+ if (!sk_image)
+ return false;
+
+ SkImageInfo dst_info =
+ SkImageInfo::MakeN32Premul(image_for_compositor->width(), 1);
+ dst_info = dst_info.makeColorSpace(sk_image->refColorSpace());
+ size_t row_bytes = image_for_compositor->width() * 4;
+
+ // Copy from SkImage into |bitmap|, while flipping the Y axis.
+ for (int row = 0; row < image_for_compositor->height(); row++) {
+ if (!sk_image->readPixels(dst_info, bitmap->pixels(), row_bytes, 0, 0))
+ return false;
+ }
+
+ *out_resource = viz::TransferableResource::MakeSoftware(
+ bitmap->id(), bitmap->sequence_number(),
+ gfx::Size(image_for_compositor->width(),
+ image_for_compositor->height()),
+ viz::RGBA_8888);
+ auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedSoftware,
+ WrapWeakPersistent(this), std::move(bitmap),
+ image_for_compositor->Size());
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ }
+
+ // TODO(junov): Figure out how to get the color space info.
+ // out_resource->color_space = ...;
+
+ return true;
+}
+
+std::unique_ptr<viz::SharedBitmap> ImageLayerBridge::CreateOrRecycleBitmap(
+ const IntSize& size) {
+ auto it = std::remove_if(
+ recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
+ [&size](const RecycledBitmap& bitmap) { return bitmap.size != size; });
+ recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
+
+ if (!recycled_bitmaps_.IsEmpty()) {
+ RecycledBitmap recycled = std::move(recycled_bitmaps_.back());
+ recycled_bitmaps_.pop_back();
+ DCHECK(recycled.size == size);
+ return std::move(recycled.bitmap);
+ }
+ return Platform::Current()->AllocateSharedBitmap(size, viz::RGBA_8888);
+}
+
+void ImageLayerBridge::ResourceReleasedGpu(
+ scoped_refptr<StaticBitmapImage> image,
+ const gpu::SyncToken& token,
+ bool lost_resource) {
+ if (image && image->IsValid()) {
+ DCHECK(image->IsTextureBacked());
+ if (token.HasData() && image->ContextProvider() &&
+ image->ContextProvider()->ContextGL()) {
+ image->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM(
+ token.GetConstData());
+ }
+ }
+ // let 'image' go out of scope to release gpu resources.
+}
+
+void ImageLayerBridge::ResourceReleasedSoftware(
+ std::unique_ptr<viz::SharedBitmap> bitmap,
+ const IntSize& size,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ DCHECK(!sync_token.HasData()); // No sync tokens for software resources.
+ if (!disposed_ && !lost_resource) {
+ RecycledBitmap recycled = {std::move(bitmap), size};
+ recycled_bitmaps_.push_back(std::move(recycled));
+ }
+}
+
+WebLayer* ImageLayerBridge::PlatformLayer() const {
+ return layer_->Layer();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
new file mode 100644
index 00000000000..9dfaae76c1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
@@ -0,0 +1,87 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
+
+#include "cc/layers/texture_layer_client.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace viz {
+class SharedBitmap;
+}
+
+namespace blink {
+
+class WebLayer;
+class WebExternalTextureLayer;
+
+class PLATFORM_EXPORT ImageLayerBridge
+ : public GarbageCollectedFinalized<ImageLayerBridge>,
+ public cc::TextureLayerClient {
+ WTF_MAKE_NONCOPYABLE(ImageLayerBridge);
+
+ public:
+ ImageLayerBridge(OpacityMode);
+ ~ImageLayerBridge();
+
+ void SetImage(scoped_refptr<StaticBitmapImage>);
+ void Dispose();
+
+ // cc::TextureLayerClient implementation.
+ bool PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+ override;
+
+ void ResourceReleasedGpu(scoped_refptr<StaticBitmapImage>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ void ResourceReleasedSoftware(std::unique_ptr<viz::SharedBitmap>,
+ const IntSize&,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ scoped_refptr<StaticBitmapImage> GetImage() { return image_; }
+
+ WebLayer* PlatformLayer() const;
+
+ void SetFilterQuality(SkFilterQuality filter_quality) {
+ filter_quality_ = filter_quality;
+ }
+ void SetUV(const FloatPoint left_top, const FloatPoint right_bottom);
+
+ bool IsAccelerated() { return image_->IsTextureBacked(); }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ std::unique_ptr<viz::SharedBitmap> CreateOrRecycleBitmap(const IntSize& size);
+
+ scoped_refptr<StaticBitmapImage> image_;
+ std::unique_ptr<WebExternalTextureLayer> layer_;
+ SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
+
+ // Shared memory bitmaps that were released by the compositor and can be used
+ // again by this ImageLayerBridge.
+ struct RecycledBitmap {
+ std::unique_ptr<viz::SharedBitmap> bitmap;
+ IntSize size;
+ };
+ Vector<RecycledBitmap> recycled_bitmaps_;
+
+ bool disposed_ = false;
+ bool has_presented_since_last_set_image_ = false;
+ OpacityMode opacity_mode_ = kNonOpaque;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
new file mode 100644
index 00000000000..c31f06e8e9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
@@ -0,0 +1,84 @@
+// Copyright 2015 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 "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace blink {
+
+std::unique_ptr<SharedContextRateLimiter> SharedContextRateLimiter::Create(
+ unsigned max_pending_ticks) {
+ return base::WrapUnique(new SharedContextRateLimiter(max_pending_ticks));
+}
+
+SharedContextRateLimiter::SharedContextRateLimiter(unsigned max_pending_ticks)
+ : max_pending_ticks_(max_pending_ticks), can_use_sync_queries_(false) {
+ context_provider_ =
+ Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (gl && gl->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+ // TODO(junov): when the GLES 3.0 command buffer is ready, we could use
+ // fenceSync instead.
+ can_use_sync_queries_ =
+ extensions_util->SupportsExtension("GL_CHROMIUM_sync_query");
+ }
+}
+
+void SharedContextRateLimiter::Tick() {
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (!gl || gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
+ return;
+
+ queries_.push_back(0);
+ if (can_use_sync_queries_) {
+ gl->GenQueriesEXT(1, &queries_.back());
+ gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, queries_.back());
+ gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+ }
+ if (queries_.size() > max_pending_ticks_) {
+ if (can_use_sync_queries_) {
+ GLuint result;
+ gl->GetQueryObjectuivEXT(queries_.front(), GL_QUERY_RESULT_EXT, &result);
+ gl->DeleteQueriesEXT(1, &queries_.front());
+ queries_.pop_front();
+ } else {
+ gl->Finish();
+ Reset();
+ }
+ }
+}
+
+void SharedContextRateLimiter::Reset() {
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (can_use_sync_queries_ && gl &&
+ gl->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ while (queries_.size() > 0) {
+ gl->DeleteQueriesEXT(1, &queries_.front());
+ queries_.pop_front();
+ }
+ } else {
+ queries_.clear();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
new file mode 100644
index 00000000000..86b0a6c0209
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
@@ -0,0 +1,59 @@
+// Copyright 2015 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_CONTEXT_RATE_LIMITER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_CONTEXT_RATE_LIMITER_H_
+
+#include <memory>
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class WebGraphicsContext3DProvider;
+
+// Purpose: to limit the amount of worked queued for execution
+// (backlog) on the GPU by blocking the main thread to allow the GPU
+// to catch up. The Prevents unsynchronized tight animation loops
+// from cause a GPU denial of service.
+//
+// How it works: The rate limiter uses GPU fences to mark each tick
+// and makes sure there are never more that 'maxPendingTicks' fences
+// that are awaiting completion. On platforms that do not support
+// fences, we use glFinish instead. glFinish will only be called in
+// unsynchronized cases that submit more than maxPendingTicks animation
+// tick per compositor frame, which should be quite rare.
+//
+// How to use it: Each unit of work that constitutes a complete animation
+// frame must call tick(). reset() must be called when the animation
+// is consumed by committing to the compositor. Several rate limiters can
+// be used concurrently: they will each use their own sequences of
+// fences which may be interleaved. When the graphics context is lost
+// and later restored, the existing rate limiter must be destroyed and
+// a new one created.
+
+class SharedContextRateLimiter final {
+ USING_FAST_MALLOC(SharedContextRateLimiter);
+ WTF_MAKE_NONCOPYABLE(SharedContextRateLimiter);
+
+ public:
+ static std::unique_ptr<SharedContextRateLimiter> Create(
+ unsigned max_pending_ticks);
+ void Tick();
+ void Reset();
+
+ private:
+ SharedContextRateLimiter(unsigned max_pending_ticks);
+
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider_;
+ Deque<GLuint> queries_;
+ unsigned max_pending_ticks_;
+ bool can_use_sync_queries_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc
new file mode 100644
index 00000000000..cfddf674cfc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc
@@ -0,0 +1,178 @@
+// Copyright 2016 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 "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+
+#include "base/single_thread_task_runner.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+SharedGpuContext* SharedGpuContext::GetInstanceForCurrentThread() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<SharedGpuContext>,
+ thread_specific_instance, ());
+ return thread_specific_instance;
+}
+
+SharedGpuContext::SharedGpuContext() = default;
+
+// static
+bool SharedGpuContext::IsGpuCompositingEnabled() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ // The check for gpu compositing enabled implies a context will
+ // desired, so we combine them into a single trip to the main thread.
+ // This also ensures that the compositing mode does not change before
+ // the context is created, so if it does change the context will be lost
+ // and this class will know to check the compositing mode again.
+ bool only_if_gpu_compositing = true;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ return !this_ptr->is_gpu_compositing_disabled_;
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+SharedGpuContext::ContextProviderWrapper() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ bool only_if_gpu_compositing = false;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ if (!this_ptr->context_provider_wrapper_)
+ return nullptr;
+ return this_ptr->context_provider_wrapper_->GetWeakPtr();
+}
+
+static void CreateContextProviderOnMainThread(
+ bool only_if_gpu_compositing,
+ bool* gpu_compositing_disabled,
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper>* wrapper,
+ WaitableEvent* waitable_event) {
+ DCHECK(IsMainThread());
+
+ Platform::ContextAttributes context_attributes;
+ context_attributes.enable_raster_interface = true;
+ context_attributes.support_grcontext = true;
+
+ *gpu_compositing_disabled = Platform::Current()->IsGpuCompositingDisabled();
+ if (*gpu_compositing_disabled && only_if_gpu_compositing) {
+ waitable_event->Signal();
+ return;
+ }
+
+ Platform::GraphicsInfo graphics_info;
+ auto context_provider =
+ Platform::Current()->CreateOffscreenGraphicsContext3DProvider(
+ context_attributes, WebURL(), nullptr, &graphics_info);
+ if (context_provider) {
+ *wrapper = std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ waitable_event->Signal();
+}
+
+void SharedGpuContext::CreateContextProviderIfNeeded(
+ bool only_if_gpu_compositing) {
+ // Once true, |is_gpu_compositing_disabled_| will always stay true.
+ if (is_gpu_compositing_disabled_ && only_if_gpu_compositing)
+ return;
+
+ // TODO(danakj): This needs to check that the context is being used on the
+ // thread it was made on, or else lock it.
+ if (context_provider_wrapper_ &&
+ context_provider_wrapper_->ContextProvider()
+ ->ContextGL()
+ ->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ // If the context isn't lost then |is_gpu_compositing_disabled_| state
+ // hasn't changed yet. RenderThreadImpl::CompositingModeFallbackToSoftware()
+ // will lose the context to let us know if it changes.
+ return;
+ }
+
+ is_gpu_compositing_disabled_ = false;
+ context_provider_wrapper_ = nullptr;
+
+ if (context_provider_factory_) {
+ // This path should only be used in unit tests.
+ auto context_provider =
+ context_provider_factory_.Run(&is_gpu_compositing_disabled_);
+ if (context_provider) {
+ context_provider_wrapper_ =
+ std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ } else if (IsMainThread()) {
+ is_gpu_compositing_disabled_ =
+ Platform::Current()->IsGpuCompositingDisabled();
+ if (is_gpu_compositing_disabled_ && only_if_gpu_compositing)
+ return;
+ auto context_provider =
+ Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
+ if (context_provider) {
+ context_provider_wrapper_ =
+ std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ } else {
+ // This synchronous round-trip to the main thread is the reason why
+ // SharedGpuContext encasulates the context provider: so we only have to do
+ // this once per thread.
+ WaitableEvent waitable_event;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ Platform::Current()->MainThread()->GetTaskRunner();
+ PostCrossThreadTask(
+ *task_runner, FROM_HERE,
+ CrossThreadBind(&CreateContextProviderOnMainThread,
+ only_if_gpu_compositing,
+ CrossThreadUnretained(&is_gpu_compositing_disabled_),
+ CrossThreadUnretained(&context_provider_wrapper_),
+ CrossThreadUnretained(&waitable_event)));
+ waitable_event.Wait();
+ if (context_provider_wrapper_ &&
+ !context_provider_wrapper_->ContextProvider()->BindToCurrentThread())
+ context_provider_wrapper_ = nullptr;
+ }
+}
+
+// static
+void SharedGpuContext::SetContextProviderFactoryForTesting(
+ ContextProviderFactory factory) {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ DCHECK(!this_ptr->context_provider_wrapper_);
+ this_ptr->context_provider_factory_ = std::move(factory);
+}
+
+// static
+void SharedGpuContext::ResetForTesting() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ this_ptr->is_gpu_compositing_disabled_ = false;
+ this_ptr->context_provider_wrapper_.reset();
+ this_ptr->context_provider_factory_.Reset();
+}
+
+bool SharedGpuContext::IsValidWithoutRestoring() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ if (!this_ptr->context_provider_wrapper_)
+ return false;
+ return this_ptr->context_provider_wrapper_->ContextProvider()
+ ->ContextGL()
+ ->GetGraphicsResetStatusKHR() == GL_NO_ERROR;
+}
+
+bool SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ bool only_if_gpu_compositing = false;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ if (!this_ptr->context_provider_wrapper_)
+ return false;
+ return !this_ptr->context_provider_wrapper_->ContextProvider()
+ ->GetGpuFeatureInfo()
+ .IsWorkaroundEnabled(
+ gpu::DISABLE_SOFTWARE_TO_ACCELERATED_CANVAS_UPGRADE);
+}
+
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h
new file mode 100644
index 00000000000..57535d41863
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h
@@ -0,0 +1,66 @@
+// Copyright 2016 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
+
+#include <memory>
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+
+class WebGraphicsContext3DProvider;
+
+// SharedGpuContext provides access to a thread-specific GPU context
+// that is shared by many callsites throughout the thread.
+// When on the main thread, provides access to the same context as
+// Platform::CreateSharedOffscreenGraphicsContext3DProvider, and the
+// same query as Platform::IsGPUCompositingEnabled().
+class PLATFORM_EXPORT SharedGpuContext {
+ public:
+ // Thread-safe query if gpu compositing is enabled. This should be done before
+ // calling ContextProviderWrapper() if the context will be used to make
+ // resources meant for the compositor. When it is false, no context will be
+ // needed and software-based resources should be given to the compositor
+ // instead.
+ static bool IsGpuCompositingEnabled();
+ // May re-create context if context was lost
+ static base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ ContextProviderWrapper();
+ static bool AllowSoftwareToAcceleratedCanvasUpgrade();
+ static bool IsValidWithoutRestoring();
+
+ using ContextProviderFactory =
+ base::RepeatingCallback<std::unique_ptr<WebGraphicsContext3DProvider>(
+ bool* is_gpu_compositing_disabled)>;
+ static void SetContextProviderFactoryForTesting(ContextProviderFactory);
+ // Resets the global instance including the |context_provider_factory_| and
+ // dropping the context. Should be called at the end of a test that uses this
+ // to not interfere with the next test.
+ static void ResetForTesting();
+
+ private:
+ friend class WTF::ThreadSpecific<SharedGpuContext>;
+
+ static SharedGpuContext* GetInstanceForCurrentThread();
+
+ SharedGpuContext();
+ void CreateContextProviderIfNeeded(bool only_if_gpu_compositing);
+
+ // Can be overridden for tests.
+ ContextProviderFactory context_provider_factory_;
+
+ // This is sticky once true, we never need to ask again.
+ bool is_gpu_compositing_disabled_ = false;
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper_;
+};
+
+} // blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
new file mode 100644
index 00000000000..368d8cb03db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
@@ -0,0 +1,272 @@
+// Copyright 2016 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 "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+
+#include <memory>
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+using testing::Test;
+
+namespace blink {
+
+namespace {
+
+template <class GLES2InterfaceType>
+class SharedGpuContextTestBase : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](GLES2InterfaceType* gl, bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ gl->SetIsContextLost(false);
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ GLES2InterfaceType gl_;
+};
+
+class SharedGpuContextTest
+ : public SharedGpuContextTestBase<FakeGLES2Interface> {};
+
+class MailboxMockGLES2Interface : public FakeGLES2Interface {
+ public:
+ MOCK_METHOD1(GenMailboxCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(GenSyncTokenCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*));
+};
+
+class MailboxSharedGpuContextTest
+ : public SharedGpuContextTestBase<MailboxMockGLES2Interface> {};
+
+// Test fixure that simulate a graphics context creation failure, when using gpu
+// compositing.
+class BadSharedGpuContextTest : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ return nullptr;
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+};
+
+// Test fixure that simulate not using gpu compositing.
+class SoftwareCompositingTest : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](FakeGLES2Interface* gl, bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = true;
+ // Return a context anyway, to ensure that's not what the class checks
+ // to determine compositing mode.
+ gl->SetIsContextLost(false);
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ FakeGLES2Interface gl_;
+};
+
+TEST_F(SharedGpuContextTest, contextLossAutoRecovery) {
+ EXPECT_NE(SharedGpuContext::ContextProviderWrapper(), nullptr);
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context =
+ SharedGpuContext::ContextProviderWrapper();
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ EXPECT_TRUE(!!context);
+
+ // Context recreation results in old provider being discarded.
+ EXPECT_TRUE(!!SharedGpuContext::ContextProviderWrapper());
+ EXPECT_FALSE(!!context);
+}
+
+TEST_F(SharedGpuContextTest, AccelerateImageBufferSurfaceAutoRecovery) {
+ // Verifies that after a context loss, attempting to allocate an
+ // AcceleratedImageBufferSurface will restore the context and succeed
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(SharedGpuContextTest, Canvas2DLayerBridgeAutoRecovery) {
+ // Verifies that after a context loss, attempting to allocate a
+ // Canvas2DLayerBridge will restore the context and succeed.
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ IntSize size(10, 10);
+ CanvasColorParams color_params;
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ std::make_unique<Canvas2DLayerBridge>(
+ size, 0,
+ /*msaa sample count*/ Canvas2DLayerBridge::kEnableAcceleration,
+ color_params);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+ bridge->BeginDestruction();
+}
+
+TEST_F(SharedGpuContextTest, IsValidWithoutRestoring) {
+ EXPECT_NE(SharedGpuContext::ContextProviderWrapper(), nullptr);
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(BadSharedGpuContextTest, IsValidWithoutRestoring) {
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(BadSharedGpuContextTest, AllowSoftwareToAcceleratedCanvasUpgrade) {
+ EXPECT_FALSE(SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade());
+}
+
+TEST_F(BadSharedGpuContextTest, AccelerateImageBufferSurfaceCreationFails) {
+ // With a bad shared context, AccelerateImageBufferSurface creation should
+ // fail gracefully
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_FALSE(!resource_provider);
+}
+
+TEST_F(SharedGpuContextTest, CompositingMode) {
+ EXPECT_TRUE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+TEST_F(BadSharedGpuContextTest, CompositingMode) {
+ EXPECT_TRUE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+TEST_F(SoftwareCompositingTest, CompositingMode) {
+ EXPECT_FALSE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+class FakeMailboxGenerator {
+ public:
+ void GenMailbox(GLbyte* name) { *name = counter_++; }
+
+ GLbyte counter_ = 1;
+};
+
+TEST_F(MailboxSharedGpuContextTest, MailboxCaching) {
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot();
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ FakeMailboxGenerator mailboxGenerator;
+ gpu::Mailbox mailbox;
+ mailbox.name[0] = 0;
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(1)
+ .WillOnce(testing::Invoke(&mailboxGenerator,
+ &FakeMailboxGenerator::GenMailbox));
+
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(0); // GenMailboxCHROMIUM must not be called!
+
+ mailbox.name[0] = 0;
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(MailboxSharedGpuContextTest, MailboxCacheSurvivesSkiaRecycling) {
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot();
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ FakeMailboxGenerator mailboxGenerator;
+ gpu::Mailbox mailbox;
+ mailbox.name[0] = 0;
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(1)
+ .WillOnce(testing::Invoke(&mailboxGenerator,
+ &FakeMailboxGenerator::GenMailbox));
+
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+
+ EXPECT_EQ(mailbox.name[0], 1);
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Destroy image and surface to return texture to recleable resource pool
+ image = nullptr;
+ resource_provider = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Re-creating surface should recycle the old GrTexture inside skia
+ resource_provider = CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ image = resource_provider->Snapshot();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(0); // GenMailboxCHROMIUM must not be called!
+
+ mailbox.name[0] = 0;
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+} // unnamed namespace
+
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc
new file mode 100644
index 00000000000..bdaa943e701
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc
@@ -0,0 +1,3260 @@
+// Copyright 2014 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 "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+
+#include <memory>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+namespace {
+
+const float kMaxInt8Value = INT8_MAX;
+const float kMaxUInt8Value = UINT8_MAX;
+const float kMaxInt16Value = INT16_MAX;
+const float kMaxUInt16Value = UINT16_MAX;
+const double kMaxInt32Value = INT32_MAX;
+const double kMaxUInt32Value = UINT32_MAX;
+
+int8_t ClampMin(int8_t value) {
+ const static int8_t kMinInt8Value = INT8_MIN + 1;
+ return value < kMinInt8Value ? kMinInt8Value : value;
+}
+
+int16_t ClampMin(int16_t value) {
+ const static int16_t kMinInt16Value = INT16_MIN + 1;
+ return value < kMinInt16Value ? kMinInt16Value : value;
+}
+
+int32_t ClampMin(int32_t value) {
+ const static int32_t kMinInt32Value = INT32_MIN + 1;
+ return value < kMinInt32Value ? kMinInt32Value : value;
+}
+
+// Return kDataFormatNumFormats if format/type combination is invalid.
+WebGLImageConversion::DataFormat GetDataFormat(GLenum destination_format,
+ GLenum destination_type) {
+ WebGLImageConversion::DataFormat dst_format =
+ WebGLImageConversion::kDataFormatRGBA8;
+ switch (destination_type) {
+ case GL_BYTE:
+ switch (destination_format) {
+ case GL_RED:
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR8_S;
+ break;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG8_S;
+ break;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB8_S;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA8_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_BYTE:
+ switch (destination_format) {
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_SRGB_EXT:
+ dst_format = WebGLImageConversion::kDataFormatRGB8;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_SRGB_ALPHA_EXT:
+ dst_format = WebGLImageConversion::kDataFormatRGBA8;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA8;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR8;
+ break;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG8;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA8;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_SHORT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR16_S;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG16_S;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB16_S;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR16;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD16;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG16;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB16;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_INT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR32_S;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG32_S;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB32_S;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR32;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD32;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG32;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB32;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_HALF_FLOAT_OES: // OES_texture_half_float
+ case GL_HALF_FLOAT:
+ switch (destination_format) {
+ case GL_RGBA:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16F;
+ break;
+ case GL_RGB:
+ dst_format = WebGLImageConversion::kDataFormatRGB16F;
+ break;
+ case GL_RG:
+ dst_format = WebGLImageConversion::kDataFormatRG16F;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA16F;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ dst_format = WebGLImageConversion::kDataFormatR16F;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA16F;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_FLOAT: // OES_texture_float
+ switch (destination_format) {
+ case GL_RGBA:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32F;
+ break;
+ case GL_RGB:
+ dst_format = WebGLImageConversion::kDataFormatRGB32F;
+ break;
+ case GL_RG:
+ dst_format = WebGLImageConversion::kDataFormatRG32F;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA32F;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ dst_format = WebGLImageConversion::kDataFormatR32F;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD32F;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA32F;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ dst_format = WebGLImageConversion::kDataFormatRGBA4444;
+ break;
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ dst_format = WebGLImageConversion::kDataFormatRGBA5551;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ dst_format = WebGLImageConversion::kDataFormatRGB565;
+ break;
+ case GL_UNSIGNED_INT_5_9_9_9_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGB5999;
+ break;
+ case GL_UNSIGNED_INT_24_8:
+ dst_format = WebGLImageConversion::kDataFormatDS24_8;
+ break;
+ case GL_UNSIGNED_INT_10F_11F_11F_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGB10F11F11F;
+ break;
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ return dst_format;
+}
+
+// The following Float to Half-Float conversion code is from the implementation
+// of ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf, "Fast Half
+// Float Conversions" by Jeroen van der Zijp, November 2008 (Revised September
+// 2010). Specially, the basetable[512] and shifttable[512] are generated as
+// follows:
+/*
+unsigned short basetable[512];
+unsigned char shifttable[512];
+
+void generatetables(){
+ unsigned int i;
+ int e;
+ for (i = 0; i < 256; ++i){
+ e = i - 127;
+ if (e < -24){ // Very small numbers map to zero
+ basetable[i | 0x000] = 0x0000;
+ basetable[i | 0x100] = 0x8000;
+ shifttable[i | 0x000] = 24;
+ shifttable[i | 0x100] = 24;
+ }
+ else if (e < -14) { // Small numbers map to denorms
+ basetable[i | 0x000] = (0x0400>>(-e-14));
+ basetable[i | 0x100] = (0x0400>>(-e-14)) | 0x8000;
+ shifttable[i | 0x000] = -e-1;
+ shifttable[i | 0x100] = -e-1;
+ }
+ else if (e <= 15){ // Normal numbers just lose precision
+ basetable[i | 0x000] = ((e+15)<<10);
+ basetable[i| 0x100] = ((e+15)<<10) | 0x8000;
+ shifttable[i|0x000] = 13;
+ shifttable[i|0x100] = 13;
+ }
+ else if (e<128){ // Large numbers map to Infinity
+ basetable[i|0x000] = 0x7C00;
+ basetable[i|0x100] = 0xFC00;
+ shifttable[i|0x000] = 24;
+ shifttable[i|0x100] = 24;
+ }
+ else { // Infinity and NaN's stay Infinity and NaN's
+ basetable[i|0x000] = 0x7C00;
+ basetable[i|0x100] = 0xFC00;
+ shifttable[i|0x000] = 13;
+ shifttable[i|0x100] = 13;
+ }
+ }
+}
+*/
+
+unsigned short g_base_table[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2, 4, 8, 16, 32, 64,
+ 128, 256, 512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192,
+ 9216, 10240, 11264, 12288, 13312, 14336, 15360, 16384, 17408, 18432, 19456,
+ 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32769, 32770, 32772, 32776,
+ 32784, 32800, 32832, 32896, 33024, 33280, 33792, 34816, 35840, 36864, 37888,
+ 38912, 39936, 40960, 41984, 43008, 44032, 45056, 46080, 47104, 48128, 49152,
+ 50176, 51200, 52224, 53248, 54272, 55296, 56320, 57344, 58368, 59392, 60416,
+ 61440, 62464, 63488, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512};
+
+unsigned char g_shift_table[512] = {
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 22,
+ 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13};
+
+unsigned short ConvertFloatToHalfFloat(float f) {
+ unsigned temp = *(reinterpret_cast<unsigned*>(&f));
+ unsigned signexp = (temp >> 23) & 0x1ff;
+ return g_base_table[signexp] +
+ ((temp & 0x007fffff) >> g_shift_table[signexp]);
+}
+
+/* BEGIN CODE SHARED WITH MOZILLA FIREFOX */
+
+// The following packing and unpacking routines are expressed in terms of
+// function templates and inline functions to achieve generality and speedup.
+// Explicit template specializations correspond to the cases that would occur.
+// Some code are merged back from Mozilla code in
+// http://mxr.mozilla.org/mozilla-central/source/content/canvas/src/WebGLTexelConversions.h
+
+//----------------------------------------------------------------------
+// Pixel unpacking routines.
+template <int format, typename SourceType, typename DstType>
+void Unpack(const SourceType*, DstType*, unsigned) {
+ NOTREACHED();
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatARGB8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1];
+ destination[1] = source[2];
+ destination[2] = source[3];
+ destination[3] = source[0];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatABGR8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ destination[1] = source[2];
+ destination[2] = source[1];
+ destination[3] = source[0];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatBGRA8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ const uint32_t* source32 = reinterpret_cast_ptr<const uint32_t*>(source);
+ uint32_t* destination32 = reinterpret_cast_ptr<uint32_t*>(destination);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfBGRA8LittleToRGBA8(source32, destination32,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfBGRA8LittleToRGBA8MSA(source32, destination32,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t bgra = source32[i];
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ uint32_t brMask = 0xff00ff00;
+ uint32_t gaMask = 0x00ff00ff;
+#else
+ uint32_t br_mask = 0x00ff00ff;
+ uint32_t ga_mask = 0xff00ff00;
+#endif
+ uint32_t rgba =
+ (((bgra >> 16) | (bgra << 16)) & br_mask) | (bgra & ga_mask);
+ destination32[i] = rgba;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA5551, uint16_t, uint8_t>(
+ const uint16_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfRGBA5551LittleToRGBA8(source, destination,
+ pixels_per_row);
+#endif
+#if WTF_CPU_ARM_NEON
+ SIMD::UnpackOneRowOfRGBA5551ToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfRGBA5551ToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint16_t packed_value = source[0];
+ uint8_t r = packed_value >> 11;
+ uint8_t g = (packed_value >> 6) & 0x1F;
+ uint8_t b = (packed_value >> 1) & 0x1F;
+ destination[0] = (r << 3) | (r & 0x7);
+ destination[1] = (g << 3) | (g & 0x7);
+ destination[2] = (b << 3) | (b & 0x7);
+ destination[3] = (packed_value & 0x1) ? 0xFF : 0x0;
+ source += 1;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA4444, uint16_t, uint8_t>(
+ const uint16_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfRGBA4444LittleToRGBA8(source, destination,
+ pixels_per_row);
+#endif
+#if WTF_CPU_ARM_NEON
+ SIMD::UnpackOneRowOfRGBA4444ToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfRGBA4444ToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint16_t packed_value = source[0];
+ uint8_t r = packed_value >> 12;
+ uint8_t g = (packed_value >> 8) & 0x0F;
+ uint8_t b = (packed_value >> 4) & 0x0F;
+ uint8_t a = packed_value & 0x0F;
+ destination[0] = r << 4 | r;
+ destination[1] = g << 4 | g;
+ destination[2] = b << 4 | b;
+ destination[3] = a << 4 | a;
+ source += 1;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRA8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[0];
+ destination[2] = source[0];
+ destination[3] = source[1];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatAR8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1];
+ destination[1] = source[1];
+ destination[2] = source[1];
+ destination[3] = source[0];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0] * kScaleFactor;
+ destination[1] = source[1] * kScaleFactor;
+ destination[2] = source[2] * kScaleFactor;
+ destination[3] = source[3] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatBGRA8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[2] * kScaleFactor;
+ destination[1] = source[1] * kScaleFactor;
+ destination[2] = source[0] * kScaleFactor;
+ destination[3] = source[3] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatABGR8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3] * kScaleFactor;
+ destination[1] = source[2] * kScaleFactor;
+ destination[2] = source[1] * kScaleFactor;
+ destination[3] = source[0] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatARGB8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1] * kScaleFactor;
+ destination[1] = source[2] * kScaleFactor;
+ destination[2] = source[3] * kScaleFactor;
+ destination[3] = source[0] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRA32F, float, float>(
+ const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[0];
+ destination[2] = source[0];
+ destination[3] = source[1];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA2_10_10_10, uint32_t, float>(
+ const uint32_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ static const float kRgbScaleFactor = 1.0f / 1023.0f;
+ static const float kAlphaScaleFactor = 1.0f / 3.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t packed_value = source[0];
+ destination[0] = static_cast<float>(packed_value & 0x3FF) * kRgbScaleFactor;
+ destination[1] =
+ static_cast<float>((packed_value >> 10) & 0x3FF) * kRgbScaleFactor;
+ destination[2] =
+ static_cast<float>((packed_value >> 20) & 0x3FF) * kRgbScaleFactor;
+ destination[3] = static_cast<float>(packed_value >> 30) * kAlphaScaleFactor;
+ source += 1;
+ destination += 4;
+ }
+}
+
+//----------------------------------------------------------------------
+// Pixel packing routines.
+//
+
+template <int format, int alphaOp, typename SourceType, typename DstType>
+void Pack(const SourceType*, DstType*, unsigned) {
+ NOTREACHED();
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToR8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToR8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToRA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToRA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ source += 4;
+ destination += 3;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort4444(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort4444MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF0) << 8) | ((source[1] & 0xF0) << 4) |
+ (source[2] & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF0) << 8) | ((source_g & 0xF0) << 4) |
+ (source_b & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF0) << 8) | ((source_g & 0xF0) << 4) |
+ (source_b & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort5551(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort5551MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xF8) << 3) |
+ ((source[2] & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xF8) << 3) |
+ ((source_b & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xF8) << 3) |
+ ((source_b & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort565(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort565MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xFC) << 3) |
+ ((source[2] & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xFC) << 3) |
+ ((source_b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xFC) << 3) |
+ ((source_b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ destination[2] = ConvertFloatToHalfFloat(source[2]);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ destination[2] = ConvertFloatToHalfFloat(source[2]);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int8_t,
+ int8_t>(const int8_t* source,
+ int8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ float scale_factor = static_cast<float>(destination[3]) / kMaxInt8Value;
+ destination[0] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint16_t,
+ uint16_t>(const uint16_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = static_cast<float>(source[3]) / kMaxUInt16Value;
+ destination[0] =
+ static_cast<uint16_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint16_t>(static_cast<float>(source[1]) * scale_factor);
+ destination[2] =
+ static_cast<uint16_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int16_t,
+ int16_t>(const int16_t* source,
+ int16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ float scale_factor = static_cast<float>(destination[3]) / kMaxInt16Value;
+ destination[0] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint32_t,
+ uint32_t>(const uint32_t* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ double scale_factor = static_cast<double>(source[3]) / kMaxUInt32Value;
+ destination[0] =
+ static_cast<uint32_t>(static_cast<double>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint32_t>(static_cast<double>(source[1]) * scale_factor);
+ destination[2] =
+ static_cast<uint32_t>(static_cast<double>(source[2]) * scale_factor);
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int32_t,
+ int32_t>(const int32_t* source,
+ int32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ double scale_factor = static_cast<double>(destination[3]) / kMaxInt32Value;
+ destination[0] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t r = static_cast<uint32_t>(source[0] * 1023.0f);
+ uint32_t g = static_cast<uint32_t>(source[1] * 1023.0f);
+ uint32_t b = static_cast<uint32_t>(source[2] * 1023.0f);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t r = static_cast<uint32_t>(source[0] * source[3] * 1023.0f);
+ uint32_t g = static_cast<uint32_t>(source[1] * source[3] * 1023.0f);
+ uint32_t b = static_cast<uint32_t>(source[2] * source[3] * 1023.0f);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1023.0f / source[3] : 1023.0f;
+ uint32_t r = static_cast<uint32_t>(source[0] * scale_factor);
+ uint32_t g = static_cast<uint32_t>(source[1] * scale_factor);
+ uint32_t b = static_cast<uint32_t>(source[2] * scale_factor);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = static_cast<float>(source[3]) / kMaxUInt8Value;
+ destination[0] =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor =
+ source[3] ? kMaxUInt8Value / static_cast<float>(source[3]) : 1.0f;
+ destination[0] =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ source += 4;
+ destination += 2;
+ }
+}
+
+bool HasAlpha(int format) {
+ return format == WebGLImageConversion::kDataFormatA8 ||
+ format == WebGLImageConversion::kDataFormatA16F ||
+ format == WebGLImageConversion::kDataFormatA32F ||
+ format == WebGLImageConversion::kDataFormatRA8 ||
+ format == WebGLImageConversion::kDataFormatAR8 ||
+ format == WebGLImageConversion::kDataFormatRA16F ||
+ format == WebGLImageConversion::kDataFormatRA32F ||
+ format == WebGLImageConversion::kDataFormatRGBA8 ||
+ format == WebGLImageConversion::kDataFormatBGRA8 ||
+ format == WebGLImageConversion::kDataFormatARGB8 ||
+ format == WebGLImageConversion::kDataFormatABGR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA16F ||
+ format == WebGLImageConversion::kDataFormatRGBA32F ||
+ format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ format == WebGLImageConversion::kDataFormatRGBA16 ||
+ format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ format == WebGLImageConversion::kDataFormatRGBA32 ||
+ format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ format == WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+}
+
+bool HasColor(int format) {
+ return format == WebGLImageConversion::kDataFormatRGBA8 ||
+ format == WebGLImageConversion::kDataFormatRGBA16F ||
+ format == WebGLImageConversion::kDataFormatRGBA32F ||
+ format == WebGLImageConversion::kDataFormatRGB8 ||
+ format == WebGLImageConversion::kDataFormatRGB16F ||
+ format == WebGLImageConversion::kDataFormatRGB32F ||
+ format == WebGLImageConversion::kDataFormatBGR8 ||
+ format == WebGLImageConversion::kDataFormatBGRA8 ||
+ format == WebGLImageConversion::kDataFormatARGB8 ||
+ format == WebGLImageConversion::kDataFormatABGR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ format == WebGLImageConversion::kDataFormatRGB565 ||
+ format == WebGLImageConversion::kDataFormatR8 ||
+ format == WebGLImageConversion::kDataFormatR16F ||
+ format == WebGLImageConversion::kDataFormatR32F ||
+ format == WebGLImageConversion::kDataFormatRA8 ||
+ format == WebGLImageConversion::kDataFormatRA16F ||
+ format == WebGLImageConversion::kDataFormatRA32F ||
+ format == WebGLImageConversion::kDataFormatAR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ format == WebGLImageConversion::kDataFormatRGBA16 ||
+ format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ format == WebGLImageConversion::kDataFormatRGBA32 ||
+ format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ format == WebGLImageConversion::kDataFormatRGB8_S ||
+ format == WebGLImageConversion::kDataFormatRGB16 ||
+ format == WebGLImageConversion::kDataFormatRGB16_S ||
+ format == WebGLImageConversion::kDataFormatRGB32 ||
+ format == WebGLImageConversion::kDataFormatRGB32_S ||
+ format == WebGLImageConversion::kDataFormatRGB10F11F11F ||
+ format == WebGLImageConversion::kDataFormatRGB5999 ||
+ format == WebGLImageConversion::kDataFormatRG8 ||
+ format == WebGLImageConversion::kDataFormatRG8_S ||
+ format == WebGLImageConversion::kDataFormatRG16 ||
+ format == WebGLImageConversion::kDataFormatRG16_S ||
+ format == WebGLImageConversion::kDataFormatRG32 ||
+ format == WebGLImageConversion::kDataFormatRG32_S ||
+ format == WebGLImageConversion::kDataFormatRG16F ||
+ format == WebGLImageConversion::kDataFormatRG32F ||
+ format == WebGLImageConversion::kDataFormatR8_S ||
+ format == WebGLImageConversion::kDataFormatR16 ||
+ format == WebGLImageConversion::kDataFormatR16_S ||
+ format == WebGLImageConversion::kDataFormatR32 ||
+ format == WebGLImageConversion::kDataFormatR32_S;
+}
+
+template <int Format>
+struct IsInt8Format {
+ STATIC_ONLY(IsInt8Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ Format == WebGLImageConversion::kDataFormatRGB8_S ||
+ Format == WebGLImageConversion::kDataFormatRG8_S ||
+ Format == WebGLImageConversion::kDataFormatR8_S;
+};
+
+template <int Format>
+struct IsInt16Format {
+ STATIC_ONLY(IsInt16Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ Format == WebGLImageConversion::kDataFormatRGB16_S ||
+ Format == WebGLImageConversion::kDataFormatRG16_S ||
+ Format == WebGLImageConversion::kDataFormatR16_S;
+};
+
+template <int Format>
+struct IsInt32Format {
+ STATIC_ONLY(IsInt32Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ Format == WebGLImageConversion::kDataFormatRGB32_S ||
+ Format == WebGLImageConversion::kDataFormatRG32_S ||
+ Format == WebGLImageConversion::kDataFormatR32_S;
+};
+
+template <int Format>
+struct IsUInt8Format {
+ STATIC_ONLY(IsUInt8Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA8 ||
+ Format == WebGLImageConversion::kDataFormatRGB8 ||
+ Format == WebGLImageConversion::kDataFormatRG8 ||
+ Format == WebGLImageConversion::kDataFormatR8 ||
+ Format == WebGLImageConversion::kDataFormatBGRA8 ||
+ Format == WebGLImageConversion::kDataFormatBGR8 ||
+ Format == WebGLImageConversion::kDataFormatARGB8 ||
+ Format == WebGLImageConversion::kDataFormatABGR8 ||
+ Format == WebGLImageConversion::kDataFormatRA8 ||
+ Format == WebGLImageConversion::kDataFormatAR8 ||
+ Format == WebGLImageConversion::kDataFormatA8;
+};
+
+template <int Format>
+struct IsUInt16Format {
+ STATIC_ONLY(IsUInt16Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA16 ||
+ Format == WebGLImageConversion::kDataFormatRGB16 ||
+ Format == WebGLImageConversion::kDataFormatRG16 ||
+ Format == WebGLImageConversion::kDataFormatR16;
+};
+
+template <int Format>
+struct IsUInt32Format {
+ STATIC_ONLY(IsUInt32Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA32 ||
+ Format == WebGLImageConversion::kDataFormatRGB32 ||
+ Format == WebGLImageConversion::kDataFormatRG32 ||
+ Format == WebGLImageConversion::kDataFormatR32;
+};
+
+template <int Format>
+struct IsFloatFormat {
+ STATIC_ONLY(IsFloatFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA32F ||
+ Format == WebGLImageConversion::kDataFormatRGB32F ||
+ Format == WebGLImageConversion::kDataFormatRA32F ||
+ Format == WebGLImageConversion::kDataFormatR32F ||
+ Format == WebGLImageConversion::kDataFormatA32F ||
+ Format == WebGLImageConversion::kDataFormatRG32F;
+};
+
+template <int Format>
+struct IsHalfFloatFormat {
+ STATIC_ONLY(IsHalfFloatFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA16F ||
+ Format == WebGLImageConversion::kDataFormatRGB16F ||
+ Format == WebGLImageConversion::kDataFormatRA16F ||
+ Format == WebGLImageConversion::kDataFormatR16F ||
+ Format == WebGLImageConversion::kDataFormatA16F ||
+ Format == WebGLImageConversion::kDataFormatRG16F;
+};
+
+template <int Format>
+struct Is32bppFormat {
+ STATIC_ONLY(Is32bppFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ Format == WebGLImageConversion::kDataFormatRGB5999 ||
+ Format == WebGLImageConversion::kDataFormatRGB10F11F11F;
+};
+
+template <int Format>
+struct Is16bppFormat {
+ STATIC_ONLY(Is16bppFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ Format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ Format == WebGLImageConversion::kDataFormatRGB565;
+};
+
+template <int Format,
+ bool IsInt8Format = IsInt8Format<Format>::value,
+ bool IsUInt8Format = IsUInt8Format<Format>::value,
+ bool IsInt16Format = IsInt16Format<Format>::value,
+ bool IsUInt16Format = IsUInt16Format<Format>::value,
+ bool IsInt32Format = IsInt32Format<Format>::value,
+ bool IsUInt32Format = IsUInt32Format<Format>::value,
+ bool IsFloat = IsFloatFormat<Format>::value,
+ bool IsHalfFloat = IsHalfFloatFormat<Format>::value,
+ bool Is16bpp = Is16bppFormat<Format>::value,
+ bool Is32bpp = Is32bppFormat<Format>::value>
+struct DataTypeForFormat {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef double Type; // Use a type that's not used in unpack/pack.
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int8_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint8_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int32_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint32_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef float Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint32_t Type;
+};
+
+template <int Format>
+struct UsesFloatIntermediateFormat {
+ STATIC_ONLY(UsesFloatIntermediateFormat);
+ static const bool value =
+ IsFloatFormat<Format>::value || IsHalfFloatFormat<Format>::value ||
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ Format == WebGLImageConversion::kDataFormatRGB10F11F11F ||
+ Format == WebGLImageConversion::kDataFormatRGB5999;
+};
+
+template <int Format>
+struct IntermediateFormat {
+ STATIC_ONLY(IntermediateFormat);
+ static const int value =
+ UsesFloatIntermediateFormat<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32F
+ : IsInt32Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32_S
+ : IsUInt32Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32
+ : IsInt16Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA16_S
+ : (IsUInt16Format<Format>::value ||
+ Is32bppFormat<Format>::value)
+ ? WebGLImageConversion::kDataFormatRGBA16
+ : IsInt8Format<Format>::value
+ ? WebGLImageConversion::
+ kDataFormatRGBA8_S
+ : WebGLImageConversion::
+ kDataFormatRGBA8;
+};
+
+unsigned TexelBytesForFormat(WebGLImageConversion::DataFormat format) {
+ switch (format) {
+ case WebGLImageConversion::kDataFormatR8:
+ case WebGLImageConversion::kDataFormatR8_S:
+ case WebGLImageConversion::kDataFormatA8:
+ return 1;
+ case WebGLImageConversion::kDataFormatRG8:
+ case WebGLImageConversion::kDataFormatRG8_S:
+ case WebGLImageConversion::kDataFormatRA8:
+ case WebGLImageConversion::kDataFormatAR8:
+ case WebGLImageConversion::kDataFormatRGBA5551:
+ case WebGLImageConversion::kDataFormatRGBA4444:
+ case WebGLImageConversion::kDataFormatRGB565:
+ case WebGLImageConversion::kDataFormatA16F:
+ case WebGLImageConversion::kDataFormatR16:
+ case WebGLImageConversion::kDataFormatR16_S:
+ case WebGLImageConversion::kDataFormatR16F:
+ case WebGLImageConversion::kDataFormatD16:
+ return 2;
+ case WebGLImageConversion::kDataFormatRGB8:
+ case WebGLImageConversion::kDataFormatRGB8_S:
+ case WebGLImageConversion::kDataFormatBGR8:
+ return 3;
+ case WebGLImageConversion::kDataFormatRGBA8:
+ case WebGLImageConversion::kDataFormatRGBA8_S:
+ case WebGLImageConversion::kDataFormatARGB8:
+ case WebGLImageConversion::kDataFormatABGR8:
+ case WebGLImageConversion::kDataFormatBGRA8:
+ case WebGLImageConversion::kDataFormatR32:
+ case WebGLImageConversion::kDataFormatR32_S:
+ case WebGLImageConversion::kDataFormatR32F:
+ case WebGLImageConversion::kDataFormatA32F:
+ case WebGLImageConversion::kDataFormatRA16F:
+ case WebGLImageConversion::kDataFormatRGBA2_10_10_10:
+ case WebGLImageConversion::kDataFormatRGB10F11F11F:
+ case WebGLImageConversion::kDataFormatRGB5999:
+ case WebGLImageConversion::kDataFormatRG16:
+ case WebGLImageConversion::kDataFormatRG16_S:
+ case WebGLImageConversion::kDataFormatRG16F:
+ case WebGLImageConversion::kDataFormatD32:
+ case WebGLImageConversion::kDataFormatD32F:
+ case WebGLImageConversion::kDataFormatDS24_8:
+ return 4;
+ case WebGLImageConversion::kDataFormatRGB16:
+ case WebGLImageConversion::kDataFormatRGB16_S:
+ case WebGLImageConversion::kDataFormatRGB16F:
+ return 6;
+ case WebGLImageConversion::kDataFormatRGBA16:
+ case WebGLImageConversion::kDataFormatRGBA16_S:
+ case WebGLImageConversion::kDataFormatRA32F:
+ case WebGLImageConversion::kDataFormatRGBA16F:
+ case WebGLImageConversion::kDataFormatRG32:
+ case WebGLImageConversion::kDataFormatRG32_S:
+ case WebGLImageConversion::kDataFormatRG32F:
+ return 8;
+ case WebGLImageConversion::kDataFormatRGB32:
+ case WebGLImageConversion::kDataFormatRGB32_S:
+ case WebGLImageConversion::kDataFormatRGB32F:
+ return 12;
+ case WebGLImageConversion::kDataFormatRGBA32:
+ case WebGLImageConversion::kDataFormatRGBA32_S:
+ case WebGLImageConversion::kDataFormatRGBA32F:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+/* END CODE SHARED WITH MOZILLA FIREFOX */
+
+class FormatConverter {
+ STACK_ALLOCATED();
+
+ public:
+ FormatConverter(const IntRect& source_data_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ const void* src_start,
+ void* dst_start,
+ int src_stride,
+ int src_row_offset,
+ int dst_stride)
+ : src_sub_rectangle_(source_data_sub_rectangle),
+ depth_(depth),
+ unpack_image_height_(unpack_image_height),
+ src_start_(src_start),
+ dst_start_(dst_start),
+ src_stride_(src_stride),
+ src_row_offset_(src_row_offset),
+ dst_stride_(dst_stride),
+ success_(false) {
+ const unsigned kMaxNumberOfComponents = 4;
+ const unsigned kMaxBytesPerComponent = 4;
+ unpacked_intermediate_src_data_ = std::make_unique<uint8_t[]>(
+ src_sub_rectangle_.Width() * kMaxNumberOfComponents *
+ kMaxBytesPerComponent);
+ DCHECK(unpacked_intermediate_src_data_.get());
+ }
+
+ void Convert(WebGLImageConversion::DataFormat src_format,
+ WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp);
+ bool Success() const { return success_; }
+
+ private:
+ template <WebGLImageConversion::DataFormat SrcFormat>
+ void Convert(WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp);
+
+ template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat>
+ void Convert(WebGLImageConversion::AlphaOp);
+
+ template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat,
+ WebGLImageConversion::AlphaOp alphaOp>
+ void Convert();
+
+ const IntRect& src_sub_rectangle_;
+ const int depth_;
+ const int unpack_image_height_;
+ const void* const src_start_;
+ void* const dst_start_;
+ const int src_stride_, src_row_offset_, dst_stride_;
+ bool success_;
+ std::unique_ptr<uint8_t[]> unpacked_intermediate_src_data_;
+};
+
+void FormatConverter::Convert(WebGLImageConversion::DataFormat src_format,
+ WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_SRCFORMAT(SrcFormat) \
+ case SrcFormat: \
+ return Convert<SrcFormat>(dst_format, alpha_op);
+
+ switch (src_format) {
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRA32F)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatARGB8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatABGR8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatAR8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatBGRA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA5551)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA4444)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA32F)
+ FORMATCONVERTER_CASE_SRCFORMAT(
+ WebGLImageConversion::kDataFormatRGBA2_10_10_10)
+ default:
+ NOTREACHED();
+ }
+#undef FORMATCONVERTER_CASE_SRCFORMAT
+}
+
+template <WebGLImageConversion::DataFormat SrcFormat>
+void FormatConverter::Convert(WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_DSTFORMAT(DstFormat) \
+ case DstFormat: \
+ return Convert<SrcFormat, DstFormat>(alpha_op);
+
+ switch (dst_format) {
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB565)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA5551)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA4444)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA8_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(
+ WebGLImageConversion::kDataFormatRGBA2_10_10_10)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG32F)
+ default:
+ NOTREACHED();
+ }
+
+#undef FORMATCONVERTER_CASE_DSTFORMAT
+}
+
+template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat>
+void FormatConverter::Convert(WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_ALPHAOP(alphaOp) \
+ case alphaOp: \
+ return Convert<SrcFormat, DstFormat, alphaOp>();
+
+ switch (alpha_op) {
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoNothing)
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoPremultiply)
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoUnmultiply)
+ default:
+ NOTREACHED();
+ }
+#undef FORMATCONVERTER_CASE_ALPHAOP
+}
+
+template <int Format>
+struct SupportsConversionFromDomElements {
+ STATIC_ONLY(SupportsConversionFromDomElements);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA8 ||
+ Format == WebGLImageConversion::kDataFormatRGB8 ||
+ Format == WebGLImageConversion::kDataFormatRG8 ||
+ Format == WebGLImageConversion::kDataFormatRA8 ||
+ Format == WebGLImageConversion::kDataFormatR8 ||
+ Format == WebGLImageConversion::kDataFormatRGBA32F ||
+ Format == WebGLImageConversion::kDataFormatRGB32F ||
+ Format == WebGLImageConversion::kDataFormatRG32F ||
+ Format == WebGLImageConversion::kDataFormatRA32F ||
+ Format == WebGLImageConversion::kDataFormatR32F ||
+ Format == WebGLImageConversion::kDataFormatRGBA16F ||
+ Format == WebGLImageConversion::kDataFormatRGB16F ||
+ Format == WebGLImageConversion::kDataFormatRG16F ||
+ Format == WebGLImageConversion::kDataFormatRA16F ||
+ Format == WebGLImageConversion::kDataFormatR16F ||
+ Format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ Format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ Format == WebGLImageConversion::kDataFormatRGB565 ||
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+};
+
+template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat,
+ WebGLImageConversion::AlphaOp alphaOp>
+void FormatConverter::Convert() {
+ // Many instantiations of this template function will never be entered, so we
+ // try to return immediately in these cases to avoid generating useless code.
+ if (SrcFormat == DstFormat &&
+ alphaOp == WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ return;
+ }
+ if (!IsFloatFormat<DstFormat>::value && IsFloatFormat<SrcFormat>::value) {
+ NOTREACHED();
+ return;
+ }
+
+ // Only textures uploaded from DOM elements or ImageData can allow DstFormat
+ // != SrcFormat.
+ const bool src_format_comes_from_dom_element_or_image_data =
+ WebGLImageConversion::SrcFormatComeFromDOMElementOrImageData(SrcFormat);
+ if (!src_format_comes_from_dom_element_or_image_data &&
+ SrcFormat != DstFormat) {
+ NOTREACHED();
+ return;
+ }
+ // Likewise, only textures uploaded from DOM elements or ImageData can
+ // possibly need to be unpremultiplied.
+ if (!src_format_comes_from_dom_element_or_image_data &&
+ alphaOp == WebGLImageConversion::kAlphaDoUnmultiply) {
+ NOTREACHED();
+ return;
+ }
+ if (src_format_comes_from_dom_element_or_image_data &&
+ alphaOp == WebGLImageConversion::kAlphaDoUnmultiply &&
+ !SupportsConversionFromDomElements<DstFormat>::value) {
+ NOTREACHED();
+ return;
+ }
+ if ((!HasAlpha(SrcFormat) || !HasColor(SrcFormat) || !HasColor(DstFormat)) &&
+ alphaOp != WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ return;
+ }
+ // If converting DOM element data to UNSIGNED_INT_5_9_9_9_REV or
+ // UNSIGNED_INT_10F_11F_11F_REV, we should always switch to FLOAT instead to
+ // avoid unpacking/packing these two types.
+ if (src_format_comes_from_dom_element_or_image_data &&
+ SrcFormat != DstFormat &&
+ (DstFormat == WebGLImageConversion::kDataFormatRGB5999 ||
+ DstFormat == WebGLImageConversion::kDataFormatRGB10F11F11F)) {
+ NOTREACHED();
+ return;
+ }
+
+ typedef typename DataTypeForFormat<SrcFormat>::Type SrcType;
+ typedef typename DataTypeForFormat<DstFormat>::Type DstType;
+ const int kIntermFormat = IntermediateFormat<DstFormat>::value;
+ typedef typename DataTypeForFormat<kIntermFormat>::Type IntermType;
+ const ptrdiff_t src_stride_in_elements = src_stride_ / sizeof(SrcType);
+ const ptrdiff_t dst_stride_in_elements = dst_stride_ / sizeof(DstType);
+ const bool kTrivialUnpack = SrcFormat == kIntermFormat;
+ const bool kTrivialPack = DstFormat == kIntermFormat &&
+ alphaOp == WebGLImageConversion::kAlphaDoNothing;
+ DCHECK(!kTrivialUnpack || !kTrivialPack);
+
+ const SrcType* src_row_start =
+ static_cast<const SrcType*>(static_cast<const void*>(
+ static_cast<const uint8_t*>(src_start_) +
+ ((src_stride_ * src_sub_rectangle_.Y()) + src_row_offset_)));
+
+ // If packing multiple images into a 3D texture, and flipY is true,
+ // then the sub-rectangle is pointing at the start of the
+ // "bottommost" of those images. Since the source pointer strides in
+ // the positive direction, we need to back it up to point at the
+ // last, or "topmost", of these images.
+ if (dst_stride_ < 0 && depth_ > 1) {
+ src_row_start -=
+ (depth_ - 1) * src_stride_in_elements * unpack_image_height_;
+ }
+
+ DstType* dst_row_start = static_cast<DstType*>(dst_start_);
+ if (kTrivialUnpack) {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Pack<DstFormat, alphaOp>(src_row_start, dst_row_start,
+ src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ } else if (kTrivialPack) {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Unpack<SrcFormat>(src_row_start, dst_row_start,
+ src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ } else {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Unpack<SrcFormat>(src_row_start,
+ reinterpret_cast<IntermType*>(
+ unpacked_intermediate_src_data_.get()),
+ src_sub_rectangle_.Width());
+ Pack<DstFormat, alphaOp>(reinterpret_cast<IntermType*>(
+ unpacked_intermediate_src_data_.get()),
+ dst_row_start, src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ }
+ success_ = true;
+ return;
+}
+
+bool FrameIsValid(const SkBitmap& frame_bitmap) {
+ return !frame_bitmap.isNull() && !frame_bitmap.empty() &&
+ frame_bitmap.colorType() == kN32_SkColorType;
+}
+
+} // anonymous namespace
+
+WebGLImageConversion::PixelStoreParams::PixelStoreParams()
+ : alignment(4),
+ row_length(0),
+ image_height(0),
+ skip_pixels(0),
+ skip_rows(0),
+ skip_images(0) {}
+
+bool WebGLImageConversion::ComputeFormatAndTypeParameters(
+ GLenum format,
+ GLenum type,
+ unsigned* components_per_pixel,
+ unsigned* bytes_per_component) {
+ switch (format) {
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_RED:
+ case GL_RED_INTEGER:
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL: // Treat it as one component.
+ *components_per_pixel = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_RG:
+ case GL_RG_INTEGER:
+ *components_per_pixel = 2;
+ break;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_SRGB_EXT: // GL_EXT_sRGB
+ *components_per_pixel = 3;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_BGRA_EXT: // GL_EXT_texture_format_BGRA8888
+ case GL_SRGB_ALPHA_EXT: // GL_EXT_sRGB
+ *components_per_pixel = 4;
+ break;
+ default:
+ return false;
+ }
+ switch (type) {
+ case GL_BYTE:
+ *bytes_per_component = sizeof(GLbyte);
+ break;
+ case GL_UNSIGNED_BYTE:
+ *bytes_per_component = sizeof(GLubyte);
+ break;
+ case GL_SHORT:
+ *bytes_per_component = sizeof(GLshort);
+ break;
+ case GL_UNSIGNED_SHORT:
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ *components_per_pixel = 1;
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ case GL_INT:
+ *bytes_per_component = sizeof(GLint);
+ break;
+ case GL_UNSIGNED_INT:
+ *bytes_per_component = sizeof(GLuint);
+ break;
+ case GL_UNSIGNED_INT_24_8_OES:
+ case GL_UNSIGNED_INT_10F_11F_11F_REV:
+ case GL_UNSIGNED_INT_5_9_9_9_REV:
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ *components_per_pixel = 1;
+ *bytes_per_component = sizeof(GLuint);
+ break;
+ case GL_FLOAT: // OES_texture_float
+ *bytes_per_component = sizeof(GLfloat);
+ break;
+ case GL_HALF_FLOAT:
+ case GL_HALF_FLOAT_OES: // OES_texture_half_float
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+GLenum WebGLImageConversion::ComputeImageSizeInBytes(
+ GLenum format,
+ GLenum type,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ const PixelStoreParams& params,
+ unsigned* image_size_in_bytes,
+ unsigned* padding_in_bytes,
+ unsigned* skip_size_in_bytes) {
+ DCHECK(image_size_in_bytes);
+ DCHECK(params.alignment == 1 || params.alignment == 2 ||
+ params.alignment == 4 || params.alignment == 8);
+ DCHECK_GE(params.row_length, 0);
+ DCHECK_GE(params.image_height, 0);
+ DCHECK_GE(params.skip_pixels, 0);
+ DCHECK_GE(params.skip_rows, 0);
+ DCHECK_GE(params.skip_images, 0);
+ if (width < 0 || height < 0 || depth < 0)
+ return GL_INVALID_VALUE;
+ if (!width || !height || !depth) {
+ *image_size_in_bytes = 0;
+ if (padding_in_bytes)
+ *padding_in_bytes = 0;
+ if (skip_size_in_bytes)
+ *skip_size_in_bytes = 0;
+ return GL_NO_ERROR;
+ }
+
+ int row_length = params.row_length > 0 ? params.row_length : width;
+ int image_height = params.image_height > 0 ? params.image_height : height;
+
+ unsigned bytes_per_component, components_per_pixel;
+ if (!ComputeFormatAndTypeParameters(format, type, &bytes_per_component,
+ &components_per_pixel))
+ return GL_INVALID_ENUM;
+ unsigned bytes_per_group = bytes_per_component * components_per_pixel;
+ CheckedNumeric<uint32_t> checked_value = static_cast<uint32_t>(row_length);
+ checked_value *= bytes_per_group;
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+
+ unsigned last_row_size;
+ if (params.row_length > 0 && params.row_length != width) {
+ CheckedNumeric<uint32_t> tmp = width;
+ tmp *= bytes_per_group;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ last_row_size = tmp.ValueOrDie();
+ } else {
+ last_row_size = checked_value.ValueOrDie();
+ }
+
+ unsigned padding = 0;
+ CheckedNumeric<uint32_t> checked_residual = checked_value % params.alignment;
+ if (!checked_residual.IsValid()) {
+ return GL_INVALID_VALUE;
+ }
+ unsigned residual = checked_residual.ValueOrDie();
+ if (residual) {
+ padding = params.alignment - residual;
+ checked_value += padding;
+ }
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ unsigned padded_row_size = checked_value.ValueOrDie();
+
+ CheckedNumeric<uint32_t> rows = image_height;
+ rows *= (depth - 1);
+ // Last image is not affected by IMAGE_HEIGHT parameter.
+ rows += height;
+ if (!rows.IsValid())
+ return GL_INVALID_VALUE;
+ checked_value *= (rows - 1);
+ // Last row is not affected by ROW_LENGTH parameter.
+ checked_value += last_row_size;
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ *image_size_in_bytes = checked_value.ValueOrDie();
+ if (padding_in_bytes)
+ *padding_in_bytes = padding;
+
+ CheckedNumeric<uint32_t> skip_size = 0;
+ if (params.skip_images > 0) {
+ CheckedNumeric<uint32_t> tmp = padded_row_size;
+ tmp *= image_height;
+ tmp *= params.skip_images;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (params.skip_rows > 0) {
+ CheckedNumeric<uint32_t> tmp = padded_row_size;
+ tmp *= params.skip_rows;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (params.skip_pixels > 0) {
+ CheckedNumeric<uint32_t> tmp = bytes_per_group;
+ tmp *= params.skip_pixels;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (!skip_size.IsValid())
+ return GL_INVALID_VALUE;
+ if (skip_size_in_bytes)
+ *skip_size_in_bytes = skip_size.ValueOrDie();
+
+ checked_value += skip_size.ValueOrDie();
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ return GL_NO_ERROR;
+}
+
+WebGLImageConversion::ImageExtractor::ImageExtractor(
+ Image* image,
+ ImageHtmlDomSource image_html_dom_source,
+ bool premultiply_alpha,
+ bool ignore_color_space) {
+ image_ = image;
+ image_html_dom_source_ = image_html_dom_source;
+ ExtractImage(premultiply_alpha, ignore_color_space);
+}
+
+void WebGLImageConversion::ImageExtractor::ExtractImage(
+ bool premultiply_alpha,
+ bool ignore_color_space) {
+ DCHECK(!image_pixel_locker_);
+
+ if (!image_)
+ return;
+
+ sk_sp<SkImage> skia_image = image_->PaintImageForCurrentFrame().GetSkImage();
+ SkImageInfo info =
+ skia_image ? SkImageInfo::MakeN32Premul(image_->width(), image_->height())
+ : SkImageInfo::MakeUnknown();
+ alpha_op_ = kAlphaDoNothing;
+ bool has_alpha = skia_image ? !skia_image->isOpaque() : true;
+
+ if ((!skia_image || ignore_color_space ||
+ (has_alpha && !premultiply_alpha)) &&
+ image_->Data()) {
+ // Attempt to get raw unpremultiplied image data.
+ std::unique_ptr<ImageDecoder> decoder(ImageDecoder::Create(
+ image_->Data(), true, ImageDecoder::kAlphaNotPremultiplied,
+ ignore_color_space ? ColorBehavior::Ignore()
+ : ColorBehavior::TransformToSRGB()));
+ if (!decoder || !decoder->FrameCount())
+ return;
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ if (!frame || frame->GetStatus() != ImageFrame::kFrameComplete)
+ return;
+ has_alpha = frame->HasAlpha();
+ SkBitmap bitmap = frame->Bitmap();
+ if (!FrameIsValid(bitmap))
+ return;
+
+ // TODO(fmalita): Partial frames are not supported currently: only fully
+ // decoded frames make it through. We could potentially relax this and
+ // use SkImage::MakeFromBitmap(bitmap) to make a copy.
+ skia_image = frame->FinalizePixelsAndGetImage();
+ info = bitmap.info();
+
+ if (has_alpha && premultiply_alpha)
+ alpha_op_ = kAlphaDoPremultiply;
+ } else if (!premultiply_alpha && has_alpha) {
+ // 1. For texImage2D with HTMLVideoElment input, assume no PremultiplyAlpha
+ // had been applied and the alpha value for each pixel is 0xFF. This is
+ // true at present; if it is changed in the future it will need
+ // adjustment accordingly.
+ // 2. For texImage2D with HTMLCanvasElement input in which alpha is already
+ // premultiplied in this port, do AlphaDoUnmultiply if
+ // UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to false.
+ if (image_html_dom_source_ != kHtmlDomVideo)
+ alpha_op_ = kAlphaDoUnmultiply;
+ }
+
+ if (!skia_image)
+ return;
+
+ image_source_format_ = SK_B32_SHIFT ? kDataFormatRGBA8 : kDataFormatBGRA8;
+ image_source_unpack_alignment_ =
+ 0; // FIXME: this seems to always be zero - why use at all?
+
+ DCHECK(skia_image->width());
+ DCHECK(skia_image->height());
+ image_width_ = skia_image->width();
+ image_height_ = skia_image->height();
+
+ // Fail if the image was downsampled because of memory limits.
+ if (image_width_ != (unsigned)image_->width() ||
+ image_height_ != (unsigned)image_->height())
+ return;
+
+ image_pixel_locker_.emplace(std::move(skia_image), info.alphaType(),
+ kN32_SkColorType);
+}
+
+unsigned WebGLImageConversion::GetChannelBitsByFormat(GLenum format) {
+ switch (format) {
+ case GL_ALPHA:
+ return kChannelAlpha;
+ case GL_RED:
+ case GL_RED_INTEGER:
+ case GL_R8:
+ case GL_R8_SNORM:
+ case GL_R8UI:
+ case GL_R8I:
+ case GL_R16UI:
+ case GL_R16I:
+ case GL_R32UI:
+ case GL_R32I:
+ case GL_R16F:
+ case GL_R32F:
+ return kChannelRed;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ case GL_RG8:
+ case GL_RG8_SNORM:
+ case GL_RG8UI:
+ case GL_RG8I:
+ case GL_RG16UI:
+ case GL_RG16I:
+ case GL_RG32UI:
+ case GL_RG32I:
+ case GL_RG16F:
+ case GL_RG32F:
+ return kChannelRG;
+ case GL_LUMINANCE:
+ return kChannelRGB;
+ case GL_LUMINANCE_ALPHA:
+ return kChannelRGBA;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_RGB8:
+ case GL_RGB8_SNORM:
+ case GL_RGB8UI:
+ case GL_RGB8I:
+ case GL_RGB16UI:
+ case GL_RGB16I:
+ case GL_RGB32UI:
+ case GL_RGB32I:
+ case GL_RGB16F:
+ case GL_RGB32F:
+ case GL_RGB565:
+ case GL_R11F_G11F_B10F:
+ case GL_RGB9_E5:
+ case GL_SRGB_EXT:
+ case GL_SRGB8:
+ return kChannelRGB;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_RGBA8:
+ case GL_RGBA8_SNORM:
+ case GL_RGBA8UI:
+ case GL_RGBA8I:
+ case GL_RGBA16UI:
+ case GL_RGBA16I:
+ case GL_RGBA32UI:
+ case GL_RGBA32I:
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ case GL_RGBA4:
+ case GL_RGB5_A1:
+ case GL_RGB10_A2:
+ case GL_RGB10_A2UI:
+ case GL_SRGB_ALPHA_EXT:
+ case GL_SRGB8_ALPHA8:
+ return kChannelRGBA;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT24:
+ case GL_DEPTH_COMPONENT32F:
+ return kChannelDepth;
+ case GL_STENCIL:
+ case GL_STENCIL_INDEX8:
+ return kChannelStencil;
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH24_STENCIL8:
+ case GL_DEPTH32F_STENCIL8:
+ return kChannelDepthStencil;
+ default:
+ return 0;
+ }
+}
+
+bool WebGLImageConversion::PackImageData(
+ Image* image,
+ const void* pixels,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ AlphaOp alpha_op,
+ DataFormat source_format,
+ unsigned source_image_width,
+ unsigned source_image_height,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ Vector<uint8_t>& data) {
+ if (!pixels)
+ return false;
+
+ unsigned packed_size;
+ // Output data is tightly packed (alignment == 1).
+ PixelStoreParams params;
+ params.alignment = 1;
+ if (ComputeImageSizeInBytes(format, type, source_image_sub_rectangle.Width(),
+ source_image_sub_rectangle.Height(), depth,
+ params, &packed_size, nullptr,
+ nullptr) != GL_NO_ERROR)
+ return false;
+ data.resize(packed_size);
+
+ return PackPixels(reinterpret_cast<const uint8_t*>(pixels), source_format,
+ source_image_width, source_image_height,
+ source_image_sub_rectangle, depth, source_unpack_alignment,
+ unpack_image_height, format, type, alpha_op, data.data(),
+ flip_y);
+}
+
+bool WebGLImageConversion::ExtractImageData(
+ const uint8_t* image_data,
+ DataFormat source_data_format,
+ const IntSize& image_data_size,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ bool premultiply_alpha,
+ Vector<uint8_t>& data) {
+ if (!image_data)
+ return false;
+ int width = image_data_size.Width();
+ int height = image_data_size.Height();
+
+ unsigned packed_size;
+ // Output data is tightly packed (alignment == 1).
+ PixelStoreParams params;
+ params.alignment = 1;
+ if (ComputeImageSizeInBytes(format, type, source_image_sub_rectangle.Width(),
+ source_image_sub_rectangle.Height(), depth,
+ params, &packed_size, nullptr,
+ nullptr) != GL_NO_ERROR)
+ return false;
+ data.resize(packed_size);
+
+ if (!PackPixels(image_data, source_data_format, width, height,
+ source_image_sub_rectangle, depth, 0, unpack_image_height,
+ format, type,
+ premultiply_alpha ? kAlphaDoPremultiply : kAlphaDoNothing,
+ data.data(), flip_y))
+ return false;
+
+ return true;
+}
+
+bool WebGLImageConversion::ExtractTextureData(
+ unsigned width,
+ unsigned height,
+ GLenum format,
+ GLenum type,
+ const PixelStoreParams& unpack_params,
+ bool flip_y,
+ bool premultiply_alpha,
+ const void* pixels,
+ Vector<uint8_t>& data) {
+ // Assumes format, type, etc. have already been validated.
+ DataFormat source_data_format = GetDataFormat(format, type);
+ if (source_data_format == kDataFormatNumFormats)
+ return false;
+
+ // Resize the output buffer.
+ unsigned int components_per_pixel, bytes_per_component;
+ if (!ComputeFormatAndTypeParameters(format, type, &components_per_pixel,
+ &bytes_per_component))
+ return false;
+ unsigned bytes_per_pixel = components_per_pixel * bytes_per_component;
+ data.resize(width * height * bytes_per_pixel);
+
+ unsigned image_size_in_bytes, skip_size_in_bytes;
+ ComputeImageSizeInBytes(format, type, width, height, 1, unpack_params,
+ &image_size_in_bytes, nullptr, &skip_size_in_bytes);
+ const uint8_t* src_data = static_cast<const uint8_t*>(pixels);
+ if (skip_size_in_bytes) {
+ src_data += skip_size_in_bytes;
+ }
+
+ if (!PackPixels(src_data, source_data_format,
+ unpack_params.row_length ? unpack_params.row_length : width,
+ height, IntRect(0, 0, width, height), 1,
+ unpack_params.alignment, 0, format, type,
+ (premultiply_alpha ? kAlphaDoPremultiply : kAlphaDoNothing),
+ data.data(), flip_y))
+ return false;
+
+ return true;
+}
+
+bool WebGLImageConversion::PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned source_data_width,
+ unsigned source_data_height,
+ const IntRect& source_data_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ unsigned destination_format,
+ unsigned destination_type,
+ AlphaOp alpha_op,
+ void* destination_data,
+ bool flip_y) {
+ DCHECK_GE(depth, 1);
+ if (unpack_image_height == 0) {
+ unpack_image_height = source_data_sub_rectangle.Height();
+ }
+ int valid_src = source_data_width * TexelBytesForFormat(source_data_format);
+ int remainder =
+ source_unpack_alignment ? (valid_src % source_unpack_alignment) : 0;
+ int src_stride =
+ remainder ? (valid_src + source_unpack_alignment - remainder) : valid_src;
+ int src_row_offset =
+ source_data_sub_rectangle.X() * TexelBytesForFormat(source_data_format);
+
+ DataFormat dst_data_format =
+ GetDataFormat(destination_format, destination_type);
+ if (dst_data_format == kDataFormatNumFormats)
+ return false;
+ int dst_stride =
+ source_data_sub_rectangle.Width() * TexelBytesForFormat(dst_data_format);
+ if (flip_y) {
+ destination_data =
+ static_cast<uint8_t*>(destination_data) +
+ dst_stride * ((depth * source_data_sub_rectangle.Height()) - 1);
+ dst_stride = -dst_stride;
+ }
+ if (!HasAlpha(source_data_format) || !HasColor(source_data_format) ||
+ !HasColor(dst_data_format))
+ alpha_op = kAlphaDoNothing;
+
+ if (source_data_format == dst_data_format && alpha_op == kAlphaDoNothing) {
+ const uint8_t* base_ptr =
+ source_data + src_stride * source_data_sub_rectangle.Y();
+ const uint8_t* base_end =
+ source_data + src_stride * source_data_sub_rectangle.MaxY();
+
+ // If packing multiple images into a 3D texture, and flipY is true,
+ // then the sub-rectangle is pointing at the start of the
+ // "bottommost" of those images. Since the source pointer strides in
+ // the positive direction, we need to back it up to point at the
+ // last, or "topmost", of these images.
+ if (flip_y && depth > 1) {
+ const ptrdiff_t distance_to_top_image =
+ (depth - 1) * src_stride * unpack_image_height;
+ base_ptr -= distance_to_top_image;
+ base_end -= distance_to_top_image;
+ }
+
+ unsigned row_size = (dst_stride > 0) ? dst_stride : -dst_stride;
+ uint8_t* dst = static_cast<uint8_t*>(destination_data);
+
+ for (int i = 0; i < depth; ++i) {
+ const uint8_t* ptr = base_ptr;
+ const uint8_t* ptr_end = base_end;
+ while (ptr < ptr_end) {
+ memcpy(dst, ptr + src_row_offset, row_size);
+ ptr += src_stride;
+ dst += dst_stride;
+ }
+ base_ptr += unpack_image_height * src_stride;
+ base_end += unpack_image_height * src_stride;
+ }
+ return true;
+ }
+
+ FormatConverter converter(source_data_sub_rectangle, depth,
+ unpack_image_height, source_data, destination_data,
+ src_stride, src_row_offset, dst_stride);
+ converter.Convert(source_data_format, dst_data_format, alpha_op);
+ if (!converter.Success())
+ return false;
+ return true;
+}
+
+void WebGLImageConversion::UnpackPixels(const uint16_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ switch (source_data_format) {
+ case kDataFormatRGBA4444: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA4444>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Unpack<WebGLImageConversion::kDataFormatRGBA4444>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA5551: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA5551>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Unpack<WebGLImageConversion::kDataFormatRGBA5551>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatBGRA8: {
+ const uint8_t* psrc = (const uint8_t*)source_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatBGRA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(psrc);
+ Unpack<WebGLImageConversion::kDataFormatBGRA8>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ default:
+ break;
+ }
+}
+
+void WebGLImageConversion::PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ switch (source_data_format) {
+ case kDataFormatRA8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatR8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA4444: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA4444>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ case kDataFormatRGBA5551: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA5551>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ case kDataFormatRGB565: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGB565>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ default:
+ break;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
new file mode 100644
index 00000000000..ad66c2f7ca1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
@@ -0,0 +1,293 @@
+// Copyright 2014 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/khronos/GLES3/gl3.h"
+
+namespace blink {
+class Image;
+class IntSize;
+
+// Helper functions for texture uploading and pixel readback.
+class PLATFORM_EXPORT WebGLImageConversion final {
+ STATIC_ONLY(WebGLImageConversion);
+
+ public:
+ // Attempt to enumerate all possible native image formats to
+ // reduce the amount of temporary allocations during texture
+ // uploading. This enum must be public because it is accessed
+ // by non-member functions.
+ // "_S" postfix indicates signed type.
+ enum DataFormat {
+ kDataFormatRGBA8 = 0,
+ kDataFormatRGBA8_S,
+ kDataFormatRGBA16,
+ kDataFormatRGBA16_S,
+ kDataFormatRGBA32,
+ kDataFormatRGBA32_S,
+ kDataFormatRGBA16F,
+ kDataFormatRGBA32F,
+ kDataFormatRGBA2_10_10_10,
+ kDataFormatRGB8,
+ kDataFormatRGB8_S,
+ kDataFormatRGB16,
+ kDataFormatRGB16_S,
+ kDataFormatRGB32,
+ kDataFormatRGB32_S,
+ kDataFormatRGB16F,
+ kDataFormatRGB32F,
+ kDataFormatBGR8,
+ kDataFormatBGRA8,
+ kDataFormatARGB8,
+ kDataFormatABGR8,
+ kDataFormatRGBA5551,
+ kDataFormatRGBA4444,
+ kDataFormatRGB565,
+ kDataFormatRGB10F11F11F,
+ kDataFormatRGB5999,
+ kDataFormatRG8,
+ kDataFormatRG8_S,
+ kDataFormatRG16,
+ kDataFormatRG16_S,
+ kDataFormatRG32,
+ kDataFormatRG32_S,
+ kDataFormatRG16F,
+ kDataFormatRG32F,
+ kDataFormatR8,
+ kDataFormatR8_S,
+ kDataFormatR16,
+ kDataFormatR16_S,
+ kDataFormatR32,
+ kDataFormatR32_S,
+ kDataFormatR16F,
+ kDataFormatR32F,
+ kDataFormatRA8,
+ kDataFormatRA16F,
+ kDataFormatRA32F,
+ kDataFormatAR8,
+ kDataFormatA8,
+ kDataFormatA16F,
+ kDataFormatA32F,
+ kDataFormatD16,
+ kDataFormatD32,
+ kDataFormatD32F,
+ kDataFormatDS24_8,
+ kDataFormatNumFormats
+ };
+
+ enum ChannelBits {
+ kChannelRed = 1,
+ kChannelGreen = 2,
+ kChannelBlue = 4,
+ kChannelAlpha = 8,
+ kChannelDepth = 16,
+ kChannelStencil = 32,
+ kChannelRG = kChannelRed | kChannelGreen,
+ kChannelRGB = kChannelRed | kChannelGreen | kChannelBlue,
+ kChannelRGBA = kChannelRGB | kChannelAlpha,
+ kChannelDepthStencil = kChannelDepth | kChannelStencil,
+ };
+
+ // Possible alpha operations that may need to occur during
+ // pixel packing. FIXME: kAlphaDoUnmultiply is lossy and must
+ // be removed.
+ enum AlphaOp {
+ kAlphaDoNothing = 0,
+ kAlphaDoPremultiply = 1,
+ kAlphaDoUnmultiply = 2
+ };
+
+ enum ImageHtmlDomSource {
+ kHtmlDomImage = 0,
+ kHtmlDomCanvas = 1,
+ kHtmlDomVideo = 2,
+ kHtmlDomNone = 3
+ };
+
+ struct PLATFORM_EXPORT PixelStoreParams final {
+ PixelStoreParams();
+
+ GLint alignment;
+ GLint row_length;
+ GLint image_height;
+ GLint skip_pixels;
+ GLint skip_rows;
+ GLint skip_images;
+ };
+
+ class PLATFORM_EXPORT ImageExtractor final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(ImageExtractor);
+
+ public:
+ ImageExtractor(Image*,
+ ImageHtmlDomSource,
+ bool premultiply_alpha,
+ bool ignore_color_space);
+
+ const void* ImagePixelData() {
+ return image_pixel_locker_ ? image_pixel_locker_->Pixels() : nullptr;
+ }
+ unsigned ImageWidth() { return image_width_; }
+ unsigned ImageHeight() { return image_height_; }
+ DataFormat ImageSourceFormat() { return image_source_format_; }
+ AlphaOp ImageAlphaOp() { return alpha_op_; }
+ unsigned ImageSourceUnpackAlignment() {
+ return image_source_unpack_alignment_;
+ }
+
+ private:
+ // Extracts the image and keeps track of its status, such as width, height,
+ // Source Alignment, format, AlphaOp, etc. This needs to lock the resources
+ // or relevant data if needed.
+ void ExtractImage(bool premultiply_alpha, bool ignore_color_space);
+
+ Image* image_;
+ Optional<ImagePixelLocker> image_pixel_locker_;
+ ImageHtmlDomSource image_html_dom_source_;
+ unsigned image_width_;
+ unsigned image_height_;
+ DataFormat image_source_format_;
+ AlphaOp alpha_op_;
+ unsigned image_source_unpack_alignment_;
+ };
+
+ // Computes the components per pixel and bytes per component
+ // for the given format and type combination. Returns false if
+ // either was an invalid enum.
+ static bool ComputeFormatAndTypeParameters(GLenum format,
+ GLenum type,
+ unsigned* components_per_pixel,
+ unsigned* bytes_per_component);
+
+ // Computes the image size in bytes. If paddingInBytes is not null, padding
+ // is also calculated in return. Returns NO_ERROR if succeed, otherwise
+ // return the suggested GL error indicating the cause of the failure:
+ // INVALID_VALUE if width/height/depth is negative or overflow happens.
+ // INVALID_ENUM if format/type is illegal.
+ // Note that imageSizeBytes does not include skipSizeInBytes, but it is
+ // guaranteed if NO_ERROR is returned, adding the two sizes won't cause
+ // overflow.
+ // |paddingInBytes| and |skipSizeInBytes| are optional and can be null, but
+ // the overflow validation is still performed.
+ static GLenum ComputeImageSizeInBytes(GLenum format,
+ GLenum type,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ const PixelStoreParams&,
+ unsigned* image_size_in_bytes,
+ unsigned* padding_in_bytes,
+ unsigned* skip_size_in_bytes);
+
+ // Check if the format is one of the formats from the ImageData or DOM
+ // elements. The format from ImageData is always RGBA8. The formats from DOM
+ // elements vary with Graphics ports, but can only be RGBA8 or BGRA8.
+ static ALWAYS_INLINE bool SrcFormatComeFromDOMElementOrImageData(
+ DataFormat src_format) {
+ return src_format == kDataFormatBGRA8 || src_format == kDataFormatRGBA8;
+ }
+
+ // The input can be either format or internalformat.
+ static unsigned GetChannelBitsByFormat(GLenum);
+
+ // The Following functions are implemented in
+ // GraphicsContext3DImagePacking.cpp.
+
+ // Packs the contents of the given Image, which is passed in |pixels|, into
+ // the passed Vector according to the given format and type, and obeying the
+ // flipY and AlphaOp flags. Returns true upon success.
+ static bool PackImageData(Image*,
+ const void* pixels,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ AlphaOp,
+ DataFormat source_format,
+ unsigned source_image_width,
+ unsigned source_image_height,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ Vector<uint8_t>& data);
+
+ // Extracts the contents of the given ImageData into the passed Vector,
+ // packing the pixel data according to the given format and type,
+ // and obeying the flipY and premultiplyAlpha flags. Returns true
+ // upon success.
+ static bool ExtractImageData(const uint8_t* image_data,
+ DataFormat source_data_format,
+ const IntSize& image_data_size,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ bool premultiply_alpha,
+ Vector<uint8_t>& data);
+
+ // Helper function which extracts the user-supplied texture
+ // data, applying the flipY and premultiplyAlpha parameters.
+ // If the data is not tightly packed according to the passed
+ // unpackAlignment, the output data will be tightly packed.
+ // Returns true if successful, false if any error occurred.
+ static bool ExtractTextureData(unsigned width,
+ unsigned height,
+ GLenum format,
+ GLenum type,
+ const PixelStoreParams& unpack_params,
+ bool flip_y,
+ bool premultiply_alpha,
+ const void* pixels,
+ Vector<uint8_t>& data);
+
+ // End GraphicsContext3DImagePacking.cpp functions
+
+ private:
+ friend class WebGLImageConversionTest;
+ // Helper for packImageData/extractImageData/extractTextureData, which
+ // implement packing of pixel data into the specified OpenGL destination
+ // format and type. A sourceUnpackAlignment of zero indicates that the source
+ // data is tightly packed. Non-zero values may take a slow path. Destination
+ // data will have no gaps between rows. Implemented in
+ // GraphicsContext3DImagePacking.cpp.
+ static bool PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned source_data_width,
+ unsigned source_data_height,
+ const IntRect& source_data_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ unsigned destination_format,
+ unsigned destination_type,
+ AlphaOp,
+ void* destination_data,
+ bool flip_y);
+ static void UnpackPixels(const uint16_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data);
+ static void PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc
new file mode 100644
index 00000000000..e3156158588
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 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 "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class WebGLImageConversionTest : public testing::Test {
+ protected:
+ void UnpackPixels(const uint16_t* source_data,
+ WebGLImageConversion::DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ WebGLImageConversion::UnpackPixels(source_data, source_data_format,
+ pixels_per_row, destination_data);
+ }
+ void PackPixels(const uint8_t* source_data,
+ WebGLImageConversion::DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ WebGLImageConversion::PackPixels(source_data, source_data_format,
+ pixels_per_row, destination_data);
+ }
+};
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA4444toRGBA8) {
+ uint16_t source_data[9] = {0x1234, 0x3456, 0x1234, 0x3456, 0x1234,
+ 0x3456, 0x1234, 0x3456, 0x1234};
+ uint8_t expected_data[36] = {
+ 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44,
+ 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66,
+ 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44};
+ uint8_t destination_data[36];
+ UnpackPixels(source_data, WebGLImageConversion::kDataFormatRGBA4444, 9,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA5551toRGBA8) {
+ uint16_t source_data[9] = {0x1234, 0x3456, 0x1234, 0x3456, 0x1234,
+ 0x3456, 0x1234, 0x3456, 0x1234};
+ uint8_t expected_data[36] = {
+ 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0,
+ 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0,
+ 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0};
+ uint8_t destination_data[36];
+ UnpackPixels(source_data, WebGLImageConversion::kDataFormatRGBA5551, 9,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toRA8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[20] = {0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56, 0x9a,
+ 0x56, 0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56,
+ 0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56};
+ uint8_t destination_data[20];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRA8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, convertBGRA8toRGBA8) {
+ uint32_t source_data[9] = {0x12345678, 0x34567888, 0x12345678,
+ 0x34567888, 0x12345678, 0x34567888,
+ 0x12345678, 0x34567888, 0x12345678};
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ uint32_t expectedData[9] = {0x56341278, 0x78563488, 0x56341278,
+ 0x78563488, 0x56341278, 0x78563488,
+ 0x56341278, 0x78563488, 0x56341278};
+#else
+ uint32_t expected_data[9] = {0x12785634, 0x34887856, 0x12785634,
+ 0x34887856, 0x12785634, 0x34887856,
+ 0x12785634, 0x34887856, 0x12785634};
+#endif
+ uint32_t destination_data[9];
+ UnpackPixels(reinterpret_cast<uint16_t*>(&source_data[0]),
+ WebGLImageConversion::kDataFormatBGRA8, 9,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toR8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[10] = {0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a};
+ uint8_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatR8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toRGBA8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[40] = {0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56};
+ uint8_t destination_data[40];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToUnsignedShort4444) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x3535, 0x3535, 0x3535, 0x3535, 0x3535,
+ 0x3535, 0x3535, 0x3535, 0x3535, 0x3535};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA4444, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToRGBA5551) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x328c, 0x328c, 0x328c, 0x328c, 0x328c,
+ 0x328c, 0x328c, 0x328c, 0x328c, 0x328c};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA5551, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToRGB565) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x32a6, 0x32a6, 0x32a6, 0x32a6, 0x32a6,
+ 0x32a6, 0x32a6, 0x32a6, 0x32a6, 0x32a6};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGB565, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
new file mode 100644
index 00000000000..8956cfa76cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -0,0 +1,266 @@
+// Copyright 2017 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 "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h"
+
+#include "build/build_config.h"
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "ui/gfx/gpu_fence.h"
+
+namespace blink {
+
+XRFrameTransport::XRFrameTransport() : submit_frame_client_binding_(this) {}
+
+XRFrameTransport::~XRFrameTransport() {
+ CallPreviousFrameCallback();
+}
+
+void XRFrameTransport::PresentChange() {
+ frame_copier_ = nullptr;
+}
+
+void XRFrameTransport::SetTransportOptions(
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr transport_options) {
+ transport_options_ = std::move(transport_options);
+}
+
+device::mojom::blink::VRSubmitFrameClientPtr
+XRFrameTransport::GetSubmitFrameClient() {
+ device::mojom::blink::VRSubmitFrameClientPtr submit_frame_client;
+ submit_frame_client_binding_.Close();
+ submit_frame_client_binding_.Bind(mojo::MakeRequest(&submit_frame_client));
+ return submit_frame_client;
+}
+
+bool XRFrameTransport::DrawingIntoSharedBuffer() {
+ switch (transport_options_->transport_method) {
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_TEXTURE_HANDLE:
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_MAILBOX_HOLDER:
+ return false;
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ DRAW_INTO_TEXTURE_MAILBOX:
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+void XRFrameTransport::FramePreImage(gpu::gles2::GLES2Interface* gl) {
+ frame_wait_time_ = WTF::TimeDelta();
+
+ // If we're expecting a fence for the previous frame and it hasn't arrived
+ // yet, wait for it to be received.
+ if (waiting_for_previous_frame_fence_) {
+ frame_wait_time_ += WaitForGpuFenceReceived();
+ }
+ // If we have a GpuFence (it may be missing if WaitForIncomingMethodCall
+ // failed), send it to the GPU service process and ask it to do an
+ // asynchronous server wait.
+ if (previous_frame_fence_) {
+ DVLOG(3) << "CreateClientGpuFenceCHROMIUM";
+ GLuint id = gl->CreateClientGpuFenceCHROMIUM(
+ previous_frame_fence_->AsClientGpuFence());
+ gl->WaitGpuFenceCHROMIUM(id);
+ gl->DestroyGpuFenceCHROMIUM(id);
+ previous_frame_fence_.reset();
+ }
+}
+
+void XRFrameTransport::CallPreviousFrameCallback() {
+ if (previous_image_release_callback_) {
+ previous_image_release_callback_->Run(gpu::SyncToken(), false);
+ previous_image_release_callback_ = nullptr;
+ }
+}
+
+void XRFrameTransport::FrameSubmitMissing(
+ device::mojom::blink::VRPresentationProvider* vr_presentation_provider,
+ gpu::gles2::GLES2Interface* gl,
+ int16_t vr_frame_id) {
+ TRACE_EVENT0("gpu", __FUNCTION__);
+ gpu::SyncToken sync_token;
+ gl->GenSyncTokenCHROMIUM(sync_token.GetData());
+ vr_presentation_provider->SubmitFrameMissing(vr_frame_id, sync_token);
+}
+
+void XRFrameTransport::FrameSubmit(
+ device::mojom::blink::VRPresentationProvider* vr_presentation_provider,
+ gpu::gles2::GLES2Interface* gl,
+ DrawingBuffer::Client* drawing_buffer_client,
+ scoped_refptr<Image> image_ref,
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback,
+ int16_t vr_frame_id,
+ bool needs_copy) {
+ DCHECK(transport_options_);
+
+ if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_TEXTURE_HANDLE) {
+#if defined(OS_WIN)
+ // Currently, we assume that this transport needs a copy.
+ DCHECK(needs_copy);
+ TRACE_EVENT0("gpu", "XRFrameTransport::CopyImage");
+ // Update last_transfer_succeeded_ value. This should usually complete
+ // without waiting.
+ if (transport_options_->wait_for_transfer_notification)
+ WaitForPreviousTransfer();
+ CallPreviousFrameCallback();
+ previous_image_release_callback_ = std::move(release_callback);
+ if (!frame_copier_ || !last_transfer_succeeded_) {
+ frame_copier_ = std::make_unique<GpuMemoryBufferImageCopy>(gl);
+ }
+ auto gpu_memory_buffer = frame_copier_->CopyImage(image_ref.get());
+ drawing_buffer_client->DrawingBufferClientRestoreTexture2DBinding();
+ drawing_buffer_client->DrawingBufferClientRestoreFramebufferBinding();
+ drawing_buffer_client->DrawingBufferClientRestoreRenderbufferBinding();
+
+ // We can fail to obtain a GpuMemoryBuffer if we don't have GPU support, or
+ // for some out-of-memory situations.
+ // TODO(billorr): Consider whether we should just drop the frame or exit
+ // presentation.
+ if (gpu_memory_buffer) {
+ // We decompose the cloned handle, and use it to create a
+ // mojo::ScopedHandle which will own cleanup of the handle, and will be
+ // passed over IPC.
+ gfx::GpuMemoryBufferHandle gpu_handle =
+ CloneHandleForIPC(gpu_memory_buffer->GetHandle());
+ vr_presentation_provider->SubmitFrameWithTextureHandle(
+ vr_frame_id, mojo::WrapPlatformFile(gpu_handle.handle.GetHandle()));
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+ } else if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_MAILBOX_HOLDER) {
+ // Currently, this transport assumes we don't need to make a separate copy
+ // of the canvas content.
+ DCHECK(!needs_copy);
+
+ // The AcceleratedStaticBitmapImage must be kept alive until the
+ // mailbox is used via createAndConsumeTextureCHROMIUM, the mailbox
+ // itself does not keep it alive. We must keep a reference to the
+ // image until the mailbox was consumed.
+ StaticBitmapImage* static_image =
+ static_cast<StaticBitmapImage*>(image_ref.get());
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::EnsureMailbox");
+ static_image->EnsureMailbox(kVerifiedSyncToken, GL_NEAREST);
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::EnsureMailbox");
+
+ // Conditionally wait for the previous render to finish. A late wait here
+ // attempts to overlap work in parallel with the previous frame's
+ // rendering. This is used if submitting fully rendered frames to GVR, but
+ // is susceptible to bad GPU scheduling if the new frame competes with the
+ // previous frame's incomplete rendering.
+ if (waiting_for_previous_frame_render_)
+ frame_wait_time_ += WaitForPreviousRenderToFinish();
+
+ // Save a reference to the image to keep it alive until next frame,
+ // but first wait for the transfer to finish before overwriting it.
+ // Usually this check is satisfied without waiting.
+ if (transport_options_->wait_for_transfer_notification)
+ WaitForPreviousTransfer();
+ previous_image_ = std::move(image_ref);
+ CallPreviousFrameCallback();
+ previous_image_release_callback_ = std::move(release_callback);
+
+ // Create mailbox and sync token for transfer.
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::GetMailbox");
+ auto mailbox = static_image->GetMailbox();
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::GetMailbox");
+ auto sync_token = static_image->GetSyncToken();
+
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::SubmitFrame");
+ vr_presentation_provider->SubmitFrame(
+ vr_frame_id, gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D),
+ frame_wait_time_);
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::SubmitFrame");
+ } else if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ DRAW_INTO_TEXTURE_MAILBOX) {
+ TRACE_EVENT0("gpu", "XRFrameTransport::SubmitFrameDrawnIntoTexture");
+ gpu::SyncToken sync_token;
+ {
+ TRACE_EVENT0("gpu", "GenSyncTokenCHROMIUM");
+ gl->GenSyncTokenCHROMIUM(sync_token.GetData());
+ }
+ if (waiting_for_previous_frame_render_)
+ frame_wait_time_ += WaitForPreviousRenderToFinish();
+ vr_presentation_provider->SubmitFrameDrawnIntoTexture(
+ vr_frame_id, sync_token, frame_wait_time_);
+ } else {
+ NOTREACHED() << "Unimplemented frame transport method";
+ }
+
+ // Set the expected notifications the next frame should wait for.
+ waiting_for_previous_frame_transfer_ =
+ transport_options_->wait_for_transfer_notification;
+ waiting_for_previous_frame_render_ =
+ transport_options_->wait_for_render_notification;
+ waiting_for_previous_frame_fence_ = transport_options_->wait_for_gpu_fence;
+}
+
+void XRFrameTransport::OnSubmitFrameTransferred(bool success) {
+ DVLOG(3) << __FUNCTION__;
+ waiting_for_previous_frame_transfer_ = false;
+ last_transfer_succeeded_ = success;
+}
+
+void XRFrameTransport::WaitForPreviousTransfer() {
+ TRACE_EVENT0("gpu", "waitForPreviousTransferToFinish");
+ while (waiting_for_previous_frame_transfer_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+}
+
+void XRFrameTransport::OnSubmitFrameRendered() {
+ DVLOG(3) << __FUNCTION__;
+ waiting_for_previous_frame_render_ = false;
+}
+
+WTF::TimeDelta XRFrameTransport::WaitForPreviousRenderToFinish() {
+ TRACE_EVENT0("gpu", "waitForPreviousRenderToFinish");
+ WTF::TimeTicks start = WTF::CurrentTimeTicks();
+ while (waiting_for_previous_frame_render_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+ return WTF::CurrentTimeTicks() - start;
+}
+
+void XRFrameTransport::OnSubmitFrameGpuFence(
+ const gfx::GpuFenceHandle& handle) {
+ // We just received a GpuFence, unblock WaitForGpuFenceReceived.
+ waiting_for_previous_frame_fence_ = false;
+ previous_frame_fence_ = std::make_unique<gfx::GpuFence>(handle);
+}
+
+WTF::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
+ TRACE_EVENT0("gpu", "WaitForGpuFenceReceived");
+ WTF::TimeTicks start = WTF::CurrentTimeTicks();
+ while (waiting_for_previous_frame_fence_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+ return WTF::CurrentTimeTicks() - start;
+}
+
+void XRFrameTransport::Trace(blink::Visitor* visitor) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
new file mode 100644
index 00000000000..f416c8821d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
@@ -0,0 +1,100 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
+
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace gfx {
+class GpuFence;
+}
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+} // namespace gpu
+
+namespace blink {
+
+class GpuMemoryBufferImageCopy;
+class Image;
+
+class PLATFORM_EXPORT XRFrameTransport final
+ : public GarbageCollectedFinalized<XRFrameTransport>,
+ public device::mojom::blink::VRSubmitFrameClient {
+ public:
+ explicit XRFrameTransport();
+ ~XRFrameTransport();
+
+ device::mojom::blink::VRSubmitFrameClientPtr GetSubmitFrameClient();
+
+ void PresentChange();
+
+ void SetTransportOptions(
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr);
+
+ bool DrawingIntoSharedBuffer();
+
+ // Call before finalizing the frame's image snapshot.
+ void FramePreImage(gpu::gles2::GLES2Interface*);
+
+ void FrameSubmit(device::mojom::blink::VRPresentationProvider*,
+ gpu::gles2::GLES2Interface*,
+ DrawingBuffer::Client*,
+ scoped_refptr<Image> image_ref,
+ std::unique_ptr<viz::SingleReleaseCallback>,
+ int16_t vr_frame_id,
+ bool needs_copy);
+
+ void FrameSubmitMissing(device::mojom::blink::VRPresentationProvider*,
+ gpu::gles2::GLES2Interface*,
+ int16_t vr_frame_id);
+
+ virtual void Trace(blink::Visitor*);
+
+ private:
+ void WaitForPreviousTransfer();
+ WTF::TimeDelta WaitForPreviousRenderToFinish();
+ WTF::TimeDelta WaitForGpuFenceReceived();
+ void CallPreviousFrameCallback();
+
+ // VRSubmitFrameClient
+ void OnSubmitFrameTransferred(bool success) override;
+ void OnSubmitFrameRendered() override;
+ void OnSubmitFrameGpuFence(const gfx::GpuFenceHandle&) override;
+
+ mojo::Binding<device::mojom::blink::VRSubmitFrameClient>
+ submit_frame_client_binding_;
+
+ // Used to keep the image alive until the next frame if using
+ // waitForPreviousTransferToFinish.
+ scoped_refptr<Image> previous_image_;
+ std::unique_ptr<viz::SingleReleaseCallback> previous_image_release_callback_;
+
+ bool waiting_for_previous_frame_transfer_ = false;
+ bool last_transfer_succeeded_ = false;
+ WTF::TimeDelta frame_wait_time_;
+ bool waiting_for_previous_frame_render_ = false;
+ // If using GpuFence to separate frames, need to wait for the previous
+ // frame's fence, but not if this is the first frame. Separately track
+ // if we're expecting a fence and the received fence itself.
+ bool waiting_for_previous_frame_fence_ = false;
+ std::unique_ptr<gfx::GpuFence> previous_frame_fence_;
+
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr transport_options_;
+
+ std::unique_ptr<GpuMemoryBufferImageCopy> frame_copier_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
new file mode 100644
index 00000000000..8eca5b8fa6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -0,0 +1,626 @@
+// Copyright 2017 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 "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+// Large parts of this file have been shamelessly borrowed from
+// platform/graphics/gpu/DrawingBuffer.cpp and simplified where applicable due
+// to the more narrow use case. It may make sense in the future to abstract out
+// some of the common bits into a base class?
+
+XRWebGLDrawingBuffer::ColorBuffer::ColorBuffer(
+ XRWebGLDrawingBuffer* drawing_buffer,
+ const IntSize& size,
+ GLuint texture_id)
+ : drawing_buffer(drawing_buffer), size(size), texture_id(texture_id) {
+ drawing_buffer->ContextGL()->GenMailboxCHROMIUM(mailbox.name);
+}
+
+XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
+ if (receive_sync_token.HasData())
+ gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
+ gl->DeleteTextures(1, &texture_id);
+}
+
+scoped_refptr<XRWebGLDrawingBuffer> XRWebGLDrawingBuffer::Create(
+ DrawingBuffer* drawing_buffer,
+ GLuint framebuffer,
+ const IntSize& size,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ bool want_multiview) {
+ DCHECK(drawing_buffer);
+
+ // Don't proceeed if the context is already lost.
+ if (drawing_buffer->destroyed())
+ return nullptr;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
+
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+ if (!extensions_util->IsValid()) {
+ return nullptr;
+ }
+
+ DCHECK(extensions_util->SupportsExtension("GL_OES_packed_depth_stencil"));
+ extensions_util->EnsureExtensionEnabled("GL_OES_packed_depth_stencil");
+ bool multisample_supported =
+ want_antialiasing &&
+ (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample") ||
+ extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) &&
+ extensions_util->SupportsExtension("GL_OES_rgb8_rgba8");
+ if (multisample_supported) {
+ extensions_util->EnsureExtensionEnabled("GL_OES_rgb8_rgba8");
+ if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample")) {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_CHROMIUM_framebuffer_multisample");
+ } else {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_EXT_multisampled_render_to_texture");
+ }
+ }
+ bool discard_framebuffer_supported =
+ extensions_util->SupportsExtension("GL_EXT_discard_framebuffer");
+ if (discard_framebuffer_supported)
+ extensions_util->EnsureExtensionEnabled("GL_EXT_discard_framebuffer");
+
+ // TODO(bajones): Support multiview.
+ bool multiview_supported = false;
+
+ scoped_refptr<XRWebGLDrawingBuffer> xr_drawing_buffer =
+ base::AdoptRef(new XRWebGLDrawingBuffer(
+ drawing_buffer, framebuffer, discard_framebuffer_supported,
+ want_alpha_channel, want_depth_buffer, want_stencil_buffer,
+ multiview_supported));
+ if (!xr_drawing_buffer->Initialize(size, multisample_supported,
+ multiview_supported)) {
+ DLOG(ERROR) << "XRWebGLDrawingBuffer Initialization Failed";
+ return nullptr;
+ }
+
+ return xr_drawing_buffer;
+}
+
+XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer,
+ GLuint framebuffer,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool multiview_supported)
+ : drawing_buffer_(drawing_buffer),
+ framebuffer_(framebuffer),
+ discard_framebuffer_supported_(discard_framebuffer_supported),
+ depth_(want_depth_buffer),
+ stencil_(want_stencil_buffer),
+ alpha_(want_alpha_channel),
+ multiview_(false) {}
+
+// TODO(bajones): The GL resources allocated in this function are leaking. Add
+// a way to clean up the buffers when the layer is GCed or the session ends.
+bool XRWebGLDrawingBuffer::Initialize(const IntSize& size,
+ bool use_multisampling,
+ bool use_multiview) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+
+ gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
+ DVLOG(2) << __FUNCTION__ << ": max_texture_size_=" << max_texture_size_;
+
+ // Check context capabilities
+ int max_sample_count = 0;
+ anti_aliasing_mode_ = kNone;
+ if (use_multisampling) {
+ gl->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &max_sample_count);
+ anti_aliasing_mode_ = kMSAAExplicitResolve;
+ if (extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) {
+ anti_aliasing_mode_ = kMSAAImplicitResolve;
+ } else if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_screen_space_antialiasing")) {
+ anti_aliasing_mode_ = kScreenSpaceAntialiasing;
+ }
+ }
+ DVLOG(2) << __FUNCTION__
+ << ": anti_aliasing_mode_=" << static_cast<int>(anti_aliasing_mode_);
+
+ storage_texture_supported_ =
+ (drawing_buffer_->webgl_version() > DrawingBuffer::kWebGL1 ||
+ extensions_util->SupportsExtension("GL_EXT_texture_storage")) &&
+ anti_aliasing_mode_ == kScreenSpaceAntialiasing;
+
+#if defined(OS_ANDROID)
+ // On Android devices use a smaller numer of samples to provide more breathing
+ // room for fill-rate-bound applications.
+ sample_count_ = std::min(2, max_sample_count);
+#else
+ sample_count_ = std::min(4, max_sample_count);
+#endif
+
+ Resize(size);
+
+ // It's possible that the drawing buffer allocation provokes a context loss,
+ // so check again just in case.
+ if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+gpu::gles2::GLES2Interface* XRWebGLDrawingBuffer::ContextGL() {
+ return drawing_buffer_->ContextGL();
+}
+
+void XRWebGLDrawingBuffer::SetMirrorClient(MirrorClient* client) {
+ mirror_client_ = client;
+ if (mirror_client_) {
+ // Immediately send a black 1x1 image to the mirror client to ensure that
+ // it has content to show.
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1);
+ mirror_client_->OnMirrorImageAvailable(
+ StaticBitmapImage::Create(surface->makeImageSnapshot()), nullptr);
+ }
+}
+
+bool XRWebGLDrawingBuffer::ContextLost() {
+ return drawing_buffer_->destroyed();
+}
+
+IntSize XRWebGLDrawingBuffer::AdjustSize(const IntSize& new_size) {
+ // Ensure we always have at least a 1x1 buffer
+ float width = std::max(1, new_size.Width());
+ float height = std::max(1, new_size.Height());
+
+ float adjusted_scale =
+ std::min(static_cast<float>(max_texture_size_) / width,
+ static_cast<float>(max_texture_size_) / height);
+
+ // Clamp if the desired size is greater than the maximum texture size for the
+ // device. Scale both dimensions proportionally so that we avoid stretching.
+ if (adjusted_scale < 1.0f) {
+ width *= adjusted_scale;
+ height *= adjusted_scale;
+ }
+
+ return IntSize(width, height);
+}
+
+void XRWebGLDrawingBuffer::UseSharedBuffer(
+ const gpu::MailboxHolder& buffer_mailbox_holder) {
+ DVLOG(3) << __FUNCTION__;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ // Create a texture backed by the shared buffer image.
+ DCHECK(!shared_buffer_texture_id_);
+ shared_buffer_texture_id_ =
+ gl->CreateAndConsumeTextureCHROMIUM(buffer_mailbox_holder.mailbox.name);
+
+ if (WantExplicitResolve()) {
+ // Bind the shared texture to the destination framebuffer of
+ // the explicit resolve step.
+ if (!resolved_framebuffer_) {
+ gl->GenFramebuffers(1, &resolved_framebuffer_);
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ } else {
+ // Bind the shared texture directly to the drawing framebuffer.
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ }
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ shared_buffer_texture_id_, 0, sample_count_);
+ } else {
+ // Explicit resolve, screen space antialiasing, or no antialiasing.
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, shared_buffer_texture_id_, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_sharedbuffer_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_sharedbuffer_ = true;
+ }
+
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void XRWebGLDrawingBuffer::DoneWithSharedBuffer() {
+ DVLOG(3) << __FUNCTION__;
+
+ BindAndResolveDestinationFramebuffer();
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ if (discard_framebuffer_supported_) {
+ // Discard the depth and stencil attachments since we're done with them.
+ // Don't discard the color buffer, we do need this rendered into the
+ // shared buffer.
+ if (WantExplicitResolve()) {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ } else {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ }
+ const GLenum kAttachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER,
+ sizeof(kAttachments) / sizeof(kAttachments[0]),
+ kAttachments);
+ }
+
+ // Always bind to the default framebuffer as a hint to the GPU to start
+ // rendering now.
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // Done with the texture created by CreateAndConsumeTexture, delete it.
+ DCHECK(shared_buffer_texture_id_);
+ gl->DeleteTextures(1, &shared_buffer_texture_id_);
+ shared_buffer_texture_id_ = 0;
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
+ IntSize adjusted_size = AdjustSize(new_size);
+
+ if (adjusted_size == size_)
+ return;
+
+ // Don't attempt to resize if the context is lost.
+ if (ContextLost())
+ return;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ size_ = adjusted_size;
+
+ // Free all mailboxes, because they are now of the wrong size. Only the
+ // first call in this loop has any effect.
+ recycled_color_buffer_queue_.clear();
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+
+ // Provide a depth and/or stencil buffer if requested.
+ if (depth_ || stencil_) {
+ if (depth_stencil_buffer_) {
+ gl->DeleteRenderbuffers(1, &depth_stencil_buffer_);
+ depth_stencil_buffer_ = 0;
+ }
+ gl->GenRenderbuffers(1, &depth_stencil_buffer_);
+ gl->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer_);
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ } else if (anti_aliasing_mode_ == kMSAAExplicitResolve) {
+ gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ } else {
+ gl->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ }
+
+ gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_);
+ }
+
+ if (WantExplicitResolve()) {
+ // If we're doing an explicit multisample resolve use the main framebuffer
+ // as the multisample target and resolve into resolved_fbo_ when needed.
+ GLenum multisample_format = alpha_ ? GL_RGBA8_OES : GL_RGB8_OES;
+
+ if (multisample_renderbuffer_) {
+ gl->DeleteRenderbuffers(1, &multisample_renderbuffer_);
+ multisample_renderbuffer_ = 0;
+ }
+
+ gl->GenRenderbuffers(1, &multisample_renderbuffer_);
+ gl->BindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_);
+ gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ multisample_format,
+ size_.Width(), size_.Height());
+
+ gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, multisample_renderbuffer_);
+
+ // Now bind the resolve target framebuffer to attach the color textures to.
+ if (!resolved_framebuffer_) {
+ gl->GenFramebuffers(1, &resolved_framebuffer_);
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ }
+
+ back_color_buffer_ = CreateColorBuffer();
+ front_color_buffer_ = nullptr;
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ back_color_buffer_->texture_id, 0, sample_count_);
+ } else {
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_resize_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_resize_ = true;
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreRenderbufferBinding();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
+XRWebGLDrawingBuffer::CreateColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ GLuint texture_id = 0;
+ gl->GenTextures(1, &texture_id);
+ gl->BindTexture(GL_TEXTURE_2D, texture_id);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format = alpha_ ? GL_RGBA8 : GL_RGB8;
+ gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size_.Width(), size_.Height());
+ } else {
+ GLenum gl_format = alpha_ ? GL_RGBA : GL_RGB;
+ gl->TexImage2D(GL_TEXTURE_2D, 0, gl_format, size_.Width(), size_.Height(),
+ 0, gl_format, GL_UNSIGNED_BYTE, nullptr);
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreTexture2DBinding();
+
+ return base::AdoptRef(new ColorBuffer(this, size_, texture_id));
+}
+
+scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
+XRWebGLDrawingBuffer::CreateOrRecycleColorBuffer() {
+ if (!recycled_color_buffer_queue_.IsEmpty()) {
+ scoped_refptr<ColorBuffer> recycled =
+ recycled_color_buffer_queue_.TakeLast();
+ if (recycled->receive_sync_token.HasData()) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ gl->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
+ }
+ DCHECK(recycled->size == size_);
+ return recycled;
+ }
+ return CreateColorBuffer();
+}
+
+bool XRWebGLDrawingBuffer::WantExplicitResolve() const {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+}
+
+void XRWebGLDrawingBuffer::BindAndResolveDestinationFramebuffer() {
+ // Ensure that the mode-appropriate destination framebuffer's color
+ // attachment contains the drawn content after any antialiasing steps needed.
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+
+ // Resolve multisample buffers if needed
+ if (WantExplicitResolve()) {
+ DVLOG(3) << __FUNCTION__ << ": explicit resolve";
+ gl->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, framebuffer_);
+ gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, resolved_framebuffer_);
+ gl->Disable(GL_SCISSOR_TEST);
+
+ int width = size_.Width();
+ int height = size_.Height();
+ // Use NEAREST, because there is no scale performed during the blit.
+ gl->BlitFramebufferCHROMIUM(0, 0, width, height, 0, 0, width, height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+
+ client->DrawingBufferClientRestoreScissorTest();
+ } else {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ if (anti_aliasing_mode_ == kScreenSpaceAntialiasing) {
+ DVLOG(3) << __FUNCTION__ << ": screen space antialiasing";
+ gl->ApplyScreenSpaceAntialiasingCHROMIUM();
+ } else {
+ DVLOG(3) << __FUNCTION__ << ": nothing to do";
+ }
+ }
+
+ // On exit, leaves the destination framebuffer active. Caller is responsible
+ // for restoring client bindings.
+}
+
+// Swap the front and back buffers. After this call the front buffer should
+// contain the previously rendered content, resolved from the multisample
+// renderbuffer if needed.
+void XRWebGLDrawingBuffer::SwapColorBuffers() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+
+ BindAndResolveDestinationFramebuffer();
+
+ // Swap buffers
+ front_color_buffer_ = back_color_buffer_;
+ back_color_buffer_ = CreateOrRecycleColorBuffer();
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ back_color_buffer_->texture_id, 0, sample_count_);
+ } else {
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_swap_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_swap_ = true;
+ }
+
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+scoped_refptr<StaticBitmapImage>
+XRWebGLDrawingBuffer::TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ scoped_refptr<ColorBuffer> buffer;
+ bool success = false;
+
+ // Ensure the context isn't lost and the framebuffer is complete before
+ // continuing.
+ if (!ContextLost()) {
+ SwapColorBuffers();
+
+ buffer = front_color_buffer_;
+
+ gl->ProduceTextureDirectCHROMIUM(buffer->texture_id, buffer->mailbox.name);
+ gl->GenUnverifiedSyncTokenCHROMIUM(buffer->produce_sync_token.GetData());
+
+ // This should only fail if the context is lost during the buffer swap.
+ if (buffer->produce_sync_token.HasData()) {
+ success = true;
+ }
+ }
+
+ if (!success) {
+ // If we can't get a mailbox, return an transparent black ImageBitmap.
+ // The only situation in which this could happen is when two or more calls
+ // to transferToImageBitmap are made back-to-back, if the framebuffer is
+ // incomplete (likely due to a failed buffer allocation), or when the
+ // context gets lost.
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
+ return StaticBitmapImage::Create(surface->makeImageSnapshot());
+ }
+
+ // This holds a ref on the XRWebGLDrawingBuffer that will keep it alive
+ // until the mailbox is released (and while the callback is running).
+ auto func =
+ WTF::Bind(mirror_client_ ? &XRWebGLDrawingBuffer::MailboxReleasedToMirror
+ : &XRWebGLDrawingBuffer::MailboxReleased,
+ scoped_refptr<XRWebGLDrawingBuffer>(this), buffer);
+
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback =
+ viz::SingleReleaseCallback::Create(std::move(func));
+
+ // Make our own textureId that is a reference on the same texture backing
+ // being used as the front buffer. We do not need to wait on the sync
+ // token since the mailbox was produced on the same GL context that we are
+ // using here. Similarly, the release callback will run on the same context so
+ // we don't need to send a sync token for this consume action back to it.
+ GLuint texture_id = gl->CreateAndConsumeTextureCHROMIUM(buffer->mailbox.name);
+
+ if (out_release_callback) {
+ *out_release_callback = std::move(release_callback);
+ } else {
+ release_callback->Run(gpu::SyncToken(), true /* lost_resource */);
+ }
+
+ return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ buffer->mailbox, buffer->produce_sync_token, texture_id,
+ drawing_buffer_->ContextProviderWeakPtr(), size_);
+}
+
+void XRWebGLDrawingBuffer::MailboxReleased(
+ scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ // If the mailbox has been returned by the compositor then it is no
+ // longer being presented, and so is no longer the front buffer.
+ if (color_buffer == front_color_buffer_)
+ front_color_buffer_ = nullptr;
+
+ // Update the SyncToken to ensure that we will wait for it even if we
+ // immediately destroy this buffer.
+ color_buffer->receive_sync_token = sync_token;
+
+ if (drawing_buffer_->destroyed() || color_buffer->size != size_ ||
+ lost_resource) {
+ return;
+ }
+
+ const size_t cache_limit = 2;
+ while (recycled_color_buffer_queue_.size() >= cache_limit)
+ recycled_color_buffer_queue_.TakeLast();
+
+ recycled_color_buffer_queue_.push_front(color_buffer);
+}
+
+void XRWebGLDrawingBuffer::MailboxReleasedToMirror(
+ scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ if (!mirror_client_ || lost_resource) {
+ MailboxReleased(std::move(color_buffer), sync_token, lost_resource);
+ return;
+ }
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ color_buffer->receive_sync_token = sync_token;
+
+ auto func =
+ WTF::Bind(&XRWebGLDrawingBuffer::MailboxReleased,
+ scoped_refptr<XRWebGLDrawingBuffer>(this), color_buffer);
+
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback =
+ viz::SingleReleaseCallback::Create(std::move(func));
+
+ GLuint texture_id =
+ gl->CreateAndConsumeTextureCHROMIUM(color_buffer->mailbox.name);
+
+ scoped_refptr<StaticBitmapImage> image =
+ AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ color_buffer->mailbox, color_buffer->produce_sync_token, texture_id,
+ drawing_buffer_->ContextProviderWeakPtr(), color_buffer->size);
+
+ mirror_client_->OnMirrorImageAvailable(std::move(image),
+ std::move(release_callback));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
new file mode 100644
index 00000000000..99514de6d6d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -0,0 +1,164 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_
+
+#include "cc/layers/texture_layer_client.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class DrawingBuffer;
+class StaticBitmapImage;
+
+class PLATFORM_EXPORT XRWebGLDrawingBuffer
+ : public RefCounted<XRWebGLDrawingBuffer> {
+ public:
+ static scoped_refptr<XRWebGLDrawingBuffer> Create(DrawingBuffer*,
+ GLuint framebuffer,
+ const IntSize&,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ bool want_multiview);
+
+ gpu::gles2::GLES2Interface* ContextGL();
+ bool ContextLost();
+
+ const IntSize& size() const { return size_; }
+
+ bool antialias() const { return anti_aliasing_mode_ != kNone; }
+ bool depth() const { return depth_; }
+ bool stencil() const { return stencil_; }
+ bool alpha() const { return alpha_; }
+ bool multiview() const { return multiview_; }
+
+ void Resize(const IntSize&);
+
+ scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ class MirrorClient {
+ public:
+ virtual void OnMirrorImageAvailable(
+ scoped_refptr<StaticBitmapImage>,
+ std::unique_ptr<viz::SingleReleaseCallback>) = 0;
+ };
+
+ void SetMirrorClient(MirrorClient*);
+
+ void UseSharedBuffer(const gpu::MailboxHolder&);
+ void DoneWithSharedBuffer();
+
+ private:
+ struct ColorBuffer : public RefCounted<ColorBuffer> {
+ ColorBuffer(XRWebGLDrawingBuffer*, const IntSize&, GLuint texture_id);
+ ~ColorBuffer();
+
+ // The owning XRWebGLDrawingBuffer. Note that DrawingBuffer is explicitly
+ // destroyed by the beginDestruction method, which will eventually drain all
+ // of its ColorBuffers.
+ scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer;
+ const IntSize size;
+ const GLuint texture_id = 0;
+
+ // The mailbox used to send this buffer to the compositor.
+ gpu::Mailbox mailbox;
+
+ // The sync token for when this buffer was sent to the compositor.
+ gpu::SyncToken produce_sync_token;
+
+ // The sync token for when this buffer was received back from the
+ // compositor.
+ gpu::SyncToken receive_sync_token;
+
+ private:
+ WTF_MAKE_NONCOPYABLE(ColorBuffer);
+ };
+
+ XRWebGLDrawingBuffer(DrawingBuffer*,
+ GLuint framebuffer,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool multiview_supported);
+
+ bool Initialize(const IntSize&, bool use_multisampling, bool use_multiview);
+
+ IntSize AdjustSize(const IntSize&);
+
+ scoped_refptr<ColorBuffer> CreateColorBuffer();
+ scoped_refptr<ColorBuffer> CreateOrRecycleColorBuffer();
+
+ bool WantExplicitResolve() const;
+ void BindAndResolveDestinationFramebuffer();
+ void SwapColorBuffers();
+
+ void MailboxReleased(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+ void MailboxReleasedToMirror(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ // Reference to the DrawingBuffer that owns the GL context for this object.
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+
+ const GLuint framebuffer_ = 0;
+ GLuint resolved_framebuffer_ = 0;
+ GLuint multisample_renderbuffer_ = 0;
+ scoped_refptr<ColorBuffer> back_color_buffer_ = 0;
+ scoped_refptr<ColorBuffer> front_color_buffer_ = 0;
+ GLuint depth_stencil_buffer_ = 0;
+ IntSize size_;
+
+ // Nonzero for shared buffer mode from UseSharedBuffer until
+ // DoneWithSharedBuffer.
+ GLuint shared_buffer_texture_id_ = 0;
+
+ // Checking framebuffer completeness is extremely expensive, it's basically a
+ // glFinish followed by a synchronous wait for a reply. Do so only once per
+ // code path, and only in DCHECK mode.
+ bool framebuffer_complete_checked_for_resize_ = false;
+ bool framebuffer_complete_checked_for_swap_ = false;
+ bool framebuffer_complete_checked_for_sharedbuffer_ = false;
+
+ // Color buffers that were released by the XR compositor can be used again.
+ Deque<scoped_refptr<ColorBuffer>> recycled_color_buffer_queue_;
+
+ bool discard_framebuffer_supported_;
+ bool depth_;
+ bool stencil_;
+ bool alpha_;
+ bool multiview_;
+
+ enum AntialiasingMode {
+ kNone,
+ kMSAAImplicitResolve,
+ kMSAAExplicitResolve,
+ kScreenSpaceAntialiasing,
+ };
+
+ AntialiasingMode anti_aliasing_mode_ = kNone;
+
+ bool storage_texture_supported_ = false;
+ int max_texture_size_ = 0;
+ int sample_count_ = 0;
+
+ MirrorClient* mirror_client_ = nullptr;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_