[Mesa-dev] [PATCH 1/2] mesa: MESA_framebuffer_flip_y extension [v3]

Fritz Koenig frkoenig at google.com
Fri Jun 29 17:52:12 UTC 2018


Adds an extension to glFramebufferParameteri
that will specify if the framebuffer is vertically
flipped. Historically system framebuffers are
vertically flipped and user framebuffers are not.
Checking to see the state was done by looking at
the name field.  This adds an explicit field.

v2:
* updated spec language [for chadv]
* correctly specifying ES 3.1 [for chadv]
* refactor access to rb->Name [for jason]
* handle GetFramebufferParameteriv [for chadv]
v3:
* correct _mesa_GetMultisamplefv [for kusmabite]
---
 docs/specs/MESA_framebuffer_flip_y.spec    | 84 ++++++++++++++++++++++
 include/GLES2/gl2ext.h                     |  5 ++
 src/mapi/glapi/registry/gl.xml             |  6 ++
 src/mesa/drivers/dri/i915/intel_fbo.c      |  7 +-
 src/mesa/drivers/dri/i965/intel_fbo.c      |  7 +-
 src/mesa/drivers/dri/nouveau/nouveau_fbo.c |  7 +-
 src/mesa/drivers/dri/radeon/radeon_fbo.c   |  7 +-
 src/mesa/drivers/dri/radeon/radeon_span.c  |  9 ++-
 src/mesa/drivers/dri/swrast/swrast.c       |  7 +-
 src/mesa/drivers/osmesa/osmesa.c           |  5 +-
 src/mesa/drivers/x11/xm_buffer.c           |  3 +-
 src/mesa/drivers/x11/xmesaP.h              |  3 +-
 src/mesa/main/accum.c                      | 17 +++--
 src/mesa/main/dd.h                         |  3 +-
 src/mesa/main/extensions_table.h           |  1 +
 src/mesa/main/fbobject.c                   | 18 ++++-
 src/mesa/main/framebuffer.c                |  1 +
 src/mesa/main/glheader.h                   |  3 +
 src/mesa/main/mtypes.h                     |  3 +
 src/mesa/main/readpix.c                    | 20 +++---
 src/mesa/state_tracker/st_cb_fbo.c         |  7 +-
 src/mesa/swrast/s_blit.c                   | 17 +++--
 src/mesa/swrast/s_clear.c                  |  3 +-
 src/mesa/swrast/s_copypix.c                | 11 +--
 src/mesa/swrast/s_depth.c                  |  6 +-
 src/mesa/swrast/s_drawpix.c                | 26 ++++---
 src/mesa/swrast/s_renderbuffer.c           |  6 +-
 src/mesa/swrast/s_renderbuffer.h           |  3 +-
 src/mesa/swrast/s_stencil.c                |  3 +-
 29 files changed, 241 insertions(+), 57 deletions(-)
 create mode 100644 docs/specs/MESA_framebuffer_flip_y.spec

diff --git a/docs/specs/MESA_framebuffer_flip_y.spec b/docs/specs/MESA_framebuffer_flip_y.spec
new file mode 100644
index 0000000000..dca77a9541
--- /dev/null
+++ b/docs/specs/MESA_framebuffer_flip_y.spec
@@ -0,0 +1,84 @@
+Name
+
+    MESA_framebuffer_flip_y
+
+Name Strings
+
+    GL_MESA_framebuffer_flip_y
+
+Contact
+
+    Fritz Koenig <frkoenig at google.com>
+
+Contributors
+
+    Fritz Koenig, Google
+    Kristian Høgsberg, Google
+    Chad Versace, Google
+
+Status
+
+    Proposal
+
+Version
+
+    Version 1, June 7, 2018
+
+Number
+
+    TBD
+
+Dependencies
+
+    OpenGL ES 3.1 is required, for FramebufferParameteri.
+
+Overview
+
+    Rendered buffers are normally returned right side up, as accessed
+    top to bottom.  This extension allows those buffers to be upside down
+    when accessed top to bottom.
+
+    This extension defines a new framebuffer parameter,
+    GL_FRAMEBUFFER_FLIP_Y_MESA, that changes the behavior of the reads and
+    writes to the framebuffer attachment points. When GL_FRAMEBUFFER_FLIP_Y_MESA
+    is GL_TRUE, render commands and pixel transfer operations access the
+    backing store of each attachment point with an y-inverted coordinate
+    system. This y-inversion is relative to the coordinate system set when
+    GL_FRAMEBUFFER_FLIP_Y_MESA is GL_FALSE.
+
+    Access through TexSubImage2D and similar calls will notice the effect of
+    the flip when they are not attached to framebuffer objects because
+    GL_FRAMEBUFFER_FLIP_Y_MESA is associated with the framebuffer object and
+    not the attachment points.
+
+IP Status
+
+    None
+
+Issues
+
+    None
+
+New Procedures and Functions
+
+    None
+
+New Types
+
+    None
+
+New Tokens
+
+    Accepted by the <pname> argument of FramebufferParameteri and
+    GetFramebufferParameteriv:
+
+        GL_FRAMEBUFFER_FLIP_Y_MESA                      0x8BBB
+
+Errors
+    GL_INVALID_OPERATION is returned from  GetFramebufferParameteriv if this
+    is called on a winsys framebuffer.
+
+Revision History
+
+    Version 1, June, 2018
+        Initial draft (Fritz Koenig)
diff --git a/include/GLES2/gl2ext.h b/include/GLES2/gl2ext.h
index a7d19a1fc8..0a93bfb865 100644
--- a/include/GLES2/gl2ext.h
+++ b/include/GLES2/gl2ext.h
@@ -2334,6 +2334,11 @@ GL_APICALL void GL_APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint quer
 #endif
 #endif /* GL_INTEL_performance_query */
 
