mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
cfa8fd3606
--HG-- extra : rebase_source : 66361f31c12774ab7d2bbb9e66a4e673f2a272ba
1428 lines
44 KiB
C++
1428 lines
44 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/layers/PLayers.h"
|
|
|
|
/* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
|
|
#include "mozilla/Util.h"
|
|
|
|
#include "LayerManagerOGL.h"
|
|
#include "ThebesLayerOGL.h"
|
|
#include "ContainerLayerOGL.h"
|
|
#include "ImageLayerOGL.h"
|
|
#include "ColorLayerOGL.h"
|
|
#include "CanvasLayerOGL.h"
|
|
#include "TiledThebesLayerOGL.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "TexturePoolOGL.h"
|
|
|
|
#include "gfxContext.h"
|
|
#include "gfxUtils.h"
|
|
#include "gfxPlatform.h"
|
|
#include "nsIWidget.h"
|
|
|
|
#include "GLContext.h"
|
|
#include "GLContextProvider.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIConsoleService.h"
|
|
|
|
#include "gfxCrashReporterUtils.h"
|
|
|
|
#include "sampler.h"
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
#include <android/log.h>
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::gl;
|
|
|
|
#ifdef CHECK_CURRENT_PROGRAM
|
|
int ShaderProgramOGL::sCurrentProgramKey = 0;
|
|
#endif
|
|
|
|
static const double kFpsWindowMs = 250.0;
|
|
static const size_t kNumFrameTimeStamps = 16;
|
|
struct FPSCounter {
|
|
FPSCounter() : mCurrentFrameIndex(0) {}
|
|
|
|
// We keep a circular buffer of the time points at which the last K
|
|
// frames were drawn. To estimate FPS, we count the number of
|
|
// frames we've drawn within the last kFPSWindowMs milliseconds and
|
|
// divide by the amount time since the first of those frames.
|
|
TimeStamp mFrames[kNumFrameTimeStamps];
|
|
size_t mCurrentFrameIndex;
|
|
|
|
void AddFrame(TimeStamp aNewFrame) {
|
|
mFrames[mCurrentFrameIndex] = aNewFrame;
|
|
mCurrentFrameIndex = (mCurrentFrameIndex + 1) % kNumFrameTimeStamps;
|
|
}
|
|
|
|
double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
|
|
AddFrame(aCurrentFrame);
|
|
return EstimateFps(aCurrentFrame);
|
|
}
|
|
|
|
double GetFpsAt(TimeStamp aNow) {
|
|
return EstimateFps(aNow);
|
|
}
|
|
|
|
private:
|
|
double EstimateFps(TimeStamp aNow) {
|
|
TimeStamp beginningOfWindow =
|
|
(aNow - TimeDuration::FromMilliseconds(kFpsWindowMs));
|
|
TimeStamp earliestFrameInWindow = aNow;
|
|
size_t numFramesDrawnInWindow = 0;
|
|
for (size_t i = 0; i < kNumFrameTimeStamps; ++i) {
|
|
const TimeStamp& frame = mFrames[i];
|
|
if (!frame.IsNull() && frame > beginningOfWindow) {
|
|
++numFramesDrawnInWindow;
|
|
earliestFrameInWindow = PR_MIN(earliestFrameInWindow, frame);
|
|
}
|
|
}
|
|
double realWindowSecs = (aNow - earliestFrameInWindow).ToSeconds();
|
|
if (realWindowSecs == 0.0 || numFramesDrawnInWindow == 1) {
|
|
return 0.0;
|
|
}
|
|
return double(numFramesDrawnInWindow - 1) / realWindowSecs;
|
|
}
|
|
};
|
|
|
|
struct FPSState {
|
|
GLuint mTexture;
|
|
FPSCounter mCompositionFps;
|
|
FPSCounter mTransactionFps;
|
|
|
|
FPSState() : mTexture(0) { }
|
|
|
|
void DrawFPS(TimeStamp, GLContext*, ShaderProgramOGL*);
|
|
|
|
static void DrawFrameCounter(GLContext* context);
|
|
|
|
void NotifyShadowTreeTransaction() {
|
|
mTransactionFps.AddFrame(TimeStamp::Now());
|
|
}
|
|
};
|
|
|
|
|
|
void
|
|
FPSState::DrawFPS(TimeStamp aNow,
|
|
GLContext* context, ShaderProgramOGL* copyprog)
|
|
{
|
|
int fps = int(mCompositionFps.AddFrameAndGetFps(aNow));
|
|
int txnFps = int(mTransactionFps.GetFpsAt(aNow));
|
|
|
|
GLint viewport[4];
|
|
context->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
|
|
|
|
if (!mTexture) {
|
|
// Bind the number of textures we need, in this case one.
|
|
context->fGenTextures(1, &mTexture);
|
|
context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
|
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MIN_FILTER,LOCAL_GL_NEAREST);
|
|
context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MAG_FILTER,LOCAL_GL_NEAREST);
|
|
|
|
unsigned char text[] = {
|
|
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, 255, 255, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
|
|
0, 255, 0, 255, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0,
|
|
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
|
|
0, 255, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 255, 0,
|
|
0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 0, 0, 255, 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,
|
|
};
|
|
|
|
// convert from 8 bit to 32 bit so that don't have to write the text above out in 32 bit format
|
|
// we rely on int being 32 bits
|
|
unsigned int* buf = (unsigned int*)malloc(64 * 8 * 4);
|
|
for (int i = 0; i < 7; i++) {
|
|
for (int j = 0; j < 41; j++) {
|
|
unsigned int purple = 0xfff000ff;
|
|
unsigned int white = 0xffffffff;
|
|
buf[i * 64 + j] = (text[i * 41 + j] == 0) ? purple : white;
|
|
}
|
|
}
|
|
context->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 64, 8, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf);
|
|
free(buf);
|
|
}
|
|
|
|
struct Vertex2D {
|
|
float x,y;
|
|
};
|
|
const Vertex2D vertices[] = {
|
|
{ -1.0f, 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f, 1.0f},
|
|
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 22.f / viewport[2], 1.0f },
|
|
|
|
{ -1.0f + 22.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 22.f / viewport[2], 1.0f },
|
|
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 44.f / viewport[2], 1.0f },
|
|
|
|
{ -1.0f + 44.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 44.f / viewport[2], 1.0f },
|
|
{ -1.0f + 66.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 66.f / viewport[2], 1.0f }
|
|
};
|
|
|
|
const Vertex2D vertices2[] = {
|
|
{ -1.0f + 80.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 80.f / viewport[2], 1.0f },
|
|
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 102.f / viewport[2], 1.0f },
|
|
|
|
{ -1.0f + 102.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 102.f / viewport[2], 1.0f },
|
|
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 124.f / viewport[2], 1.0f },
|
|
|
|
{ -1.0f + 124.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 124.f / viewport[2], 1.0f },
|
|
{ -1.0f + 146.f / viewport[2], 1.0f - 42.f / viewport[3] },
|
|
{ -1.0f + 146.f / viewport[2], 1.0f },
|
|
};
|
|
|
|
int v1 = fps % 10;
|
|
int v10 = (fps % 100) / 10;
|
|
int v100 = (fps % 1000) / 100;
|
|
|
|
int txn1 = txnFps % 10;
|
|
int txn10 = (txnFps % 100) / 10;
|
|
int txn100 = (txnFps % 1000) / 100;
|
|
|
|
// Feel free to comment these texture coordinates out and use one
|
|
// of the ones below instead, or play around with your own values.
|
|
const GLfloat texCoords[] = {
|
|
(v100 * 4.f) / 64, 7.f / 8,
|
|
(v100 * 4.f) / 64, 0.0f,
|
|
(v100 * 4.f + 4) / 64, 7.f / 8,
|
|
(v100 * 4.f + 4) / 64, 0.0f,
|
|
|
|
(v10 * 4.f) / 64, 7.f / 8,
|
|
(v10 * 4.f) / 64, 0.0f,
|
|
(v10 * 4.f + 4) / 64, 7.f / 8,
|
|
(v10 * 4.f + 4) / 64, 0.0f,
|
|
|
|
(v1 * 4.f) / 64, 7.f / 8,
|
|
(v1 * 4.f) / 64, 0.0f,
|
|
(v1 * 4.f + 4) / 64, 7.f / 8,
|
|
(v1 * 4.f + 4) / 64, 0.0f,
|
|
};
|
|
|
|
const GLfloat texCoords2[] = {
|
|
(txn100 * 4.f) / 64, 7.f / 8,
|
|
(txn100 * 4.f) / 64, 0.0f,
|
|
(txn100 * 4.f + 4) / 64, 7.f / 8,
|
|
(txn100 * 4.f + 4) / 64, 0.0f,
|
|
|
|
(txn10 * 4.f) / 64, 7.f / 8,
|
|
(txn10 * 4.f) / 64, 0.0f,
|
|
(txn10 * 4.f + 4) / 64, 7.f / 8,
|
|
(txn10 * 4.f + 4) / 64, 0.0f,
|
|
|
|
(txn1 * 4.f) / 64, 7.f / 8,
|
|
(txn1 * 4.f) / 64, 0.0f,
|
|
(txn1 * 4.f + 4) / 64, 7.f / 8,
|
|
(txn1 * 4.f + 4) / 64, 0.0f,
|
|
};
|
|
|
|
// Turn necessary features on
|
|
context->fEnable(LOCAL_GL_BLEND);
|
|
context->fBlendFunc(LOCAL_GL_ONE, LOCAL_GL_SRC_COLOR);
|
|
|
|
context->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
|
|
|
copyprog->Activate();
|
|
copyprog->SetTextureUnit(0);
|
|
|
|
// we're going to use client-side vertex arrays for this.
|
|
context->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
|
|
// "COPY"
|
|
context->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
|
|
LOCAL_GL_ONE, LOCAL_GL_ZERO);
|
|
|
|
// enable our vertex attribs; we'll call glVertexPointer below
|
|
// to fill with the correct data.
|
|
GLint vcattr = copyprog->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
|
|
GLint tcattr = copyprog->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
|
|
|
|
context->fEnableVertexAttribArray(vcattr);
|
|
context->fEnableVertexAttribArray(tcattr);
|
|
|
|
context->fVertexAttribPointer(vcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, vertices);
|
|
|
|
context->fVertexAttribPointer(tcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, texCoords);
|
|
|
|
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
|
|
|
|
context->fVertexAttribPointer(vcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, vertices2);
|
|
|
|
context->fVertexAttribPointer(tcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, texCoords2);
|
|
|
|
context->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 12);
|
|
}
|
|
|
|
/**
|
|
* LayerManagerOGL
|
|
*/
|
|
LayerManagerOGL::LayerManagerOGL(nsIWidget *aWidget, int aSurfaceWidth, int aSurfaceHeight,
|
|
bool aIsRenderingToEGLSurface)
|
|
: mWidget(aWidget)
|
|
, mWidgetSize(-1, -1)
|
|
, mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
|
|
, mBackBufferFBO(0)
|
|
, mBackBufferTexture(0)
|
|
, mBackBufferSize(-1, -1)
|
|
, mHasBGRA(0)
|
|
, mIsRenderingToEGLSurface(aIsRenderingToEGLSurface)
|
|
{
|
|
}
|
|
|
|
LayerManagerOGL::~LayerManagerOGL()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::Destroy()
|
|
{
|
|
if (!mDestroyed) {
|
|
if (mRoot) {
|
|
RootLayer()->Destroy();
|
|
}
|
|
mRoot = nullptr;
|
|
|
|
CleanupResources();
|
|
|
|
mDestroyed = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::CleanupResources()
|
|
{
|
|
if (!mGLContext)
|
|
return;
|
|
|
|
if (mRoot) {
|
|
RootLayer()->CleanupResources();
|
|
}
|
|
|
|
nsRefPtr<GLContext> ctx = mGLContext->GetSharedContext();
|
|
if (!ctx) {
|
|
ctx = mGLContext;
|
|
}
|
|
|
|
ctx->MakeCurrent();
|
|
|
|
for (uint32_t i = 0; i < mPrograms.Length(); ++i) {
|
|
for (uint32_t type = MaskNone; type < NumMaskTypes; ++type) {
|
|
delete mPrograms[i].mVariations[type];
|
|
}
|
|
}
|
|
mPrograms.Clear();
|
|
|
|
ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
|
|
|
if (mBackBufferFBO) {
|
|
ctx->fDeleteFramebuffers(1, &mBackBufferFBO);
|
|
mBackBufferFBO = 0;
|
|
}
|
|
|
|
if (mBackBufferTexture) {
|
|
ctx->fDeleteTextures(1, &mBackBufferTexture);
|
|
mBackBufferTexture = 0;
|
|
}
|
|
|
|
if (mQuadVBO) {
|
|
ctx->fDeleteBuffers(1, &mQuadVBO);
|
|
mQuadVBO = 0;
|
|
}
|
|
|
|
mGLContext = nullptr;
|
|
}
|
|
|
|
already_AddRefed<mozilla::gl::GLContext>
|
|
LayerManagerOGL::CreateContext()
|
|
{
|
|
nsRefPtr<GLContext> context;
|
|
|
|
#ifdef XP_WIN
|
|
if (PR_GetEnv("MOZ_LAYERS_PREFER_EGL")) {
|
|
printf_stderr("Trying GL layers...\n");
|
|
context = gl::GLContextProviderEGL::CreateForWindow(mWidget);
|
|
}
|
|
#endif
|
|
|
|
if (!context)
|
|
context = gl::GLContextProvider::CreateForWindow(mWidget);
|
|
|
|
if (!context) {
|
|
NS_WARNING("Failed to create LayerManagerOGL context");
|
|
}
|
|
return context.forget();
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::AddPrograms(ShaderProgramType aType)
|
|
{
|
|
for (uint32_t maskType = MaskNone; maskType < NumMaskTypes; ++maskType) {
|
|
if (ProgramProfileOGL::ProgramExists(aType, static_cast<MaskType>(maskType))) {
|
|
mPrograms[aType].mVariations[maskType] = new ShaderProgramOGL(this->gl(),
|
|
ProgramProfileOGL::GetProfileFor(aType, static_cast<MaskType>(maskType)));
|
|
} else {
|
|
mPrograms[aType].mVariations[maskType] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayerManagerOGL::Initialize(nsRefPtr<GLContext> aContext, bool force)
|
|
{
|
|
ScopedGfxFeatureReporter reporter("GL Layers", force);
|
|
|
|
// Do not allow double initialization
|
|
NS_ABORT_IF_FALSE(mGLContext == nullptr, "Don't reinitialize layer managers");
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
if (!aContext)
|
|
NS_RUNTIMEABORT("We need a context on Android");
|
|
#endif
|
|
|
|
if (!aContext)
|
|
return false;
|
|
|
|
mGLContext = aContext;
|
|
mGLContext->SetFlipped(true);
|
|
|
|
MakeCurrent();
|
|
|
|
mHasBGRA =
|
|
mGLContext->IsExtensionSupported(gl::GLContext::EXT_texture_format_BGRA8888) ||
|
|
mGLContext->IsExtensionSupported(gl::GLContext::EXT_bgra);
|
|
|
|
mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
|
|
LOCAL_GL_ONE, LOCAL_GL_ONE);
|
|
mGLContext->fEnable(LOCAL_GL_BLEND);
|
|
|
|
mPrograms.AppendElements(NumProgramTypes);
|
|
for (int type = 0; type < NumProgramTypes; ++type) {
|
|
AddPrograms(static_cast<ShaderProgramType>(type));
|
|
}
|
|
|
|
// initialise a common shader to check that we can actually compile a shader
|
|
if (!mPrograms[gl::RGBALayerProgramType].mVariations[MaskNone]->Initialize()) {
|
|
return false;
|
|
}
|
|
|
|
|
|
mGLContext->fGenFramebuffers(1, &mBackBufferFBO);
|
|
|
|
if (mGLContext->WorkAroundDriverBugs()) {
|
|
|
|
/**
|
|
* We'll test the ability here to bind NPOT textures to a framebuffer, if
|
|
* this fails we'll try ARB_texture_rectangle.
|
|
*/
|
|
|
|
GLenum textureTargets[] = {
|
|
LOCAL_GL_TEXTURE_2D,
|
|
LOCAL_GL_NONE
|
|
};
|
|
|
|
if (mGLContext->IsGLES2()) {
|
|
textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
|
|
}
|
|
|
|
mFBOTextureTarget = LOCAL_GL_NONE;
|
|
|
|
for (uint32_t i = 0; i < ArrayLength(textureTargets); i++) {
|
|
GLenum target = textureTargets[i];
|
|
if (!target)
|
|
continue;
|
|
|
|
mGLContext->fGenTextures(1, &mBackBufferTexture);
|
|
mGLContext->fBindTexture(target, mBackBufferTexture);
|
|
mGLContext->fTexParameteri(target,
|
|
LOCAL_GL_TEXTURE_MIN_FILTER,
|
|
LOCAL_GL_NEAREST);
|
|
mGLContext->fTexParameteri(target,
|
|
LOCAL_GL_TEXTURE_MAG_FILTER,
|
|
LOCAL_GL_NEAREST);
|
|
mGLContext->fTexImage2D(target,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
5, 3, /* sufficiently NPOT */
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
LOCAL_GL_UNSIGNED_BYTE,
|
|
NULL);
|
|
|
|
// unbind this texture, in preparation for binding it to the FBO
|
|
mGLContext->fBindTexture(target, 0);
|
|
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackBufferFBO);
|
|
mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
|
|
LOCAL_GL_COLOR_ATTACHMENT0,
|
|
target,
|
|
mBackBufferTexture,
|
|
0);
|
|
|
|
if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) ==
|
|
LOCAL_GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
mFBOTextureTarget = target;
|
|
break;
|
|
}
|
|
|
|
// We weren't succesful with this texture, so we don't need it
|
|
// any more.
|
|
mGLContext->fDeleteTextures(1, &mBackBufferTexture);
|
|
}
|
|
|
|
if (mFBOTextureTarget == LOCAL_GL_NONE) {
|
|
/* Unable to find a texture target that works with FBOs and NPOT textures */
|
|
return false;
|
|
}
|
|
} else {
|
|
// not trying to work around driver bugs, so TEXTURE_2D should just work
|
|
mFBOTextureTarget = LOCAL_GL_TEXTURE_2D;
|
|
}
|
|
|
|
// back to default framebuffer, to avoid confusion
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
|
|
|
if (mFBOTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
|
|
/* If we're using TEXTURE_RECTANGLE, then we must have the ARB
|
|
* extension -- the EXT variant does not provide support for
|
|
* texture rectangle access inside GLSL (sampler2DRect,
|
|
* texture2DRect).
|
|
*/
|
|
if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle))
|
|
return false;
|
|
}
|
|
|
|
// If we're double-buffered, we don't need this fbo anymore.
|
|
if (mGLContext->IsDoubleBuffered()) {
|
|
mGLContext->fDeleteFramebuffers(1, &mBackBufferFBO);
|
|
mBackBufferFBO = 0;
|
|
}
|
|
|
|
/* Create a simple quad VBO */
|
|
|
|
mGLContext->fGenBuffers(1, &mQuadVBO);
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
|
|
|
|
GLfloat vertices[] = {
|
|
/* First quad vertices */
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
|
|
/* Then quad texcoords */
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
|
|
/* Then flipped quad texcoords */
|
|
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
|
};
|
|
mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, sizeof(vertices), vertices, LOCAL_GL_STATIC_DRAW);
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
|
|
nsCOMPtr<nsIConsoleService>
|
|
console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
|
|
|
if (console) {
|
|
nsString msg;
|
|
msg +=
|
|
NS_LITERAL_STRING("OpenGL LayerManager Initialized Succesfully.\nVersion: ");
|
|
msg += NS_ConvertUTF8toUTF16(
|
|
nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VERSION)));
|
|
msg += NS_LITERAL_STRING("\nVendor: ");
|
|
msg += NS_ConvertUTF8toUTF16(
|
|
nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VENDOR)));
|
|
msg += NS_LITERAL_STRING("\nRenderer: ");
|
|
msg += NS_ConvertUTF8toUTF16(
|
|
nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_RENDERER)));
|
|
msg += NS_LITERAL_STRING("\nFBO Texture Target: ");
|
|
if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D)
|
|
msg += NS_LITERAL_STRING("TEXTURE_2D");
|
|
else
|
|
msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
|
|
console->LogStringMessage(msg.get());
|
|
}
|
|
|
|
if (NS_IsMainThread()) {
|
|
Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
|
|
Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter");
|
|
} else {
|
|
// We have to dispatch an event to the main thread to read the pref.
|
|
class ReadDrawFPSPref : public nsRunnable {
|
|
public:
|
|
NS_IMETHOD Run()
|
|
{
|
|
Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
|
|
Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter");
|
|
return NS_OK;
|
|
}
|
|
};
|
|
NS_DispatchToMainThread(new ReadDrawFPSPref());
|
|
}
|
|
|
|
reporter.SetSuccessful();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetClippingRegion(const nsIntRegion& aClippingRegion)
|
|
{
|
|
mClippingRegion = aClippingRegion;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::BeginTransaction()
|
|
{
|
|
mInTransaction = true;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::BeginTransactionWithTarget(gfxContext *aTarget)
|
|
{
|
|
mInTransaction = true;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
|
|
Log();
|
|
#endif
|
|
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return;
|
|
}
|
|
|
|
mTarget = aTarget;
|
|
}
|
|
|
|
bool
|
|
LayerManagerOGL::EndEmptyTransaction(EndTransactionFlags aFlags)
|
|
{
|
|
mInTransaction = false;
|
|
|
|
if (!mRoot)
|
|
return false;
|
|
|
|
EndTransaction(nullptr, nullptr, aFlags);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::EndTransaction(DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData,
|
|
EndTransactionFlags aFlags)
|
|
{
|
|
mInTransaction = false;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
|
|
Log();
|
|
#endif
|
|
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return;
|
|
}
|
|
|
|
if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
|
|
// The results of our drawing always go directly into a pixel buffer,
|
|
// so we don't need to pass any global transform here.
|
|
mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
|
|
|
|
mThebesLayerCallback = aCallback;
|
|
mThebesLayerCallbackData = aCallbackData;
|
|
SetCompositingDisabled(aFlags & END_NO_COMPOSITE);
|
|
|
|
Render();
|
|
|
|
mThebesLayerCallback = nullptr;
|
|
mThebesLayerCallbackData = nullptr;
|
|
}
|
|
|
|
mTarget = NULL;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
Log();
|
|
MOZ_LAYERS_LOG(("]----- EndTransaction"));
|
|
#endif
|
|
}
|
|
|
|
already_AddRefed<gfxASurface>
|
|
LayerManagerOGL::CreateOptimalMaskSurface(const gfxIntSize &aSize)
|
|
{
|
|
return gfxPlatform::GetPlatform()->
|
|
CreateOffscreenImageSurface(aSize, gfxASurface::CONTENT_ALPHA);
|
|
}
|
|
|
|
already_AddRefed<ThebesLayer>
|
|
LayerManagerOGL::CreateThebesLayer()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
nsRefPtr<ThebesLayer> layer = new ThebesLayerOGL(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
already_AddRefed<ContainerLayer>
|
|
LayerManagerOGL::CreateContainerLayer()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
nsRefPtr<ContainerLayer> layer = new ContainerLayerOGL(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
already_AddRefed<ImageLayer>
|
|
LayerManagerOGL::CreateImageLayer()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
nsRefPtr<ImageLayer> layer = new ImageLayerOGL(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
already_AddRefed<ColorLayer>
|
|
LayerManagerOGL::CreateColorLayer()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
nsRefPtr<ColorLayer> layer = new ColorLayerOGL(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
already_AddRefed<CanvasLayer>
|
|
LayerManagerOGL::CreateCanvasLayer()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
nsRefPtr<CanvasLayer> layer = new CanvasLayerOGL(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
LayerOGL*
|
|
LayerManagerOGL::RootLayer() const
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
return static_cast<LayerOGL*>(mRoot->ImplData());
|
|
}
|
|
|
|
bool LayerManagerOGL::sDrawFPS = false;
|
|
bool LayerManagerOGL::sFrameCounter = false;
|
|
|
|
static uint16_t sFrameCount = 0;
|
|
void
|
|
FPSState::DrawFrameCounter(GLContext* context)
|
|
{
|
|
SAMPLER_FRAME_NUMBER(sFrameCount);
|
|
|
|
context->fEnable(LOCAL_GL_SCISSOR_TEST);
|
|
|
|
uint16_t frameNumber = sFrameCount;
|
|
for (size_t i = 0; i < 16; i++) {
|
|
context->fScissor(3*i, 0, 3, 3);
|
|
|
|
// We should do this using a single draw call
|
|
// instead of 16 glClear()
|
|
if ((frameNumber >> i) & 0x1) {
|
|
context->fClearColor(0.0, 0.0, 0.0, 0.0);
|
|
} else {
|
|
context->fClearColor(1.0, 1.0, 1.0, 0.0);
|
|
}
|
|
context->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
|
|
}
|
|
// We intentionally overflow at 2^16.
|
|
sFrameCount++;
|
|
}
|
|
|
|
// |aTexCoordRect| is the rectangle from the texture that we want to
|
|
// draw using the given program. The program already has a necessary
|
|
// offset and scale, so the geometry that needs to be drawn is a unit
|
|
// square from 0,0 to 1,1.
|
|
//
|
|
// |aTexSize| is the actual size of the texture, as it can be larger
|
|
// than the rectangle given by |aTexCoordRect|.
|
|
void
|
|
LayerManagerOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
|
|
const nsIntRect& aTexCoordRect,
|
|
const nsIntSize& aTexSize,
|
|
GLenum aWrapMode /* = LOCAL_GL_REPEAT */,
|
|
bool aFlipped /* = false */)
|
|
{
|
|
NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
|
|
GLuint vertAttribIndex =
|
|
aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
|
|
GLuint texCoordAttribIndex =
|
|
aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
|
|
NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?");
|
|
|
|
// clear any bound VBO so that glVertexAttribPointer() goes back to
|
|
// "pointer mode"
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
|
|
// Given what we know about these textures and coordinates, we can
|
|
// compute fmod(t, 1.0f) to get the same texture coordinate out. If
|
|
// the texCoordRect dimension is < 0 or > width/height, then we have
|
|
// wraparound that we need to deal with by drawing multiple quads,
|
|
// because we can't rely on full non-power-of-two texture support
|
|
// (which is required for the REPEAT wrap mode).
|
|
|
|
GLContext::RectTriangles rects;
|
|
|
|
nsIntSize realTexSize = aTexSize;
|
|
if (!mGLContext->CanUploadNonPowerOfTwo()) {
|
|
realTexSize = nsIntSize(NextPowerOfTwo(aTexSize.width),
|
|
NextPowerOfTwo(aTexSize.height));
|
|
}
|
|
|
|
if (aWrapMode == LOCAL_GL_REPEAT) {
|
|
rects.addRect(/* dest rectangle */
|
|
0.0f, 0.0f, 1.0f, 1.0f,
|
|
/* tex coords */
|
|
aTexCoordRect.x / GLfloat(realTexSize.width),
|
|
aTexCoordRect.y / GLfloat(realTexSize.height),
|
|
aTexCoordRect.XMost() / GLfloat(realTexSize.width),
|
|
aTexCoordRect.YMost() / GLfloat(realTexSize.height),
|
|
aFlipped);
|
|
} else {
|
|
GLContext::DecomposeIntoNoRepeatTriangles(aTexCoordRect, realTexSize,
|
|
rects, aFlipped);
|
|
}
|
|
|
|
mGLContext->fVertexAttribPointer(vertAttribIndex, 2,
|
|
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
|
|
rects.vertexPointer());
|
|
|
|
mGLContext->fVertexAttribPointer(texCoordAttribIndex, 2,
|
|
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
|
|
rects.texCoordPointer());
|
|
|
|
{
|
|
mGLContext->fEnableVertexAttribArray(texCoordAttribIndex);
|
|
{
|
|
mGLContext->fEnableVertexAttribArray(vertAttribIndex);
|
|
|
|
mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
|
|
|
|
mGLContext->fDisableVertexAttribArray(vertAttribIndex);
|
|
}
|
|
mGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::NotifyShadowTreeTransaction()
|
|
{
|
|
if (mFPS) {
|
|
mFPS->NotifyShadowTreeTransaction();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::Render()
|
|
{
|
|
SAMPLE_LABEL("LayerManagerOGL", "Render");
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return;
|
|
}
|
|
|
|
nsIntRect rect;
|
|
if (mIsRenderingToEGLSurface) {
|
|
rect = nsIntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
|
|
} else {
|
|
mWidget->GetClientBounds(rect);
|
|
}
|
|
WorldTransformRect(rect);
|
|
|
|
GLint width = rect.width;
|
|
GLint height = rect.height;
|
|
|
|
// We can't draw anything to something with no area
|
|
// so just return
|
|
if (width == 0 || height == 0)
|
|
return;
|
|
|
|
// If the widget size changed, we have to force a MakeCurrent
|
|
// to make sure that GL sees the updated widget size.
|
|
if (mWidgetSize.width != width ||
|
|
mWidgetSize.height != height)
|
|
{
|
|
MakeCurrent(true);
|
|
|
|
mWidgetSize.width = width;
|
|
mWidgetSize.height = height;
|
|
} else {
|
|
MakeCurrent();
|
|
}
|
|
|
|
#if MOZ_WIDGET_ANDROID
|
|
TexturePoolOGL::Fill(gl());
|
|
#endif
|
|
|
|
SetupBackBuffer(width, height);
|
|
SetupPipeline(width, height, ApplyWorldTransform);
|
|
|
|
// Default blend function implements "OVER"
|
|
mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
|
|
LOCAL_GL_ONE, LOCAL_GL_ONE);
|
|
mGLContext->fEnable(LOCAL_GL_BLEND);
|
|
|
|
const nsIntRect *clipRect = mRoot->GetClipRect();
|
|
|
|
if (clipRect) {
|
|
nsIntRect r = *clipRect;
|
|
WorldTransformRect(r);
|
|
mGLContext->fScissor(r.x, r.y, r.width, r.height);
|
|
} else {
|
|
mGLContext->fScissor(0, 0, width, height);
|
|
}
|
|
|
|
if (CompositingDisabled()) {
|
|
RootLayer()->RenderLayer(mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO,
|
|
nsIntPoint(0, 0));
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
return;
|
|
}
|
|
|
|
mGLContext->fEnable(LOCAL_GL_SCISSOR_TEST);
|
|
|
|
// If the Java compositor is being used, this clear will be done in
|
|
// DrawWindowUnderlay. Make sure the bits used here match up with those used
|
|
// in mobile/android/base/gfx/LayerRenderer.java
|
|
#ifndef MOZ_JAVA_COMPOSITOR
|
|
mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
|
|
mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
|
|
#endif
|
|
|
|
// Allow widget to render a custom background.
|
|
mWidget->DrawWindowUnderlay(this, rect);
|
|
|
|
// Render our layers.
|
|
RootLayer()->RenderLayer(mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO,
|
|
nsIntPoint(0, 0));
|
|
|
|
// Allow widget to render a custom foreground too.
|
|
mWidget->DrawWindowOverlay(this, rect);
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (gfxUtils::sDumpPainting) {
|
|
nsIntRect rect;
|
|
if (mIsRenderingToEGLSurface) {
|
|
rect = nsIntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
|
|
} else {
|
|
mWidget->GetBounds(rect);
|
|
}
|
|
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(rect.Size(), gfxASurface::CONTENT_COLOR_ALPHA);
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(surf);
|
|
CopyToTarget(ctx);
|
|
|
|
WriteSnapshotToDumpFile(this, surf);
|
|
}
|
|
#endif
|
|
|
|
if (mTarget) {
|
|
CopyToTarget(mTarget);
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
return;
|
|
}
|
|
|
|
if (sDrawFPS && !mFPS) {
|
|
mFPS = new FPSState();
|
|
} else if (!sDrawFPS && mFPS) {
|
|
mFPS = nullptr;
|
|
}
|
|
|
|
if (mFPS) {
|
|
mFPS->DrawFPS(TimeStamp::Now(), mGLContext, GetProgram(Copy2DProgramType));
|
|
} else if (sFrameCounter) {
|
|
FPSState::DrawFrameCounter(mGLContext);
|
|
}
|
|
|
|
if (mGLContext->IsDoubleBuffered()) {
|
|
mGLContext->SwapBuffers();
|
|
LayerManager::PostPresent();
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
return;
|
|
}
|
|
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
|
|
|
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
|
|
ShaderProgramOGL *copyprog = GetProgram(Copy2DProgramType);
|
|
|
|
if (mFBOTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
|
|
copyprog = GetProgram(Copy2DRectProgramType);
|
|
}
|
|
|
|
mGLContext->fBindTexture(mFBOTextureTarget, mBackBufferTexture);
|
|
|
|
copyprog->Activate();
|
|
copyprog->SetTextureUnit(0);
|
|
|
|
if (copyprog->GetTexCoordMultiplierUniformLocation() != -1) {
|
|
copyprog->SetTexCoordMultiplier(width, height);
|
|
}
|
|
|
|
// we're going to use client-side vertex arrays for this.
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
|
|
// "COPY"
|
|
mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
|
|
LOCAL_GL_ONE, LOCAL_GL_ZERO);
|
|
|
|
// enable our vertex attribs; we'll call glVertexPointer below
|
|
// to fill with the correct data.
|
|
GLint vcattr = copyprog->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
|
|
GLint tcattr = copyprog->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
|
|
|
|
mGLContext->fEnableVertexAttribArray(vcattr);
|
|
mGLContext->fEnableVertexAttribArray(tcattr);
|
|
|
|
const nsIntRect *r;
|
|
nsIntRegionRectIterator iter(mClippingRegion);
|
|
|
|
while ((r = iter.Next()) != nullptr) {
|
|
nsIntRect cRect = *r; r = &cRect;
|
|
WorldTransformRect(cRect);
|
|
float left = (GLfloat)r->x / width;
|
|
float right = (GLfloat)r->XMost() / width;
|
|
float top = (GLfloat)r->y / height;
|
|
float bottom = (GLfloat)r->YMost() / height;
|
|
|
|
float vertices[] = { left * 2.0f - 1.0f,
|
|
-(top * 2.0f - 1.0f),
|
|
right * 2.0f - 1.0f,
|
|
-(top * 2.0f - 1.0f),
|
|
left * 2.0f - 1.0f,
|
|
-(bottom * 2.0f - 1.0f),
|
|
right * 2.0f - 1.0f,
|
|
-(bottom * 2.0f - 1.0f) };
|
|
|
|
// Use inverted texture coordinates since our projection matrix also has a
|
|
// flip and we need to cancel that out.
|
|
float coords[] = { left, 1 - top,
|
|
right, 1 - top,
|
|
left, 1 - bottom,
|
|
right, 1 - bottom };
|
|
|
|
mGLContext->fVertexAttribPointer(vcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, vertices);
|
|
|
|
mGLContext->fVertexAttribPointer(tcattr,
|
|
2, LOCAL_GL_FLOAT,
|
|
LOCAL_GL_FALSE,
|
|
0, coords);
|
|
|
|
mGLContext->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
|
|
}
|
|
|
|
mGLContext->fDisableVertexAttribArray(vcattr);
|
|
mGLContext->fDisableVertexAttribArray(tcattr);
|
|
|
|
mGLContext->fFlush();
|
|
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetWorldTransform(const gfxMatrix& aMatrix)
|
|
{
|
|
NS_ASSERTION(aMatrix.PreservesAxisAlignedRectangles(),
|
|
"SetWorldTransform only accepts matrices that satisfy PreservesAxisAlignedRectangles");
|
|
NS_ASSERTION(!aMatrix.HasNonIntegerScale(),
|
|
"SetWorldTransform only accepts matrices with integer scale");
|
|
|
|
mWorldMatrix = aMatrix;
|
|
}
|
|
|
|
gfxMatrix&
|
|
LayerManagerOGL::GetWorldTransform(void)
|
|
{
|
|
return mWorldMatrix;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::WorldTransformRect(nsIntRect& aRect)
|
|
{
|
|
gfxRect grect(aRect.x, aRect.y, aRect.width, aRect.height);
|
|
grect = mWorldMatrix.TransformBounds(grect);
|
|
aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height());
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetSurfaceSize(int width, int height)
|
|
{
|
|
mSurfaceSize.width = width;
|
|
mSurfaceSize.height = height;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetupPipeline(int aWidth, int aHeight, WorldTransforPolicy aTransformPolicy)
|
|
{
|
|
// Set the viewport correctly.
|
|
mGLContext->fViewport(0, 0, aWidth, aHeight);
|
|
|
|
// We flip the view matrix around so that everything is right-side up; we're
|
|
// drawing directly into the window's back buffer, so this keeps things
|
|
// looking correct.
|
|
//
|
|
// XXX: We keep track of whether the window size changed, so we could skip
|
|
// this update if it hadn't changed since the last call. We will need to
|
|
// track changes to aTransformPolicy and mWorldMatrix for this to work
|
|
// though.
|
|
|
|
// Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
|
|
// 2, 2) and flip the contents.
|
|
gfxMatrix viewMatrix;
|
|
viewMatrix.Translate(-gfxPoint(1.0, -1.0));
|
|
viewMatrix.Scale(2.0f / float(aWidth), 2.0f / float(aHeight));
|
|
viewMatrix.Scale(1.0f, -1.0f);
|
|
|
|
if (aTransformPolicy == ApplyWorldTransform) {
|
|
viewMatrix = mWorldMatrix * viewMatrix;
|
|
}
|
|
|
|
gfx3DMatrix matrix3d = gfx3DMatrix::From2D(viewMatrix);
|
|
matrix3d._33 = 0.0f;
|
|
|
|
SetLayerProgramProjectionMatrix(matrix3d);
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetupBackBuffer(int aWidth, int aHeight)
|
|
{
|
|
if (mGLContext->IsDoubleBuffered()) {
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
|
return;
|
|
}
|
|
|
|
// Do we have a FBO of the right size already?
|
|
if (mBackBufferSize.width == aWidth &&
|
|
mBackBufferSize.height == aHeight)
|
|
{
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackBufferFBO);
|
|
return;
|
|
}
|
|
|
|
// we already have a FBO, but we need to resize its texture.
|
|
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
mGLContext->fBindTexture(mFBOTextureTarget, mBackBufferTexture);
|
|
mGLContext->fTexImage2D(mFBOTextureTarget,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
aWidth, aHeight,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
LOCAL_GL_UNSIGNED_BYTE,
|
|
NULL);
|
|
mGLContext->fBindTexture(mFBOTextureTarget, 0);
|
|
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackBufferFBO);
|
|
mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
|
|
LOCAL_GL_COLOR_ATTACHMENT0,
|
|
mFBOTextureTarget,
|
|
mBackBufferTexture,
|
|
0);
|
|
|
|
GLenum result = mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
|
|
if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
|
|
nsAutoCString msg;
|
|
msg.Append("Framebuffer not complete -- error 0x");
|
|
msg.AppendInt(result, 16);
|
|
NS_RUNTIMEABORT(msg.get());
|
|
}
|
|
|
|
mBackBufferSize.width = aWidth;
|
|
mBackBufferSize.height = aHeight;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::CopyToTarget(gfxContext *aTarget)
|
|
{
|
|
nsIntRect rect;
|
|
if (mIsRenderingToEGLSurface) {
|
|
rect = nsIntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
|
|
} else {
|
|
mWidget->GetBounds(rect);
|
|
}
|
|
GLint width = rect.width;
|
|
GLint height = rect.height;
|
|
|
|
if ((int64_t(width) * int64_t(height) * int64_t(4)) > PR_INT32_MAX) {
|
|
NS_ERROR("Widget size too big - integer overflow!");
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<gfxImageSurface> imageSurface =
|
|
new gfxImageSurface(gfxIntSize(width, height),
|
|
gfxASurface::ImageFormatARGB32);
|
|
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
|
|
mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO);
|
|
|
|
if (!mGLContext->IsGLES2()) {
|
|
// GLES2 promises that binding to any custom FBO will attach
|
|
// to GL_COLOR_ATTACHMENT0 attachment point.
|
|
if (mGLContext->IsDoubleBuffered()) {
|
|
mGLContext->fReadBuffer(LOCAL_GL_BACK);
|
|
}
|
|
else {
|
|
mGLContext->fReadBuffer(LOCAL_GL_COLOR_ATTACHMENT0);
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(imageSurface->Stride() == width * 4,
|
|
"Image Surfaces being created with weird stride!");
|
|
|
|
mGLContext->ReadPixelsIntoImageSurface(imageSurface);
|
|
|
|
aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
aTarget->Scale(1.0, -1.0);
|
|
aTarget->Translate(-gfxPoint(0.0, height));
|
|
aTarget->SetSource(imageSurface);
|
|
aTarget->Paint();
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::SetLayerProgramProjectionMatrix(const gfx3DMatrix& aMatrix)
|
|
{
|
|
for (unsigned int i = 0; i < mPrograms.Length(); ++i) {
|
|
for (uint32_t mask = MaskNone; mask < NumMaskTypes; ++mask) {
|
|
if (mPrograms[i].mVariations[mask]) {
|
|
mPrograms[i].mVariations[mask]->CheckAndSetProjectionMatrix(aMatrix);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static GLenum
|
|
GetFrameBufferInternalFormat(GLContext* gl,
|
|
GLuint aCurrentFrameBuffer,
|
|
nsIWidget* aWidget)
|
|
{
|
|
if (aCurrentFrameBuffer == 0) { // default framebuffer
|
|
return aWidget->GetGLFrameBufferFormat();
|
|
}
|
|
return LOCAL_GL_RGBA;
|
|
}
|
|
|
|
void
|
|
LayerManagerOGL::CreateFBOWithTexture(const nsIntRect& aRect, InitMode aInit,
|
|
GLuint aCurrentFrameBuffer,
|
|
GLuint *aFBO, GLuint *aTexture)
|
|
{
|
|
GLuint tex, fbo;
|
|
|
|
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
mGLContext->fGenTextures(1, &tex);
|
|
mGLContext->fBindTexture(mFBOTextureTarget, tex);
|
|
|
|
if (aInit == InitModeCopy) {
|
|
// We're going to create an RGBA temporary fbo. But to
|
|
// CopyTexImage() from the current framebuffer, the framebuffer's
|
|
// format has to be compatible with the new texture's. So we
|
|
// check the format of the framebuffer here and take a slow path
|
|
// if it's incompatible.
|
|
GLenum format =
|
|
GetFrameBufferInternalFormat(gl(), aCurrentFrameBuffer, mWidget);
|
|
|
|
bool isFormatCompatibleWithRGBA
|
|
= gl()->IsGLES2() ? (format == LOCAL_GL_RGBA)
|
|
: true;
|
|
|
|
if (isFormatCompatibleWithRGBA) {
|
|
mGLContext->fCopyTexImage2D(mFBOTextureTarget,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
aRect.x, aRect.y,
|
|
aRect.width, aRect.height,
|
|
0);
|
|
} else {
|
|
// Curses, incompatible formats. Take a slow path.
|
|
//
|
|
// XXX Technically CopyTexSubImage2D also has the requirement of
|
|
// matching formats, but it doesn't seem to affect us in the
|
|
// real world.
|
|
mGLContext->fTexImage2D(mFBOTextureTarget,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
aRect.width, aRect.height,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
LOCAL_GL_UNSIGNED_BYTE,
|
|
NULL);
|
|
mGLContext->fCopyTexSubImage2D(mFBOTextureTarget,
|
|
0, // level
|
|
0, 0, // offset
|
|
aRect.x, aRect.y,
|
|
aRect.width, aRect.height);
|
|
}
|
|
} else {
|
|
mGLContext->fTexImage2D(mFBOTextureTarget,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
aRect.width, aRect.height,
|
|
0,
|
|
LOCAL_GL_RGBA,
|
|
LOCAL_GL_UNSIGNED_BYTE,
|
|
NULL);
|
|
}
|
|
mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
|
|
LOCAL_GL_LINEAR);
|
|
mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MAG_FILTER,
|
|
LOCAL_GL_LINEAR);
|
|
mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_S,
|
|
LOCAL_GL_CLAMP_TO_EDGE);
|
|
mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_T,
|
|
LOCAL_GL_CLAMP_TO_EDGE);
|
|
mGLContext->fBindTexture(mFBOTextureTarget, 0);
|
|
|
|
mGLContext->fGenFramebuffers(1, &fbo);
|
|
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
|
|
mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
|
|
LOCAL_GL_COLOR_ATTACHMENT0,
|
|
mFBOTextureTarget,
|
|
tex,
|
|
0);
|
|
|
|
// Making this call to fCheckFramebufferStatus prevents a crash on
|
|
// PowerVR. See bug 695246.
|
|
GLenum result = mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
|
|
if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
|
|
nsAutoCString msg;
|
|
msg.Append("Framebuffer not complete -- error 0x");
|
|
msg.AppendInt(result, 16);
|
|
msg.Append(", mFBOTextureTarget 0x");
|
|
msg.AppendInt(mFBOTextureTarget, 16);
|
|
msg.Append(", aRect.width ");
|
|
msg.AppendInt(aRect.width);
|
|
msg.Append(", aRect.height ");
|
|
msg.AppendInt(aRect.height);
|
|
NS_RUNTIMEABORT(msg.get());
|
|
}
|
|
|
|
SetupPipeline(aRect.width, aRect.height, DontApplyWorldTransform);
|
|
mGLContext->fScissor(0, 0, aRect.width, aRect.height);
|
|
|
|
if (aInit == InitModeClear) {
|
|
mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
|
|
mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
*aFBO = fbo;
|
|
*aTexture = tex;
|
|
}
|
|
|
|
already_AddRefed<ShadowThebesLayer>
|
|
LayerManagerOGL::CreateShadowThebesLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
#ifdef FORCE_BASICTILEDTHEBESLAYER
|
|
return nsRefPtr<ShadowThebesLayer>(new TiledThebesLayerOGL(this)).forget();
|
|
#else
|
|
return nsRefPtr<ShadowThebesLayerOGL>(new ShadowThebesLayerOGL(this)).forget();
|
|
#endif
|
|
}
|
|
|
|
already_AddRefed<ShadowContainerLayer>
|
|
LayerManagerOGL::CreateShadowContainerLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ShadowContainerLayerOGL>(new ShadowContainerLayerOGL(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ShadowImageLayer>
|
|
LayerManagerOGL::CreateShadowImageLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ShadowImageLayerOGL>(new ShadowImageLayerOGL(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ShadowColorLayer>
|
|
LayerManagerOGL::CreateShadowColorLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ShadowColorLayerOGL>(new ShadowColorLayerOGL(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ShadowCanvasLayer>
|
|
LayerManagerOGL::CreateShadowCanvasLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ShadowCanvasLayerOGL>(new ShadowCanvasLayerOGL(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ShadowRefLayer>
|
|
LayerManagerOGL::CreateShadowRefLayer()
|
|
{
|
|
if (LayerManagerOGL::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ShadowRefLayerOGL>(new ShadowRefLayerOGL(this)).forget();
|
|
}
|
|
|
|
} /* layers */
|
|
} /* mozilla */
|