st/mesa: implement PBO downloads for ReadPixels
authorNicolai Hähnle <nicolai.haehnle@amd.com>
Tue, 26 Apr 2016 18:19:28 +0000 (13:19 -0500)
committerNicolai Hähnle <nicolai.haehnle@amd.com>
Wed, 1 Jun 2016 20:37:51 +0000 (22:37 +0200)
v2: require PIPE_CAP_SAMPLER_VIEW_TARGET; technically only needed for some of
    the texture targets, but all hardware that has shader images should also
    have this cap.

Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
src/mesa/state_tracker/st_cb_readpixels.c
src/mesa/state_tracker/st_pbo.c
src/mesa/state_tracker/st_pbo.h

index 62ddc99a94737fa1dcec1ba82dc9de8c3228d548..6df3a39c053f2dee7022d161ade79a89da72d854 100644 (file)
@@ -34,6 +34,7 @@
 #include "main/framebuffer.h"
 #include "util/u_inlines.h"
 #include "util/u_format.h"
+#include "cso_cache/cso_context.h"
 
 #include "st_cb_fbo.h"
 #include "st_atom.h"
@@ -42,6 +43,7 @@
 #include "st_cb_readpixels.h"
 #include "state_tracker/st_cb_texture.h"
 #include "state_tracker/st_format.h"
+#include "state_tracker/st_pbo.h"
 #include "state_tracker/st_texture.h"
 
 static boolean