+#ifndef GL_MESA_framebuffer_flip_y
+#define GL_MESA_framebuffer_flip_y 1
+#define GL_FRAMEBUFFER_FLIP_Y_MESA        0x8BBB
+#endif /* GL_MESA_framebuffer_flip_y */
+
 #ifndef GL_MESA_program_binary_formats
 #define GL_MESA_program_binary_formats 1
 #define GL_PROGRAM_BINARY_FORMAT_MESA     0x875F
diff --git a/src/mapi/glapi/registry/gl.xml b/src/mapi/glapi/registry/gl.xml
index 833478aa51..13882eff7b 100644
--- a/src/mapi/glapi/registry/gl.xml
+++ b/src/mapi/glapi/registry/gl.xml
@@ -6568,6 +6568,7 @@ typedef unsigned int GLhandleARB;
         <enum value="0x8BB5" name="GL_VERTEX_PROGRAM_CALLBACK_MESA"/>
         <enum value="0x8BB6" name="GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA"/>
         <enum value="0x8BB7" name="GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA"/>
+        <enum value="0x8BBB" name="GL_FRAMEBUFFER_FLIP_Y_MESA"/>
     </enums>
 
     <enums namespace="GL" start="0x8BC0" end="0x8BFF" vendor="QCOM" comment="Reassigned from AMD to QCOM">
@@ -44356,6 +44357,11 @@ typedef unsigned int GLhandleARB;
                 <enum name="GL_TEXTURE_2D_STACK_BINDING_MESAX"/>
             </require>
         </extension>
+        <extension name="GL_MESA_framebuffer_flip_y" supported="gles2">
+            <require>
+                <enum name="GL_FRAMEBUFFER_FLIP_Y_MESA"/>
+            </require>
+        </extension>
         <extension name="GL_MESA_pack_invert" supported="gl">
             <require>
                 <enum name="GL_PACK_INVERT_MESA"/>
