blob: 2a8d2d6cf74323f596c7a73317deb47a8ad79753 [file] [log] [blame]
/*
* 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 "platform/graphics/gpu/DrawingBuffer.h"
#include "cc/resources/shared_bitmap.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/capabilities.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/AcceleratedStaticBitmapImage.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/WebGraphicsContext3DProviderWrapper.h"
#include "platform/graphics/gpu/Extensions3DUtil.h"
#include "platform/tracing/TraceEvent.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebExternalBitmap.h"
#include "public/platform/WebExternalTextureLayer.h"
#include "skia/ext/texture_handle.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
#include "wtf/CheckedNumeric.h"
#include "wtf/typed_arrays/ArrayBufferContents.h"
#include <algorithm>
#include <memory>
namespace blink {
namespace {
const float s_resourceAdjustedRatio = 0.5;
static bool shouldFailDrawingBufferCreationForTesting = false;
} // namespace
PassRefPtr<DrawingBuffer> DrawingBuffer::create(
std::unique_ptr<WebGraphicsContext3DProvider> contextProvider,
Client* client,
const IntSize& size,
bool premultipliedAlpha,
bool wantAlphaChannel,
bool wantDepthBuffer,
bool wantStencilBuffer,
bool wantAntialiasing,
PreserveDrawingBuffer preserve,
WebGLVersion webGLVersion,
ChromiumImageUsage chromiumImageUsage) {
ASSERT(contextProvider);
if (shouldFailDrawingBufferCreationForTesting) {
shouldFailDrawingBufferCreationForTesting = false;
return nullptr;
}
std::unique_ptr<Extensions3DUtil> extensionsUtil =
Extensions3DUtil::create(contextProvider->contextGL());
if (!extensionsUtil->isValid()) {
// This might be the first time we notice that the GL context is lost.
return nullptr;
}
ASSERT(extensionsUtil->supportsExtension("GL_OES_packed_depth_stencil"));
extensionsUtil->ensureExtensionEnabled("GL_OES_packed_depth_stencil");
bool multisampleSupported =
wantAntialiasing && (extensionsUtil->supportsExtension(
"GL_CHROMIUM_framebuffer_multisample") ||
extensionsUtil->supportsExtension(
"GL_EXT_multisampled_render_to_texture")) &&
extensionsUtil->supportsExtension("GL_OES_rgb8_rgba8");
if (multisampleSupported) {
extensionsUtil->ensureExtensionEnabled("GL_OES_rgb8_rgba8");
if (extensionsUtil->supportsExtension(
"GL_CHROMIUM_framebuffer_multisample"))
extensionsUtil->ensureExtensionEnabled(
"GL_CHROMIUM_framebuffer_multisample");
else
extensionsUtil->ensureExtensionEnabled(
"GL_EXT_multisampled_render_to_texture");
}
bool discardFramebufferSupported =
extensionsUtil->supportsExtension("GL_EXT_discard_framebuffer");
if (discardFramebufferSupported)
extensionsUtil->ensureExtensionEnabled("GL_EXT_discard_framebuffer");
RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(
std::move(contextProvider), std::move(extensionsUtil), client,
discardFramebufferSupported, wantAlphaChannel, premultipliedAlpha,
preserve, webGLVersion, wantDepthBuffer, wantStencilBuffer,
chromiumImageUsage));
if (!drawingBuffer->initialize(size, multisampleSupported)) {
drawingBuffer->beginDestruction();
return PassRefPtr<DrawingBuffer>();
}
return drawingBuffer.release();
}
void DrawingBuffer::forceNextDrawingBufferCreationToFail() {
shouldFailDrawingBufferCreationForTesting = true;
}
DrawingBuffer::DrawingBuffer(
std::unique_ptr<WebGraphicsContext3DProvider> contextProvider,
std::unique_ptr<Extensions3DUtil> extensionsUtil,
Client* client,
bool discardFramebufferSupported,
bool wantAlphaChannel,
bool premultipliedAlpha,
PreserveDrawingBuffer preserve,
WebGLVersion webGLVersion,
bool wantDepth,
bool wantStencil,
ChromiumImageUsage chromiumImageUsage)
: m_client(client),
m_preserveDrawingBuffer(preserve),
m_webGLVersion(webGLVersion),
m_contextProvider(wrapUnique(
new WebGraphicsContext3DProviderWrapper(std::move(contextProvider)))),
m_gl(this->contextProvider()->contextGL()),
m_extensionsUtil(std::move(extensionsUtil)),
m_discardFramebufferSupported(discardFramebufferSupported),
m_wantAlphaChannel(wantAlphaChannel),
m_premultipliedAlpha(premultipliedAlpha),
m_softwareRendering(this->contextProvider()->isSoftwareRendering()),
m_wantDepth(wantDepth),
m_wantStencil(wantStencil),
m_chromiumImageUsage(chromiumImageUsage) {
// Used by browser tests to detect the use of a DrawingBuffer.
TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation",
TRACE_EVENT_SCOPE_GLOBAL);
}
DrawingBuffer::~DrawingBuffer() {
DCHECK(m_destructionInProgress);
m_layer.reset();
m_contextProvider.reset();
}
void DrawingBuffer::markContentsChanged() {
m_contentsChanged = true;
m_contentsChangeCommitted = false;
}
bool DrawingBuffer::bufferClearNeeded() const {
return m_bufferClearNeeded;
}
void DrawingBuffer::setBufferClearNeeded(bool flag) {
if (m_preserveDrawingBuffer == Discard) {
m_bufferClearNeeded = flag;
} else {
ASSERT(!m_bufferClearNeeded);
}
}
gpu::gles2::GLES2Interface* DrawingBuffer::contextGL() {
return m_gl;
}
WebGraphicsContext3DProvider* DrawingBuffer::contextProvider() {
return m_contextProvider->contextProvider();
}
void DrawingBuffer::setIsHidden(bool hidden) {
if (m_isHidden == hidden)
return;
m_isHidden = hidden;
if (m_isHidden)
m_recycledColorBufferQueue.clear();
}
void DrawingBuffer::setFilterQuality(SkFilterQuality filterQuality) {
if (m_filterQuality != filterQuality) {
m_filterQuality = filterQuality;
if (m_layer)
m_layer->setNearestNeighbor(filterQuality == kNone_SkFilterQuality);
}
}
bool DrawingBuffer::requiresAlphaChannelToBePreserved() {
return m_client->DrawingBufferClientIsBoundForDraw() &&
defaultBufferRequiresAlphaChannelToBePreserved();
}
bool DrawingBuffer::defaultBufferRequiresAlphaChannelToBePreserved() {
if (wantExplicitResolve()) {
return !m_wantAlphaChannel &&
getMultisampledRenderbufferFormat() == GL_RGBA8_OES;
}
bool rgbEmulation =
contextProvider()->getCapabilities().emulate_rgb_buffer_with_rgba ||
(shouldUseChromiumImage() &&
contextProvider()->getCapabilities().chromium_image_rgb_emulation);
return !m_wantAlphaChannel && rgbEmulation;
}
std::unique_ptr<cc::SharedBitmap> DrawingBuffer::createOrRecycleBitmap() {
auto it = std::remove_if(
m_recycledBitmaps.begin(), m_recycledBitmaps.end(),
[this](const RecycledBitmap& bitmap) { return bitmap.size != m_size; });
m_recycledBitmaps.shrink(it - m_recycledBitmaps.begin());
if (!m_recycledBitmaps.isEmpty()) {
RecycledBitmap recycled = std::move(m_recycledBitmaps.back());
m_recycledBitmaps.pop_back();
DCHECK(recycled.size == m_size);
return std::move(recycled.bitmap);
}
return Platform::current()->allocateSharedBitmap(m_size);
}
bool DrawingBuffer::PrepareTextureMailbox(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) {
ScopedStateRestorer scopedStateRestorer(this);
bool forceGpuResult = false;
return prepareTextureMailboxInternal(outMailbox, outReleaseCallback,
forceGpuResult);
}
bool DrawingBuffer::prepareTextureMailboxInternal(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback,
bool forceGpuResult) {
DCHECK(m_stateRestorer);
if (m_destructionInProgress) {
// 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;
}
ASSERT(!m_isHidden);
if (!m_contentsChanged)
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 (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
return false;
TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox");
if (m_newMailboxCallback)
(*m_newMailboxCallback)();
// Resolve the multisampled buffer into m_backColorBuffer texture.
if (m_antiAliasingMode != None)
resolveMultisampleFramebufferInternal();
if (m_softwareRendering && !forceGpuResult) {
return finishPrepareTextureMailboxSoftware(outMailbox, outReleaseCallback);
} else {
return finishPrepareTextureMailboxGpu(outMailbox, outReleaseCallback);
}
}
bool DrawingBuffer::finishPrepareTextureMailboxSoftware(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) {
std::unique_ptr<cc::SharedBitmap> bitmap = createOrRecycleBitmap();
if (!bitmap)
return false;
// Read the framebuffer into |bitmap|.
{
unsigned char* pixels = bitmap->pixels();
DCHECK(pixels);
bool needPremultiply = m_wantAlphaChannel && !m_premultipliedAlpha;
WebGLImageConversion::AlphaOp op =
needPremultiply ? WebGLImageConversion::AlphaDoPremultiply
: WebGLImageConversion::AlphaDoNothing;
readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia,
op);
}
*outMailbox = cc::TextureMailbox(bitmap.get(), m_size);
// 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,
RefPtr<DrawingBuffer>(this),
WTF::passed(std::move(bitmap)), m_size);
*outReleaseCallback =
cc::SingleReleaseCallback::Create(convertToBaseCallback(std::move(func)));
return true;
}
bool DrawingBuffer::finishPrepareTextureMailboxGpu(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) {
DCHECK(m_stateRestorer);
if (m_webGLVersion > WebGL1) {
m_stateRestorer->setPixelUnpackBufferBindingDirty();
m_gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
// Specify the buffer that we will put in the mailbox.
RefPtr<ColorBuffer> colorBufferForMailbox;
if (m_preserveDrawingBuffer == Discard) {
// If we can discard the backbuffer, send the old backbuffer directly
// into the mailbox, and allocate (or recycle) a new backbuffer.
colorBufferForMailbox = m_backColorBuffer;
m_backColorBuffer = 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 (m_discardFramebufferSupported) {
const GLenum attachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
GL_STENCIL_ATTACHMENT};
m_stateRestorer->setFramebufferBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments);
}
} else {
// If we can't discard the backbuffer, create (or recycle) a buffer to put
// in the mailbox, and copy backbuffer's contents there.
colorBufferForMailbox = createOrRecycleColorBuffer();
m_gl->CopySubTextureCHROMIUM(
m_backColorBuffer->textureId, colorBufferForMailbox->textureId, 0, 0, 0,
0, m_size.width(), m_size.height(), GL_FALSE, GL_FALSE, GL_FALSE);
}
// Put colorBufferForMailbox into its mailbox, and populate its
// produceSyncToken with that point.
{
m_gl->ProduceTextureDirectCHROMIUM(colorBufferForMailbox->textureId,
colorBufferForMailbox->parameters.target,
colorBufferForMailbox->mailbox.name);
const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM();
#if OS(MACOSX)
m_gl->DescheduleUntilFinishedCHROMIUM();
#endif
m_gl->Flush();
m_gl->GenSyncTokenCHROMIUM(
fenceSync, colorBufferForMailbox->produceSyncToken.GetData());
}
// Populate the output mailbox and callback.
{
bool isOverlayCandidate = colorBufferForMailbox->imageId != 0;
bool secureOutputOnly = false;
*outMailbox = cc::TextureMailbox(
colorBufferForMailbox->mailbox, colorBufferForMailbox->produceSyncToken,
colorBufferForMailbox->parameters.target, gfx::Size(m_size),
isOverlayCandidate, secureOutputOnly);
// 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,
RefPtr<DrawingBuffer>(this), colorBufferForMailbox);
*outReleaseCallback = cc::SingleReleaseCallback::Create(
convertToBaseCallback(std::move(func)));
}
// Point |m_frontColorBuffer| to the buffer that we are now presenting.
m_frontColorBuffer = colorBufferForMailbox;
m_contentsChanged = false;
setBufferClearNeeded(true);
return true;
}
void DrawingBuffer::mailboxReleasedGpu(RefPtr<ColorBuffer> colorBuffer,
const gpu::SyncToken& syncToken,
bool lostResource) {
// 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 (colorBuffer == m_frontColorBuffer)
m_frontColorBuffer = nullptr;
// Update the SyncToken to ensure that we will wait for it even if we
// immediately destroy this buffer.
colorBuffer->receiveSyncToken = syncToken;
if (m_destructionInProgress || colorBuffer->size != m_size ||
m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lostResource ||
m_isHidden) {
return;
}
// Creation of image backed mailboxes is very expensive, so be less
// aggressive about pruning them. Pruning is done in FIFO order.
size_t cacheLimit = 1;
if (shouldUseChromiumImage())
cacheLimit = 4;
while (m_recycledColorBufferQueue.size() >= cacheLimit)
m_recycledColorBufferQueue.takeLast();
m_recycledColorBufferQueue.prepend(colorBuffer);
}
void DrawingBuffer::mailboxReleasedSoftware(
std::unique_ptr<cc::SharedBitmap> bitmap,
const IntSize& size,
const gpu::SyncToken& syncToken,
bool lostResource) {
DCHECK(!syncToken.HasData()); // No sync tokens for software resources.
if (m_destructionInProgress || lostResource || m_isHidden || size != m_size)
return; // Just delete the bitmap.
RecycledBitmap recycled = {std::move(bitmap), m_size};
m_recycledBitmaps.append(std::move(recycled));
}
PassRefPtr<StaticBitmapImage> DrawingBuffer::transferToStaticBitmapImage() {
ScopedStateRestorer scopedStateRestorer(this);
// This can be null if the context is lost before the first call to
// grContext().
GrContext* grContext = contextProvider()->grContext();
cc::TextureMailbox textureMailbox;
std::unique_ptr<cc::SingleReleaseCallback> releaseCallback;
bool success = false;
if (grContext) {
bool forceGpuResult = true;
success = prepareTextureMailboxInternal(&textureMailbox, &releaseCallback,
forceGpuResult);
}
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, or when the context gets
// lost.
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(m_size.width(), m_size.height());
return StaticBitmapImage::create(surface->makeImageSnapshot());
}
DCHECK_EQ(m_size.width(), textureMailbox.size_in_pixels().width());
DCHECK_EQ(m_size.height(), textureMailbox.size_in_pixels().height());
// Make our own textureId that is a reference on the same texture backing
// being used as the front buffer (which was returned from
// PrepareTextureMailbox()). We do not need to wait on the sync token in
// |textureMailbox| since the mailbox was produced on the same |m_gl| context
// that we are using here. Similarly, the |releaseCallback| 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 PrepareTextureMailbox(), we could just use
// the actual texture id and avoid needing to produce/consume a mailbox.
GLuint textureId = m_gl->CreateAndConsumeTextureCHROMIUM(
GL_TEXTURE_2D, textureMailbox.name());
// 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|.
releaseCallback->Run(gpu::SyncToken(), true /* lostResource */);
// We reuse the same mailbox name from above since our texture id was consumed
// from it.
const auto& skImageMailbox = textureMailbox.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& skImageSyncToken = textureMailbox.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(
skImageMailbox, skImageSyncToken, textureId,
m_contextProvider->createWeakPtr(), m_size);
}
DrawingBuffer::ColorBufferParameters
DrawingBuffer::gpuMemoryBufferColorBufferParameters() {
#if OS(MACOSX)
// A CHROMIUM_image backed texture requires a specialized set of parameters
// on OSX.
ColorBufferParameters parameters;
parameters.target = GC3D_TEXTURE_RECTANGLE_ARB;
if (m_wantAlphaChannel) {
parameters.creationInternalColorFormat = GL_RGBA;
parameters.internalColorFormat = GL_RGBA;
} else if (contextProvider()
->getCapabilities()
.chromium_image_rgb_emulation) {
parameters.creationInternalColorFormat = GL_RGB;
parameters.internalColorFormat = GL_RGBA;
} else {
GLenum format =
defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB;
parameters.creationInternalColorFormat = format;
parameters.internalColorFormat = format;
}
// Unused when CHROMIUM_image is being used.
parameters.colorFormat = 0;
return parameters;
#else
return textureColorBufferParameters();
#endif
}
DrawingBuffer::ColorBufferParameters
DrawingBuffer::textureColorBufferParameters() {
ColorBufferParameters parameters;
parameters.target = GL_TEXTURE_2D;
if (m_wantAlphaChannel) {
parameters.internalColorFormat = GL_RGBA;
parameters.creationInternalColorFormat = GL_RGBA;
parameters.colorFormat = GL_RGBA;
} else if (contextProvider()
->getCapabilities()
.emulate_rgb_buffer_with_rgba) {
parameters.internalColorFormat = GL_RGBA;
parameters.creationInternalColorFormat = GL_RGBA;
parameters.colorFormat = GL_RGBA;
} else {
GLenum format =
defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB;
parameters.creationInternalColorFormat = format;
parameters.internalColorFormat = format;
parameters.colorFormat = format;
}
return parameters;
}
PassRefPtr<DrawingBuffer::ColorBuffer>
DrawingBuffer::createOrRecycleColorBuffer() {
DCHECK(m_stateRestorer);
if (!m_recycledColorBufferQueue.isEmpty()) {
RefPtr<ColorBuffer> recycled = m_recycledColorBufferQueue.takeLast();
if (recycled->receiveSyncToken.HasData())
m_gl->WaitSyncTokenCHROMIUM(recycled->receiveSyncToken.GetData());
DCHECK(recycled->size == m_size);
return recycled;
}
return createColorBuffer(m_size);
}
DrawingBuffer::ColorBuffer::ColorBuffer(DrawingBuffer* drawingBuffer,
const ColorBufferParameters& parameters,
const IntSize& size,
GLuint textureId,
GLuint imageId)
: drawingBuffer(drawingBuffer),
parameters(parameters),
size(size),
textureId(textureId),
imageId(imageId) {
drawingBuffer->contextGL()->GenMailboxCHROMIUM(mailbox.name);
}
DrawingBuffer::ColorBuffer::~ColorBuffer() {
gpu::gles2::GLES2Interface* gl = drawingBuffer->m_gl;
if (receiveSyncToken.HasData())
gl->WaitSyncTokenCHROMIUM(receiveSyncToken.GetConstData());
if (imageId) {
gl->BindTexture(parameters.target, textureId);
gl->ReleaseTexImage2DCHROMIUM(parameters.target, imageId);
gl->DestroyImageCHROMIUM(imageId);
switch (parameters.target) {
case GL_TEXTURE_2D:
// Restore the texture binding for GL_TEXTURE_2D, since the client will
// expect the previous state.
if (drawingBuffer->m_client)
drawingBuffer->m_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;
}
}
gl->DeleteTextures(1, &textureId);
}
bool DrawingBuffer::initialize(const IntSize& size, bool useMultisampling) {
ScopedStateRestorer scopedStateRestorer(this);
if (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
// Need to try to restore the context again later.
return false;
}
m_gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
int maxSampleCount = 0;
m_antiAliasingMode = None;
if (useMultisampling) {
m_gl->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
m_antiAliasingMode = MSAAExplicitResolve;
if (m_extensionsUtil->supportsExtension(
"GL_EXT_multisampled_render_to_texture")) {
m_antiAliasingMode = MSAAImplicitResolve;
} else if (m_extensionsUtil->supportsExtension(
"GL_CHROMIUM_screen_space_antialiasing")) {
m_antiAliasingMode = ScreenSpaceAntialiasing;
}
}
// 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.
m_storageTextureSupported =
(m_webGLVersion > WebGL1 ||
m_extensionsUtil->supportsExtension("GL_EXT_texture_storage")) &&
m_antiAliasingMode == ScreenSpaceAntialiasing;
m_sampleCount = std::min(4, maxSampleCount);
m_stateRestorer->setFramebufferBindingDirty();
m_gl->GenFramebuffers(1, &m_fbo);
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
if (wantExplicitResolve()) {
m_gl->GenFramebuffers(1, &m_multisampleFBO);
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
m_gl->GenRenderbuffers(1, &m_multisampleRenderbuffer);
}
if (!resizeFramebufferInternal(size))
return false;
if (m_depthStencilBuffer) {
DCHECK(wantDepthOrStencil());
m_hasImplicitStencilBuffer = !m_wantStencil;
}
if (m_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
return false;
}
return true;
}
bool DrawingBuffer::copyToPlatformTexture(gpu::gles2::GLES2Interface* gl,
GLuint texture,
GLenum internalFormat,
GLenum destType,
GLint level,
bool premultiplyAlpha,
bool flipY,
const IntPoint& destTextureOffset,
const IntRect& sourceSubRectangle,
SourceDrawingBuffer sourceBuffer) {
ScopedStateRestorer scopedStateRestorer(this);
if (m_contentsChanged) {
if (m_antiAliasingMode != None)
resolveMultisampleFramebufferInternal();
m_gl->Flush();
}
// Assume that the destination target is GL_TEXTURE_2D.
if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(
GL_TEXTURE_2D, internalFormat, destType, level))
return false;
// Contexts may be in a different share group. We must transfer the texture
// through a mailbox first.
GLenum target = 0;
gpu::Mailbox mailbox;
gpu::SyncToken produceSyncToken;
if (sourceBuffer == FrontBuffer && m_frontColorBuffer) {
target = m_frontColorBuffer->parameters.target;
mailbox = m_frontColorBuffer->mailbox;
produceSyncToken = m_frontColorBuffer->produceSyncToken;
} else {
target = m_backColorBuffer->parameters.target;
m_gl->GenMailboxCHROMIUM(mailbox.name);
m_gl->ProduceTextureDirectCHROMIUM(m_backColorBuffer->textureId, target,
mailbox.name);
const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM();
m_gl->Flush();
m_gl->GenSyncTokenCHROMIUM(fenceSync, produceSyncToken.GetData());
}
DCHECK(produceSyncToken.HasData());
gl->WaitSyncTokenCHROMIUM(produceSyncToken.GetConstData());
GLuint sourceTexture =
gl->CreateAndConsumeTextureCHROMIUM(target, mailbox.name);
GLboolean unpackPremultiplyAlphaNeeded = GL_FALSE;
GLboolean unpackUnpremultiplyAlphaNeeded = GL_FALSE;
if (m_wantAlphaChannel && m_premultipliedAlpha && !premultiplyAlpha)
unpackUnpremultiplyAlphaNeeded = GL_TRUE;
else if (m_wantAlphaChannel && !m_premultipliedAlpha && premultiplyAlpha)
unpackPremultiplyAlphaNeeded = GL_TRUE;
gl->CopySubTextureCHROMIUM(
sourceTexture, texture, destTextureOffset.x(), destTextureOffset.y(),
sourceSubRectangle.x(), sourceSubRectangle.y(),
sourceSubRectangle.width(), sourceSubRectangle.height(), flipY,
unpackPremultiplyAlphaNeeded, unpackUnpremultiplyAlphaNeeded);
gl->DeleteTextures(1, &sourceTexture);
const GLuint64 fenceSync = gl->InsertFenceSyncCHROMIUM();
gl->Flush();
gpu::SyncToken syncToken;
gl->GenSyncTokenCHROMIUM(fenceSync, syncToken.GetData());
m_gl->WaitSyncTokenCHROMIUM(syncToken.GetData());
return true;
}
WebLayer* DrawingBuffer::platformLayer() {
if (!m_layer) {
m_layer =
Platform::current()->compositorSupport()->createExternalTextureLayer(
this);
m_layer->setOpaque(!m_wantAlphaChannel);
m_layer->setBlendBackgroundColor(m_wantAlphaChannel);
m_layer->setPremultipliedAlpha(m_premultipliedAlpha);
m_layer->setNearestNeighbor(m_filterQuality == kNone_SkFilterQuality);
GraphicsLayer::registerContentsLayer(m_layer->layer());
}
return m_layer->layer();
}
void DrawingBuffer::clearPlatformLayer() {
if (m_layer)
m_layer->clearTexture();
m_gl->Flush();
}
void DrawingBuffer::beginDestruction() {
ASSERT(!m_destructionInProgress);
m_destructionInProgress = true;
clearPlatformLayer();
m_recycledColorBufferQueue.clear();
if (m_multisampleFBO)
m_gl->DeleteFramebuffers(1, &m_multisampleFBO);
if (m_fbo)
m_gl->DeleteFramebuffers(1, &m_fbo);
if (m_multisampleRenderbuffer)
m_gl->DeleteRenderbuffers(1, &m_multisampleRenderbuffer);
if (m_depthStencilBuffer)
m_gl->DeleteRenderbuffers(1, &m_depthStencilBuffer);
m_size = IntSize();
m_backColorBuffer = nullptr;
m_frontColorBuffer = nullptr;
m_multisampleRenderbuffer = 0;
m_depthStencilBuffer = 0;
m_multisampleFBO = 0;
m_fbo = 0;
if (m_layer)
GraphicsLayer::unregisterContentsLayer(m_layer->layer());
m_client = nullptr;
}
bool DrawingBuffer::resizeDefaultFramebuffer(const IntSize& size) {
DCHECK(m_stateRestorer);
// Recreate m_backColorBuffer.
m_backColorBuffer = createColorBuffer(size);
attachColorBufferToReadFramebuffer();
if (wantExplicitResolve()) {
m_stateRestorer->setFramebufferBindingDirty();
m_stateRestorer->setRenderbufferBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderbuffer);
m_gl->RenderbufferStorageMultisampleCHROMIUM(
GL_RENDERBUFFER, m_sampleCount, getMultisampledRenderbufferFormat(),
size.width(), size.height());
if (m_gl->GetError() == GL_OUT_OF_MEMORY)
return false;
m_gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, m_multisampleRenderbuffer);
}
if (wantDepthOrStencil()) {
m_stateRestorer->setFramebufferBindingDirty();
m_stateRestorer->setRenderbufferBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER,
m_multisampleFBO ? m_multisampleFBO : m_fbo);
if (!m_depthStencilBuffer)
m_gl->GenRenderbuffers(1, &m_depthStencilBuffer);
m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
if (m_antiAliasingMode == MSAAImplicitResolve) {
m_gl->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount,
GL_DEPTH24_STENCIL8_OES,
size.width(), size.height());
} else if (m_antiAliasingMode == MSAAExplicitResolve) {
m_gl->RenderbufferStorageMultisampleCHROMIUM(
GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(),
size.height());
} else {
m_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.
m_gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_depthStencilBuffer);
m_gl->BindRenderbuffer(GL_RENDERBUFFER, 0);
}
if (wantExplicitResolve()) {
m_stateRestorer->setFramebufferBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
if (m_gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return false;
}
m_stateRestorer->setFramebufferBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
return m_gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
GL_FRAMEBUFFER_COMPLETE;
}
void DrawingBuffer::clearFramebuffers(GLbitfield clearMask) {
ScopedStateRestorer scopedStateRestorer(this);
clearFramebuffersInternal(clearMask);
}
void DrawingBuffer::clearFramebuffersInternal(GLbitfield clearMask) {
DCHECK(m_stateRestorer);
m_stateRestorer->setFramebufferBindingDirty();
// We will clear the multisample FBO, but we also need to clear the
// non-multisampled buffer.
if (m_multisampleFBO) {
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
m_gl->Clear(GL_COLOR_BUFFER_BIT);
}
m_gl->BindFramebuffer(GL_FRAMEBUFFER,
m_multisampleFBO ? m_multisampleFBO : m_fbo);
m_gl->Clear(clearMask);
}
IntSize DrawingBuffer::adjustSize(const IntSize& desiredSize,
const IntSize& curSize,
int maxTextureSize) {
IntSize adjustedSize = desiredSize;
// Clamp if the desired size is greater than the maximum texture size for the
// device.
if (adjustedSize.height() > maxTextureSize)
adjustedSize.setHeight(maxTextureSize);
if (adjustedSize.width() > maxTextureSize)
adjustedSize.setWidth(maxTextureSize);
return adjustedSize;
}
bool DrawingBuffer::resize(const IntSize& newSize) {
ScopedStateRestorer scopedStateRestorer(this);
return resizeFramebufferInternal(newSize);
}
bool DrawingBuffer::resizeFramebufferInternal(const IntSize& newSize) {
DCHECK(m_stateRestorer);
DCHECK(!newSize.isEmpty());
IntSize adjustedSize = adjustSize(newSize, m_size, m_maxTextureSize);
if (adjustedSize.isEmpty())
return false;
if (adjustedSize != m_size) {
do {
if (!resizeDefaultFramebuffer(adjustedSize)) {
adjustedSize.scale(s_resourceAdjustedRatio);
continue;
}
break;
} while (!adjustedSize.isEmpty());
m_size = adjustedSize;
// Free all mailboxes, because they are now of the wrong size. Only the
// first call in this loop has any effect.
m_recycledColorBufferQueue.clear();
m_recycledBitmaps.clear();
if (adjustedSize.isEmpty())
return false;
}
m_stateRestorer->setClearStateDirty();
m_gl->Disable(GL_SCISSOR_TEST);
m_gl->ClearColor(0, 0, 0,
defaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0);
m_gl->ColorMask(true, true, true, true);
GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
if (!!m_depthStencilBuffer) {
m_gl->ClearDepthf(1.0f);
clearMask |= GL_DEPTH_BUFFER_BIT;
m_gl->DepthMask(true);
}
if (!!m_depthStencilBuffer) {
m_gl->ClearStencil(0);
clearMask |= GL_STENCIL_BUFFER_BIT;
m_gl->StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
}
clearFramebuffersInternal(clearMask);
return true;
}
void DrawingBuffer::resolveAndBindForReadAndDraw() {
{
ScopedStateRestorer scopedStateRestorer(this);
resolveMultisampleFramebufferInternal();
}
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
}
void DrawingBuffer::resolveMultisampleFramebufferInternal() {
DCHECK(m_stateRestorer);
m_stateRestorer->setFramebufferBindingDirty();
if (wantExplicitResolve() && !m_contentsChangeCommitted) {
m_stateRestorer->setClearStateDirty();
m_gl->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_multisampleFBO);
m_gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, m_fbo);
m_gl->Disable(GL_SCISSOR_TEST);
int width = m_size.width();
int height = m_size.height();
// Use NEAREST, because there is no scale performed during the blit.
GLuint filter = GL_NEAREST;
m_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()
->getCapabilities()
.disable_multisampling_color_mask_usage) {
m_gl->ClearColor(0, 0, 0, 1);
m_gl->ColorMask(false, false, false, true);
}
}
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
if (m_antiAliasingMode == ScreenSpaceAntialiasing)
m_gl->ApplyScreenSpaceAntialiasingCHROMIUM();
m_contentsChangeCommitted = true;
}
void DrawingBuffer::restoreFramebufferBindings() {
m_client->DrawingBufferClientRestoreFramebufferBinding();
}
void DrawingBuffer::restoreAllState() {
m_client->DrawingBufferClientRestoreScissorTest();
m_client->DrawingBufferClientRestoreMaskAndClearValues();
m_client->DrawingBufferClientRestorePixelPackAlignment();
m_client->DrawingBufferClientRestoreTexture2DBinding();
m_client->DrawingBufferClientRestoreRenderbufferBinding();
m_client->DrawingBufferClientRestoreFramebufferBinding();
m_client->DrawingBufferClientRestorePixelUnpackBufferBinding();
}
bool DrawingBuffer::multisample() const {
return m_antiAliasingMode != None;
}
void DrawingBuffer::bind(GLenum target) {
m_gl->BindFramebuffer(target,
wantExplicitResolve() ? m_multisampleFBO : m_fbo);
}
bool DrawingBuffer::paintRenderingResultsToImageData(
int& width,
int& height,
SourceDrawingBuffer sourceBuffer,
WTF::ArrayBufferContents& contents) {
ScopedStateRestorer scopedStateRestorer(this);
ASSERT(!m_premultipliedAlpha);
width = size().width();
height = size().height();
CheckedNumeric<int> dataSize = 4;
dataSize *= width;
dataSize *= height;
if (!dataSize.IsValid())
return false;
WTF::ArrayBufferContents pixels(width * height, 4,
WTF::ArrayBufferContents::NotShared,
WTF::ArrayBufferContents::DontInitialize);
GLuint fbo = 0;
m_stateRestorer->setFramebufferBindingDirty();
if (sourceBuffer == FrontBuffer && m_frontColorBuffer) {
m_gl->GenFramebuffers(1, &fbo);
m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
m_frontColorBuffer->parameters.target,
m_frontColorBuffer->textureId, 0);
} else {
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
}
readBackFramebuffer(static_cast<unsigned char*>(pixels.data()), width, height,
ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
flipVertically(static_cast<uint8_t*>(pixels.data()), width, height);
if (fbo) {
m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
m_frontColorBuffer->parameters.target, 0, 0);
m_gl->DeleteFramebuffers(1, &fbo);
}
pixels.transfer(contents);
return true;
}
void DrawingBuffer::readBackFramebuffer(unsigned char* pixels,
int width,
int height,
ReadbackOrder readbackOrder,
WebGLImageConversion::AlphaOp op) {
DCHECK(m_stateRestorer);
m_stateRestorer->setPixelPackAlignmentDirty();
m_gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
m_gl->ReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
size_t bufferSize = 4 * width * height;
if (readbackOrder == ReadbackSkia) {
#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 < bufferSize; i += 4) {
std::swap(pixels[i], pixels[i + 2]);
}
#endif
}
if (op == WebGLImageConversion::AlphaDoPremultiply) {
for (size_t i = 0; i < bufferSize; i += 4) {
pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
}
} else if (op != WebGLImageConversion::AlphaDoNothing) {
ASSERT_NOT_REACHED();
}
}
void DrawingBuffer::flipVertically(uint8_t* framebuffer,
int width,
int height) {
std::vector<uint8_t> scanline(width * 4);
unsigned rowBytes = width * 4;
unsigned count = height / 2;
for (unsigned i = 0; i < count; i++) {
uint8_t* rowA = framebuffer + i * rowBytes;
uint8_t* rowB = framebuffer + (height - i - 1) * rowBytes;
memcpy(scanline.data(), rowB, rowBytes);
memcpy(rowB, rowA, rowBytes);
memcpy(rowA, scanline.data(), rowBytes);
}
}
RefPtr<DrawingBuffer::ColorBuffer> DrawingBuffer::createColorBuffer(
const IntSize& size) {
DCHECK(m_stateRestorer);
m_stateRestorer->setFramebufferBindingDirty();
m_stateRestorer->setTextureBindingDirty();
// Select the Parameters for the texture object. Allocate the backing
// GpuMemoryBuffer and GLImage, if one is going to be used.
ColorBufferParameters parameters;
GLuint imageId = 0;
if (shouldUseChromiumImage()) {
parameters = gpuMemoryBufferColorBufferParameters();
imageId = m_gl->CreateGpuMemoryBufferImageCHROMIUM(
size.width(), size.height(), parameters.creationInternalColorFormat,
GC3D_SCANOUT_CHROMIUM);
} else {
parameters = textureColorBufferParameters();
}
// Allocate the texture for this object.
GLuint textureId = 0;
{
m_gl->GenTextures(1, &textureId);
m_gl->BindTexture(parameters.target, textureId);
m_gl->TexParameteri(parameters.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
m_gl->TexParameteri(parameters.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
m_gl->TexParameteri(parameters.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 (imageId) {
m_gl->BindTexImage2DCHROMIUM(parameters.target, imageId);
} else {
if (m_storageTextureSupported) {
GLenum internalStorageFormat = GL_NONE;
if (parameters.creationInternalColorFormat == GL_RGB) {
internalStorageFormat = GL_RGB8;
} else if (parameters.creationInternalColorFormat == GL_RGBA) {
internalStorageFormat = GL_RGBA8;
} else {
NOTREACHED();
}
m_gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internalStorageFormat,
size.width(), size.height());
} else {
m_gl->TexImage2D(parameters.target, 0,
parameters.creationInternalColorFormat, size.width(),
size.height(), 0, parameters.colorFormat,
GL_UNSIGNED_BYTE, 0);
}
}
// Clear the alpha channel if this is RGB emulated.
if (imageId && !m_wantAlphaChannel &&
contextProvider()->getCapabilities().chromium_image_rgb_emulation) {
GLuint fbo = 0;
m_stateRestorer->setClearStateDirty();
m_gl->GenFramebuffers(1, &fbo);
m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
parameters.target, textureId, 0);
m_gl->ClearColor(0, 0, 0, 1);
m_gl->ColorMask(false, false, false, true);
m_gl->Clear(GL_COLOR_BUFFER_BIT);
m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
parameters.target, 0, 0);
m_gl->DeleteFramebuffers(1, &fbo);
}
return adoptRef(new ColorBuffer(this, parameters, size, textureId, imageId));
}
void DrawingBuffer::attachColorBufferToReadFramebuffer() {
DCHECK(m_stateRestorer);
m_stateRestorer->setFramebufferBindingDirty();
m_stateRestorer->setTextureBindingDirty();
m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
GLenum target = m_backColorBuffer->parameters.target;
GLenum id = m_backColorBuffer->textureId;
m_gl->BindTexture(target, id);
if (m_antiAliasingMode == MSAAImplicitResolve)
m_gl->FramebufferTexture2DMultisampleEXT(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id, 0, m_sampleCount);
else
m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id,
0);
}
bool DrawingBuffer::wantExplicitResolve() {
return m_antiAliasingMode == MSAAExplicitResolve;
}
bool DrawingBuffer::wantDepthOrStencil() {
return m_wantDepth || m_wantStencil;
}
GLenum DrawingBuffer::getMultisampledRenderbufferFormat() {
DCHECK(wantExplicitResolve());
if (m_wantAlphaChannel)
return GL_RGBA8_OES;
if (shouldUseChromiumImage() &&
contextProvider()->getCapabilities().chromium_image_rgb_emulation)
return GL_RGBA8_OES;
if (contextProvider()
->getCapabilities()
.disable_webgl_rgb_multisampling_usage)
return GL_RGBA8_OES;
return GL_RGB8_OES;
}
DrawingBuffer::ScopedStateRestorer::ScopedStateRestorer(
DrawingBuffer* drawingBuffer)
: m_drawingBuffer(drawingBuffer) {
// If this is a nested restorer, save the previous restorer.
m_previousStateRestorer = drawingBuffer->m_stateRestorer;
m_drawingBuffer->m_stateRestorer = this;
}
DrawingBuffer::ScopedStateRestorer::~ScopedStateRestorer() {
DCHECK_EQ(m_drawingBuffer->m_stateRestorer, this);
m_drawingBuffer->m_stateRestorer = m_previousStateRestorer;
Client* client = m_drawingBuffer->m_client;
if (!client)
return;
if (m_clearStateDirty) {
client->DrawingBufferClientRestoreScissorTest();
client->DrawingBufferClientRestoreMaskAndClearValues();
}
if (m_pixelPackAlignmentDirty)
client->DrawingBufferClientRestorePixelPackAlignment();
if (m_textureBindingDirty)
client->DrawingBufferClientRestoreTexture2DBinding();
if (m_renderbufferBindingDirty)
client->DrawingBufferClientRestoreRenderbufferBinding();
if (m_framebufferBindingDirty)
client->DrawingBufferClientRestoreFramebufferBinding();
if (m_pixelUnpackBufferBindingDirty)
client->DrawingBufferClientRestorePixelUnpackBufferBinding();
}
bool DrawingBuffer::shouldUseChromiumImage() {
return RuntimeEnabledFeatures::webGLImageChromiumEnabled() &&
m_chromiumImageUsage == AllowChromiumImage;
}
} // namespace blink