Bug 628566 - Implement basic tiling of ImageLayerOGL. r=joe

This commit is contained in:
Florian Hänel 2011-05-19 12:53:02 +02:00
parent 2bbc61681d
commit 9b0ef26bc8
8 changed files with 252 additions and 104 deletions

View File

@ -213,10 +213,6 @@ LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
void
LayerManager::Mutated(Layer* aLayer)
{
NS_ABORT_IF_FALSE(!aLayer->GetTileSourceRect() ||
(LAYERS_BASIC == GetBackendType() &&
Layer::TYPE_IMAGE == aLayer->GetType()),
"Tiling not supported for this manager/layer type");
}
#endif // DEBUG

View File

@ -441,6 +441,11 @@ ImageLayerOGL::RenderLayer(int,
CairoImageOGL *cairoImage =
static_cast<CairoImageOGL*>(image.get());
cairoImage->SetTiling(mUseTileSourceRect);
gl()->MakeCurrent();
unsigned int iwidth = cairoImage->mSize.width;
unsigned int iheight = cairoImage->mSize.height;
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, cairoImage->mTexture.GetTextureID());
@ -452,23 +457,108 @@ ImageLayerOGL::RenderLayer(int,
sGLXLibrary.BindTexImage(pixmap);
}
#endif
ColorTextureLayerProgram *program =
mOGLManager->GetColorTextureLayerProgram(cairoImage->mLayerProgram);
ApplyFilter(mFilter);
program->Activate();
program->SetLayerQuadRect(nsIntRect(0, 0,
cairoImage->mSize.width,
cairoImage->mSize.height));
// The following uniform controls the scaling of the vertex coords.
// Instead of setting the scale here and using coords in the range [0,1], we
// set an identity transform and use pixel coordinates below
program->SetLayerQuadRect(nsIntRect(0, 0, 1, 1));
program->SetLayerTransform(GetEffectiveTransform());
program->SetLayerOpacity(GetEffectiveOpacity());
program->SetRenderOffset(aOffset);
program->SetTextureUnit(0);
mOGLManager->BindAndDrawQuad(program);
nsIntRect rect = GetVisibleRegion().GetBounds();
bool tileIsWholeImage = (mTileSourceRect == nsIntRect(0, 0, iwidth, iheight))
|| !mUseTileSourceRect;
bool imageIsPowerOfTwo = ((iwidth & (iwidth - 1)) == 0 &&
(iheight & (iheight - 1)) == 0);
bool canDoNPOT = (
gl()->IsExtensionSupported(GLContext::ARB_texture_non_power_of_two) ||
gl()->IsExtensionSupported(GLContext::OES_texture_npot));
GLContext::RectTriangles triangleBuffer;
// do GL_REPEAT if we can - should be the fastest option.
// draw a single rect for the whole region, a little overdraw
// on the gpu should be faster than tesselating
// maybe we can write a shader that can also handle texture subrects
// and repeat?
if (tileIsWholeImage && (imageIsPowerOfTwo || canDoNPOT)) {
// we need to anchor the repeating texture appropriately
// otherwise it will start from the region border instead
// of the layer origin. This is the offset into the texture
// that the region border represents
float tex_offset_u = (float)(rect.x % iwidth) / iwidth;
float tex_offset_v = (float)(rect.y % iheight) / iheight;
triangleBuffer.addRect(rect.x, rect.y,
rect.x + rect.width, rect.y + rect.height,
tex_offset_u, tex_offset_v,
tex_offset_u + (float)rect.width / (float)iwidth,
tex_offset_v + (float)rect.height / (float)iheight);
}
// can't do fast path via GL_REPEAT - we have to tessellate individual rects.
else {
unsigned int twidth = mTileSourceRect.width;
unsigned int theight = mTileSourceRect.height;
nsIntRegion region = GetVisibleRegion();
// image subrect in texture coordinates
float subrect_tl_u = float(mTileSourceRect.x) / float(iwidth);
float subrect_tl_v = float(mTileSourceRect.y) / float(iheight);
float subrect_br_u = float(mTileSourceRect.width + mTileSourceRect.x) / float(iwidth);
float subrect_br_v = float(mTileSourceRect.height + mTileSourceRect.y) / float(iheight);
// round rect position down to multiples of texture size
// this way we start at multiples of rect positions
rect.x = (rect.x / iwidth) * iwidth;
rect.y = (rect.y / iheight) * iheight;
// round up size to accomodate for rounding down above
rect.width = (rect.width / iwidth + 2) * iwidth;
rect.height = (rect.height / iheight + 2) * iheight;
// tesselate the visible region with tiles of subrect size
for (int y = rect.y; y < rect.y + rect.height; y += theight) {
for (int x = rect.x; x < rect.x + rect.width; x += twidth) {
// when we already tessellate, we might as well save on overdraw here
if (!region.Intersects(nsIntRect(x, y, twidth, theight))) {
continue;
}
triangleBuffer.addRect(x, y,
x + twidth, y + theight,
subrect_tl_u, subrect_tl_v,
subrect_br_u, subrect_br_v);
}
}
}
GLuint vertAttribIndex =
program->AttribLocation(LayerProgram::VertexAttrib);
GLuint texCoordAttribIndex =
program->AttribLocation(LayerProgram::TexCoordAttrib);
NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?");
gl()->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
gl()->fVertexAttribPointer(vertAttribIndex, 2,
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
triangleBuffer.vertexPointer());
gl()->fVertexAttribPointer(texCoordAttribIndex, 2,
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
triangleBuffer.texCoordPointer());
{
gl()->fEnableVertexAttribArray(texCoordAttribIndex);
{
gl()->fEnableVertexAttribArray(vertAttribIndex);
gl()->fDrawArrays(LOCAL_GL_TRIANGLES, 0, triangleBuffer.elements());
gl()->fDisableVertexAttribArray(vertAttribIndex);
}
gl()->fDisableVertexAttribArray(texCoordAttribIndex);
}
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
if (cairoImage->mSurface) {
sGLXLibrary.ReleaseTexImage(pixmap);
@ -741,7 +831,7 @@ PlanarYCbCrImageOGL::UpdateTextures(GLContext *gl)
}
CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager)
: CairoImage(nsnull), mSize(0, 0)
: CairoImage(nsnull), mSize(0, 0), mTiling(false)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread to create a cairo image");
@ -788,6 +878,25 @@ CairoImageOGL::SetData(const CairoImage::Data &aData)
tex);
}
void CairoImageOGL::SetTiling(bool aTiling)
{
if (aTiling == mTiling)
return;
mozilla::gl::GLContext *gl = mTexture.GetGLContext();
gl->MakeCurrent();
gl->fActiveTexture(LOCAL_GL_TEXTURE0);
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture.GetTextureID());
mTiling = aTiling;
if (aTiling) {
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_REPEAT);
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_REPEAT);
} else {
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
}
}
ShadowImageLayerOGL::ShadowImageLayerOGL(LayerManagerOGL* aManager)
: ShadowImageLayer(aManager, nsnull)
, LayerOGL(aManager)