diff --git a/src/mesa/drivers/dri/i915/intel_fbo.c b/src/mesa/drivers/dri/i915/intel_fbo.c
index 827a77f722..31b65fb53b 100644
--- a/src/mesa/drivers/dri/i915/intel_fbo.c
+++ b/src/mesa/drivers/dri/i915/intel_fbo.c
@@ -86,7 +86,8 @@ intel_map_renderbuffer(struct gl_context *ctx,
 		       GLuint x, GLuint y, GLuint w, GLuint h,
 		       GLbitfield mode,
 		       GLubyte **out_map,
-		       GLint *out_stride)
+		       GLint *out_stride,
+		       GLboolean inverted_y)
 {
    struct intel_context *intel = intel_context(ctx);
    struct swrast_renderbuffer *srb = (struct swrast_renderbuffer *)rb;
@@ -94,6 +95,10 @@ intel_map_renderbuffer(struct gl_context *ctx,
    void *map;
    int stride;
 
+   /* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+   if (rb->Name != 0)
+      assert(!inverted_y);
+
    if (srb->Buffer) {
       /* this is a malloc'd renderbuffer (accum buffer), not an irb */
       GLint bpp = _mesa_get_format_bytes(rb->Format);
diff --git a/src/mesa/drivers/dri/i965/intel_fbo.c b/src/mesa/drivers/dri/i965/intel_fbo.c
index fb84b738c0..7ef525c121 100644
--- a/src/mesa/drivers/dri/i965/intel_fbo.c
+++ b/src/mesa/drivers/dri/i965/intel_fbo.c
@@ -105,7 +105,8 @@ intel_map_renderbuffer(struct gl_context *ctx,
 		       GLuint x, GLuint y, GLuint w, GLuint h,
 		       GLbitfield mode,
 		       GLubyte **out_map,
-		       GLint *out_stride)
+		       GLint *out_stride,
+		       GLboolean inverted_y)
 {
    struct brw_context *brw = brw_context(ctx);
    struct swrast_renderbuffer *srb = (struct swrast_renderbuffer *)rb;
@@ -114,6 +115,10 @@ intel_map_renderbuffer(struct gl_context *ctx,
    void *map;
    ptrdiff_t stride;
 
+   /* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+   if (rb->Name != 0)
+      assert(!inverted_y);
+
    if (srb->Buffer) {
       /* this is a malloc'd renderbuffer (accum buffer), not an irb */
       GLint bpp = _mesa_get_format_bytes(rb->Format);
diff --git a/src/mesa/drivers/dri/nouveau/nouveau_fbo.c b/src/mesa/drivers/dri/nouveau/nouveau_fbo.c
index c78d4baa12..82583672ce 100644
--- a/src/mesa/drivers/dri/nouveau/nouveau_fbo.c
+++ b/src/mesa/drivers/dri/nouveau/nouveau_fbo.c
@@ -133,13 +133,18 @@ nouveau_renderbuffer_map(struct gl_context *ctx,
 			 GLuint x, GLuint y, GLuint w, GLuint h,
 			 GLbitfield mode,
 			 GLubyte **out_map,
-			 GLint *out_stride)
+			 GLint *out_stride,
+			 GLboolean inverted_y)
 {
 	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
 	GLubyte *map;
 	int stride;
 	int flags = 0;
 
+	/* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+	if (rb->Name != 0)
+		assert(!inverted_y);
+
 	if (mode & GL_MAP_READ_BIT)
 		flags |= NOUVEAU_BO_RD;
 	if (mode & GL_MAP_WRITE_BIT)
diff --git a/src/mesa/drivers/dri/radeon/radeon_fbo.c b/src/mesa/drivers/dri/radeon/radeon_fbo.c
index 37c9c3f094..58927c0ac6 100644
--- a/src/mesa/drivers/dri/radeon/radeon_fbo.c
+++ b/src/mesa/drivers/dri/radeon/radeon_fbo.c
@@ -226,7 +226,8 @@ radeon_map_renderbuffer(struct gl_context *ctx,
 		       GLuint x, GLuint y, GLuint w, GLuint h,
 		       GLbitfield mode,
 		       GLubyte **out_map,
-		       GLint *out_stride)
+		       GLint *out_stride,
+		       GLboolean inverted_y)
 {
    struct radeon_context *const rmesa = RADEON_CONTEXT(ctx);
    struct radeon_renderbuffer *rrb = radeon_renderbuffer(rb);
@@ -236,6 +237,10 @@ radeon_map_renderbuffer(struct gl_context *ctx,
    int ret;
    int src_x, src_y;
 
+   /* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+   if (rb->Name != 0)
+      assert(!inverted_y);
+
    if (!rrb || !rrb->bo) {
 	   *out_map = NULL;
 	   *out_stride = 0;
diff --git a/src/mesa/drivers/dri/radeon/radeon_span.c b/src/mesa/drivers/dri/radeon/radeon_span.c
index 648df5cc14..eb37d509e0 100644
--- a/src/mesa/drivers/dri/radeon/radeon_span.c
+++ b/src/mesa/drivers/dri/radeon/radeon_span.c
@@ -53,7 +53,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
 static void
-radeon_renderbuffer_map(struct gl_context *ctx, struct gl_renderbuffer *rb)
+radeon_renderbuffer_map(struct gl_context *ctx,
+												struct gl_renderbuffer *rb,
+												GLboolean inverted_y)
 {
 	struct radeon_renderbuffer *rrb = radeon_renderbuffer(rb);
 	GLubyte *map;
@@ -64,7 +66,7 @@ radeon_renderbuffer_map(struct gl_context *ctx, struct gl_renderbuffer *rb)
 
 	ctx->Driver.MapRenderbuffer(ctx, rb, 0, 0, rb->Width, rb->Height,
 				    GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-				    &map, &stride);
+				    &map, &stride, inverted_y);
 
 	rrb->base.Map = map;
 	rrb->base.RowStride = stride;
@@ -96,7 +98,8 @@ radeon_map_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb)
 
 	/* check for render to textures */
 	for (i = 0; i < BUFFER_COUNT; i++)
-		radeon_renderbuffer_map(ctx, fb->Attachment[i].Renderbuffer);
+		radeon_renderbuffer_map(ctx, fb->Attachment[i].Renderbuffer,
+			fb->InvertedY);
 
         if (_mesa_is_front_buffer_drawing(fb))
 		RADEON_CONTEXT(ctx)->front_buffer_dirty = true;
diff --git a/src/mesa/drivers/dri/swrast/swrast.c b/src/mesa/drivers/dri/swrast/swrast.c
index ae5874f592..828df8894e 100644
--- a/src/mesa/drivers/dri/swrast/swrast.c
+++ b/src/mesa/drivers/dri/swrast/swrast.c
@@ -470,13 +470,18 @@ swrast_map_renderbuffer(struct gl_context *ctx,
 			GLuint x, GLuint y, GLuint w, GLuint h,
 			GLbitfield mode,
 			GLubyte **out_map,
-			GLint *out_stride)
+			GLint *out_stride,
+			GLboolean inverted_y)
 {
    struct dri_swrast_renderbuffer *xrb = dri_swrast_renderbuffer(rb);
    GLubyte *map = xrb->Base.Buffer;
    int cpp = _mesa_get_format_bytes(rb->Format);
    int stride = rb->Width * cpp;
 
+   /* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+   if (rb->Name != 0)
+      assert(!inverted_y);
+
    if (rb->AllocStorage == swrast_alloc_front_storage) {
       __DRIdrawable *dPriv = xrb->dPriv;
       __DRIscreen *sPriv = dPriv->driScreenPriv;
diff --git a/src/mesa/drivers/osmesa/osmesa.c b/src/mesa/drivers/osmesa/osmesa.c
index 3423eb6323..ef6907d270 100644
--- a/src/mesa/drivers/osmesa/osmesa.c
+++ b/src/mesa/drivers/osmesa/osmesa.c
@@ -573,7 +573,8 @@ osmesa_MapRenderbuffer(struct gl_context *ctx,
                        struct gl_renderbuffer *rb,
                        GLuint x, GLuint y, GLuint w, GLuint h,
                        GLbitfield mode,
-                       GLubyte **mapOut, GLint *rowStrideOut)
+                       GLubyte **mapOut, GLint *rowStrideOut,
+                       GLboolean inverted_y)
 {
    const OSMesaContext osmesa = OSMESA_CONTEXT(ctx);
 
@@ -601,7 +602,7 @@ osmesa_MapRenderbuffer(struct gl_context *ctx,
    }
    else {
       _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
-                                    mapOut, rowStrideOut);
+                                    mapOut, rowStrideOut, inverted_y);
    }
 }
 
diff --git a/src/mesa/drivers/x11/xm_buffer.c b/src/mesa/drivers/x11/xm_buffer.c
index 97c7814585..536bf139e1 100644
--- a/src/mesa/drivers/x11/xm_buffer.c
+++ b/src/mesa/drivers/x11/xm_buffer.c
@@ -423,7 +423,8 @@ xmesa_MapRenderbuffer(struct gl_context *ctx,
                       struct gl_renderbuffer *rb,
                       GLuint x, GLuint y, GLuint w, GLuint h,
                       GLbitfield mode,
-                      GLubyte **mapOut, GLint *rowStrideOut)
+                      GLubyte **mapOut, GLint *rowStrideOut,
+                      GLboolean inverted_y)
 {
    struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
 
diff --git a/src/mesa/drivers/x11/xmesaP.h b/src/mesa/drivers/x11/xmesaP.h
index ff3ddc4ddd..fb1960712e 100644
--- a/src/mesa/drivers/x11/xmesaP.h
+++ b/src/mesa/drivers/x11/xmesaP.h
@@ -358,7 +358,8 @@ xmesa_MapRenderbuffer(struct gl_context *ctx,
                       struct gl_renderbuffer *rb,
                       GLuint x, GLuint y, GLuint w, GLuint h,
                       GLbitfield mode,
-                      GLubyte **mapOut, GLint *rowStrideOut);
+                      GLubyte **mapOut, GLint *rowStrideOut,
+                      GLboolean inverted_y);
 
 extern void
 xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb);
diff --git a/src/mesa/main/accum.c b/src/mesa/main/accum.c
index f5ac8a1027..4a6ba41390 100644
--- a/src/mesa/main/accum.c
+++ b/src/mesa/main/accum.c
@@ -82,7 +82,8 @@ _mesa_clear_accum_buffer(struct gl_context *ctx)
    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
 
    ctx->Driver.MapRenderbuffer(ctx, accRb, x, y, width, height,
-                               GL_MAP_WRITE_BIT, &accMap, &accRowStride);
+                               GL_MAP_WRITE_BIT, &accMap, &accRowStride,
+                               ctx->DrawBuffer->InvertedY);
 
    if (!accMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
@@ -137,7 +138,8 @@ accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
 
    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
                                GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-                               &accMap, &accRowStride);
+                               &accMap, &accRowStride,
+                               ctx->DrawBuffer->InvertedY);
 
    if (!accMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
@@ -206,7 +208,8 @@ accum_or_load(struct gl_context *ctx, GLfloat value,
 
    /* Map accum buffer */
    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
-                               mappingFlags, &accMap, &accRowStride);
+                               mappingFlags, &accMap, &accRowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!accMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
       return;
@@ -215,7 +218,8 @@ accum_or_load(struct gl_context *ctx, GLfloat value,
    /* Map color buffer */
    ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
                                GL_MAP_READ_BIT,
-                               &colorMap, &colorRowStride);
+                               &colorMap, &colorRowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!colorMap) {
       ctx->Driver.UnmapRenderbuffer(ctx, accRb);
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
@@ -288,7 +292,7 @@ accum_return(struct gl_context *ctx, GLfloat value,
    /* Map accum buffer */
    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
                                GL_MAP_READ_BIT,
-                               &accMap, &accRowStride);
+                               &accMap, &accRowStride, fb->InvertedY);
    if (!accMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
       return;
@@ -308,7 +312,8 @@ accum_return(struct gl_context *ctx, GLfloat value,
 
       /* Map color buffer */
       ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
-                                  mappingFlags, &colorMap, &colorRowStride);
+                                  mappingFlags, &colorMap, &colorRowStride,
+                                  fb->InvertedY);
       if (!colorMap) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
          continue;
diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h
index 1b048d3ff8..448388ec26 100644
--- a/src/mesa/main/dd.h
+++ b/src/mesa/main/dd.h
@@ -429,7 +429,8 @@ struct dd_function_table {
 			   struct gl_renderbuffer *rb,
 			   GLuint x, GLuint y, GLuint w, GLuint h,
 			   GLbitfield mode,
-			   GLubyte **mapOut, GLint *rowStrideOut);
+			   GLubyte **mapOut, GLint *rowStrideOut,
+			   GLboolean inverted_y);
 
    void (*UnmapRenderbuffer)(struct gl_context *ctx,
 			     struct gl_renderbuffer *rb);
