8087201: OGL: rendering of lcd text is slow
authorbae
Thu, 09 Jul 2015 16:37:35 +0300
changeset 31882 0268245131a9
parent 31881 6855efe87d08
child 31883 be405ebc8355
8087201: OGL: rendering of lcd text is slow Reviewed-by: serb, prr
jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLContext.java
jdk/src/java.desktop/share/native/common/java2d/opengl/J2D_GL/glext.h
jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c
jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.h
jdk/src/java.desktop/share/native/common/java2d/opengl/OGLFuncs.h
jdk/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c
--- a/jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLContext.java	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLContext.java	Thu Jul 09 16:37:35 2015 +0300
@@ -191,6 +191,10 @@
         /** Indicates the presence of the GL_ARB_texture_rectangle extension. */
         @Native
         static final int CAPS_EXT_TEXRECT      = (FIRST_PRIVATE_CAP << 4);
+        /** Indicates the presence of the GL_NV_texture_barrier extension. */
+        @Native
+        static final int CAPS_EXT_TEXBARRIER = (FIRST_PRIVATE_CAP << 5);
+
 
         OGLContextCaps(int caps, String adapterId) {
             super(caps, adapterId);
@@ -217,6 +221,9 @@
             if ((caps & CAPS_EXT_TEXRECT) != 0) {
                 sb.append("CAPS_EXT_TEXRECT|");
             }
+            if ((caps & CAPS_EXT_TEXBARRIER) != 0) {
+                sb.append("CAPS_EXT_TEXBARRIER|");
+            }
             return sb.toString();
         }
     }
--- a/jdk/src/java.desktop/share/native/common/java2d/opengl/J2D_GL/glext.h	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/native/common/java2d/opengl/J2D_GL/glext.h	Thu Jul 09 16:37:35 2015 +0300
@@ -6506,6 +6506,13 @@
 typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid *string);
 #endif
 
+#ifndef GL_NV_texture_barrier
+#define GL_NV_texture_barrier 1
+#ifdef GL_EXT_PROTOTYPES
+GLAPI void APIENTRY glTextureBarrierNV (void);
+#endif /* GL_EXT_PROTOTYPES */
+typedef void (APIENTRYP PNFGLTEXTUREBARRIERNVPROC) (void);
+#endif
 
 #ifdef __cplusplus
 }
--- a/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c	Thu Jul 09 16:37:35 2015 +0300
@@ -913,6 +913,10 @@
             }
         }
     }
