mesa: improve ARB_copy_image internal format compat check
[mesa.git] / src / mesa / main / copyimage.c
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 #include "glformats.h"
37
38 enum mesa_block_class {
39 BLOCK_CLASS_128_BITS,
40 BLOCK_CLASS_64_BITS
41 };
42
43 static bool
44 prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
45 struct gl_texture_object **tex_obj,
46 struct gl_texture_image **tex_image, GLuint *tmp_tex,
47 const char *dbg_prefix)
48 {
49 struct gl_renderbuffer *rb;
50
51 if (name == 0) {
52 _mesa_error(ctx, GL_INVALID_VALUE,
53 "glCopyImageSubData(%sName = %d)", dbg_prefix, name);
54 return false;
55 }
56
57 /*
58 * INVALID_ENUM is generated
59 * * if either <srcTarget> or <dstTarget>
60 * - is not RENDERBUFFER or a valid non-proxy texture target
61 * - is TEXTURE_BUFFER, or
62 * - is one of the cubemap face selectors described in table 3.17,
63 */
64 switch (*target) {
65 case GL_RENDERBUFFER:
66 /* Not a texture target, but valid */
67 case GL_TEXTURE_1D:
68 case GL_TEXTURE_1D_ARRAY:
69 case GL_TEXTURE_2D:
70 case GL_TEXTURE_3D:
71 case GL_TEXTURE_CUBE_MAP:
72 case GL_TEXTURE_RECTANGLE:
73 case GL_TEXTURE_2D_ARRAY:
74 case GL_TEXTURE_CUBE_MAP_ARRAY:
75 case GL_TEXTURE_2D_MULTISAMPLE:
76 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
77 /* These are all valid */
78 break;
79 case GL_TEXTURE_EXTERNAL_OES:
80 /* Only exists in ES */
81 case GL_TEXTURE_BUFFER:
82 default:
83 _mesa_error(ctx, GL_INVALID_ENUM,
84 "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
85 _mesa_lookup_enum_by_nr(*target));
86 return false;
87 }
88
89 if (*target == GL_RENDERBUFFER) {
90 rb = _mesa_lookup_renderbuffer(ctx, name);
91 if (!rb) {
92 _mesa_error(ctx, GL_INVALID_VALUE,
93 "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
94 return false;
95 }
96
97 if (!rb->Name) {
98 _mesa_error(ctx, GL_INVALID_OPERATION,
99 "glCopyImageSubData(%sName incomplete)", dbg_prefix);
100 return false;
101 }
102
103 if (level != 0) {
104 _mesa_error(ctx, GL_INVALID_VALUE,
105 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
106 return false;
107 }
108
109 if (rb->NumSamples > 1)
110 *target = GL_TEXTURE_2D_MULTISAMPLE;
111 else
112 *target = GL_TEXTURE_2D;
113
114 *tmp_tex = 0;
115 _mesa_GenTextures(1, tmp_tex);
116 if (*tmp_tex == 0)
117 return false; /* Error already set by GenTextures */
118
119 _mesa_BindTexture(*target, *tmp_tex);
120 *tex_obj = _mesa_lookup_texture(ctx, *tmp_tex);
121 *tex_image = _mesa_get_tex_image(ctx, *tex_obj, *target, 0);
122
123 if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, *tex_image)) {
124 _mesa_problem(ctx, "Failed to create texture from renderbuffer");
125 return false;
126 }
127
128 if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) {
129 rb->NeedsFinishRenderTexture = true;
130 ctx->Driver.FinishRenderTexture(ctx, rb);
131 }
132 } else {
133 *tex_obj = _mesa_lookup_texture(ctx, name);
134 if (!*tex_obj) {
135 _mesa_error(ctx, GL_INVALID_VALUE,
136 "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
137 return false;
138 }
139
140 _mesa_test_texobj_completeness(ctx, *tex_obj);
141 if (!(*tex_obj)->_BaseComplete ||
142 (level != 0 && !(*tex_obj)->_MipmapComplete)) {
143 _mesa_error(ctx, GL_INVALID_OPERATION,
144 "glCopyImageSubData(%sName incomplete)", dbg_prefix);
145 return false;
146 }
147
148 if ((*tex_obj)->Target != *target) {
149 _mesa_error(ctx, GL_INVALID_ENUM,
150 "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
151 _mesa_lookup_enum_by_nr(*target));
152 return false;
153 }
154
155 if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
156 _mesa_error(ctx, GL_INVALID_VALUE,
157 "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level);
158 return false;
159 }
160
161 *tex_image = _mesa_select_tex_image(*tex_obj, *target, level);
162 if (!*tex_image) {
163 _mesa_error(ctx, GL_INVALID_VALUE,
164 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
165 return false;
166 }
167 }
168
169 return true;
170 }
171
172 static bool
173 check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
174 int x, int y, int z, int width, int height, int depth,
175 const char *dbg_prefix)
176 {
177 if (width < 0 || height < 0 || depth < 0) {
178 _mesa_error(ctx, GL_INVALID_VALUE,
179 "glCopyImageSubData(%sWidth, %sHeight, or %sDepth is negative)",
180 dbg_prefix, dbg_prefix, dbg_prefix);
181 return false;
182 }
183
184 if (x < 0 || y < 0 || z < 0) {
185 _mesa_error(ctx, GL_INVALID_VALUE,
186 "glCopyImageSubData(%sX, %sY, or %sZ is negative)",
187 dbg_prefix, dbg_prefix, dbg_prefix);
188 return false;
189 }
190
191 if (x + width > tex_image->Width) {
192 _mesa_error(ctx, GL_INVALID_VALUE,
193 "glCopyImageSubData(%sX or %sWidth exceeds image bounds)",
194 dbg_prefix, dbg_prefix);
195 return false;
196 }
197
198 switch (tex_image->TexObject->Target) {
199 case GL_TEXTURE_1D:
200 case GL_TEXTURE_1D_ARRAY:
201 if (y != 0 || height != 1) {
202 _mesa_error(ctx, GL_INVALID_VALUE,
203 "glCopyImageSubData(%sY or %sHeight exceeds image bounds)",
204 dbg_prefix, dbg_prefix);
205 return false;
206 }
207 break;
208 default:
209 if (y + height > tex_image->Height) {
210 _mesa_error(ctx, GL_INVALID_VALUE,
211 "glCopyImageSubData(%sY or %sHeight exceeds image bounds)",
212 dbg_prefix, dbg_prefix);
213 return false;
214 }
215 break;
216 }
217
218 switch (tex_image->TexObject->Target) {
219 case GL_TEXTURE_1D:
220 case GL_TEXTURE_2D:
221 case GL_TEXTURE_2D_MULTISAMPLE:
222 case GL_TEXTURE_RECTANGLE:
223 if (z != 0 || depth != 1) {
224 _mesa_error(ctx, GL_INVALID_VALUE,
225 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)",
226 dbg_prefix, dbg_prefix);
227 return false;
228 }
229 break;
230 case GL_TEXTURE_CUBE_MAP:
231 if (z < 0 || z + depth > 6) {
232 _mesa_error(ctx, GL_INVALID_VALUE,
233 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)",
234 dbg_prefix, dbg_prefix);
235 return false;
236 }
237 break;
238 case GL_TEXTURE_1D_ARRAY:
239 if (z < 0 || z + depth > tex_image->Height) {
240 _mesa_error(ctx, GL_INVALID_VALUE,
241 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)",
242 dbg_prefix, dbg_prefix);
243 return false;
244 }
245 break;
246 case GL_TEXTURE_CUBE_MAP_ARRAY:
247 case GL_TEXTURE_2D_ARRAY:
248 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
249 case GL_TEXTURE_3D:
250 if (z < 0 || z + depth > tex_image->Depth) {
251 _mesa_error(ctx, GL_INVALID_VALUE,
252 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)",
253 dbg_prefix, dbg_prefix);
254 return false;
255 }
256 break;
257 }
258
259 return true;
260 }
261
262 static bool
263 compressed_format_compatible(struct gl_context *ctx,
264 GLenum compressedFormat, GLenum otherFormat)
265 {
266 enum mesa_block_class compressedClass, otherClass;
267
268 /* Two view-incompatible compressed formats are never compatible. */
269 if (_mesa_is_compressed_format(ctx, otherFormat)) {
270 return false;
271 }
272
273 /*
274 * From ARB_copy_image spec:
275 * Table 4.X.1 (Compatible internal formats for copying between
276 * compressed and uncompressed internal formats)
277 * ---------------------------------------------------------------------
278 * | Texel / | Uncompressed | |
279 * | Block | internal format | Compressed internal format |
280 * | size | | |
281 * ---------------------------------------------------------------------
282 * | 128-bit | RGBA32UI, | COMPRESSED_RGBA_S3TC_DXT3_EXT, |
283 * | | RGBA32I, | COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,|
284 * | | RGBA32F | COMPRESSED_RGBA_S3TC_DXT5_EXT, |
285 * | | | COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,|
286 * | | | COMPRESSED_RG_RGTC2, |
287 * | | | COMPRESSED_SIGNED_RG_RGTC2, |
288 * | | | COMPRESSED_RGBA_BPTC_UNORM, |
289 * | | | COMPRESSED_SRGB_ALPHA_BPTC_UNORM, |
290 * | | | COMPRESSED_RGB_BPTC_SIGNED_FLOAT, |
291 * | | | COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT |
292 * ---------------------------------------------------------------------
293 * | 64-bit | RGBA16F, RG32F, | COMPRESSED_RGB_S3TC_DXT1_EXT, |
294 * | | RGBA16UI, RG32UI, | COMPRESSED_SRGB_S3TC_DXT1_EXT, |
295 * | | RGBA16I, RG32I, | COMPRESSED_RGBA_S3TC_DXT1_EXT, |
296 * | | RGBA16, | COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,|
297 * | | RGBA16_SNORM | COMPRESSED_RED_RGTC1, |
298 * | | | COMPRESSED_SIGNED_RED_RGTC1 |
299 * ---------------------------------------------------------------------
300 */
301
302 switch (compressedFormat) {
303 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
304 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
305 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
306 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
307 case GL_COMPRESSED_RG_RGTC2:
308 case GL_COMPRESSED_SIGNED_RG_RGTC2:
309 case GL_COMPRESSED_RGBA_BPTC_UNORM:
310 case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
311 case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
312 case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
313 compressedClass = BLOCK_CLASS_128_BITS;
314 break;
315 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
316 case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
317 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
318 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
319 case GL_COMPRESSED_RED_RGTC1:
320 case GL_COMPRESSED_SIGNED_RED_RGTC1:
321 compressedClass = BLOCK_CLASS_64_BITS;
322 break;
323 default:
324 return false;
325 }
326
327 switch (otherFormat) {
328 case GL_RGBA32UI:
329 case GL_RGBA32I:
330 case GL_RGBA32F:
331 otherClass = BLOCK_CLASS_128_BITS;
332 break;
333 case GL_RGBA16F:
334 case GL_RG32F:
335 case GL_RGBA16UI:
336 case GL_RG32UI:
337 case GL_RGBA16I:
338 case GL_RG32I:
339 case GL_RGBA16:
340 case GL_RGBA16_SNORM:
341 otherClass = BLOCK_CLASS_64_BITS;
342 break;
343 default:
344 return false;
345 }
346
347 return compressedClass == otherClass;
348 }
349
350 static bool
351 copy_format_compatible(struct gl_context *ctx,
352 GLenum srcFormat, GLenum dstFormat)
353 {
354 /*
355 * From ARB_copy_image spec:
356 * For the purposes of CopyImageSubData, two internal formats
357 * are considered compatible if any of the following conditions are
358 * met:
359 * * the formats are the same,
360 * * the formats are considered compatible according to the
361 * compatibility rules used for texture views as defined in
362 * section 3.9.X. In particular, if both internal formats are listed
363 * in the same entry of Table 3.X.2, they are considered compatible, or
364 * * one format is compressed and the other is uncompressed and
365 * Table 4.X.1 lists the two formats in the same row.
366 */
367
368 if (_mesa_texture_view_compatible_format(ctx, srcFormat, dstFormat)) {
369 /* Also checks if formats are equal. */
370 return true;
371 } else if (_mesa_is_compressed_format(ctx, srcFormat)) {
372 return compressed_format_compatible(ctx, srcFormat, dstFormat);
373 } else if (_mesa_is_compressed_format(ctx, dstFormat)) {
374 return compressed_format_compatible(ctx, dstFormat, srcFormat);
375 }
376
377 return false;
378 }
379
380 void GLAPIENTRY
381 _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
382 GLint srcX, GLint srcY, GLint srcZ,
383 GLuint dstName, GLenum dstTarget, GLint dstLevel,
384 GLint dstX, GLint dstY, GLint dstZ,
385 GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)
386 {
387 GET_CURRENT_CONTEXT(ctx);
388 GLuint tmpTexNames[2] = { 0, 0 };
389 struct gl_texture_object *srcTexObj, *dstTexObj;
390 struct gl_texture_image *srcTexImage, *dstTexImage;
391 GLuint src_bw, src_bh, dst_bw, dst_bh;
392 int i, srcNewZ, dstNewZ;
393
394 if (MESA_VERBOSE & VERBOSE_API)
395 _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, "
396 "%u, %s, %d, %d, %d, %d, "
397 "%d, %d, %d)\n",
398 srcName, _mesa_lookup_enum_by_nr(srcTarget), srcLevel,
399 srcX, srcY, srcZ,
400 dstName, _mesa_lookup_enum_by_nr(dstTarget), dstLevel,
401 dstX, dstY, dstZ,
402 srcWidth, srcHeight, srcWidth);
403
404 if (!ctx->Extensions.ARB_copy_image) {
405 _mesa_error(ctx, GL_INVALID_OPERATION,
406 "glCopyImageSubData(extension not available)");
407 return;
408 }
409
410 if (!prepare_target(ctx, srcName, &srcTarget, srcLevel,
411 &srcTexObj, &srcTexImage, &tmpTexNames[0], "src"))
412 goto cleanup;
413
414 if (!prepare_target(ctx, dstName, &dstTarget, dstLevel,
415 &dstTexObj, &dstTexImage, &tmpTexNames[1], "dst"))
416 goto cleanup;
417
418 _mesa_get_format_block_size(srcTexImage->TexFormat, &src_bw, &src_bh);
419 if ((srcX % src_bw != 0) || (srcY % src_bh != 0) ||
420 (srcWidth % src_bw != 0) || (srcHeight % src_bh != 0)) {
421 _mesa_error(ctx, GL_INVALID_VALUE,
422 "glCopyImageSubData(unaligned src rectangle)");
423 goto cleanup;
424 }
425
426 _mesa_get_format_block_size(dstTexImage->TexFormat, &dst_bw, &dst_bh);
427 if ((dstX % dst_bw != 0) || (dstY % dst_bh != 0)) {
428 _mesa_error(ctx, GL_INVALID_VALUE,
429 "glCopyImageSubData(unaligned dst rectangle)");
430 goto cleanup;
431 }
432
433 if (!check_region_bounds(ctx, srcTexImage, srcX, srcY, srcZ,
434 srcWidth, srcHeight, srcDepth, "src"))
435 goto cleanup;
436
437 if (!check_region_bounds(ctx, dstTexImage, dstX, dstY, dstZ,
438 (srcWidth / src_bw) * dst_bw,
439 (srcHeight / src_bh) * dst_bh, srcDepth, "dst"))
440 goto cleanup;
441
442 if (!copy_format_compatible(ctx, srcTexImage->InternalFormat,
443 dstTexImage->InternalFormat)) {
444 _mesa_error(ctx, GL_INVALID_OPERATION,
445 "glCopyImageSubData(internalFormat mismatch)");
446 goto cleanup;
447 }
448
449 for (i = 0; i < srcDepth; ++i) {
450 if (srcTexObj->Target == GL_TEXTURE_CUBE_MAP) {
451 srcTexImage = srcTexObj->Image[i + srcZ][srcLevel];
452 srcNewZ = 0;
453 } else {
454 srcNewZ = srcZ + i;
455 }
456
457 if (dstTexObj->Target == GL_TEXTURE_CUBE_MAP) {
458 dstTexImage = dstTexObj->Image[i + dstZ][dstLevel];
459 dstNewZ = 0;
460 } else {
461 dstNewZ = dstZ + i;
462 }
463
464 ctx->Driver.CopyImageSubData(ctx, srcTexImage, srcX, srcY, srcNewZ,
465 dstTexImage, dstX, dstY, dstNewZ,
466 srcWidth, srcHeight);
467 }
468
469 cleanup:
470 _mesa_DeleteTextures(2, tmpTexNames);
471 }