llvmpipe: improve rasterization discard logic
[mesa.git] / src / gallium / state_trackers / osmesa / osmesa.c
index 35fa3381d0453c4c5761a9690e41e4a236ace01c..8baec0a0e44deb0162939a64851f5cbdd4ed1c57 100644 (file)
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
 
 
@@ -31,7 +32,7 @@
  * may be set to "softpipe" or "llvmpipe" to override.
  *
  * With softpipe we could render directly into the user's buffer by using a
- * display target resource.  However, softpipe doesn't suport "upside-down"
+ * display target resource.  However, softpipe doesn't support "upside-down"
  * rendering which would be needed for the OSMESA_Y_UP=TRUE case.
  *
  * With llvmpipe we could only render directly into the user's buffer when its
@@ -48,6 +49,7 @@
  */
 
 
+#include <stdio.h>
 #include "GL/osmesa.h"
 
 #include "glapi/glapi.h"  /* for OSMesaGetProcAddress below */
 
 #include "util/u_atomic.h"
 #include "util/u_box.h"
+#include "util/u_debug.h"
 #include "util/u_format.h"
+#include "util/u_inlines.h"
 #include "util/u_memory.h"
 
+#include "postprocess/filters.h"
+#include "postprocess/postprocess.h"
+
 #include "state_tracker/st_api.h"
 #include "state_tracker/st_gl_api.h"
 
@@ -80,6 +87,8 @@ struct osmesa_buffer
    struct pipe_resource *textures[ST_ATTACHMENT_COUNT];
 
    void *map;
+
+   struct osmesa_buffer *next;  /**< next in linked list */
 };
 
 
@@ -87,6 +96,8 @@ struct osmesa_context
 {
    struct st_context_iface *stctx;
 
+   boolean ever_used;     /*< Has this context ever been current? */
+
    struct osmesa_buffer *current_buffer;
 
    enum pipe_format depth_stencil_format, accum_format;
@@ -96,9 +107,23 @@ struct osmesa_context
    GLint user_row_length; /*< user-specified number of pixels per row */
    GLboolean y_up;        /*< TRUE  -> Y increases upward */
                           /*< FALSE -> Y increases downward */
+
+   /** Which postprocessing filters are enabled. */
+   unsigned pp_enabled[PP_FILTERS];
+   struct pp_queue_t *pp;
 };
 
 
+/**
+ * Linked list of all osmesa_buffers.
+ * We can re-use an osmesa_buffer from one OSMesaMakeCurrent() call to
+ * the next unless the color/depth/stencil/accum formats change.
+ * We have to do this to be compatible with the original OSMesa implementation
+ * because some apps call OSMesaMakeCurrent() several times during rendering
+ * a frame.
+ */
+static struct osmesa_buffer *BufferList = NULL;
+
 
 /**
  * Called from the ST manager.
@@ -144,7 +169,7 @@ get_st_manager(void)
 }
 
 
-static INLINE boolean
+static inline boolean
 little_endian(void)
 {
    const unsigned ui = 1;
@@ -251,6 +276,12 @@ osmesa_init_st_visual(struct st_visual *vis,
                       enum pipe_format accum_format)
 {
    vis->buffer_mask = ST_ATTACHMENT_FRONT_LEFT_MASK;
+
+   if (ds_format != PIPE_FORMAT_NONE)
+      vis->buffer_mask |= ST_ATTACHMENT_DEPTH_STENCIL_MASK;
+   if (accum_format != PIPE_FORMAT_NONE)
+      vis->buffer_mask |= ST_ATTACHMENT_ACCUM;
+
    vis->color_format = color_format;
    vis->depth_stencil_format = ds_format;
    vis->accum_format = accum_format;
@@ -262,7 +293,7 @@ osmesa_init_st_visual(struct st_visual *vis,
 /**
  * Return the osmesa_buffer that corresponds to an st_framebuffer_iface.
  */
