2 * Windows (Win32/Win64) device driver for Mesa
10 #include "main/context.h"
11 #include "main/extensions.h"
12 #include "main/framebuffer.h"
13 #include "main/renderbuffer.h"
14 #include "main/macros.h"
15 #include "main/version.h"
16 #include "main/vtxfmt.h"
17 #include "drivers/common/driverfuncs.h"
18 #include "drivers/common/meta.h"
20 #include "swrast/swrast.h"
21 #include "swrast/s_renderbuffer.h"
22 #include "swrast_setup/swrast_setup.h"
24 #include "tnl/t_context.h"
25 #include "tnl/t_pipeline.h"
28 /* linked list of our Framebuffers (windows) */
29 static WMesaFramebuffer FirstFramebuffer = NULL;
33 * Create a new WMesaFramebuffer object which will correspond to the
34 * given HDC (Window handle).
36 static WMesaFramebuffer
37 wmesa_new_framebuffer(HDC hdc, struct gl_config *visual)
40 = malloc(sizeof(struct wmesa_framebuffer));
42 _mesa_initialize_window_framebuffer(&pwfb->Base, visual);
44 /* insert at head of list */
45 pwfb->next = FirstFramebuffer;
46 FirstFramebuffer = pwfb;
52 * Given an hdc, free the corresponding WMesaFramebuffer
55 wmesa_free_framebuffer(HDC hdc)
57 WMesaFramebuffer pwfb, prev;
58 for (pwfb = FirstFramebuffer; pwfb; pwfb = pwfb->next) {
64 struct gl_framebuffer *fb;
65 if (pwfb == FirstFramebuffer)
66 FirstFramebuffer = pwfb->next;
68 prev->next = pwfb->next;
70 _mesa_reference_framebuffer(&fb, NULL);
75 * Given an hdc, return the corresponding WMesaFramebuffer
77 static WMesaFramebuffer
78 wmesa_lookup_framebuffer(HDC hdc)
80 WMesaFramebuffer pwfb;
81 for (pwfb = FirstFramebuffer; pwfb; pwfb = pwfb->next) {
90 * Given a struct gl_framebuffer, return the corresponding WMesaFramebuffer.
92 static WMesaFramebuffer wmesa_framebuffer(struct gl_framebuffer *fb)
94 return (WMesaFramebuffer) fb;
99 * Given a struct gl_context, return the corresponding WMesaContext.
101 static WMesaContext wmesa_context(const struct gl_context *ctx)
103 return (WMesaContext) ctx;
108 * Every driver should implement a GetString function in order to
109 * return a meaningful GL_RENDERER string.
111 static const GLubyte *wmesa_get_string(struct gl_context *ctx, GLenum name)
113 return (name == GL_RENDERER) ?
114 (GLubyte *) "Mesa Windows GDI Driver" : NULL;
119 * Determine the pixel format based on the pixel size.
121 static void wmSetPixelFormat(WMesaFramebuffer pwfb, HDC hDC)
123 pwfb->cColorBits = GetDeviceCaps(hDC, BITSPIXEL);
125 /* Only 16 and 32 bit targets are supported now */
126 assert(pwfb->cColorBits == 0 ||
127 pwfb->cColorBits == 16 ||
128 pwfb->cColorBits == 24 ||
129 pwfb->cColorBits == 32);
131 switch(pwfb->cColorBits){
133 pwfb->pixelformat = PF_INDEX8;
136 pwfb->pixelformat = PF_5R6G5B;
140 pwfb->pixelformat = PF_8R8G8B;
143 pwfb->pixelformat = PF_BADFORMAT;
149 * Create DIB for back buffer.
150 * We write into this memory with the span routines and then blit it
151 * to the window on a buffer swap.
153 static BOOL wmCreateBackingStore(WMesaFramebuffer pwfb, long lxSize, long lySize)
155 LPBITMAPINFO pbmi = &(pwfb->bmi);
158 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
159 pbmi->bmiHeader.biWidth = lxSize;
160 pbmi->bmiHeader.biHeight= -lySize;
161 pbmi->bmiHeader.biPlanes = 1;
162 pbmi->bmiHeader.biBitCount = GetDeviceCaps(pwfb->hDC, BITSPIXEL);
163 pbmi->bmiHeader.biCompression = BI_RGB;
164 pbmi->bmiHeader.biSizeImage = 0;
165 pbmi->bmiHeader.biXPelsPerMeter = 0;
166 pbmi->bmiHeader.biYPelsPerMeter = 0;
167 pbmi->bmiHeader.biClrUsed = 0;
168 pbmi->bmiHeader.biClrImportant = 0;
170 pwfb->cColorBits = pbmi->bmiHeader.biBitCount;
171 pwfb->ScanWidth = (lxSize * (pwfb->cColorBits / 8) + 3) & ~3;
173 hic = CreateIC("display", NULL, NULL, NULL);
174 pwfb->dib_hDC = CreateCompatibleDC(hic);
176 pwfb->hbmDIB = CreateDIBSection(hic,
179 (void **)&(pwfb->pbPixels),
182 pwfb->hOldBitmap = SelectObject(pwfb->dib_hDC, pwfb->hbmDIB);
186 wmSetPixelFormat(pwfb, pwfb->hDC);
191 static void wmDeleteBackingStore(WMesaFramebuffer pwfb)
194 SelectObject(pwfb->dib_hDC, pwfb->hOldBitmap);
195 DeleteDC(pwfb->dib_hDC);
196 DeleteObject(pwfb->hbmDIB);
202 * Find the width and height of the window named by hdc.
205 get_window_size(HDC hdc, GLuint *width, GLuint *height)
207 if (WindowFromDC(hdc)) {
209 GetClientRect(WindowFromDC(hdc), &rect);
210 *width = rect.right - rect.left;
211 *height = rect.bottom - rect.top;
213 else { /* Memory context */
214 /* From contributed code - use the size of the desktop
215 * for the size of a memory context (?) */
216 *width = GetDeviceCaps(hdc, HORZRES);
217 *height = GetDeviceCaps(hdc, VERTRES);
223 wmesa_get_buffer_size(struct gl_framebuffer *buffer, GLuint *width, GLuint *height)
225 WMesaFramebuffer pwfb = wmesa_framebuffer(buffer);
226 get_window_size(pwfb->hDC, width, height);
230 static void wmesa_flush(struct gl_context *ctx)
232 WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->WinSysDrawBuffer);
234 if (ctx->Visual.doubleBufferMode == 1) {
235 BitBlt(pwfb->hDC, 0, 0, pwfb->Base.Width, pwfb->Base.Height,
236 pwfb->dib_hDC, 0, 0, SRCCOPY);
239 /* Do nothing for single buffer */
244 /**********************************************************************/
245 /***** CLEAR Functions *****/
246 /**********************************************************************/
249 * Clear the color/depth/stencil buffers.
251 static void clear(struct gl_context *ctx, GLbitfield mask)
253 #define FLIP(Y) (ctx->DrawBuffer->Height - (Y) - 1)
254 const GLint x = ctx->DrawBuffer->_Xmin;
255 const GLint y = ctx->DrawBuffer->_Ymin;
256 const GLint height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
257 const GLint width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
259 WMesaContext pwc = wmesa_context(ctx);
260 WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
263 /* Let swrast do all the work if the masks are not set to
264 * clear all channels. */
265 if (!ctx->Color.ColorMask[0][0] ||
266 !ctx->Color.ColorMask[0][1] ||
267 !ctx->Color.ColorMask[0][2] ||
268 !ctx->Color.ColorMask[0][3]) {
269 _swrast_Clear(ctx, mask);
273 if (mask & BUFFER_BITS_COLOR) {
274 /* setup the clearing color */
275 const union gl_color_union color = ctx->Color.ClearColor;
277 UNCLAMPED_FLOAT_TO_UBYTE(col[0], color.f[0]);
278 UNCLAMPED_FLOAT_TO_UBYTE(col[1], color.f[1]);
279 UNCLAMPED_FLOAT_TO_UBYTE(col[2], color.f[2]);
280 pwc->clearColorRef = RGB(col[0], col[1], col[2]);
281 DeleteObject(pwc->clearPen);
282 DeleteObject(pwc->clearBrush);
283 pwc->clearPen = CreatePen(PS_SOLID, 1, pwc->clearColorRef);
284 pwc->clearBrush = CreateSolidBrush(pwc->clearColorRef);
288 if (mask & BUFFER_BIT_BACK_LEFT) {
291 UINT bytesPerPixel = pwfb->cColorBits / 8;
292 LPBYTE lpb, clearRow;
300 /* Try for a fast clear - clearing entire buffer with a single
302 if (width == ctx->DrawBuffer->Width &&
303 height == ctx->DrawBuffer->Height) { /* entire buffer */
304 /* Now check for an easy clear value */
305 switch (bytesPerPixel) {
307 bColor = BGR8(GetRValue(pwc->clearColorRef),
308 GetGValue(pwc->clearColorRef),
309 GetBValue(pwc->clearColorRef));
310 memset(pwfb->pbPixels, bColor,
311 pwfb->ScanWidth * height);
315 wColor = BGR16(GetRValue(pwc->clearColorRef),
316 GetGValue(pwc->clearColorRef),
317 GetBValue(pwc->clearColorRef));
318 if (((wColor >> 8) & 0xff) == (wColor & 0xff)) {
319 memset(pwfb->pbPixels, wColor & 0xff,
320 pwfb->ScanWidth * height);
327 if (GetRValue(pwc->clearColorRef) ==
328 GetGValue(pwc->clearColorRef) &&
329 GetRValue(pwc->clearColorRef) ==
330 GetBValue(pwc->clearColorRef)) {
331 memset(pwfb->pbPixels,
332 GetRValue(pwc->clearColorRef),
333 pwfb->ScanWidth * height);
343 /* Need to clear a row at a time. Begin by setting the first
344 * row in the area to be cleared to the clear color. */
346 clearRow = pwfb->pbPixels +
347 pwfb->ScanWidth * FLIP(y) +
349 switch (bytesPerPixel) {
352 bColor = BGR8(GetRValue(pwc->clearColorRef),
353 GetGValue(pwc->clearColorRef),
354 GetBValue(pwc->clearColorRef));
355 memset(lpb, bColor, width);
358 lpw = (LPWORD)clearRow;
359 wColor = BGR16(GetRValue(pwc->clearColorRef),
360 GetGValue(pwc->clearColorRef),
361 GetBValue(pwc->clearColorRef));
362 for (i=0; i<width; i++)
367 r = GetRValue(pwc->clearColorRef);
368 g = GetGValue(pwc->clearColorRef);
369 b = GetBValue(pwc->clearColorRef);
370 for (i=0; i<width; i++) {
377 lpdw = (LPDWORD)clearRow;
378 dwColor = BGR32(GetRValue(pwc->clearColorRef),
379 GetGValue(pwc->clearColorRef),
380 GetBValue(pwc->clearColorRef));
381 for (i=0; i<width; i++)
388 /* copy cleared row to other rows in buffer */
389 lpb = clearRow - pwfb->ScanWidth;
390 rowSize = width * bytesPerPixel;
391 for (i=1; i<height; i++) {
392 memcpy(lpb, clearRow, rowSize);
393 lpb -= pwfb->ScanWidth;
396 mask &= ~BUFFER_BIT_BACK_LEFT;
400 if (mask & BUFFER_BIT_FRONT_LEFT) {
402 HPEN Old_Pen = SelectObject(DC, pwc->clearPen);
403 HBRUSH Old_Brush = SelectObject(DC, pwc->clearBrush);
408 FLIP(y) - height + 1);
409 SelectObject(DC, Old_Pen);
410 SelectObject(DC, Old_Brush);
411 mask &= ~BUFFER_BIT_FRONT_LEFT;
414 /* Call swrast if there is anything left to clear (like DEPTH) */
416 _swrast_Clear(ctx, mask);
423 /**********************************************************************/
424 /***** BUFFER Functions *****/
425 /**********************************************************************/
431 wmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
433 _mesa_delete_renderbuffer(ctx, rb);
438 * This is called by Mesa whenever it determines that the window size
439 * has changed. Do whatever's needed to cope with that.
442 wmesa_renderbuffer_storage(struct gl_context *ctx,
443 struct gl_renderbuffer *rb,
444 GLenum internalFormat,
455 * Called by ctx->Driver.ResizeBuffers()
456 * Resize the front/back colorbuffers to match the latest window size.
459 wmesa_resize_buffers(struct gl_context *ctx, struct gl_framebuffer *buffer,
460 GLuint width, GLuint height)
462 WMesaFramebuffer pwfb = wmesa_framebuffer(buffer);
464 if (pwfb->Base.Width != width || pwfb->Base.Height != height) {
465 /* Realloc back buffer */
466 if (ctx->Visual.doubleBufferMode == 1) {
467 wmDeleteBackingStore(pwfb);
468 wmCreateBackingStore(pwfb, width, height);
471 _mesa_resize_framebuffer(ctx, buffer, width, height);
476 * Called by glViewport.
477 * This is a good time for us to poll the current window size and adjust
478 * our renderbuffers to match the current window size.
479 * Remember, we have no opportunity to respond to conventional
480 * resize events since the driver has no event loop.
482 * MakeCurrent also ends up making a call here, so that ensures
483 * we get the viewport set correctly, even if the app does not call
484 * glViewport and relies on the defaults.
486 static void wmesa_viewport(struct gl_context *ctx,
488 GLsizei width, GLsizei height)
490 GLuint new_width, new_height;
492 wmesa_get_buffer_size(ctx->WinSysDrawBuffer, &new_width, &new_height);
495 * Resize buffers if the window size changed.
497 wmesa_resize_buffers(ctx, ctx->WinSysDrawBuffer, new_width, new_height);
498 ctx->NewState |= _NEW_BUFFERS; /* to update scissor / window bounds */
505 * Called when the driver should update it's state, based on the new_state
508 static void wmesa_update_state(struct gl_context *ctx, GLuint new_state)
510 _swrast_InvalidateState(ctx, new_state);
511 _swsetup_InvalidateState(ctx, new_state);
512 _vbo_InvalidateState(ctx, new_state);
513 _tnl_InvalidateState(ctx, new_state);
515 /* TODO - This code is not complete yet because I
516 * don't know what to do for all state updates.
519 if (new_state & _NEW_BUFFERS) {
527 /**********************************************************************/
528 /***** WMESA Functions *****/
529 /**********************************************************************/
531 WMesaContext WMesaCreateContext(HDC hDC,
535 GLboolean alpha_flag)
538 struct dd_function_table functions;
539 GLint red_bits, green_bits, blue_bits, alpha_bits;
540 struct gl_context *ctx;
541 struct gl_config *visual;
545 /* Indexed mode not supported */
549 /* Allocate wmesa context */
550 c = CALLOC_STRUCT(wmesa_context);
555 /* I do not understand this contributed code */
556 /* Support memory and device contexts */
557 if(WindowFromDC(hDC) != NULL) {
558 c->hDC = GetDC(WindowFromDC(hDC)); /* huh ???? */
567 /* Get data for visual */
568 /* Dealing with this is actually a bit of overkill because Mesa will end
569 * up treating all color component size requests less than 8 by using
570 * a single byte per channel. In addition, the interface to the span
571 * routines passes colors as an entire byte per channel anyway, so there
572 * is nothing to be saved by telling the visual to be 16 bits if the device
573 * is 16 bits. That is, Mesa is going to compute colors down to 8 bits per
575 * But we go through the motions here anyway.
577 switch (GetDeviceCaps(c->hDC, BITSPIXEL)) {
579 red_bits = green_bits = blue_bits = 5;
583 red_bits = green_bits = blue_bits = 8;
587 /* Create visual based on flags */
588 visual = _mesa_create_visual(db_flag, /* db_flag */
589 GL_FALSE, /* stereo */
590 red_bits, green_bits, blue_bits, /* color RGB */
591 alpha_flag ? alpha_bits : 0, /* color A */
592 DEFAULT_SOFTWARE_DEPTH_BITS, /* depth_bits */
593 8, /* stencil_bits */
594 16,16,16, /* accum RGB */
595 alpha_flag ? 16 : 0, /* accum A */
596 1); /* num samples */
603 /* Set up driver functions */
604 _mesa_init_driver_functions(&functions);
605 functions.GetString = wmesa_get_string;
606 functions.UpdateState = wmesa_update_state;
607 functions.GetBufferSize = wmesa_get_buffer_size;
608 functions.Flush = wmesa_flush;
609 functions.Clear = clear;
610 functions.ResizeBuffers = wmesa_resize_buffers;
611 functions.Viewport = wmesa_viewport;
613 /* initialize the Mesa context data */
615 _mesa_initialize_context(ctx, API_OPENGL_COMPAT, visual,
618 /* visual no longer needed - it was copied by _mesa_initialize_context() */
619 _mesa_destroy_visual(visual);
621 _mesa_enable_sw_extensions(ctx);
622 _mesa_enable_1_3_extensions(ctx);
623 _mesa_enable_1_4_extensions(ctx);
624 _mesa_enable_1_5_extensions(ctx);
625 _mesa_enable_2_0_extensions(ctx);
626 _mesa_enable_2_1_extensions(ctx);
628 _mesa_meta_init(ctx);
630 /* Initialize the software rasterizer and helper modules. */
631 if (!_swrast_CreateContext(ctx) ||
632 !_vbo_CreateContext(ctx) ||
633 !_tnl_CreateContext(ctx) ||
634 !_swsetup_CreateContext(ctx)) {
635 _mesa_free_context_data(ctx);
639 _swsetup_Wakeup(ctx);
640 TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline;
642 _mesa_compute_version(ctx);
644 /* Exec table initialization requires the version to be computed */
645 _mesa_initialize_dispatch_tables(ctx);
646 _mesa_initialize_vbo_vtxfmt(ctx);
652 void WMesaDestroyContext( WMesaContext pwc )
654 struct gl_context *ctx = &pwc->gl_ctx;
655 WMesaFramebuffer pwfb;
656 GET_CURRENT_CONTEXT(cur_ctx);
658 if (cur_ctx == ctx) {
659 /* unbind current if deleting current context */
660 WMesaMakeCurrent(NULL, NULL);
663 /* clean up frame buffer resources */
664 pwfb = wmesa_lookup_framebuffer(pwc->hDC);
666 if (ctx->Visual.doubleBufferMode == 1)
667 wmDeleteBackingStore(pwfb);
668 wmesa_free_framebuffer(pwc->hDC);
671 /* Release for device, not memory contexts */
672 if (WindowFromDC(pwc->hDC) != NULL)
674 ReleaseDC(WindowFromDC(pwc->hDC), pwc->hDC);
676 DeleteObject(pwc->clearPen);
677 DeleteObject(pwc->clearBrush);
679 _mesa_meta_free(ctx);
681 _swsetup_DestroyContext(ctx);
682 _tnl_DestroyContext(ctx);
683 _vbo_DestroyContext(ctx);
684 _swrast_DestroyContext(ctx);
686 _mesa_free_context_data(ctx);
692 * Create a new color renderbuffer.
694 static struct gl_renderbuffer *
695 wmesa_new_renderbuffer(void)
697 struct gl_renderbuffer *rb = CALLOC_STRUCT(gl_renderbuffer);
701 _mesa_init_renderbuffer(rb, (GLuint)0);
703 rb->_BaseFormat = GL_RGBA;
704 rb->InternalFormat = GL_RGBA;
705 rb->Delete = wmesa_delete_renderbuffer;
706 rb->AllocStorage = wmesa_renderbuffer_storage;
711 void WMesaMakeCurrent(WMesaContext c, HDC hdc)
713 WMesaFramebuffer pwfb;
716 /* return if already current */
717 GET_CURRENT_CONTEXT(ctx);
718 WMesaContext pwc = wmesa_context(ctx);
719 if (pwc && c == pwc && pwc->hDC == hdc)
723 pwfb = wmesa_lookup_framebuffer(hdc);
725 /* Lazy creation of framebuffers */
726 if (c && !pwfb && hdc) {
727 struct gl_renderbuffer *rb;
728 struct gl_config *visual = &c->gl_ctx.Visual;
729 GLuint width, height;
731 get_window_size(hdc, &width, &height);
733 c->clearPen = CreatePen(PS_SOLID, 1, 0);
734 c->clearBrush = CreateSolidBrush(0);
736 pwfb = wmesa_new_framebuffer(hdc, visual);
738 /* Create back buffer if double buffered */
739 if (visual->doubleBufferMode == 1) {
740 wmCreateBackingStore(pwfb, width, height);
743 /* make render buffers */
744 if (visual->doubleBufferMode == 1) {
745 rb = wmesa_new_renderbuffer();
746 _mesa_add_renderbuffer(&pwfb->Base, BUFFER_BACK_LEFT, rb);
748 rb = wmesa_new_renderbuffer();
749 _mesa_add_renderbuffer(&pwfb->Base, BUFFER_FRONT_LEFT, rb);
751 /* Let Mesa own the Depth, Stencil, and Accum buffers */
752 _swrast_add_soft_renderbuffers(&pwfb->Base,
753 GL_FALSE, /* color */
754 visual->depthBits > 0,
755 visual->stencilBits > 0,
756 visual->accumRedBits > 0,
757 visual->alphaBits >0,
762 _mesa_make_current(&c->gl_ctx, &pwfb->Base, &pwfb->Base);
764 _mesa_make_current(NULL, NULL, NULL);
768 void WMesaSwapBuffers( HDC hdc )
770 GET_CURRENT_CONTEXT(ctx);
771 WMesaContext pwc = wmesa_context(ctx);
772 WMesaFramebuffer pwfb = wmesa_lookup_framebuffer(hdc);
775 _mesa_problem(NULL, "wmesa: swapbuffers on unknown hdc");
779 /* If we're swapping the buffer associated with the current context
780 * we have to flush any pending rendering commands first.
782 if (pwc->hDC == hdc) {
783 _mesa_notifySwapBuffers(ctx);
785 BitBlt(pwfb->hDC, 0, 0, pwfb->Base.Width, pwfb->Base.Height,
786 pwfb->dib_hDC, 0, 0, SRCCOPY);
789 /* XXX for now only allow swapping current window */
790 _mesa_problem(NULL, "wmesa: can't swap non-current window");
794 void WMesaShareLists(WMesaContext ctx_to_share, WMesaContext ctx)
796 _mesa_share_state(&ctx->gl_ctx, &ctx_to_share->gl_ctx);