+    if (OGLContext_IsExtensionAvailable(e, "GL_NV_texture_barrier")) {
+        *caps |= CAPS_EXT_TEXBARRIER;
+    }
+
     // stuff vendor descriptor in the upper bits of the caps
     if (vendor != NULL) {
         if (strncmp(vendor, "ATI", 3) == 0) {
--- a/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.h	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLContext.h	Thu Jul 09 16:37:35 2015 +0300
@@ -132,6 +132,8 @@
     sun_java2d_opengl_OGLContext_OGLContextCaps_CAPS_EXT_GRAD_SHADER
 #define CAPS_EXT_TEXRECT     \
     sun_java2d_opengl_OGLContext_OGLContextCaps_CAPS_EXT_TEXRECT
+#define CAPS_EXT_TEXBARRIER  \
+    sun_java2d_opengl_OGLContext_OGLContextCaps_CAPS_EXT_TEXBARRIER
 
 /**
  * Evaluates to true if the given capability bitmask is present for the
--- a/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLFuncs.h	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLFuncs.h	Thu Jul 09 16:37:35 2015 +0300
@@ -173,6 +173,11 @@
 typedef void (GLAPIENTRY *glDeleteObjectARBType)(GLhandleARB);
 
 /**
+ * GL_NV_texture_barrier extension function typedef's
+ */
+typedef void (GLAPIENTRY *glTextureBarrierNVType) (void);
+
+/**
  * REMIND: this caused an internal error in the MS compiler!?!?
  *
  *#define OGL_CHECK_FUNC_ERR(f) \
@@ -316,7 +321,8 @@
     OGL_##action##_EXT_FUNC(glGetProgramivARB); \
     OGL_##action##_EXT_FUNC(glGetInfoLogARB); \
     OGL_##action##_EXT_FUNC(glGetObjectParameterivARB); \
-    OGL_##action##_EXT_FUNC(glDeleteObjectARB);
+    OGL_##action##_EXT_FUNC(glDeleteObjectARB); \
+    OGL_##action##_EXT_FUNC(glTextureBarrierNV);
 
 #define OGL_EXPRESS_ALL_FUNCS(action) \
     OGL_EXPRESS_BASE_FUNCS(action) \
--- a/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c	Thu Jul 09 15:26:37 2015 +0300
+++ b/jdk/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c	Thu Jul 09 16:37:35 2015 +0300
@@ -46,8 +46,8 @@
  */
 #define OGLTR_CACHE_WIDTH       512
 #define OGLTR_CACHE_HEIGHT      512
-#define OGLTR_CACHE_CELL_WIDTH  16
-#define OGLTR_CACHE_CELL_HEIGHT 16
+#define OGLTR_CACHE_CELL_WIDTH  32
+#define OGLTR_CACHE_CELL_HEIGHT 32
 
 /**
  * The current "glyph mode" state.  This variable is used to track the
@@ -68,26 +68,17 @@
 static GlyphMode glyphMode = MODE_NOT_INITED;
 
 /**
- * This enum indicates the current state of the hardware glyph cache.
- * Initially the CacheStatus is set to CACHE_NOT_INITED, and then it is
- * set to either GRAY or LCD when the glyph cache is initialized.
- */
-typedef enum {
-    CACHE_NOT_INITED,
-    CACHE_GRAY,
-    CACHE_LCD
-} CacheStatus;
-static CacheStatus cacheStatus = CACHE_NOT_INITED;
-
-/**
- * This is the one glyph cache.  Once it is initialized as either GRAY or
- * LCD, it stays in that mode for the duration of the application.  It should
+ * There are two separate glyph caches: for AA and for LCD.
+ * Once one of them is initialized as either GRAY or LCD, it
+ * stays in that mode for the duration of the application.  It should
  * be safe to use this one glyph cache for all screens in a multimon
  * environment, since the glyph cache texture is shared between all contexts,
  * and (in theory) OpenGL drivers should be smart enough to manage that
  * texture across all screens.
  */
-static GlyphCacheInfo *glyphCache = NULL;
+
+static GlyphCacheInfo *glyphCacheLCD = NULL;
+static GlyphCacheInfo *glyphCacheAA = NULL;
 
 /**
  * The handle to the LCD text fragment program object.
@@ -138,7 +129,7 @@
  *     (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_NOCACHE_TILE_SIZE)
  */
 #define OGLTR_CACHED_DEST_WIDTH  512
-#define OGLTR_CACHED_DEST_HEIGHT 32
+#define OGLTR_CACHED_DEST_HEIGHT (OGLTR_CACHE_CELL_HEIGHT * 2)
 
 /**
  * The handle to the "cached destination" texture object.
@@ -212,8 +203,11 @@
                      OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,
                      pixelFormat, GL_UNSIGNED_BYTE, NULL);
 
-    cacheStatus = (lcdCache ? CACHE_LCD : CACHE_GRAY);
-    glyphCache = gcinfo;
+    if (lcdCache) {
+        glyphCacheLCD = gcinfo;
+    } else {
+        glyphCacheAA = gcinfo;
+    }
 
     return JNI_TRUE;
 }
@@ -223,24 +217,24 @@
  * associated with the given OGLContext.
  */
 static void
-OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder)
+OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat)
 {
-    GLenum pixelFormat;
     CacheCellInfo *ccinfo;
+    GlyphCacheInfo *gcinfo;
 
     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache");
 
-    if ((glyphCache == NULL) || (glyph->image == NULL)) {
+    if (pixelFormat == GL_LUMINANCE) {
+        gcinfo = glyphCacheAA;
+    } else {
+        gcinfo = glyphCacheLCD;
+    }
+
+    if ((gcinfo == NULL) || (glyph->image == NULL)) {
         return;
     }
 
-    if (cacheStatus == CACHE_LCD) {
-        pixelFormat = rgbOrder ? GL_RGB : GL_BGR;
-    } else {
-        pixelFormat = GL_LUMINANCE;
-    }
-
-    AccelGlyphCache_AddGlyph(glyphCache, glyph);
+    AccelGlyphCache_AddGlyph(gcinfo, glyph);
     ccinfo = (CacheCellInfo *) glyph->cellInfo;
 
     if (ccinfo != NULL) {
@@ -413,24 +407,31 @@
  * gamma lookup table textures.
  */
 static jboolean
-OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast)
+OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID,
+                              GLuint dstTextureID,
+                              jint contrast)
 {
     // bind the texture containing glyph data to texture unit 0
     j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
     j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);
+    j2d_glEnable(GL_TEXTURE_2D);
 
     // bind the texture tile containing destination data to texture unit 1
     j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
-    if (cachedDestTextureID == 0) {
-        cachedDestTextureID =
-            OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,
-                                         OGLTR_CACHED_DEST_WIDTH,
-                                         OGLTR_CACHED_DEST_HEIGHT);
+    if (dstTextureID != 0) {
+        j2d_glBindTexture(GL_TEXTURE_2D, dstTextureID);
+    } else {
         if (cachedDestTextureID == 0) {
-            return JNI_FALSE;
+            cachedDestTextureID =
+                OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,
+                                             OGLTR_CACHED_DEST_WIDTH,
+                                             OGLTR_CACHED_DEST_HEIGHT);
+            if (cachedDestTextureID == 0) {
+                return JNI_FALSE;
+            }
         }
+        j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);
     }