View File

@ -233,6 +233,9 @@ public:
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
nsRefPtr<gfxASurface> mSurface;
#endif
void SetTiling(bool aTiling);
private:
bool mTiling;
};
class ShadowImageLayerOGL : public ShadowImageLayer,

View File

@ -80,7 +80,11 @@ $FRAGMENT_SHADER_HEADER$
uniform float uLayerOpacity;
#endif
#ifdef GL_ES // for tiling, texcoord can be greater than the lowfp range
varying mediump vec2 vTexCoord;
#else
varying vec2 vTexCoord;
#endif
@end
// This is a basic Layer vertex shader. It's used for all
@ -97,7 +101,11 @@ uniform vec4 uRenderTargetOffset;
attribute vec4 aVertexCoord;
attribute vec2 aTexCoord;
#ifdef GL_ES // for tiling, texcoord can be greater than the lowfp range
varying mediump vec2 vTexCoord;
#else
varying vec2 vTexCoord;
#endif
void main()
{

View File

@ -120,20 +120,22 @@ BindAndDrawQuadWithTextureRect(GLContext* aGl,
GLContext::DecomposeIntoNoRepeatTriangles(aTexCoordRect, aTexSize, rects);
}
// vertex position buffer is 2 floats, not normalized, 0 stride.
aGl->fVertexAttribPointer(vertAttribIndex, 2,
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
rects.vertexCoords);
rects.vertexPointer());
// texture coord buffer is 2 floats, not normalized, 0 stride.
aGl->fVertexAttribPointer(texCoordAttribIndex, 2,
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
rects.texCoords);
rects.texCoordPointer());
{
aGl->fEnableVertexAttribArray(texCoordAttribIndex);
{
aGl->fEnableVertexAttribArray(vertAttribIndex);
aGl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.numRects * 6);
aGl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
aGl->fDisableVertexAttribArray(vertAttribIndex);
}

