440 lines
15 KiB
C++
440 lines
15 KiB
C++
// Filename: wglGraphicsWindow.cxx
|
|
// Created by: drose (20Dec02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wglGraphicsWindow.h"
|
|
#include "config_wgldisplay.h"
|
|
#include "config_windisplay.h"
|
|
#include "wglGraphicsPipe.h"
|
|
#include "pStatTimer.h"
|
|
#include "glgsg.h"
|
|
|
|
#include <wingdi.h>
|
|
|
|
TypeHandle wglGraphicsWindow::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
wglGraphicsWindow::
|
|
wglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
|
|
const string &name,
|
|
const FrameBufferProperties &fb_prop,
|
|
const WindowProperties &win_prop,
|
|
int flags,
|
|
GraphicsStateGuardian *gsg,
|
|
GraphicsOutput *host) :
|
|
WinGraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
|
|
{
|
|
_hdc = (HDC)0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
wglGraphicsWindow::
|
|
~wglGraphicsWindow() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::begin_frame
|
|
// Access: Public, Virtual
|
|
// Description: This function will be called within the draw thread
|
|
// before beginning rendering for a given frame. It
|
|
// should do whatever setup is required, and return true
|
|
// if the frame should be rendered, or false if it
|
|
// should be skipped.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool wglGraphicsWindow::
|
|
begin_frame(FrameMode mode, Thread *current_thread) {
|
|
|
|
begin_frame_spam(mode);
|
|
if (_gsg == (GraphicsStateGuardian *)NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (!get_unexposed_draw() && !_got_expose_event) {
|
|
if (wgldisplay_cat.is_spam()) {
|
|
wgldisplay_cat.spam()
|
|
<< "Not drawing " << this << ": unexposed.\n";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (wgldisplay_cat.is_spam()) {
|
|
wgldisplay_cat.spam()
|
|
<< "Drawing " << this << ": exposed.\n";
|
|
}
|
|
|
|
wglGraphicsStateGuardian *wglgsg;
|
|
DCAST_INTO_R(wglgsg, _gsg, false);
|
|
|
|
HGLRC context = wglgsg->get_context(_hdc);
|
|
nassertr(context, false);
|
|
|
|
wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
|
|
wglgsg->reset_if_new();
|
|
|
|
if (mode == FM_render) {
|
|
clear_cube_map_selection();
|
|
}
|
|
|
|
_gsg->set_current_properties(&get_fb_properties());
|
|
return _gsg->begin_frame(current_thread);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::end_frame
|
|
// Access: Public, Virtual
|
|
// Description: This function will be called within the draw thread
|
|
// after rendering is completed for a given frame. It
|
|
// should do whatever finalization is required.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
end_frame(FrameMode mode, Thread *current_thread) {
|
|
end_frame_spam(mode);
|
|
|
|
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
|
|
|
|
if (mode == FM_render) {
|
|
copy_to_textures();
|
|
}
|
|
|
|
_gsg->end_frame(current_thread);
|
|
|
|
if (mode == FM_render) {
|
|
trigger_flip();
|
|
clear_cube_map_selection();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::begin_flip
|
|
// Access: Public, Virtual
|
|
// Description: This function will be called within the draw thread
|
|
// after end_frame() has been called on all windows, to
|
|
// initiate the exchange of the front and back buffers.
|
|
//
|
|
// This should instruct the window to prepare for the
|
|
// flip at the next video sync, but it should not wait.
|
|
//
|
|
// We have the two separate functions, begin_flip() and
|
|
// end_flip(), to make it easier to flip all of the
|
|
// windows at the same time.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
begin_flip() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::ready_flip
|
|
// Access: Public, Virtual
|
|
// Description: This function will be called within the draw thread
|
|
// after end_frame() has been called on all windows, to
|
|
// initiate the exchange of the front and back buffers.
|
|
//
|
|
// This should instruct the window to prepare for the
|
|
// flip when command, but will not actually flip
|
|
//
|
|
// We have the two separate functions, begin_flip() and
|
|
// end_flip(), to make it easier to flip all of the
|
|
// windows at the same time.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
ready_flip() {
|
|
if (_hdc) {
|
|
// The documentation on SwapBuffers() is not at all clear on
|
|
// whether the GL context needs to be current before it can be
|
|
// called. Empirically, it appears that it is not necessary in
|
|
// many cases, but it definitely is necessary at least in the case
|
|
// of Mesa on Windows.
|
|
wglGraphicsStateGuardian *wglgsg;
|
|
DCAST_INTO_V(wglgsg, _gsg);
|
|
HGLRC context = wglgsg->get_context(_hdc);
|
|
nassertv(context);
|
|
wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
|
|
wglgsg->finish();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::end_flip
|
|
// Access: Public, Virtual
|
|
// Description: This function will be called within the draw thread
|
|
// after begin_flip() has been called on all windows, to
|
|
// finish the exchange of the front and back buffers.
|
|
//
|
|
// This should cause the window to wait for the flip, if
|
|
// necessary.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
end_flip() {
|
|
if (_hdc != NULL && _flip_ready) {
|
|
// The documentation on SwapBuffers() is not at all clear on
|
|
// whether the GL context needs to be current before it can be
|
|
// called. Empirically, it appears that it is not necessary in
|
|
// many cases, but it definitely is necessary at least in the case
|
|
// of Mesa on Windows.
|
|
wglGraphicsStateGuardian *wglgsg;
|
|
DCAST_INTO_V(wglgsg, _gsg);
|
|
HGLRC context = wglgsg->get_context(_hdc);
|
|
nassertv(context);
|
|
wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
|
|
SwapBuffers(_hdc);
|
|
}
|
|
WinGraphicsWindow::end_flip();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::close_window
|
|
// Access: Protected, Virtual
|
|
// Description: Closes the window right now. Called from the window
|
|
// thread.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
close_window() {
|
|
if (_gsg != (GraphicsStateGuardian *)NULL) {
|
|
wglGraphicsPipe::wgl_make_current(_hdc, NULL, &_make_current_pcollector);
|
|
_gsg.clear();
|
|
}
|
|
ReleaseDC(_hWnd, _hdc);
|
|
_hdc = (HDC)0;
|
|
WinGraphicsWindow::close_window();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::open_window
|
|
// Access: Protected, Virtual
|
|
// Description: Opens the window right now. Called from the window
|
|
// thread. Returns true if the window is successfully
|
|
// opened, or false if there was a problem.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool wglGraphicsWindow::
|
|
open_window() {
|
|
if (!WinGraphicsWindow::open_window()) {
|
|
return false;
|
|
}
|
|
|
|
// GSG creation/initialization.
|
|
|
|
wglGraphicsStateGuardian *wglgsg;
|
|
if (_gsg == 0) {
|
|
// There is no old gsg. Create a new one.
|
|
wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, NULL);
|
|
wglgsg->choose_pixel_format(_fb_properties, false);
|
|
_gsg = wglgsg;
|
|
} else {
|
|
// If the old gsg has the wrong pixel format, create a
|
|
// new one that shares with the old gsg.
|
|
DCAST_INTO_R(wglgsg, _gsg, false);
|
|
if (!wglgsg->get_fb_properties().subsumes(_fb_properties)) {
|
|
wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, wglgsg);
|
|
wglgsg->choose_pixel_format(_fb_properties, false);
|
|
_gsg = wglgsg;
|
|
}
|
|
}
|
|
|
|
// Set up the pixel format of the window appropriately for GL.
|
|
|
|
_hdc = GetDC(_hWnd);
|
|
int pfnum = wglgsg->get_pfnum();
|
|
PIXELFORMATDESCRIPTOR pixelformat;
|
|
DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
|
|
&pixelformat);
|
|
|
|
#ifdef NOTIFY_DEBUG
|
|
char msg[200];
|
|
sprintf(msg, "Selected GL PixelFormat is #%d", pfnum);
|
|
print_pfd(&pixelformat, msg);
|
|
#endif
|
|
|
|
BOOL set_pfnum = SetPixelFormat(_hdc, pfnum, &pixelformat);
|
|
|
|
if (!set_pfnum) {
|
|
if (wglgsg->fail_pfnum()) {
|
|
wgldisplay_cat.error()
|
|
<< "SetPixelFormat(" << pfnum << ") failed; trying "
|
|
<< wglgsg->get_pfnum() << " instead\n";
|
|
|
|
pfnum = wglgsg->get_pfnum();
|
|
DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
|
|
&pixelformat);
|
|
|
|
#ifdef NOTIFY_DEBUG
|
|
sprintf(msg, "Selected GL PixelFormat is #%d", pfnum);
|
|
print_pfd(&pixelformat, msg);
|
|
#endif
|
|
|
|
DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
|
|
&pixelformat);
|
|
set_pfnum = SetPixelFormat(_hdc, pfnum, &pixelformat);
|
|
}
|
|
}
|
|
|
|
if (!set_pfnum) {
|
|
wgldisplay_cat.error()
|
|
<< "SetPixelFormat(" << pfnum << ") failed after window create\n";
|
|
close_window();
|
|
return false;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
if (gl_force_invalid) {
|
|
wgldisplay_cat.error()
|
|
<< "Artificially failing window.\n";
|
|
close_window();
|
|
return false;
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
// Initializes _colormap
|
|
setup_colormap(pixelformat);
|
|
|
|
// Initialize the gsg.
|
|
wglGraphicsPipe::wgl_make_current(_hdc, wglgsg->get_context(_hdc), &_make_current_pcollector);
|
|
wglgsg->reset_if_new();
|
|
wglgsg->report_my_gl_errors();
|
|
if (!wglgsg->get_fb_properties().verify_hardware_software
|
|
(_fb_properties,wglgsg->get_gl_renderer())) {
|
|
close_window();
|
|
return false;
|
|
}
|
|
_fb_properties = wglgsg->get_fb_properties();
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::setup_colormap
|
|
// Access: Private
|
|
// Description: Sets up a colormap for the window matching the
|
|
// selected pixel format. This is necessary before
|
|
// creating a GL context.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
setup_colormap(const PIXELFORMATDESCRIPTOR &pixelformat) {
|
|
LOGPALETTE *logical;
|
|
int n;
|
|
|
|
if (!(pixelformat.dwFlags & PFD_NEED_PALETTE ||
|
|
pixelformat.iPixelType == PFD_TYPE_COLORINDEX))
|
|
return;
|
|
|
|
n = 1 << pixelformat.cColorBits;
|
|
|
|
/* allocate a bunch of memory for the logical palette (assume 256
|
|
colors in a Win32 palette */
|
|
logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
|
|
sizeof(PALETTEENTRY) * n);
|
|
memset(logical, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n);
|
|
|
|
/* set the entries in the logical palette */
|
|
logical->palVersion = 0x300;
|
|
logical->palNumEntries = n;
|
|
|
|
/* start with a copy of the current system palette */
|
|
GetSystemPaletteEntries(_hdc, 0, 256, &logical->palPalEntry[0]);
|
|
|
|
if (pixelformat.iPixelType == PFD_TYPE_RGBA) {
|
|
int redMask = (1 << pixelformat.cRedBits) - 1;
|
|
int greenMask = (1 << pixelformat.cGreenBits) - 1;
|
|
int blueMask = (1 << pixelformat.cBlueBits) - 1;
|
|
int i;
|
|
|
|
/* fill in an RGBA color palette */
|
|
for (i = 0; i < n; ++i) {
|
|
logical->palPalEntry[i].peRed =
|
|
(((i >> pixelformat.cRedShift) & redMask) * 255) / redMask;
|
|
logical->palPalEntry[i].peGreen =
|
|
(((i >> pixelformat.cGreenShift) & greenMask) * 255) / greenMask;
|
|
logical->palPalEntry[i].peBlue =
|
|
(((i >> pixelformat.cBlueShift) & blueMask) * 255) / blueMask;
|
|
logical->palPalEntry[i].peFlags = 0;
|
|
}
|
|
}
|
|
|
|
_colormap = CreatePalette(logical);
|
|
free(logical);
|
|
|
|
SelectPalette(_hdc, _colormap, FALSE);
|
|
RealizePalette(_hdc);
|
|
}
|
|
|
|
#ifdef NOTIFY_DEBUG
|
|
|
|
//typedef enum {Software, MCD, ICD} OGLDriverType;
|
|
static char *OGLDrvStrings[3] = {"Software","MCD","ICD"};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: wglGraphicsWindow::print_pfd
|
|
// Access: Private, Static
|
|
// Description: Reports information about the selected pixel format
|
|
// descriptor, along with the indicated message.
|
|
////////////////////////////////////////////////////////////////////
|
|
void wglGraphicsWindow::
|
|
print_pfd(PIXELFORMATDESCRIPTOR *pfd, char *msg) {
|
|
if (!wgldisplay_cat.is_debug()) {
|
|
return;
|
|
}
|
|
|
|
OGLDriverType drvtype;
|
|
if ((pfd->dwFlags & PFD_GENERIC_ACCELERATED) &&
|
|
(pfd->dwFlags & PFD_GENERIC_FORMAT)) {
|
|
drvtype=MCD;
|
|
} else if (!(pfd->dwFlags & PFD_GENERIC_ACCELERATED) && !(pfd->dwFlags & PFD_GENERIC_FORMAT)) {
|
|
drvtype=ICD;
|
|
} else {
|
|
drvtype=Software;
|
|
}
|
|
|
|
#define PRINT_FLAG(FLG) ((pfd->dwFlags & PFD_##FLG) ? (" PFD_" #FLG "|") : "")
|
|
wgldisplay_cat.debug()
|
|
<< "================================\n";
|
|
|
|
wgldisplay_cat.debug()
|
|
<< msg << ", " << OGLDrvStrings[drvtype] << " driver\n"
|
|
<< "PFD flags: 0x" << (void*)pfd->dwFlags << " ("
|
|
<< PRINT_FLAG(GENERIC_ACCELERATED)
|
|
<< PRINT_FLAG(GENERIC_FORMAT)
|
|
<< PRINT_FLAG(DOUBLEBUFFER)
|
|
<< PRINT_FLAG(SUPPORT_OPENGL)
|
|
<< PRINT_FLAG(SUPPORT_GDI)
|
|
<< PRINT_FLAG(STEREO)
|
|
<< PRINT_FLAG(DRAW_TO_WINDOW)
|
|
<< PRINT_FLAG(DRAW_TO_BITMAP)
|
|
<< PRINT_FLAG(SWAP_EXCHANGE)
|
|
<< PRINT_FLAG(SWAP_COPY)
|
|
<< PRINT_FLAG(SWAP_LAYER_BUFFERS)
|
|
<< PRINT_FLAG(NEED_PALETTE)
|
|
<< PRINT_FLAG(NEED_SYSTEM_PALETTE)
|
|
<< PRINT_FLAG(SUPPORT_DIRECTDRAW) << ")\n"
|
|
<< "PFD iPixelType: "
|
|
<< ((pfd->iPixelType==PFD_TYPE_RGBA) ? "PFD_TYPE_RGBA":"PFD_TYPE_COLORINDEX")
|
|
<< endl
|
|
<< "PFD cColorBits: " << (DWORD)pfd->cColorBits
|
|
<< " R: " << (DWORD)pfd->cRedBits
|
|
<<" G: " << (DWORD)pfd->cGreenBits
|
|
<<" B: " << (DWORD)pfd->cBlueBits << endl
|
|
<< "PFD cAlphaBits: " << (DWORD)pfd->cAlphaBits
|
|
<< " DepthBits: " << (DWORD)pfd->cDepthBits
|
|
<<" StencilBits: " << (DWORD)pfd->cStencilBits
|
|
<<" AccumBits: " << (DWORD)pfd->cAccumBits
|
|
<< endl;
|
|
}
|
|
#endif
|
|
|