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#include "util/u_memory.h"
36
37static GLboolean
38set_renderbuffer_format(struct gl_renderbuffer *rb, GLenum internalFormat)
39{
40	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
41
42	rb->InternalFormat  = internalFormat;
43
44	switch (internalFormat) {
45	case GL_RGB:
46	case GL_RGB8:
47		rb->_BaseFormat  = GL_RGB;
48		rb->Format = MESA_FORMAT_B8G8R8X8_UNORM;
49		s->cpp = 4;
50		break;
51	case GL_RGBA:
52	case GL_RGBA8:
53		rb->_BaseFormat  = GL_RGBA;
54		rb->Format = MESA_FORMAT_B8G8R8A8_UNORM;
55		s->cpp = 4;
56		break;
57	case GL_RGB5:
58		rb->_BaseFormat  = GL_RGB;
59		rb->Format = MESA_FORMAT_B5G6R5_UNORM;
60		s->cpp = 2;
61		break;
62	case GL_DEPTH_COMPONENT16:
63		rb->_BaseFormat  = GL_DEPTH_COMPONENT;
64		rb->Format = MESA_FORMAT_Z_UNORM16;
65		s->cpp = 2;
66		break;
67	case GL_DEPTH_COMPONENT:
68	case GL_DEPTH_COMPONENT24:
69	case GL_STENCIL_INDEX8_EXT:
70	case GL_DEPTH24_STENCIL8_EXT:
71		rb->_BaseFormat  = GL_DEPTH_STENCIL;
72		rb->Format = MESA_FORMAT_S8_UINT_Z24_UNORM;
73		s->cpp = 4;
74		break;
75	default:
76		return GL_FALSE;
77	}
78
79	s->format = rb->Format;
80
81	return GL_TRUE;
82}
83
84static GLboolean
85nouveau_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
86			     GLenum internalFormat,
87			     GLuint width, GLuint height)
88{
89	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
90
91	if (!set_renderbuffer_format(rb, internalFormat))
92		return GL_FALSE;
93
94	rb->Width = width;
95	rb->Height = height;
96
97	nouveau_surface_alloc(ctx, s, TILED, NOUVEAU_BO_VRAM | NOUVEAU_BO_MAP,
98			      rb->Format, width, height);
99
100	context_dirty(ctx, FRAMEBUFFER);
101	return GL_TRUE;
102}
103
104static void
105nouveau_renderbuffer_del(struct gl_context *ctx, struct gl_renderbuffer *rb)
106{
107	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
108
109	nouveau_surface_ref(NULL, s);
110	_mesa_delete_renderbuffer(ctx, rb);
111}
112
113static struct gl_renderbuffer *
114nouveau_renderbuffer_new(struct gl_context *ctx, GLuint name)
115{
116	struct gl_renderbuffer *rb;
117
118	rb = (struct gl_renderbuffer *)
119		CALLOC_STRUCT(nouveau_renderbuffer);
120	if (!rb)
121		return NULL;
122
123	_mesa_init_renderbuffer(rb, name);
124
125	rb->AllocStorage = nouveau_renderbuffer_storage;
126	rb->Delete = nouveau_renderbuffer_del;
127
128	return rb;
129}
130
131static void
132nouveau_renderbuffer_map(struct gl_context *ctx,
133			 struct gl_renderbuffer *rb,
134			 GLuint x, GLuint y, GLuint w, GLuint h,
135			 GLbitfield mode,
136			 GLubyte **out_map,
137			 GLint *out_stride,
138			 bool flip_y)
139{
140	struct nouveau_surface *s = &to_nouveau_renderbuffer(rb)->surface;
141	GLubyte *map;
142	int stride;
143	int flags = 0;
144
145	/* driver does not support GL_FRAMEBUFFER_FLIP_Y_MESA */
146	assert((rb->Name == 0) == flip_y);
147
148	if (mode & GL_MAP_READ_BIT)
149		flags |= NOUVEAU_BO_RD;
150	if (mode & GL_MAP_WRITE_BIT)
151		flags |= NOUVEAU_BO_WR;
152
153	nouveau_bo_map(s->bo, flags, context_client(ctx));
154
155	map = s->bo->map;
156	stride = s->pitch;
157
158	if (rb->Name == 0) {
159		map += stride * (rb->Height - 1);
160		stride = -stride;
161	}
162
163	map += x * s->cpp;
164	map += (int)y * stride;
165
166	*out_map = map;
167	*out_stride = stride;
168}
169
170static void
171nouveau_renderbuffer_unmap(struct gl_context *ctx,
172			   struct gl_renderbuffer *rb)
173{
174}
175
176static GLboolean
177nouveau_renderbuffer_dri_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
178				 GLenum internalFormat,
179				 GLuint width, GLuint height)
180{
181	if (!set_renderbuffer_format(rb, internalFormat))
182		return GL_FALSE;
183
184	rb->Width = width;
185	rb->Height = height;
186
187	return GL_TRUE;
188}
189
190struct gl_renderbuffer *
191nouveau_renderbuffer_dri_new(GLenum format, __DRIdrawable *drawable)
192{
193	struct gl_renderbuffer *rb;
194
195	rb = nouveau_renderbuffer_new(NULL, 0);
196	if (!rb)
197		return NULL;
198
199	rb->AllocStorage = nouveau_renderbuffer_dri_storage;
200
201	if (!set_renderbuffer_format(rb, format)) {
202		nouveau_renderbuffer_del(NULL, rb);
203		return NULL;
204	}
205
206	return rb;
207}
208
209static struct gl_framebuffer *
210nouveau_framebuffer_new(struct gl_context *ctx, GLuint name)
211{
212	struct nouveau_framebuffer *nfb;
213
214	nfb = CALLOC_STRUCT(nouveau_framebuffer);
215	if (!nfb)
216		return NULL;
217
218	_mesa_initialize_user_framebuffer(&nfb->base, name);
219
220	return &nfb->base;
221}
222
223struct gl_framebuffer *
224nouveau_framebuffer_dri_new(const struct gl_config *visual)
225{
226	struct nouveau_framebuffer *nfb;
227
228	nfb = CALLOC_STRUCT(nouveau_framebuffer);
229	if (!nfb)
230		return NULL;
231
232	_mesa_initialize_window_framebuffer(&nfb->base, visual);
233	nfb->need_front = !visual->doubleBufferMode;
234
235	return &nfb->base;
236}
237
238static void
239nouveau_bind_framebuffer(struct gl_context *ctx, GLenum target,
240			 struct gl_framebuffer *dfb,
241			 struct gl_framebuffer *rfb)
242{
243	context_dirty(ctx, FRAMEBUFFER);
244}
245
246static void
247nouveau_framebuffer_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb,
248				 GLenum attachment, struct gl_renderbuffer *rb)
249{
250	_mesa_FramebufferRenderbuffer_sw(ctx, fb, attachment, rb);
251
252	context_dirty(ctx, FRAMEBUFFER);
253}
254
255static void
256nouveau_render_texture(struct gl_context *ctx, struct gl_framebuffer *fb,
257		       struct gl_renderbuffer_attachment *att)
258{
259	struct gl_renderbuffer *rb = att->Renderbuffer;
260	struct gl_texture_image *ti = rb->TexImage;
261
262	/* Update the renderbuffer fields from the texture. */
263	nouveau_surface_ref(&to_nouveau_teximage(ti)->surface,
264			    &to_nouveau_renderbuffer(rb)->surface);
265
266	context_dirty(ctx, FRAMEBUFFER);
267}
268
269static void
270nouveau_finish_render_texture(struct gl_context *ctx,
271			      struct gl_renderbuffer *rb)
272{
273	if (rb && rb->TexImage)
274		texture_dirty(rb->TexImage->TexObject);
275}
276
277static int
278validate_format_bpp(mesa_format format)
279{
280	switch (format) {
281	case MESA_FORMAT_B8G8R8X8_UNORM:
282	case MESA_FORMAT_B8G8R8A8_UNORM:
283	case MESA_FORMAT_S8_UINT_Z24_UNORM:
284		return 32;
285	case MESA_FORMAT_B5G6R5_UNORM:
286	case MESA_FORMAT_Z_UNORM16:
287		return 16;
288	default:
289		return 0;
290	}
291}
292
293static void
294nouveau_check_framebuffer_complete(struct gl_context *ctx,
295				   struct gl_framebuffer *fb)
296{
297	struct gl_renderbuffer_attachment *color =
298		&fb->Attachment[BUFFER_COLOR0];
299	struct gl_renderbuffer_attachment *depth =
300		&fb->Attachment[BUFFER_DEPTH];
301	int color_bpp = 0, zeta_bpp;
302
303	if (color->Type == GL_TEXTURE) {
304		color_bpp = validate_format_bpp(
305				color->Renderbuffer->TexImage->TexFormat);
306		if (!color_bpp)
307			goto err;
308	}
309
310	if (depth->Type == GL_TEXTURE) {
311		zeta_bpp = validate_format_bpp(
312				depth->Renderbuffer->TexImage->TexFormat);
313		if (!zeta_bpp)
314			goto err;
315		/* NV04/NV05 requires same bpp-ness for color/zeta */
316		if (context_chipset(ctx) < 0x10 &&
317		    color_bpp && color_bpp != zeta_bpp)
318			goto err;
319	}
320
321	return;
322err:
323	fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
324	return;
325}
326
327void
328nouveau_fbo_functions_init(struct dd_function_table *functions)
329{
330	functions->NewFramebuffer = nouveau_framebuffer_new;
331	functions->NewRenderbuffer = nouveau_renderbuffer_new;
332	functions->MapRenderbuffer = nouveau_renderbuffer_map;
333	functions->UnmapRenderbuffer = nouveau_renderbuffer_unmap;
334	functions->BindFramebuffer = nouveau_bind_framebuffer;
335	functions->FramebufferRenderbuffer = nouveau_framebuffer_renderbuffer;
336	functions->RenderTexture = nouveau_render_texture;
337	functions->FinishRenderTexture = nouveau_finish_render_texture;
338	functions->ValidateFramebuffer = nouveau_check_framebuffer_complete;
339}
340