View File

@ -1217,10 +1217,10 @@ GLContext::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
// now put the coords into the d[xy]0 .. d[xy]1 coordinate space
// from the 0..1 that it comes out of decompose
GLfloat *v = rects.vertexCoords;
for (int i = 0; i < rects.numRects * 6; ++i) {
v[i*2] = (v[i*2] * (dx1 - dx0)) + dx0;
v[i*2+1] = (v[i*2+1] * (dy1 - dy0)) + dy0;
RectTriangles::vert_coord* v = (RectTriangles::vert_coord*)rects.vertexPointer();
for (int i = 0; i < rects.elements(); ++i) {
v[i].x = (v[i].x * (dx1 - dx0)) + dx0;
v[i].y = (v[i].y * (dy1 - dy0)) + dy0;
}
}
@ -1230,13 +1230,13 @@ GLContext::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexCoords);
fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoords);
fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexPointer());
fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoordPointer());
fEnableVertexAttribArray(0);
fEnableVertexAttribArray(1);
fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.numRects * 6);
fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
fDisableVertexAttribArray(0);
fDisableVertexAttribArray(1);
@ -1462,28 +1462,35 @@ void
GLContext::RectTriangles::addRect(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1)
{
NS_ASSERTION(numRects < 4, "Overflow in number of rectangles, max 4!");
vert_coord v;
v.x = x0; v.y = y0;
vertexCoords.AppendElement(v);
v.x = x1; v.y = y0;
vertexCoords.AppendElement(v);
v.x = x0; v.y = y1;
vertexCoords.AppendElement(v);
GLfloat *v = &vertexCoords[numRects*6*2];
GLfloat *t = &texCoords[numRects*6*2];
v.x = x0; v.y = y1;
vertexCoords.AppendElement(v);
v.x = x1; v.y = y0;
vertexCoords.AppendElement(v);
v.x = x1; v.y = y1;
vertexCoords.AppendElement(v);
*v++ = x0; *v++ = y0;
*v++ = x1; *v++ = y0;
*v++ = x0; *v++ = y1;
tex_coord t;
t.u = tx0; t.v = ty0;
texCoords.AppendElement(t);
t.u = tx1; t.v = ty0;
texCoords.AppendElement(t);
t.u = tx0; t.v = ty1;
texCoords.AppendElement(t);
*v++ = x0; *v++ = y1;
*v++ = x1; *v++ = y0;
*v++ = x1; *v++ = y1;
*t++ = tx0; *t++ = ty0;
*t++ = tx1; *t++ = ty0;
*t++ = tx0; *t++ = ty1;
*t++ = tx0; *t++ = ty1;
*t++ = tx1; *t++ = ty0;
*t++ = tx1; *t++ = ty1;
numRects++;
t.u = tx0; t.v = ty1;
texCoords.AppendElement(t);
t.u = tx1; t.v = ty0;
texCoords.AppendElement(t);
t.u = tx1; t.v = ty1;
texCoords.AppendElement(t);
}
static GLfloat

View File

