/** * Replace data in a subrange of buffer object. If the data range * specified by size + offset extends beyond the end of the buffer or * if data is NULL, no copy is performed. * Called via glBufferSubDataARB(). */ static void intel_bufferobj_subdata(struct gl_context * ctx, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); bool busy; if (size == 0) return; assert(intel_obj); /* If we have a single copy in system memory, update that */ if (intel_obj->sys_buffer) { if (intel_obj->source) release_buffer(intel_obj); if (intel_obj->buffer == NULL) { memcpy((char *)intel_obj->sys_buffer + offset, data, size); return; } free(intel_obj->sys_buffer); intel_obj->sys_buffer = NULL; } /* Otherwise we need to update the copy in video memory. */ busy = drm_intel_bo_busy(intel_obj->buffer) || drm_intel_bo_references(intel->batch.bo, intel_obj->buffer); if (busy) { if (size == intel_obj->Base.Size) { /* Replace the current busy bo with fresh data. */ drm_intel_bo_unreference(intel_obj->buffer); intel_bufferobj_alloc_buffer(intel, intel_obj); drm_intel_bo_subdata(intel_obj->buffer, 0, size, data); } else { perf_debug("Using a blit copy to avoid stalling on %ldb " "glBufferSubData() to a busy buffer object.\n", (long)size); drm_intel_bo *temp_bo = drm_intel_bo_alloc(intel->bufmgr, "subdata temp", size, 64); drm_intel_bo_subdata(temp_bo, 0, size, data); intel_emit_linear_blit(intel, intel_obj->buffer, offset, temp_bo, 0, size); drm_intel_bo_unreference(temp_bo); } } else { drm_intel_bo_subdata(intel_obj->buffer, offset, size, data); } }
/** * Called via glMapBufferARB(). */ static void * intel_bufferobj_map(struct gl_context * ctx, GLenum target, GLenum access, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); GLboolean read_only = (access == GL_READ_ONLY_ARB); GLboolean write_only = (access == GL_WRITE_ONLY_ARB); assert(intel_obj); if (intel_obj->sys_buffer) { if (!read_only && intel_obj->source) { release_buffer(intel_obj); } if (!intel_obj->buffer || intel_obj->source) { obj->Pointer = intel_obj->sys_buffer; obj->Length = obj->Size; obj->Offset = 0; return obj->Pointer; } free(intel_obj->sys_buffer); intel_obj->sys_buffer = NULL; } /* Flush any existing batchbuffer that might reference this data. */ if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) intel_flush(ctx); if (intel_obj->region) intel_bufferobj_cow(intel, intel_obj); if (intel_obj->buffer == NULL) { obj->Pointer = NULL; return NULL; } if (write_only) { drm_intel_gem_bo_map_gtt(intel_obj->buffer); intel_obj->mapped_gtt = GL_TRUE; } else { drm_intel_bo_map(intel_obj->buffer, !read_only); intel_obj->mapped_gtt = GL_FALSE; } obj->Pointer = intel_obj->buffer->virtual; obj->Length = obj->Size; obj->Offset = 0; return obj->Pointer; }
/** * Called via glGetBufferSubDataARB(). */ static void intel_bufferobj_get_subdata(struct gl_context * ctx, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data, struct gl_buffer_object *obj) { struct intel_buffer_object *intel_obj = intel_buffer_object(obj); struct intel_context *intel = intel_context(ctx); assert(intel_obj); if (intel_obj->sys_buffer) memcpy(data, (char *)intel_obj->sys_buffer + offset, size); else { if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) { intel_batchbuffer_flush(intel); } drm_intel_bo_get_subdata(intel_obj->buffer, offset, size, data); } }
/** * Replace data in a subrange of buffer object. If the data range * specified by size + offset extends beyond the end of the buffer or * if data is NULL, no copy is performed. * Called via glBufferSubDataARB(). */ static void intel_bufferobj_subdata(GLcontext * ctx, GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); if (size == 0) return; assert(intel_obj); if (intel_obj->region) intel_bufferobj_cow(intel, intel_obj); if (intel_obj->sys_buffer) memcpy((char *)intel_obj->sys_buffer + offset, data, size); else { /* Flush any existing batchbuffer that might reference this data. */ if (drm_intel_bo_busy(intel_obj->buffer) || drm_intel_bo_references(intel->batch->buf, intel_obj->buffer)) { drm_intel_bo *temp_bo; temp_bo = drm_intel_bo_alloc(intel->bufmgr, "subdata temp", size, 64); drm_intel_bo_subdata(temp_bo, 0, size, data); intel_emit_linear_blit(intel, intel_obj->buffer, offset, temp_bo, 0, size); drm_intel_bo_unreference(temp_bo); } else { drm_intel_bo_subdata(intel_obj->buffer, offset, size, data); } } }
/** * Called via glMapBufferRange(). * * The goal of this extension is to allow apps to accumulate their rendering * at the same time as they accumulate their buffer object. Without it, * you'd end up blocking on execution of rendering every time you mapped * the buffer to put new data in. * * We support it in 3 ways: If unsynchronized, then don't bother * flushing the batchbuffer before mapping the buffer, which can save blocking * in many cases. If we would still block, and they allow the whole buffer * to be invalidated, then just allocate a new buffer to replace the old one. * If not, and we'd block, and they allow the subrange of the buffer to be * invalidated, then we can make a new little BO, let them write into that, * and blit it into the real BO at unmap time. */ static void * intel_bufferobj_map_range(GLcontext * ctx, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); assert(intel_obj); /* _mesa_MapBufferRange (GL entrypoint) sets these, but the vbo module also * internally uses our functions directly. */ obj->Offset = offset; obj->Length = length; obj->AccessFlags = access; if (intel_obj->sys_buffer) { obj->Pointer = intel_obj->sys_buffer + offset; return obj->Pointer; } if (intel_obj->region) intel_bufferobj_cow(intel, intel_obj); /* If the mapping is synchronized with other GL operations, flush * the batchbuffer so that GEM knows about the buffer access for later * syncing. */ if (!(access & GL_MAP_UNSYNCHRONIZED_BIT) && drm_intel_bo_references(intel->batch->buf, intel_obj->buffer)) intelFlush(ctx); if (intel_obj->buffer == NULL) { obj->Pointer = NULL; return NULL; } /* If the user doesn't care about existing buffer contents and mapping * would cause us to block, then throw out the old buffer. */ if (!(access & GL_MAP_UNSYNCHRONIZED_BIT) && (access & GL_MAP_INVALIDATE_BUFFER_BIT) && drm_intel_bo_busy(intel_obj->buffer)) { drm_intel_bo_unreference(intel_obj->buffer); intel_obj->buffer = dri_bo_alloc(intel->bufmgr, "bufferobj", intel_obj->Base.Size, 64); } /* If the user is mapping a range of an active buffer object but * doesn't require the current contents of that range, make a new * BO, and we'll copy what they put in there out at unmap or * FlushRange time. */ if ((access & GL_MAP_INVALIDATE_RANGE_BIT) && drm_intel_bo_busy(intel_obj->buffer)) { if (access & GL_MAP_FLUSH_EXPLICIT_BIT) { intel_obj->range_map_buffer = _mesa_malloc(length); obj->Pointer = intel_obj->range_map_buffer; } else { intel_obj->range_map_bo = drm_intel_bo_alloc(intel->bufmgr, "range map", length, 64); if (!(access & GL_MAP_READ_BIT) && intel->intelScreen->kernel_exec_fencing) { drm_intel_gem_bo_map_gtt(intel_obj->range_map_bo); intel_obj->mapped_gtt = GL_TRUE; } else { drm_intel_bo_map(intel_obj->range_map_bo, (access & GL_MAP_WRITE_BIT) != 0); intel_obj->mapped_gtt = GL_FALSE; } obj->Pointer = intel_obj->range_map_bo->virtual; } return obj->Pointer; } if (!(access & GL_MAP_READ_BIT) && intel->intelScreen->kernel_exec_fencing) { drm_intel_gem_bo_map_gtt(intel_obj->buffer); intel_obj->mapped_gtt = GL_TRUE; } else { drm_intel_bo_map(intel_obj->buffer, (access & GL_MAP_WRITE_BIT) != 0); intel_obj->mapped_gtt = GL_FALSE; } obj->Pointer = intel_obj->buffer->virtual + offset; return obj->Pointer; }
bool intel_bo_has_reloc(struct intel_bo *bo, struct intel_bo *target_bo) { return drm_intel_bo_references(gem_bo(bo), gem_bo(target_bo)); }
/** * \brief A fast path for glGetTexImage. * * \see intel_readpixels_tiled_memcpy() */ bool intel_gettexsubimage_tiled_memcpy(struct gl_context *ctx, struct gl_texture_image *texImage, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing) { struct brw_context *brw = brw_context(ctx); struct intel_texture_image *image = intel_texture_image(texImage); int dst_pitch; /* The miptree's buffer. */ drm_intel_bo *bo; int error = 0; uint32_t cpp; mem_copy_fn mem_copy = NULL; /* This fastpath is restricted to specific texture types: * a 2D BGRA, RGBA, L8 or A8 texture. It could be generalized to support * more types. * * FINISHME: The restrictions below on packing alignment and packing row * length are likely unneeded now because we calculate the destination stride * with _mesa_image_row_stride. However, before removing the restrictions * we need tests. */ if (!brw->has_llc || !(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) || !(texImage->TexObject->Target == GL_TEXTURE_2D || texImage->TexObject->Target == GL_TEXTURE_RECTANGLE) || pixels == NULL || _mesa_is_bufferobj(packing->BufferObj) || packing->Alignment > 4 || packing->SkipPixels > 0 || packing->SkipRows > 0 || (packing->RowLength != 0 && packing->RowLength != width) || packing->SwapBytes || packing->LsbFirst || packing->Invert) return false; /* We can't handle copying from RGBX or BGRX because the tiled_memcpy * function doesn't set the last channel to 1. */ if (texImage->TexFormat == MESA_FORMAT_B8G8R8X8_UNORM || texImage->TexFormat == MESA_FORMAT_R8G8B8X8_UNORM) return false; if (!intel_get_memcpy(texImage->TexFormat, format, type, &mem_copy, &cpp, INTEL_DOWNLOAD)) return false; /* If this is a nontrivial texture view, let another path handle it instead. */ if (texImage->TexObject->MinLayer) return false; if (!image->mt || (image->mt->tiling != I915_TILING_X && image->mt->tiling != I915_TILING_Y)) { /* The algorithm is written only for X- or Y-tiled memory. */ return false; } /* Since we are going to write raw data to the miptree, we need to resolve * any pending fast color clears before we start. */ intel_miptree_resolve_color(brw, image->mt); bo = image->mt->bo; if (drm_intel_bo_references(brw->batch.bo, bo)) { perf_debug("Flushing before mapping a referenced bo.\n"); intel_batchbuffer_flush(brw); } error = brw_bo_map(brw, bo, false /* write enable */, "miptree"); if (error) { DBG("%s: failed to map bo\n", __func__); return false; } dst_pitch = _mesa_image_row_stride(packing, width, format, type); DBG("%s: level=%d x,y=(%d,%d) (w,h)=(%d,%d) format=0x%x type=0x%x " "mesa_format=0x%x tiling=%d " "packing=(alignment=%d row_length=%d skip_pixels=%d skip_rows=%d)\n", __func__, texImage->Level, xoffset, yoffset, width, height, format, type, texImage->TexFormat, image->mt->tiling, packing->Alignment, packing->RowLength, packing->SkipPixels, packing->SkipRows); int level = texImage->Level + texImage->TexObject->MinLevel; /* Adjust x and y offset based on miplevel */ xoffset += image->mt->level[level].level_x; yoffset += image->mt->level[level].level_y; tiled_to_linear( xoffset * cpp, (xoffset + width) * cpp, yoffset, yoffset + height, pixels - (ptrdiff_t) yoffset * dst_pitch - (ptrdiff_t) xoffset * cpp, bo->virtual, dst_pitch, image->mt->pitch, brw->has_swizzling, image->mt->tiling, mem_copy ); drm_intel_bo_unmap(bo); return true; }
/** * Called via glMapBufferRange and glMapBuffer * * The goal of this extension is to allow apps to accumulate their rendering * at the same time as they accumulate their buffer object. Without it, * you'd end up blocking on execution of rendering every time you mapped * the buffer to put new data in. * * We support it in 3 ways: If unsynchronized, then don't bother * flushing the batchbuffer before mapping the buffer, which can save blocking * in many cases. If we would still block, and they allow the whole buffer * to be invalidated, then just allocate a new buffer to replace the old one. * If not, and we'd block, and they allow the subrange of the buffer to be * invalidated, then we can make a new little BO, let them write into that, * and blit it into the real BO at unmap time. */ static void * intel_bufferobj_map_range(struct gl_context * ctx, GLintptr offset, GLsizeiptr length, GLbitfield access, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); assert(intel_obj); /* _mesa_MapBufferRange (GL entrypoint) sets these, but the vbo module also * internally uses our functions directly. */ obj->Offset = offset; obj->Length = length; obj->AccessFlags = access; if (intel_obj->sys_buffer) { const bool read_only = (access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == GL_MAP_READ_BIT; if (!read_only && intel_obj->source) release_buffer(intel_obj); if (!intel_obj->buffer || intel_obj->source) { obj->Pointer = intel_obj->sys_buffer + offset; return obj->Pointer; } free(intel_obj->sys_buffer); intel_obj->sys_buffer = NULL; } if (intel_obj->buffer == NULL) { obj->Pointer = NULL; return NULL; } /* If the access is synchronized (like a normal buffer mapping), then get * things flushed out so the later mapping syncs appropriately through GEM. * If the user doesn't care about existing buffer contents and mapping would * cause us to block, then throw out the old buffer. * * If they set INVALIDATE_BUFFER, we can pitch the current contents to * achieve the required synchronization. */ if (!(access & GL_MAP_UNSYNCHRONIZED_BIT)) { if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) { if (access & GL_MAP_INVALIDATE_BUFFER_BIT) { drm_intel_bo_unreference(intel_obj->buffer); intel_bufferobj_alloc_buffer(intel, intel_obj); } else { perf_debug("Stalling on the GPU for mapping a busy buffer " "object\n"); intel_flush(ctx); } } else if (drm_intel_bo_busy(intel_obj->buffer) && (access & GL_MAP_INVALIDATE_BUFFER_BIT)) { drm_intel_bo_unreference(intel_obj->buffer); intel_bufferobj_alloc_buffer(intel, intel_obj); } } /* If the user is mapping a range of an active buffer object but * doesn't require the current contents of that range, make a new * BO, and we'll copy what they put in there out at unmap or * FlushRange time. */ if ((access & GL_MAP_INVALIDATE_RANGE_BIT) && drm_intel_bo_busy(intel_obj->buffer)) { if (access & GL_MAP_FLUSH_EXPLICIT_BIT) { intel_obj->range_map_buffer = malloc(length); obj->Pointer = intel_obj->range_map_buffer; } else { intel_obj->range_map_bo = drm_intel_bo_alloc(intel->bufmgr, "range map", length, 64); if (!(access & GL_MAP_READ_BIT)) { drm_intel_gem_bo_map_gtt(intel_obj->range_map_bo); } else { drm_intel_bo_map(intel_obj->range_map_bo, (access & GL_MAP_WRITE_BIT) != 0); } obj->Pointer = intel_obj->range_map_bo->virtual; } return obj->Pointer; } if (access & GL_MAP_UNSYNCHRONIZED_BIT) drm_intel_gem_bo_map_unsynchronized(intel_obj->buffer); else if (!(access & GL_MAP_READ_BIT)) { drm_intel_gem_bo_map_gtt(intel_obj->buffer); } else { drm_intel_bo_map(intel_obj->buffer, (access & GL_MAP_WRITE_BIT) != 0); } obj->Pointer = intel_obj->buffer->virtual + offset; return obj->Pointer; }
if (brw->has_llc) { if (offset + size <= intel_obj->gpu_active_start || intel_obj->gpu_active_end <= offset) { drm_intel_gem_bo_map_unsynchronized(intel_obj->buffer); memcpy(intel_obj->buffer->virtual + offset, data, size); drm_intel_bo_unmap(intel_obj->buffer); if (intel_obj->gpu_active_end > intel_obj->gpu_active_start) intel_obj->prefer_stall_to_blit = true; return; } } busy = drm_intel_bo_busy(intel_obj->buffer) || drm_intel_bo_references(brw->batch.bo, intel_obj->buffer); if (busy) { if (size == intel_obj->Base.Size) { /* Replace the current busy bo so the subdata doesn't stall. */ drm_intel_bo_unreference(intel_obj->buffer); intel_bufferobj_alloc_buffer(brw, intel_obj); } else if (!intel_obj->prefer_stall_to_blit) { perf_debug("Using a blit copy to avoid stalling on " "glBufferSubData(%ld, %ld) (%ldkb) to a busy " "(%d-%d) buffer object.\n", (long)offset, (long)offset + size, (long)(size/1024), intel_obj->gpu_active_start, intel_obj->gpu_active_end); drm_intel_bo *temp_bo = drm_intel_bo_alloc(brw->bufmgr, "subdata temp", size, 64);
/** * Replace data in a subrange of buffer object. If the data range * specified by size + offset extends beyond the end of the buffer or * if data is NULL, no copy is performed. * Called via glBufferSubDataARB(). */ static void intel_bufferobj_subdata(struct gl_context * ctx, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data, struct gl_buffer_object *obj) { struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *intel_obj = intel_buffer_object(obj); bool busy; if (size == 0) return; assert(intel_obj); /* If we have a single copy in system memory, update that */ if (intel_obj->sys_buffer) { if (intel_obj->source) release_buffer(intel_obj); if (intel_obj->buffer == NULL) { memcpy((char *)intel_obj->sys_buffer + offset, data, size); return; } free(intel_obj->sys_buffer); intel_obj->sys_buffer = NULL; } /* Otherwise we need to update the copy in video memory. */ busy = drm_intel_bo_busy(intel_obj->buffer) || drm_intel_bo_references(intel->batch.bo, intel_obj->buffer); /* replace the current busy bo with fresh data */ if (busy && size == intel_obj->Base.Size) { drm_intel_bo_unreference(intel_obj->buffer); intel_bufferobj_alloc_buffer(intel, intel_obj); drm_intel_bo_subdata(intel_obj->buffer, 0, size, data); } else if (intel->gen < 6) { if (busy) { drm_intel_bo *temp_bo; temp_bo = drm_intel_bo_alloc(intel->bufmgr, "subdata temp", size, 64); drm_intel_bo_subdata(temp_bo, 0, size, data); intel_emit_linear_blit(intel, intel_obj->buffer, offset, temp_bo, 0, size); drm_intel_bo_unreference(temp_bo); } else { drm_intel_bo_subdata(intel_obj->buffer, offset, size, data); } } else { /* Can't use the blit to modify the buffer in the middle of batch. */ if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) { intel_batchbuffer_flush(intel); } drm_intel_bo_subdata(intel_obj->buffer, offset, size, data); } }
/** * \brief A fast path for glReadPixels * * This fast path is taken when the source format is BGRA, RGBA, * A or L and when the texture memory is X- or Y-tiled. It downloads * the source data by directly mapping the memory without a GTT fence. * This then needs to be de-tiled on the CPU before presenting the data to * the user in the linear fasion. * * This is a performance win over the conventional texture download path. * In the conventional texture download path, the texture is either mapped * through the GTT or copied to a linear buffer with the blitter before * handing off to a software path. This allows us to avoid round-tripping * through the GPU (in the case where we would be blitting) and do only a * single copy operation. */ static bool intel_readpixels_tiled_memcpy(struct gl_context * ctx, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels, const struct gl_pixelstore_attrib *pack) { struct brw_context *brw = brw_context(ctx); struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; /* This path supports reading from color buffers only */ if (rb == NULL) return false; struct intel_renderbuffer *irb = intel_renderbuffer(rb); int dst_pitch; /* The miptree's buffer. */ drm_intel_bo *bo; int error = 0; uint32_t cpp; mem_copy_fn mem_copy = NULL; /* This fastpath is restricted to specific renderbuffer types: * a 2D BGRA, RGBA, L8 or A8 texture. It could be generalized to support * more types. */ if (!brw->has_llc || !(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) || pixels == NULL || _mesa_is_bufferobj(pack->BufferObj) || pack->Alignment > 4 || pack->SkipPixels > 0 || pack->SkipRows > 0 || (pack->RowLength != 0 && pack->RowLength != width) || pack->SwapBytes || pack->LsbFirst || pack->Invert) return false; /* Only a simple blit, no scale, bias or other mapping. */ if (ctx->_ImageTransferState) return false; /* It is possible that the renderbuffer (or underlying texture) is * multisampled. Since ReadPixels from a multisampled buffer requires a * multisample resolve, we can't handle this here */ if (rb->NumSamples > 1) return false; /* We can't handle copying from RGBX or BGRX because the tiled_memcpy * function doesn't set the last channel to 1. Note this checks BaseFormat * rather than TexFormat in case the RGBX format is being simulated with an * RGBA format. */ if (rb->_BaseFormat == GL_RGB) return false; if (!intel_get_memcpy(rb->Format, format, type, &mem_copy, &cpp)) return false; if (!irb->mt || (irb->mt->tiling != I915_TILING_X && irb->mt->tiling != I915_TILING_Y)) { /* The algorithm is written only for X- or Y-tiled memory. */ return false; } /* Since we are going to read raw data to the miptree, we need to resolve * any pending fast color clears before we start. */ intel_miptree_all_slices_resolve_color(brw, irb->mt, 0); bo = irb->mt->bo; if (drm_intel_bo_references(brw->batch.bo, bo)) { perf_debug("Flushing before mapping a referenced bo.\n"); intel_batchbuffer_flush(brw); } error = brw_bo_map(brw, bo, false /* write enable */, "miptree"); if (error) { DBG("%s: failed to map bo\n", __func__); return false; } xoffset += irb->mt->level[irb->mt_level].slice[irb->mt_layer].x_offset; yoffset += irb->mt->level[irb->mt_level].slice[irb->mt_layer].y_offset; dst_pitch = _mesa_image_row_stride(pack, width, format, type); /* For a window-system renderbuffer, the buffer is actually flipped * vertically, so we need to handle that. Since the detiling function * can only really work in the forwards direction, we have to be a * little creative. First, we compute the Y-offset of the first row of * the renderbuffer (in renderbuffer coordinates). We then match that * with the last row of the client's data. Finally, we give * tiled_to_linear a negative pitch so that it walks through the * client's data backwards as it walks through the renderbufer forwards. */ if (rb->Name == 0) { yoffset = rb->Height - yoffset - height; pixels += (ptrdiff_t) (height - 1) * dst_pitch; dst_pitch = -dst_pitch; } /* We postponed printing this message until having committed to executing * the function. */ DBG("%s: x,y=(%d,%d) (w,h)=(%d,%d) format=0x%x type=0x%x " "mesa_format=0x%x tiling=%d " "pack=(alignment=%d row_length=%d skip_pixels=%d skip_rows=%d)\n", __func__, xoffset, yoffset, width, height, format, type, rb->Format, irb->mt->tiling, pack->Alignment, pack->RowLength, pack->SkipPixels, pack->SkipRows); tiled_to_linear( xoffset * cpp, (xoffset + width) * cpp, yoffset, yoffset + height, pixels - (ptrdiff_t) yoffset * dst_pitch - (ptrdiff_t) xoffset * cpp, bo->virtual + irb->mt->offset, dst_pitch, irb->mt->pitch, brw->has_swizzling, irb->mt->tiling, mem_copy ); drm_intel_bo_unmap(bo); return true; }