2 * Windows (Win32/Win64) device driver for Mesa
10 #include "main/context.h"
11 #include "main/api_exec.h"
12 #include "main/extensions.h"
13 #include "main/framebuffer.h"
14 #include "main/renderbuffer.h"
15 #include "main/macros.h"
16 #include "main/version.h"
17 #include "main/vtxfmt.h"
18 #include "drivers/common/driverfuncs.h"
19 #include "drivers/common/meta.h"
21 #include "swrast/swrast.h"
22 #include "swrast/s_renderbuffer.h"
23 #include "swrast_setup/swrast_setup.h"
25 #include "tnl/t_context.h"
26 #include "tnl/t_pipeline.h"
29 /* linked list of our Framebuffers (windows) */
30 static WMesaFramebuffer FirstFramebuffer
= NULL
;
34 * Create a new WMesaFramebuffer object which will correspond to the
35 * given HDC (Window handle).
37 static WMesaFramebuffer
38 wmesa_new_framebuffer(HDC hdc
, struct gl_config
*visual
)
41 = malloc(sizeof(struct wmesa_framebuffer
));
43 _mesa_initialize_window_framebuffer(&pwfb
->Base
, visual
);
45 /* insert at head of list */
46 pwfb
->next
= FirstFramebuffer
;
47 FirstFramebuffer
= pwfb
;
53 * Given an hdc, free the corresponding WMesaFramebuffer
56 wmesa_free_framebuffer(HDC hdc
)
58 WMesaFramebuffer pwfb
, prev
;
59 for (pwfb
= FirstFramebuffer
; pwfb
; pwfb
= pwfb
->next
) {
65 struct gl_framebuffer
*fb
;
66 if (pwfb
== FirstFramebuffer
)
67 FirstFramebuffer
= pwfb
->next
;
69 prev
->next
= pwfb
->next
;
71 _mesa_reference_framebuffer(&fb
, NULL
);
76 * Given an hdc, return the corresponding WMesaFramebuffer
78 static WMesaFramebuffer
79 wmesa_lookup_framebuffer(HDC hdc
)
81 WMesaFramebuffer pwfb
;
82 for (pwfb
= FirstFramebuffer
; pwfb
; pwfb
= pwfb
->next
) {
91 * Given a struct gl_framebuffer, return the corresponding WMesaFramebuffer.
93 static WMesaFramebuffer
wmesa_framebuffer(struct gl_framebuffer
*fb
)
95 return (WMesaFramebuffer
) fb
;
100 * Given a struct gl_context, return the corresponding WMesaContext.
102 static WMesaContext
wmesa_context(const struct gl_context
*ctx
)
104 return (WMesaContext
) ctx
;
109 * Every driver should implement a GetString function in order to
110 * return a meaningful GL_RENDERER string.
112 static const GLubyte
*wmesa_get_string(struct gl_context
*ctx
, GLenum name
)
114 return (name
== GL_RENDERER
) ?
115 (GLubyte
*) "Mesa Windows GDI Driver" : NULL
;
120 * Determine the pixel format based on the pixel size.
122 static void wmSetPixelFormat(WMesaFramebuffer pwfb
, HDC hDC
)
124 pwfb
->cColorBits
= GetDeviceCaps(hDC
, BITSPIXEL
);
126 /* Only 16 and 32 bit targets are supported now */
127 assert(pwfb
->cColorBits
== 0 ||
128 pwfb
->cColorBits
== 16 ||
129 pwfb
->cColorBits
== 24 ||
130 pwfb
->cColorBits
== 32);
132 switch(pwfb
->cColorBits
){
134 pwfb
->pixelformat
= PF_INDEX8
;
137 pwfb
->pixelformat
= PF_5R6G5B
;
141 pwfb
->pixelformat
= PF_8R8G8B
;
144 pwfb
->pixelformat
= PF_BADFORMAT
;
150 * Create DIB for back buffer.
151 * We write into this memory with the span routines and then blit it
152 * to the window on a buffer swap.
154 static BOOL
wmCreateBackingStore(WMesaFramebuffer pwfb
, long lxSize
, long lySize
)
156 LPBITMAPINFO pbmi
= &(pwfb
->bmi
);
159 pbmi
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
160 pbmi
->bmiHeader
.biWidth
= lxSize
;
161 pbmi
->bmiHeader
.biHeight
= -lySize
;
162 pbmi
->bmiHeader
.biPlanes
= 1;
163 pbmi
->bmiHeader
.biBitCount
= GetDeviceCaps(pwfb
->hDC
, BITSPIXEL
);
164 pbmi
->bmiHeader
.biCompression
= BI_RGB
;
165 pbmi
->bmiHeader
.biSizeImage
= 0;
166 pbmi
->bmiHeader
.biXPelsPerMeter
= 0;
167 pbmi
->bmiHeader
.biYPelsPerMeter
= 0;
168 pbmi
->bmiHeader
.biClrUsed
= 0;
169 pbmi
->bmiHeader
.biClrImportant
= 0;
171 pwfb
->cColorBits
= pbmi
->bmiHeader
.biBitCount
;
172 pwfb
->ScanWidth
= (lxSize
* (pwfb
->cColorBits
/ 8) + 3) & ~3;
174 hic
= CreateIC("display", NULL
, NULL
, NULL
);
175 pwfb
->dib_hDC
= CreateCompatibleDC(hic
);
177 pwfb
->hbmDIB
= CreateDIBSection(hic
,
180 (void **)&(pwfb
->pbPixels
),
183 pwfb
->hOldBitmap
= SelectObject(pwfb
->dib_hDC
, pwfb
->hbmDIB
);
187 wmSetPixelFormat(pwfb
, pwfb
->hDC
);
192 static void wmDeleteBackingStore(WMesaFramebuffer pwfb
)
195 SelectObject(pwfb
->dib_hDC
, pwfb
->hOldBitmap
);
196 DeleteDC(pwfb
->dib_hDC
);
197 DeleteObject(pwfb
->hbmDIB
);
203 * Find the width and height of the window named by hdc.
206 get_window_size(HDC hdc
, GLuint
*width
, GLuint
*height
)
208 if (WindowFromDC(hdc
)) {
210 GetClientRect(WindowFromDC(hdc
), &rect
);
211 *width
= rect
.right
- rect
.left
;
212 *height
= rect
.bottom
- rect
.top
;
214 else { /* Memory context */
215 /* From contributed code - use the size of the desktop
216 * for the size of a memory context (?) */
217 *width
= GetDeviceCaps(hdc
, HORZRES
);
218 *height
= GetDeviceCaps(hdc
, VERTRES
);
224 wmesa_get_buffer_size(struct gl_framebuffer
*buffer
, GLuint
*width
, GLuint
*height
)
226 WMesaFramebuffer pwfb
= wmesa_framebuffer(buffer
);
227 get_window_size(pwfb
->hDC
, width
, height
);
231 static void wmesa_flush(struct gl_context
*ctx
)
233 WMesaFramebuffer pwfb
= wmesa_framebuffer(ctx
->WinSysDrawBuffer
);
235 if (ctx
->Visual
.doubleBufferMode
== 1) {
236 BitBlt(pwfb
->hDC
, 0, 0, pwfb
->Base
.Width
, pwfb
->Base
.Height
,
237 pwfb
->dib_hDC
, 0, 0, SRCCOPY
);
240 /* Do nothing for single buffer */
245 /**********************************************************************/
246 /***** CLEAR Functions *****/
247 /**********************************************************************/
250 * Clear the color/depth/stencil buffers.
252 static void clear(struct gl_context
*ctx
, GLbitfield mask
)
254 #define FLIP(Y) (ctx->DrawBuffer->Height - (Y) - 1)
255 const GLint x
= ctx
->DrawBuffer
->_Xmin
;
256 const GLint y
= ctx
->DrawBuffer
->_Ymin
;
257 const GLint height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
258 const GLint width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
260 WMesaContext pwc
= wmesa_context(ctx
);
261 WMesaFramebuffer pwfb
= wmesa_framebuffer(ctx
->DrawBuffer
);
264 /* Let swrast do all the work if the masks are not set to
265 * clear all channels. */
266 if (!ctx
->Color
.ColorMask
[0][0] ||
267 !ctx
->Color
.ColorMask
[0][1] ||
268 !ctx
->Color
.ColorMask
[0][2] ||
269 !ctx
->Color
.ColorMask
[0][3]) {
270 _swrast_Clear(ctx
, mask
);
274 if (mask
& BUFFER_BITS_COLOR
) {
275 /* setup the clearing color */
276 const union gl_color_union color
= ctx
->Color
.ClearColor
;
278 UNCLAMPED_FLOAT_TO_UBYTE(col
[0], color
.f
[0]);
279 UNCLAMPED_FLOAT_TO_UBYTE(col
[1], color
.f
[1]);
280 UNCLAMPED_FLOAT_TO_UBYTE(col
[2], color
.f
[2]);
281 pwc
->clearColorRef
= RGB(col
[0], col
[1], col
[2]);
282 DeleteObject(pwc
->clearPen
);
283 DeleteObject(pwc
->clearBrush
);
284 pwc
->clearPen
= CreatePen(PS_SOLID
, 1, pwc
->clearColorRef
);
285 pwc
->clearBrush
= CreateSolidBrush(pwc
->clearColorRef
);
289 if (mask
& BUFFER_BIT_BACK_LEFT
) {
292 UINT bytesPerPixel
= pwfb
->cColorBits
/ 8;
293 LPBYTE lpb
, clearRow
;
301 /* Try for a fast clear - clearing entire buffer with a single
303 if (width
== ctx
->DrawBuffer
->Width
&&
304 height
== ctx
->DrawBuffer
->Height
) { /* entire buffer */
305 /* Now check for an easy clear value */
306 switch (bytesPerPixel
) {
308 bColor
= BGR8(GetRValue(pwc
->clearColorRef
),
309 GetGValue(pwc
->clearColorRef
),
310 GetBValue(pwc
->clearColorRef
));
311 memset(pwfb
->pbPixels
, bColor
,
312 pwfb
->ScanWidth
* height
);
316 wColor
= BGR16(GetRValue(pwc
->clearColorRef
),
317 GetGValue(pwc
->clearColorRef
),
318 GetBValue(pwc
->clearColorRef
));
319 if (((wColor
>> 8) & 0xff) == (wColor
& 0xff)) {
320 memset(pwfb
->pbPixels
, wColor
& 0xff,
321 pwfb
->ScanWidth
* height
);
328 if (GetRValue(pwc
->clearColorRef
) ==
329 GetGValue(pwc
->clearColorRef
) &&
330 GetRValue(pwc
->clearColorRef
) ==
331 GetBValue(pwc
->clearColorRef
)) {
332 memset(pwfb
->pbPixels
,
333 GetRValue(pwc
->clearColorRef
),
334 pwfb
->ScanWidth
* height
);
344 /* Need to clear a row at a time. Begin by setting the first
345 * row in the area to be cleared to the clear color. */
347 clearRow
= pwfb
->pbPixels
+
348 pwfb
->ScanWidth
* FLIP(y
) +
350 switch (bytesPerPixel
) {
353 bColor
= BGR8(GetRValue(pwc
->clearColorRef
),
354 GetGValue(pwc
->clearColorRef
),
355 GetBValue(pwc
->clearColorRef
));
356 memset(lpb
, bColor
, width
);
359 lpw
= (LPWORD
)clearRow
;
360 wColor
= BGR16(GetRValue(pwc
->clearColorRef
),
361 GetGValue(pwc
->clearColorRef
),
362 GetBValue(pwc
->clearColorRef
));
363 for (i
=0; i
<width
; i
++)
368 r
= GetRValue(pwc
->clearColorRef
);
369 g
= GetGValue(pwc
->clearColorRef
);
370 b
= GetBValue(pwc
->clearColorRef
);
371 for (i
=0; i
<width
; i
++) {
378 lpdw
= (LPDWORD
)clearRow
;
379 dwColor
= BGR32(GetRValue(pwc
->clearColorRef
),
380 GetGValue(pwc
->clearColorRef
),
381 GetBValue(pwc
->clearColorRef
));
382 for (i
=0; i
<width
; i
++)
389 /* copy cleared row to other rows in buffer */
390 lpb
= clearRow
- pwfb
->ScanWidth
;
391 rowSize
= width
* bytesPerPixel
;
392 for (i
=1; i
<height
; i
++) {
393 memcpy(lpb
, clearRow
, rowSize
);
394 lpb
-= pwfb
->ScanWidth
;
397 mask
&= ~BUFFER_BIT_BACK_LEFT
;
401 if (mask
& BUFFER_BIT_FRONT_LEFT
) {
403 HPEN Old_Pen
= SelectObject(DC
, pwc
->clearPen
);
404 HBRUSH Old_Brush
= SelectObject(DC
, pwc
->clearBrush
);
409 FLIP(y
) - height
+ 1);
410 SelectObject(DC
, Old_Pen
);
411 SelectObject(DC
, Old_Brush
);
412 mask
&= ~BUFFER_BIT_FRONT_LEFT
;
415 /* Call swrast if there is anything left to clear (like DEPTH) */
417 _swrast_Clear(ctx
, mask
);
424 /**********************************************************************/
425 /***** BUFFER Functions *****/
426 /**********************************************************************/
432 wmesa_delete_renderbuffer(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
)
434 _mesa_delete_renderbuffer(ctx
, rb
);
439 * This is called by Mesa whenever it determines that the window size
440 * has changed. Do whatever's needed to cope with that.
443 wmesa_renderbuffer_storage(struct gl_context
*ctx
,
444 struct gl_renderbuffer
*rb
,
445 GLenum internalFormat
,
456 * Called by ctx->Driver.ResizeBuffers()
457 * Resize the front/back colorbuffers to match the latest window size.
460 wmesa_resize_buffers(struct gl_context
*ctx
, struct gl_framebuffer
*buffer
,
461 GLuint width
, GLuint height
)
463 WMesaFramebuffer pwfb
= wmesa_framebuffer(buffer
);
465 if (pwfb
->Base
.Width
!= width
|| pwfb
->Base
.Height
!= height
) {
466 /* Realloc back buffer */
467 if (ctx
->Visual
.doubleBufferMode
== 1) {
468 wmDeleteBackingStore(pwfb
);
469 wmCreateBackingStore(pwfb
, width
, height
);
472 _mesa_resize_framebuffer(ctx
, buffer
, width
, height
);
477 * Called by glViewport.
478 * This is a good time for us to poll the current window size and adjust
479 * our renderbuffers to match the current window size.
480 * Remember, we have no opportunity to respond to conventional
481 * resize events since the driver has no event loop.
483 * MakeCurrent also ends up making a call here, so that ensures
484 * we get the viewport set correctly, even if the app does not call
485 * glViewport and relies on the defaults.
487 static void wmesa_viewport(struct gl_context
*ctx
)
489 GLuint new_width
, new_height
;
491 wmesa_get_buffer_size(ctx
->WinSysDrawBuffer
, &new_width
, &new_height
);
494 * Resize buffers if the window size changed.
496 wmesa_resize_buffers(ctx
, ctx
->WinSysDrawBuffer
, new_width
, new_height
);
497 ctx
->NewState
|= _NEW_BUFFERS
; /* to update scissor / window bounds */
504 * Called when the driver should update it's state, based on the new_state
507 static void wmesa_update_state(struct gl_context
*ctx
, GLuint new_state
)
509 _swrast_InvalidateState(ctx
, new_state
);
510 _swsetup_InvalidateState(ctx
, new_state
);
511 _vbo_InvalidateState(ctx
, new_state
);
512 _tnl_InvalidateState(ctx
, new_state
);
514 /* TODO - This code is not complete yet because I
515 * don't know what to do for all state updates.
518 if (new_state
& _NEW_BUFFERS
) {
526 /**********************************************************************/
527 /***** WMESA Functions *****/
528 /**********************************************************************/
530 WMesaContext
WMesaCreateContext(HDC hDC
,
534 GLboolean alpha_flag
)
537 struct dd_function_table functions
;
538 GLint red_bits
, green_bits
, blue_bits
, alpha_bits
;
539 struct gl_context
*ctx
;
540 struct gl_config
*visual
;
544 /* Indexed mode not supported */
548 /* Allocate wmesa context */
549 c
= CALLOC_STRUCT(wmesa_context
);
554 /* I do not understand this contributed code */
555 /* Support memory and device contexts */
556 if(WindowFromDC(hDC
) != NULL
) {
557 c
->hDC
= GetDC(WindowFromDC(hDC
)); /* huh ???? */
566 /* Get data for visual */
567 /* Dealing with this is actually a bit of overkill because Mesa will end
568 * up treating all color component size requests less than 8 by using
569 * a single byte per channel. In addition, the interface to the span
570 * routines passes colors as an entire byte per channel anyway, so there
571 * is nothing to be saved by telling the visual to be 16 bits if the device
572 * is 16 bits. That is, Mesa is going to compute colors down to 8 bits per
574 * But we go through the motions here anyway.
576 switch (GetDeviceCaps(c
->hDC
, BITSPIXEL
)) {
578 red_bits
= green_bits
= blue_bits
= 5;
582 red_bits
= green_bits
= blue_bits
= 8;
586 /* Create visual based on flags */
587 visual
= _mesa_create_visual(db_flag
, /* db_flag */
588 GL_FALSE
, /* stereo */
589 red_bits
, green_bits
, blue_bits
, /* color RGB */
590 alpha_flag
? alpha_bits
: 0, /* color A */
591 DEFAULT_SOFTWARE_DEPTH_BITS
, /* depth_bits */
592 8, /* stencil_bits */
593 16,16,16, /* accum RGB */
594 alpha_flag
? 16 : 0, /* accum A */
595 1); /* num samples */
602 /* Set up driver functions */
603 _mesa_init_driver_functions(&functions
);
604 functions
.GetString
= wmesa_get_string
;
605 functions
.UpdateState
= wmesa_update_state
;
606 functions
.Flush
= wmesa_flush
;
607 functions
.Clear
= clear
;
608 functions
.ResizeBuffers
= wmesa_resize_buffers
;
609 functions
.Viewport
= wmesa_viewport
;
611 /* initialize the Mesa context data */
613 _mesa_initialize_context(ctx
, API_OPENGL_COMPAT
, visual
,
616 /* visual no longer needed - it was copied by _mesa_initialize_context() */
617 _mesa_destroy_visual(visual
);
619 _mesa_enable_sw_extensions(ctx
);
621 _mesa_meta_init(ctx
);
623 /* Initialize the software rasterizer and helper modules. */
624 if (!_swrast_CreateContext(ctx
) ||
625 !_vbo_CreateContext(ctx
) ||
626 !_tnl_CreateContext(ctx
) ||
627 !_swsetup_CreateContext(ctx
)) {
628 _mesa_free_context_data(ctx
);
632 _swsetup_Wakeup(ctx
);
633 TNL_CONTEXT(ctx
)->Driver
.RunPipeline
= _tnl_run_pipeline
;
635 _mesa_compute_version(ctx
);
637 /* Exec table initialization requires the version to be computed */
638 _mesa_initialize_dispatch_tables(ctx
);
639 _mesa_initialize_vbo_vtxfmt(ctx
);
645 void WMesaDestroyContext( WMesaContext pwc
)
647 struct gl_context
*ctx
= &pwc
->gl_ctx
;
648 WMesaFramebuffer pwfb
;
649 GET_CURRENT_CONTEXT(cur_ctx
);
651 if (cur_ctx
== ctx
) {
652 /* unbind current if deleting current context */
653 WMesaMakeCurrent(NULL
, NULL
);
656 /* clean up frame buffer resources */
657 pwfb
= wmesa_lookup_framebuffer(pwc
->hDC
);
659 if (ctx
->Visual
.doubleBufferMode
== 1)
660 wmDeleteBackingStore(pwfb
);
661 wmesa_free_framebuffer(pwc
->hDC
);
664 /* Release for device, not memory contexts */
665 if (WindowFromDC(pwc
->hDC
) != NULL
)
667 ReleaseDC(WindowFromDC(pwc
->hDC
), pwc
->hDC
);
669 DeleteObject(pwc
->clearPen
);
670 DeleteObject(pwc
->clearBrush
);
672 _mesa_meta_free(ctx
);
674 _swsetup_DestroyContext(ctx
);
675 _tnl_DestroyContext(ctx
);
676 _vbo_DestroyContext(ctx
);
677 _swrast_DestroyContext(ctx
);
679 _mesa_free_context_data(ctx
);
685 * Create a new color renderbuffer.
687 static struct gl_renderbuffer
*
688 wmesa_new_renderbuffer(void)
690 struct gl_renderbuffer
*rb
= CALLOC_STRUCT(gl_renderbuffer
);
694 _mesa_init_renderbuffer(rb
, (GLuint
)0);
696 rb
->_BaseFormat
= GL_RGBA
;
697 rb
->InternalFormat
= GL_RGBA
;
698 rb
->Delete
= wmesa_delete_renderbuffer
;
699 rb
->AllocStorage
= wmesa_renderbuffer_storage
;
704 void WMesaMakeCurrent(WMesaContext c
, HDC hdc
)
706 WMesaFramebuffer pwfb
;
709 /* return if already current */
710 GET_CURRENT_CONTEXT(ctx
);
711 WMesaContext pwc
= wmesa_context(ctx
);
712 if (pwc
&& c
== pwc
&& pwc
->hDC
== hdc
)
716 pwfb
= wmesa_lookup_framebuffer(hdc
);
718 /* Lazy creation of framebuffers */
719 if (c
&& !pwfb
&& hdc
) {
720 struct gl_renderbuffer
*rb
;
721 struct gl_config
*visual
= &c
->gl_ctx
.Visual
;
722 GLuint width
, height
;
724 get_window_size(hdc
, &width
, &height
);
726 c
->clearPen
= CreatePen(PS_SOLID
, 1, 0);
727 c
->clearBrush
= CreateSolidBrush(0);
729 pwfb
= wmesa_new_framebuffer(hdc
, visual
);
731 /* Create back buffer if double buffered */
732 if (visual
->doubleBufferMode
== 1) {
733 wmCreateBackingStore(pwfb
, width
, height
);
736 /* make render buffers */
737 if (visual
->doubleBufferMode
== 1) {
738 rb
= wmesa_new_renderbuffer();
739 _mesa_add_renderbuffer(&pwfb
->Base
, BUFFER_BACK_LEFT
, rb
);
741 rb
= wmesa_new_renderbuffer();
742 _mesa_add_renderbuffer(&pwfb
->Base
, BUFFER_FRONT_LEFT
, rb
);
744 /* Let Mesa own the Depth, Stencil, and Accum buffers */
745 _swrast_add_soft_renderbuffers(&pwfb
->Base
,
746 GL_FALSE
, /* color */
747 visual
->depthBits
> 0,
748 visual
->stencilBits
> 0,
749 visual
->accumRedBits
> 0,
750 visual
->alphaBits
>0,
755 _mesa_make_current(&c
->gl_ctx
, &pwfb
->Base
, &pwfb
->Base
);
757 _mesa_make_current(NULL
, NULL
, NULL
);
761 void WMesaSwapBuffers( HDC hdc
)
763 GET_CURRENT_CONTEXT(ctx
);
764 WMesaContext pwc
= wmesa_context(ctx
);
765 WMesaFramebuffer pwfb
= wmesa_lookup_framebuffer(hdc
);
768 _mesa_problem(NULL
, "wmesa: swapbuffers on unknown hdc");
772 /* If we're swapping the buffer associated with the current context
773 * we have to flush any pending rendering commands first.
775 if (pwc
->hDC
== hdc
) {
776 _mesa_notifySwapBuffers(ctx
);
778 BitBlt(pwfb
->hDC
, 0, 0, pwfb
->Base
.Width
, pwfb
->Base
.Height
,
779 pwfb
->dib_hDC
, 0, 0, SRCCOPY
);
782 /* XXX for now only allow swapping current window */
783 _mesa_problem(NULL
, "wmesa: can't swap non-current window");
787 void WMesaShareLists(WMesaContext ctx_to_share
, WMesaContext ctx
)
789 _mesa_share_state(&ctx
->gl_ctx
, &ctx_to_share
->gl_ctx
);