@@ -71,11 +73,143 @@ needs_integer_signed_unsigned_conversion(const struct gl_context *ctx,
 
 static bool
 try_pbo_readpixels(struct st_context *st, struct st_renderbuffer *strb,
+                   bool invert_y,
                    GLint x, GLint y, GLsizei width, GLsizei height,
                    enum pipe_format src_format, enum pipe_format dst_format,
                    const struct gl_pixelstore_attrib *pack, void *pixels)
 {
-   return false;
+   struct pipe_context *pipe = st->pipe;
+   struct cso_context *cso = st->cso_context;
+   struct pipe_surface *surface = strb->surface;
+   struct pipe_resource *texture = strb->texture;
+   const struct util_format_description *desc;
+   struct st_pbo_addresses addr;
+   struct pipe_framebuffer_state fb;
+   enum pipe_texture_target view_target;
+   bool success = false;
+
+   if (texture->nr_samples > 1)
+      return false;
+
+   desc = util_format_description(dst_format);
+
+   /* Compute PBO addresses */
+   addr.bytes_per_pixel = desc->block.bits / 8;
+   addr.xoffset = x;
+   addr.yoffset = y;
+   addr.width = width;
+   addr.height = height;
+   addr.depth = 1;
+   if (!st_pbo_addresses_pixelstore(st, GL_TEXTURE_2D, false, pack, pixels, &addr))
+      return false;
+
+   cso_save_state(cso, (CSO_BIT_FRAGMENT_SAMPLER_VIEWS |
+                        CSO_BIT_FRAGMENT_SAMPLERS |
+                        CSO_BIT_FRAGMENT_IMAGE0 |
+                        CSO_BIT_VERTEX_ELEMENTS |
+                        CSO_BIT_AUX_VERTEX_BUFFER_SLOT |
+                        CSO_BIT_FRAMEBUFFER |
+                        CSO_BIT_VIEWPORT |
+                        CSO_BIT_RASTERIZER |
+                        CSO_BIT_DEPTH_STENCIL_ALPHA |
+                        CSO_BIT_STREAM_OUTPUTS |
+                        CSO_BIT_PAUSE_QUERIES |
+                        CSO_BITS_ALL_SHADERS));
+   cso_save_constant_buffer_slot0(cso, PIPE_SHADER_FRAGMENT);
+
+   /* Set up the sampler_view */
+   {
+      struct pipe_sampler_view templ;
+      struct pipe_sampler_view *sampler_view;
+      struct pipe_sampler_state sampler = {0};
+      const struct pipe_sampler_state *samplers[1] = {&sampler};
+
+      u_sampler_view_default_template(&templ, texture, src_format);
+
+      switch (texture->target) {
+      case PIPE_TEXTURE_CUBE:
+      case PIPE_TEXTURE_CUBE_ARRAY:
+         view_target = PIPE_TEXTURE_2D_ARRAY;
+         break;
+      default:
+         view_target = texture->target;
+         break;
+      }
+
+      templ.target = view_target;
+      templ.u.tex.first_level = surface->u.tex.level;
+      templ.u.tex.last_level = templ.u.tex.first_level;
+
+      if (view_target != PIPE_TEXTURE_3D) {
+         templ.u.tex.first_layer = surface->u.tex.first_layer;
+         templ.u.tex.last_layer = templ.u.tex.last_layer;
+      } else {
+         addr.constants.layer_offset = surface->u.tex.first_layer;
+      }
+
+      sampler_view = pipe->create_sampler_view(pipe, texture, &templ);
+      if (sampler_view == NULL)
+         goto fail;
+
+      cso_set_sampler_views(cso, PIPE_SHADER_FRAGMENT, 1, &sampler_view);
+
+      pipe_sampler_view_reference(&sampler_view, NULL);
+
+      cso_set_samplers(cso, PIPE_SHADER_FRAGMENT, 1, samplers);
+   }
+
+   /* Set up destination image */
+   {
+      struct pipe_image_view image;
+
+      memset(&image, 0, sizeof(image));
+      pipe_resource_reference(&image.resource, addr.buffer);
+      image.format = dst_format;
+      image.access = PIPE_IMAGE_ACCESS_WRITE;
+      image.u.buf.first_element = addr.first_element;
+      image.u.buf.last_element = addr.last_element;
+
+      cso_set_shader_images(cso, PIPE_SHADER_FRAGMENT, 0, 1, &image);
+   }
+
+   /* Set up no-attachment framebuffer */
+   memset(&fb, 0, sizeof(fb));
+   fb.width = surface->width;
+   fb.height = surface->height;
+   fb.samples = 1;
+   fb.layers = 1;
+   cso_set_framebuffer(cso, &fb);
+
+   cso_set_viewport_dims(cso, fb.width, fb.height, invert_y);
+
+   if (invert_y)
+      st_pbo_addresses_invert_y(&addr, fb.height);
+
+   {
+      struct pipe_depth_stencil_alpha_state dsa;
+      memset(&dsa, 0, sizeof(dsa));
+      cso_set_depth_stencil_alpha(cso, &dsa);
+   }
+
+   /* Set up the fragment shader */
+   {
+      void *fs = st_pbo_get_download_fs(st, view_target);
+      if (!fs)
+         goto fail;
+
+      cso_set_fragment_shader_handle(cso, fs);
+   }
+
+   success = st_pbo_draw(st, &addr, fb.width, fb.height);
+
+   /* Buffer written via shader images needs explicit synchronization. */
+   pipe->memory_barrier(pipe, PIPE_BARRIER_ALL);
+
+fail:
+   cso_restore_state(cso);
+   cso_restore_constant_buffer_slot0(cso, PIPE_SHADER_FRAGMENT);
+
+   return success;
 }
 
 /**
@@ -167,8 +301,10 @@ st_ReadPixels(struct gl_context *ctx, GLint x, GLint y,
       goto fallback;
    }
 
-   if (_mesa_is_bufferobj(pack->BufferObj)) {
-      if (try_pbo_readpixels(st, strb, x, y, width, height,
+   if (st->pbo.download_enabled && _mesa_is_bufferobj(pack->BufferObj)) {
+      if (try_pbo_readpixels(st, strb,
+                             st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP,
+                             x, y, width, height,
                              src_format, dst_format,
                              pack, pixels))
          return;
index f8787148ea6cbd7543074e32978e26ba191578df..ca6bdea1b66f078a2da26008ccdc19310e3a286e 100644 (file)
@@ -153,6 +153,22 @@ st_pbo_addresses_pixelstore(struct st_context *st,
    return true;
 }
 
+/* For download from a framebuffer, we may have to invert the Y axis. The
+ * setup is as follows:
+ * - set viewport to inverted, so that the position sysval is correct for
+ *   texel fetches
+ * - this function adjusts the fragment shader's constant buffer to compute
+ *   the correct destination addresses.
+ */
+void
+st_pbo_addresses_invert_y(struct st_pbo_addresses *addr,
+                          unsigned viewport_height)
+{
+   addr->constants.xoffset +=
+      (viewport_height - 1 + 2 * addr->constants.yoffset) * addr->constants.stride;
+   addr->constants.stride = -addr->constants.stride;
+}
+
 /* Setup all vertex pipeline state, rasterizer state, and fragment shader
  * constants, and issue the draw call for PBO upload/download.
  *
@@ -530,6 +546,7 @@ st_init_pbo_helpers(struct st_context *st)
 
    st->pbo.download_enabled =
       st->pbo.upload_enabled &&
+      screen->get_param(screen, PIPE_CAP_SAMPLER_VIEW_TARGET) &&
       screen->get_param(screen, PIPE_CAP_FRAMEBUFFER_NO_ATTACHMENT) &&
       screen->get_shader_param(screen, PIPE_SHADER_FRAGMENT,
                                        PIPE_SHADER_CAP_MAX_SHADER_IMAGES) >= 1;
index 14ae6ba23e6341d935d54ebeeda98d972def8fb4..17359c47733433202e7f70b2ed403edad328e28c 100644 (file)
@@ -70,6 +70,10 @@ st_pbo_addresses_pixelstore(struct st_context *st,
                             const void *pixels,
                             struct st_pbo_addresses *addr);
 
+void
+st_pbo_addresses_invert_y(struct st_pbo_addresses *addr,
+                          unsigned viewport_height);
+
 bool
 st_pbo_draw(struct st_context *st, const struct st_pbo_addresses *addr,
             unsigned surface_width, unsigned surface_height);