mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-29 23:52:22 +00:00
0c437492b4
For now, OpenGL and Vulkan renderers are broken as I focused on getting the software renderer working (which was quite tricky to get right). This fixes a couple of issues: the segfault when warping the screen (due to the scene rendering move invalidating the warp buffer), and warp always having 320x200 resolution. There's still the problem of the effect being too subtle at high resolution, but that's just a matter of updating the tables and tweaking the code in D_WarpScreen. Another issue is the Draw functions should probably write directly to the main frame buffer or even one passed in as a parameter. This would remove the need for binding the main buffer at the beginning and end of the frame.
494 lines
12 KiB
C
494 lines
12 KiB
C
/*
|
|
vid_win.c
|
|
|
|
Win32 SW vid component
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "winquake.h"
|
|
#include <ddraw.h>
|
|
|
|
#include "QF/cvar.h"
|
|
#include "QF/qargs.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/vid.h"
|
|
|
|
#include "context_win.h"
|
|
#include "r_internal.h"
|
|
#include "vid_internal.h"
|
|
#include "vid_sw.h"
|
|
|
|
typedef union {
|
|
byte bgra[4];
|
|
uint32_t value;
|
|
} win_palette_t;
|
|
|
|
static win_palette_t st2d_8to32table[256];
|
|
static byte current_palette[768];
|
|
static int palette_changed;
|
|
|
|
static LPDIRECTDRAW dd_Object;
|
|
static HINSTANCE hInstDDraw;
|
|
static LPDIRECTDRAWSURFACE dd_frontbuffer;
|
|
static LPDIRECTDRAWSURFACE dd_backbuffer;
|
|
static RECT src_rect;
|
|
static RECT dst_rect;
|
|
static HDC win_gdi;
|
|
static HDC dib_section;
|
|
static HBITMAP dib_bitmap;
|
|
static HGDIOBJ previous_dib;
|
|
static int using_ddraw;
|
|
|
|
static LPDIRECTDRAWCLIPPER dd_Clipper;
|
|
|
|
typedef HRESULT (WINAPI *ddCreateProc_t) (GUID FAR *,
|
|
LPDIRECTDRAW FAR *,
|
|
IUnknown FAR *);
|
|
static ddCreateProc_t ddCreate;
|
|
|
|
static int dd_window_width = 640;
|
|
static int dd_window_height = 480;
|
|
|
|
static sw_framebuffer_t swfb;
|
|
static framebuffer_t fb = { .buffer = &swfb };
|
|
|
|
static void
|
|
DD_UpdateRects (int width, int height)
|
|
{
|
|
POINT p = { .x = 0, .y = 0 };
|
|
// first we need to figure out where on the primary surface our window
|
|
// lives
|
|
ClientToScreen (win_mainwindow, &p);
|
|
GetClientRect (win_mainwindow, &dst_rect);
|
|
OffsetRect (&dst_rect, p.x, p.y);
|
|
SetRect (&src_rect, 0, 0, width, height);
|
|
}
|
|
|
|
|
|
static void
|
|
VID_CreateDDrawDriver (int width, int height)
|
|
{
|
|
DDSURFACEDESC ddsd;
|
|
|
|
using_ddraw = false;
|
|
dd_window_width = width;
|
|
dd_window_height = height;
|
|
|
|
fb.width = width;
|
|
fb.height = height;
|
|
swfb.color = malloc (width * height);
|
|
swfb.rowbytes = width;
|
|
|
|
if (!(hInstDDraw = LoadLibrary ("ddraw.dll"))) {
|
|
return;
|
|
}
|
|
if (!(ddCreate = (ddCreateProc_t) GetProcAddress (hInstDDraw,
|
|
"DirectDrawCreate"))) {
|
|
return;
|
|
}
|
|
|
|
if (FAILED (ddCreate (0, &dd_Object, 0))) {
|
|
return;
|
|
}
|
|
if (FAILED (dd_Object->lpVtbl->SetCooperativeLevel (dd_Object,
|
|
win_mainwindow,
|
|
DDSCL_NORMAL))) {
|
|
return;
|
|
}
|
|
|
|
// the primary surface in windowed mode is the full screen
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (ddsd);
|
|
ddsd.dwFlags = DDSD_CAPS;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
|
|
|
|
// ...and create it
|
|
if (FAILED (dd_Object->lpVtbl->CreateSurface (dd_Object, &ddsd,
|
|
&dd_frontbuffer, 0))) {
|
|
return;
|
|
}
|
|
|
|
// not using a clipper will slow things down and switch aero off
|
|
if (FAILED (IDirectDraw_CreateClipper (dd_Object, 0, &dd_Clipper, 0))) {
|
|
return;
|
|
}
|
|
if (FAILED (IDirectDrawClipper_SetHWnd (dd_Clipper, 0, win_mainwindow))) {
|
|
return;
|
|
}
|
|
if (FAILED (IDirectDrawSurface_SetClipper (dd_frontbuffer,
|
|
dd_Clipper))) {
|
|
return;
|
|
}
|
|
|
|
// the secondary surface is an offscreen surface that is the currect
|
|
// dimensions
|
|
// this will be blitted to the correct location on the primary surface
|
|
// (which is the full screen) during our draw op
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (ddsd);
|
|
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
|
|
ddsd.dwWidth = width;
|
|
ddsd.dwHeight = height;
|
|
|
|
if (FAILED (IDirectDraw_CreateSurface (dd_Object, &ddsd,
|
|
&dd_backbuffer, 0))) {
|
|
return;
|
|
}
|
|
|
|
// direct draw is working now
|
|
using_ddraw = true;
|
|
|
|
// create initial rects
|
|
DD_UpdateRects (dd_window_width, dd_window_height);
|
|
}
|
|
|
|
static void
|
|
VID_CreateGDIDriver (int width, int height, const byte *palette, byte **buffer,
|
|
int *rowbytes)
|
|
{
|
|
// common bitmap definition
|
|
typedef struct dibinfo {
|
|
BITMAPINFOHEADER header;
|
|
RGBQUAD acolors[256];
|
|
} dibinfo_t;
|
|
|
|
dibinfo_t dibheader;
|
|
BITMAPINFO *pbmiDIB = (BITMAPINFO *) & dibheader;
|
|
int i;
|
|
byte *dib_base = 0;
|
|
|
|
win_gdi = GetDC (win_mainwindow);
|
|
memset (&dibheader, 0, sizeof (dibheader));
|
|
|
|
// fill in the bitmap info
|
|
pbmiDIB->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
|
|
pbmiDIB->bmiHeader.biWidth = width;
|
|
pbmiDIB->bmiHeader.biHeight = height;
|
|
pbmiDIB->bmiHeader.biPlanes = 1;
|
|
pbmiDIB->bmiHeader.biCompression = BI_RGB;
|
|
pbmiDIB->bmiHeader.biSizeImage = 0;
|
|
pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmiDIB->bmiHeader.biClrUsed = 256;
|
|
pbmiDIB->bmiHeader.biClrImportant = 256;
|
|
pbmiDIB->bmiHeader.biBitCount = 8;
|
|
|
|
// fill in the palette
|
|
for (i = 0; i < 256; i++) {
|
|
// d_8to24table isn't filled in yet so this is just for testing
|
|
dibheader.acolors[i].rgbRed = palette[i * 3];
|
|
dibheader.acolors[i].rgbGreen = palette[i * 3 + 1];
|
|
dibheader.acolors[i].rgbBlue = palette[i * 3 + 2];
|
|
}
|
|
|
|
// create the DIB section
|
|
dib_bitmap = CreateDIBSection (win_gdi,
|
|
pbmiDIB,
|
|
DIB_RGB_COLORS,
|
|
(void **) &dib_base, 0, 0);
|
|
|
|
// set video buffers
|
|
if (pbmiDIB->bmiHeader.biHeight > 0) {
|
|
// bottom up
|
|
buffer[0] = dib_base + (height - 1) * width;
|
|
rowbytes[0] = -width;
|
|
} else {
|
|
// top down
|
|
buffer[0] = dib_base;
|
|
rowbytes[0] = width;
|
|
}
|
|
|
|
// clear the buffer
|
|
memset (dib_base, 0xff, width * height);
|
|
|
|
if ((dib_section = CreateCompatibleDC (win_gdi)) == 0)
|
|
Sys_Error ("DIB_Init() - CreateCompatibleDC failed\n");
|
|
|
|
if ((previous_dib = SelectObject (dib_section, dib_bitmap)) == 0)
|
|
Sys_Error ("DIB_Init() - SelectObject failed\n");
|
|
|
|
// create a palette
|
|
VID_InitGamma (palette);
|
|
viddef.vid_internal->set_palette (viddef.vid_internal->data, palette);
|
|
}
|
|
|
|
void
|
|
Win_UnloadAllDrivers (void)
|
|
{
|
|
// shut down ddraw
|
|
if (swfb.color) {
|
|
free (swfb.color);
|
|
swfb.color = 0;
|
|
}
|
|
if (swfb.depth) {
|
|
free (swfb.depth);
|
|
swfb.depth = 0;
|
|
}
|
|
|
|
if (dd_Clipper) {
|
|
IDirectDrawClipper_Release (dd_Clipper);
|
|
dd_Clipper = 0;
|
|
}
|
|
|
|
if (dd_frontbuffer) {
|
|
IDirectDrawSurface_Release (dd_frontbuffer);
|
|
dd_frontbuffer = 0;
|
|
}
|
|
|
|
if (dd_backbuffer) {
|
|
IDirectDrawSurface_Release (dd_backbuffer);
|
|
dd_backbuffer = 0;
|
|
}
|
|
|
|
if (dd_Object) {
|
|
IDirectDraw_Release (dd_Object);
|
|
dd_Object = 0;
|
|
}
|
|
|
|
if (hInstDDraw) {
|
|
FreeLibrary (hInstDDraw);
|
|
hInstDDraw = 0;
|
|
}
|
|
|
|
ddCreate = 0;
|
|
|
|
// shut down gdi
|
|
if (dib_section) {
|
|
SelectObject (dib_section, previous_dib);
|
|
DeleteDC (dib_section);
|
|
dib_section = 0;
|
|
}
|
|
|
|
if (dib_bitmap) {
|
|
DeleteObject (dib_bitmap);
|
|
dib_bitmap = 0;
|
|
}
|
|
|
|
if (win_gdi) {
|
|
// if win_gdi exists then win_mainwindow must also be valid
|
|
ReleaseDC (win_mainwindow, win_gdi);
|
|
win_gdi = 0;
|
|
}
|
|
// not using ddraw now
|
|
using_ddraw = false;
|
|
}
|
|
|
|
static void
|
|
Win_CreateDriver (void)
|
|
{
|
|
if (vid_ddraw->int_val) {
|
|
VID_CreateDDrawDriver (viddef.width, viddef.height);
|
|
}
|
|
if (!using_ddraw) {
|
|
// directdraw failed or was not requested
|
|
//
|
|
// if directdraw failed, it may be partially initialized, so make sure
|
|
// the slate is clean
|
|
Win_UnloadAllDrivers ();
|
|
|
|
VID_CreateGDIDriver (viddef.width, viddef.height, viddef.palette,
|
|
&swfb.color, &swfb.rowbytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
win_init_bufers (void *data)
|
|
{
|
|
sw_ctx_t *ctx = data;
|
|
|
|
ctx->framebuffer = &fb;
|
|
|
|
Win_UnloadAllDrivers ();
|
|
Win_CreateDriver ();
|
|
}
|
|
|
|
static void
|
|
win_set_palette (sw_ctx_t *ctx, const byte *palette)
|
|
{
|
|
palette_changed = 1;
|
|
if (palette != current_palette) {
|
|
memcpy (current_palette, palette, sizeof (current_palette));
|
|
}
|
|
for (int i = 0; i < 256; i++) {
|
|
const byte *pal = palette + 3 * i;
|
|
st2d_8to32table[i].bgra[0] = viddef.gammatable[pal[2]];
|
|
st2d_8to32table[i].bgra[1] = viddef.gammatable[pal[1]];
|
|
st2d_8to32table[i].bgra[2] = viddef.gammatable[pal[0]];
|
|
st2d_8to32table[i].bgra[3] = 255;
|
|
}
|
|
if (!win_minimized && !using_ddraw && dib_section) {
|
|
RGBQUAD colors[256];
|
|
memcpy (colors, st2d_8to32table, sizeof (colors));
|
|
for (int i = 0; i < 256; i++) {
|
|
colors[i].rgbReserved = 0;
|
|
}
|
|
colors[0].rgbRed = 0;
|
|
colors[0].rgbGreen = 0;
|
|
colors[0].rgbBlue = 0;
|
|
colors[255].rgbRed = 0xff;
|
|
colors[255].rgbGreen = 0xff;
|
|
colors[255].rgbBlue = 0xff;
|
|
|
|
if (SetDIBColorTable (dib_section, 0, 256, colors) == 0) {
|
|
Sys_Printf ("win_set_palette() - SetDIBColorTable failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dd_blit_rect (vrect_t *rect)
|
|
{
|
|
RECT TheRect;
|
|
RECT sRect, dRect;
|
|
DDSURFACEDESC ddsd;
|
|
|
|
memset (&ddsd, 0, sizeof (ddsd));
|
|
ddsd.dwSize = sizeof (DDSURFACEDESC);
|
|
|
|
// lock the correct subrect
|
|
TheRect.left = rect->x;
|
|
TheRect.right = rect->x + rect->width;
|
|
TheRect.top = rect->y;
|
|
TheRect.bottom = rect->y + rect->height;
|
|
|
|
if (IDirectDrawSurface_Lock (dd_backbuffer, &TheRect, &ddsd,
|
|
DDLOCK_WRITEONLY | DDLOCK_SURFACEMEMORYPTR,
|
|
0) == DDERR_WASSTILLDRAWING) {
|
|
return;
|
|
}
|
|
|
|
// convert pitch to 32-bit addressable
|
|
ddsd.lPitch >>= 2;
|
|
|
|
byte *src = swfb.color + rect->y * swfb.rowbytes + rect->x;
|
|
unsigned *dst = ddsd.lpSurface;
|
|
for (int y = rect->height; y-- > 0; ) {
|
|
for (int x = rect->width; x-- > 0; ) {
|
|
*dst++ = st2d_8to32table[*src++].value;
|
|
}
|
|
src += swfb.rowbytes - rect->width;
|
|
dst += ddsd.lPitch - rect->width;
|
|
}
|
|
|
|
IDirectDrawSurface_Unlock (dd_backbuffer, 0);
|
|
|
|
// correctly offset source
|
|
sRect.left = src_rect.left + rect->x;
|
|
sRect.right = src_rect.left + rect->x + rect->width;
|
|
sRect.top = src_rect.top + rect->y;
|
|
sRect.bottom = src_rect.top + rect->y + rect->height;
|
|
|
|
// correctly offset dest
|
|
dRect.left = dst_rect.left + rect->x;
|
|
dRect.right = dst_rect.left + rect->x + rect->width;
|
|
dRect.top = dst_rect.top + rect->y;
|
|
dRect.bottom = dst_rect.top + rect->y + rect->height;
|
|
|
|
// copy to front buffer
|
|
IDirectDrawSurface_Blt (dd_frontbuffer, &dRect, dd_backbuffer,
|
|
&sRect, 0, 0);
|
|
}
|
|
|
|
static void
|
|
win_sw_update (sw_ctx_t *ctx, vrect_t *rects)
|
|
{
|
|
vrect_t full_rect;
|
|
if (!win_palettized && palette_changed) {
|
|
palette_changed = false;
|
|
full_rect.x = 0;
|
|
full_rect.y = 0;
|
|
full_rect.width = viddef.width;
|
|
full_rect.height = viddef.height;
|
|
full_rect.next = 0;
|
|
rects = &full_rect;
|
|
}
|
|
|
|
if (using_ddraw) {
|
|
while (rects) {
|
|
dd_blit_rect (rects);
|
|
rects = rects->next;
|
|
}
|
|
} else if (dib_section) {
|
|
while (rects) {
|
|
BitBlt (win_gdi, rects->x, rects->y,
|
|
rects->x + rects->width, rects->y + rects->height,
|
|
dib_section, rects->x, rects->y, SRCCOPY);
|
|
rects = rects->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
win_choose_visual (sw_ctx_t *ctx)
|
|
{
|
|
}
|
|
|
|
static void
|
|
win_set_background (void)
|
|
{
|
|
// because we have set the background brush for the window to 0 (to
|
|
// avoid flickering when re-sizing the window on the desktop), we clear
|
|
// the window to black when created, otherwise it will be empty while
|
|
// Quake starts up. This also prevents a screen flash to white when
|
|
// switching drivers. it still flashes, but at least it's black now
|
|
HDC hdc = GetDC (win_mainwindow);
|
|
PatBlt (hdc, 0, 0, win_rect.right, win_rect.bottom, BLACKNESS);
|
|
ReleaseDC (win_mainwindow, hdc);
|
|
}
|
|
|
|
static void
|
|
win_create_context (sw_ctx_t *ctx)
|
|
{
|
|
// shutdown any old driver that was active
|
|
Win_UnloadAllDrivers ();
|
|
|
|
win_sw_context = ctx;
|
|
|
|
win_set_background ();
|
|
|
|
// create the new driver
|
|
using_ddraw = false;
|
|
|
|
viddef.vid_internal->init_buffers = win_init_bufers;
|
|
}
|
|
|
|
sw_ctx_t *
|
|
Win_SW_Context (void)
|
|
{
|
|
sw_ctx_t *ctx = calloc (1, sizeof (sw_ctx_t));
|
|
ctx->set_palette = win_set_palette;
|
|
ctx->choose_visual = win_choose_visual;
|
|
ctx->create_context = win_create_context;
|
|
ctx->update = win_sw_update;
|
|
return ctx;
|
|
}
|
|
|
|
void
|
|
Win_SW_Init_Cvars (void)
|
|
{
|
|
}
|