--- old/src/java.desktop/share/classes/sun/java2d/opengl/OGLContext.java 2015-07-03 14:54:05.000000000 +0300 +++ new/src/java.desktop/share/classes/sun/java2d/opengl/OGLContext.java 2015-07-03 14:54:04.000000000 +0300 @@ -194,6 +194,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); @@ -223,6 +227,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(); } } --- old/src/java.desktop/share/native/common/java2d/opengl/J2D_GL/glext.h 2015-07-03 14:54:05.000000000 +0300 +++ new/src/java.desktop/share/native/common/java2d/opengl/J2D_GL/glext.h 2015-07-03 14:54:05.000000000 +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 } --- old/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c 2015-07-03 14:54:06.000000000 +0300 +++ new/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c 2015-07-03 14:54:06.000000000 +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) { --- old/src/java.desktop/share/native/common/java2d/opengl/OGLContext.h 2015-07-03 14:54:07.000000000 +0300 +++ new/src/java.desktop/share/native/common/java2d/opengl/OGLContext.h 2015-07-03 14:54:07.000000000 +0300 @@ -134,6 +134,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 --- old/src/java.desktop/share/native/common/java2d/opengl/OGLFuncs.h 2015-07-03 14:54:08.000000000 +0300 +++ new/src/java.desktop/share/native/common/java2d/opengl/OGLFuncs.h 2015-07-03 14:54:08.000000000 +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) \ --- old/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c 2015-07-03 14:54:08.000000000 +0300 +++ new/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c 2015-07-03 14:54:08.000000000 +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)) { - return; - } - - if (cacheStatus == CACHE_LCD) { - pixelFormat = rgbOrder ? GL_RGB : GL_BGR; + if (pixelFormat == GL_LUMINANCE) { + gcinfo = glyphCacheAA; } else { - pixelFormat = GL_LUMINANCE; + gcinfo = glyphCacheLCD; + } + + if ((gcinfo == NULL) || (glyph->image == NULL)) { + return; } - 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); - - // 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; + 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; + } 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); - - // update the remaining destination texture coordinates - dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH; - dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT; + 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; + + 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,29 @@ 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. +#ifdef MACOSX + if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) && + dstOps->textureTarget == GL_TEXTURE_2D) + { + dstTextureID = dstOps->textureID; + } +#endif + for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) { jint x, y; jfloat glyphx, glyphy; @@ -1003,8 +1064,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 +1084,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); } }