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