copyimage.c revision af69d88d
1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 2014 Intel Corporation. All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Jason Ekstrand <jason.ekstrand@intel.com> 26 */ 27 28#include "glheader.h" 29#include "errors.h" 30#include "enums.h" 31#include "copyimage.h" 32#include "teximage.h" 33#include "texobj.h" 34#include "fbobject.h" 35#include "textureview.h" 36 37static bool 38prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, 39 struct gl_texture_object **tex_obj, 40 struct gl_texture_image **tex_image, GLuint *tmp_tex, 41 const char *dbg_prefix) 42{ 43 struct gl_renderbuffer *rb; 44 45 if (name == 0) { 46 _mesa_error(ctx, GL_INVALID_VALUE, 47 "glCopyImageSubData(%sName = %d)", dbg_prefix, name); 48 return false; 49 } 50 51 /* 52 * INVALID_ENUM is generated 53 * * if either <srcTarget> or <dstTarget> 54 * - is not RENDERBUFFER or a valid non-proxy texture target 55 * - is TEXTURE_BUFFER, or 56 * - is one of the cubemap face selectors described in table 3.17, 57 */ 58 switch (*target) { 59 case GL_RENDERBUFFER: 60 /* Not a texture target, but valid */ 61 case GL_TEXTURE_1D: 62 case GL_TEXTURE_1D_ARRAY: 63 case GL_TEXTURE_2D: 64 case GL_TEXTURE_3D: 65 case GL_TEXTURE_CUBE_MAP: 66 case GL_TEXTURE_RECTANGLE: 67 case GL_TEXTURE_2D_ARRAY: 68 case GL_TEXTURE_CUBE_MAP_ARRAY: 69 case GL_TEXTURE_2D_MULTISAMPLE: 70 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: 71 /* These are all valid */ 72 break; 73 case GL_TEXTURE_EXTERNAL_OES: 74 /* Only exists in ES */ 75 case GL_TEXTURE_BUFFER: 76 default: 77 _mesa_error(ctx, GL_INVALID_ENUM, 78 "glCopyImageSubData(%sTarget = %s)", dbg_prefix, 79 _mesa_lookup_enum_by_nr(*target)); 80 return false; 81 } 82 83 if (*target == GL_RENDERBUFFER) { 84 rb = _mesa_lookup_renderbuffer(ctx, name); 85 if (!rb) { 86 _mesa_error(ctx, GL_INVALID_VALUE, 87 "glCopyImageSubData(%sName = %u)", dbg_prefix, name); 88 return false; 89 } 90 91 if (!rb->Name) { 92 _mesa_error(ctx, GL_INVALID_OPERATION, 93 "glCopyImageSubData(%sName incomplete)", dbg_prefix); 94 return false; 95 } 96 97 if (level != 0) { 98 _mesa_error(ctx, GL_INVALID_VALUE, 99 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level); 100 return false; 101 } 102 103 if (rb->NumSamples > 1) 104 *target = GL_TEXTURE_2D_MULTISAMPLE; 105 else 106 *target = GL_TEXTURE_2D; 107 108 *tmp_tex = 0; 109 _mesa_GenTextures(1, tmp_tex); 110 if (*tmp_tex == 0) 111 return false; /* Error already set by GenTextures */ 112 113 _mesa_BindTexture(*target, *tmp_tex); 114 *tex_obj = _mesa_lookup_texture(ctx, *tmp_tex); 115 *tex_image = _mesa_get_tex_image(ctx, *tex_obj, *target, 0); 116 117 if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, *tex_image)) { 118 _mesa_problem(ctx, "Failed to create texture from renderbuffer"); 119 return false; 120 } 121 122 if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) { 123 rb->NeedsFinishRenderTexture = true; 124 ctx->Driver.FinishRenderTexture(ctx, rb); 125 } 126 } else { 127 *tex_obj = _mesa_lookup_texture(ctx, name); 128 if (!*tex_obj) { 129 _mesa_error(ctx, GL_INVALID_VALUE, 130 "glCopyImageSubData(%sName = %u)", dbg_prefix, name); 131 return false; 132 } 133 134 _mesa_test_texobj_completeness(ctx, *tex_obj); 135 if (!(*tex_obj)->_BaseComplete || 136 (level != 0 && !(*tex_obj)->_MipmapComplete)) { 137 _mesa_error(ctx, GL_INVALID_OPERATION, 138 "glCopyImageSubData(%sName incomplete)", dbg_prefix); 139 return false; 140 } 141 142 if ((*tex_obj)->Target != *target) { 143 _mesa_error(ctx, GL_INVALID_ENUM, 144 "glCopyImageSubData(%sTarget = %s)", dbg_prefix, 145 _mesa_lookup_enum_by_nr(*target)); 146 return false; 147 } 148 149 if (level < 0 || level >= MAX_TEXTURE_LEVELS) { 150 _mesa_error(ctx, GL_INVALID_VALUE, 151 "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level); 152 return false; 153 } 154 155 *tex_image = _mesa_select_tex_image(ctx, *tex_obj, *target, level); 156 if (!*tex_image) { 157 _mesa_error(ctx, GL_INVALID_VALUE, 158 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level); 159 return false; 160 } 161 } 162 163 return true; 164} 165 166static bool 167check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image, 168 int x, int y, int z, int width, int height, int depth, 169 const char *dbg_prefix) 170{ 171 if (width < 0 || height < 0 || depth < 0) { 172 _mesa_error(ctx, GL_INVALID_VALUE, 173 "glCopyImageSubData(%sWidth, %sHeight, or %sDepth is negative)", 174 dbg_prefix, dbg_prefix, dbg_prefix); 175 return false; 176 } 177 178 if (x < 0 || y < 0 || z < 0) { 179 _mesa_error(ctx, GL_INVALID_VALUE, 180 "glCopyImageSubData(%sX, %sY, or %sZ is negative)", 181 dbg_prefix, dbg_prefix, dbg_prefix); 182 return false; 183 } 184 185 if (x + width > tex_image->Width) { 186 _mesa_error(ctx, GL_INVALID_VALUE, 187 "glCopyImageSubData(%sX or %sWidth exceeds image bounds)", 188 dbg_prefix, dbg_prefix); 189 return false; 190 } 191 192 switch (tex_image->TexObject->Target) { 193 case GL_TEXTURE_1D: 194 case GL_TEXTURE_1D_ARRAY: 195 if (y != 0 || height != 1) { 196 _mesa_error(ctx, GL_INVALID_VALUE, 197 "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", 198 dbg_prefix, dbg_prefix); 199 return false; 200 } 201 break; 202 default: 203 if (y + height > tex_image->Height) { 204 _mesa_error(ctx, GL_INVALID_VALUE, 205 "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", 206 dbg_prefix, dbg_prefix); 207 return false; 208 } 209 break; 210 } 211 212 switch (tex_image->TexObject->Target) { 213 case GL_TEXTURE_1D: 214 case GL_TEXTURE_2D: 215 case GL_TEXTURE_2D_MULTISAMPLE: 216 case GL_TEXTURE_RECTANGLE: 217 if (z != 0 || depth != 1) { 218 _mesa_error(ctx, GL_INVALID_VALUE, 219 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", 220 dbg_prefix, dbg_prefix); 221 return false; 222 } 223 break; 224 case GL_TEXTURE_CUBE_MAP: 225 if (z < 0 || z + depth > 6) { 226 _mesa_error(ctx, GL_INVALID_VALUE, 227 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", 228 dbg_prefix, dbg_prefix); 229 return false; 230 } 231 break; 232 case GL_TEXTURE_1D_ARRAY: 233 if (z < 0 || z + depth > tex_image->Height) { 234 _mesa_error(ctx, GL_INVALID_VALUE, 235 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", 236 dbg_prefix, dbg_prefix); 237 return false; 238 } 239 break; 240 case GL_TEXTURE_CUBE_MAP_ARRAY: 241 case GL_TEXTURE_2D_ARRAY: 242 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: 243 case GL_TEXTURE_3D: 244 if (z < 0 || z + depth > tex_image->Depth) { 245 _mesa_error(ctx, GL_INVALID_VALUE, 246 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", 247 dbg_prefix, dbg_prefix); 248 return false; 249 } 250 break; 251 } 252 253 return true; 254} 255 256void GLAPIENTRY 257_mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, 258 GLint srcX, GLint srcY, GLint srcZ, 259 GLuint dstName, GLenum dstTarget, GLint dstLevel, 260 GLint dstX, GLint dstY, GLint dstZ, 261 GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) 262{ 263 GET_CURRENT_CONTEXT(ctx); 264 GLuint tmpTexNames[2] = { 0, 0 }; 265 struct gl_texture_object *srcTexObj, *dstTexObj; 266 struct gl_texture_image *srcTexImage, *dstTexImage; 267 GLuint src_bw, src_bh, dst_bw, dst_bh; 268 int i, srcNewZ, dstNewZ, Bpt; 269 270 if (MESA_VERBOSE & VERBOSE_API) 271 _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, " 272 "%u, %s, %d, %d, %d, %d, " 273 "%d, %d, %d)\n", 274 srcName, _mesa_lookup_enum_by_nr(srcTarget), srcLevel, 275 srcX, srcY, srcZ, 276 dstName, _mesa_lookup_enum_by_nr(dstTarget), dstLevel, 277 dstX, dstY, dstZ, 278 srcWidth, srcHeight, srcWidth); 279 280 if (!ctx->Extensions.ARB_copy_image) { 281 _mesa_error(ctx, GL_INVALID_OPERATION, 282 "glCopyImageSubData(extension not available)"); 283 return; 284 } 285 286 if (!prepare_target(ctx, srcName, &srcTarget, srcLevel, 287 &srcTexObj, &srcTexImage, &tmpTexNames[0], "src")) 288 goto cleanup; 289 290 if (!prepare_target(ctx, dstName, &dstTarget, dstLevel, 291 &dstTexObj, &dstTexImage, &tmpTexNames[1], "dst")) 292 goto cleanup; 293 294 _mesa_get_format_block_size(srcTexImage->TexFormat, &src_bw, &src_bh); 295 if ((srcX % src_bw != 0) || (srcY % src_bh != 0) || 296 (srcWidth % src_bw != 0) || (srcHeight % src_bh != 0)) { 297 _mesa_error(ctx, GL_INVALID_VALUE, 298 "glCopyImageSubData(unaligned src rectangle)"); 299 goto cleanup; 300 } 301 302 _mesa_get_format_block_size(dstTexImage->TexFormat, &dst_bw, &dst_bh); 303 if ((dstX % dst_bw != 0) || (dstY % dst_bh != 0)) { 304 _mesa_error(ctx, GL_INVALID_VALUE, 305 "glCopyImageSubData(unaligned dst rectangle)"); 306 goto cleanup; 307 } 308 309 /* Very simple sanity check. This is sufficient if one of the textures 310 * is compressed. */ 311 Bpt = _mesa_get_format_bytes(srcTexImage->TexFormat); 312 if (_mesa_get_format_bytes(dstTexImage->TexFormat) != Bpt) { 313 _mesa_error(ctx, GL_INVALID_VALUE, 314 "glCopyImageSubData(internalFormat mismatch)"); 315 goto cleanup; 316 } 317 318 if (!check_region_bounds(ctx, srcTexImage, srcX, srcY, srcZ, 319 srcWidth, srcHeight, srcDepth, "src")) 320 goto cleanup; 321 322 if (!check_region_bounds(ctx, dstTexImage, dstX, dstY, dstZ, 323 (srcWidth / src_bw) * dst_bw, 324 (srcHeight / src_bh) * dst_bh, srcDepth, "dst")) 325 goto cleanup; 326 327 if (_mesa_is_format_compressed(srcTexImage->TexFormat)) { 328 /* XXX: Technically, we should probaby do some more specific checking 329 * here. However, this should be sufficient for all compressed 330 * formats that mesa supports since it is a direct memory copy. 331 */ 332 } else if (_mesa_is_format_compressed(dstTexImage->TexFormat)) { 333 } else if (_mesa_texture_view_compatible_format(ctx, 334 srcTexImage->InternalFormat, 335 dstTexImage->InternalFormat)) { 336 } else { 337 return; /* Error logged by _mesa_texture_view_compatible_format */ 338 } 339 340 for (i = 0; i < srcDepth; ++i) { 341 if (srcTexObj->Target == GL_TEXTURE_CUBE_MAP) { 342 srcTexImage = srcTexObj->Image[i + srcZ][srcLevel]; 343 srcNewZ = 0; 344 } else { 345 srcNewZ = srcZ + i; 346 } 347 348 if (dstTexObj->Target == GL_TEXTURE_CUBE_MAP) { 349 dstTexImage = dstTexObj->Image[i + dstZ][dstLevel]; 350 dstNewZ = 0; 351 } else { 352 dstNewZ = dstZ + i; 353 } 354 355 ctx->Driver.CopyImageSubData(ctx, srcTexImage, srcX, srcY, srcNewZ, 356 dstTexImage, dstX, dstY, dstNewZ, 357 srcWidth, srcHeight); 358 } 359 360cleanup: 361 _mesa_DeleteTextures(2, tmpTexNames); 362} 363