mesa: check if GL_ARB_copy_image is enabled in _mesa_CopyImageSubData()
[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
37 static bool
38 prepare_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
166 static bool
167 check_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
256 void 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
360 cleanup:
361 _mesa_DeleteTextures(2, tmpTexNames);
362 }