static bool intel_copy_texsubimage(struct intel_context *intel, struct intel_texture_image *intelImage, GLint dstx, GLint dsty, GLint slice, struct intel_renderbuffer *irb, GLint x, GLint y, GLsizei width, GLsizei height) { const GLenum internalFormat = intelImage->base.Base.InternalFormat; intel_prepare_render(intel); if (!intelImage->mt || !irb || !irb->mt) { if (unlikely(INTEL_DEBUG & DEBUG_PERF)) fprintf(stderr, "%s fail %p %p (0x%08x)\n", __FUNCTION__, intelImage->mt, irb, internalFormat); return false; } /* blit from src buffer to texture */ if (!intel_miptree_blit(intel, irb->mt, irb->mt_level, irb->mt_layer, x, y, irb->Base.Base.Name == 0, intelImage->mt, intelImage->base.Base.Level, intelImage->base.Base.Face + slice, dstx, dsty, false, width, height, GL_COPY)) { return false; } return true; }
static bool intel_copy_texsubimage(struct brw_context *brw, struct intel_texture_image *intelImage, GLint dstx, GLint dsty, GLint slice, struct intel_renderbuffer *irb, GLint x, GLint y, GLsizei width, GLsizei height) { const GLenum internalFormat = intelImage->base.Base.InternalFormat; bool ret; /* No pixel transfer operations (zoom, bias, mapping), just a blit */ if (brw->ctx._ImageTransferState) return false; intel_prepare_render(brw); /* glCopyTexSubImage() can be called on a multisampled renderbuffer (if * that renderbuffer is associated with the window system framebuffer), * however the hardware blitter can't handle this case, so fall back to * meta (which can, since it uses ReadPixels). */ if (irb->Base.Base.NumSamples != 0) return false; /* glCopyTexSubImage() can't be called on a multisampled texture. */ assert(intelImage->base.Base.NumSamples == 0); if (!intelImage->mt || !irb || !irb->mt) { if (unlikely(INTEL_DEBUG & DEBUG_PERF)) fprintf(stderr, "%s fail %p %p (0x%08x)\n", __func__, intelImage->mt, irb, internalFormat); return false; } /* account for view parameters and face index */ int dst_level = intelImage->base.Base.Level + intelImage->base.Base.TexObject->MinLevel; int dst_slice = slice + intelImage->base.Base.Face + intelImage->base.Base.TexObject->MinLayer; _mesa_unlock_texture(&brw->ctx, intelImage->base.Base.TexObject); /* blit from src buffer to texture */ ret = intel_miptree_blit(brw, irb->mt, irb->mt_level, irb->mt_layer, x, y, irb->Base.Base.Name == 0, intelImage->mt, dst_level, dst_slice, dstx, dsty, false, width, height, GL_COPY); _mesa_lock_texture(&brw->ctx, intelImage->base.Base.TexObject); return ret; }
static void intel_miptree_copy_slice(struct intel_context *intel, struct intel_mipmap_tree *dst_mt, struct intel_mipmap_tree *src_mt, int level, int face, int depth) { mesa_format format = src_mt->format; uint32_t width = src_mt->level[level].width; uint32_t height = src_mt->level[level].height; int slice; if (face > 0) slice = face; else slice = depth; assert(depth < src_mt->level[level].depth); assert(src_mt->format == dst_mt->format); if (dst_mt->compressed) { height = ALIGN(height, dst_mt->align_h) / dst_mt->align_h; width = ALIGN(width, dst_mt->align_w); } uint32_t dst_x, dst_y, src_x, src_y; intel_miptree_get_image_offset(dst_mt, level, slice, &dst_x, &dst_y); intel_miptree_get_image_offset(src_mt, level, slice, &src_x, &src_y); DBG("validate blit mt %s %p %d,%d/%d -> mt %s %p %d,%d/%d (%dx%d)\n", _mesa_get_format_name(src_mt->format), src_mt, src_x, src_y, src_mt->region->pitch, _mesa_get_format_name(dst_mt->format), dst_mt, dst_x, dst_y, dst_mt->region->pitch, width, height); if (!intel_miptree_blit(intel, src_mt, level, slice, 0, 0, false, dst_mt, level, slice, 0, 0, false, width, height, GL_COPY)) { perf_debug("miptree validate blit for %s failed\n", _mesa_get_format_name(format)); intel_miptree_copy_slice_sw(intel, dst_mt, src_mt, level, slice, width, height); } }
static void intel_miptree_map_blit(struct intel_context *intel, struct intel_mipmap_tree *mt, struct intel_miptree_map *map, unsigned int level, unsigned int slice) { map->mt = intel_miptree_create(intel, GL_TEXTURE_2D, mt->format, 0, 0, map->w, map->h, 1, false, INTEL_MIPTREE_TILING_NONE); if (!map->mt) { fprintf(stderr, "Failed to allocate blit temporary\n"); goto fail; } map->stride = map->mt->region->pitch; if (!intel_miptree_blit(intel, mt, level, slice, map->x, map->y, false, map->mt, 0, 0, 0, 0, false, map->w, map->h, GL_COPY)) { fprintf(stderr, "Failed to blit\n"); goto fail; } intel_batchbuffer_flush(intel); map->ptr = intel_miptree_map_raw(intel, map->mt); DBG("%s: %d,%d %dx%d from mt %p (%s) %d,%d = %p/%d\n", __func__, map->x, map->y, map->w, map->h, mt, _mesa_get_format_name(mt->format), level, slice, map->ptr, map->stride); return; fail: intel_miptree_release(&map->mt); map->ptr = NULL; map->stride = 0; }
static void intel_miptree_unmap_blit(struct intel_context *intel, struct intel_mipmap_tree *mt, struct intel_miptree_map *map, unsigned int level, unsigned int slice) { struct gl_context *ctx = &intel->ctx; intel_miptree_unmap_raw(intel, map->mt); if (map->mode & GL_MAP_WRITE_BIT) { bool ok = intel_miptree_blit(intel, map->mt, 0, 0, 0, 0, false, mt, level, slice, map->x, map->y, false, map->w, map->h, GL_COPY); WARN_ONCE(!ok, "Failed to blit from linear temporary mapping"); } intel_miptree_release(&map->mt); }
/* XXX: Do this for TexSubImage also: */ static bool try_pbo_upload(struct gl_context *ctx, struct gl_texture_image *image, const struct gl_pixelstore_attrib *unpack, GLenum format, GLenum type, const void *pixels) { struct intel_texture_image *intelImage = intel_texture_image(image); struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *pbo = intel_buffer_object(unpack->BufferObj); GLuint src_offset; drm_intel_bo *src_buffer; if (!_mesa_is_bufferobj(unpack->BufferObj)) return false; DBG("trying pbo upload\n"); if (intel->ctx._ImageTransferState || unpack->SkipPixels || unpack->SkipRows) { DBG("%s: image transfer\n", __FUNCTION__); return false; } ctx->Driver.AllocTextureImageBuffer(ctx, image); if (!intelImage->mt) { DBG("%s: no miptree\n", __FUNCTION__); return false; } if (!_mesa_format_matches_format_and_type(intelImage->mt->format, format, type, false)) { DBG("%s: format mismatch (upload to %s with format 0x%x, type 0x%x)\n", __FUNCTION__, _mesa_get_format_name(intelImage->mt->format), format, type); return false; } if (image->TexObject->Target == GL_TEXTURE_1D_ARRAY || image->TexObject->Target == GL_TEXTURE_2D_ARRAY) { DBG("%s: no support for array textures\n", __FUNCTION__); return false; } src_buffer = intel_bufferobj_source(intel, pbo, 64, &src_offset); /* note: potential 64-bit ptr to 32-bit int cast */ src_offset += (GLuint) (unsigned long) pixels; int src_stride = _mesa_image_row_stride(unpack, image->Width, format, type); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(intel, src_buffer, intelImage->mt->format, src_offset, image->Width, image->Height, src_stride, I915_TILING_NONE); if (!pbo_mt) return false; if (!intel_miptree_blit(intel, pbo_mt, 0, 0, 0, 0, false, intelImage->mt, image->Level, image->Face, 0, 0, false, image->Width, image->Height, GL_COPY)) { DBG("%s: blit failed\n", __FUNCTION__); intel_miptree_release(&pbo_mt); return false; } intel_miptree_release(&pbo_mt); DBG("%s: success\n", __FUNCTION__); return true; }
/** * Try to do a glBlitFramebuffer using glCopyTexSubImage2D * We can do this when the dst renderbuffer is actually a texture and * there is no scaling, mirroring or scissoring. * * \return new buffer mask indicating the buffers left to blit using the * normal path. */ static GLbitfield intel_blit_framebuffer_with_blitter(struct gl_context *ctx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { struct brw_context *brw = brw_context(ctx); /* Sync up the state of window system buffers. We need to do this before * we go looking for the buffers. */ intel_prepare_render(brw); if (mask & GL_COLOR_BUFFER_BIT) { GLint i; const struct gl_framebuffer *drawFb = ctx->DrawBuffer; const struct gl_framebuffer *readFb = ctx->ReadBuffer; struct gl_renderbuffer *src_rb = readFb->_ColorReadBuffer; struct intel_renderbuffer *src_irb = intel_renderbuffer(src_rb); if (!src_irb) { perf_debug("glBlitFramebuffer(): missing src renderbuffer. " "Falling back to software rendering.\n"); return mask; } /* If the source and destination are the same size with no mirroring, * the rectangles are within the size of the texture and there is no * scissor, then we can probably use the blit engine. */ if (!(srcX0 - srcX1 == dstX0 - dstX1 && srcY0 - srcY1 == dstY0 - dstY1 && srcX1 >= srcX0 && srcY1 >= srcY0 && srcX0 >= 0 && srcX1 <= readFb->Width && srcY0 >= 0 && srcY1 <= readFb->Height && dstX0 >= 0 && dstX1 <= drawFb->Width && dstY0 >= 0 && dstY1 <= drawFb->Height && !ctx->Scissor.Enabled)) { perf_debug("glBlitFramebuffer(): non-1:1 blit. " "Falling back to software rendering.\n"); return mask; } /* Blit to all active draw buffers. We don't do any pre-checking, * because we assume that copying to MRTs is rare, and failure midway * through copying is even more rare. Even if it was to occur, it's * safe to let meta start the copy over from scratch, because * glBlitFramebuffer completely overwrites the destination pixels, and * results are undefined if any destination pixels have a dependency on * source pixels. */ for (i = 0; i < ctx->DrawBuffer->_NumColorDrawBuffers; i++) { struct gl_renderbuffer *dst_rb = ctx->DrawBuffer->_ColorDrawBuffers[i]; struct intel_renderbuffer *dst_irb = intel_renderbuffer(dst_rb); if (!dst_irb) { perf_debug("glBlitFramebuffer(): missing dst renderbuffer. " "Falling back to software rendering.\n"); return mask; } gl_format src_format = _mesa_get_srgb_format_linear(src_rb->Format); gl_format dst_format = _mesa_get_srgb_format_linear(dst_rb->Format); if (src_format != dst_format) { perf_debug("glBlitFramebuffer(): unsupported blit from %s to %s. " "Falling back to software rendering.\n", _mesa_get_format_name(src_format), _mesa_get_format_name(dst_format)); return mask; } if (!intel_miptree_blit(brw, src_irb->mt, src_irb->mt_level, src_irb->mt_layer, srcX0, srcY0, src_rb->Name == 0, dst_irb->mt, dst_irb->mt_level, dst_irb->mt_layer, dstX0, dstY0, dst_rb->Name == 0, dstX1 - dstX0, dstY1 - dstY0, GL_COPY)) { perf_debug("glBlitFramebuffer(): unknown blit failure. " "Falling back to software rendering.\n"); return mask; } } mask &= ~GL_COLOR_BUFFER_BIT; } return mask; }
static bool do_blit_drawpixels(struct gl_context * ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid * pixels) { struct brw_context *brw = brw_context(ctx); struct intel_buffer_object *src = intel_buffer_object(unpack->BufferObj); GLuint src_offset; drm_intel_bo *src_buffer; DBG("%s\n", __FUNCTION__); if (!intel_check_blit_fragment_ops(ctx, false)) return false; if (ctx->DrawBuffer->_NumColorDrawBuffers != 1) { DBG("%s: fallback due to MRT\n", __FUNCTION__); return false; } struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0]; struct intel_renderbuffer *irb = intel_renderbuffer(rb); if (!_mesa_format_matches_format_and_type(irb->mt->format, format, type, false)) { DBG("%s: bad format for blit\n", __FUNCTION__); return false; } if (unpack->SwapBytes || unpack->LsbFirst || unpack->SkipPixels || unpack->SkipRows) { DBG("%s: bad packing params\n", __FUNCTION__); return false; } int src_stride = _mesa_image_row_stride(unpack, width, format, type); bool src_flip = false; /* Mesa flips the src_stride for unpack->Invert, but we want our mt to have * a normal src_stride. */ if (unpack->Invert) { src_stride = -src_stride; src_flip = true; } src_offset = (GLintptr)pixels; src_offset += _mesa_image_offset(2, unpack, width, height, format, type, 0, 0, 0); intel_prepare_render(brw); src_buffer = intel_bufferobj_buffer(brw, src, src_offset, width * height * irb->mt->cpp); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(brw, src_buffer, irb->mt->format, src_offset, width, height, src_stride, I915_TILING_NONE); if (!pbo_mt) return false; if (!intel_miptree_blit(brw, pbo_mt, 0, 0, 0, 0, src_flip, irb->mt, irb->mt_level, irb->mt_layer, x, y, _mesa_is_winsys_fbo(ctx->DrawBuffer), width, height, GL_COPY)) { DBG("%s: blit failed\n", __FUNCTION__); intel_miptree_release(&pbo_mt); return false; } intel_miptree_release(&pbo_mt); if (ctx->Query.CurrentOcclusionObject) ctx->Query.CurrentOcclusionObject->Result += width * height; intel_check_front_buffer_rendering(brw); DBG("%s: success\n", __FUNCTION__); return true; }
/** * CopyPixels with the blitter. Don't support zooming, pixel transfer, etc. */ static bool do_blit_copypixels(struct gl_context * ctx, GLint srcx, GLint srcy, GLsizei width, GLsizei height, GLint dstx, GLint dsty, GLenum type) { struct brw_context *brw = brw_context(ctx); struct gl_framebuffer *fb = ctx->DrawBuffer; struct gl_framebuffer *read_fb = ctx->ReadBuffer; GLint orig_dstx; GLint orig_dsty; GLint orig_srcx; GLint orig_srcy; struct intel_renderbuffer *draw_irb = NULL; struct intel_renderbuffer *read_irb = NULL; /* Update draw buffer bounds */ _mesa_update_state(ctx); intel_prepare_render(brw); switch (type) { case GL_COLOR: if (fb->_NumColorDrawBuffers != 1) { perf_debug("glCopyPixels() fallback: MRT\n"); return false; } draw_irb = intel_renderbuffer(fb->_ColorDrawBuffers[0]); read_irb = intel_renderbuffer(read_fb->_ColorReadBuffer); break; case GL_DEPTH_STENCIL_EXT: draw_irb = intel_renderbuffer(fb->Attachment[BUFFER_DEPTH].Renderbuffer); read_irb = intel_renderbuffer(read_fb->Attachment[BUFFER_DEPTH].Renderbuffer); break; case GL_DEPTH: perf_debug("glCopyPixels() fallback: GL_DEPTH\n"); return false; case GL_STENCIL: perf_debug("glCopyPixels() fallback: GL_STENCIL\n"); return false; default: perf_debug("glCopyPixels(): Unknown type\n"); return false; } if (!draw_irb) { perf_debug("glCopyPixels() fallback: missing draw buffer\n"); return false; } if (!read_irb) { perf_debug("glCopyPixels() fallback: missing read buffer\n"); return false; } if (draw_irb->mt->num_samples > 1 || read_irb->mt->num_samples > 1) { perf_debug("glCopyPixels() fallback: multisampled buffers\n"); return false; } if (ctx->_ImageTransferState) { perf_debug("glCopyPixels(): Unsupported image transfer state\n"); return false; } if (ctx->Depth.Test) { perf_debug("glCopyPixels(): Unsupported depth test state\n"); return false; } if (ctx->Stencil._Enabled) { perf_debug("glCopyPixels(): Unsupported stencil test state\n"); return false; } if (ctx->Fog.Enabled || ctx->Texture._MaxEnabledTexImageUnit != -1 || ctx->FragmentProgram._Enabled) { perf_debug("glCopyPixels(): Unsupported fragment shader state\n"); return false; } if (ctx->Color.AlphaEnabled || ctx->Color.BlendEnabled) { perf_debug("glCopyPixels(): Unsupported blend state\n"); return false; } if (!ctx->Color.ColorMask[0][0] || !ctx->Color.ColorMask[0][1] || !ctx->Color.ColorMask[0][2] || !ctx->Color.ColorMask[0][3]) { perf_debug("glCopyPixels(): Unsupported color mask state\n"); return false; } if (ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F) { perf_debug("glCopyPixles(): Unsupported pixel zoom\n"); return false; } intel_batchbuffer_flush(brw); /* Clip to destination buffer. */ orig_dstx = dstx; orig_dsty = dsty; if (!_mesa_clip_to_region(fb->_Xmin, fb->_Ymin, fb->_Xmax, fb->_Ymax, &dstx, &dsty, &width, &height)) goto out; /* Adjust src coords for our post-clipped destination origin */ srcx += dstx - orig_dstx; srcy += dsty - orig_dsty; /* Clip to source buffer. */ orig_srcx = srcx; orig_srcy = srcy; if (!_mesa_clip_to_region(0, 0, read_fb->Width, read_fb->Height, &srcx, &srcy, &width, &height)) goto out; /* Adjust dst coords for our post-clipped source origin */ dstx += srcx - orig_srcx; dsty += srcy - orig_srcy; if (!intel_miptree_blit(brw, read_irb->mt, read_irb->mt_level, read_irb->mt_layer, srcx, srcy, _mesa_is_winsys_fbo(read_fb), draw_irb->mt, draw_irb->mt_level, draw_irb->mt_layer, dstx, dsty, _mesa_is_winsys_fbo(fb), width, height, (ctx->Color.ColorLogicOpEnabled ? ctx->Color.LogicOp : GL_COPY))) { DBG("%s: blit failure\n", __FUNCTION__); return false; } if (ctx->Query.CurrentOcclusionObject) ctx->Query.CurrentOcclusionObject->Result += width * height; out: DBG("%s: success\n", __FUNCTION__); return true; }
static bool intel_blit_texsubimage(struct gl_context * ctx, struct gl_texture_image *texImage, GLint xoffset, GLint yoffset, GLint width, GLint height, GLenum format, GLenum type, const void *pixels, const struct gl_pixelstore_attrib *packing) { struct intel_context *intel = intel_context(ctx); struct intel_texture_image *intelImage = intel_texture_image(texImage); /* Try to do a blit upload of the subimage if the texture is * currently busy. */ if (!intelImage->mt) return false; /* The blitter can't handle Y tiling */ if (intelImage->mt->region->tiling == I915_TILING_Y) return false; if (texImage->TexObject->Target != GL_TEXTURE_2D) return false; if (!drm_intel_bo_busy(intelImage->mt->region->bo)) return false; DBG("BLT subimage %s target %s level %d offset %d,%d %dx%d\n", __func__, _mesa_enum_to_string(texImage->TexObject->Target), texImage->Level, xoffset, yoffset, width, height); pixels = _mesa_validate_pbo_teximage(ctx, 2, width, height, 1, format, type, pixels, packing, "glTexSubImage"); if (!pixels) return false; struct intel_mipmap_tree *temp_mt = intel_miptree_create(intel, GL_TEXTURE_2D, texImage->TexFormat, 0, 0, width, height, 1, false, INTEL_MIPTREE_TILING_NONE); if (!temp_mt) goto err; GLubyte *dst = intel_miptree_map_raw(intel, temp_mt); if (!dst) goto err; if (!_mesa_texstore(ctx, 2, texImage->_BaseFormat, texImage->TexFormat, temp_mt->region->pitch, &dst, width, height, 1, format, type, pixels, packing)) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "intelTexSubImage"); } intel_miptree_unmap_raw(temp_mt); bool ret; ret = intel_miptree_blit(intel, temp_mt, 0, 0, 0, 0, false, intelImage->mt, texImage->Level, texImage->Face, xoffset, yoffset, false, width, height, COLOR_LOGICOP_COPY); assert(ret); intel_miptree_release(&temp_mt); _mesa_unmap_teximage_pbo(ctx, packing); return ret; err: _mesa_error(ctx, GL_OUT_OF_MEMORY, "intelTexSubImage"); intel_miptree_release(&temp_mt); _mesa_unmap_teximage_pbo(ctx, packing); return false; }
static bool do_blit_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 brw_context *brw = brw_context(ctx); struct intel_buffer_object *dst = intel_buffer_object(pack->BufferObj); GLuint dst_offset; drm_intel_bo *dst_buffer; GLint dst_x, dst_y; GLuint dirty; DBG("%s\n", __FUNCTION__); assert(_mesa_is_bufferobj(pack->BufferObj)); struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; struct intel_renderbuffer *irb = intel_renderbuffer(rb); if (ctx->_ImageTransferState || !_mesa_format_matches_format_and_type(irb->mt->format, format, type, false)) { DBG("%s - bad format for blit\n", __FUNCTION__); return false; } if (pack->SwapBytes || pack->LsbFirst) { DBG("%s: bad packing params\n", __FUNCTION__); return false; } int dst_stride = _mesa_image_row_stride(pack, width, format, type); bool dst_flip = false; /* Mesa flips the dst_stride for pack->Invert, but we want our mt to have a * normal dst_stride. */ struct gl_pixelstore_attrib uninverted_pack = *pack; if (pack->Invert) { dst_stride = -dst_stride; dst_flip = true; uninverted_pack.Invert = false; } dst_offset = (GLintptr)pixels; dst_offset += _mesa_image_offset(2, &uninverted_pack, width, height, format, type, 0, 0, 0); if (!_mesa_clip_copytexsubimage(ctx, &dst_x, &dst_y, &x, &y, &width, &height)) { return true; } dirty = brw->front_buffer_dirty; intel_prepare_render(brw); brw->front_buffer_dirty = dirty; dst_buffer = intel_bufferobj_buffer(brw, dst, dst_offset, height * dst_stride); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(brw, dst_buffer, irb->mt->format, dst_offset, width, height, dst_stride, I915_TILING_NONE); if (!intel_miptree_blit(brw, irb->mt, irb->mt_level, irb->mt_layer, x, y, _mesa_is_winsys_fbo(ctx->ReadBuffer), pbo_mt, 0, 0, 0, 0, dst_flip, width, height, GL_COPY)) { return false; } intel_miptree_release(&pbo_mt); DBG("%s - DONE\n", __FUNCTION__); return true; }