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