diff --git a/src/mesa/main/extensions_table.h b/src/mesa/main/extensions_table.h
index 7af48a4ad9..29ef35c20a 100644
--- a/src/mesa/main/extensions_table.h
+++ b/src/mesa/main/extensions_table.h
@@ -323,6 +323,7 @@ EXT(KHR_texture_compression_astc_hdr        , KHR_texture_compression_astc_hdr
 EXT(KHR_texture_compression_astc_ldr        , KHR_texture_compression_astc_ldr       , GLL, GLC,  x , ES2, 2012)
 EXT(KHR_texture_compression_astc_sliced_3d  , KHR_texture_compression_astc_sliced_3d , GLL, GLC,  x , ES2, 2015)
 
+EXT(MESA_framebuffer_flip_y                 , MESA_framebuffer_flip_y                ,   x,   x,  x ,  31, 2018)
 EXT(MESA_pack_invert                        , MESA_pack_invert                       , GLL, GLC,  x ,  x , 2002)
 EXT(MESA_shader_integer_functions           , MESA_shader_integer_functions          , GLL, GLC,  x ,  30, 2016)
 EXT(MESA_texture_signed_rgba                , EXT_texture_snorm                      , GLL, GLC,  x ,  x , 2009)
diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c
index fa7a9361df..962a19446c 100644
--- a/src/mesa/main/fbobject.c
+++ b/src/mesa/main/fbobject.c
@@ -1430,6 +1430,10 @@ framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb,
       if (!ctx->Extensions.ARB_sample_locations)
          goto invalid_pname_enum;
       break;
+   case GL_FRAMEBUFFER_FLIP_Y_MESA:
+      if (!ctx->Extensions.MESA_framebuffer_flip_y)
+         goto invalid_pname_enum;
+      cannot_be_winsys_fbo = true;
    default:
       goto invalid_pname_enum;
    }
