Exemple #1
0
/**
 * Undo the pairing/interleaving between depth and stencil buffers.
 * irb should be a depth/stencil or stencil renderbuffer.
 */
void
intel_unpair_depth_stencil(GLcontext * ctx, struct intel_renderbuffer *irb)
{
   if (irb->PairedStencil) {
      /* irb is a depth/stencil buffer */
      struct gl_renderbuffer *stencilRb;
      struct intel_renderbuffer *stencilIrb;

      ASSERT(irb->Base._ActualFormat == GL_DEPTH24_STENCIL8_EXT);

      stencilRb = _mesa_lookup_renderbuffer(ctx, irb->PairedStencil);
      stencilIrb = intel_renderbuffer(stencilRb);
      if (stencilIrb) {
         /* need to extract stencil values from the depth buffer */
         ASSERT(stencilIrb->PairedDepth == irb->Base.Name);
         map_regions(ctx, irb, stencilIrb);
         _mesa_extract_stencil(ctx, &irb->Base, &stencilIrb->Base);
         unmap_regions(ctx, irb, stencilIrb);
         stencilIrb->PairedDepth = 0;
      }
      irb->PairedStencil = 0;
   }
   else if (irb->PairedDepth) {
      /* irb is a stencil buffer */
      struct gl_renderbuffer *depthRb;
      struct intel_renderbuffer *depthIrb;

      ASSERT(irb->Base._ActualFormat == GL_STENCIL_INDEX8_EXT ||
             irb->Base._ActualFormat == GL_DEPTH24_STENCIL8_EXT);

      depthRb = _mesa_lookup_renderbuffer(ctx, irb->PairedDepth);
      depthIrb = intel_renderbuffer(depthRb);
      if (depthIrb) {
         /* need to extract stencil values from the depth buffer */
         ASSERT(depthIrb->PairedStencil == irb->Base.Name);
         map_regions(ctx, depthIrb, irb);
         _mesa_extract_stencil(ctx, &depthIrb->Base, &irb->Base);
         unmap_regions(ctx, depthIrb, irb);
         depthIrb->PairedStencil = 0;
      }
      irb->PairedDepth = 0;
   }
   else {
      _mesa_problem(ctx, "Problem in undo_depth_stencil_pairing");
   }

   ASSERT(irb->PairedStencil == 0);
   ASSERT(irb->PairedDepth == 0);
}
Exemple #2
0
static __DRIimage *
intel_create_image_from_renderbuffer(__DRIcontext *context,
				     int renderbuffer, void *loaderPrivate)
{
   __DRIimage *image;
   struct intel_context *intel = context->driverPrivate;
   struct gl_renderbuffer *rb;
   struct intel_renderbuffer *irb;

   rb = _mesa_lookup_renderbuffer(&intel->ctx, renderbuffer);
   if (!rb) {
      _mesa_error(&intel->ctx,
		  GL_INVALID_OPERATION, "glRenderbufferExternalMESA");
      return NULL;
   }

   irb = intel_renderbuffer(rb);
   image = calloc(1, sizeof *image);
   if (image == NULL)
      return NULL;

   image->internal_format = rb->InternalFormat;
   image->format = rb->Format;
   image->offset = 0;
   image->data = loaderPrivate;
   intel_region_reference(&image->region, irb->mt->region);
   intel_setup_image_from_dimensions(image);
   image->dri_format = intel_dri_format(image->format);

   rb->NeedsFinishRenderTexture = true;
   return image;
}
Exemple #3
0
static __DRIimage *
radeon_create_image_from_renderbuffer(__DRIcontext *context,
                                      int renderbuffer, void *loaderPrivate)
{
   __DRIimage *image;
   radeonContextPtr radeon = context->driverPrivate;
   struct gl_renderbuffer *rb;
   struct radeon_renderbuffer *rrb;

   rb = _mesa_lookup_renderbuffer(&radeon->glCtx, renderbuffer);
   if (!rb) {
      _mesa_error(&radeon->glCtx,
                  GL_INVALID_OPERATION, "glRenderbufferExternalMESA");
      return NULL;
   }

   rrb = radeon_renderbuffer(rb);
   image = calloc(1, sizeof *image);
   if (image == NULL)
      return NULL;

   image->internal_format = rb->InternalFormat;
   image->format = rb->Format;
   image->cpp = rrb->cpp;
   image->data_type = GL_UNSIGNED_BYTE;
   image->data = loaderPrivate;
   radeon_bo_ref(rrb->bo);
   image->bo = rrb->bo;

   image->width = rb->Width;
   image->height = rb->Height;
   image->pitch = rrb->pitch / image->cpp;

   return image;
}
Exemple #4
0
void GLAPIENTRY
_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
{
   GLint i;
   GET_CURRENT_CONTEXT(ctx);

   ASSERT_OUTSIDE_BEGIN_END(ctx);
   FLUSH_VERTICES(ctx, _NEW_BUFFERS);

   for (i = 0; i < n; i++) {
      if (renderbuffers[i] > 0) {
	 struct gl_renderbuffer *rb;
	 rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]);
	 if (rb) {
            /* check if deleting currently bound renderbuffer object */
            if (rb == ctx->CurrentRenderbuffer) {
               /* bind default */
               ASSERT(rb->RefCount >= 2);
               _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
            }

	    /* Remove from hash table immediately, to free the ID.
             * But the object will not be freed until it's no longer
             * referenced anywhere else.
             */
	    _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);

            if (rb != &DummyRenderbuffer) {
               /* no longer referenced by hash table */
               _mesa_reference_renderbuffer(&rb, NULL);
	    }
	 }
      }
   }
}
Exemple #5
0
GLboolean GLAPIENTRY
_mesa_IsRenderbufferEXT(GLuint renderbuffer)
{
   GET_CURRENT_CONTEXT(ctx);
   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
   if (renderbuffer) {
      struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
      if (rb != NULL && rb != &DummyRenderbuffer)
         return GL_TRUE;
   }
   return GL_FALSE;
}
static __DRIimage *
intel_create_image_from_renderbuffer(__DRIcontext *context,
				     int renderbuffer, void *loaderPrivate)
{
   __DRIimage *image;
   struct intel_context *intel = context->driverPrivate;
   struct gl_renderbuffer *rb;
   struct intel_renderbuffer *irb;

   rb = _mesa_lookup_renderbuffer(&intel->ctx, renderbuffer);
   if (!rb) {
      _mesa_error(&intel->ctx,
		  GL_INVALID_OPERATION, "glRenderbufferExternalMESA");
      return NULL;
   }

   irb = intel_renderbuffer(rb);
   image = calloc(1, sizeof *image);
   if (image == NULL)
      return NULL;

   image->internal_format = rb->InternalFormat;
   image->format = rb->Format;
   image->offset = 0;
   image->data = loaderPrivate;
   intel_region_reference(&image->region, irb->mt->region);

   switch (image->format) {
   case MESA_FORMAT_RGB565:
      image->dri_format = __DRI_IMAGE_FORMAT_RGB565;
      break;
   case MESA_FORMAT_XRGB8888:
      image->dri_format = __DRI_IMAGE_FORMAT_XRGB8888;
      break;
   case MESA_FORMAT_ARGB8888:
      image->dri_format = __DRI_IMAGE_FORMAT_ARGB8888;
      break;
   case MESA_FORMAT_RGBA8888_REV:
      image->dri_format = __DRI_IMAGE_FORMAT_ABGR8888;
      break;
   case MESA_FORMAT_R8:
      image->dri_format = __DRI_IMAGE_FORMAT_R8;
      break;
   case MESA_FORMAT_RG88:
      image->dri_format = __DRI_IMAGE_FORMAT_GR88;
      break;
   }

   rb->NeedsFinishRenderTexture = true;
   return image;
}
Exemple #7
0
void GLAPIENTRY
_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
{
   struct gl_renderbuffer *newRb;
   GET_CURRENT_CONTEXT(ctx);

   ASSERT_OUTSIDE_BEGIN_END(ctx);

   if (target != GL_RENDERBUFFER_EXT) {
         _mesa_error(ctx, GL_INVALID_ENUM,
                  "glBindRenderbufferEXT(target)");
      return;
   }

   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
   /* The above doesn't fully flush the drivers in the way that a
    * glFlush does, but that is required here:
    */
   if (ctx->Driver.Flush)
      ctx->Driver.Flush(ctx);


   if (renderbuffer) {
      newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
      if (newRb == &DummyRenderbuffer) {
         /* ID was reserved, but no real renderbuffer object made yet */
         newRb = NULL;
      }
      if (!newRb) {
	 /* create new renderbuffer object */
	 newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
	 if (!newRb) {
	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
	    return;
	 }
         ASSERT(newRb->AllocStorage);
         _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
         newRb->RefCount = 1; /* referenced by hash table */
      }
   }
   else {
      newRb = NULL;
   }

   ASSERT(newRb != &DummyRenderbuffer);

   _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb);
}
Exemple #8
0
static __DRIimage *
intel_create_image_from_renderbuffer(__DRIcontext *context,
				     int renderbuffer, void *loaderPrivate)
{
   __DRIimage *image;
   struct brw_context *brw = context->driverPrivate;
   struct gl_context *ctx = &brw->ctx;
   struct gl_renderbuffer *rb;
   struct intel_renderbuffer *irb;

   rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
   if (!rb) {
      _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferExternalMESA");
      return NULL;
   }

   irb = intel_renderbuffer(rb);
   intel_miptree_make_shareable(brw, irb->mt);
   image = calloc(1, sizeof *image);
   if (image == NULL)
      return NULL;

   image->internal_format = rb->InternalFormat;
   image->format = rb->Format;
   image->offset = 0;
   image->data = loaderPrivate;
   drm_intel_bo_unreference(image->bo);
   image->bo = irb->mt->bo;
   drm_intel_bo_reference(irb->mt->bo);
   image->width = rb->Width;
   image->height = rb->Height;
   image->pitch = irb->mt->pitch;
   image->dri_format = driGLFormatToImageFormat(image->format);
   image->has_depthstencil = irb->mt->stencil_mt? true : false;

   rb->NeedsFinishRenderTexture = true;
   return image;
}
Exemple #9
0
/**
 * Prepare the source or destination resource, including:
 * - Error checking
 * - Creating texture wrappers for renderbuffers
 * \param name  the texture or renderbuffer name
 * \param target  GL_TEXTURE target or GL_RENDERBUFFER.  For the later, will
 *                be changed to a compatible GL_TEXTURE target.
 * \param level  mipmap level
 * \param tex_obj  returns a pointer to a texture object
 * \param tex_image  returns a pointer to a texture image
 * \param tmp_tex  returns temporary texture object name
 * \return true if success, false if error
 */
