/** * Try to do a glCopyPixels for simple cases with a blit by calling * pipe->resource_copy_region(). * * We can do this when we're copying color pixels (depth/stencil * eventually) with no pixel zoom, no pixel transfer ops, no * per-fragment ops, the src/dest regions don't overlap and the * src/dest pixel formats are the same. */ static GLboolean blit_copy_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, GLsizei width, GLsizei height, GLint dstx, GLint dsty, GLenum type) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct gl_pixelstore_attrib pack, unpack; GLint readX, readY, readW, readH; if (type == GL_COLOR && ctx->Pixel.ZoomX == 1.0 && ctx->Pixel.ZoomY == 1.0 && ctx->_ImageTransferState == 0x0 && !ctx->Color.BlendEnabled && !ctx->Color.AlphaEnabled && !ctx->Depth.Test && !ctx->Fog.Enabled && !ctx->Stencil.Enabled && !ctx->FragmentProgram.Enabled && !ctx->VertexProgram.Enabled && !ctx->Shader.CurrentFragmentProgram && st_fb_orientation(ctx->ReadBuffer) == st_fb_orientation(ctx->DrawBuffer) && ctx->DrawBuffer->_NumColorDrawBuffers == 1 && !ctx->Query.CondRenderQuery) { struct st_renderbuffer *rbRead, *rbDraw; GLint drawX, drawY; /* * Clip the read region against the src buffer bounds. * We'll still allocate a temporary buffer/texture for the original * src region size but we'll only read the region which is on-screen. * This may mean that we draw garbage pixels into the dest region, but * that's expected. */ readX = srcx; readY = srcy; readW = width; readH = height; pack = ctx->DefaultPacking; if (!_mesa_clip_readpixels(ctx, &readX, &readY, &readW, &readH, &pack)) return GL_TRUE; /* all done */ /* clip against dest buffer bounds and scissor box */ drawX = dstx + pack.SkipPixels; drawY = dsty + pack.SkipRows; unpack = pack; if (!_mesa_clip_drawpixels(ctx, &drawX, &drawY, &readW, &readH, &unpack)) return GL_TRUE; /* all done */ readX = readX - pack.SkipPixels + unpack.SkipPixels; readY = readY - pack.SkipRows + unpack.SkipRows; rbRead = st_get_color_read_renderbuffer(ctx); rbDraw = st_renderbuffer(ctx->DrawBuffer->_ColorDrawBuffers[0]); if ((rbRead != rbDraw || !regions_overlap(readX, readY, drawX, drawY, readW, readH)) && rbRead->Base.Format == rbDraw->Base.Format) { struct pipe_box srcBox; /* flip src/dst position if needed */ if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { /* both buffers will have the same orientation */ readY = ctx->ReadBuffer->Height - readY - readH; drawY = ctx->DrawBuffer->Height - drawY - readH; } u_box_2d(readX, readY, readW, readH, &srcBox); pipe->resource_copy_region(pipe, rbDraw->texture, rbDraw->rtt_level, drawX, drawY, 0, rbRead->texture, rbRead->rtt_level, &srcBox); return GL_TRUE; } } return GL_FALSE; }
static void st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, GLsizei width, GLsizei height, GLint dstx, GLint dsty, GLenum type) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; struct st_renderbuffer *rbRead; void *driver_vp, *driver_fp; struct pipe_resource *pt; struct pipe_sampler_view *sv[2]; int num_sampler_view = 1; GLfloat *color; enum pipe_format srcFormat, texFormat; GLboolean invertTex = GL_FALSE; GLint readX, readY, readW, readH; GLuint sample_count; struct gl_pixelstore_attrib pack = ctx->DefaultPacking; struct st_fp_variant *fpv; st_validate_state(st); if (type == GL_STENCIL) { /* can't use texturing to do stencil */ copy_stencil_pixels(ctx, srcx, srcy, width, height, dstx, dsty); return; } if (blit_copy_pixels(ctx, srcx, srcy, width, height, dstx, dsty, type)) return; /* * The subsequent code implements glCopyPixels by copying the source * pixels into a temporary texture that's then applied to a textured quad. * When we draw the textured quad, all the usual per-fragment operations * are handled. */ /* * Get vertex/fragment shaders */ if (type == GL_COLOR) { rbRead = st_get_color_read_renderbuffer(ctx); color = NULL; fpv = get_color_fp_variant(st); driver_fp = fpv->driver_shader; driver_vp = make_passthrough_vertex_shader(st, GL_FALSE); if (st->pixel_xfer.pixelmap_enabled) { sv[1] = st->pixel_xfer.pixelmap_sampler_view; num_sampler_view++; } } else { assert(type == GL_DEPTH); rbRead = st_renderbuffer(ctx->ReadBuffer->_DepthBuffer); color = ctx->Current.Attrib[VERT_ATTRIB_COLOR0]; fpv = get_depth_stencil_fp_variant(st, GL_TRUE, GL_FALSE); driver_fp = fpv->driver_shader; driver_vp = make_passthrough_vertex_shader(st, GL_TRUE); } /* update fragment program constants */ st_upload_constants(st, fpv->parameters, PIPE_SHADER_FRAGMENT); if (rbRead->Base.Wrapped) rbRead = st_renderbuffer(rbRead->Base.Wrapped); sample_count = rbRead->texture->nr_samples; /* I believe this would be legal, presumably would need to do a resolve for color, and for depth/stencil spec says to just use one of the depth/stencil samples per pixel? Need some transfer clarifications. */ assert(sample_count < 2); srcFormat = rbRead->texture->format; if (screen->is_format_supported(screen, srcFormat, st->internal_target, sample_count, PIPE_BIND_SAMPLER_VIEW)) { texFormat = srcFormat; } else { /* srcFormat can't be used as a texture format */ if (type == GL_DEPTH) { texFormat = st_choose_format(screen, GL_DEPTH_COMPONENT, GL_NONE, GL_NONE, st->internal_target, sample_count, PIPE_BIND_DEPTH_STENCIL); assert(texFormat != PIPE_FORMAT_NONE); } else { /* default color format */ texFormat = st_choose_format(screen, GL_RGBA, GL_NONE, GL_NONE, st->internal_target, sample_count, PIPE_BIND_SAMPLER_VIEW); assert(texFormat != PIPE_FORMAT_NONE); } } /* Invert src region if needed */ if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { srcy = ctx->ReadBuffer->Height - srcy - height; invertTex = !invertTex; } /* Clip the read region against the src buffer bounds. * We'll still allocate a temporary buffer/texture for the original * src region size but we'll only read the region which is on-screen. * This may mean that we draw garbage pixels into the dest region, but * that's expected. */ readX = srcx; readY = srcy; readW = width; readH = height; _mesa_clip_readpixels(ctx, &readX, &readY, &readW, &readH, &pack); readW = MAX2(0, readW); readH = MAX2(0, readH); /* alloc temporary texture */ pt = alloc_texture(st, width, height, texFormat); if (!pt) return; sv[0] = st_create_texture_sampler_view(st->pipe, pt); if (!sv[0]) { pipe_resource_reference(&pt, NULL); return; } /* Make temporary texture which is a copy of the src region. */ if (srcFormat == texFormat) { struct pipe_box src_box; u_box_2d(readX, readY, readW, readH, &src_box); /* copy source framebuffer surface into mipmap/texture */ pipe->resource_copy_region(pipe, pt, /* dest tex */ 0, /* dest lvl */ pack.SkipPixels, pack.SkipRows, 0, /* dest pos */ rbRead->texture, /* src tex */ rbRead->rtt_level, /* src lvl */ &src_box); } else { /* CPU-based fallback/conversion */ struct pipe_transfer *ptRead = pipe_get_transfer(st->pipe, rbRead->texture, rbRead->rtt_level, rbRead->rtt_face + rbRead->rtt_slice, PIPE_TRANSFER_READ, readX, readY, readW, readH); struct pipe_transfer *ptTex; enum pipe_transfer_usage transfer_usage; if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback processing\n", __FUNCTION__); if (type == GL_DEPTH && util_format_is_depth_and_stencil(pt->format)) transfer_usage = PIPE_TRANSFER_READ_WRITE; else transfer_usage = PIPE_TRANSFER_WRITE; ptTex = pipe_get_transfer(st->pipe, pt, 0, 0, transfer_usage, 0, 0, width, height); /* copy image from ptRead surface to ptTex surface */ if (type == GL_COLOR) { /* alternate path using get/put_tile() */ GLfloat *buf = (GLfloat *) malloc(width * height * 4 * sizeof(GLfloat)); enum pipe_format readFormat, drawFormat; readFormat = util_format_linear(rbRead->texture->format); drawFormat = util_format_linear(pt->format); pipe_get_tile_rgba_format(pipe, ptRead, 0, 0, readW, readH, readFormat, buf); pipe_put_tile_rgba_format(pipe, ptTex, pack.SkipPixels, pack.SkipRows, readW, readH, drawFormat, buf); free(buf); } else { /* GL_DEPTH */ GLuint *buf = (GLuint *) malloc(width * height * sizeof(GLuint)); pipe_get_tile_z(pipe, ptRead, 0, 0, readW, readH, buf); pipe_put_tile_z(pipe, ptTex, pack.SkipPixels, pack.SkipRows, readW, readH, buf); free(buf); } pipe->transfer_destroy(pipe, ptRead); pipe->transfer_destroy(pipe, ptTex); } /* OK, the texture 'pt' contains the src image/pixels. Now draw a * textured quad with that texture. */ draw_textured_quad(ctx, dstx, dsty, ctx->Current.RasterPos[2], width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY, sv, num_sampler_view, driver_vp, driver_fp, color, invertTex, GL_FALSE, GL_FALSE); pipe_resource_reference(&pt, NULL); pipe_sampler_view_reference(&sv[0], NULL); }
static void st_CopyPixels(GLcontext *ctx, GLint srcx, GLint srcy, GLsizei width, GLsizei height, GLint dstx, GLint dsty, GLenum type) { struct st_context *st = ctx->st; struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; struct st_renderbuffer *rbRead; struct st_vertex_program *stvp; struct st_fragment_program *stfp; struct pipe_texture *pt; GLfloat *color; enum pipe_format srcFormat, texFormat; pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL); st_validate_state(st); if (srcx < 0) { width -= -srcx; dstx += -srcx; srcx = 0; } if (srcy < 0) { height -= -srcy; dsty += -srcy; srcy = 0; } if (dstx < 0) { width -= -dstx; srcx += -dstx; dstx = 0; } if (dsty < 0) { height -= -dsty; srcy += -dsty; dsty = 0; } if (width < 0 || height < 0) return; if (type == GL_STENCIL) { /* can't use texturing to do stencil */ copy_stencil_pixels(ctx, srcx, srcy, width, height, dstx, dsty); return; } if (type == GL_COLOR) { rbRead = st_get_color_read_renderbuffer(ctx); color = NULL; stfp = combined_drawpix_fragment_program(ctx); stvp = st_make_passthrough_vertex_shader(ctx->st, GL_FALSE); } else { assert(type == GL_DEPTH); rbRead = st_renderbuffer(ctx->ReadBuffer->_DepthBuffer); color = ctx->Current.Attrib[VERT_ATTRIB_COLOR0]; stfp = make_fragment_shader_z(ctx->st); stvp = st_make_passthrough_vertex_shader(ctx->st, GL_TRUE); } srcFormat = rbRead->texture->format; if (screen->is_format_supported(screen, srcFormat, PIPE_TEXTURE_2D, PIPE_TEXTURE_USAGE_SAMPLER, 0)) { texFormat = srcFormat; } else { /* srcFormat can't be used as a texture format */ if (type == GL_DEPTH) { texFormat = st_choose_format(pipe, GL_DEPTH_COMPONENT, PIPE_TEXTURE_2D, PIPE_TEXTURE_USAGE_DEPTH_STENCIL); assert(texFormat != PIPE_FORMAT_NONE); /* XXX no depth texture formats??? */ } else { /* default color format */ texFormat = st_choose_format(pipe, GL_RGBA, PIPE_TEXTURE_2D, PIPE_TEXTURE_USAGE_SAMPLER); assert(texFormat != PIPE_FORMAT_NONE); } } if (st_fb_orientation(ctx->DrawBuffer) == Y_0_TOP) { srcy = ctx->DrawBuffer->Height - srcy - height; if (srcy < 0) { height -= -srcy; srcy = 0; } if (height < 0) return; } pt = st_texture_create(ctx->st, PIPE_TEXTURE_2D, texFormat, 0, width, height, 1, PIPE_TEXTURE_USAGE_SAMPLER); if (!pt) return; if (srcFormat == texFormat) { /* copy source framebuffer surface into mipmap/texture */ struct pipe_surface *psRead = screen->get_tex_surface(screen, rbRead->texture, 0, 0, 0, PIPE_BUFFER_USAGE_GPU_READ); struct pipe_surface *psTex = screen->get_tex_surface(screen, pt, 0, 0, 0, PIPE_BUFFER_USAGE_GPU_WRITE ); pipe->surface_copy(pipe, psTex, /* dest */ 0, 0, /* destx/y */ psRead, srcx, srcy, width, height); pipe_surface_reference(&psRead, NULL); pipe_surface_reference(&psTex, NULL); } else { /* CPU-based fallback/conversion */ struct pipe_transfer *ptRead = st_cond_flush_get_tex_transfer(st, rbRead->texture, 0, 0, 0, PIPE_TRANSFER_READ, srcx, srcy, width, height); struct pipe_transfer *ptTex; enum pipe_transfer_usage transfer_usage; if (type == GL_DEPTH && pf_is_depth_and_stencil(pt->format)) transfer_usage = PIPE_TRANSFER_READ_WRITE; else transfer_usage = PIPE_TRANSFER_WRITE; ptTex = st_cond_flush_get_tex_transfer(st, pt, 0, 0, 0, transfer_usage, 0, 0, width, height); if (type == GL_COLOR) { /* alternate path using get/put_tile() */ GLfloat *buf = (GLfloat *) _mesa_malloc(width * height * 4 * sizeof(GLfloat)); pipe_get_tile_rgba(ptRead, 0, 0, width, height, buf); pipe_put_tile_rgba(ptTex, 0, 0, width, height, buf); _mesa_free(buf); } else { /* GL_DEPTH */ GLuint *buf = (GLuint *) _mesa_malloc(width * height * sizeof(GLuint)); pipe_get_tile_z(ptRead, 0, 0, width, height, buf); pipe_put_tile_z(ptTex, 0, 0, width, height, buf); _mesa_free(buf); } screen->tex_transfer_destroy(ptRead); screen->tex_transfer_destroy(ptTex); } /* draw textured quad */ draw_textured_quad(ctx, dstx, dsty, ctx->Current.RasterPos[2], width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY, pt, stvp, stfp, color, GL_TRUE); pipe_texture_reference(&pt, NULL); }