From: Nicolai Hähnle Date: Tue, 26 Apr 2016 18:19:28 +0000 (-0500) Subject: st/mesa: implement PBO downloads for ReadPixels X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ed0e9862c56d13381f0414b5a835fd31960edfe6;p=mesa.git st/mesa: implement PBO downloads for ReadPixels 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 Reviewed-by: Marek Olšák --- diff --git a/src/mesa/state_tracker/st_cb_readpixels.c b/src/mesa/state_tracker/st_cb_readpixels.c index 62ddc99a947..6df3a39c053 100644 --- a/src/mesa/state_tracker/st_cb_readpixels.c +++ b/src/mesa/state_tracker/st_cb_readpixels.c @@ -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; diff --git a/src/mesa/state_tracker/st_pbo.c b/src/mesa/state_tracker/st_pbo.c index f8787148ea6..ca6bdea1b66 100644 --- a/src/mesa/state_tracker/st_pbo.c +++ b/src/mesa/state_tracker/st_pbo.c @@ -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; diff --git a/src/mesa/state_tracker/st_pbo.h b/src/mesa/state_tracker/st_pbo.h index 14ae6ba23e6..17359c47733 100644 --- a/src/mesa/state_tracker/st_pbo.h +++ b/src/mesa/state_tracker/st_pbo.h @@ -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);