static bool
prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
               struct gl_texture_object **tex_obj,
               struct gl_texture_image **tex_image, GLuint *tmp_tex,
               const char *dbg_prefix)
{
   if (name == 0) {
      _mesa_error(ctx, GL_INVALID_VALUE,
                  "glCopyImageSubData(%sName = %d)", dbg_prefix, name);
      return false;
   }

   /*
    * INVALID_ENUM is generated
    *  * if either <srcTarget> or <dstTarget>
    *   - is not RENDERBUFFER or a valid non-proxy texture target
    *   - is TEXTURE_BUFFER, or
    *   - is one of the cubemap face selectors described in table 3.17,
    */
   switch (*target) {
   case GL_RENDERBUFFER:
      /* Not a texture target, but valid */
   case GL_TEXTURE_1D:
   case GL_TEXTURE_1D_ARRAY:
   case GL_TEXTURE_2D:
   case GL_TEXTURE_3D:
   case GL_TEXTURE_CUBE_MAP:
   case GL_TEXTURE_RECTANGLE:
   case GL_TEXTURE_2D_ARRAY:
   case GL_TEXTURE_CUBE_MAP_ARRAY:
   case GL_TEXTURE_2D_MULTISAMPLE:
   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
      /* These are all valid */
      break;
   case GL_TEXTURE_EXTERNAL_OES:
      /* Only exists in ES */
   case GL_TEXTURE_BUFFER:
   default:
      _mesa_error(ctx, GL_INVALID_ENUM,
                  "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
                  _mesa_lookup_enum_by_nr(*target));
      return false;
   }

   if (*target == GL_RENDERBUFFER) {
      struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name);
      if (!rb) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
         return false;
      }

      if (!rb->Name) {
         _mesa_error(ctx, GL_INVALID_OPERATION,
                     "glCopyImageSubData(%sName incomplete)", dbg_prefix);
         return false;
      }

      if (level != 0) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
         return false;
      }

      if (rb->NumSamples > 1)
         *target = GL_TEXTURE_2D_MULTISAMPLE;
      else
         *target = GL_TEXTURE_2D;

      *tmp_tex = 0;
      _mesa_GenTextures(1, tmp_tex);
      if (*tmp_tex == 0)
         return false; /* Error already set by GenTextures */

      _mesa_BindTexture(*target, *tmp_tex);
      *tex_obj = _mesa_lookup_texture(ctx, *tmp_tex);
      *tex_image = _mesa_get_tex_image(ctx, *tex_obj, *target, 0);

      if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, *tex_image)) {
         _mesa_problem(ctx, "Failed to create texture from renderbuffer");
         return false;
      }

      if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) {
         rb->NeedsFinishRenderTexture = true;
         ctx->Driver.FinishRenderTexture(ctx, rb);
      }
   } else {
      *tex_obj = _mesa_lookup_texture(ctx, name);
      if (!*tex_obj) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
         return false;
      }

      _mesa_test_texobj_completeness(ctx, *tex_obj);
      if (!(*tex_obj)->_BaseComplete ||
          (level != 0 && !(*tex_obj)->_MipmapComplete)) {
         _mesa_error(ctx, GL_INVALID_OPERATION,
                     "glCopyImageSubData(%sName incomplete)", dbg_prefix);
         return false;
      }

      if ((*tex_obj)->Target != *target) {
         _mesa_error(ctx, GL_INVALID_ENUM,
                     "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
                     _mesa_lookup_enum_by_nr(*target));
         return false;
      }

      if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level);
         return false;
      }

      *tex_image = _mesa_select_tex_image(*tex_obj, *target, level);
      if (!*tex_image) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
         return false;
      }
   }

   return true;
}
Exemple #10
0
void GLAPIENTRY
_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
                                 GLenum renderbufferTarget,
                                 GLuint renderbuffer)
{
   struct gl_renderbuffer_attachment *att;
   struct gl_framebuffer *fb;
   struct gl_renderbuffer *rb;
   GET_CURRENT_CONTEXT(ctx);

   ASSERT_OUTSIDE_BEGIN_END(ctx);

   switch (target) {
#if FEATURE_EXT_framebuffer_blit
   case GL_DRAW_FRAMEBUFFER_EXT:
      if (!ctx->Extensions.EXT_framebuffer_blit) {
         _mesa_error(ctx, GL_INVALID_ENUM,
                     "glFramebufferRenderbufferEXT(target)");
         return;
      }
      fb = ctx->DrawBuffer;
      break;
   case GL_READ_FRAMEBUFFER_EXT:
      if (!ctx->Extensions.EXT_framebuffer_blit) {
         _mesa_error(ctx, GL_INVALID_ENUM,
                     "glFramebufferRenderbufferEXT(target)");
         return;
      }
      fb = ctx->ReadBuffer;
      break;
#endif
   case GL_FRAMEBUFFER_EXT:
      fb = ctx->DrawBuffer;
      break;
   default:
      _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferRenderbufferEXT(target)");
      return;
   }

   if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
      _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferRenderbufferEXT(renderbufferTarget)");
      return;
   }

   if (fb->Name == 0) {
      /* Can't attach new renderbuffers to a window system framebuffer */
      _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
      return;
   }

   att = _mesa_get_attachment(ctx, fb, attachment);
   if (att == NULL) {
      _mesa_error(ctx, GL_INVALID_ENUM,
                 "glFramebufferRenderbufferEXT(attachment)");
      return;
   }

   if (renderbuffer) {
      rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
      if (!rb) {
	 _mesa_error(ctx, GL_INVALID_OPERATION,
		     "glFramebufferRenderbufferEXT(renderbuffer)");
	 return;
      }
   }
   else {
      /* remove renderbuffer attachment */
      rb = NULL;
   }

   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
   /* The above doesn't fully flush the drivers in the way that a
    * glFlush does, but that is required here:
    */
   if (ctx->Driver.Flush)
      ctx->Driver.Flush(ctx);

   assert(ctx->Driver.FramebufferRenderbuffer);
   ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb);

   /* Some subsequent GL commands may depend on the framebuffer's visual
    * after the binding is updated.  Update visual info now.
    */
   _mesa_update_framebuffer_visual(fb);
}
Exemple #11
0
/**
 * Prepare the source or destination resource.  This involves error
 * checking and returning the relevant gl_texture_image or gl_renderbuffer.
 * Note that one of the resulting tex_image or renderbuffer pointers will be
 * NULL and the other will be non-null.
 *
 * \param name  the texture or renderbuffer name
 * \param target  One of GL_TEXTURE_x target or GL_RENDERBUFFER
 * \param level  mipmap level
 * \param z  src or dest Z
 * \param depth  number of slices/faces/layers to copy
 * \param tex_image  returns a pointer to a texture image
 * \param renderbuffer  returns a pointer to a renderbuffer
 * \return true if success, false if error
 */
