mesa: handle some cases of 0x0 render targets
[mesa.git] / src / mesa / state_tracker / st_cb_fbo.c
index fc8a5ea7f621c7565a9b0598882108a8b488ae3d..45a05421f8ecd56437458364db221a3de80eefac 100644 (file)
@@ -77,7 +77,6 @@ init_renderbuffer_bits(struct st_renderbuffer *strb,
    return info.size;
 }
 
-
 /**
  * gl_renderbuffer::AllocStorage()
  * This is called to allocate the original drawing surface, and
@@ -90,80 +89,99 @@ st_renderbuffer_alloc_storage(GLcontext * ctx, struct gl_renderbuffer *rb,
 {
    struct pipe_context *pipe = ctx->st->pipe;
    struct st_renderbuffer *strb = st_renderbuffer(rb);
-   enum pipe_format pipeFormat;
-   GLbitfield flags = 0x0; /* XXX needed? */
-   int ret;
-
-   if (!strb->surface) {
-      /* first time surface creation */
-      strb->surface = pipe->winsys->surface_alloc(pipe->winsys);
-      assert(strb->surface);
-      assert(strb->surface->refcount);
-      assert(strb->surface->winsys);
-      if (!strb->surface)
-         return GL_FALSE;
+   struct pipe_texture template;
+   unsigned surface_usage;
+
+   /* Free the old surface and texture
+    */
+   pipe_surface_reference( &strb->surface, NULL );
+   pipe_texture_reference( &strb->texture, NULL );
+
+
+   memset(&template, 0, sizeof(template));
+
+   if (strb->format != PIPE_FORMAT_NONE) {
+      template.format = strb->format;
    }
-   else if (strb->surface->buffer) {
-      /* release/discard the old surface buffer */
-      pipe_reference_buffer(pipe, &strb->surface->buffer, NULL);
+   else {
+      template.format = st_choose_renderbuffer_format(pipe, internalFormat);
    }
 
-   /* Determine surface format here */
-   if (strb->format != PIPE_FORMAT_NONE) {
-      assert(strb->format != 0);
-      /* we'll hit this for front/back color bufs */
-      pipeFormat = strb->format;
+   strb->Base.Width  = width;
+   strb->Base.Height = height;
+   init_renderbuffer_bits(strb, template.format);
+
+   template.target = PIPE_TEXTURE_2D;
+   template.compressed = 0;
+   pf_get_block(template.format, &template.block);
+   template.width[0] = width;
+   template.height[0] = height;
+   template.depth[0] = 1;
+   template.last_level = 0;
+   template.nr_samples = rb->Samples;
+
+   if (pf_is_depth_stencil(template.format)) {
+      template.tex_usage = PIPE_TEXTURE_USAGE_DEPTH_STENCIL;
    }
    else {
-      pipeFormat = st_choose_renderbuffer_format(pipe, internalFormat);
+      template.tex_usage = (PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
+                            PIPE_TEXTURE_USAGE_RENDER_TARGET);
    }
 
-   init_renderbuffer_bits(strb, pipeFormat);
-
-   ret = pipe->winsys->surface_alloc_storage(pipe->winsys,
-                                             strb->surface,
-                                             width,
-                                             height,
-                                             pipeFormat,
-                                             flags);
-   if (ret || !strb->surface->buffer) {
-      if (pipeFormat == DEFAULT_ACCUM_PIPE_FORMAT) {
-         /* Accum buffer.  Try a different surface format.  Since accum
-          * buffers are s/w only for now, the surface pixel format doesn't
-          * really matter, only that the buffer is large enough.
-          */
-         int sz, mult;
-         enum pipe_format accum_format;
-
-         /* allocate a buffer of (typically) double height to get 64bpp */
-         accum_format = st_choose_renderbuffer_format(pipe, GL_RGBA);
-         sz = pf_get_size(accum_format);
-         mult = pf_get_size(DEFAULT_ACCUM_PIPE_FORMAT) / sz;
-
-         ret = pipe->winsys->surface_alloc_storage(pipe->winsys,
-                                                   strb->surface,
-                                                   width, height * mult,
-                                                   accum_format, flags);
-         if (ret)
-            return GL_FALSE; /* we've _really_ failed */
-
-      }
-      else {
-         return GL_FALSE; /* out of memory, try s/w buffer? */
-      }
+
+   /* Probably need dedicated flags for surface usage too: 
+    */
+   surface_usage = (PIPE_BUFFER_USAGE_GPU_READ |
+                    PIPE_BUFFER_USAGE_GPU_WRITE);
+#if 0
+                    PIPE_BUFFER_USAGE_CPU_READ |
+                    PIPE_BUFFER_USAGE_CPU_WRITE);
+#endif
+
+   strb->texture = pipe->screen->texture_create( pipe->screen,
+                                                 &template );
+
+   /* Special path for accum buffers.  
+    *
+    * Try a different surface format.  Since accum buffers are s/w
+    * only for now, the surface pixel format doesn't really matter,
+    * only that the buffer is large enough.
+    */
+   if (!strb->texture && template.format == DEFAULT_ACCUM_PIPE_FORMAT) 
+   {
+      /* Actually, just setting this usage value should be sufficient
+       * to tell the driver to go ahead and allocate the buffer, even
+       * if HW doesn't support the format.
+       */
+      template.tex_usage = 0;
+      surface_usage = (PIPE_BUFFER_USAGE_CPU_READ |
+                       PIPE_BUFFER_USAGE_CPU_WRITE);
+
+      strb->texture = pipe->screen->texture_create( pipe->screen,
+                                                    &template );
+
    }
 
-   ASSERT(strb->surface->buffer);
-   ASSERT(strb->surface->format);
-   ASSERT(strb->surface->cpp);
-   ASSERT(strb->surface->width == width);
-   /*ASSERT(strb->surface->height == height);*/
-   ASSERT(strb->surface->pitch);
+   if (!strb->texture) 
+      return FALSE;
+
+   strb->surface = pipe->screen->get_tex_surface( pipe->screen,
+                                                  strb->texture,
+                                                  0, 0, 0,
+                                                  surface_usage );
+
+   assert(strb->surface->texture);
+   assert(strb->surface->buffer);
+   assert(strb->surface->format);
+   assert(strb->surface->block.size);
+   assert(strb->surface->block.width);
+   assert(strb->surface->block.height);
+   assert(strb->surface->width == width);
+   assert(strb->surface->height == height);
+   assert(strb->surface->stride);
 
-   strb->Base.Width  = width;
-   strb->Base.Height = height;
 
-   return GL_TRUE;
+   return strb->surface != NULL;
 }
 
 
@@ -175,10 +193,8 @@ st_renderbuffer_delete(struct gl_renderbuffer *rb)
 {
    struct st_renderbuffer *strb = st_renderbuffer(rb);
    ASSERT(strb);
-   if (strb->surface) {
-      struct pipe_winsys *ws = strb->surface->winsys;
-      ws->surface_release(ws, &strb->surface);
-   }
+   pipe_surface_reference(&strb->surface, NULL);
+   pipe_texture_reference(&strb->texture, NULL);
    free(strb);
 }
 
@@ -235,7 +251,7 @@ st_new_renderbuffer(GLcontext *ctx, GLuint name)
  * renderbuffer).  The window system code determines the format.
  */
 struct gl_renderbuffer *
-st_new_renderbuffer_fb(enum pipe_format format)
+st_new_renderbuffer_fb(enum pipe_format format, int samples)
 {
    struct st_renderbuffer *strb;
 
@@ -247,6 +263,7 @@ st_new_renderbuffer_fb(enum pipe_format format)
 
    _mesa_init_renderbuffer(&strb->Base, 0);
    strb->Base.ClassID = 0x4242; /* just a unique value */
+   strb->Base.Samples = samples;
    strb->format = format;
 
    switch (format) {
@@ -335,12 +352,13 @@ st_render_texture(GLcontext *ctx,
                   struct gl_framebuffer *fb,
                   struct gl_renderbuffer_attachment *att)
 {
-   struct st_context *st = ctx->st;
    struct st_renderbuffer *strb;
    struct gl_renderbuffer *rb;
-   struct pipe_context *pipe = st->pipe;
-   struct pipe_screen *screen = pipe->screen;
    struct pipe_texture *pt;
+   struct st_texture_object *stObj;
+   const struct gl_texture_image *texImage =
+      att->Texture->Image[att->CubeMapFace][att->TextureLevel];
+
 
    assert(!att->Renderbuffer);
 
@@ -357,23 +375,30 @@ st_render_texture(GLcontext *ctx,
    strb = st_renderbuffer(rb);
 
    /* get the texture for the texture object */
+   stObj = st_texture_object(att->Texture);
+
+   /* point renderbuffer at texobject */
+   strb->rtt = stObj;
+   strb->rtt_level = att->TextureLevel;
+   strb->rtt_face = att->CubeMapFace;
+   strb->rtt_slice = att->Zoffset;
+
+   rb->Width = texImage->Width2;
+   rb->Height = texImage->Height2;
+   /*printf("***** render to texture level %d: %d x %d\n", att->TextureLevel, rb->Width, rb->Height);*/
+
    pt = st_get_texobj_texture(att->Texture);
-   assert(pt);
-   assert(pt->width[att->TextureLevel]);
 
-   rb->Width = pt->width[att->TextureLevel];
-   rb->Height = pt->height[att->TextureLevel];
+   pipe_texture_reference( &strb->texture, pt );
 
-   /* the renderbuffer's surface is inside the texture */
-   strb->surface = screen->get_tex_surface(screen, pt,
-                                           att->CubeMapFace,
-                                           att->TextureLevel,
-                                           att->Zoffset);
-   assert(strb->surface);
-   assert(screen->is_format_supported(screen, strb->surface->format, PIPE_TEXTURE));
-   assert(screen->is_format_supported(screen, strb->surface->format, PIPE_SURFACE));
+   pipe_surface_reference(&strb->surface, NULL);
 
-   init_renderbuffer_bits(strb, pt->format);
+   /* the new surface will be created during framebuffer validation */
+
+   if (pt) {
+      /*printf("***** pipe texture %d x %d\n", pt->width[0], pt->height[0]);*/
+      init_renderbuffer_bits(strb, pt->format);
+   }
 
    /*
    printf("RENDER TO TEXTURE obj=%p pt=%p surf=%p  %d x %d\n",
@@ -396,22 +421,23 @@ static void
 st_finish_render_texture(GLcontext *ctx,
                          struct gl_renderbuffer_attachment *att)
 {
+   struct pipe_screen *screen = ctx->st->pipe->screen;
    struct st_renderbuffer *strb = st_renderbuffer(att->Renderbuffer);
 
-   assert(strb);
+   if (!strb)
+      return;
+
+   st_flush( ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL );
 
-   ctx->st->pipe->flush(ctx->st->pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
+   if (strb->surface)
+      screen->tex_surface_release( screen, &strb->surface );
 
-   ctx->st->pipe->texture_update(ctx->st->pipe,
-                                 st_get_texobj_texture(att->Texture),
-                                 att->CubeMapFace, 1 << att->TextureLevel);
+   strb->rtt = NULL;
 
    /*
    printf("FINISH RENDER TO TEXTURE surf=%p\n", strb->surface);
    */
 
-   pipe_surface_reference(&strb->surface, NULL);
-
    _mesa_reference_renderbuffer(&att->Renderbuffer, NULL);
 
    /* restore previous framebuffer state */