static void st_adjust_blit_for_srgb(struct pipe_blit_info *blit, bool framebuffer_srgb) { if (!framebuffer_srgb) { blit->dst.format = util_format_linear(blit->dst.format); blit->src.format = util_format_linear(blit->src.format); } }
/** * Get the format and binding of an attachment. */ void dri_drawable_get_format(struct dri_drawable *drawable, enum st_attachment_type statt, enum pipe_format *format, unsigned *bind) { switch (statt) { case ST_ATTACHMENT_FRONT_LEFT: case ST_ATTACHMENT_BACK_LEFT: case ST_ATTACHMENT_FRONT_RIGHT: case ST_ATTACHMENT_BACK_RIGHT: /* Other pieces of the driver stack get confused and behave incorrectly * when they get an sRGB drawable. st/mesa receives "drawable->stvis" * though other means and handles it correctly, so we don't really need * to use an sRGB format here. */ *format = util_format_linear(drawable->stvis.color_format); *bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW; break; case ST_ATTACHMENT_DEPTH_STENCIL: *format = drawable->stvis.depth_stencil_format; *bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */ break; default: *format = PIPE_FORMAT_NONE; *bind = 0; break; } }
static void st_adjust_blit_for_msaa_resolve(struct pipe_blit_info *blit) { /* Even though we do multisample resolves at the time of the blit, OpenGL * specification defines them as if they happen at the time of rendering, * which means that the type of averaging we do during the resolve should * only depend on the source format; the destination format should be * ignored. But, specification doesn't seem to be strict about it. * * It has been observed that mulitisample resolves produce slightly better * looking images when averaging is done using destination format. NVIDIA's * proprietary OpenGL driver also follows this approach. * * When multisampling, if the source and destination formats are equal * (aside from the color space), we choose to blit in sRGB space to get * this higher quality image. */ if (blit->src.resource->nr_samples > 1 && blit->dst.resource->nr_samples <= 1) { blit->dst.format = blit->dst.resource->format; if (util_format_is_srgb(blit->dst.resource->format)) blit->src.format = util_format_srgb(blit->src.resource->format); else blit->src.format = util_format_linear(blit->src.resource->format); } }
static void default_src_texture(struct pipe_sampler_view *src_templ, struct pipe_resource *src, unsigned srclevel) { bool cube_as_2darray = src->screen->get_param(src->screen, PIPE_CAP_SAMPLER_VIEW_TARGET); memset(src_templ, 0, sizeof(*src_templ)); if (cube_as_2darray && (src->target == PIPE_TEXTURE_CUBE || src->target == PIPE_TEXTURE_CUBE_ARRAY)) src_templ->target = PIPE_TEXTURE_2D_ARRAY; else src_templ->target = src->target; if (src->target == PIPE_BUFFER) { src_templ->target = PIPE_TEXTURE_1D; src_templ->format = PIPE_FORMAT_R8_UINT; } else { src_templ->format = util_format_linear(src->format); } src_templ->u.tex.first_level = srclevel; src_templ->u.tex.last_level = srclevel; src_templ->u.tex.first_layer = 0; src_templ->u.tex.last_layer = src->target == PIPE_TEXTURE_3D ? u_minify(src->depth0, srclevel) - 1 : (unsigned)(src->array_size - 1); src_templ->swizzle_r = PIPE_SWIZZLE_X; src_templ->swizzle_g = PIPE_SWIZZLE_Y; src_templ->swizzle_b = PIPE_SWIZZLE_Z; src_templ->swizzle_a = PIPE_SWIZZLE_W; }
static GLboolean update_single_texture(struct st_context *st, struct pipe_sampler_view **sampler_view, GLuint texUnit) { struct pipe_context *pipe = st->pipe; struct gl_context *ctx = st->ctx; const struct gl_sampler_object *samp; struct gl_texture_object *texObj; struct st_texture_object *stObj; enum pipe_format view_format; GLboolean retval; samp = _mesa_get_samplerobj(ctx, texUnit); texObj = ctx->Texture.Unit[texUnit]._Current; if (!texObj) { texObj = _mesa_get_fallback_texture(ctx, TEXTURE_2D_INDEX); samp = &texObj->Sampler; } stObj = st_texture_object(texObj); retval = st_finalize_texture(ctx, st->pipe, texObj); if (!retval) { /* out of mem */ return GL_FALSE; } /* Determine the format of the texture sampler view */ if (texObj->Target == GL_TEXTURE_BUFFER) { view_format = st_mesa_format_to_pipe_format(stObj->base._BufferObjectFormat); } else { view_format = stObj->pt->format; /* If sRGB decoding is off, use the linear format */ if (samp->sRGBDecode == GL_SKIP_DECODE_EXT) { view_format = util_format_linear(view_format); } } /* if sampler view has changed dereference it */ if (stObj->sampler_view) { if (check_sampler_swizzle(stObj->sampler_view, stObj->base._Swizzle, stObj->base.DepthMode) || (view_format != stObj->sampler_view->format) || stObj->base.BaseLevel != stObj->sampler_view->u.tex.first_level) { pipe_sampler_view_reference(&stObj->sampler_view, NULL); } } *sampler_view = st_get_texture_sampler_view_from_stobj(stObj, pipe, samp, view_format); return GL_TRUE; }
static void default_dst_texture(struct pipe_surface *dst_templ, struct pipe_resource *dst, unsigned dstlevel, unsigned dstz) { memset(dst_templ, 0, sizeof(*dst_templ)); if (dst->target == PIPE_BUFFER) dst_templ->format = PIPE_FORMAT_R8_UINT; else dst_templ->format = util_format_linear(dst->format); dst_templ->u.tex.level = dstlevel; dst_templ->u.tex.first_layer = dstz; dst_templ->u.tex.last_layer = dstz; }
static GLboolean update_single_texture(struct st_context *st, struct pipe_sampler_view **sampler_view, GLuint texUnit, unsigned glsl_version) { struct gl_context *ctx = st->ctx; const struct gl_sampler_object *samp; struct gl_texture_object *texObj; struct st_texture_object *stObj; enum pipe_format view_format; GLboolean retval; samp = _mesa_get_samplerobj(ctx, texUnit); texObj = ctx->Texture.Unit[texUnit]._Current; if (!texObj) { texObj = _mesa_get_fallback_texture(ctx, TEXTURE_2D_INDEX); samp = &texObj->Sampler; } stObj = st_texture_object(texObj); retval = st_finalize_texture(ctx, st->pipe, texObj); if (!retval) { /* out of mem */ return GL_FALSE; } /* Determine the format of the texture sampler view */ if (texObj->Target == GL_TEXTURE_BUFFER) { view_format = st_mesa_format_to_pipe_format(st, stObj->base._BufferObjectFormat); } else { view_format = stObj->surface_based ? stObj->surface_format : stObj->pt->format; /* If sRGB decoding is off, use the linear format */ if (samp->sRGBDecode == GL_SKIP_DECODE_EXT) { view_format = util_format_linear(view_format); } } *sampler_view = st_get_texture_sampler_view_from_stobj(st, stObj, view_format, glsl_version); return GL_TRUE; }
void util_blitter_default_src_texture(struct pipe_sampler_view *src_templ, struct pipe_resource *src, unsigned srclevel) { memset(src_templ, 0, sizeof(*src_templ)); src_templ->format = util_format_linear(src->format); src_templ->u.tex.first_level = srclevel; src_templ->u.tex.last_level = srclevel; src_templ->u.tex.first_layer = 0; src_templ->u.tex.last_layer = src->target == PIPE_TEXTURE_3D ? src->depth0 - 1 : src->array_size - 1; src_templ->swizzle_r = PIPE_SWIZZLE_RED; src_templ->swizzle_g = PIPE_SWIZZLE_GREEN; src_templ->swizzle_b = PIPE_SWIZZLE_BLUE; src_templ->swizzle_a = PIPE_SWIZZLE_ALPHA; }
/** * Determine the format for the texture sampler view. */ static enum pipe_format get_sampler_view_format(struct st_context *st, const struct st_texture_object *stObj, const struct gl_sampler_object *samp) { enum pipe_format format; if (stObj->base.Target == GL_TEXTURE_BUFFER) { format = st_mesa_format_to_pipe_format(st, stObj->base._BufferObjectFormat); } else { format = stObj->surface_based ? stObj->surface_format : stObj->pt->format; if (util_format_is_depth_and_stencil(format)) { if (stObj->base.StencilSampling) { format = util_format_stencil_only(format); } else { GLenum baseFormat = _mesa_texture_base_format(&stObj->base); if (baseFormat == GL_STENCIL_INDEX) { format = util_format_stencil_only(format); } } } else { /* If sRGB decoding is off, use the linear format */ if (samp->sRGBDecode == GL_SKIP_DECODE_EXT) { format = util_format_linear(format); } /* Use R8_UNORM for video formats */ switch (format) { case PIPE_FORMAT_NV12: case PIPE_FORMAT_IYUV: format = PIPE_FORMAT_R8_UNORM; break; default: break; } } } return format; }
void util_blitter_default_dst_texture(struct pipe_surface *dst_templ, struct pipe_resource *dst, unsigned dstlevel, unsigned dstz, const struct pipe_box *srcbox) { memset(dst_templ, 0, sizeof(*dst_templ)); dst_templ->format = dst->format; if (util_format_is_depth_or_stencil(dst->format)) { dst_templ->usage = PIPE_BIND_DEPTH_STENCIL; } else { dst_templ->usage = PIPE_BIND_RENDER_TARGET; } dst_templ->format = util_format_linear(dst->format); dst_templ->u.tex.level = dstlevel; dst_templ->u.tex.first_layer = dstz; dst_templ->u.tex.last_layer = dstz + srcbox->depth - 1; }
/** * When doing GL render to texture, we have to be sure that finalize_texture() * didn't yank out the pipe_resource that we earlier created a surface for. * Check for that here and create a new surface if needed. */ static void update_renderbuffer_surface(struct st_context *st, struct st_renderbuffer *strb) { struct pipe_context *pipe = st->pipe; struct pipe_resource *resource = strb->rtt ? strb->rtt->pt : strb->texture; int rtt_width = strb->Base.Width; int rtt_height = strb->Base.Height; enum pipe_format format = st->ctx->Color.sRGBEnabled ? resource->format : util_format_linear(resource->format); if (!strb->surface || strb->surface->format != format || strb->surface->texture != resource || strb->surface->width != rtt_width || strb->surface->height != rtt_height) { GLuint level; /* find matching mipmap level size */ for (level = 0; level <= resource->last_level; level++) { if (u_minify(resource->width0, level) == rtt_width && u_minify(resource->height0, level) == rtt_height) { struct pipe_surface surf_tmpl; memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = format; surf_tmpl.u.tex.level = level; surf_tmpl.u.tex.first_layer = strb->rtt_face + strb->rtt_slice; surf_tmpl.u.tex.last_layer = strb->rtt_face + strb->rtt_slice; pipe_surface_reference(&strb->surface, NULL); strb->surface = pipe->create_surface(pipe, resource, &surf_tmpl); #if 0 printf("-- alloc new surface %d x %d into tex %p\n", strb->surface->width, strb->surface->height, texture); #endif break; } } } }
/** * Return default texture resource binding bitmask for the given format. */ static GLuint default_bindings(struct st_context *st, enum pipe_format format) { struct pipe_screen *screen = st->pipe->screen; const unsigned target = PIPE_TEXTURE_2D; unsigned bindings; if (util_format_is_depth_or_stencil(format)) bindings = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_DEPTH_STENCIL; else bindings = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET; if (screen->is_format_supported(screen, format, target, 0, bindings)) return bindings; else { /* Try non-sRGB. */ format = util_format_linear(format); if (screen->is_format_supported(screen, format, target, 0, bindings)) return bindings; else return PIPE_BIND_SAMPLER_VIEW; } }
/** * Create or update the pipe_surface of a FBO renderbuffer. * This is usually called after st_finalize_texture. */ void st_update_renderbuffer_surface(struct st_context *st, struct st_renderbuffer *strb) { struct pipe_context *pipe = st->pipe; struct pipe_resource *resource = strb->texture; int rtt_width = strb->Base.Width; int rtt_height = strb->Base.Height; int rtt_depth = strb->Base.Depth; /* * For winsys fbo, it is possible that the renderbuffer is sRGB-capable but * the format of strb->texture is linear (because we have no control over * the format). Check strb->Base.Format instead of strb->texture->format * to determine if the rb is sRGB-capable. */ boolean enable_srgb = (st->ctx->Color.sRGBEnabled && _mesa_get_format_color_encoding(strb->Base.Format) == GL_SRGB); enum pipe_format format = (enable_srgb) ? util_format_srgb(resource->format) : util_format_linear(resource->format); unsigned first_layer, last_layer, level; if (resource->target == PIPE_TEXTURE_1D_ARRAY) { rtt_depth = rtt_height; rtt_height = 1; } /* find matching mipmap level size */ for (level = 0; level <= resource->last_level; level++) { if (u_minify(resource->width0, level) == rtt_width && u_minify(resource->height0, level) == rtt_height && (resource->target != PIPE_TEXTURE_3D || u_minify(resource->depth0, level) == rtt_depth)) { break; } } assert(level <= resource->last_level); /* determine the layer bounds */ if (strb->rtt_layered) { first_layer = 0; last_layer = util_max_layer(strb->texture, level); } else { first_layer = last_layer = strb->rtt_face + strb->rtt_slice; } if (!strb->surface || strb->surface->texture->nr_samples != strb->Base.NumSamples || strb->surface->format != format || strb->surface->texture != resource || strb->surface->width != rtt_width || strb->surface->height != rtt_height || strb->surface->u.tex.level != level || strb->surface->u.tex.first_layer != first_layer || strb->surface->u.tex.last_layer != last_layer) { /* create a new pipe_surface */ struct pipe_surface surf_tmpl; memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = format; surf_tmpl.u.tex.level = level; surf_tmpl.u.tex.first_layer = first_layer; surf_tmpl.u.tex.last_layer = last_layer; pipe_surface_reference(&strb->surface, NULL); strb->surface = pipe->create_surface(pipe, resource, &surf_tmpl); } }
static void emit_mrt(struct fd_ringbuffer *ring, unsigned nr_bufs, struct pipe_surface **bufs, uint32_t *bases, uint32_t bin_w, bool decode_srgb) { enum a4xx_tile_mode tile_mode; unsigned i; if (bin_w) { tile_mode = 2; } else { tile_mode = TILE4_LINEAR; } for (i = 0; i < A4XX_MAX_RENDER_TARGETS; i++) { enum a4xx_color_fmt format = 0; enum a3xx_color_swap swap = WZYX; bool srgb = false; struct fd_resource *rsc = NULL; struct fd_resource_slice *slice = NULL; uint32_t stride = 0; uint32_t base = 0; uint32_t offset = 0; if ((i < nr_bufs) && bufs[i]) { struct pipe_surface *psurf = bufs[i]; enum pipe_format pformat = psurf->format; rsc = fd_resource(psurf->texture); /* In case we're drawing to Z32F_S8, the "color" actually goes to * the stencil */ if (rsc->stencil) { rsc = rsc->stencil; pformat = rsc->base.b.format; bases++; } slice = fd_resource_slice(rsc, psurf->u.tex.level); format = fd4_pipe2color(pformat); swap = fd4_pipe2swap(pformat); if (decode_srgb) srgb = util_format_is_srgb(pformat); else pformat = util_format_linear(pformat); debug_assert(psurf->u.tex.first_layer == psurf->u.tex.last_layer); offset = fd_resource_offset(rsc, psurf->u.tex.level, psurf->u.tex.first_layer); if (bin_w) { stride = bin_w * rsc->cpp; if (bases) { base = bases[i]; } } else { stride = slice->pitch * rsc->cpp; } } else if ((i < nr_bufs) && bases) { base = bases[i]; } OUT_PKT0(ring, REG_A4XX_RB_MRT_BUF_INFO(i), 3); OUT_RING(ring, A4XX_RB_MRT_BUF_INFO_COLOR_FORMAT(format) | A4XX_RB_MRT_BUF_INFO_COLOR_TILE_MODE(tile_mode) | A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH(stride) | A4XX_RB_MRT_BUF_INFO_COLOR_SWAP(swap) | COND(srgb, A4XX_RB_MRT_BUF_INFO_COLOR_SRGB)); if (bin_w || (i >= nr_bufs) || !bufs[i]) { OUT_RING(ring, base); OUT_RING(ring, A4XX_RB_MRT_CONTROL3_STRIDE(stride)); } else { OUT_RELOCW(ring, rsc->bo, offset, 0, 0); /* RB_MRT[i].CONTROL3.STRIDE not emitted by c2d.. * not sure if we need to skip it for bypass or * not. */ OUT_RING(ring, A4XX_RB_MRT_CONTROL3_STRIDE(0)); } } }
/** * Copy pixel block from src surface to dst surface. * Overlapping regions are acceptable. * Flipping and stretching are supported. * \param filter one of PIPE_TEX_MIPFILTER_NEAREST/LINEAR * \param writemask controls which channels in the dest surface are sourced * from the src surface. Disabled channels are sourced * from (0,0,0,1). */ void util_blit_pixels(struct blit_state *ctx, struct pipe_resource *src_tex, unsigned src_level, int srcX0, int srcY0, int srcX1, int srcY1, int srcZ0, struct pipe_surface *dst, int dstX0, int dstY0, int dstX1, int dstY1, float z, uint filter, uint writemask, uint zs_writemask) { struct pipe_context *pipe = ctx->pipe; enum pipe_format src_format, dst_format; const int srcW = abs(srcX1 - srcX0); const int srcH = abs(srcY1 - srcY0); boolean overlap; boolean is_stencil, is_depth, blit_depth, blit_stencil; const struct util_format_description *src_desc = util_format_description(src_tex->format); struct pipe_blit_info info; assert(filter == PIPE_TEX_MIPFILTER_NEAREST || filter == PIPE_TEX_MIPFILTER_LINEAR); assert(src_level <= src_tex->last_level); /* do the regions overlap? */ overlap = src_tex == dst->texture && dst->u.tex.level == src_level && dst->u.tex.first_layer == srcZ0 && regions_overlap(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1); src_format = util_format_linear(src_tex->format); dst_format = util_format_linear(dst->texture->format); /* See whether we will blit depth or stencil. */ is_depth = util_format_has_depth(src_desc); is_stencil = util_format_has_stencil(src_desc); blit_depth = is_depth && (zs_writemask & BLIT_WRITEMASK_Z); blit_stencil = is_stencil && (zs_writemask & BLIT_WRITEMASK_STENCIL); assert((writemask && !zs_writemask && !is_depth && !is_stencil) || (!writemask && (blit_depth || blit_stencil))); /* * XXX: z parameter is deprecated. dst->u.tex.first_layer * specificies the destination layer. */ assert(z == 0.0f); /* * Check for simple case: no format conversion, no flipping, no stretching, * no overlapping, same number of samples. * Filter mode should not matter since there's no stretching. */ if (formats_compatible(src_format, dst_format) && src_tex->nr_samples == dst->texture->nr_samples && is_stencil == blit_stencil && is_depth == blit_depth && srcX0 < srcX1 && dstX0 < dstX1 && srcY0 < srcY1 && dstY0 < dstY1 && (dstX1 - dstX0) == (srcX1 - srcX0) && (dstY1 - dstY0) == (srcY1 - srcY0) && !overlap) { struct pipe_box src_box; src_box.x = srcX0; src_box.y = srcY0; src_box.z = srcZ0; src_box.width = srcW; src_box.height = srcH; src_box.depth = 1; pipe->resource_copy_region(pipe, dst->texture, dst->u.tex.level, dstX0, dstY0, dst->u.tex.first_layer,/* dest */ src_tex, src_level, &src_box); return; } memset(&info, 0, sizeof info); info.dst.resource = dst->texture; info.dst.level = dst->u.tex.level; info.dst.box.x = dstX0; info.dst.box.y = dstY0; info.dst.box.z = dst->u.tex.first_layer; info.dst.box.width = dstX1 - dstX0; info.dst.box.height = dstY1 - dstY0; assert(info.dst.box.width >= 0); assert(info.dst.box.height >= 0); info.dst.box.depth = 1; info.dst.format = dst->texture->format; info.src.resource = src_tex; info.src.level = src_level; info.src.box.x = srcX0; info.src.box.y = srcY0; info.src.box.z = srcZ0; info.src.box.width = srcX1 - srcX0; info.src.box.height = srcY1 - srcY0; info.src.box.depth = 1; info.src.format = src_tex->format; info.mask = writemask | (zs_writemask << 4); info.filter = filter; info.scissor_enable = 0; pipe->blit(pipe, &info); }
/** * Called by ctx->Driver.RenderTexture */ static void st_render_texture(struct gl_context *ctx, struct gl_framebuffer *fb, struct gl_renderbuffer_attachment *att) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct st_renderbuffer *strb; struct gl_renderbuffer *rb; struct pipe_resource *pt; struct st_texture_object *stObj; const struct gl_texture_image *texImage; struct pipe_surface surf_tmpl; if (!st_finalize_texture(ctx, pipe, att->Texture)) return; pt = st_get_texobj_resource(att->Texture); assert(pt); /* get pointer to texture image we're rendeing to */ texImage = _mesa_get_attachment_teximage(att); /* create new renderbuffer which wraps the texture image. * Use the texture's name as the renderbuffer's name so that we have * something that's non-zero (to determine vertical orientation) and * possibly helpful for debugging. */ rb = st_new_renderbuffer(ctx, att->Texture->Name); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glFramebufferTexture()"); return; } _mesa_reference_renderbuffer(&att->Renderbuffer, rb); assert(rb->RefCount == 1); rb->AllocStorage = NULL; /* should not get called */ strb = st_renderbuffer(rb); assert(strb->Base.RefCount > 0); /* 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; rb->_BaseFormat = texImage->_BaseFormat; rb->InternalFormat = texImage->InternalFormat; pipe_resource_reference( &strb->texture, pt ); pipe_surface_release(pipe, &strb->surface); assert(strb->rtt_level <= strb->texture->last_level); /* new surface for rendering into the texture */ memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = ctx->Color.sRGBEnabled ? strb->texture->format : util_format_linear(strb->texture->format); surf_tmpl.usage = PIPE_BIND_RENDER_TARGET; surf_tmpl.u.tex.level = strb->rtt_level; surf_tmpl.u.tex.first_layer = strb->rtt_face + strb->rtt_slice; surf_tmpl.u.tex.last_layer = strb->rtt_face + strb->rtt_slice; strb->surface = pipe->create_surface(pipe, strb->texture, &surf_tmpl); strb->Base.Format = st_pipe_format_to_mesa_format(pt->format); /* Invalidate buffer state so that the pipe's framebuffer state * gets updated. * That's where the new renderbuffer (which we just created) gets * passed to the pipe as a (color/depth) render target. */ st_invalidate_state(ctx, _NEW_BUFFERS); /* Need to trigger a call to update_framebuffer() since we just * attached a new renderbuffer. */ ctx->NewState |= _NEW_BUFFERS; }
.dst = { .resource = dst, .box = { .x = dstx, .y = dsty, .z = dstz, .width = src_box->width, .height = src_box->height, .depth = src_box->depth, }, .format = util_format_linear(dst->format), }, .src = { .resource = src, .box = *src_box, .format = util_format_linear(src->format), }, .mask = PIPE_MASK_RGBA, .filter = PIPE_TEX_FILTER_NEAREST, }; render_blit(pctx, &info); } /* Optimal hardware path for blitting pixels. * Scaling, format conversion, up- and downsampling (resolve) are allowed. */ static void fd_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info) { struct pipe_blit_info info = *blit_info;
/** * Do a CopyTex[Sub]Image1/2/3D() using a hardware (blit) path if possible. * Note that the region to copy has already been clipped so we know we * won't read from outside the source renderbuffer's bounds. * * Note: srcY=0=Bottom of renderbuffer (GL convention) */ static void st_CopyTexSubImage(struct gl_context *ctx, GLuint dims, struct gl_texture_image *texImage, GLint destX, GLint destY, GLint destZ, struct gl_renderbuffer *rb, GLint srcX, GLint srcY, GLsizei width, GLsizei height) { struct st_texture_image *stImage = st_texture_image(texImage); const GLenum texBaseFormat = texImage->_BaseFormat; struct st_renderbuffer *strb = st_renderbuffer(rb); struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; enum pipe_format dest_format, src_format; GLboolean matching_base_formats; GLuint color_writemask, zs_writemask, sample_count; struct pipe_surface *dest_surface = NULL; GLboolean do_flip = (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP); struct pipe_surface surf_tmpl; unsigned int dst_usage; GLint srcY0, srcY1; /* make sure finalize_textures has been called? */ if (0) st_validate_state(st); if (!strb || !strb->surface || !stImage->pt) { debug_printf("%s: null strb or stImage\n", __FUNCTION__); return; } sample_count = strb->surface->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); assert(strb); assert(strb->surface); assert(stImage->pt); src_format = strb->surface->format; dest_format = stImage->pt->format; /* * Determine if the src framebuffer and dest texture have the same * base format. We need this to detect a case such as the framebuffer * being GL_RGBA but the texture being GL_RGB. If the actual hardware * texture format stores RGBA we need to set A=1 (overriding the * framebuffer's alpha values). We can't do that with the blit or * textured-quad paths. */ matching_base_formats = (_mesa_get_format_base_format(strb->Base.Format) == _mesa_get_format_base_format(texImage->TexFormat)); if (ctx->_ImageTransferState) { goto fallback; } if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) { /* 1D arrays might be thought of as 2D images but the actual layout * might not be that way. At some points, we convert OpenGL's 1D * array 'height' into gallium 'layers' and that prevents the blit * utility code from doing the right thing. Simpy use the memcpy-based * fallback. */ goto fallback; } if (matching_base_formats && src_format == dest_format && !do_flip) { /* use surface_copy() / blit */ struct pipe_box src_box; unsigned dstLevel; u_box_2d_zslice(srcX, srcY, strb->surface->u.tex.first_layer, width, height, &src_box); /* If stImage->pt is an independent image (not a pointer into a full * mipmap) stImage->pt.last_level will be zero and we need to use that * as the dest level. */ dstLevel = MIN2(stImage->base.Level, stImage->pt->last_level); /* for resource_copy_region(), y=0=top, always */ pipe->resource_copy_region(pipe, /* dest */ stImage->pt, dstLevel, destX, destY, destZ + stImage->base.Face, /* src */ strb->texture, strb->surface->u.tex.level, &src_box); return; } if (texBaseFormat == GL_DEPTH_STENCIL) { goto fallback; } if (texBaseFormat == GL_DEPTH_COMPONENT) { color_writemask = 0; zs_writemask = BLIT_WRITEMASK_Z; dst_usage = PIPE_BIND_DEPTH_STENCIL; } else { color_writemask = compatible_src_dst_formats(ctx, &strb->Base, texImage); zs_writemask = 0; dst_usage = PIPE_BIND_RENDER_TARGET; } if ((!color_writemask && !zs_writemask) || !screen->is_format_supported(screen, src_format, PIPE_TEXTURE_2D, sample_count, PIPE_BIND_SAMPLER_VIEW) || !screen->is_format_supported(screen, dest_format, PIPE_TEXTURE_2D, 0, dst_usage)) { goto fallback; } if (do_flip) { srcY1 = strb->Base.Height - srcY - height; srcY0 = srcY1 + height; } else { srcY0 = srcY; srcY1 = srcY0 + height; } /* Disable conditional rendering. */ if (st->render_condition) { pipe->render_condition(pipe, NULL, 0); } memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = util_format_linear(stImage->pt->format); surf_tmpl.usage = dst_usage; surf_tmpl.u.tex.level = stImage->base.Level; surf_tmpl.u.tex.first_layer = stImage->base.Face + destZ; surf_tmpl.u.tex.last_layer = stImage->base.Face + destZ; dest_surface = pipe->create_surface(pipe, stImage->pt, &surf_tmpl); util_blit_pixels(st->blit, strb->texture, strb->surface->u.tex.level, srcX, srcY0, srcX + width, srcY1, strb->surface->u.tex.first_layer, dest_surface, destX, destY, destX + width, destY + height, 0.0, PIPE_TEX_MIPFILTER_NEAREST, color_writemask, zs_writemask); pipe_surface_reference(&dest_surface, NULL); /* Restore conditional rendering state. */ if (st->render_condition) { pipe->render_condition(pipe, st->render_condition, st->condition_mode); } return; fallback: /* software fallback */ fallback_copy_texsubimage(ctx, strb, stImage, texBaseFormat, destX, destY, destZ, srcX, srcY, width, height); }
/** * Try issuing a quad blit. */ static bool try_blit(struct svga_context *svga, const struct pipe_blit_info *blit_info) { struct svga_winsys_screen *sws = svga_screen(svga->pipe.screen)->sws; struct pipe_resource *src = blit_info->src.resource; struct pipe_resource *dst = blit_info->dst.resource; struct pipe_resource *newSrc = NULL; struct pipe_resource *newDst = NULL; bool can_create_src_view; bool can_create_dst_view; bool ret = true; struct pipe_blit_info blit = *blit_info; SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_BLITBLITTER); /** * Avoid using util_blitter_blit() for these depth formats on non-vgpu10 * devices because these depth formats only support comparison mode * and not ordinary sampling. */ if (!svga_have_vgpu10(svga) && (blit.mask & PIPE_MASK_Z) && (svga_texture(dst)->key.format == SVGA3D_Z_D16 || svga_texture(dst)->key.format == SVGA3D_Z_D24X8 || svga_texture(dst)->key.format == SVGA3D_Z_D24S8)) { ret = false; goto done; } /** * If format is srgb and blend is enabled then color values need * to be converted into linear format. */ if (is_blending_enabled(svga, &blit)) { blit.src.format = util_format_linear(blit.src.format); blit.dst.format = util_format_linear(blit.dst.format); } /* Check if we can create shader resource view and * render target view for the quad blitter to work */ can_create_src_view = is_view_format_compatible(src->format, svga_texture(src)->key.format, blit.src.format); can_create_dst_view = is_view_format_compatible(dst->format, svga_texture(dst)->key.format, blit.dst.format); if ((blit.mask & PIPE_MASK_S) || ((!can_create_dst_view || !can_create_src_view) && !svga_have_vgpu10(svga))) { /* Can't do stencil blits with textured quad blitter */ debug_warn_once("using software stencil blit"); ret = false; goto done; } if (!util_blitter_is_blit_supported(svga->blitter, &blit)) { debug_printf("svga: blit unsupported %s -> %s\n", util_format_short_name(blit.src.resource->format), util_format_short_name(blit.dst.resource->format)); ret = false; goto done; } /* XXX turn off occlusion and streamout queries */ util_blitter_save_vertex_buffer_slot(svga->blitter, svga->curr.vb); util_blitter_save_vertex_elements(svga->blitter, (void*)svga->curr.velems); util_blitter_save_vertex_shader(svga->blitter, svga->curr.vs); util_blitter_save_geometry_shader(svga->blitter, svga->curr.user_gs); util_blitter_save_so_targets(svga->blitter, svga->num_so_targets, (struct pipe_stream_output_target**)svga->so_targets); util_blitter_save_rasterizer(svga->blitter, (void*)svga->curr.rast); util_blitter_save_viewport(svga->blitter, &svga->curr.viewport); util_blitter_save_scissor(svga->blitter, &svga->curr.scissor); util_blitter_save_fragment_shader(svga->blitter, svga->curr.fs); util_blitter_save_blend(svga->blitter, (void*)svga->curr.blend); util_blitter_save_depth_stencil_alpha(svga->blitter, (void*)svga->curr.depth); util_blitter_save_stencil_ref(svga->blitter, &svga->curr.stencil_ref); util_blitter_save_sample_mask(svga->blitter, svga->curr.sample_mask); util_blitter_save_framebuffer(svga->blitter, &svga->curr.framebuffer); util_blitter_save_fragment_sampler_states(svga->blitter, svga->curr.num_samplers[PIPE_SHADER_FRAGMENT], (void**)svga->curr.sampler[PIPE_SHADER_FRAGMENT]); util_blitter_save_fragment_sampler_views(svga->blitter, svga->curr.num_sampler_views[PIPE_SHADER_FRAGMENT], svga->curr.sampler_views[PIPE_SHADER_FRAGMENT]); if (!can_create_src_view) { struct pipe_resource template; struct pipe_blit_info copy_region_blit;
/** * glGetTexImage() helper: decompress a compressed texture by rendering * a textured quad. Store the results in the user's buffer. */ static void decompress_with_blit(struct gl_context * ctx, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct st_texture_image *stImage = st_texture_image(texImage); struct st_texture_object *stObj = st_texture_object(texImage->TexObject); const GLuint width = texImage->Width; const GLuint height = texImage->Height; struct pipe_resource *dst_texture; struct pipe_blit_info blit; unsigned bind = (PIPE_BIND_RENDER_TARGET | PIPE_BIND_TRANSFER_READ); struct pipe_transfer *tex_xfer; ubyte *map; /* create temp / dest surface */ if (!util_create_rgba_texture(pipe, width, height, bind, &dst_texture)) { _mesa_problem(ctx, "util_create_rgba_texture() failed " "in decompress_with_blit()"); return; } blit.src.resource = stObj->pt; blit.src.level = texImage->Level; blit.src.format = util_format_linear(stObj->pt->format); blit.dst.resource = dst_texture; blit.dst.level = 0; blit.dst.format = dst_texture->format; blit.src.box.x = blit.dst.box.x = 0; blit.src.box.y = blit.dst.box.y = 0; blit.src.box.z = 0; /* XXX compressed array textures? */ blit.dst.box.z = 0; blit.src.box.width = blit.dst.box.width = width; blit.src.box.height = blit.dst.box.height = height; blit.src.box.depth = blit.dst.box.depth = 1; blit.mask = PIPE_MASK_RGBA; blit.filter = PIPE_TEX_FILTER_NEAREST; blit.scissor_enable = FALSE; /* blit/render/decompress */ st->pipe->blit(st->pipe, &blit); pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels); map = pipe_transfer_map(pipe, dst_texture, 0, 0, PIPE_TRANSFER_READ, 0, 0, width, height, &tex_xfer); if (!map) { goto end; } /* copy/pack data into user buffer */ if (_mesa_format_matches_format_and_type(stImage->base.TexFormat, format, type, ctx->Pack.SwapBytes)) { /* memcpy */ const uint bytesPerRow = width * util_format_get_blocksize(stImage->pt->format); /* map the dst_surface so we can read from it */ GLuint row; for (row = 0; row < height; row++) { GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } pipe_transfer_unmap(pipe, tex_xfer); } else { /* format translation via floats */ GLuint row; enum pipe_format pformat = util_format_linear(dst_texture->format); GLfloat *rgba; rgba = malloc(width * 4 * sizeof(GLfloat)); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); goto end; } for (row = 0; row < height; row++) { const GLbitfield transferOps = 0x0; /* bypassed for glGetTexImage() */ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback format translation\n", __FUNCTION__); /* get float[4] rgba row from surface */ pipe_get_tile_rgba_format(tex_xfer, map, 0, row, width, 1, pformat, rgba); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dest, &ctx->Pack, transferOps); } free(rgba); } end: if (map) pipe_transfer_unmap(pipe, tex_xfer); _mesa_unmap_pbo_dest(ctx, &ctx->Pack); pipe_resource_reference(&dst_texture, NULL); }
/** * glGetTexImage() helper: decompress a compressed texture by rendering * a textured quad. Store the results in the user's buffer. */ static void decompress_with_blit(struct gl_context * ctx, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct st_texture_image *stImage = st_texture_image(texImage); struct st_texture_object *stObj = st_texture_object(texImage->TexObject); struct pipe_sampler_view *src_view; const GLuint width = texImage->Width; const GLuint height = texImage->Height; struct pipe_surface *dst_surface; struct pipe_resource *dst_texture; struct pipe_transfer *tex_xfer; unsigned bind = (PIPE_BIND_RENDER_TARGET | /* util_blit may choose to render */ PIPE_BIND_TRANSFER_READ); /* create temp / dest surface */ if (!util_create_rgba_surface(pipe, width, height, bind, &dst_texture, &dst_surface)) { _mesa_problem(ctx, "util_create_rgba_surface() failed " "in decompress_with_blit()"); return; } /* Disable conditional rendering. */ if (st->render_condition) { pipe->render_condition(pipe, NULL, 0); } /* Create sampler view that limits fetches to the source mipmap level */ { struct pipe_sampler_view sv_temp; u_sampler_view_default_template(&sv_temp, stObj->pt, stObj->pt->format); sv_temp.format = util_format_linear(sv_temp.format); sv_temp.u.tex.first_level = sv_temp.u.tex.last_level = texImage->Level; src_view = pipe->create_sampler_view(pipe, stObj->pt, &sv_temp); if (!src_view) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); return; } } /* blit/render/decompress */ util_blit_pixels_tex(st->blit, src_view, /* pipe_resource (src) */ 0, 0, /* src x0, y0 */ width, height, /* src x1, y1 */ dst_surface, /* pipe_surface (dst) */ 0, 0, /* dst x0, y0 */ width, height, /* dst x1, y1 */ 0.0, /* z */ PIPE_TEX_MIPFILTER_NEAREST); /* Restore conditional rendering state. */ if (st->render_condition) { pipe->render_condition(pipe, st->render_condition, st->condition_mode); } /* map the dst_surface so we can read from it */ tex_xfer = pipe_get_transfer(pipe, dst_texture, 0, 0, PIPE_TRANSFER_READ, 0, 0, width, height); pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels); /* copy/pack data into user buffer */ if (_mesa_format_matches_format_and_type(stImage->base.TexFormat, format, type, ctx->Pack.SwapBytes)) { /* memcpy */ const uint bytesPerRow = width * util_format_get_blocksize(stImage->pt->format); ubyte *map = pipe_transfer_map(pipe, tex_xfer); GLuint row; for (row = 0; row < height; row++) { GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } pipe_transfer_unmap(pipe, tex_xfer); } else { /* format translation via floats */ GLuint row; enum pipe_format pformat = util_format_linear(dst_texture->format); GLfloat *rgba; rgba = (GLfloat *) malloc(width * 4 * sizeof(GLfloat)); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); goto end; } for (row = 0; row < height; row++) { const GLbitfield transferOps = 0x0; /* bypassed for glGetTexImage() */ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback format translation\n", __FUNCTION__); /* get float[4] rgba row from surface */ pipe_get_tile_rgba_format(pipe, tex_xfer, 0, row, width, 1, pformat, rgba); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dest, &ctx->Pack, transferOps); } free(rgba); } end: _mesa_unmap_pbo_dest(ctx, &ctx->Pack); pipe->transfer_destroy(pipe, tex_xfer); /* destroy the temp / dest surface */ util_destroy_rgba_surface(dst_texture, dst_surface); pipe_sampler_view_release(pipe, &src_view); }
/** * Create or update the pipe_surface of a FBO renderbuffer. * This is usually called after st_finalize_texture. */ void st_update_renderbuffer_surface(struct st_context *st, struct st_renderbuffer *strb) { struct pipe_context *pipe = st->pipe; struct pipe_resource *resource = strb->texture; int rtt_width = strb->Base.Width; int rtt_height = strb->Base.Height; int rtt_depth = strb->Base.Depth; enum pipe_format format = st->ctx->Color.sRGBEnabled ? resource->format : util_format_linear(resource->format); unsigned first_layer, last_layer, level; if (resource->target == PIPE_TEXTURE_1D_ARRAY) { rtt_depth = rtt_height; rtt_height = 1; } /* find matching mipmap level size */ for (level = 0; level <= resource->last_level; level++) { if (u_minify(resource->width0, level) == rtt_width && u_minify(resource->height0, level) == rtt_height && (resource->target != PIPE_TEXTURE_3D || u_minify(resource->depth0, level) == rtt_depth)) { break; } } assert(level <= resource->last_level); /* determine the layer bounds */ if (strb->rtt_layered) { first_layer = 0; last_layer = util_max_layer(strb->texture, level); } else { first_layer = last_layer = strb->rtt_face + strb->rtt_slice; } if (!strb->surface || strb->surface->texture->nr_samples != strb->Base.NumSamples || strb->surface->format != format || strb->surface->texture != resource || strb->surface->width != rtt_width || strb->surface->height != rtt_height || strb->surface->u.tex.level != level || strb->surface->u.tex.first_layer != first_layer || strb->surface->u.tex.last_layer != last_layer) { /* create a new pipe_surface */ struct pipe_surface surf_tmpl; memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = format; surf_tmpl.u.tex.level = level; surf_tmpl.u.tex.first_layer = first_layer; surf_tmpl.u.tex.last_layer = last_layer; pipe_surface_reference(&strb->surface, NULL); strb->surface = pipe->create_surface(pipe, resource, &surf_tmpl); } }
/** * Copy pixel block from src surface to dst surface. * Overlapping regions are acceptable. * Flipping and stretching are supported. * \param filter one of PIPE_TEX_MIPFILTER_NEAREST/LINEAR * \param writemask controls which channels in the dest surface are sourced * from the src surface. Disabled channels are sourced * from (0,0,0,1). */ void util_blit_pixels(struct blit_state *ctx, struct pipe_resource *src_tex, unsigned src_level, int srcX0, int srcY0, int srcX1, int srcY1, int srcZ0, struct pipe_surface *dst, int dstX0, int dstY0, int dstX1, int dstY1, float z, uint filter, uint writemask, uint zs_writemask) { struct pipe_context *pipe = ctx->pipe; struct pipe_screen *screen = pipe->screen; enum pipe_format src_format, dst_format; struct pipe_sampler_view *sampler_view = NULL; struct pipe_sampler_view sv_templ; struct pipe_surface *dst_surface; struct pipe_framebuffer_state fb; const int srcW = abs(srcX1 - srcX0); const int srcH = abs(srcY1 - srcY0); unsigned offset; boolean overlap; float s0, t0, s1, t1; boolean normalized; boolean is_stencil, is_depth, blit_depth, blit_stencil; const struct util_format_description *src_desc = util_format_description(src_tex->format); assert(filter == PIPE_TEX_MIPFILTER_NEAREST || filter == PIPE_TEX_MIPFILTER_LINEAR); assert(src_level <= src_tex->last_level); /* do the regions overlap? */ overlap = src_tex == dst->texture && dst->u.tex.level == src_level && dst->u.tex.first_layer == srcZ0 && regions_overlap(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1); src_format = util_format_linear(src_tex->format); dst_format = util_format_linear(dst->texture->format); /* See whether we will blit depth or stencil. */ is_depth = util_format_has_depth(src_desc); is_stencil = util_format_has_stencil(src_desc); blit_depth = is_depth && (zs_writemask & BLIT_WRITEMASK_Z); blit_stencil = is_stencil && (zs_writemask & BLIT_WRITEMASK_STENCIL); assert((writemask && !zs_writemask && !is_depth && !is_stencil) || (!writemask && (blit_depth || blit_stencil))); /* * Check for simple case: no format conversion, no flipping, no stretching, * no overlapping, same number of samples. * Filter mode should not matter since there's no stretching. */ if (formats_compatible(src_format, dst_format) && src_tex->nr_samples == dst->texture->nr_samples && is_stencil == blit_stencil && is_depth == blit_depth && srcX0 < srcX1 && dstX0 < dstX1 && srcY0 < srcY1 && dstY0 < dstY1 && (dstX1 - dstX0) == (srcX1 - srcX0) && (dstY1 - dstY0) == (srcY1 - srcY0) && !overlap) { struct pipe_box src_box; src_box.x = srcX0; src_box.y = srcY0; src_box.z = srcZ0; src_box.width = srcW; src_box.height = srcH; src_box.depth = 1; pipe->resource_copy_region(pipe, dst->texture, dst->u.tex.level, dstX0, dstY0, dst->u.tex.first_layer,/* dest */ src_tex, src_level, &src_box); return; } /* XXX Reading multisample textures is unimplemented. */ assert(src_tex->nr_samples <= 1); if (src_tex->nr_samples > 1) { return; } /* It's a mistake to call this function with a stencil format and * without shader stencil export. We don't do software fallbacks here. * Ignore stencil and only copy depth. */ if (blit_stencil && !ctx->has_stencil_export) { blit_stencil = FALSE; if (!blit_depth) return; } if (dst_format == dst->format) { dst_surface = dst; } else { struct pipe_surface templ = *dst; templ.format = dst_format; dst_surface = pipe->create_surface(pipe, dst->texture, &templ); } /* Create a temporary texture when src and dest alias. */ if (src_tex == dst_surface->texture && dst_surface->u.tex.level == src_level && dst_surface->u.tex.first_layer == srcZ0) { /* Make a temporary texture which contains a copy of the source pixels. * Then we'll sample from the temporary texture. */ struct pipe_resource texTemp; struct pipe_resource *tex; struct pipe_sampler_view sv_templ; struct pipe_box src_box; const int srcLeft = MIN2(srcX0, srcX1); const int srcTop = MIN2(srcY0, srcY1); if (srcLeft != srcX0) { /* left-right flip */ int tmp = dstX0; dstX0 = dstX1; dstX1 = tmp; } if (srcTop != srcY0) { /* up-down flip */ int tmp = dstY0; dstY0 = dstY1; dstY1 = tmp; } /* create temp texture */ memset(&texTemp, 0, sizeof(texTemp)); texTemp.target = ctx->internal_target; texTemp.format = src_format; texTemp.last_level = 0; texTemp.width0 = srcW; texTemp.height0 = srcH; texTemp.depth0 = 1; texTemp.array_size = 1; texTemp.bind = PIPE_BIND_SAMPLER_VIEW; tex = screen->resource_create(screen, &texTemp); if (!tex) return; src_box.x = srcLeft; src_box.y = srcTop; src_box.z = srcZ0; src_box.width = srcW; src_box.height = srcH; src_box.depth = 1; /* load temp texture */ pipe->resource_copy_region(pipe, tex, 0, 0, 0, 0, /* dest */ src_tex, src_level, &src_box); normalized = tex->target != PIPE_TEXTURE_RECT; if(normalized) { s0 = 0.0f; s1 = 1.0f; t0 = 0.0f; t1 = 1.0f; } else { s0 = 0; s1 = srcW; t0 = 0; t1 = srcH; } u_sampler_view_default_template(&sv_templ, tex, tex->format); if (!blit_depth && blit_stencil) { /* set a stencil-only format, e.g. Z24S8 --> X24S8 */ sv_templ.format = util_format_stencil_only(tex->format); assert(sv_templ.format != PIPE_FORMAT_NONE); } sampler_view = pipe->create_sampler_view(pipe, tex, &sv_templ); if (!sampler_view) { pipe_resource_reference(&tex, NULL); return; } pipe_resource_reference(&tex, NULL); } else { /* Directly sample from the source resource/texture */ u_sampler_view_default_template(&sv_templ, src_tex, src_format); if (!blit_depth && blit_stencil) { /* set a stencil-only format, e.g. Z24S8 --> X24S8 */ sv_templ.format = util_format_stencil_only(src_format); assert(sv_templ.format != PIPE_FORMAT_NONE); } sampler_view = pipe->create_sampler_view(pipe, src_tex, &sv_templ); if (!sampler_view) { return; } s0 = srcX0; s1 = srcX1; t0 = srcY0; t1 = srcY1; normalized = sampler_view->texture->target != PIPE_TEXTURE_RECT; if(normalized) { s0 /= (float)(u_minify(sampler_view->texture->width0, src_level)); s1 /= (float)(u_minify(sampler_view->texture->width0, src_level)); t0 /= (float)(u_minify(sampler_view->texture->height0, src_level)); t1 /= (float)(u_minify(sampler_view->texture->height0, src_level)); } } assert(screen->is_format_supported(screen, sampler_view->format, ctx->internal_target, sampler_view->texture->nr_samples, PIPE_BIND_SAMPLER_VIEW)); assert(screen->is_format_supported(screen, dst_format, ctx->internal_target, dst_surface->texture->nr_samples, is_depth || is_stencil ? PIPE_BIND_DEPTH_STENCIL : PIPE_BIND_RENDER_TARGET)); /* save state (restored below) */ cso_save_blend(ctx->cso); cso_save_depth_stencil_alpha(ctx->cso); cso_save_rasterizer(ctx->cso); cso_save_sample_mask(ctx->cso); cso_save_samplers(ctx->cso, PIPE_SHADER_FRAGMENT); cso_save_sampler_views(ctx->cso, PIPE_SHADER_FRAGMENT); cso_save_stream_outputs(ctx->cso); cso_save_viewport(ctx->cso); cso_save_framebuffer(ctx->cso); cso_save_fragment_shader(ctx->cso); cso_save_vertex_shader(ctx->cso); cso_save_geometry_shader(ctx->cso); cso_save_vertex_elements(ctx->cso); cso_save_aux_vertex_buffer_slot(ctx->cso); cso_save_render_condition(ctx->cso); /* set misc state we care about */ if (writemask) cso_set_blend(ctx->cso, &ctx->blend_write_color); else cso_set_blend(ctx->cso, &ctx->blend_keep_color); cso_set_sample_mask(ctx->cso, ~0); cso_set_rasterizer(ctx->cso, &ctx->rasterizer); cso_set_vertex_elements(ctx->cso, 2, ctx->velem); cso_set_stream_outputs(ctx->cso, 0, NULL, 0); cso_set_render_condition(ctx->cso, NULL, 0); /* default sampler state */ ctx->sampler.normalized_coords = normalized; ctx->sampler.min_img_filter = filter; ctx->sampler.mag_img_filter = filter; ctx->sampler.min_lod = src_level; ctx->sampler.max_lod = src_level; /* Depth stencil state, fragment shader and sampler setup depending on what * we blit. */ if (blit_depth && blit_stencil) { cso_single_sampler(ctx->cso, PIPE_SHADER_FRAGMENT, 0, &ctx->sampler); /* don't filter stencil */ ctx->sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; cso_single_sampler(ctx->cso, PIPE_SHADER_FRAGMENT, 1, &ctx->sampler); cso_set_depth_stencil_alpha(ctx->cso, &ctx->dsa_write_depthstencil); set_depthstencil_fragment_shader(ctx, sampler_view->texture->target); } else if (blit_depth) { cso_single_sampler(ctx->cso, PIPE_SHADER_FRAGMENT, 0, &ctx->sampler); cso_set_depth_stencil_alpha(ctx->cso, &ctx->dsa_write_depth); set_depth_fragment_shader(ctx, sampler_view->texture->target); } else if (blit_stencil) { /* don't filter stencil */ ctx->sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; cso_single_sampler(ctx->cso, PIPE_SHADER_FRAGMENT, 0, &ctx->sampler); cso_set_depth_stencil_alpha(ctx->cso, &ctx->dsa_write_stencil); set_stencil_fragment_shader(ctx, sampler_view->texture->target); } else { /* color */ cso_single_sampler(ctx->cso, PIPE_SHADER_FRAGMENT, 0, &ctx->sampler); cso_set_depth_stencil_alpha(ctx->cso, &ctx->dsa_keep_depthstencil); set_fragment_shader(ctx, writemask, sampler_view->texture->target); } cso_single_sampler_done(ctx->cso, PIPE_SHADER_FRAGMENT); /* textures */ if (blit_depth && blit_stencil) { /* Setup two samplers, one for depth and the other one for stencil. */ struct pipe_sampler_view templ; struct pipe_sampler_view *views[2]; templ = *sampler_view; templ.format = util_format_stencil_only(templ.format); assert(templ.format != PIPE_FORMAT_NONE); views[0] = sampler_view; views[1] = pipe->create_sampler_view(pipe, views[0]->texture, &templ); cso_set_sampler_views(ctx->cso, PIPE_SHADER_FRAGMENT, 2, views); pipe_sampler_view_reference(&views[1], NULL); } else { cso_set_sampler_views(ctx->cso, PIPE_SHADER_FRAGMENT, 1, &sampler_view); } /* viewport */ ctx->viewport.scale[0] = 0.5f * dst_surface->width; ctx->viewport.scale[1] = 0.5f * dst_surface->height; ctx->viewport.scale[2] = 0.5f; ctx->viewport.scale[3] = 1.0f; ctx->viewport.translate[0] = 0.5f * dst_surface->width; ctx->viewport.translate[1] = 0.5f * dst_surface->height; ctx->viewport.translate[2] = 0.5f; ctx->viewport.translate[3] = 0.0f; cso_set_viewport(ctx->cso, &ctx->viewport); set_vertex_shader(ctx); cso_set_geometry_shader_handle(ctx->cso, NULL); /* drawing dest */ memset(&fb, 0, sizeof(fb)); fb.width = dst_surface->width; fb.height = dst_surface->height; if (blit_depth || blit_stencil) { fb.zsbuf = dst_surface; } else { fb.nr_cbufs = 1; fb.cbufs[0] = dst_surface; } cso_set_framebuffer(ctx->cso, &fb); /* draw quad */ offset = setup_vertex_data_tex(ctx, (float) dstX0 / dst_surface->width * 2.0f - 1.0f, (float) dstY0 / dst_surface->height * 2.0f - 1.0f, (float) dstX1 / dst_surface->width * 2.0f - 1.0f, (float) dstY1 / dst_surface->height * 2.0f - 1.0f, s0, t0, s1, t1, z); if (ctx->vbuf) { util_draw_vertex_buffer(ctx->pipe, ctx->cso, ctx->vbuf, cso_get_aux_vertex_buffer_slot(ctx->cso), offset, PIPE_PRIM_TRIANGLE_FAN, 4, /* verts */ 2); /* attribs/vert */ } /* restore state we changed */ cso_restore_blend(ctx->cso); cso_restore_depth_stencil_alpha(ctx->cso); cso_restore_rasterizer(ctx->cso); cso_restore_sample_mask(ctx->cso); cso_restore_samplers(ctx->cso, PIPE_SHADER_FRAGMENT); cso_restore_sampler_views(ctx->cso, PIPE_SHADER_FRAGMENT); cso_restore_viewport(ctx->cso); cso_restore_framebuffer(ctx->cso); cso_restore_fragment_shader(ctx->cso); cso_restore_vertex_shader(ctx->cso); cso_restore_geometry_shader(ctx->cso); cso_restore_vertex_elements(ctx->cso); cso_restore_aux_vertex_buffer_slot(ctx->cso); cso_restore_stream_outputs(ctx->cso); cso_restore_render_condition(ctx->cso); pipe_sampler_view_reference(&sampler_view, NULL); if (dst_surface != dst) pipe_surface_reference(&dst_surface, NULL); }
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_BlitFramebuffer(struct gl_context *ctx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { const GLbitfield depthStencil = (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); struct st_context *st = st_context(ctx); const uint pFilter = ((filter == GL_NEAREST) ? PIPE_TEX_FILTER_NEAREST : PIPE_TEX_FILTER_LINEAR); struct gl_framebuffer *readFB = ctx->ReadBuffer; struct gl_framebuffer *drawFB = ctx->DrawBuffer; struct { GLint srcX0, srcY0, srcX1, srcY1; GLint dstX0, dstY0, dstX1, dstY1; } clip; struct pipe_blit_info blit; st_validate_state(st); clip.srcX0 = srcX0; clip.srcY0 = srcY0; clip.srcX1 = srcX1; clip.srcY1 = srcY1; clip.dstX0 = dstX0; clip.dstY0 = dstY0; clip.dstX1 = dstX1; clip.dstY1 = dstY1; /* NOTE: If the src and dst dimensions don't match, we cannot simply adjust * the integer coordinates to account for clipping (or scissors) because that * would make us cut off fractional parts, affecting the result of the blit. * * XXX: This should depend on mask ! */ if (!_mesa_clip_blit(ctx, &clip.srcX0, &clip.srcY0, &clip.srcX1, &clip.srcY1, &clip.dstX0, &clip.dstY0, &clip.dstX1, &clip.dstY1)) { return; /* nothing to draw/blit */ } blit.scissor_enable = (dstX0 != clip.dstX0) || (dstY0 != clip.dstY0) || (dstX1 != clip.dstX1) || (dstY1 != clip.dstY1); if (st_fb_orientation(drawFB) == Y_0_TOP) { /* invert Y for dest */ dstY0 = drawFB->Height - dstY0; dstY1 = drawFB->Height - dstY1; /* invert Y for clip */ clip.dstY0 = drawFB->Height - clip.dstY0; clip.dstY1 = drawFB->Height - clip.dstY1; } if (blit.scissor_enable) { blit.scissor.minx = MIN2(clip.dstX0, clip.dstX1); blit.scissor.miny = MIN2(clip.dstY0, clip.dstY1); blit.scissor.maxx = MAX2(clip.dstX0, clip.dstX1); blit.scissor.maxy = MAX2(clip.dstY0, clip.dstY1); #if 0 debug_printf("scissor = (%i,%i)-(%i,%i)\n", blit.scissor.minx,blit.scissor.miny, blit.scissor.maxx,blit.scissor.maxy); #endif } if (st_fb_orientation(readFB) == Y_0_TOP) { /* invert Y for src */ srcY0 = readFB->Height - srcY0; srcY1 = readFB->Height - srcY1; } if (srcY0 > srcY1 && dstY0 > dstY1) { /* Both src and dst are upside down. Swap Y to make it * right-side up to increase odds of using a fast path. * Recall that all Gallium raster coords have Y=0=top. */ GLint tmp; tmp = srcY0; srcY0 = srcY1; srcY1 = tmp; tmp = dstY0; dstY0 = dstY1; dstY1 = tmp; } blit.src.box.depth = 1; blit.dst.box.depth = 1; /* Destination dimensions have to be positive: */ if (dstX0 < dstX1) { blit.dst.box.x = dstX0; blit.src.box.x = srcX0; blit.dst.box.width = dstX1 - dstX0; blit.src.box.width = srcX1 - srcX0; } else { blit.dst.box.x = dstX1; blit.src.box.x = srcX1; blit.dst.box.width = dstX0 - dstX1; blit.src.box.width = srcX0 - srcX1; } if (dstY0 < dstY1) { blit.dst.box.y = dstY0; blit.src.box.y = srcY0; blit.dst.box.height = dstY1 - dstY0; blit.src.box.height = srcY1 - srcY0; } else { blit.dst.box.y = dstY1; blit.src.box.y = srcY1; blit.dst.box.height = dstY0 - dstY1; blit.src.box.height = srcY0 - srcY1; } blit.filter = pFilter; if (mask & GL_COLOR_BUFFER_BIT) { struct gl_renderbuffer_attachment *srcAtt = &readFB->Attachment[readFB->_ColorReadBufferIndex]; blit.mask = PIPE_MASK_RGBA; if (srcAtt->Type == GL_TEXTURE) { struct st_texture_object *srcObj = st_texture_object(srcAtt->Texture); GLuint i; if (!srcObj || !srcObj->pt) { return; } for (i = 0; i < drawFB->_NumColorDrawBuffers; i++) { struct st_renderbuffer *dstRb = st_renderbuffer(drawFB->_ColorDrawBuffers[i]); if (dstRb) { struct pipe_surface *dstSurf = dstRb->surface; if (dstSurf) { blit.dst.resource = dstSurf->texture; blit.dst.level = dstSurf->u.tex.level; blit.dst.box.z = dstSurf->u.tex.first_layer; blit.dst.format = util_format_linear(dstSurf->format); blit.src.resource = srcObj->pt; blit.src.level = srcAtt->TextureLevel; blit.src.box.z = srcAtt->Zoffset + srcAtt->CubeMapFace; blit.src.format = util_format_linear(srcObj->pt->format); st->pipe->blit(st->pipe, &blit); } } } } else { struct st_renderbuffer *srcRb = st_renderbuffer(readFB->_ColorReadBuffer); struct pipe_surface *srcSurf; GLuint i; if (!srcRb || !srcRb->surface) { return; } srcSurf = srcRb->surface; for (i = 0; i < drawFB->_NumColorDrawBuffers; i++) { struct st_renderbuffer *dstRb = st_renderbuffer(drawFB->_ColorDrawBuffers[i]); if (dstRb) { struct pipe_surface *dstSurf = dstRb->surface; if (dstSurf) { blit.dst.resource = dstSurf->texture; blit.dst.level = dstSurf->u.tex.level; blit.dst.box.z = dstSurf->u.tex.first_layer; blit.dst.format = util_format_linear(dstSurf->format); blit.src.resource = srcSurf->texture; blit.src.level = srcSurf->u.tex.level; blit.src.box.z = srcSurf->u.tex.first_layer; blit.src.format = util_format_linear(srcSurf->format); st->pipe->blit(st->pipe, &blit); } } } } } if (mask & depthStencil) { /* depth and/or stencil blit */ /* get src/dst depth surfaces */ struct gl_renderbuffer_attachment *srcDepth = &readFB->Attachment[BUFFER_DEPTH]; struct gl_renderbuffer_attachment *dstDepth = &drawFB->Attachment[BUFFER_DEPTH]; struct gl_renderbuffer_attachment *srcStencil = &readFB->Attachment[BUFFER_STENCIL]; struct gl_renderbuffer_attachment *dstStencil = &drawFB->Attachment[BUFFER_STENCIL]; struct st_renderbuffer *srcDepthRb = st_renderbuffer(srcDepth->Renderbuffer); struct st_renderbuffer *dstDepthRb = st_renderbuffer(dstDepth->Renderbuffer); struct pipe_surface *dstDepthSurf = dstDepthRb ? dstDepthRb->surface : NULL; struct st_renderbuffer *srcStencilRb = st_renderbuffer(srcStencil->Renderbuffer); struct st_renderbuffer *dstStencilRb = st_renderbuffer(dstStencil->Renderbuffer); struct pipe_surface *dstStencilSurf = dstStencilRb ? dstStencilRb->surface : NULL; if (st_is_depth_stencil_combined(srcDepth, srcStencil) && st_is_depth_stencil_combined(dstDepth, dstStencil)) { blit.mask = 0; if (mask & GL_DEPTH_BUFFER_BIT) blit.mask |= PIPE_MASK_Z; if (mask & GL_STENCIL_BUFFER_BIT) blit.mask |= PIPE_MASK_S; blit.dst.resource = dstDepthSurf->texture; blit.dst.level = dstDepthSurf->u.tex.level; blit.dst.box.z = dstDepthSurf->u.tex.first_layer; blit.dst.format = dstDepthSurf->format; blit.src.resource = srcDepthRb->texture; blit.src.level = srcDepthRb->surface->u.tex.level; blit.src.box.z = srcDepthRb->surface->u.tex.first_layer; blit.src.format = srcDepthRb->surface->format; st->pipe->blit(st->pipe, &blit); } else { /* blitting depth and stencil separately */ if (mask & GL_DEPTH_BUFFER_BIT) { blit.mask = PIPE_MASK_Z; blit.dst.resource = dstDepthSurf->texture; blit.dst.level = dstDepthSurf->u.tex.level; blit.dst.box.z = dstDepthSurf->u.tex.first_layer; blit.dst.format = dstDepthSurf->format; blit.src.resource = srcDepthRb->texture; blit.src.level = srcDepthRb->surface->u.tex.level; blit.src.box.z = srcDepthRb->surface->u.tex.first_layer; blit.src.format = srcDepthRb->surface->format; st->pipe->blit(st->pipe, &blit); } if (mask & GL_STENCIL_BUFFER_BIT) { blit.mask = PIPE_MASK_S; blit.dst.resource = dstStencilSurf->texture; blit.dst.level = dstStencilSurf->u.tex.level; blit.dst.box.z = dstStencilSurf->u.tex.first_layer; blit.dst.format = dstStencilSurf->format; blit.src.resource = srcStencilRb->texture; blit.src.level = srcStencilRb->surface->u.tex.level; blit.src.box.z = srcStencilRb->surface->u.tex.first_layer; blit.src.format = srcStencilRb->surface->format; st->pipe->blit(st->pipe, &blit); } } } }
/** * Do a CopyTexSubImage operation using a read transfer from the source, * a write transfer to the destination and get_tile()/put_tile() to access * the pixels/texels. * * Note: srcY=0=TOP of renderbuffer */ static void fallback_copy_texsubimage(struct gl_context *ctx, struct st_renderbuffer *strb, struct st_texture_image *stImage, GLenum baseFormat, GLint destX, GLint destY, GLint destZ, GLint srcX, GLint srcY, GLsizei width, GLsizei height) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct pipe_transfer *src_trans; GLvoid *texDest; enum pipe_transfer_usage transfer_usage; void *map; if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback processing\n", __FUNCTION__); if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { srcY = strb->Base.Height - srcY - height; } map = pipe_transfer_map(pipe, strb->texture, strb->rtt_level, strb->rtt_face + strb->rtt_slice, PIPE_TRANSFER_READ, srcX, srcY, width, height, &src_trans); if ((baseFormat == GL_DEPTH_COMPONENT || baseFormat == GL_DEPTH_STENCIL) && util_format_is_depth_and_stencil(stImage->pt->format)) transfer_usage = PIPE_TRANSFER_READ_WRITE; else transfer_usage = PIPE_TRANSFER_WRITE; /* XXX this used to ignore destZ param */ texDest = st_texture_image_map(st, stImage, destZ, transfer_usage, destX, destY, width, height); if (baseFormat == GL_DEPTH_COMPONENT || baseFormat == GL_DEPTH_STENCIL) { const GLboolean scaleOrBias = (ctx->Pixel.DepthScale != 1.0F || ctx->Pixel.DepthBias != 0.0F); GLint row, yStep; uint *data; /* determine bottom-to-top vs. top-to-bottom order for src buffer */ if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { srcY = height - 1; yStep = -1; } else { srcY = 0; yStep = 1; } data = malloc(width * sizeof(uint)); if (data) { /* To avoid a large temp memory allocation, do copy row by row */ for (row = 0; row < height; row++, srcY += yStep) { pipe_get_tile_z(src_trans, map, 0, srcY, width, 1, data); if (scaleOrBias) { _mesa_scale_and_bias_depth_uint(ctx, width, data); } pipe_put_tile_z(stImage->transfer, texDest, 0, row, width, 1, data); } } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyTexSubImage()"); } free(data); } else { /* RGBA format */ GLfloat *tempSrc = malloc(width * height * 4 * sizeof(GLfloat)); if (tempSrc && texDest) { const GLint dims = 2; const GLint dstRowStride = stImage->transfer->stride; struct gl_texture_image *texImage = &stImage->base; struct gl_pixelstore_attrib unpack = ctx->DefaultPacking; if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { unpack.Invert = GL_TRUE; } /* get float/RGBA image from framebuffer */ /* XXX this usually involves a lot of int/float conversion. * try to avoid that someday. */ pipe_get_tile_rgba_format(src_trans, map, 0, 0, width, height, util_format_linear(strb->texture->format), tempSrc); /* Store into texture memory. * Note that this does some special things such as pixel transfer * ops and format conversion. In particular, if the dest tex format * is actually RGBA but the user created the texture as GL_RGB we * need to fill-in/override the alpha channel with 1.0. */ _mesa_texstore(ctx, dims, texImage->_BaseFormat, texImage->TexFormat, dstRowStride, (GLubyte **) &texDest, width, height, 1, GL_RGBA, GL_FLOAT, tempSrc, /* src */ &unpack); } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage"); } free(tempSrc); } st_texture_image_unmap(st, stImage); pipe->transfer_unmap(pipe, src_trans); }
/** * Do a CopyTex[Sub]Image1/2/3D() using a hardware (blit) path if possible. * Note that the region to copy has already been clipped so we know we * won't read from outside the source renderbuffer's bounds. * * Note: srcY=0=Bottom of renderbuffer (GL convention) */ static void st_copy_texsubimage(struct gl_context *ctx, struct gl_texture_image *texImage, GLint destX, GLint destY, GLint destZ, struct gl_renderbuffer *rb, GLint srcX, GLint srcY, GLsizei width, GLsizei height) { struct st_texture_image *stImage = st_texture_image(texImage); const GLenum texBaseFormat = texImage->_BaseFormat; struct gl_framebuffer *fb = ctx->ReadBuffer; struct st_renderbuffer *strb; struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; enum pipe_format dest_format, src_format; GLboolean matching_base_formats; GLuint format_writemask, sample_count; struct pipe_surface *dest_surface = NULL; GLboolean do_flip = (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP); struct pipe_surface surf_tmpl; unsigned int dst_usage; GLint srcY0, srcY1; /* make sure finalize_textures has been called? */ if (0) st_validate_state(st); /* determine if copying depth or color data */ if (texBaseFormat == GL_DEPTH_COMPONENT || texBaseFormat == GL_DEPTH_STENCIL) { strb = st_renderbuffer(fb->Attachment[BUFFER_DEPTH].Renderbuffer); } else { /* texBaseFormat == GL_RGB, GL_RGBA, GL_ALPHA, etc */ strb = st_renderbuffer(fb->_ColorReadBuffer); } if (!strb || !strb->surface || !stImage->pt) { debug_printf("%s: null strb or stImage\n", __FUNCTION__); return; } sample_count = strb->surface->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); assert(strb); assert(strb->surface); assert(stImage->pt); src_format = strb->surface->format; dest_format = stImage->pt->format; /* * Determine if the src framebuffer and dest texture have the same * base format. We need this to detect a case such as the framebuffer * being GL_RGBA but the texture being GL_RGB. If the actual hardware * texture format stores RGBA we need to set A=1 (overriding the * framebuffer's alpha values). We can't do that with the blit or * textured-quad paths. */ matching_base_formats = (_mesa_get_format_base_format(strb->Base.Format) == _mesa_get_format_base_format(texImage->TexFormat)); if (ctx->_ImageTransferState) { goto fallback; } if (matching_base_formats && src_format == dest_format && !do_flip) { /* use surface_copy() / blit */ struct pipe_box src_box; u_box_2d_zslice(srcX, srcY, strb->surface->u.tex.first_layer, width, height, &src_box); /* for resource_copy_region(), y=0=top, always */ pipe->resource_copy_region(pipe, /* dest */ stImage->pt, stImage->base.Level, destX, destY, destZ + stImage->base.Face, /* src */ strb->texture, strb->surface->u.tex.level, &src_box); return; } if (texBaseFormat == GL_DEPTH_STENCIL) { goto fallback; } if (texBaseFormat == GL_DEPTH_COMPONENT) { format_writemask = TGSI_WRITEMASK_XYZW; dst_usage = PIPE_BIND_DEPTH_STENCIL; } else { format_writemask = compatible_src_dst_formats(ctx, &strb->Base, texImage); dst_usage = PIPE_BIND_RENDER_TARGET; } if (!format_writemask || !screen->is_format_supported(screen, src_format, PIPE_TEXTURE_2D, sample_count, PIPE_BIND_SAMPLER_VIEW) || !screen->is_format_supported(screen, dest_format, PIPE_TEXTURE_2D, 0, dst_usage)) { goto fallback; } if (do_flip) { srcY1 = strb->Base.Height - srcY - height; srcY0 = srcY1 + height; } else { srcY0 = srcY; srcY1 = srcY0 + height; } /* Disable conditional rendering. */ if (st->render_condition) { pipe->render_condition(pipe, NULL, 0); } memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = util_format_linear(stImage->pt->format); surf_tmpl.usage = dst_usage; surf_tmpl.u.tex.level = stImage->base.Level; surf_tmpl.u.tex.first_layer = stImage->base.Face + destZ; surf_tmpl.u.tex.last_layer = stImage->base.Face + destZ; dest_surface = pipe->create_surface(pipe, stImage->pt, &surf_tmpl); util_blit_pixels_writemask(st->blit, strb->texture, strb->surface->u.tex.level, srcX, srcY0, srcX + width, srcY1, strb->surface->u.tex.first_layer, dest_surface, destX, destY, destX + width, destY + height, 0.0, PIPE_TEX_MIPFILTER_NEAREST, format_writemask); pipe_surface_reference(&dest_surface, NULL); /* Restore conditional rendering state. */ if (st->render_condition) { pipe->render_condition(pipe, st->render_condition, st->condition_mode); } return; fallback: /* software fallback */ fallback_copy_texsubimage(ctx, strb, stImage, texBaseFormat, destX, destY, destZ, srcX, srcY, width, height); }
static void r300_blit(struct pipe_context *pipe, const struct pipe_blit_info *blit) { struct r300_context *r300 = r300_context(pipe); struct pipe_framebuffer_state *fb = (struct pipe_framebuffer_state*)r300->fb_state.state; struct pipe_blit_info info = *blit; /* The driver supports sRGB textures but not framebuffers. Blitting * from sRGB to sRGB should be the same as blitting from linear * to linear, so use that, This avoids incorrect linearization. */ if (util_format_is_srgb(info.src.format)) { info.src.format = util_format_linear(info.src.format); info.dst.format = util_format_linear(info.dst.format); } /* MSAA resolve. */ if (info.src.resource->nr_samples > 1 && !util_format_is_depth_or_stencil(info.src.resource->format)) { r300_msaa_resolve(pipe, &info); return; } /* Can't read MSAA textures. */ if (info.src.resource->nr_samples > 1) { return; } /* Blit a combined depth-stencil resource as color. * S8Z24 is the only supported stencil format. */ if ((info.mask & PIPE_MASK_S) && info.src.format == PIPE_FORMAT_S8_UINT_Z24_UNORM && info.dst.format == PIPE_FORMAT_S8_UINT_Z24_UNORM) { if (info.dst.resource->nr_samples > 1) { /* Cannot do that with MSAA buffers. */ info.mask &= ~PIPE_MASK_S; if (!(info.mask & PIPE_MASK_Z)) { return; } } else { /* Single-sample buffer. */ info.src.format = PIPE_FORMAT_B8G8R8A8_UNORM; info.dst.format = PIPE_FORMAT_B8G8R8A8_UNORM; if (info.mask & PIPE_MASK_Z) { info.mask = PIPE_MASK_RGBA; /* depth+stencil */ } else { info.mask = PIPE_MASK_B; /* stencil only */ } } } /* Decompress ZMASK. */ if (r300->zmask_in_use && !r300->locked_zbuffer) { if (fb->zsbuf->texture == info.src.resource || fb->zsbuf->texture == info.dst.resource) { r300_decompress_zmask(r300); } } r300_blitter_begin(r300, R300_BLIT | (info.render_condition_enable ? 0 : R300_IGNORE_RENDER_COND)); util_blitter_blit(r300->blitter, &info); r300_blitter_end(r300); }
/** * This uses a blit to copy the read buffer to a texture format which matches * the format and type combo and then a fast read-back is done using memcpy. * We can do arbitrary X/Y/Z/W/0/1 swizzling here as long as there is * a format which matches the swizzling. * * If such a format isn't available, we fall back to _mesa_readpixels. * * NOTE: Some drivers use a blit to convert between tiled and linear * texture layouts during texture uploads/downloads, so the blit * we do here should be free in such cases. */ static void st_readpixels(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *pack, GLvoid *pixels) { struct st_context *st = st_context(ctx); struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); struct st_renderbuffer *strb = st_renderbuffer(rb); struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; struct pipe_resource *src; struct pipe_resource *dst = NULL; struct pipe_resource dst_templ; enum pipe_format dst_format, src_format; struct pipe_blit_info blit; unsigned bind = PIPE_BIND_TRANSFER_READ; struct pipe_transfer *tex_xfer; ubyte *map = NULL; /* Validate state (to be sure we have up-to-date framebuffer surfaces) * and flush the bitmap cache prior to reading. */ st_validate_state(st); st_flush_bitmap_cache(st); if (!st->prefer_blit_based_texture_transfer) { goto fallback; } /* This must be done after state validation. */ src = strb->texture; /* XXX Fallback for depth-stencil formats due to an incomplete * stencil blit implementation in some drivers. */ if (format == GL_DEPTH_STENCIL) { goto fallback; } /* We are creating a texture of the size of the region being read back. * Need to check for NPOT texture support. */ if (!screen->get_param(screen, PIPE_CAP_NPOT_TEXTURES) && (!util_is_power_of_two(width) || !util_is_power_of_two(height))) { goto fallback; } /* If the base internal format and the texture format don't match, we have * to use the slow path. */ if (rb->_BaseFormat != _mesa_get_format_base_format(rb->Format)) { goto fallback; } /* See if the texture format already matches the format and type, * in which case the memcpy-based fast path will likely be used and * we don't have to blit. */ if (_mesa_format_matches_format_and_type(rb->Format, format, type, pack->SwapBytes)) { goto fallback; } if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_TRUE)) { goto fallback; } /* Convert the source format to what is expected by ReadPixels * and see if it's supported. */ src_format = util_format_linear(src->format); src_format = util_format_luminance_to_red(src_format); src_format = util_format_intensity_to_red(src_format); if (!src_format || !screen->is_format_supported(screen, src_format, src->target, src->nr_samples, PIPE_BIND_SAMPLER_VIEW)) { goto fallback; } if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL) bind |= PIPE_BIND_DEPTH_STENCIL; else bind |= PIPE_BIND_RENDER_TARGET; /* Choose the destination format by finding the best match * for the format+type combo. */ dst_format = st_choose_matching_format(screen, bind, format, type, pack->SwapBytes); if (dst_format == PIPE_FORMAT_NONE) { goto fallback; } /* create the destination texture */ memset(&dst_templ, 0, sizeof(dst_templ)); dst_templ.target = PIPE_TEXTURE_2D; dst_templ.format = dst_format; dst_templ.bind = bind; dst_templ.usage = PIPE_USAGE_STAGING; st_gl_texture_dims_to_pipe_dims(GL_TEXTURE_2D, width, height, 1, &dst_templ.width0, &dst_templ.height0, &dst_templ.depth0, &dst_templ.array_size); dst = screen->resource_create(screen, &dst_templ); if (!dst) { goto fallback; } memset(&blit, 0, sizeof(blit)); blit.src.resource = src; blit.src.level = strb->surface->u.tex.level; blit.src.format = src_format; blit.dst.resource = dst; blit.dst.level = 0; blit.dst.format = dst->format; blit.src.box.x = x; blit.dst.box.x = 0; blit.src.box.y = y; blit.dst.box.y = 0; blit.src.box.z = strb->surface->u.tex.first_layer; blit.dst.box.z = 0; blit.src.box.width = blit.dst.box.width = width; blit.src.box.height = blit.dst.box.height = height; blit.src.box.depth = blit.dst.box.depth = 1; blit.mask = st_get_blit_mask(rb->_BaseFormat, format); blit.filter = PIPE_TEX_FILTER_NEAREST; blit.scissor_enable = FALSE; if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { blit.src.box.y = rb->Height - blit.src.box.y; blit.src.box.height = -blit.src.box.height; } /* blit */ st->pipe->blit(st->pipe, &blit); /* map resources */ pixels = _mesa_map_pbo_dest(ctx, pack, pixels); map = pipe_transfer_map_3d(pipe, dst, 0, PIPE_TRANSFER_READ, 0, 0, 0, width, height, 1, &tex_xfer); if (!map) { _mesa_unmap_pbo_dest(ctx, pack); pipe_resource_reference(&dst, NULL); goto fallback; } /* memcpy data into a user buffer */ { const uint bytesPerRow = width * util_format_get_blocksize(dst_format); GLuint row; for (row = 0; row < (unsigned) height; row++) { GLvoid *dest = _mesa_image_address3d(pack, pixels, width, height, format, type, 0, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } } pipe_transfer_unmap(pipe, tex_xfer); _mesa_unmap_pbo_dest(ctx, pack); pipe_resource_reference(&dst, NULL); return; fallback: _mesa_readpixels(ctx, x, y, width, height, format, type, pack, pixels); }
/** * Called by ctx->Driver.RenderTexture */ static void st_render_texture(struct gl_context *ctx, struct gl_framebuffer *fb, struct gl_renderbuffer_attachment *att) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct gl_renderbuffer *rb = att->Renderbuffer; struct st_renderbuffer *strb = st_renderbuffer(rb); struct pipe_resource *pt; struct st_texture_object *stObj; const struct gl_texture_image *texImage; struct pipe_surface surf_tmpl; if (!st_finalize_texture(ctx, pipe, att->Texture)) return; pt = st_get_texobj_resource(att->Texture); assert(pt); /* get pointer to texture image we're rendeing to */ texImage = _mesa_get_attachment_teximage(att); /* 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; pipe_resource_reference( &strb->texture, pt ); pipe_surface_release(pipe, &strb->surface); assert(strb->rtt_level <= strb->texture->last_level); /* new surface for rendering into the texture */ memset(&surf_tmpl, 0, sizeof(surf_tmpl)); surf_tmpl.format = ctx->Color.sRGBEnabled ? strb->texture->format : util_format_linear(strb->texture->format); surf_tmpl.u.tex.level = strb->rtt_level; surf_tmpl.u.tex.first_layer = strb->rtt_face + strb->rtt_slice; surf_tmpl.u.tex.last_layer = strb->rtt_face + strb->rtt_slice; strb->surface = pipe->create_surface(pipe, strb->texture, &surf_tmpl); strb->Base.Format = st_pipe_format_to_mesa_format(pt->format); /* Invalidate buffer state so that the pipe's framebuffer state * gets updated. * That's where the new renderbuffer (which we just created) gets * passed to the pipe as a (color/depth) render target. */ st_invalidate_state(ctx, _NEW_BUFFERS); /* Need to trigger a call to update_framebuffer() since we just * attached a new renderbuffer. */ ctx->NewState |= _NEW_BUFFERS; }