static bool
prepare_target(struct gl_context *ctx, GLuint name, GLenum target,
               int level, int z, int depth,
               struct gl_texture_image **tex_image,
               struct gl_renderbuffer **renderbuffer,
               mesa_format *format,
               GLenum *internalFormat,
               GLuint *width,
               GLuint *height,
               GLuint *num_samples,
               const char *dbg_prefix)
{
   if (name == 0) {
      _mesa_error(ctx, GL_INVALID_VALUE,
                  "glCopyImageSubData(%sName = %d)", dbg_prefix, name);
      return false;
   }

   /*
    * INVALID_ENUM is generated
    *  * if either <srcTarget> or <dstTarget>
    *   - is not RENDERBUFFER or a valid non-proxy texture target
    *   - is TEXTURE_BUFFER, or
    *   - is one of the cubemap face selectors described in table 3.17,
    */
   switch (target) {
   case GL_RENDERBUFFER:
      /* Not a texture target, but valid */
   case GL_TEXTURE_1D:
   case GL_TEXTURE_1D_ARRAY:
   case GL_TEXTURE_2D:
   case GL_TEXTURE_3D:
   case GL_TEXTURE_CUBE_MAP:
   case GL_TEXTURE_RECTANGLE:
   case GL_TEXTURE_2D_ARRAY:
   case GL_TEXTURE_CUBE_MAP_ARRAY:
   case GL_TEXTURE_2D_MULTISAMPLE:
   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
      /* These are all valid */
      break;
   case GL_TEXTURE_EXTERNAL_OES:
      /* Only exists in ES */
   case GL_TEXTURE_BUFFER:
   default:
      _mesa_error(ctx, GL_INVALID_ENUM,
                  "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
                  _mesa_enum_to_string(target));
      return false;
   }

   if (target == GL_RENDERBUFFER) {
      struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name);

      if (!rb) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
         return false;
      }

      if (!rb->Name) {
         _mesa_error(ctx, GL_INVALID_OPERATION,
                     "glCopyImageSubData(%sName incomplete)", dbg_prefix);
         return false;
      }

      if (level != 0) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
         return false;
      }

      *renderbuffer = rb;
      *format = rb->Format;
      *internalFormat = rb->InternalFormat;
      *width = rb->Width;
      *height = rb->Height;
      *num_samples = rb->NumSamples;
      *tex_image = NULL;
   } else {
      struct gl_texture_object *texObj = _mesa_lookup_texture(ctx, name);

      if (!texObj) {
         /*
          * From GL_ARB_copy_image specification:
          * "INVALID_VALUE is generated if either <srcName> or <dstName> does
          * not correspond to a valid renderbuffer or texture object according
          * to the corresponding target parameter."
          */
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
         return false;
      }

      _mesa_test_texobj_completeness(ctx, texObj);
      if (!texObj->_BaseComplete ||
          (level != 0 && !texObj->_MipmapComplete)) {
         _mesa_error(ctx, GL_INVALID_OPERATION,
                     "glCopyImageSubData(%sName incomplete)", dbg_prefix);
         return false;
      }

      /* Note that target will not be a cube face name */
      if (texObj->Target != target) {
         /*
          * From GL_ARB_copy_image_specification:
          * "INVALID_ENUM is generated if the target does not match the type
          * of the object."
          */
         _mesa_error(ctx, GL_INVALID_ENUM,
                     "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
                     _mesa_enum_to_string(target));
         return false;
      }

      if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level);
         return false;
      }

      if (target == GL_TEXTURE_CUBE_MAP) {
         int i;

         assert(z < MAX_FACES);  /* should have been caught earlier */

         /* make sure all the cube faces are present */
         for (i = 0; i < depth; i++) {
            if (!texObj->Image[z+i][level]) {
               /* missing cube face */
               _mesa_error(ctx, GL_INVALID_VALUE,
                           "glCopyImageSubData(missing cube face)");
               return false;
            }
         }

         *tex_image = texObj->Image[z][level];
      }
      else {
         *tex_image = _mesa_select_tex_image(texObj, target, level);
      }

      if (!*tex_image) {
         _mesa_error(ctx, GL_INVALID_VALUE,
                     "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
         return false;
      }

      *renderbuffer = NULL;
      *format = (*tex_image)->TexFormat;
      *internalFormat = (*tex_image)->InternalFormat;
      *width = (*tex_image)->Width;
      *height = (*tex_image)->Height;
      *num_samples = (*tex_image)->NumSamples;
   }

   return true;
}