@ -807,15 +807,34 @@ public:
/** Helper for DecomposeIntoNoRepeatTriangles
*/
struct RectTriangles {
RectTriangles() : numRects(0) { }
RectTriangles() { }
void addRect(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1);
int numRects;
/* max is 4 rectangles, each made up of 2 triangles (3 2-coord vertices each) */
GLfloat vertexCoords[4*3*2*2];
GLfloat texCoords[4*3*2*2];
/**
* these return a float pointer to the start of each array respectively.
* Use it for glVertexAttribPointer calls.
* We can return NULL if we choose to use Vertex Buffer Objects here.
*/
float* vertexPointer() {
return &vertexCoords[0].x;
};
float* texCoordPointer() {
return &texCoords[0].u;
};
unsigned int elements() {
return vertexCoords.Length();
};
typedef struct { GLfloat x,y; } vert_coord;
typedef struct { GLfloat u,v; } tex_coord;
private:
// default is 4 rectangles, each made up of 2 triangles (3 coord vertices each)
nsAutoTArray<vert_coord, 6> vertexCoords;
nsAutoTArray<tex_coord, 6> texCoords;
};
/**

View File

@ -48,6 +48,7 @@
#include "nsFrameLoader.h"
#include "nsViewportFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsIObserver.h"
typedef nsContentView::ViewConfig ViewConfig;
using namespace mozilla::layers;
@ -58,6 +59,28 @@ namespace layout {
typedef FrameMetrics::ViewID ViewID;
typedef RenderFrameParent::ViewMap ViewMap;
nsRefPtr<ImageContainer> sCheckerboard = nsnull;
class CheckerBoardPatternDeleter : public nsIObserver
{
public:
NS_DECL_NSIOBSERVER
NS_DECL_ISUPPORTS
};
NS_IMPL_ISUPPORTS1(CheckerBoardPatternDeleter, nsIObserver)
NS_IMETHODIMP
CheckerBoardPatternDeleter::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
sCheckerboard = nsnull;
}
return NS_OK;
}
// Represents (affine) transforms that are calculated from a content view.
struct ViewTransform {
ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1)
@ -389,64 +412,34 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
}
}
#define BOARDSIZE 32
#define CHECKERSIZE 16
already_AddRefed<gfxASurface>
GetBackgroundImage()
{
// XXX TODO FIXME/bug XXXXXX: this is obviously a hacky placeloader
// impl. Unclear how the background pattern source should be set.
#define WHT 0xffff
#define GRY 0xD69A
#define WLINE8 WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT
#define GLINE8 GRY,GRY,GRY,GRY,GRY,GRY,GRY,GRY
#define WROW16 WLINE8, GLINE8
#define GROW16 GLINE8, WLINE8
static const unsigned short kCheckerboard[] = {
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,WROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,
GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16,GROW16
};
static unsigned int data[BOARDSIZE * BOARDSIZE];
static bool initialized = false;
if (!initialized) {
initialized = true;
for (unsigned int y = 0; y < BOARDSIZE; y++) {
for (unsigned int x = 0; x < BOARDSIZE; x++) {
bool col_odd = (x / CHECKERSIZE) & 1;
bool row_odd = (y / CHECKERSIZE) & 1;
if (col_odd ^ row_odd) { // xor
data[y * BOARDSIZE + x] = 0xFFFFFFFF;
}
else {
data[y * BOARDSIZE + x] = 0xFFDDDDDD;
}
}
}
}
nsRefPtr<gfxASurface> s =
new gfxImageSurface((unsigned char*)kCheckerboard,
gfxIntSize(64, 64),
64 * 2,
gfxASurface::ImageFormatRGB16_565);
new gfxImageSurface((unsigned char*) data,
gfxIntSize(BOARDSIZE, BOARDSIZE),
BOARDSIZE * sizeof(unsigned int),
gfxASurface::ImageFormatARGB32);
return s.forget();
}
@ -505,15 +498,26 @@ BuildBackgroundPatternFor(ContainerLayer* aContainer,
gfxIntSize bgImageSize = bgImage->GetSize();
// Set up goop needed to get a cairo image into its own layer
nsRefPtr<ImageContainer> c = aManager->CreateImageContainer();
const Image::Format fmts[] = { Image::CAIRO_SURFACE };
nsRefPtr<Image> img = c->CreateImage(fmts, 1);
CairoImage::Data data = { bgImage.get(), bgImageSize };
static_cast<CairoImage*>(img.get())->SetData(data);
c->SetCurrentImage(img);
if (!sCheckerboard) {
sCheckerboard = aManager->CreateImageContainer().get();
const Image::Format fmts[] = { Image::CAIRO_SURFACE };
nsRefPtr<Image> img = sCheckerboard->CreateImage(fmts, 1);
CairoImage::Data data = { bgImage.get(), bgImageSize };
static_cast<CairoImage*>(img.get())->SetData(data);
sCheckerboard->SetCurrentImage(img);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService) {
return;
}
nsresult rv = observerService->AddObserver(new CheckerBoardPatternDeleter, "xpcom-shutdown", PR_FALSE);
if (NS_FAILED(rv)) {
return;
}
}
nsRefPtr<ImageLayer> layer = aManager->CreateImageLayer();
layer->SetContainer(c);
layer->SetContainer(sCheckerboard);
// The tile source is the entire background image
nsIntRect tileSource(0, 0, bgImageSize.width, bgImageSize.height);