-    j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);
 
     // note that GL_TEXTURE_2D was already enabled for texture unit 0,
     // but we need to explicitly enable it for texture unit 1
@@ -472,14 +473,14 @@
         return;
     }
 
-    if (glyphCache == NULL) {
+    if (glyphCacheAA == NULL) {
         if (!OGLTR_InitGlyphCache(JNI_FALSE)) {
             return;
         }
     }
 
     j2d_glEnable(GL_TEXTURE_2D);
-    j2d_glBindTexture(GL_TEXTURE_2D, glyphCache->cacheID);
+    j2d_glBindTexture(GL_TEXTURE_2D, glyphCacheAA->cacheID);
     j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
     // for grayscale/monochrome text, the current OpenGL source color
@@ -522,6 +523,7 @@
         j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
         j2d_glDisable(GL_TEXTURE_2D);
         j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
+        j2d_glDisable(GL_TEXTURE_2D);
         break;
 
     case MODE_NO_CACHE_GRAY:
@@ -547,7 +549,7 @@
 
     if (ginfo->cellInfo == NULL) {
         // attempt to add glyph to accelerated glyph cache
-        OGLTR_AddToGlyphCache(ginfo, JNI_FALSE);
+        OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE);
 
         if (ginfo->cellInfo == NULL) {
             // we'll just no-op in the rare case that the cell is NULL
@@ -707,7 +709,8 @@
 OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
                            GlyphInfo *ginfo, jint x, jint y,
                            jint glyphIndex, jint totalGlyphs,
-                           jboolean rgbOrder, jint contrast)
+                           jboolean rgbOrder, jint contrast,
+                            GLuint dstTextureID)
 {
     CacheCellInfo *cell;
     jint dx1, dy1, dx2, dy2;
@@ -718,7 +721,7 @@
         CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
         j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
-        if (glyphCache == NULL) {
+        if (glyphCacheLCD == NULL) {
             if (!OGLTR_InitGlyphCache(JNI_TRUE)) {
                 return JNI_FALSE;
             }
@@ -727,11 +730,13 @@
         if (rgbOrder != lastRGBOrder) {
             // need to invalidate the cache in this case; see comments
             // for lastRGBOrder above
-            AccelGlyphCache_Invalidate(glyphCache);
+            AccelGlyphCache_Invalidate(glyphCacheLCD);
             lastRGBOrder = rgbOrder;
         }
 
-        if (!OGLTR_EnableLCDGlyphModeState(glyphCache->cacheID, contrast)) {
+        if (!OGLTR_EnableLCDGlyphModeState(glyphCacheLCD->cacheID,
+                                           dstTextureID, contrast))
+        {
             return JNI_FALSE;
         }
 
@@ -750,7 +755,7 @@
         j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
 
         // attempt to add glyph to accelerated glyph cache
-        OGLTR_AddToGlyphCache(ginfo, rgbOrder);
+        OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR);
 
         if (ginfo->cellInfo == NULL) {
             // we'll just no-op in the rare case that the cell is NULL
@@ -767,16 +772,34 @@
     dx2 = dx1 + ginfo->width;
     dy2 = dy1 + ginfo->height;
 
-    // copy destination into second cached texture, if necessary
-    OGLTR_UpdateCachedDestination(dstOps, ginfo,
-                                  dx1, dy1, dx2, dy2,
-                                  glyphIndex, totalGlyphs);
+    if (dstTextureID == 0) {
+        // copy destination into second cached texture, if necessary
+        OGLTR_UpdateCachedDestination(dstOps, ginfo,
+                                      dx1, dy1, dx2, dy2,
+                                      glyphIndex, totalGlyphs);
 
-    // texture coordinates of the destination tile
-    dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
-    dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;
-    dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
-    dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;
+        // texture coordinates of the destination tile
+        dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
+        dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;
+        dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
+        dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;
+    } else {
+        jint gw = ginfo->width;
+        jint gh = ginfo->height;
+
+        // this accounts for lower-left origin of the destination region
+        jint dxadj = dstOps->xOffset + x;
+        jint dyadj = dstOps->yOffset + dstOps->height - (y + gh);
+
+        // update the remaining destination texture coordinates
+        dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;
+        dtx2 = ((GLfloat)dxadj + gw) / dstOps->textureWidth;
+
+        dty1 = ((GLfloat)dyadj + gh) / dstOps->textureHeight;
+        dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;
+
+        j2d_glTextureBarrierNV();
+    }
 
     // render composed texture to the destination surface
     j2d_glBegin(GL_QUADS);
@@ -837,7 +860,8 @@
 OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,
                           GlyphInfo *ginfo, jint x, jint y,
                           jint rowBytesOffset,
-                          jboolean rgbOrder, jint contrast)
+                          jboolean rgbOrder, jint contrast,
+                          GLuint dstTextureID)
 {
     GLfloat tx1, ty1, tx2, ty2;
     GLfloat dtx1, dty1, dtx2, dty2;
@@ -859,7 +883,9 @@
             }
         }
 
-        if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, contrast)) {
+        if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID,
+                                           dstTextureID, contrast))
+        {
             return JNI_FALSE;
         }
 