@@ -1482,6 +1486,9 @@ framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb,
    case GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB:
       fb->SampleLocationPixelGrid = !!param;
       break;
+   case GL_FRAMEBUFFER_FLIP_Y_MESA:
+      fb->InvertedY = param;
+      break;
    }
 
    switch (pname) {
@@ -1489,7 +1496,7 @@ framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb,
    case GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB:
       if (fb == ctx->DrawBuffer)
          ctx->NewDriverState |= ctx->DriverFlags.NewSampleLocations;
-      break;
+       break;
    default:
       invalidate_framebuffer(fb);
       ctx->NewState |= _NEW_BUFFERS;
@@ -1574,6 +1581,12 @@ validate_get_framebuffer_parameteriv_pname(struct gl_context *ctx,
          goto invalid_pname_enum;
       cannot_be_winsys_fbo = false;
       break;
+   case GL_FRAMEBUFFER_FLIP_Y_MESA:
+      if (!ctx->Extensions.MESA_framebuffer_flip_y) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname);
+         return false;
+      }
+      break;
    default:
       goto invalid_pname_enum;
    }
@@ -1638,6 +1651,9 @@ get_framebuffer_parameteriv(struct gl_context *ctx, struct gl_framebuffer *fb,
    case GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB:
       *params = fb->SampleLocationPixelGrid;
       break;
+   case GL_FRAMEBUFFER_FLIP_Y_MESA:
+      *params = fb->InvertedY;
+      break;
    }
 }
 
diff --git a/src/mesa/main/framebuffer.c b/src/mesa/main/framebuffer.c
index 3ec8b91eaa..c66bab91a0 100644
--- a/src/mesa/main/framebuffer.c
+++ b/src/mesa/main/framebuffer.c
@@ -159,6 +159,7 @@ _mesa_initialize_window_framebuffer(struct gl_framebuffer *fb,
    fb->_AllColorBuffersFixedPoint = !visual->floatMode;
    fb->_HasSNormOrFloatColorBuffer = visual->floatMode;
    fb->_HasAttachments = true;
+   fb->InvertedY = true;
 
    fb->SampleLocationTable = NULL;
    fb->ProgrammableSampleLocations = 0;
diff --git a/src/mesa/main/glheader.h b/src/mesa/main/glheader.h
index 16648820b1..1a91d54312 100644
--- a/src/mesa/main/glheader.h
+++ b/src/mesa/main/glheader.h
@@ -160,6 +160,9 @@ typedef void *GLeglImageOES;
 #define GL_HALF_FLOAT_OES                                       0x8D61
 #endif
 
+#ifndef GL_MESA_framebuffer_flip_y
+#define GL_FRAMEBUFFER_FLIP_Y_MESA                              0x8BBB
+#endif
 
 /**
  * Internal token to represent a GLSL shader program (a collection of
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index 0dfff31396..605fa8a564 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -3509,6 +3509,8 @@ struct gl_framebuffer
 
    /** Delete this framebuffer */
    void (*Delete)(struct gl_framebuffer *fb);
+
+   GLboolean InvertedY;
 };
 
 
@@ -4253,6 +4255,7 @@ struct gl_extensions
    GLboolean KHR_texture_compression_astc_hdr;
    GLboolean KHR_texture_compression_astc_ldr;
    GLboolean KHR_texture_compression_astc_sliced_3d;
+   GLboolean MESA_framebuffer_flip_y;
    GLboolean MESA_tile_raster_order;
    GLboolean MESA_pack_invert;
    GLboolean EXT_shader_framebuffer_fetch;
diff --git a/src/mesa/main/readpix.c b/src/mesa/main/readpix.c
index c5fc66988b..7cda5a0620 100644
--- a/src/mesa/main/readpix.c
+++ b/src/mesa/main/readpix.c
@@ -234,7 +234,7 @@ readpixels_memcpy(struct gl_context *ctx,
 					   format, type, 0, 0);
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-			       &map, &stride);
+			       &map, &stride, ctx->ReadBuffer->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return GL_TRUE;  /* don't bother trying the slow path */
@@ -285,7 +285,7 @@ read_uint_depth_pixels( struct gl_context *ctx,
       return GL_FALSE;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-			       &map, &stride);
