meta: Avoid random memory access on error
[mesa.git] / src / mesa / drivers / common / meta_copy_image.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
25 #include "glheader.h"
26 #include "context.h"
27 #include "enums.h"
28 #include "imports.h"
29 #include "macros.h"
30 #include "teximage.h"
31 #include "texobj.h"
32 #include "fbobject.h"
33 #include "framebuffer.h"
34 #include "buffers.h"
35 #include "state.h"
36 #include "mtypes.h"
37 #include "meta.h"
38
39 /**
40 * Create a texture image that wraps a renderbuffer.
41 */
42 static struct gl_texture_image *
43 wrap_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
44 {
45 GLenum texTarget;
46 struct gl_texture_object *texObj;
47 struct gl_texture_image *texImage;
48
49 if (rb->NumSamples > 1)
50 texTarget = GL_TEXTURE_2D_MULTISAMPLE;
51 else
52 texTarget = GL_TEXTURE_2D;
53
54 /* Texture ID is not significant since it never goes into the hash table */
55 texObj = ctx->Driver.NewTextureObject(ctx, 0, texTarget);
56 assert(texObj);
57 if (!texObj)
58 return NULL;
59
60 texImage = _mesa_get_tex_image(ctx, texObj, texTarget, 0);
61 assert(texImage);
62 if (!texImage)
63 return NULL;
64
65 if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, texImage)) {
66 _mesa_problem(ctx, "Failed to create texture from renderbuffer");
67 return NULL;
68 }
69
70 if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) {
71 rb->NeedsFinishRenderTexture = true;
72 ctx->Driver.FinishRenderTexture(ctx, rb);
73 }
74
75 return texImage;
76 }
77
78
79 /* This function makes a texture view without bothering with all of the API
80 * checks. Most of them are the same for CopyTexSubImage so checking would
81 * be redundant. The one major difference is that we don't check for
82 * whether the texture is immutable or not. However, since the view will
83 * be created and then immediately destroyed, this should not be a problem.
84 */
85 static bool
86 make_view(struct gl_context *ctx, struct gl_texture_image *tex_image,
87 struct gl_texture_image **view_tex_image, GLuint *view_tex_name,
88 GLenum internal_format)
89 {
90 struct gl_texture_object *tex_obj = tex_image->TexObject;
91 struct gl_texture_object *view_tex_obj;
92 mesa_format tex_format;
93
94 /* Set up the new texture object */
95 _mesa_GenTextures(1, view_tex_name);
96 view_tex_obj = _mesa_lookup_texture(ctx, *view_tex_name);
97 if (!view_tex_obj)
98 return false;
99
100 tex_format = _mesa_choose_texture_format(ctx, view_tex_obj, tex_obj->Target,
101 0, internal_format,
102 GL_NONE, GL_NONE);
103
104 if (!ctx->Driver.TestProxyTexImage(ctx, tex_obj->Target, 0, tex_format,
105 tex_image->Width, tex_image->Height,
106 tex_image->Depth, 0)) {
107 _mesa_DeleteTextures(1, view_tex_name);
108 *view_tex_name = 0;
109 return false;
110 }
111
112 assert(tex_obj->Target != 0);
113 assert(tex_obj->TargetIndex < NUM_TEXTURE_TARGETS);
114
115 view_tex_obj->Target = tex_obj->Target;
116 view_tex_obj->TargetIndex = tex_obj->TargetIndex;
117
118 *view_tex_image = _mesa_get_tex_image(ctx, view_tex_obj, tex_obj->Target, 0);
119
120 if (!*view_tex_image) {
121 _mesa_DeleteTextures(1, view_tex_name);
122 *view_tex_name = 0;
123 return false;
124 }
125
126 _mesa_init_teximage_fields(ctx, *view_tex_image,
127 tex_image->Width, tex_image->Height,
128 tex_image->Depth,
129 0, internal_format, tex_format);
130
131 view_tex_obj->MinLevel = tex_image->Level;
132 view_tex_obj->NumLevels = 1;
133 view_tex_obj->MinLayer = tex_obj->MinLayer;
134 view_tex_obj->NumLayers = tex_obj->NumLayers;
135 view_tex_obj->Immutable = tex_obj->Immutable;
136 view_tex_obj->ImmutableLevels = tex_obj->ImmutableLevels;
137
138 if (ctx->Driver.TextureView != NULL &&
139 !ctx->Driver.TextureView(ctx, view_tex_obj, tex_obj)) {
140 _mesa_DeleteTextures(1, view_tex_name);
141 *view_tex_name = 0;
142 return false; /* driver recorded error */
143 }
144
145 return true;
146 }
147
148 /** A partial implementation of glCopyImageSubData
149 *
150 * This is a partial implementation of glCopyImageSubData that works only
151 * if both textures are uncompressed and the destination texture is
152 * renderable. It uses a slight abuse of a texture view (see make_view) to
153 * turn the source texture into the destination texture type and then uses
154 * _mesa_meta_BlitFramebuffers to do the copy.
155 */
156 bool
157 _mesa_meta_CopyImageSubData_uncompressed(struct gl_context *ctx,
158 struct gl_texture_image *src_tex_image,
159 struct gl_renderbuffer *src_renderbuffer,
160 int src_x, int src_y, int src_z,
161 struct gl_texture_image *dst_tex_image,
162 struct gl_renderbuffer *dst_renderbuffer,
163 int dst_x, int dst_y, int dst_z,
164 int src_width, int src_height)
165 {
166 mesa_format src_format, dst_format;
167 GLint src_internal_format, dst_internal_format;
168 GLuint src_view_texture = 0;
169 struct gl_texture_image *src_view_tex_image;
170 struct gl_framebuffer *readFb;
171 struct gl_framebuffer *drawFb = NULL;
172 bool success = false;
173 GLbitfield mask;
174 GLenum status, attachment;
175
176 if (src_renderbuffer) {
177 src_format = src_renderbuffer->Format;
178 src_internal_format = src_renderbuffer->InternalFormat;
179 } else {
180 assert(src_tex_image);
181 src_format = src_tex_image->TexFormat;
182 src_internal_format = src_tex_image->InternalFormat;
183 }
184
185 if (dst_renderbuffer) {
186 dst_format = dst_renderbuffer->Format;
187 dst_internal_format = dst_renderbuffer->InternalFormat;
188 } else {
189 assert(dst_tex_image);
190 dst_format = dst_tex_image->TexFormat;
191 dst_internal_format = dst_tex_image->InternalFormat;
192 }
193
194 if (_mesa_is_format_compressed(src_format))
195 return false;
196
197 if (_mesa_is_format_compressed(dst_format))
198 return false;
199
200 if (src_internal_format == dst_internal_format) {
201 src_view_tex_image = src_tex_image;
202 } else {
203 if (src_renderbuffer) {
204 assert(src_tex_image == NULL);
205 src_tex_image = wrap_renderbuffer(ctx, src_renderbuffer);
206 }
207 if (!make_view(ctx, src_tex_image, &src_view_tex_image, &src_view_texture,
208 dst_internal_format))
209 goto cleanup;
210 }
211
212 /* We really only need to stash the bound framebuffers and scissor. */
213 _mesa_meta_begin(ctx, MESA_META_SCISSOR);
214
215 readFb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF);
216 if (readFb == NULL)
217 goto meta_end;
218
219 drawFb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF);
220 if (drawFb == NULL)
221 goto meta_end;
222
223 _mesa_bind_framebuffers(ctx, drawFb, readFb);
224
225 switch (_mesa_get_format_base_format(src_format)) {
226 case GL_DEPTH_COMPONENT:
227 attachment = GL_DEPTH_ATTACHMENT;
228 mask = GL_DEPTH_BUFFER_BIT;
229 break;
230 case GL_DEPTH_STENCIL:
231 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
232 mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
233 break;
234 case GL_STENCIL_INDEX:
235 attachment = GL_STENCIL_ATTACHMENT;
236 mask = GL_STENCIL_BUFFER_BIT;
237 break;
238 default:
239 attachment = GL_COLOR_ATTACHMENT0;
240 mask = GL_COLOR_BUFFER_BIT;
241 _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);
242 _mesa_ReadBuffer(GL_COLOR_ATTACHMENT0);
243 }
244
245 if (src_view_tex_image) {
246 /* Prefer the tex image because, even if we have a renderbuffer, we may
247 * have had to wrap it in a texture view.
248 */
249 _mesa_meta_framebuffer_texture_image(ctx, ctx->ReadBuffer, attachment,
250 src_view_tex_image, src_z);
251 } else {
252 _mesa_framebuffer_renderbuffer(ctx, ctx->ReadBuffer, attachment,
253 src_renderbuffer);
254 }
255
256 status = _mesa_check_framebuffer_status(ctx, ctx->ReadBuffer);
257 if (status != GL_FRAMEBUFFER_COMPLETE)
258 goto meta_end;
259
260 if (dst_renderbuffer) {
261 _mesa_framebuffer_renderbuffer(ctx, ctx->DrawBuffer, attachment,
262 dst_renderbuffer);
263 } else {
264 _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer, attachment,
265 dst_tex_image, dst_z);
266 }
267
268 status = _mesa_check_framebuffer_status(ctx, ctx->DrawBuffer);
269 if (status != GL_FRAMEBUFFER_COMPLETE)
270 goto meta_end;
271
272 /* Explicitly disable sRGB encoding */
273 ctx->DrawBuffer->Visual.sRGBCapable = false;
274
275 /* Since we've bound a new draw framebuffer, we need to update its
276 * derived state -- _Xmin, etc -- for BlitFramebuffer's clipping to
277 * be correct.
278 */
279 _mesa_update_state(ctx);
280
281 /* We skip the core BlitFramebuffer checks for format consistency.
282 * We have already created views to ensure that the texture formats
283 * match.
284 */
285 ctx->Driver.BlitFramebuffer(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
286 src_x, src_y,
287 src_x + src_width, src_y + src_height,
288 dst_x, dst_y,
289 dst_x + src_width, dst_y + src_height,
290 mask, GL_NEAREST);
291
292 success = true;
293
294 meta_end:
295 _mesa_reference_framebuffer(&readFb, NULL);
296 _mesa_reference_framebuffer(&drawFb, NULL);
297 _mesa_meta_end(ctx);
298
299 cleanup:
300 _mesa_DeleteTextures(1, &src_view_texture);
301
302 /* If we got a renderbuffer source, delete the temporary texture */
303 if (src_renderbuffer && src_tex_image)
304 ctx->Driver.DeleteTexture(ctx, src_tex_image->TexObject);
305
306 return success;
307 }