@@ -907,18 +933,29 @@
             dxadj = dstOps->xOffset + x;
             dyadj = dstOps->yOffset + dstOps->height - (y + sh);
 
-            // copy destination into cached texture tile (the lower-left
-            // corner of the destination region will be positioned at the
-            // lower-left corner (0,0) of the texture)
-            j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
-            j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
-                                    0, 0,
-                                    dxadj, dyadj,
-                                    sw, sh);
+            if (dstTextureID == 0) {
+                // copy destination into cached texture tile (the lower-left
+                // corner of the destination region will be positioned at the
+                // lower-left corner (0,0) of the texture)
+                j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
+                j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
+                                        0, 0,
+                                        dxadj, dyadj,
+                                        sw, sh);
+                // update the remaining destination texture coordinates
+                dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;
+                dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;
+            } else {
+                // use the destination texture directly
+                // update the remaining destination texture coordinates
+                dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;
+                dtx2 = ((GLfloat)dxadj + sw) / dstOps->textureWidth;
 
-            // update the remaining destination texture coordinates
-            dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;
-            dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;
+                dty1 = ((GLfloat)dyadj + sh) / dstOps->textureHeight;
+                dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;
+
+                j2d_glTextureBarrierNV();
+            }
 
             // render composed texture to the destination surface
             j2d_glBegin(GL_QUADS);
@@ -953,6 +990,7 @@
                     unsigned char *images, unsigned char *positions)
 {
     int glyphCounter;
+    GLuint dstTextureID = 0;
 
     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");
 
@@ -966,6 +1004,27 @@
     glyphMode = MODE_NOT_INITED;
     isCachedDestValid = JNI_FALSE;
 
+    // We have to obtain an information about destination content
+    // in order to render lcd glyphs. It could be done by copying
+    // a part of desitination buffer into an intermediate texture
+    // using glCopyTexSubImage2D(). However, on macosx this path is
+    // slow, and it dramatically reduces the overall speed of lcd
+    // text rendering.
+    //
+    // In some cases, we can use a texture from the destination
+    // surface data in oredr to avoid this slow reading routine.
+    // It requires:
+    //  * An appropriate textureTarget for the destination SD.
+    //    In particular, we need GL_TEXTURE_2D
+    //  * Means to prevent read-after-write problem.
+    //    At the moment, a GL_NV_texture_barrier extension is used
+    //    to achieve this.
+    if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) &&
+        dstOps->textureTarget == GL_TEXTURE_2D)
+    {
+        dstTextureID = dstOps->textureID;
+    }
+
     for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
         jint x, y;
         jfloat glyphx, glyphy;
@@ -1003,8 +1062,7 @@
 
         if (grayscale) {
             // grayscale or monochrome glyph data
-            if (cacheStatus != CACHE_LCD &&
-                ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
+            if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
             {
                 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);
@@ -1024,19 +1082,20 @@
             }
 
             if (rowBytesOffset == 0 &&
-                cacheStatus != CACHE_GRAY &&
                 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
             {
                 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,
                                                 ginfo, x, y,
                                                 glyphCounter, totalGlyphs,
-                                                rgbOrder, lcdContrast);
+                                                rgbOrder, lcdContrast,
+                                                dstTextureID);
             } else {
                 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,
                                                ginfo, x, y,
                                                rowBytesOffset,
-                                               rgbOrder, lcdContrast);
+                                               rgbOrder, lcdContrast,
+                                               dstTextureID);
             }
         }