+			       &map, &stride, fb->InvertedY);
 
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
@@ -343,7 +343,7 @@ read_depth_pixels( struct gl_context *ctx,
 					   GL_DEPTH_COMPONENT, type, 0, 0);
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-			       &map, &stride);
+			       &map, &stride, fb->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return;
@@ -391,7 +391,7 @@ read_stencil_pixels( struct gl_context *ctx,
       return;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-			       &map, &stride);
+			       &map, &stride, fb->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return;
@@ -462,7 +462,7 @@ read_rgba_pixels( struct gl_context *ctx,
 
    /* Map the source render buffer */
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-                               &map, &rb_stride);
+                               &map, &rb_stride, fb->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return;
@@ -652,7 +652,7 @@ fast_read_depth_stencil_pixels(struct gl_context *ctx,
       return GL_FALSE;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
-			       &map, &stride);
+			       &map, &stride, fb->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return GL_TRUE;  /* don't bother trying the slow path */
@@ -692,14 +692,14 @@ fast_read_depth_stencil_pixels_separate(struct gl_context *ctx,
       return GL_FALSE;
 
    ctx->Driver.MapRenderbuffer(ctx, depthRb, x, y, width, height,
-			       GL_MAP_READ_BIT, &depthMap, &depthStride);
+			       GL_MAP_READ_BIT, &depthMap, &depthStride, fb->InvertedY);
    if (!depthMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return GL_TRUE;  /* don't bother trying the slow path */
    }
 
    ctx->Driver.MapRenderbuffer(ctx, stencilRb, x, y, width, height,
-			       GL_MAP_READ_BIT, &stencilMap, &stencilStride);
+			       GL_MAP_READ_BIT, &stencilMap, &stencilStride, fb->InvertedY);
    if (!stencilMap) {
       ctx->Driver.UnmapRenderbuffer(ctx, depthRb);
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
@@ -756,7 +756,7 @@ slow_read_depth_stencil_pixels_separate(struct gl_context *ctx,
     * If one buffer, only map it once.
     */
    ctx->Driver.MapRenderbuffer(ctx, depthRb, x, y, width, height,
-			       GL_MAP_READ_BIT, &depthMap, &depthStride);
+			       GL_MAP_READ_BIT, &depthMap, &depthStride, fb->InvertedY);
    if (!depthMap) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
       return;
@@ -765,7 +765,7 @@ slow_read_depth_stencil_pixels_separate(struct gl_context *ctx,
    if (stencilRb != depthRb) {
       ctx->Driver.MapRenderbuffer(ctx, stencilRb, x, y, width, height,
                                   GL_MAP_READ_BIT, &stencilMap,
-                                  &stencilStride);
+                                  &stencilStride, fb->InvertedY);
       if (!stencilMap) {
          ctx->Driver.UnmapRenderbuffer(ctx, depthRb);
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
diff --git a/src/mesa/state_tracker/st_cb_fbo.c b/src/mesa/state_tracker/st_cb_fbo.c
index b851db6488..9e3e13702a 100644
--- a/src/mesa/state_tracker/st_cb_fbo.c
+++ b/src/mesa/state_tracker/st_cb_fbo.c
@@ -770,7 +770,8 @@ st_MapRenderbuffer(struct gl_context *ctx,
                    struct gl_renderbuffer *rb,
                    GLuint x, GLuint y, GLuint w, GLuint h,
                    GLbitfield mode,
-                   GLubyte **mapOut, GLint *rowStrideOut)
+                   GLubyte **mapOut, GLint *rowStrideOut,
+                   GLboolean inverted_y)
 {
    struct st_context *st = st_context(ctx);
    struct st_renderbuffer *strb = st_renderbuffer(rb);
@@ -779,6 +780,10 @@ st_MapRenderbuffer(struct gl_context *ctx,
    GLuint y2;
    GLubyte *map;
 
+   /* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
+   if (rb->Name != 0)
+      assert(!inverted_y);
+
    if (strb->software) {
       /* software-allocated renderbuffer (probably an accum buffer) */
       if (strb->data) {
diff --git a/src/mesa/swrast/s_blit.c b/src/mesa/swrast/s_blit.c
index 19fe8484eb..929295f3c2 100644
--- a/src/mesa/swrast/s_blit.c
+++ b/src/mesa/swrast/s_blit.c
@@ -253,7 +253,7 @@ blit_nearest(struct gl_context *ctx,
          ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0,
                                      readRb->Width, readRb->Height,
                                      GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-                                     &map, &rowStride);
+                                     &map, &rowStride, readFb->InvertedY);
          if (!map) {
             goto fail_no_memory;
          }
@@ -280,14 +280,16 @@ blit_nearest(struct gl_context *ctx,
          ctx->Driver.MapRenderbuffer(ctx, readRb,
                                      srcXpos, srcYpos,
                                      srcWidth, srcHeight,
-                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride,
+                                     readFb->InvertedY);
          if (!srcMap) {
             goto fail_no_memory;
          }
          ctx->Driver.MapRenderbuffer(ctx, drawRb,
                                      dstXpos, dstYpos,
                                      dstWidth, dstHeight,
-                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+                                     drawFb->InvertedY);
          if (!dstMap) {
             ctx->Driver.UnmapRenderbuffer(ctx, readRb);
             goto fail_no_memory;
@@ -594,7 +596,8 @@ blit_linear(struct gl_context *ctx,
          ctx->Driver.MapRenderbuffer(ctx, readRb,
                                      0, 0, readRb->Width, readRb->Height,
                                      GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-                                     &srcMap, &srcRowStride);
+                                     &srcMap, &srcRowStride,
+                                     readFb->InvertedY);
          if (!srcMap) {
             goto fail_no_memory;
          }
@@ -609,13 +612,15 @@ blit_linear(struct gl_context *ctx,
           */
          ctx->Driver.MapRenderbuffer(ctx, readRb,
                                      0, 0, readRb->Width, readRb->Height,
-                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride,
+                                     readFb->InvertedY);
          if (!srcMap) {
             goto fail_no_memory;
          }
          ctx->Driver.MapRenderbuffer(ctx, drawRb,
                                      0, 0, drawRb->Width, drawRb->Height,
-                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+                                     drawFb->InvertedY);
          if (!dstMap) {
             ctx->Driver.UnmapRenderbuffer(ctx, readRb);
             goto fail_no_memory;
diff --git a/src/mesa/swrast/s_clear.c b/src/mesa/swrast/s_clear.c
index ddafb67c98..787c7d8fdb 100644
--- a/src/mesa/swrast/s_clear.c
+++ b/src/mesa/swrast/s_clear.c
@@ -66,7 +66,8 @@ clear_rgba_buffer(struct gl_context *ctx, struct gl_renderbuffer *rb,
 
    /* map dest buffer */
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               mapMode, &map, &rowStride);
+                               mapMode, &map, &rowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClear(color)");
       return;
diff --git a/src/mesa/swrast/s_copypix.c b/src/mesa/swrast/s_copypix.c
index 0dbccc0f61..2532c31adc 100644
--- a/src/mesa/swrast/s_copypix.c
+++ b/src/mesa/swrast/s_copypix.c
@@ -503,7 +503,7 @@ swrast_fast_copy_pixels(struct gl_context *ctx,
       ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
                                   srcRb->Width, srcRb->Height,
                                   GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-                                  &map, &rowStride);
+                                  &map, &rowStride, srcFb->InvertedY);
       if (!map) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
          return GL_TRUE; /* don't retry with slow path */
@@ -530,14 +530,16 @@ swrast_fast_copy_pixels(struct gl_context *ctx,
       /* different src/dst buffers */
       ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
                                   width, height,
-                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride,
+                                  srcFb->InvertedY);
       if (!srcMap) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
          return GL_TRUE; /* don't retry with slow path */
       }
       ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
                                   width, height,
-                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+                                  dstFb->InvertedY);
       if (!dstMap) {
          ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
@@ -598,7 +600,8 @@ map_readbuffer(struct gl_context *ctx, GLenum type)
    ctx->Driver.MapRenderbuffer(ctx, rb,
                                0, 0, rb->Width, rb->Height,
                                GL_MAP_READ_BIT,
-                               &srb->Map, &srb->RowStride);
+                               &srb->Map, &srb->RowStride,
+                               fb->InvertedY);
 
    return rb;
 }
diff --git a/src/mesa/swrast/s_depth.c b/src/mesa/swrast/s_depth.c
index ffadc05a73..2ad4e56497 100644
--- a/src/mesa/swrast/s_depth.c
+++ b/src/mesa/swrast/s_depth.c
@@ -570,7 +570,8 @@ _swrast_clear_depth_buffer(struct gl_context *ctx)
    }
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               mapMode, &map, &rowStride);
+                               mapMode, &map, &rowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClear(depth)");
       return;
@@ -695,7 +696,8 @@ _swrast_clear_depth_stencil_buffer(struct gl_context *ctx)
    }
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               mapMode, &map, &rowStride);
+                               mapMode, &map, &rowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClear(depth+stencil)");
       return;
