blob: f261034c18f6d95ea5b1ad4dab58b01088b54b92 [file] [log] [blame]
#include <stdlib.h>
#include <stdio.h>
#include "glheader.h"
#include "macros.h"
#include "mtypes.h"
#include "enums.h"
#include "colortab.h"
#include "convolve.h"
#include "context.h"
#include "simple_list.h"
#include "texcompress.h"
#include "texformat.h"
#include "texobj.h"
#include "texstore.h"
#include "teximage.h"
#include "intel_context.h"
#include "intel_mipmap_tree.h"
#include "intel_buffer_objects.h"
#include "intel_batchbuffer.h"
#include "intel_tex.h"
#include "intel_ioctl.h"
#include "intel_blit.h"
#include "intel_fbo.h"
#define FILE_DEBUG_FLAG DEBUG_TEXTURE
/* Functions to store texture images. Where possible, mipmap_tree's
* will be created or further instantiated with image data, otherwise
* images will be stored in malloc'd memory. A validation step is
* required to pull those images into a mipmap tree, or otherwise
* decide a fallback is required.
*/
static int
logbase2(int n)
{
GLint i = 1;
GLint log2 = 0;
while (n > i) {
i *= 2;
log2++;
}
return log2;
}
/* Otherwise, store it in memory if (Border != 0) or (any dimension ==
* 1).
*
* Otherwise, if max_level >= level >= min_level, create tree with
* space for textures from min_level down to max_level.
*
* Otherwise, create tree with space for textures from (level
* 0)..(1x1). Consider pruning this tree at a validation if the
* saving is worth it.
*/
static void
guess_and_alloc_mipmap_tree(struct intel_context *intel,
struct intel_texture_object *intelObj,
struct intel_texture_image *intelImage)
{
GLuint firstLevel;
GLuint lastLevel;
GLuint width = intelImage->base.Width;
GLuint height = intelImage->base.Height;
GLuint depth = intelImage->base.Depth;
GLuint l2width, l2height, l2depth;
GLuint i, comp_byte = 0;
DBG("%s\n", __FUNCTION__);
if (intelImage->base.Border ||
((intelImage->base._BaseFormat == GL_DEPTH_COMPONENT) &&
((intelObj->base.WrapS == GL_CLAMP_TO_BORDER) ||
(intelObj->base.WrapT == GL_CLAMP_TO_BORDER))))
return;
if (intelImage->level > intelObj->base.BaseLevel &&
(intelImage->base.Width == 1 ||
(intelObj->base.Target != GL_TEXTURE_1D &&
intelImage->base.Height == 1) ||
(intelObj->base.Target == GL_TEXTURE_3D &&
intelImage->base.Depth == 1)))
return;
/* If this image disrespects BaseLevel, allocate from level zero.
* Usually BaseLevel == 0, so it's unlikely to happen.
*/
if (intelImage->level < intelObj->base.BaseLevel)
firstLevel = 0;
else
firstLevel = intelObj->base.BaseLevel;
/* Figure out image dimensions at start level.
*/
for (i = intelImage->level; i > firstLevel; i--) {
width <<= 1;
if (height != 1)
height <<= 1;
if (depth != 1)
depth <<= 1;
}
/* Guess a reasonable value for lastLevel. This is probably going
* to be wrong fairly often and might mean that we have to look at
* resizable buffers, or require that buffers implement lazy
* pagetable arrangements.
*/
if ((intelObj->base.MinFilter == GL_NEAREST ||
intelObj->base.MinFilter == GL_LINEAR) &&
intelImage->level == firstLevel) {
lastLevel = firstLevel;
}
else {
l2width = logbase2(width);
l2height = logbase2(height);
l2depth = logbase2(depth);
lastLevel = firstLevel + MAX2(MAX2(l2width, l2height), l2depth);
}
assert(!intelObj->mt);
if (intelImage->base.IsCompressed)
comp_byte = intel_compressed_num_bytes(intelImage->base.TexFormat->MesaFormat);
intelObj->mt = intel_miptree_create(intel,
intelObj->base.Target,
intelImage->base.InternalFormat,
firstLevel,
lastLevel,
width,
height,
depth,
intelImage->base.TexFormat->TexelBytes,
comp_byte);
DBG("%s - success\n", __FUNCTION__);
}
static GLuint
target_to_face(GLenum target)
{
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
return ((GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X);
default:
return 0;
}
}
/* There are actually quite a few combinations this will work for,
* more than what I've listed here.
*/
static GLboolean
check_pbo_format(GLint internalFormat,
GLenum format, GLenum type,
const struct gl_texture_format *mesa_format)
{
switch (internalFormat) {
case 4:
case GL_RGBA:
return (format == GL_BGRA &&
(type == GL_UNSIGNED_BYTE ||
type == GL_UNSIGNED_INT_8_8_8_8_REV) &&
mesa_format == &_mesa_texformat_argb8888);
case 3:
case GL_RGB:
return (format == GL_RGB &&
type == GL_UNSIGNED_SHORT_5_6_5 &&
mesa_format == &_mesa_texformat_rgb565);
case GL_YCBCR_MESA:
return (type == GL_UNSIGNED_SHORT_8_8_MESA || type == GL_UNSIGNED_BYTE);
default:
return GL_FALSE;
}
}
/* XXX: Do this for TexSubImage also:
*/
static GLboolean
try_pbo_upload(struct intel_context *intel,
struct intel_texture_image *intelImage,
const struct gl_pixelstore_attrib *unpack,
GLint internalFormat,
GLint width, GLint height,
GLenum format, GLenum type, const void *pixels)
{
struct intel_buffer_object *pbo = intel_buffer_object(unpack->BufferObj);
GLuint src_offset, src_stride;
GLuint dst_offset, dst_stride;
if (!pbo ||
intel->ctx._ImageTransferState ||
unpack->SkipPixels || unpack->SkipRows) {
_mesa_printf("%s: failure 1\n", __FUNCTION__);
return GL_FALSE;
}
src_offset = (GLuint) pixels;
if (unpack->RowLength > 0)
src_stride = unpack->RowLength;
else
src_stride = width;
dst_offset = intel_miptree_image_offset(intelImage->mt,
intelImage->face,
intelImage->level);
dst_stride = intelImage->mt->pitch;
intelFlush(&intel->ctx);
LOCK_HARDWARE(intel);
{
dri_bo *src_buffer = intel_bufferobj_buffer(intel, pbo, INTEL_READ);
dri_bo *dst_buffer = intel_region_buffer(intel,
intelImage->mt->region,
INTEL_WRITE_FULL);
intelEmitCopyBlit(intel,
intelImage->mt->cpp,
src_stride, src_buffer, src_offset, GL_FALSE,
dst_stride, dst_buffer, dst_offset, GL_FALSE,
0, 0, 0, 0, width, height,
GL_COPY);
intel_batchbuffer_flush(intel->batch);
}
UNLOCK_HARDWARE(intel);
return GL_TRUE;
}
static GLboolean
try_pbo_zcopy(struct intel_context *intel,
struct intel_texture_image *intelImage,
const struct gl_pixelstore_attrib *unpack,
GLint internalFormat,
GLint width, GLint height,
GLenum format, GLenum type, const void *pixels)
{
struct intel_buffer_object *pbo = intel_buffer_object(unpack->BufferObj);
GLuint src_offset, src_stride;
GLuint dst_offset, dst_stride;
if (!pbo ||
intel->ctx._ImageTransferState ||
unpack->SkipPixels || unpack->SkipRows) {
_mesa_printf("%s: failure 1\n", __FUNCTION__);
return GL_FALSE;
}
src_offset = (GLuint) pixels;
if (unpack->RowLength > 0)
src_stride = unpack->RowLength;
else
src_stride = width;
dst_offset = intel_miptree_image_offset(intelImage->mt,
intelImage->face,
intelImage->level);
dst_stride = intelImage->mt->pitch;
if (src_stride != dst_stride || dst_offset != 0 || src_offset != 0) {
_mesa_printf("%s: failure 2\n", __FUNCTION__);
return GL_FALSE;
}
intel_region_attach_pbo(intel, intelImage->mt->region, pbo);
return GL_TRUE;
}
static void
intelTexImage(GLcontext * ctx,
GLint dims,
GLenum target, GLint level,
GLint internalFormat,
GLint width, GLint height, GLint depth,
GLint border,
GLenum format, GLenum type, const void *pixels,
const struct gl_pixelstore_attrib *unpack,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage, GLsizei imageSize, int compressed)
{
struct intel_context *intel = intel_context(ctx);
struct intel_texture_object *intelObj = intel_texture_object(texObj);
struct intel_texture_image *intelImage = intel_texture_image(texImage);
GLint postConvWidth = width;
GLint postConvHeight = height;
GLint texelBytes, sizeInBytes;
GLuint dstRowStride, srcRowStride = texImage->RowStride;
DBG("%s target %s level %d %dx%dx%d border %d\n", __FUNCTION__,
_mesa_lookup_enum_by_nr(target), level, width, height, depth, border);
intelFlush(ctx);
intelImage->face = target_to_face(target);
intelImage->level = level;
if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) {
_mesa_adjust_image_for_convolution(ctx, dims, &postConvWidth,
&postConvHeight);
}
/* choose the texture format */
texImage->TexFormat = intelChooseTextureFormat(ctx, internalFormat,
format, type);
_mesa_set_fetch_functions(texImage, dims);
if (texImage->TexFormat->TexelBytes == 0) {
/* must be a compressed format */
texelBytes = 0;
texImage->IsCompressed = GL_TRUE;
texImage->CompressedSize =
ctx->Driver.CompressedTextureSize(ctx, texImage->Width,
texImage->Height, texImage->Depth,
texImage->TexFormat->MesaFormat);
} else {
texelBytes = texImage->TexFormat->TexelBytes;
/* Minimum pitch of 32 bytes */
if (postConvWidth * texelBytes < 32) {
postConvWidth = 32 / texelBytes;
texImage->RowStride = postConvWidth;
}
if (!intelImage->mt) {
assert(texImage->RowStride == postConvWidth);
}
}
/* Release the reference to a potentially orphaned buffer.
* Release any old malloced memory.
*/
if (intelImage->mt) {
intel_miptree_release(intel, &intelImage->mt);
assert(!texImage->Data);
}
else if (texImage->Data) {
_mesa_free_texmemory(texImage->Data);
texImage->Data = NULL;
}
/* If this is the only texture image in the tree, could call
* bmBufferData with NULL data to free the old block and avoid
* waiting on any outstanding fences.
*/
if (intelObj->mt &&
intelObj->mt->first_level == level &&
intelObj->mt->last_level == level &&
intelObj->mt->target != GL_TEXTURE_CUBE_MAP_ARB &&
!intel_miptree_match_image(intelObj->mt, &intelImage->base,
intelImage->face, intelImage->level)) {
DBG("release it\n");
intel_miptree_release(intel, &intelObj->mt);
assert(!intelObj->mt);
}
if (!intelObj->mt) {
guess_and_alloc_mipmap_tree(intel, intelObj, intelImage);
if (!intelObj->mt) {
DBG("guess_and_alloc_mipmap_tree: failed\n");
}
}
assert(!intelImage->mt);
if (intelObj->mt &&
intel_miptree_match_image(intelObj->mt, &intelImage->base,
intelImage->face, intelImage->level)) {
intel_miptree_reference(&intelImage->mt, intelObj->mt);
assert(intelImage->mt);
}
if (!intelImage->mt)
DBG("XXX: Image did not fit into tree - storing in local memory!\n");
/* PBO fastpaths:
*/
if (dims <= 2 &&
intelImage->mt &&
intel_buffer_object(unpack->BufferObj) &&
check_pbo_format(internalFormat, format,
type, intelImage->base.TexFormat)) {
DBG("trying pbo upload\n");
/* Attempt to texture directly from PBO data (zero copy upload).
*
* Currently disable as it can lead to worse as well as better
* performance (in particular when intel_region_cow() is
* required).
*/
if (intelObj->mt == intelImage->mt &&
intelObj->mt->first_level == level &&
intelObj->mt->last_level == level) {
if (try_pbo_zcopy(intel, intelImage, unpack,
internalFormat,
width, height, format, type, pixels)) {
DBG("pbo zcopy upload succeeded\n");
return;
}
}
/* Otherwise, attempt to use the blitter for PBO image uploads.
*/
if (try_pbo_upload(intel, intelImage, unpack,
internalFormat,
width, height, format, type, pixels)) {
DBG("pbo upload succeeded\n");
return;
}
DBG("pbo upload failed\n");
}
/* intelCopyTexImage calls this function with pixels == NULL, with
* the expectation that the mipmap tree will be set up but nothing
* more will be done. This is where those calls return:
*/
if (compressed) {
pixels = _mesa_validate_pbo_compressed_teximage(ctx, imageSize, pixels,
unpack,
"glCompressedTexImage");
} else {
pixels = _mesa_validate_pbo_teximage(ctx, dims, width, height, 1,
format, type,
pixels, unpack, "glTexImage");
}
LOCK_HARDWARE(intel);
if (intelImage->mt) {
texImage->Data = intel_miptree_image_map(intel,
intelImage->mt,
intelImage->face,
intelImage->level,
&dstRowStride,
intelImage->base.ImageOffsets);
texImage->RowStride = dstRowStride / intelImage->mt->cpp;
}
else {
/* Allocate regular memory and store the image there temporarily. */
if (texImage->IsCompressed) {
sizeInBytes = texImage->CompressedSize;
dstRowStride =
_mesa_compressed_row_stride(texImage->TexFormat->MesaFormat, width);
assert(dims != 3);
}
else {
dstRowStride = postConvWidth * texelBytes;
sizeInBytes = depth * dstRowStride * postConvHeight;
}
texImage->Data = _mesa_alloc_texmemory(sizeInBytes);
}
DBG("Upload image %dx%dx%d row_len %d "
"pitch %d\n",
width, height, depth, width * texelBytes, dstRowStride);
/* Copy data. Would like to know when it's ok for us to eg. use
* the blitter to copy. Or, use the hardware to do the format
* conversion and copy:
*/
if (pixels) {
if (compressed) {
if (intelImage->mt) {
struct intel_region *dst = intelImage->mt->region;
_mesa_copy_rect(texImage->Data, dst->cpp, dst->pitch,
0, 0,
intelImage->mt->level[level].width,
intelImage->mt->level[level].height/4,
pixels,
srcRowStride,
0, 0);
} else
memcpy(texImage->Data, pixels, imageSize);
} else if (!texImage->TexFormat->StoreImage(ctx, dims,
texImage->_BaseFormat,
texImage->TexFormat,
texImage->Data, 0, 0, 0, /* dstX/Y/Zoffset */
dstRowStride,
texImage->ImageOffsets,
width, height, depth,
format, type, pixels, unpack)) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage");
}
}
/* GL_SGIS_generate_mipmap */
if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
intel_generate_mipmap(ctx, target, texObj);
}
_mesa_unmap_teximage_pbo(ctx, unpack);
if (intelImage->mt) {
intel_miptree_image_unmap(intel, intelImage->mt);
texImage->Data = NULL;
}
UNLOCK_HARDWARE(intel);
}
void
intelTexImage3D(GLcontext * ctx,
GLenum target, GLint level,
GLint internalFormat,
GLint width, GLint height, GLint depth,
GLint border,
GLenum format, GLenum type, const void *pixels,
const struct gl_pixelstore_attrib *unpack,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
intelTexImage(ctx, 3, target, level,
internalFormat, width, height, depth, border,
format, type, pixels, unpack, texObj, texImage, 0, 0);
}
void
intelTexImage2D(GLcontext * ctx,
GLenum target, GLint level,
GLint internalFormat,
GLint width, GLint height, GLint border,
GLenum format, GLenum type, const void *pixels,
const struct gl_pixelstore_attrib *unpack,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
intelTexImage(ctx, 2, target, level,
internalFormat, width, height, 1, border,
format, type, pixels, unpack, texObj, texImage, 0, 0);
}
void
intelTexImage1D(GLcontext * ctx,
GLenum target, GLint level,
GLint internalFormat,
GLint width, GLint border,
GLenum format, GLenum type, const void *pixels,
const struct gl_pixelstore_attrib *unpack,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
intelTexImage(ctx, 1, target, level,
internalFormat, width, 1, 1, border,
format, type, pixels, unpack, texObj, texImage, 0, 0);
}
void intelCompressedTexImage2D( GLcontext *ctx, GLenum target, GLint level,
GLint internalFormat,
GLint width, GLint height, GLint border,
GLsizei imageSize, const GLvoid *data,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage )
{
intelTexImage(ctx, 2, target, level,
internalFormat, width, height, 1, border,
0, 0, data, &ctx->Unpack, texObj, texImage, imageSize, 1);
}
/**
* Need to map texture image into memory before copying image data,
* then unmap it.
*/
static void
intel_get_tex_image(GLcontext * ctx, GLenum target, GLint level,
GLenum format, GLenum type, GLvoid * pixels,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage, int compressed)
{
struct intel_context *intel = intel_context(ctx);
struct intel_texture_image *intelImage = intel_texture_image(texImage);
/* Map */
if (intelImage->mt) {
/* Image is stored in hardware format in a buffer managed by the
* kernel. Need to explicitly map and unmap it.
*/
intelImage->base.Data =
intel_miptree_image_map(intel,
intelImage->mt,
intelImage->face,
intelImage->level,
&intelImage->base.RowStride,
intelImage->base.ImageOffsets);
intelImage->base.RowStride /= intelImage->mt->cpp;
}
else {
/* Otherwise, the image should actually be stored in
* intelImage->base.Data. This is pretty confusing for
* everybody, I'd much prefer to separate the two functions of
* texImage->Data - storage for texture images in main memory
* and access (ie mappings) of images. In other words, we'd
* create a new texImage->Map field and leave Data simply for
* storage.
*/
assert(intelImage->base.Data);
}
if (compressed) {
_mesa_get_compressed_teximage(ctx, target, level, pixels,
texObj, texImage);
} else {
_mesa_get_teximage(ctx, target, level, format, type, pixels,
texObj, texImage);
}
/* Unmap */
if (intelImage->mt) {
intel_miptree_image_unmap(intel, intelImage->mt);
intelImage->base.Data = NULL;
}
}
void
intelGetTexImage(GLcontext * ctx, GLenum target, GLint level,
GLenum format, GLenum type, GLvoid * pixels,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
intel_get_tex_image(ctx, target, level, format, type, pixels,
texObj, texImage, 0);
}
void
intelGetCompressedTexImage(GLcontext *ctx, GLenum target, GLint level,
GLvoid *pixels,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
intel_get_tex_image(ctx, target, level, 0, 0, pixels,
texObj, texImage, 1);
}
void
intelSetTexOffset(__DRIcontext *pDRICtx, GLint texname,
unsigned long long offset, GLint depth, GLuint pitch)
{
struct intel_context *intel = pDRICtx->driverPrivate;
struct gl_texture_object *tObj = _mesa_lookup_texture(&intel->ctx, texname);
struct intel_texture_object *intelObj = intel_texture_object(tObj);
if (!intelObj)
return;
if (intelObj->mt)
intel_miptree_release(intel, &intelObj->mt);
intelObj->imageOverride = GL_TRUE;
intelObj->depthOverride = depth;
intelObj->pitchOverride = pitch;
if (offset)
intelObj->textureOffset = offset;
}
void
intelSetTexBuffer(__DRIcontext *pDRICtx, GLint target, __DRIdrawable *dPriv)
{
struct intel_framebuffer *intel_fb = dPriv->driverPrivate;
struct intel_context *intel = pDRICtx->driverPrivate;
struct intel_texture_object *intelObj;
struct intel_texture_image *intelImage;
struct intel_mipmap_tree *mt;
struct intel_renderbuffer *rb;
struct gl_texture_unit *texUnit;
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
int level = 0, type, format, internalFormat;
texUnit = &intel->ctx.Texture.Unit[intel->ctx.Texture.CurrentUnit];
texObj = _mesa_select_tex_object(&intel->ctx, texUnit, target);
intelObj = intel_texture_object(texObj);
if (!intelObj)
return;
__driParseEvents(pDRICtx, dPriv);
rb = intel_fb->color_rb[0];
type = GL_BGRA;
format = GL_UNSIGNED_BYTE;
internalFormat = (rb->region->cpp == 3 ? 3 : 4);
mt = intel_miptree_create_for_region(intel, target,
internalFormat,
0, 0, rb->region, 1, 0);
if (mt == NULL)
return;
_mesa_lock_texture(&intel->ctx, texObj);
if (intelObj->mt)
intel_miptree_release(intel, &intelObj->mt);
intelObj->mt = mt;
texImage = _mesa_get_tex_image(&intel->ctx, texObj, target, level);
_mesa_init_teximage_fields(&intel->ctx, target, texImage,
rb->region->pitch, rb->region->height, 1,
0, internalFormat);
intelImage = intel_texture_image(texImage);
intelImage->face = target_to_face(target);
intelImage->level = level;
texImage->TexFormat = intelChooseTextureFormat(&intel->ctx, internalFormat,
type, format);
_mesa_set_fetch_functions(texImage, 2);
texImage->RowStride = rb->region->pitch;
intel_miptree_reference(&intelImage->mt, intelObj->mt);
if (!intel_miptree_match_image(intelObj->mt, &intelImage->base,
intelImage->face, intelImage->level)) {
fprintf(stderr, "miptree doesn't match image\n");
}
_mesa_unlock_texture(&intel->ctx, texObj);
}