1/*
2 * Copyright (C) 2009 Francisco Jerez.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "nouveau_driver.h"
28#include "nouveau_fbo.h"
29#include "nouveau_context.h"
30#include "nouveau_texture.h"
31
32#include "main/framebuffer.h"
33#include "main/renderbuffer.h"
34#include "main/fbobject.h"
35
36static GLboolean
37set_renderbuffer_format(struct gl_renderbuffer *rb, GLenum internalFormat)
38{
39	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
40
41	rb->InternalFormat  = internalFormat;
42
43	switch (internalFormat) {
44	case GL_RGB:
45	case GL_RGB8:
46		rb->_BaseFormat  = GL_RGB;
47		rb->Format = MESA_FORMAT_B8G8R8X8_UNORM;
48		s->cpp = 4;
49		break;
50	case GL_RGBA:
51	case GL_RGBA8:
52		rb->_BaseFormat  = GL_RGBA;
53		rb->Format = MESA_FORMAT_B8G8R8A8_UNORM;
54		s->cpp = 4;
55		break;
56	case GL_RGB5:
57		rb->_BaseFormat  = GL_RGB;
58		rb->Format = MESA_FORMAT_B5G6R5_UNORM;
59		s->cpp = 2;
60		break;
61	case GL_DEPTH_COMPONENT16:
62		rb->_BaseFormat  = GL_DEPTH_COMPONENT;
63		rb->Format = MESA_FORMAT_Z_UNORM16;
64		s->cpp = 2;
65		break;
66	case GL_DEPTH_COMPONENT:
67	case GL_DEPTH_COMPONENT24:
68	case GL_STENCIL_INDEX8_EXT:
69	case GL_DEPTH24_STENCIL8_EXT:
70		rb->_BaseFormat  = GL_DEPTH_STENCIL;
71		rb->Format = MESA_FORMAT_S8_UINT_Z24_UNORM;
72		s->cpp = 4;
73		break;
74	default:
75		return GL_FALSE;
76	}
77
78	s->format = rb->Format;
79
80	return GL_TRUE;
81}
82
83static GLboolean
84nouveau_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
85			     GLenum internalFormat,
86			     GLuint width, GLuint height)
87{
88	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
89
90	if (!set_renderbuffer_format(rb, internalFormat))
91		return GL_FALSE;
92
93	rb->Width = width;
94	rb->Height = height;
95
96	nouveau_surface_alloc(ctx, s, TILED, NOUVEAU_BO_VRAM | NOUVEAU_BO_MAP,
97			      rb->Format, width, height);
98
99	context_dirty(ctx, FRAMEBUFFER);
100	return GL_TRUE;
101}
102
103static void
104nouveau_renderbuffer_del(struct gl_context *ctx, struct gl_renderbuffer *rb)
105{
106	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
107
108	nouveau_surface_ref(NULL, s);
109	_mesa_delete_renderbuffer(ctx, rb);
110}
111
112static struct gl_renderbuffer *
113nouveau_renderbuffer_new(struct gl_context *ctx, GLuint name)
114{
115	struct gl_renderbuffer *rb;
116
117	rb = (struct gl_renderbuffer *)
118		CALLOC_STRUCT(nouveau_renderbuffer);
119	if (!rb)
120		return NULL;
121
122	_mesa_init_renderbuffer(rb, name);
123
124	rb->AllocStorage = nouveau_renderbuffer_storage;
125	rb->Delete = nouveau_renderbuffer_del;
126
127	return rb;
128}
129
130static void
131nouveau_renderbuffer_map(struct gl_context *ctx,
132			 struct gl_renderbuffer *rb,
133			 GLuint x, GLuint y, GLuint w, GLuint h,
134			 GLbitfield mode,
135			 GLubyte **out_map,
136			 GLint *out_stride,
137			 bool flip_y)
138{
139	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
140	GLubyte *map;
141	int stride;
142	int flags = 0;
143
144	/* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
145	assert((rb->Name == 0) == flip_y);
146
147	if (mode & GL_MAP_READ_BIT)
148		flags |= NOUVEAU_BO_RD;
149	if (mode & GL_MAP_WRITE_BIT)
150		flags |= NOUVEAU_BO_WR;
151
152	nouveau_bo_map(s->bo, flags, context_client(ctx));
153
154	map = s->bo->map;
155	stride = s->pitch;
156
157	if (rb->Name == 0) {
158		map += stride * (rb->Height - 1);
159		stride = -stride;
160	}
161
162	map += x * s->cpp;
163	map += (int)y * stride;
164
165	*out_map = map;
166	*out_stride = stride;
167}
168
169static void
170nouveau_renderbuffer_unmap(struct gl_context *ctx,
171			   struct gl_renderbuffer *rb)
172{
173}
174
175static GLboolean
176nouveau_renderbuffer_dri_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
177				 GLenum internalFormat,
178				 GLuint width, GLuint height)
179{
180	if (!set_renderbuffer_format(rb, internalFormat))
181		return GL_FALSE;
182
183	rb->Width = width;
184	rb->Height = height;
185
186	return GL_TRUE;
187}
188
189struct gl_renderbuffer *
190nouveau_renderbuffer_dri_new(GLenum format, __DRIdrawable *drawable)
191{
192	struct gl_renderbuffer *rb;
193
194	rb = nouveau_renderbuffer_new(NULL, 0);
195	if (!rb)
196		return NULL;
197
198	rb->AllocStorage = nouveau_renderbuffer_dri_storage;
199
200	if (!set_renderbuffer_format(rb, format)) {
201		nouveau_renderbuffer_del(NULL, rb);
202		return NULL;
203	}
204
205	return rb;
206}
207
208static struct gl_framebuffer *
209nouveau_framebuffer_new(struct gl_context *ctx, GLuint name)
210{
211	struct nouveau_framebuffer *nfb;
212
213	nfb = CALLOC_STRUCT(nouveau_framebuffer);
214	if (!nfb)
215		return NULL;
216
217	_mesa_initialize_user_framebuffer(&nfb->base, name);
218
219	return &nfb->base;
220}
221
222struct gl_framebuffer *
223nouveau_framebuffer_dri_new(const struct gl_config *visual)
224{
225	struct nouveau_framebuffer *nfb;
226
227	nfb = CALLOC_STRUCT(nouveau_framebuffer);
228	if (!nfb)
229		return NULL;
230
231	_mesa_initialize_window_framebuffer(&nfb->base, visual);
232	nfb->need_front = !visual->doubleBufferMode;
233
234	return &nfb->base;
235}
236
237static void
238nouveau_bind_framebuffer(struct gl_context *ctx, GLenum target,
239			 struct gl_framebuffer *dfb,
240			 struct gl_framebuffer *rfb)
241{
242	context_dirty(ctx, FRAMEBUFFER);
243}
244
245static void
246nouveau_framebuffer_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb,
247				 GLenum attachment, struct gl_renderbuffer *rb)
248{
249	_mesa_FramebufferRenderbuffer_sw(ctx, fb, attachment, rb);
250
251	context_dirty(ctx, FRAMEBUFFER);
252}
253
254static void
255nouveau_render_texture(struct gl_context *ctx, struct gl_framebuffer *fb,
256		       struct gl_renderbuffer_attachment *att)
257{
258	struct gl_renderbuffer *rb = att->Renderbuffer;
259	struct gl_texture_image *ti = rb->TexImage;
260
261	/* Update the renderbuffer fields from the texture. */
262	nouveau_surface_ref(&to_nouveau_teximage(ti)->surface,
263			    &to_nouveau_renderbuffer(rb)->surface);
264
265	context_dirty(ctx, FRAMEBUFFER);
266}
267
268static void
269nouveau_finish_render_texture(struct gl_context *ctx,
270			      struct gl_renderbuffer *rb)
271{
272	if (rb && rb->TexImage)
273		texture_dirty(rb->TexImage->TexObject);
274}
275
276static int
277validate_format_bpp(mesa_format format)
278{
279	switch (format) {
280	case MESA_FORMAT_B8G8R8X8_UNORM:
281	case MESA_FORMAT_B8G8R8A8_UNORM:
282	case MESA_FORMAT_S8_UINT_Z24_UNORM:
283		return 32;
284	case MESA_FORMAT_B5G6R5_UNORM:
285	case MESA_FORMAT_Z_UNORM16:
286		return 16;
287	default:
288		return 0;
289	}
290}
291
292static void
293nouveau_check_framebuffer_complete(struct gl_context *ctx,
294				   struct gl_framebuffer *fb)
295{
296	struct gl_renderbuffer_attachment *color =
297		&fb->Attachment[BUFFER_COLOR0];
298	struct gl_renderbuffer_attachment *depth =
299		&fb->Attachment[BUFFER_DEPTH];
300	int color_bpp = 0, zeta_bpp;
301
302	if (color->Type == GL_TEXTURE) {
303		color_bpp = validate_format_bpp(
304				color->Renderbuffer->TexImage->TexFormat);
305		if (!color_bpp)
306			goto err;
307	}
308
309	if (depth->Type == GL_TEXTURE) {
310		zeta_bpp = validate_format_bpp(
311				depth->Renderbuffer->TexImage->TexFormat);
312		if (!zeta_bpp)
313			goto err;
314		/* NV04/NV05 requires same bpp-ness for color/zeta */
315		if (context_chipset(ctx) < 0x10 &&
316		    color_bpp && color_bpp != zeta_bpp)
317			goto err;
318	}
319
320	return;
321err:
322	fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
323	return;
324}
325
326void
327nouveau_fbo_functions_init(struct dd_function_table *functions)
328{
329	functions->NewFramebuffer = nouveau_framebuffer_new;
330	functions->NewRenderbuffer = nouveau_renderbuffer_new;
331	functions->MapRenderbuffer = nouveau_renderbuffer_map;
332	functions->UnmapRenderbuffer = nouveau_renderbuffer_unmap;
333	functions->BindFramebuffer = nouveau_bind_framebuffer;
334	functions->FramebufferRenderbuffer = nouveau_framebuffer_renderbuffer;
335	functions->RenderTexture = nouveau_render_texture;
336	functions->FinishRenderTexture = nouveau_finish_render_texture;
337	functions->ValidateFramebuffer = nouveau_check_framebuffer_complete;
338}
339