diff --git a/src/mesa/swrast/s_drawpix.c b/src/mesa/swrast/s_drawpix.c
index f05528d0d2..99a962b080 100644
--- a/src/mesa/swrast/s_drawpix.c
+++ b/src/mesa/swrast/s_drawpix.c
@@ -55,7 +55,8 @@ fast_draw_rgb_ubyte_pixels(struct gl_context *ctx,
                            GLint x, GLint y,
                            GLsizei width, GLsizei height,
                            const struct gl_pixelstore_attrib *unpack,
-                           const GLvoid *pixels)
+                           const GLvoid *pixels,
+                           GLboolean inverted_y)
 {
    const GLubyte *src = (const GLubyte *)
       _mesa_image_address2d(unpack, pixels, width,
@@ -67,7 +68,8 @@ fast_draw_rgb_ubyte_pixels(struct gl_context *ctx,
    GLint dstRowStride;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               GL_MAP_WRITE_BIT, &dst, &dstRowStride);
+                               GL_MAP_WRITE_BIT, &dst, &dstRowStride,
+                               inverted_y);
 
    if (!dst) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
@@ -102,7 +104,8 @@ fast_draw_rgba_ubyte_pixels(struct gl_context *ctx,
                            GLint x, GLint y,
                            GLsizei width, GLsizei height,
                            const struct gl_pixelstore_attrib *unpack,
-                           const GLvoid *pixels)
+                           const GLvoid *pixels,
+                           GLboolean inverted_y)
 {
    const GLubyte *src = (const GLubyte *)
       _mesa_image_address2d(unpack, pixels, width,
@@ -114,7 +117,8 @@ fast_draw_rgba_ubyte_pixels(struct gl_context *ctx,
    GLint dstRowStride;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               GL_MAP_WRITE_BIT, &dst, &dstRowStride);
+                               GL_MAP_WRITE_BIT, &dst, &dstRowStride,
+                               inverted_y);
 
    if (!dst) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
@@ -151,7 +155,8 @@ fast_draw_generic_pixels(struct gl_context *ctx,
                          GLsizei width, GLsizei height,
                          GLenum format, GLenum type,
                          const struct gl_pixelstore_attrib *unpack,
-                         const GLvoid *pixels)
+                         const GLvoid *pixels,
+                         GLboolean inverted_y)
 {
    const GLubyte *src = (const GLubyte *)
       _mesa_image_address2d(unpack, pixels, width,
@@ -164,7 +169,8 @@ fast_draw_generic_pixels(struct gl_context *ctx,
    GLint dstRowStride;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               GL_MAP_WRITE_BIT, &dst, &dstRowStride);
+                               GL_MAP_WRITE_BIT, &dst, &dstRowStride,
+                               inverted_y);
 
    if (!dst) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
@@ -197,6 +203,7 @@ fast_draw_rgba_pixels(struct gl_context *ctx, GLint x, GLint y,
                       const struct gl_pixelstore_attrib *userUnpack,
                       const GLvoid *pixels)
 {
+   struct gl_framebuffer *fb = ctx->DrawBuffer;
    struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0];
    SWcontext *swrast = SWRAST_CONTEXT(ctx);
    struct gl_pixelstore_attrib unpack;
@@ -228,7 +235,7 @@ fast_draw_rgba_pixels(struct gl_context *ctx, GLint x, GLint y,
        (rb->Format == MESA_FORMAT_B8G8R8X8_UNORM ||
         rb->Format == MESA_FORMAT_B8G8R8A8_UNORM)) {
       fast_draw_rgb_ubyte_pixels(ctx, rb, x, y, width, height,
-                                 &unpack, pixels);
+                                 &unpack, pixels, fb->InvertedY);
       return GL_TRUE;
    }
 
