meta: Infer bind_fbo_image parameters from an incoming image.
[mesa.git] / src / mesa / drivers / common / meta_generate_mipmap.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2009 VMware, Inc. 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 /**
26 * Meta operations. Some GL operations can be expressed in terms of
27 * other GL operations. For example, glBlitFramebuffer() can be done
28 * with texture mapping and glClear() can be done with polygon rendering.
29 *
30 * \author Brian Paul
31 */
32
33 #include "main/arrayobj.h"
34 #include "main/buffers.h"
35 #include "main/enums.h"
36 #include "main/enable.h"
37 #include "main/fbobject.h"
38 #include "main/macros.h"
39 #include "main/mipmap.h"
40 #include "main/teximage.h"
41 #include "main/texobj.h"
42 #include "main/texparam.h"
43 #include "main/varray.h"
44 #include "main/viewport.h"
45 #include "drivers/common/meta.h"
46
47
48 /**
49 * Check if the call to _mesa_meta_GenerateMipmap() will require a
50 * software fallback. The fallback path will require that the texture
51 * images are mapped.
52 * \return GL_TRUE if a fallback is needed, GL_FALSE otherwise
53 */
54 static bool
55 fallback_required(struct gl_context *ctx, GLenum target,
56 struct gl_texture_object *texObj)
57 {
58 const GLuint fboSave = ctx->DrawBuffer->Name;
59 struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
60 struct gl_texture_image *baseImage;
61 GLuint srcLevel;
62 GLenum status;
63
64 /* check for fallbacks */
65 if (target == GL_TEXTURE_3D) {
66 _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
67 "glGenerateMipmap() to %s target\n",
68 _mesa_lookup_enum_by_nr(target));
69 return true;
70 }
71
72 srcLevel = texObj->BaseLevel;
73 baseImage = _mesa_select_tex_image(ctx, texObj, target, srcLevel);
74 if (!baseImage) {
75 _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
76 "glGenerateMipmap() couldn't find base teximage\n");
77 return true;
78 }
79
80 if (_mesa_is_format_compressed(baseImage->TexFormat)) {
81 _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
82 "glGenerateMipmap() with %s format\n",
83 _mesa_get_format_name(baseImage->TexFormat));
84 return true;
85 }
86
87 if (_mesa_get_format_color_encoding(baseImage->TexFormat) == GL_SRGB &&
88 !ctx->Extensions.EXT_texture_sRGB_decode) {
89 /* The texture format is sRGB but we can't turn off sRGB->linear
90 * texture sample conversion. So we won't be able to generate the
91 * right colors when rendering. Need to use a fallback.
92 */
93 _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
94 "glGenerateMipmap() of sRGB texture without "
95 "sRGB decode\n");
96 return true;
97 }
98
99 /*
100 * Test that we can actually render in the texture's format.
101 */
102 if (!mipmap->FBO)
103 _mesa_GenFramebuffers(1, &mipmap->FBO);
104 _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, mipmap->FBO);
105
106 _mesa_meta_bind_fbo_image(baseImage, 0);
107
108 status = _mesa_CheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
109
110 _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, fboSave);
111
112 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
113 _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
114 "glGenerateMipmap() got incomplete FBO\n");
115 return true;
116 }
117
118 return false;
119 }
120
121 void
122 _mesa_meta_glsl_generate_mipmap_cleanup(struct gen_mipmap_state *mipmap)
123 {
124 if (mipmap->VAO == 0)
125 return;
126 _mesa_DeleteVertexArrays(1, &mipmap->VAO);
127 mipmap->VAO = 0;
128 _mesa_DeleteBuffers(1, &mipmap->VBO);
129 mipmap->VBO = 0;
130
131 _mesa_meta_blit_shader_table_cleanup(&mipmap->shaders);
132 }
133
134 static GLboolean
135 prepare_mipmap_level(struct gl_context *ctx,
136 struct gl_texture_object *texObj, GLuint level,
137 GLsizei width, GLsizei height, GLsizei depth,
138 GLenum intFormat, mesa_format format)
139 {
140 if (texObj->Target == GL_TEXTURE_1D_ARRAY) {
141 /* Work around Mesa expecting the number of array slices in "height". */
142 height = depth;
143 depth = 1;
144 }
145
146 return _mesa_prepare_mipmap_level(ctx, texObj, level, width, height, depth,
147 0, intFormat, format);
148 }
149
150 /**
151 * Called via ctx->Driver.GenerateMipmap()
152 * Note: We don't yet support 3D textures, 1D/2D array textures or texture
153 * borders.
154 */
155 void
156 _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
157 struct gl_texture_object *texObj)
158 {
159 struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
160 struct vertex verts[4];
161 const GLuint baseLevel = texObj->BaseLevel;
162 const GLuint maxLevel = texObj->MaxLevel;
163 const GLint maxLevelSave = texObj->MaxLevel;
164 const GLboolean genMipmapSave = texObj->GenerateMipmap;
165 const GLuint currentTexUnitSave = ctx->Texture.CurrentUnit;
166 const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
167 ctx->Extensions.ARB_fragment_shader;
168 GLenum faceTarget;
169 GLuint dstLevel;
170 GLuint samplerSave;
171
172 if (fallback_required(ctx, target, texObj)) {
173 _mesa_generate_mipmap(ctx, target, texObj);
174 return;
175 }
176
177 if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
178 target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
179 faceTarget = target;
180 target = GL_TEXTURE_CUBE_MAP;
181 } else {
182 faceTarget = target;
183 }
184
185 _mesa_meta_begin(ctx, MESA_META_ALL);
186
187 /* Choose between glsl version and fixed function version of
188 * GenerateMipmap function.
189 */
190 if (use_glsl_version) {
191 _mesa_meta_setup_vertex_objects(&mipmap->VAO, &mipmap->VBO, true,
192 2, 3, 0);
193 _mesa_meta_setup_blit_shader(ctx, target, &mipmap->shaders);
194 } else {
195 _mesa_meta_setup_ff_tnl_for_blit(&mipmap->VAO, &mipmap->VBO, 3);
196 _mesa_set_enable(ctx, target, GL_TRUE);
197 }
198
199 samplerSave = ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
200 ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
201
202 if (currentTexUnitSave != 0)
203 _mesa_BindTexture(target, texObj->Name);
204
205 if (!mipmap->Sampler) {
206 _mesa_GenSamplers(1, &mipmap->Sampler);
207 _mesa_BindSampler(ctx->Texture.CurrentUnit, mipmap->Sampler);
208
209 _mesa_SamplerParameteri(mipmap->Sampler,
210 GL_TEXTURE_MIN_FILTER,
211 GL_LINEAR_MIPMAP_LINEAR);
212 _mesa_SamplerParameteri(mipmap->Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
213 _mesa_SamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
214 _mesa_SamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
215 _mesa_SamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
216
217 /* We don't want to encode or decode sRGB values; treat them as linear.
218 * This is not technically correct for GLES3 but we don't get any API
219 * error at the moment.
220 */
221 if (ctx->Extensions.EXT_texture_sRGB_decode) {
222 _mesa_SamplerParameteri(mipmap->Sampler, GL_TEXTURE_SRGB_DECODE_EXT,
223 GL_SKIP_DECODE_EXT);
224 }
225 } else {
226 _mesa_BindSampler(ctx->Texture.CurrentUnit, mipmap->Sampler);
227 }
228
229 assert(mipmap->FBO != 0);
230 _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, mipmap->FBO);
231
232 _mesa_TexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE);
233
234 /* Silence valgrind warnings about reading uninitialized stack. */
235 memset(verts, 0, sizeof(verts));
236
237 /* setup vertex positions */
238 verts[0].x = -1.0F;
239 verts[0].y = -1.0F;
240 verts[1].x = 1.0F;
241 verts[1].y = -1.0F;
242 verts[2].x = 1.0F;
243 verts[2].y = 1.0F;
244 verts[3].x = -1.0F;
245 verts[3].y = 1.0F;
246
247 /* texture is already locked, unlock now */
248 _mesa_unlock_texture(ctx, texObj);
249
250 for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) {
251 const struct gl_texture_image *srcImage;
252 struct gl_texture_image *dstImage;
253 const GLuint srcLevel = dstLevel - 1;
254 GLuint layer;
255 GLsizei srcWidth, srcHeight, srcDepth;
256 GLsizei dstWidth, dstHeight, dstDepth;
257
258 srcImage = _mesa_select_tex_image(ctx, texObj, faceTarget, srcLevel);
259 assert(srcImage->Border == 0);
260
261 /* src size */
262 srcWidth = srcImage->Width;
263 if (target == GL_TEXTURE_1D_ARRAY) {
264 srcHeight = 1;
265 srcDepth = srcImage->Height;
266 } else {
267 srcHeight = srcImage->Height;
268 srcDepth = srcImage->Depth;
269 }
270
271 /* new dst size */
272 dstWidth = minify(srcWidth, 1);
273 dstHeight = minify(srcHeight, 1);
274 dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth;
275
276 if (dstWidth == srcWidth &&
277 dstHeight == srcHeight &&
278 dstDepth == srcDepth) {
279 /* all done */
280 break;
281 }
282
283 /* Allocate storage for the destination mipmap image(s) */
284
285 /* Set MaxLevel large enough to hold the new level when we allocate it */
286 _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, dstLevel);
287
288 if (!prepare_mipmap_level(ctx, texObj, dstLevel,
289 dstWidth, dstHeight, dstDepth,
290 srcImage->InternalFormat,
291 srcImage->TexFormat)) {
292 /* All done. We either ran out of memory or we would go beyond the
293 * last valid level of an immutable texture if we continued.
294 */
295 break;
296 }
297 dstImage = _mesa_select_tex_image(ctx, texObj, faceTarget, dstLevel);
298
299 /* limit minification to src level */
300 _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, srcLevel);
301
302 /* setup viewport */
303 _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight);
304 _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);
305
306 for (layer = 0; layer < dstDepth; ++layer) {
307 /* Setup texture coordinates */
308 _mesa_meta_setup_texture_coords(faceTarget,
309 layer,
310 0, 0, 1, /* width, height never used here */
311 verts[0].tex,
312 verts[1].tex,
313 verts[2].tex,
314 verts[3].tex);
315
316 /* upload vertex data */
317 _mesa_BufferData(GL_ARRAY_BUFFER_ARB, sizeof(verts),
318 verts, GL_DYNAMIC_DRAW_ARB);
319
320 _mesa_meta_bind_fbo_image(dstImage, layer);
321
322 /* sanity check */
323 if (_mesa_CheckFramebufferStatus(GL_FRAMEBUFFER) !=
324 GL_FRAMEBUFFER_COMPLETE) {
325 _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
326 "_mesa_meta_GenerateMipmap()");
327 break;
328 }
329
330 assert(dstWidth == ctx->DrawBuffer->Width);
331 if (target == GL_TEXTURE_1D_ARRAY) {
332 assert(dstHeight == 1);
333 } else {
334 assert(dstHeight == ctx->DrawBuffer->Height);
335 }
336
337 _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
338 }
339 }
340
341 _mesa_lock_texture(ctx, texObj); /* relock */
342
343 _mesa_BindSampler(ctx->Texture.CurrentUnit, samplerSave);
344
345 _mesa_meta_end(ctx);
346
347 _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, maxLevelSave);
348 if (genMipmapSave)
349 _mesa_TexParameteri(target, GL_GENERATE_MIPMAP, genMipmapSave);
350 }