-static INLINE struct osmesa_buffer *
+static inline struct osmesa_buffer *
 stfbi_to_osbuffer(struct st_framebuffer_iface *stfbi)
 {
    return (struct osmesa_buffer *) stfbi->st_manager_private;
@@ -289,6 +320,28 @@ osmesa_st_framebuffer_flush_front(struct st_context_iface *stctx,
    unsigned y, bytes, bpp;
    int dst_stride;
 
+   if (osmesa->pp) {
+      struct pipe_resource *zsbuf = NULL;
+      unsigned i;
+
+      /* Find the z/stencil buffer if there is one */
+      for (i = 0; i < ARRAY_SIZE(osbuffer->textures); i++) {
+         struct pipe_resource *res = osbuffer->textures[i];
+         if (res) {
+            const struct util_format_description *desc =
+               util_format_description(res->format);
+
+            if (util_format_has_depth(desc)) {
+               zsbuf = res;
+               break;
+            }
+         }
+      }
+
+      /* run the postprocess stage(s) */
+      pp_run(osmesa->pp, res, res, zsbuf);
+   }
+
    u_box_2d(0, 0, res->width0, res->height0, &box);
 
    map = pipe->transfer_map(pipe, res, 0, PIPE_TRANSFER_READ, &box,
@@ -329,7 +382,8 @@ osmesa_st_framebuffer_flush_front(struct st_context_iface *stctx,
  * its resources).
  */
 static boolean
-osmesa_st_framebuffer_validate(struct st_framebuffer_iface *stfbi,
+osmesa_st_framebuffer_validate(struct st_context_iface *stctx,
+                               struct st_framebuffer_iface *stfbi,
                                const enum st_attachment_type *statts,
                                unsigned count,
                                struct pipe_resource **out)
@@ -352,8 +406,8 @@ osmesa_st_framebuffer_validate(struct st_framebuffer_iface *stfbi,
    templat.flags = 0;
 
    for (i = 0; i < count; i++) {
-      enum pipe_format format;
-      unsigned bind;
+      enum pipe_format format = PIPE_FORMAT_NONE;
+      unsigned bind = 0;
 
       /*
        * At this time, we really only need to handle the front-left color
@@ -372,16 +426,22 @@ osmesa_st_framebuffer_validate(struct st_framebuffer_iface *stfbi,
          format = osbuffer->visual.accum_format;
          bind = PIPE_BIND_RENDER_TARGET;
       }
+      else {
+         debug_warning("Unexpected attachment type in "
+                       "osmesa_st_framebuffer_validate()");
+      }
 
       templat.format = format;
       templat.bind = bind;
-      out[i] = osbuffer->textures[i] =
+      pipe_resource_reference(&out[i], NULL);
+      out[i] = osbuffer->textures[statts[i]] =
          screen->resource_create(screen, &templat);
    }
 
    return TRUE;
 }
 
+static uint32_t osmesa_fb_ID = 0;
 
 static struct st_framebuffer_iface *
 osmesa_create_st_framebuffer(void)
@@ -391,11 +451,16 @@ osmesa_create_st_framebuffer(void)
       stfbi->flush_front = osmesa_st_framebuffer_flush_front;
       stfbi->validate = osmesa_st_framebuffer_validate;
       p_atomic_set(&stfbi->stamp, 1);
+      stfbi->ID = p_atomic_inc_return(&osmesa_fb_ID);
+      stfbi->state_manager = get_st_manager();
    }
    return stfbi;
 }
 
 
+/**
+ * Create new buffer and add to linked list.
+ */
 static struct osmesa_buffer *
 osmesa_create_buffer(enum pipe_format color_format,
                      enum pipe_format ds_format,
@@ -410,14 +475,52 @@ osmesa_create_buffer(enum pipe_format color_format,
 
       osmesa_init_st_visual(&osbuffer->visual, color_format,
                             ds_format, accum_format);
+
+      /* insert into linked list */
+      osbuffer->next = BufferList;
+      BufferList = osbuffer;
    }
+
    return osbuffer;
 }
 
 
+/**
+ * Search linked list for a buffer with matching pixel formats and size.
+ */
+static struct osmesa_buffer *
+osmesa_find_buffer(enum pipe_format color_format,
+                   enum pipe_format ds_format,
+                   enum pipe_format accum_format,
+                   GLsizei width, GLsizei height)
+{
+   struct osmesa_buffer *b;
+
+   /* Check if we already have a suitable buffer for the given formats */
+   for (b = BufferList; b; b = b->next) {
+      if (b->visual.color_format == color_format &&
+          b->visual.depth_stencil_format == ds_format &&
+          b->visual.accum_format == accum_format &&
+          b->width == width &&
+          b->height == height) {
+         return b;
+      }
+   }
+   return NULL;
+}
+
+
 static void
 osmesa_destroy_buffer(struct osmesa_buffer *osbuffer)
 {
+   struct st_api *stapi = get_st_api();
+
+   /*
+    * Notify the state manager that the associated framebuffer interface
+    * is no longer valid.
+    */
+   stapi->destroy_drawable(stapi, osbuffer->stfb);
+
    FREE(osbuffer->stfb);
    FREE(osbuffer);
 }
@@ -453,12 +556,40 @@ OSMesaCreateContext(GLenum format, OSMesaContext sharelist)
 GLAPI OSMesaContext GLAPIENTRY
 OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits,
                        GLint accumBits, OSMesaContext sharelist)
+{
+   int attribs[100], n = 0;
+
+   attribs[n++] = OSMESA_FORMAT;
+   attribs[n++] = format;
+   attribs[n++] = OSMESA_DEPTH_BITS;
+   attribs[n++] = depthBits;
+   attribs[n++] = OSMESA_STENCIL_BITS;
+   attribs[n++] = stencilBits;
+   attribs[n++] = OSMESA_ACCUM_BITS;
+   attribs[n++] = accumBits;
+   attribs[n++] = 0;
+
+   return OSMesaCreateContextAttribs(attribs, sharelist);
+}
+
+
+/**
+ * New in Mesa 11.2
+ *
+ * Create context with attribute list.
+ */
+GLAPI OSMesaContext GLAPIENTRY
+OSMesaCreateContextAttribs(const int *attribList, OSMesaContext sharelist)
 {
    OSMesaContext osmesa;
    struct st_context_iface *st_shared;
    enum st_context_error st_error = 0;
    struct st_context_attribs attribs;
    struct st_api *stapi = get_st_api();
+   GLenum format = GL_RGBA;
+   int depthBits = 0, stencilBits = 0, accumBits = 0;
+   int profile = OSMESA_COMPAT_PROFILE, version_major = 1, version_minor = 0;
+   int i;
 
    if (sharelist) {
       st_shared = sharelist->stctx;
@@ -467,6 +598,64 @@ OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits,
       st_shared = NULL;
    }
 
+   for (i = 0; attribList[i]; i += 2) {
+      switch (attribList[i]) {
+      case OSMESA_FORMAT:
+         format = attribList[i+1];
+         switch (format) {
+         case OSMESA_COLOR_INDEX:
+         case OSMESA_RGBA:
+         case OSMESA_BGRA:
+         case OSMESA_ARGB:
+         case OSMESA_RGB:
+         case OSMESA_BGR:
+         case OSMESA_RGB_565:
+            /* legal */
+            break;
+         default:
+            return NULL;
+         }
+         break;
+      case OSMESA_DEPTH_BITS:
+         depthBits = attribList[i+1];
+         if (depthBits < 0)
+            return NULL;
+         break;
+      case OSMESA_STENCIL_BITS:
+         stencilBits = attribList[i+1];
+         if (stencilBits < 0)
+            return NULL;
+         break;
+      case OSMESA_ACCUM_BITS:
+         accumBits = attribList[i+1];
+         if (accumBits < 0)
+            return NULL;
+         break;
+      case OSMESA_PROFILE:
+         profile = attribList[i+1];
+         if (profile != OSMESA_CORE_PROFILE &&
+             profile != OSMESA_COMPAT_PROFILE)
+            return NULL;
+         break;
+      case OSMESA_CONTEXT_MAJOR_VERSION:
+         version_major = attribList[i+1];
+         if (version_major < 1)
+            return NULL;
+         break;
+      case OSMESA_CONTEXT_MINOR_VERSION:
+         version_minor = attribList[i+1];
+         if (version_minor < 0)
+            return NULL;
+         break;
+      case 0:
+         /* end of list */
+         break;
+      default:
+         fprintf(stderr, "Bad attribute in OSMesaCreateContextAttribs()\n");
+         return NULL;
+      }
+   }
+
    osmesa = (OSMesaContext) CALLOC_STRUCT(osmesa_context);
    if (!osmesa)
       return NULL;
@@ -491,11 +680,17 @@ OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits,
    /*
     * Create the rendering context
     */
-   attribs.profile = ST_PROFILE_DEFAULT;
-   attribs.major = 2;
-   attribs.minor = 1;
+   memset(&attribs, 0, sizeof(attribs));
+   attribs.profile = (profile == OSMESA_CORE_PROFILE)
+      ? ST_PROFILE_OPENGL_CORE : ST_PROFILE_DEFAULT;
+   attribs.major = version_major;
+   attribs.minor = version_minor;
    attribs.flags = 0;  /* ST_CONTEXT_FLAG_x */
    attribs.options.force_glsl_extensions_warn = FALSE;
+   attribs.options.disable_blend_func_extended = FALSE;
+   attribs.options.disable_glsl_line_continuations = FALSE;
+   attribs.options.disable_shader_bit_encoding = FALSE;
+   attribs.options.force_glsl_version = 0;
 
    osmesa_init_st_visual(&attribs.visual,
                          PIPE_FORMAT_R8G8B8A8_UNORM,
@@ -519,6 +714,7 @@ OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits,
 }
 
 
+
 /**
  * Destroy an Off-Screen Mesa rendering context.
  *
@@ -528,6 +724,7 @@ GLAPI void GLAPIENTRY
 OSMesaDestroyContext(OSMesaContext osmesa)
 {
    if (osmesa) {
+      pp_free(osmesa->pp);
       osmesa->stctx->destroy(osmesa->stctx);
       FREE(osmesa);
    }
@@ -564,10 +761,11 @@ OSMesaMakeCurrent(OSMesaContext osmesa, void *buffer, GLenum type,
    struct osmesa_buffer *osbuffer;
    enum pipe_format color_format;
 
-   if (osmesa->format == OSMESA_RGB_565 && type != GL_UNSIGNED_SHORT_5_6_5) {
+   if (!osmesa || !buffer || width < 1 || height < 1) {
       return GL_FALSE;
    }
-   if (width < 1 || height < 1) {
+
+   if (osmesa->format == OSMESA_RGB_565 && type != GL_UNSIGNED_SHORT_5_6_5) {
       return GL_FALSE;
    }
 
@@ -577,24 +775,52 @@ OSMesaMakeCurrent(OSMesaContext osmesa, void *buffer, GLenum type,
       return GL_FALSE;
    }
 
-   osbuffer = osmesa_create_buffer(color_format,
-                                   osmesa->depth_stencil_format,
-                                   osmesa->accum_format);
+   /* See if we already have a buffer that uses these pixel formats */
+   osbuffer = osmesa_find_buffer(color_format,
+                                 osmesa->depth_stencil_format,
+                                 osmesa->accum_format, width, height);
+   if (!osbuffer) {
+      /* Existing buffer found, create new buffer */
+      osbuffer = osmesa_create_buffer(color_format,
+                                      osmesa->depth_stencil_format,
+                                      osmesa->accum_format);
+   }
 
    osbuffer->width = width;
    osbuffer->height = height;
    osbuffer->map = buffer;
 
-   if (osmesa->current_buffer) {
-      /* free old buffer */
-      osmesa_destroy_buffer(osmesa->current_buffer);
-   }
+   /* XXX unused for now */
+   (void) osmesa_destroy_buffer;
 
    osmesa->current_buffer = osbuffer;
    osmesa->type = type;
 
    stapi->make_current(stapi, osmesa->stctx, osbuffer->stfb, osbuffer->stfb);
 
+   if (!osmesa->ever_used) {
+      /* one-time init, just postprocessing for now */
+      boolean any_pp_enabled = FALSE;
+      unsigned i;
+
+      for (i = 0; i < ARRAY_SIZE(osmesa->pp_enabled); i++) {
+         if (osmesa->pp_enabled[i]) {
+            any_pp_enabled = TRUE;
+            break;
+         }
+      }
+
+      if (any_pp_enabled) {
+         osmesa->pp = pp_init(osmesa->stctx->pipe,
+                              osmesa->pp_enabled,
+                              osmesa->stctx->cso_context);
+
+         pp_init_fbos(osmesa->pp, width, height);
+      }
+
+      osmesa->ever_used = TRUE;
+   }
+
    return GL_TRUE;
 }
 
@@ -663,7 +889,6 @@ OSMesaGetIntegerv(GLint pname, GLint *value)
          int maxLevels = screen->get_param(screen,
                                            PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
          *value = 1 << (maxLevels - 1);
-         *value = 8 * 1024;
       }
       return;
    default:
@@ -758,15 +983,17 @@ struct name_function
 static struct name_function functions[] = {
    { "OSMesaCreateContext", (OSMESAproc) OSMesaCreateContext },
    { "OSMesaCreateContextExt", (OSMESAproc) OSMesaCreateContextExt },
+   { "OSMesaCreateContextAttribs", (OSMESAproc) OSMesaCreateContextAttribs },
    { "OSMesaDestroyContext", (OSMESAproc) OSMesaDestroyContext },
    { "OSMesaMakeCurrent", (OSMESAproc) OSMesaMakeCurrent },
    { "OSMesaGetCurrentContext", (OSMESAproc) OSMesaGetCurrentContext },
-   { "OSMesaPixelsStore", (OSMESAproc) OSMesaPixelStore },
+   { "OSMesaPixelStore", (OSMESAproc) OSMesaPixelStore },
    { "OSMesaGetIntegerv", (OSMESAproc) OSMesaGetIntegerv },
    { "OSMesaGetDepthBuffer", (OSMESAproc) OSMesaGetDepthBuffer },
    { "OSMesaGetColorBuffer", (OSMESAproc) OSMesaGetColorBuffer },
    { "OSMesaGetProcAddress", (OSMESAproc) OSMesaGetProcAddress },
    { "OSMesaColorClamp", (OSMESAproc) OSMesaColorClamp },
+   { "OSMesaPostprocess", (OSMESAproc) OSMesaPostprocess },
    { NULL, NULL }
 };
 
@@ -791,3 +1018,27 @@ OSMesaColorClamp(GLboolean enable)
    _mesa_ClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
                     enable ? GL_TRUE : GL_FIXED_ONLY_ARB);
 }
+
+
+GLAPI void GLAPIENTRY
+OSMesaPostprocess(OSMesaContext osmesa, const char *filter,
+                  unsigned enable_value)
+{
+   if (!osmesa->ever_used) {
+      /* We can only enable/disable postprocess filters before a context
+       * is made current for the first time.
+       */
+      unsigned i;
+
+      for (i = 0; i < PP_FILTERS; i++) {
+         if (strcmp(pp_filters[i].name, filter) == 0) {
+            osmesa->pp_enabled[i] = enable_value;
+            return;
+         }
+      }
+      debug_warning("OSMesaPostprocess(unknown filter)\n");
+   }
+   else {
+      debug_warning("Calling OSMesaPostprocess() after OSMesaMakeCurrent()\n");
+   }
+}