@@ -237,14 +244,15 @@ fast_draw_rgba_pixels(struct gl_context *ctx, GLint x, GLint y,
        (rb->Format == MESA_FORMAT_B8G8R8X8_UNORM ||
         rb->Format == MESA_FORMAT_B8G8R8A8_UNORM)) {
       fast_draw_rgba_ubyte_pixels(ctx, rb, x, y, width, height,
-                                  &unpack, pixels);
+                                  &unpack, pixels, fb->InvertedY);
       return GL_TRUE;
    }
 
    if (_mesa_format_matches_format_and_type(rb->Format, format, type,
                                             ctx->Unpack.SwapBytes, NULL)) {
       fast_draw_generic_pixels(ctx, rb, x, y, width, height,
-                               format, type, &unpack, pixels);
+                               format, type, &unpack, pixels,
+                               fb->InvertedY);
       return GL_TRUE;
    }
 
diff --git a/src/mesa/swrast/s_renderbuffer.c b/src/mesa/swrast/s_renderbuffer.c
index f76489c200..6756f66842 100644
--- a/src/mesa/swrast/s_renderbuffer.c
+++ b/src/mesa/swrast/s_renderbuffer.c
@@ -180,7 +180,8 @@ _swrast_map_soft_renderbuffer(struct gl_context *ctx,
                               GLuint x, GLuint y, GLuint w, GLuint h,
                               GLbitfield mode,
                               GLubyte **out_map,
-                              GLint *out_stride)
+                              GLint *out_stride,
+                              GLboolean inverted_y)
 {
    struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
    GLubyte *map = srb->Buffer;
@@ -578,7 +579,8 @@ map_attachment(struct gl_context *ctx,
       ctx->Driver.MapRenderbuffer(ctx, rb,
                                   0, 0, rb->Width, rb->Height,
                                   GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
-                                  &srb->Map, &srb->RowStride);
+                                  &srb->Map, &srb->RowStride,
+                                  fb->InvertedY);
    }
 
    assert(srb->Map);
diff --git a/src/mesa/swrast/s_renderbuffer.h b/src/mesa/swrast/s_renderbuffer.h
index 2595d7c177..0f82a1b83b 100644
--- a/src/mesa/swrast/s_renderbuffer.h
+++ b/src/mesa/swrast/s_renderbuffer.h
@@ -43,7 +43,8 @@ _swrast_map_soft_renderbuffer(struct gl_context *ctx,
                               GLuint x, GLuint y, GLuint w, GLuint h,
                               GLbitfield mode,
                               GLubyte **out_map,
-                              GLint *out_stride);
+                              GLint *out_stride,
+                              GLboolean inverted_y);
 
 extern void
 _swrast_unmap_soft_renderbuffer(struct gl_context *ctx,
diff --git a/src/mesa/swrast/s_stencil.c b/src/mesa/swrast/s_stencil.c
index 7a4dc45ae8..3f0f069198 100644
--- a/src/mesa/swrast/s_stencil.c
+++ b/src/mesa/swrast/s_stencil.c
@@ -579,7 +579,8 @@ _swrast_clear_stencil_buffer(struct gl_context *ctx)
    }
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
-                               mapMode, &map, &rowStride);
+                               mapMode, &map, &rowStride,
+                               ctx->DrawBuffer->InvertedY);
    if (!map) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClear(stencil)");
       return;
-- 
2.18.0.399.gad0ab374a1-goog



More information about the mesa-dev mailing list