diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj b/desmume/src/frontend/windows/DeSmuME.vcxproj index 794d7106f..d4d430baa 100644 --- a/desmume/src/frontend/windows/DeSmuME.vcxproj +++ b/desmume/src/frontend/windows/DeSmuME.vcxproj @@ -284,6 +284,8 @@ + + @@ -371,6 +373,7 @@ + @@ -582,6 +585,8 @@ + + @@ -657,6 +662,7 @@ + diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters index 4047bb282..20bfe9dc2 100644 --- a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters +++ b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters @@ -921,6 +921,15 @@ utils\colorspacehandler + + frontend\Windows + + + frontend\Windows + + + frontend\Windows + @@ -1610,6 +1619,15 @@ utils\colorspacehandler + + frontend\Windows + + + frontend\Windows + + + frontend\Windows + diff --git a/desmume/src/frontend/windows/ddraw.cpp b/desmume/src/frontend/windows/ddraw.cpp new file mode 100644 index 000000000..afbdb58b9 --- /dev/null +++ b/desmume/src/frontend/windows/ddraw.cpp @@ -0,0 +1,185 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#include "ddraw.h" + +#include "directx/ddraw.h" +#include "GPU.h" +#include "types.h" + +const char *DDerrors[] = { + "no errors", + "Unable to initialize DirectDraw", + "Unable to set DirectDraw Cooperative Level", + "Unable to create DirectDraw primary surface", + "Unable to set DirectDraw clipper" }; + +DDRAW::DDRAW() : + handle(NULL), clip(NULL) +{ + surface.primary = NULL; + surface.back = NULL; + memset(&surfDesc, 0, sizeof(surfDesc)); + memset(&surfDescBack, 0, sizeof(surfDescBack)); +} + +u32 DDRAW::create(HWND hwnd) +{ + if (handle) return 0; + + if (FAILED(DirectDrawCreateEx(NULL, (LPVOID*)&handle, IID_IDirectDraw7, NULL))) + return 1; + + if (FAILED(handle->SetCooperativeLevel(hwnd, DDSCL_NORMAL))) + return 2; + + createSurfaces(hwnd); + + return 0; +} + +bool DDRAW::release() +{ + if (!handle) return true; + + if (clip != NULL) clip->Release(); + if (surface.back != NULL) surface.back->Release(); + if (surface.primary != NULL) surface.primary->Release(); + + if (FAILED(handle->Release())) return false; + return true; +} + +bool DDRAW::createBackSurface(int width, int height) +{ + if (surface.back) { surface.back->Release(); surface.back = NULL; } + + memset(&surfDescBack, 0, sizeof(surfDescBack)); + surfDescBack.dwSize = sizeof(surfDescBack); + surfDescBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + surfDescBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + + if (systemMemory) + surfDescBack.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + else + surfDescBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + + + surfDescBack.dwWidth = width; + surfDescBack.dwHeight = height; + + if (FAILED(handle->CreateSurface(&surfDescBack, &surface.back, NULL))) return false; + + return true; +} + +bool DDRAW::createSurfaces(HWND hwnd) +{ + if (!handle) return true; + + if (clip) { clip->Release(); clip = NULL; } + if (surface.primary) { surface.primary->Release(); surface.primary = NULL; } + + + // primary + memset(&surfDesc, 0, sizeof(surfDesc)); + surfDesc.dwSize = sizeof(surfDesc); + surfDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + surfDesc.dwFlags = DDSD_CAPS; + if (FAILED(handle->CreateSurface(&surfDesc, &surface.primary, NULL))) + return false; + + //default doesnt matter much, itll get adjusted later + if (!createBackSurface(GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2)) + return false; + + if (FAILED(handle->CreateClipper(0, &clip, NULL))) return false; + if (FAILED(clip->SetHWnd(0, hwnd))) return false; + if (FAILED(surface.primary->SetClipper(clip))) return false; + + return true; +} + +bool DDRAW::lock() +{ + if (!handle) return true; + if (!surface.back) return false; + memset(&surfDescBack, 0, sizeof(surfDescBack)); + surfDescBack.dwSize = sizeof(surfDescBack); + surfDescBack.dwFlags = DDSD_ALL; + + HRESULT res = surface.back->Lock(NULL, &surfDescBack, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); + if (FAILED(res)) + { + //INFO("DDraw failed: Lock %i\n", res); + if (res == DDERR_SURFACELOST) + { + res = surface.back->Restore(); + if (FAILED(res)) return false; + } + } + return true; +} + +bool DDRAW::unlock() +{ + if (!handle) return true; + if (!surface.back) return false; + if (FAILED(surface.back->Unlock((LPRECT)surfDescBack.lpSurface))) return false; + return true; +} + +bool DDRAW::OK() +{ + if (!handle) return false; + if (!surface.primary) return false; + if (!surface.back) return false; + return true; +} + +bool DDRAW::blt(LPRECT dst, LPRECT src) +{ + if (!handle) return true; + if (!surface.primary) return false; + if (!surface.back) return false; + + if (vSync) + { + //this seems to block the whole process. this destroys the display thread and will easily block the emulator to 30fps. + //IDirectDraw7_WaitForVerticalBlank(handle,DDWAITVB_BLOCKBEGIN,0); + + for (;;) + { + BOOL vblank; + IDirectDraw7_GetVerticalBlankStatus(handle, &vblank); + if (vblank) break; + //must be a greedy loop since vblank is small relative to 1msec minimum Sleep() resolution. + } + } + + HRESULT res = surface.primary->Blt(dst, surface.back, src, DDBLT_WAIT, 0); + if (FAILED(res)) + { + //INFO("DDraw failed: Blt %i\n", res); + if (res == DDERR_SURFACELOST) + { + res = surface.primary->Restore(); + if (FAILED(res)) return false; + } + } + return true; +} diff --git a/desmume/src/frontend/windows/ddraw.h b/desmume/src/frontend/windows/ddraw.h new file mode 100644 index 000000000..ca7df272c --- /dev/null +++ b/desmume/src/frontend/windows/ddraw.h @@ -0,0 +1,54 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#ifndef _DDRAW_H_ +#define _DDRAW_H_ + +#include "types.h" +#include "directx/ddraw.h" + +extern const char *DDerrors[]; + +struct DDRAW +{ + DDRAW(); + + u32 create(HWND hwnd); + bool release(); + bool createSurfaces(HWND hwnd); + bool createBackSurface(int width, int height); + bool lock(); + bool unlock(); + bool blt(LPRECT dst, LPRECT src); + bool OK(); + + LPDIRECTDRAW7 handle; + struct + { + LPDIRECTDRAWSURFACE7 primary; + LPDIRECTDRAWSURFACE7 back; + } surface; + + DDSURFACEDESC2 surfDesc; + DDSURFACEDESC2 surfDescBack; + LPDIRECTDRAWCLIPPER clip; + + bool systemMemory = false; + bool vSync = false; +}; + +#endif \ No newline at end of file diff --git a/desmume/src/frontend/windows/display.cpp b/desmume/src/frontend/windows/display.cpp new file mode 100644 index 000000000..61021e197 --- /dev/null +++ b/desmume/src/frontend/windows/display.cpp @@ -0,0 +1,961 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#include "display.h" + +#include +#include + +#include "main.h" +#include "windriver.h" +#include "winutil.h" + +DDRAW ddraw; +GLDISPLAY gldisplay; +u32 displayMethod; + +VideoInfo video; + +int gpu_bpp = 18; + + +int frameskiprate = 0; +int lastskiprate = 0; +int emu_paused = 0; +bool frameAdvance = false; + +bool SeparationBorderDrag = true; +int ScreenGapColor = 0xFFFFFF; +/*const u32 gapColors[16] = { +Color::Gray, +Color::Brown, +Color::Red, +Color::Pink, +Color::Orange, +Color::Yellow, +Color::LightGreen, +Color::Lime, +Color::Green, +Color::SeaGreen, +Color::SkyBlue, +Color::Blue, +Color::DarkBlue, +Color::DarkViolet, +Color::Purple, +Color::Fuchsia +};*/ + +RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect; +RECT MainScreenSrcRect, SubScreenSrcRect; + +bool ForceRatio = true; +bool PadToInteger = false; +float screenSizeRatio = 1.0f; +bool vCenterResizedScr = true; + +//triple buffering logic +struct DisplayBuffer +{ + DisplayBuffer() + : buffer(NULL) + , size(0) + { + } + u32* buffer; + size_t size; //[256*192*4]; +} displayBuffers[3]; + +volatile int currDisplayBuffer = -1; +volatile int newestDisplayBuffer = -2; +slock_t *display_mutex = NULL; +sthread_t *display_thread = NULL; +volatile bool display_die = false; +HANDLE display_wakeup_event = INVALID_HANDLE_VALUE; +HANDLE display_done_event = INVALID_HANDLE_VALUE; +DWORD display_done_timeout = 500; + +int displayPostponeType = 0; +DWORD displayPostponeUntil = ~0; +bool displayNoPostponeNext = false; + +DWORD display_invoke_argument = 0; +void(*display_invoke_function)(DWORD) = NULL; +HANDLE display_invoke_ready_event = INVALID_HANDLE_VALUE; +HANDLE display_invoke_done_event = INVALID_HANDLE_VALUE; +DWORD display_invoke_timeout = 500; +CRITICAL_SECTION display_invoke_handler_cs; +static void InvokeOnMainThread(void(*function)(DWORD), DWORD argument); + +int scanline_filter_a = 0; +int scanline_filter_b = 2; +int scanline_filter_c = 2; +int scanline_filter_d = 4; + +#pragma pack(push,1) +struct pix24 +{ + u8 b, g, r; + FORCEINLINE pix24(u32 s) : b(s & 0xFF), g((s & 0xFF00) >> 8), r((s & 0xFF0000) >> 16) {} +}; +struct pix16 +{ + u16 c; + FORCEINLINE pix16(u32 s) : c(((s & 0xF8) >> 3) | ((s & 0xFC00) >> 5) | ((s & 0xF80000) >> 8)) {} +}; +struct pix15 +{ + u16 c; + FORCEINLINE pix15(u32 s) : c(((s & 0xF8) >> 3) | ((s & 0xF800) >> 6) | ((s & 0xF80000) >> 9)) {} +}; +#pragma pack(pop) + +void GetNdsScreenRect(RECT* r) +{ + RECT zero; + SetRect(&zero, 0, 0, 0, 0); + if (zero == MainScreenRect) *r = SubScreenRect; + else if (zero == SubScreenRect || video.layout == 2) *r = MainScreenRect; + else + { + RECT* dstRects[2] = { &MainScreenRect, &SubScreenRect }; + int left = min(dstRects[0]->left, dstRects[1]->left); + int top = min(dstRects[0]->top, dstRects[1]->top); + int right = max(dstRects[0]->right, dstRects[1]->right); + int bottom = max(dstRects[0]->bottom, dstRects[1]->bottom); + SetRect(r, left, top, right, bottom); + } +} + +struct DisplayLayoutInfo +{ + int vx, vy, vw, vh; + float widthScale, heightScale; + int bufferWidth, bufferHeight; +}; +//performs aspect ratio letterboxing correction and integer clamping +DisplayLayoutInfo CalculateDisplayLayout(RECT rcClient, bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight) +{ + DisplayLayoutInfo ret; + + //do maths on the viewport and the native resolution and the user settings to get a display rectangle + SIZE sz = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top }; + + float widthScale = (float)sz.cx / targetWidth; + float heightScale = (float)sz.cy / targetHeight; + if (maintainAspect) + { + if (widthScale > heightScale) widthScale = heightScale; + if (heightScale > widthScale) heightScale = widthScale; + } + if (maintainInteger) + { + widthScale = floorf(widthScale); + heightScale = floorf(heightScale); + } + ret.vw = (int)(widthScale * targetWidth); + ret.vh = (int)(heightScale * targetHeight); + ret.vx = (sz.cx - ret.vw) / 2; + ret.vy = (sz.cy - ret.vh) / 2; + ret.widthScale = widthScale; + ret.heightScale = heightScale; + ret.bufferWidth = sz.cx; + ret.bufferHeight = sz.cy; + + return ret; +} +//reformulates CalculateDisplayLayout() into a format more convenient for this purpose +RECT CalculateDisplayLayoutWrapper(RECT rcClient, int targetWidth, int targetHeight, int tbHeight, bool maximized) +{ + bool maintainInteger = !!PadToInteger; + bool maintainAspect = !!ForceRatio; + + if (maintainInteger) maintainAspect = true; + + //nothing to do here if maintain aspect isnt chosen + if (!maintainAspect) return rcClient; + + RECT rc = { rcClient.left, rcClient.top + tbHeight, rcClient.right, rcClient.bottom }; + DisplayLayoutInfo dli = CalculateDisplayLayout(rc, maintainAspect, maintainInteger, targetWidth, targetHeight); + rc.left = rcClient.left + dli.vx; + rc.top = rcClient.top + dli.vy; + rc.right = rc.left + dli.vw; + rc.bottom = rc.top + dli.vh + tbHeight; + return rc; +} + +template static void doRotate(void* dst) +{ + u8* buffer = (u8*)dst; + int size = video.size(); + u32* src = (u32*)video.finalBuffer(); + int width = video.width; + int height = video.height; + int pitch = ddraw.surfDescBack.lPitch; + + switch (video.rotation) + { + case 0: + case 180: + { + if (pitch == 1024) + { + if (video.rotation == 0) + if (sizeof(T) == sizeof(u32)) + memcpy(buffer, src, size * sizeof(u32)); + else + for (int i = 0; i < size; i++) + ((T*)buffer)[i] = src[i]; + else // if(video.rotation==180) + for (int i = 0, j = size - 1; j >= 0; i++, j--) + ((T*)buffer)[i] = src[j]; + } + else + { + if (video.rotation == 0) + if (sizeof(T) != sizeof(u32)) + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + ((T*)buffer)[x] = src[(y * width) + x]; + + buffer += pitch; + } + else + for (int y = 0; y < height; y++) + { + memcpy(buffer, &src[y * width], width * sizeof(u32)); + buffer += pitch; + } + else // if(video.rotation==180) + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + ((T*)buffer)[x] = src[height*width - (y * width) - x - 1]; + + buffer += pitch; + } + } + } + break; + case 90: + case 270: + { + if (video.rotation == 90) + for (int y = 0; y < width; y++) + { + for (int x = 0; x < height; x++) + ((T*)buffer)[x] = src[(((height - 1) - x) * width) + y]; + + buffer += pitch; + } + else + for (int y = 0; y < width; y++) + { + for (int x = 0; x < height; x++) + ((T*)buffer)[x] = src[((x)* width) + (width - 1) - y]; + + buffer += pitch; + } + } + break; + } +} +static void DD_FillRect(LPDIRECTDRAWSURFACE7 surf, int left, int top, int right, int bottom, DWORD color) +{ + RECT r; + SetRect(&r, left, top, right, bottom); + DDBLTFX fx; + memset(&fx, 0, sizeof(DDBLTFX)); + fx.dwSize = sizeof(DDBLTFX); + //fx.dwFillColor = color; + fx.dwFillColor = 0; //color is just for debug + surf->Blt(&r, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx); +} + +static void OGL_DrawTexture(RECT* srcRects, RECT* dstRects) +{ + //don't change the original rects + RECT sRects[2]; + sRects[0] = RECT(srcRects[0]); + sRects[1] = RECT(srcRects[1]); + + //use clear+scissor for gap + if (video.screengap > 0) + { + //adjust client rect into scissor rect (0,0 at bottomleft) + glScissor(dstRects[2].left, dstRects[2].bottom, dstRects[2].right - dstRects[2].left, dstRects[2].top - dstRects[2].bottom); + + u32 color_rev = (u32)ScreenGapColor; + int r = (color_rev >> 0) & 0xFF; + int g = (color_rev >> 8) & 0xFF; + int b = (color_rev >> 16) & 0xFF; + glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + + //draw two screens + glBegin(GL_QUADS); + for (int i = 0; i<2; i++) + { + //none of this makes any goddamn sense. dont even try. + int idx = i; + int ofs = 0; + switch (video.rotation) + { + case 0: + break; + case 90: + ofs = 3; + idx = 1 - i; + std::swap(sRects[idx].right, sRects[idx].bottom); + std::swap(sRects[idx].left, sRects[idx].top); + break; + case 180: + idx = 1 - i; + ofs = 2; + break; + case 270: + std::swap(sRects[idx].right, sRects[idx].bottom); + std::swap(sRects[idx].left, sRects[idx].top); + ofs = 1; + break; + } + float u1 = sRects[idx].left / (float)video.width; + float u2 = sRects[idx].right / (float)video.width; + float v1 = sRects[idx].top / (float)video.height; + float v2 = sRects[idx].bottom / (float)video.height; + float u[] = { u1,u2,u2,u1 }; + float v[] = { v1,v1,v2,v2 }; + + glTexCoord2f(u[(ofs + 0) % 4], v[(ofs + 0) % 4]); + glVertex2i(dstRects[i].left, dstRects[i].top); + + glTexCoord2f(u[(ofs + 1) % 4], v[(ofs + 1) % 4]); + glVertex2i(dstRects[i].right, dstRects[i].top); + + glTexCoord2f(u[(ofs + 2) % 4], v[(ofs + 2) % 4]); + glVertex2i(dstRects[i].right, dstRects[i].bottom); + + glTexCoord2f(u[(ofs + 3) % 4], v[(ofs + 3) % 4]); + glVertex2i(dstRects[i].left, dstRects[i].bottom); + } + glEnd(); +} +static void OGL_DoDisplay() +{ + if (!gldisplay.begin(MainWindow->getHWnd())) return; + + //the ds screen fills the texture entirely, so we dont have garbage at edge to worry about, + //but we need to make sure this is clamped for when filtering is selected + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (gldisplay.filter) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + RECT rc; + HWND hwnd = MainWindow->getHWnd(); + GetClientRect(hwnd, &rc); + int width = rc.right - rc.left; + int height = rc.bottom - rc.top; + + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, (float)width, (float)height, 0.0f, -100.0f, 100.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + //clear entire area, for cases where the screen is maximized + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + // get dest and src rects + RECT dr[] = { MainScreenRect, SubScreenRect, GapRect }; + for (int i = 0; i<2; i++) //dont change gap rect, for some reason + { + ScreenToClient(hwnd, (LPPOINT)&dr[i].left); + ScreenToClient(hwnd, (LPPOINT)&dr[i].right); + } + dr[2].bottom = height - dr[2].bottom; + dr[2].top = height - dr[2].top; + + RECT srcRects[2]; + const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main); + + if (video.swap == 0) + { + srcRects[0] = MainScreenSrcRect; + srcRects[1] = SubScreenSrcRect; + if (osd) osd->swapScreens = false; + } + else if (video.swap == 1) + { + srcRects[0] = SubScreenSrcRect; + srcRects[1] = MainScreenSrcRect; + if (osd) osd->swapScreens = true; + } + else if (video.swap == 2) + { + srcRects[0] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect; + srcRects[1] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect; + if (osd) osd->swapScreens = !isMainGPUFirst; + } + else if (video.swap == 3) + { + srcRects[0] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect; + srcRects[1] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect; + if (osd) osd->swapScreens = isMainGPUFirst; + } + + //printf("%d,%d %dx%d -- %d,%d %dx%d\n", + // srcRects[0].left,srcRects[0].top, srcRects[0].right-srcRects[0].left, srcRects[0].bottom-srcRects[0].top, + // srcRects[1].left,srcRects[1].top, srcRects[1].right-srcRects[1].left, srcRects[1].bottom-srcRects[1].top + // ); + + glEnable(GL_TEXTURE_2D); + // draw DS display + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, video.width, video.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, video.finalBuffer()); + OGL_DrawTexture(srcRects, dr); + + // draw HUD + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, aggDraw.hud->buf().buf()); + OGL_DrawTexture(srcRects, dr); + glDisable(GL_BLEND); + + gldisplay.showPage(); + + gldisplay.end(); +} +static void DD_DoDisplay() +{ + if (ddraw.surfDescBack.dwWidth != video.rotatedwidth() || ddraw.surfDescBack.dwHeight != video.rotatedheight()) + ddraw.createBackSurface(video.rotatedwidth(), video.rotatedheight()); + + if (!ddraw.lock()) return; + char* buffer = (char*)ddraw.surfDescBack.lpSurface; + + switch (ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount) + { + case 32: + doRotate(ddraw.surfDescBack.lpSurface); + break; + case 24: + doRotate(ddraw.surfDescBack.lpSurface); + break; + case 16: + if (ddraw.surfDescBack.ddpfPixelFormat.dwGBitMask != 0x3E0) + doRotate(ddraw.surfDescBack.lpSurface); + else + doRotate(ddraw.surfDescBack.lpSurface); + break; + case 0: + break; + default: + INFO("Unsupported color depth: %i bpp\n", ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount); + //emu_halt(); + break; + } + if (!ddraw.unlock()) return; + + RECT* dstRects[2] = { &MainScreenRect, &SubScreenRect }; + RECT* srcRects[2]; + const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main); + + if (video.swap == 0) + { + srcRects[0] = &MainScreenSrcRect; + srcRects[1] = &SubScreenSrcRect; + if (osd) osd->swapScreens = false; + } + else if (video.swap == 1) + { + srcRects[0] = &SubScreenSrcRect; + srcRects[1] = &MainScreenSrcRect; + if (osd) osd->swapScreens = true; + } + else if (video.swap == 2) + { + srcRects[0] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect; + srcRects[1] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect; + if (osd) osd->swapScreens = !isMainGPUFirst; + } + else if (video.swap == 3) + { + srcRects[0] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect; + srcRects[1] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect; + if (osd) osd->swapScreens = isMainGPUFirst; + } + + //this code fills in all the undrawn areas + //it is probably a waste of time to keep black-filling all this, but we need to do it to be safe. + { + RECT wr; + GetWindowRect(MainWindow->getHWnd(), &wr); + RECT r; + GetNdsScreenRect(&r); + int left = r.left; + int top = r.top; + int right = r.right; + int bottom = r.bottom; + //printf("%d %d %d %d / %d %d %d %d\n",fullScreen.left,fullScreen.top,fullScreen.right,fullScreen.bottom,left,top,right,bottom); + //printf("%d %d %d %d / %d %d %d %d\n",MainScreenRect.left,MainScreenRect.top,MainScreenRect.right,MainScreenRect.bottom,SubScreenRect.left,SubScreenRect.top,SubScreenRect.right,SubScreenRect.bottom); + if (ddraw.OK()) + { + DD_FillRect(ddraw.surface.primary, 0, 0, left, top, RGB(255, 0, 0)); //topleft + DD_FillRect(ddraw.surface.primary, left, 0, right, top, RGB(128, 0, 0)); //topcenter + DD_FillRect(ddraw.surface.primary, right, 0, wr.right, top, RGB(0, 255, 0)); //topright + DD_FillRect(ddraw.surface.primary, 0, top, left, bottom, RGB(0, 128, 0)); //left + DD_FillRect(ddraw.surface.primary, right, top, wr.right, bottom, RGB(0, 0, 255)); //right + DD_FillRect(ddraw.surface.primary, 0, bottom, left, wr.bottom, RGB(0, 0, 128)); //bottomleft + DD_FillRect(ddraw.surface.primary, left, bottom, right, wr.bottom, RGB(255, 0, 255)); //bottomcenter + DD_FillRect(ddraw.surface.primary, right, bottom, wr.right, wr.bottom, RGB(0, 255, 255)); //bottomright + if (video.layout == 1) + { + DD_FillRect(ddraw.surface.primary, SubScreenRect.left, wr.top, wr.right, SubScreenRect.top, RGB(0, 0, 0)); //Top gap left when centering the resized screen + DD_FillRect(ddraw.surface.primary, SubScreenRect.left, SubScreenRect.bottom, wr.right, wr.bottom, RGB(0, 0, 0)); //Bottom gap left when centering the resized screen + } + } + } + + for (int i = 0; i < 2; i++) + { + if (i && video.layout == 2) + break; + + if (!ddraw.blt(dstRects[i], srcRects[i])) return; + } + + if (video.layout == 1) return; + if (video.layout == 2) return; + + // Gap + if (video.screengap > 0) + { + //u32 color = gapColors[win_fw_config.fav_colour]; + //u32 color_rev = (((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16)); + u32 color_rev = (u32)ScreenGapColor; + + HDC dc; + HBRUSH brush; + + dc = GetDC(MainWindow->getHWnd()); + brush = CreateSolidBrush(color_rev); + + FillRect(dc, &GapRect, brush); + + DeleteObject((HGDIOBJ)brush); + ReleaseDC(MainWindow->getHWnd(), dc); + } +} + +void displayProc() +{ + slock_lock(display_mutex); + + //find a buffer to display + int todo = newestDisplayBuffer; + bool alreadyDisplayed = (todo == currDisplayBuffer); + + slock_unlock(display_mutex); + + //something new to display: + if (!alreadyDisplayed) { + //start displaying a new buffer + currDisplayBuffer = todo; + video.srcBuffer = (u8*)displayBuffers[currDisplayBuffer].buffer; + video.srcBufferSize = displayBuffers[currDisplayBuffer].size; + } + DoDisplay(); +} +void displayThread(void *arg) +{ + do + { + if ((MainWindow == NULL) || IsMinimized(MainWindow->getHWnd())) + { + WaitForSingleObject(display_wakeup_event, INFINITE); + } + else if ((emu_paused || !execute || !romloaded) && (!HudEditorMode && !CommonSettings.hud.ShowInputDisplay && !CommonSettings.hud.ShowGraphicalInputDisplay)) + { + WaitForSingleObject(display_wakeup_event, 250); + } + else + { + WaitForSingleObject(display_wakeup_event, 10); + } + + if (display_die) + { + break; + } + + displayProc(); + SetEvent(display_done_event); + } while (!display_die); +} + +static void DoDisplay_DrawHud() +{ + osd->update(); + DrawHUD(); + osd->clear(); +} + +void Display() +{ + const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo(); + + if (CommonSettings.single_core()) + { + video.srcBuffer = (u8*)dispInfo.masterCustomBuffer; + video.srcBufferSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2; + DoDisplay(); + } + else + { + if (display_thread == NULL) + { + display_mutex = slock_new(); + display_thread = sthread_create(&displayThread, nullptr); + } + + slock_lock(display_mutex); + + if (int diff = (currDisplayBuffer + 1) % 3 - newestDisplayBuffer) + newestDisplayBuffer += diff; + else newestDisplayBuffer = (currDisplayBuffer + 2) % 3; + + DisplayBuffer& db = displayBuffers[newestDisplayBuffer]; + size_t targetSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2; + if (db.size != targetSize) + { + free_aligned(db.buffer); + db.buffer = (u32*)malloc_alignedPage(targetSize); + db.size = targetSize; + } + memcpy(db.buffer, dispInfo.masterCustomBuffer, targetSize); + + slock_unlock(display_mutex); + + SetEvent(display_wakeup_event); + } +} + +//does a single display work unit. only to be used from the display thread +void DoDisplay() +{ + Lock lock(win_backbuffer_sync); + + if (displayPostponeType && !displayNoPostponeNext && (displayPostponeType < 0 || timeGetTime() < displayPostponeUntil)) + return; + displayNoPostponeNext = false; + + //we have to do a copy here because we're about to draw the OSD onto it. bummer. + if (gpu_bpp == 15) + ColorspaceConvertBuffer555To8888Opaque((u16 *)video.srcBuffer, video.buffer, video.srcBufferSize / 2); + else + ColorspaceConvertBuffer888XTo8888Opaque((u32*)video.srcBuffer, video.buffer, video.srcBufferSize / 4); + + //some games use the backlight for fading effects + const size_t pixCount = video.prefilterWidth * video.prefilterHeight / 2; + const NDSDisplayInfo &displayInfo = GPU->GetDisplayInfo(); + ColorspaceApplyIntensityToBuffer32(video.buffer, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Main]); + ColorspaceApplyIntensityToBuffer32(video.buffer + pixCount, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Touch]); + + // Lua draws to the HUD buffer, so clear it here instead of right before redrawing the HUD. + aggDraw.hud->clear(); + if (AnyLuaActive()) + { + if (sthread_isself(display_thread)) + { + InvokeOnMainThread((void(*)(DWORD)) + CallRegisteredLuaFunctions, LUACALL_AFTEREMULATIONGUI); + } + else + { + CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI); + } + } + + // draw hud + DoDisplay_DrawHud(); + if (displayMethod == DISPMETHOD_DDRAW_HW || displayMethod == DISPMETHOD_DDRAW_SW) + { + // DirectDraw doesn't support alpha blending, so we must scale and overlay the HUD ourselves. + T_AGG_RGBA target((u8*)video.buffer, video.prefilterWidth, video.prefilterHeight, video.prefilterWidth * 4); + target.transformImage(aggDraw.hud->image(), 0, 0, video.prefilterWidth, video.prefilterHeight); + } + + //apply user's filter + video.filter(); + + if (displayMethod == DISPMETHOD_DDRAW_HW || displayMethod == DISPMETHOD_DDRAW_SW) + { + gldisplay.kill(); + DD_DoDisplay(); + } + else + { + OGL_DoDisplay(); + } +} + +void KillDisplay() +{ + display_die = true; + SetEvent(display_wakeup_event); + if (display_thread) + sthread_join(display_thread); +} + +void UpdateWndRects(HWND hwnd, RECT* newClientRect) +{ + POINT ptClient; + RECT rc; + + GapRect = {0,0,0,0}; + + bool maximized = IsZoomed(hwnd) != FALSE; + + int wndWidth, wndHeight; + int defHeight = video.height; + if (video.layout == 0) + defHeight += video.scaledscreengap(); + float ratio; + int oneScreenHeight, gapHeight; + int tbheight; + + //if we're in the middle of resizing the window, GetClientRect will return the old rect + if (newClientRect) + rc = RECT(*newClientRect); + else + GetClientRect(hwnd, &rc); + + if (maximized) + rc = FullScreenRect; + + tbheight = MainWindowToolbar->GetHeight(); + + if (video.layout == 1) //horizontal + { + rc = CalculateDisplayLayoutWrapper(rc, (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * 2 / screenSizeRatio), GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized); + + wndWidth = (rc.bottom - rc.top) - tbheight; + wndHeight = (rc.right - rc.left); + + ratio = ((float)wndHeight / (float)512); + oneScreenHeight = (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * ratio); + int oneScreenWidth = (int)((float)GPU_FRAMEBUFFER_NATIVE_HEIGHT * ratio); + int vResizedScrOffset = 0; + + // Main screen + ptClient.x = rc.left; + ptClient.y = rc.top; + ClientToScreen(hwnd, &ptClient); + MainScreenRect.left = ptClient.x; + MainScreenRect.top = ptClient.y; + ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio); + ptClient.y = (rc.top + wndWidth); + ClientToScreen(hwnd, &ptClient); + MainScreenRect.right = ptClient.x; + MainScreenRect.bottom = ptClient.y; + + // Sub screen + ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio); + if (vCenterResizedScr && ForceRatio) + vResizedScrOffset = (wndWidth - oneScreenWidth * (2 - screenSizeRatio)) / 2; + ptClient.y = rc.top + vResizedScrOffset; + ClientToScreen(hwnd, &ptClient); + SubScreenRect.left = ptClient.x; + SubScreenRect.top = ptClient.y; + ptClient.x = (rc.left + oneScreenHeight * 2); + if (ForceRatio) + ptClient.y = (rc.top + vResizedScrOffset + oneScreenWidth * (2 - screenSizeRatio)); + else + ptClient.y = (rc.top + wndWidth); + ClientToScreen(hwnd, &ptClient); + SubScreenRect.right = ptClient.x; + SubScreenRect.bottom = ptClient.y; + } + else + if (video.layout == 2) //one screen + { + rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized); + + wndWidth = (rc.bottom - rc.top) - tbheight; + wndHeight = (rc.right - rc.left); + + ratio = ((float)wndHeight / (float)defHeight); + oneScreenHeight = (int)((video.height) * ratio); + + // Main screen + ptClient.x = rc.left; + ptClient.y = rc.top; + ClientToScreen(hwnd, &ptClient); + MainScreenRect.left = ptClient.x; + MainScreenRect.top = ptClient.y; + ptClient.x = (rc.left + oneScreenHeight); + ptClient.y = (rc.top + wndWidth); + ClientToScreen(hwnd, &ptClient); + MainScreenRect.right = ptClient.x; + MainScreenRect.bottom = ptClient.y; + SetRectEmpty(&SubScreenRect); + } + else + if (video.layout == 0) //vertical + { + //apply logic to correct things if forced integer is selected + if ((video.rotation == 90) || (video.rotation == 270)) + { + rc = CalculateDisplayLayoutWrapper(rc, (GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2) + video.screengap, GPU_FRAMEBUFFER_NATIVE_WIDTH, tbheight, maximized); + } + else + { + rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, (GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2) + video.screengap, tbheight, maximized); + } + + if ((video.rotation == 90) || (video.rotation == 270)) + { + wndWidth = (rc.bottom - rc.top) - tbheight; + wndHeight = (rc.right - rc.left); + } + else + { + wndWidth = (rc.right - rc.left); + wndHeight = (rc.bottom - rc.top) - tbheight; + } + + ratio = ((float)wndHeight / (float)defHeight); + + oneScreenHeight = (int)((video.height / 2) * ratio); + gapHeight = (wndHeight - (oneScreenHeight * 2)); + + if ((video.rotation == 90) || (video.rotation == 270)) + { + // Main screen + ptClient.x = rc.left; + ptClient.y = rc.top; + ClientToScreen(hwnd, &ptClient); + MainScreenRect.left = ptClient.x; + MainScreenRect.top = ptClient.y; + ptClient.x = (rc.left + oneScreenHeight); + ptClient.y = (rc.top + wndWidth); + ClientToScreen(hwnd, &ptClient); + MainScreenRect.right = ptClient.x; + MainScreenRect.bottom = ptClient.y; + + //if there was no specified screen gap, extend the top screen to cover the extra column + if (video.screengap == 0) MainScreenRect.right += gapHeight; + + // Sub screen + ptClient.x = (rc.left + oneScreenHeight + gapHeight); + ptClient.y = rc.top; + ClientToScreen(hwnd, &ptClient); + SubScreenRect.left = ptClient.x; + SubScreenRect.top = ptClient.y; + ptClient.x = (rc.left + oneScreenHeight + gapHeight + oneScreenHeight); + ptClient.y = (rc.top + wndWidth); + ClientToScreen(hwnd, &ptClient); + SubScreenRect.right = ptClient.x; + SubScreenRect.bottom = ptClient.y; + + // Gap + GapRect.left = (rc.left + oneScreenHeight); + GapRect.top = rc.top; + GapRect.right = (rc.left + oneScreenHeight + gapHeight); + GapRect.bottom = (rc.top + wndWidth); + } + else + { + + + // Main screen + ptClient.x = rc.left; + ptClient.y = rc.top; + ClientToScreen(hwnd, &ptClient); + MainScreenRect.left = ptClient.x; + MainScreenRect.top = ptClient.y; + ptClient.x = (rc.left + wndWidth); + ptClient.y = (rc.top + oneScreenHeight); + ClientToScreen(hwnd, &ptClient); + MainScreenRect.right = ptClient.x; + MainScreenRect.bottom = ptClient.y; + + //if there was no specified screen gap, extend the top screen to cover the extra row + if (video.screengap == 0) MainScreenRect.bottom += gapHeight; + + // Sub screen + ptClient.x = rc.left; + ptClient.y = (rc.top + oneScreenHeight + gapHeight); + ClientToScreen(hwnd, &ptClient); + SubScreenRect.left = ptClient.x; + SubScreenRect.top = ptClient.y; + ptClient.x = (rc.left + wndWidth); + ptClient.y = (rc.top + oneScreenHeight + gapHeight + oneScreenHeight); + ClientToScreen(hwnd, &ptClient); + SubScreenRect.right = ptClient.x; + SubScreenRect.bottom = ptClient.y; + + // Gap + GapRect.left = rc.left; + GapRect.top = (rc.top + oneScreenHeight); + GapRect.right = (rc.left + wndWidth); + GapRect.bottom = (rc.top + oneScreenHeight + gapHeight); + } + } + + MainScreenRect.top += tbheight; + MainScreenRect.bottom += tbheight; + SubScreenRect.top += tbheight; + SubScreenRect.bottom += tbheight; + GapRect.top += tbheight; + GapRect.bottom += tbheight; +} + +static void InvokeOnMainThread(void(*function)(DWORD), DWORD argument) +{ + ResetEvent(display_invoke_ready_event); + display_invoke_argument = argument; + display_invoke_function = function; + PostMessage(MainWindow->getHWnd(), WM_CUSTINVOKE, 0, 0); // in case a modal dialog or menu is open + SignalObjectAndWait(display_invoke_ready_event, display_invoke_done_event, display_invoke_timeout, FALSE); + display_invoke_function = NULL; +} +void _ServiceDisplayThreadInvocation() +{ + Lock lock(display_invoke_handler_cs); + DWORD res = WaitForSingleObject(display_invoke_ready_event, display_invoke_timeout); + if (res != WAIT_ABANDONED && display_invoke_function) + display_invoke_function(display_invoke_argument); + display_invoke_function = NULL; + SetEvent(display_invoke_done_event); +} diff --git a/desmume/src/frontend/windows/display.h b/desmume/src/frontend/windows/display.h new file mode 100644 index 000000000..e43c801fa --- /dev/null +++ b/desmume/src/frontend/windows/display.h @@ -0,0 +1,108 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ + +#include "main.h" +#include "NDSSystem.h" +#include "debug.h" +#include "GPU.h" +#include "resource.h" +#include "lua-engine.h" +#include "video.h" +#include "frontend/modules/osd/agg/agg_osd.h" +#include "rthreads/rthreads.h" +#include "ddraw.h" +#include "ogl_display.h" + +extern DDRAW ddraw; +extern GLDISPLAY gldisplay; +extern u32 displayMethod; +const u32 DISPMETHOD_DDRAW_HW = 1; +const u32 DISPMETHOD_DDRAW_SW = 2; +const u32 DISPMETHOD_OPENGL = 3; + +extern int gpu_bpp; + +extern int emu_paused; +extern bool frameAdvance; +extern int frameskiprate; +extern int lastskiprate; +extern VideoInfo video; + +extern RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect; +extern RECT MainScreenSrcRect, SubScreenSrcRect; +const int kGapNone = 0; +const int kGapBorder = 5; +const int kGapNDS = 64; // extremely tilted (but some games seem to use this value) +const int kGapNDS2 = 90; // more normal viewing angle +const int kScale1point5x = 65535; +const int kScale2point5x = 65534; + +extern bool ForceRatio; +extern bool PadToInteger; +extern float screenSizeRatio; +extern bool vCenterResizedScr; + +extern bool SeparationBorderDrag; +extern int ScreenGapColor; + +extern slock_t *display_mutex; +extern HANDLE display_wakeup_event; +extern HANDLE display_done_event; +extern DWORD display_done_timeout; + +extern int displayPostponeType; +extern DWORD displayPostponeUntil; +extern bool displayNoPostponeNext; + +extern void(*display_invoke_function)(DWORD); +extern HANDLE display_invoke_ready_event; +extern HANDLE display_invoke_done_event; +extern DWORD display_invoke_timeout; +extern CRITICAL_SECTION display_invoke_handler_cs; + +// Scanline filter parameters. The first set is from commandline.cpp, the second +// set is externed to scanline.cpp. +// TODO: When videofilter.cpp becomes Windows-friendly, remove the second set of +// variables, as they will no longer be necessary at that point. +extern int _scanline_filter_a; +extern int _scanline_filter_b; +extern int _scanline_filter_c; +extern int _scanline_filter_d; +extern int scanline_filter_a; +extern int scanline_filter_b; +extern int scanline_filter_c; +extern int scanline_filter_d; + +void Display(); +void DoDisplay(); +void KillDisplay(); + +void GetNdsScreenRect(RECT* r); + +void _ServiceDisplayThreadInvocation(); +FORCEINLINE void ServiceDisplayThreadInvocations() +{ + if (display_invoke_function) + _ServiceDisplayThreadInvocation(); +} + +void UpdateWndRects(HWND hwnd, RECT* newClientRect = NULL); + +#endif \ No newline at end of file diff --git a/desmume/src/frontend/windows/main.cpp b/desmume/src/frontend/windows/main.cpp index 241ba08f7..d140dd503 100755 --- a/desmume/src/frontend/windows/main.cpp +++ b/desmume/src/frontend/windows/main.cpp @@ -91,6 +91,7 @@ #include "frontend/modules/ImageOut.h" #include "winutil.h" #include "ogl.h" +#include "display.h" //tools and dialogs #include "pathsettings.h" @@ -296,19 +297,10 @@ msgBoxInterface msgBoxWnd = { #include "winpcap.h" -VideoInfo video; - #ifndef PUBLIC_RELEASE #define DEVELOPER_MENU_ITEMS #endif -const int kGapNone = 0; -const int kGapBorder = 5; -const int kGapNDS = 64; // extremely tilted (but some games seem to use this value) -const int kGapNDS2 = 90; // more normal viewing angle -const int kScale1point5x = 65535; -const int kScale2point5x = 65534; - static BOOL OpenCore(const char* filename); BOOL Mic_DeInit_Physical(); BOOL Mic_Init_Physical(); @@ -324,8 +316,6 @@ bool start_paused; extern bool killStylusTopScreen; extern bool killStylusOffScreen; -int gpu_bpp = 18; - extern LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); void InitRamSearch(); void FilterUpdate(HWND hwnd, bool user=true); @@ -340,49 +330,11 @@ Lock::Lock() : m_cs(&win_execute_sync) { EnterCriticalSection(m_cs); } Lock::Lock(CRITICAL_SECTION& cs) : m_cs(&cs) { EnterCriticalSection(m_cs); } Lock::~Lock() { LeaveCriticalSection(m_cs); } -//====================== DirectDraw -const char *DDerrors[] = { "no errors", - "Unable to initialize DirectDraw", - "Unable to set DirectDraw Cooperative Level", - "Unable to create DirectDraw primary surface", - "Unable to set DirectDraw clipper"}; -struct DDRAW -{ - DDRAW(): - handle(NULL), clip(NULL) - { - surface.primary = NULL; - surface.back = NULL; - memset(&surfDesc, 0, sizeof(surfDesc)); - memset(&surfDescBack, 0, sizeof(surfDescBack)); - } - - u32 create(HWND hwnd); - bool release(); - bool createSurfaces(HWND hwnd); - bool createBackSurface(int width, int height); - bool lock(); - bool unlock(); - bool blt(LPRECT dst, LPRECT src); - bool OK(); - - LPDIRECTDRAW7 handle; - struct - { - LPDIRECTDRAWSURFACE7 primary; - LPDIRECTDRAWSURFACE7 back; - } surface; - - DDSURFACEDESC2 surfDesc; - DDSURFACEDESC2 surfDescBack; - LPDIRECTDRAWCLIPPER clip; -} ddraw; - //===================== Input vars #define WM_CUSTKEYDOWN (WM_USER+50) #define WM_CUSTKEYUP (WM_USER+51) -#define WM_CUSTINVOKE (WM_USER+52) +//#define WM_CUSTINVOKE (WM_USER+52) #ifndef __WISPSHRD_H #define WM_TABLET_DEFBASE 0x02C0 @@ -420,8 +372,6 @@ bool gConsoleTopmost = false; //HDC hdc; HACCEL hAccel; HINSTANCE hAppInst = NULL; -RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect; -RECT MainScreenSrcRect, SubScreenSrcRect; int WndX = 0; int WndY = 0; @@ -454,28 +404,11 @@ volatile bool paused = true; volatile BOOL pausedByMinimize = FALSE; u32 glock = 0; -// Scanline filter parameters. The first set is from commandline.cpp, the second -// set is externed to scanline.cpp. -// TODO: When videofilter.cpp becomes Windows-friendly, remove the second set of -// variables, as they will no longer be necessary at that point. -extern int _scanline_filter_a; -extern int _scanline_filter_b; -extern int _scanline_filter_c; -extern int _scanline_filter_d; -int scanline_filter_a = 0; -int scanline_filter_b = 2; -int scanline_filter_c = 2; -int scanline_filter_d = 4; - BOOL finished = FALSE; bool romloaded = false; void SetScreenGap(int gap); -bool ForceRatio = true; -bool PadToInteger = false; -bool SeparationBorderDrag = true; -int ScreenGapColor = 0xFFFFFF; float DefaultWidth; float DefaultHeight; float widthTradeOff; @@ -507,38 +440,12 @@ GPU3DInterface *core3DList[] = { }; bool autoframeskipenab=1; -int frameskiprate=0; -int lastskiprate=0; -int emu_paused = 0; -bool frameAdvance = false; bool continuousframeAdvancing = false; unsigned short windowSize = 0; bool fsWindow = false; bool autoHideCursor = false; -float screenSizeRatio = 1.0f; -bool vCenterResizedScr = true; - -/*const u32 gapColors[16] = { - Color::Gray, - Color::Brown, - Color::Red, - Color::Pink, - Color::Orange, - Color::Yellow, - Color::LightGreen, - Color::Lime, - Color::Green, - Color::SeaGreen, - Color::SkyBlue, - Color::Blue, - Color::DarkBlue, - Color::DarkViolet, - Color::Purple, - Color::Fuchsia -};*/ - class GPUEventHandlerWindows : public GPUEventHandlerDefault { public: @@ -778,23 +685,6 @@ void SetMinWindowSize() MainWindow->setMinSize(video.rotatedwidthgap(), video.rotatedheightgap()); } -static void GetNdsScreenRect(RECT* r) -{ - RECT zero; - SetRect(&zero,0,0,0,0); - if(zero == MainScreenRect) *r = SubScreenRect; - else if(zero == SubScreenRect || video.layout == 2) *r = MainScreenRect; - else - { - RECT* dstRects [2] = {&MainScreenRect, &SubScreenRect}; - int left = min(dstRects[0]->left,dstRects[1]->left); - int top = min(dstRects[0]->top,dstRects[1]->top); - int right = max(dstRects[0]->right,dstRects[1]->right); - int bottom = max(dstRects[0]->bottom,dstRects[1]->bottom); - SetRect(r,left,top,right,bottom); - } -} - void UnscaleScreenCoords(s32& x, s32& y) { HWND hwnd = MainWindow->getHWnd(); @@ -966,23 +856,12 @@ void ToDSScreenRelativeCoords(s32& x, s32& y, int whichScreen) } } -// END Rotation definitions - //-----window style handling---- -const u32 DISPMETHOD_DDRAW_HW = 1; -const u32 DISPMETHOD_DDRAW_SW = 2; -const u32 DISPMETHOD_OPENGL = 3; - const u32 DWS_NORMAL = 0; const u32 DWS_ALWAYSONTOP = 1; const u32 DWS_LOCKDOWN = 2; const u32 DWS_FULLSCREEN = 4; const u32 DWS_VSYNC = 8; -const u32 DWS_DDRAW_SW = 16; -const u32 DWS_DDRAW_HW = 32; -const u32 DWS_OPENGL = 64; -const u32 DWS_DISPMETHODS = (DWS_DDRAW_SW|DWS_DDRAW_HW|DWS_OPENGL); -const u32 DWS_FILTER = 128; const u32 DWS_FS_MENU = 256; const u32 DWS_FS_WINDOW = 512; @@ -1141,347 +1020,6 @@ static void PopulateLuaSubmenu() } } -template static void doRotate(void* dst) -{ - u8* buffer = (u8*)dst; - int size = video.size(); - u32* src = (u32*)video.finalBuffer(); - int width = video.width; - int height = video.height; - int pitch = ddraw.surfDescBack.lPitch; - - switch(video.rotation) - { - case 0: - case 180: - { - if(pitch == 1024) - { - if(video.rotation==0) - if(sizeof(T) == sizeof(u32)) - memcpy(buffer, src, size * sizeof(u32)); - else - for(int i = 0; i < size; i++) - ((T*)buffer)[i] = src[i]; - else // if(video.rotation==180) - for(int i = 0, j=size-1; j>=0; i++,j--) - ((T*)buffer)[i] = src[j]; - } - else - { - if(video.rotation==0) - if(sizeof(T) != sizeof(u32)) - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - ((T*)buffer)[x] = src[(y * width) + x]; - - buffer += pitch; - } - else - for(int y = 0; y < height; y++) - { - memcpy(buffer, &src[y * width], width * sizeof(u32)); - buffer += pitch; - } - else // if(video.rotation==180) - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - ((T*)buffer)[x] = src[height*width - (y * width) - x - 1]; - - buffer += pitch; - } - } - } - break; - case 90: - case 270: - { - if(video.rotation == 90) - for(int y = 0; y < width; y++) - { - for(int x = 0; x < height; x++) - ((T*)buffer)[x] = src[(((height-1)-x) * width) + y]; - - buffer += pitch; - } - else - for(int y = 0; y < width; y++) - { - for(int x = 0; x < height; x++) - ((T*)buffer)[x] = src[((x) * width) + (width-1) - y]; - - buffer += pitch; - } - } - break; - } -} - -struct DisplayLayoutInfo -{ - int vx,vy,vw,vh; - float widthScale, heightScale; - int bufferWidth, bufferHeight; -}; - -//performs aspect ratio letterboxing correction and integer clamping -DisplayLayoutInfo CalculateDisplayLayout(RECT rcClient, bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight) -{ - DisplayLayoutInfo ret; - - //do maths on the viewport and the native resolution and the user settings to get a display rectangle - SIZE sz = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top }; - - float widthScale = (float)sz.cx / targetWidth; - float heightScale = (float)sz.cy / targetHeight; - if(maintainAspect) - { - if(widthScale > heightScale) widthScale = heightScale; - if(heightScale > widthScale) heightScale = widthScale; - } - if(maintainInteger) - { - widthScale = floorf(widthScale); - heightScale = floorf(heightScale); - } - ret.vw = (int)(widthScale * targetWidth); - ret.vh = (int)(heightScale * targetHeight); - ret.vx = (sz.cx - ret.vw)/2; - ret.vy = (sz.cy - ret.vh)/2; - ret.widthScale = widthScale; - ret.heightScale = heightScale; - ret.bufferWidth = sz.cx; - ret.bufferHeight = sz.cy; - - return ret; -} - -//reformulates CalculateDisplayLayout() into a format more convenient for this purpose -RECT CalculateDisplayLayoutWrapper(RECT rcClient, int targetWidth, int targetHeight, int tbHeight, bool maximized) -{ - bool maintainInteger = !!PadToInteger; - bool maintainAspect = !!ForceRatio; - - if(maintainInteger) maintainAspect = true; - - //nothing to do here if maintain aspect isnt chosen - if(!maintainAspect) return rcClient; - - RECT rc = { rcClient.left, rcClient.top + tbHeight, rcClient.right, rcClient.bottom }; - DisplayLayoutInfo dli = CalculateDisplayLayout(rc, maintainAspect, maintainInteger, targetWidth, targetHeight); - rc.left = rcClient.left + dli.vx; - rc.top = rcClient.top + dli.vy; - rc.right = rc.left + dli.vw; - rc.bottom = rc.top + dli.vh + tbHeight; - return rc; -} - -void UpdateWndRects(HWND hwnd, RECT* newClientRect = NULL) -{ - POINT ptClient; - RECT rc; - - GapRect = {0,0,0,0}; - - bool maximized = IsZoomed(hwnd)!=FALSE; - - int wndWidth, wndHeight; - int defHeight = video.height; - if(video.layout == 0) - defHeight += video.scaledscreengap(); - float ratio; - int oneScreenHeight, gapHeight; - int tbheight; - - //if we're in the middle of resizing the window, GetClientRect will return the old rect - if (newClientRect) - rc = RECT(*newClientRect); - else - GetClientRect(hwnd, &rc); - - if(maximized) - rc = FullScreenRect; - - tbheight = MainWindowToolbar->GetHeight(); - - if (video.layout == 1) //horizontal - { - rc = CalculateDisplayLayoutWrapper(rc, (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * 2 / screenSizeRatio), GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized); - - wndWidth = (rc.bottom - rc.top) - tbheight; - wndHeight = (rc.right - rc.left); - - ratio = ((float)wndHeight / (float)512); - oneScreenHeight = (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * ratio); - int oneScreenWidth = (int)((float)GPU_FRAMEBUFFER_NATIVE_HEIGHT * ratio); - int vResizedScrOffset = 0; - - // Main screen - ptClient.x = rc.left; - ptClient.y = rc.top; - ClientToScreen(hwnd, &ptClient); - MainScreenRect.left = ptClient.x; - MainScreenRect.top = ptClient.y; - ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio); - ptClient.y = (rc.top + wndWidth); - ClientToScreen(hwnd, &ptClient); - MainScreenRect.right = ptClient.x; - MainScreenRect.bottom = ptClient.y; - - // Sub screen - ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio); - if(vCenterResizedScr && ForceRatio) - vResizedScrOffset = (wndWidth - oneScreenWidth * (2 - screenSizeRatio)) / 2; - ptClient.y = rc.top + vResizedScrOffset; - ClientToScreen(hwnd, &ptClient); - SubScreenRect.left = ptClient.x; - SubScreenRect.top = ptClient.y; - ptClient.x = (rc.left + oneScreenHeight * 2); - if(ForceRatio) - ptClient.y = (rc.top + vResizedScrOffset + oneScreenWidth * (2 - screenSizeRatio)); - else - ptClient.y = (rc.top + wndWidth); - ClientToScreen(hwnd, &ptClient); - SubScreenRect.right = ptClient.x; - SubScreenRect.bottom = ptClient.y; - } - else - if (video.layout == 2) //one screen - { - rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized); - - wndWidth = (rc.bottom - rc.top) - tbheight; - wndHeight = (rc.right - rc.left); - - ratio = ((float)wndHeight / (float)defHeight); - oneScreenHeight = (int)((video.height) * ratio); - - // Main screen - ptClient.x = rc.left; - ptClient.y = rc.top; - ClientToScreen(hwnd, &ptClient); - MainScreenRect.left = ptClient.x; - MainScreenRect.top = ptClient.y; - ptClient.x = (rc.left + oneScreenHeight); - ptClient.y = (rc.top + wndWidth); - ClientToScreen(hwnd, &ptClient); - MainScreenRect.right = ptClient.x; - MainScreenRect.bottom = ptClient.y; - SetRectEmpty(&SubScreenRect); - } - else - if (video.layout == 0) //vertical - { - //apply logic to correct things if forced integer is selected - if((video.rotation == 90) || (video.rotation == 270)) - { - rc = CalculateDisplayLayoutWrapper(rc, (GPU_FRAMEBUFFER_NATIVE_HEIGHT*2) + video.screengap, GPU_FRAMEBUFFER_NATIVE_WIDTH, tbheight, maximized); - } - else - { - rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, (GPU_FRAMEBUFFER_NATIVE_HEIGHT*2) + video.screengap, tbheight, maximized); - } - - if((video.rotation == 90) || (video.rotation == 270)) - { - wndWidth = (rc.bottom - rc.top) - tbheight; - wndHeight = (rc.right - rc.left); - } - else - { - wndWidth = (rc.right - rc.left); - wndHeight = (rc.bottom - rc.top) - tbheight; - } - - ratio = ((float)wndHeight / (float)defHeight); - - oneScreenHeight = (int)((video.height/2) * ratio); - gapHeight = (wndHeight - (oneScreenHeight * 2)); - - if((video.rotation == 90) || (video.rotation == 270)) - { - // Main screen - ptClient.x = rc.left; - ptClient.y = rc.top; - ClientToScreen(hwnd, &ptClient); - MainScreenRect.left = ptClient.x; - MainScreenRect.top = ptClient.y; - ptClient.x = (rc.left + oneScreenHeight); - ptClient.y = (rc.top + wndWidth); - ClientToScreen(hwnd, &ptClient); - MainScreenRect.right = ptClient.x; - MainScreenRect.bottom = ptClient.y; - - //if there was no specified screen gap, extend the top screen to cover the extra column - if(video.screengap == 0) MainScreenRect.right += gapHeight; - - // Sub screen - ptClient.x = (rc.left + oneScreenHeight + gapHeight); - ptClient.y = rc.top; - ClientToScreen(hwnd, &ptClient); - SubScreenRect.left = ptClient.x; - SubScreenRect.top = ptClient.y; - ptClient.x = (rc.left + oneScreenHeight + gapHeight + oneScreenHeight); - ptClient.y = (rc.top + wndWidth); - ClientToScreen(hwnd, &ptClient); - SubScreenRect.right = ptClient.x; - SubScreenRect.bottom = ptClient.y; - - // Gap - GapRect.left = (rc.left + oneScreenHeight); - GapRect.top = rc.top; - GapRect.right = (rc.left + oneScreenHeight + gapHeight); - GapRect.bottom = (rc.top + wndWidth); - } - else - { - - - // Main screen - ptClient.x = rc.left; - ptClient.y = rc.top; - ClientToScreen(hwnd, &ptClient); - MainScreenRect.left = ptClient.x; - MainScreenRect.top = ptClient.y; - ptClient.x = (rc.left + wndWidth); - ptClient.y = (rc.top + oneScreenHeight); - ClientToScreen(hwnd, &ptClient); - MainScreenRect.right = ptClient.x; - MainScreenRect.bottom = ptClient.y; - - //if there was no specified screen gap, extend the top screen to cover the extra row - if(video.screengap == 0) MainScreenRect.bottom += gapHeight; - - // Sub screen - ptClient.x = rc.left; - ptClient.y = (rc.top + oneScreenHeight + gapHeight); - ClientToScreen(hwnd, &ptClient); - SubScreenRect.left = ptClient.x; - SubScreenRect.top = ptClient.y; - ptClient.x = (rc.left + wndWidth); - ptClient.y = (rc.top + oneScreenHeight + gapHeight + oneScreenHeight); - ClientToScreen(hwnd, &ptClient); - SubScreenRect.right = ptClient.x; - SubScreenRect.bottom = ptClient.y; - - // Gap - GapRect.left = rc.left; - GapRect.top = (rc.top + oneScreenHeight); - GapRect.right = (rc.left + wndWidth); - GapRect.bottom = (rc.top + oneScreenHeight + gapHeight); - } - } - - MainScreenRect.top += tbheight; - MainScreenRect.bottom += tbheight; - SubScreenRect.top += tbheight; - SubScreenRect.bottom += tbheight; - GapRect.top += tbheight; - GapRect.bottom += tbheight; -} - void FixAspectRatio(); void LCDsSwap(int swapVal) @@ -1657,650 +1195,6 @@ void doLCDsLayout(int videoLayout) } } -#pragma pack(push,1) -struct pix24 -{ - u8 b,g,r; - FORCEINLINE pix24(u32 s) : b(s&0xFF), g((s&0xFF00)>>8), r((s&0xFF0000)>>16) {} -}; -struct pix16 -{ - u16 c; - FORCEINLINE pix16(u32 s) : c(((s&0xF8)>>3) | ((s&0xFC00)>>5) | ((s&0xF80000)>>8)) {} -}; -struct pix15 -{ - u16 c; - FORCEINLINE pix15(u32 s) : c(((s&0xF8)>>3) | ((s&0xF800)>>6) | ((s&0xF80000)>>9)) {} -}; -#pragma pack(pop) - -static void DD_FillRect(LPDIRECTDRAWSURFACE7 surf, int left, int top, int right, int bottom, DWORD color) -{ - RECT r; - SetRect(&r,left,top,right,bottom); - DDBLTFX fx; - memset(&fx,0,sizeof(DDBLTFX)); - fx.dwSize = sizeof(DDBLTFX); - //fx.dwFillColor = color; - fx.dwFillColor = 0; //color is just for debug - surf->Blt(&r,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&fx); -} - -struct GLDISPLAY -{ - HGLRC privateContext; - HDC privateDC; - bool init; - bool active; - bool wantVsync, haveVsync; - - GLDISPLAY() - : init(false) - , active(false) - , wantVsync(false) - , haveVsync(false) - { - } - - bool initialize() - { - //do we need to use another HDC? - if(init) return true; - init = initContext(MainWindow->getHWnd(),&privateContext); - setvsync(!!(GetStyle()&DWS_VSYNC)); - return init; - } - - void kill() - { - if(!init) return; - wglDeleteContext(privateContext); - privateContext = NULL; - haveVsync = false; - init = false; - } - - bool begin() - { - DWORD myThread = GetCurrentThreadId(); - - //always use another context for display logic - //1. if this is a single threaded process (3d rendering and display in the main thread) then alternating contexts is benign - //2. if this is a multi threaded process (3d rendernig and display in other threads) then the display needs some context - - if(!init) - { - if(!initialize()) return false; - } - - privateDC = GetDC(MainWindow->getHWnd()); - wglMakeCurrent(privateDC,privateContext); - - //go ahead and sync the vsync setting while we have the context - if (wantVsync != haveVsync) - _setvsync(); - - return active = true; - } - - void end() - { - wglMakeCurrent(NULL,privateContext); - ReleaseDC(MainWindow->getHWnd(),privateDC); - privateDC = NULL; - active = false; - } - - - - //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl - bool WGLExtensionSupported(const char *extension_name) - { - // this is pointer to function which returns pointer to string with list of all wgl extensions - PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL; - - // determine pointer to wglGetExtensionsStringEXT function - _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT"); - - if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) - { - // string was not found - return false; - } - - // extension is supported - return true; - } - - void setvsync(bool vsync) - { - wantVsync = vsync; - } - - void _setvsync() - { - //even if it doesn't work, we'll track it - haveVsync = wantVsync; - - if (!WGLExtensionSupported("WGL_EXT_swap_control")) return; - - //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl - PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; - PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL; - { - // Extension is supported, init pointers. - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); - - // this is another function from WGL_EXT_swap_control extension - wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT"); - } - - wglSwapIntervalEXT(wantVsync ? 1 : 0); - } - - void showPage() - { - SwapBuffers(privateDC); - } -} gldisplay; - - -static void OGL_DrawTexture(RECT* srcRects, RECT* dstRects) -{ - //don't change the original rects - RECT sRects[2]; - sRects[0] = RECT(srcRects[0]); - sRects[1] = RECT(srcRects[1]); - - //use clear+scissor for gap - if (video.screengap > 0) - { - //adjust client rect into scissor rect (0,0 at bottomleft) - glScissor(dstRects[2].left, dstRects[2].bottom, dstRects[2].right - dstRects[2].left, dstRects[2].top - dstRects[2].bottom); - - u32 color_rev = (u32)ScreenGapColor; - int r = (color_rev >> 0) & 0xFF; - int g = (color_rev >> 8) & 0xFF; - int b = (color_rev >> 16) & 0xFF; - glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f); - glEnable(GL_SCISSOR_TEST); - glClear(GL_COLOR_BUFFER_BIT); - glDisable(GL_SCISSOR_TEST); - } - - //draw two screens - glBegin(GL_QUADS); - for (int i = 0; i<2; i++) - { - //none of this makes any goddamn sense. dont even try. - int idx = i; - int ofs = 0; - switch (video.rotation) - { - case 0: - break; - case 90: - ofs = 3; - idx = 1 - i; - std::swap(sRects[idx].right, sRects[idx].bottom); - std::swap(sRects[idx].left, sRects[idx].top); - break; - case 180: - idx = 1 - i; - ofs = 2; - break; - case 270: - std::swap(sRects[idx].right, sRects[idx].bottom); - std::swap(sRects[idx].left, sRects[idx].top); - ofs = 1; - break; - } - float u1 = sRects[idx].left / (float)video.width; - float u2 = sRects[idx].right / (float)video.width; - float v1 = sRects[idx].top / (float)video.height; - float v2 = sRects[idx].bottom / (float)video.height; - float u[] = { u1,u2,u2,u1 }; - float v[] = { v1,v1,v2,v2 }; - - glTexCoord2f(u[(ofs + 0) % 4], v[(ofs + 0) % 4]); - glVertex2i(dstRects[i].left, dstRects[i].top); - - glTexCoord2f(u[(ofs + 1) % 4], v[(ofs + 1) % 4]); - glVertex2i(dstRects[i].right, dstRects[i].top); - - glTexCoord2f(u[(ofs + 2) % 4], v[(ofs + 2) % 4]); - glVertex2i(dstRects[i].right, dstRects[i].bottom); - - glTexCoord2f(u[(ofs + 3) % 4], v[(ofs + 3) % 4]); - glVertex2i(dstRects[i].left, dstRects[i].bottom); - } - glEnd(); -} -static void OGL_DoDisplay() -{ - if(!gldisplay.begin()) return; - - //the ds screen fills the texture entirely, so we dont have garbage at edge to worry about, - //but we need to make sure this is clamped for when filtering is selected - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); - - if(GetStyle()&DWS_FILTER) - { - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); - } - else - { - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); - } - - RECT rc; - HWND hwnd = MainWindow->getHWnd(); - GetClientRect(hwnd,&rc); - int width = rc.right - rc.left; - int height = rc.bottom - rc.top; - - glViewport(0,0,width,height); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, (float)width, (float)height, 0.0f, -100.0f, 100.0f); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - //clear entire area, for cases where the screen is maximized - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT); - - // get dest and src rects - RECT dr[] = {MainScreenRect, SubScreenRect, GapRect}; - for(int i=0;i<2;i++) //dont change gap rect, for some reason - { - ScreenToClient(hwnd,(LPPOINT)&dr[i].left); - ScreenToClient(hwnd,(LPPOINT)&dr[i].right); - } - dr[2].bottom = height - dr[2].bottom; - dr[2].top = height - dr[2].top; - - RECT srcRects [2]; - const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main); - - if(video.swap == 0) - { - srcRects[0] = MainScreenSrcRect; - srcRects[1] = SubScreenSrcRect; - if(osd) osd->swapScreens = false; - } - else if(video.swap == 1) - { - srcRects[0] = SubScreenSrcRect; - srcRects[1] = MainScreenSrcRect; - if(osd) osd->swapScreens = true; - } - else if(video.swap == 2) - { - srcRects[0] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect; - srcRects[1] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect; - if(osd) osd->swapScreens = !isMainGPUFirst; - } - else if(video.swap == 3) - { - srcRects[0] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect; - srcRects[1] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect; - if(osd) osd->swapScreens = isMainGPUFirst; - } - - //printf("%d,%d %dx%d -- %d,%d %dx%d\n", - // srcRects[0].left,srcRects[0].top, srcRects[0].right-srcRects[0].left, srcRects[0].bottom-srcRects[0].top, - // srcRects[1].left,srcRects[1].top, srcRects[1].right-srcRects[1].left, srcRects[1].bottom-srcRects[1].top - // ); - - glEnable(GL_TEXTURE_2D); - // draw DS display - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, video.width, video.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, video.finalBuffer()); - OGL_DrawTexture(srcRects, dr); - - // draw HUD - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, aggDraw.hud->buf().buf()); - OGL_DrawTexture(srcRects, dr); - glDisable(GL_BLEND); - - gldisplay.showPage(); - - gldisplay.end(); -} - -//the directdraw final presentation portion of display, including rotating -static void DD_DoDisplay() -{ - if (!ddraw.lock()) return; - char* buffer = (char*)ddraw.surfDescBack.lpSurface; - - if(ddraw.surfDescBack.dwWidth != video.rotatedwidth() || ddraw.surfDescBack.dwHeight != video.rotatedheight()) - { - ddraw.createBackSurface(video.rotatedwidth(),video.rotatedheight()); - } - - switch (ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount) - { - case 32: - doRotate(ddraw.surfDescBack.lpSurface); - break; - case 24: - doRotate(ddraw.surfDescBack.lpSurface); - break; - case 16: - if (ddraw.surfDescBack.ddpfPixelFormat.dwGBitMask != 0x3E0) - doRotate(ddraw.surfDescBack.lpSurface); - else - doRotate(ddraw.surfDescBack.lpSurface); - break; - case 0: - break; - default: - INFO("Unsupported color depth: %i bpp\n", ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount); - //emu_halt(); - break; - } - if (!ddraw.unlock()) return; - - RECT* dstRects [2] = {&MainScreenRect, &SubScreenRect}; - RECT* srcRects [2]; - const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main); - - if(video.swap == 0) - { - srcRects[0] = &MainScreenSrcRect; - srcRects[1] = &SubScreenSrcRect; - if(osd) osd->swapScreens = false; - } - else if(video.swap == 1) - { - srcRects[0] = &SubScreenSrcRect; - srcRects[1] = &MainScreenSrcRect; - if(osd) osd->swapScreens = true; - } - else if(video.swap == 2) - { - srcRects[0] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect; - srcRects[1] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect; - if(osd) osd->swapScreens = !isMainGPUFirst; - } - else if(video.swap == 3) - { - srcRects[0] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect; - srcRects[1] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect; - if(osd) osd->swapScreens = isMainGPUFirst; - } - - //this code fills in all the undrawn areas - //it is probably a waste of time to keep black-filling all this, but we need to do it to be safe. - { - RECT wr; - GetWindowRect(MainWindow->getHWnd(),&wr); - RECT r; - GetNdsScreenRect(&r); - int left = r.left; - int top = r.top; - int right = r.right; - int bottom = r.bottom; - //printf("%d %d %d %d / %d %d %d %d\n",fullScreen.left,fullScreen.top,fullScreen.right,fullScreen.bottom,left,top,right,bottom); - //printf("%d %d %d %d / %d %d %d %d\n",MainScreenRect.left,MainScreenRect.top,MainScreenRect.right,MainScreenRect.bottom,SubScreenRect.left,SubScreenRect.top,SubScreenRect.right,SubScreenRect.bottom); - if(ddraw.OK()) - { - DD_FillRect(ddraw.surface.primary,0,0,left,top,RGB(255,0,0)); //topleft - DD_FillRect(ddraw.surface.primary,left,0,right,top,RGB(128,0,0)); //topcenter - DD_FillRect(ddraw.surface.primary,right,0,wr.right,top,RGB(0,255,0)); //topright - DD_FillRect(ddraw.surface.primary,0,top,left,bottom,RGB(0,128,0)); //left - DD_FillRect(ddraw.surface.primary,right,top,wr.right,bottom,RGB(0,0,255)); //right - DD_FillRect(ddraw.surface.primary,0,bottom,left,wr.bottom,RGB(0,0,128)); //bottomleft - DD_FillRect(ddraw.surface.primary,left,bottom,right,wr.bottom,RGB(255,0,255)); //bottomcenter - DD_FillRect(ddraw.surface.primary,right,bottom,wr.right,wr.bottom,RGB(0,255,255)); //bottomright - if (video.layout == 1) - { - DD_FillRect(ddraw.surface.primary, SubScreenRect.left, wr.top, wr.right, SubScreenRect.top, RGB(0, 128, 128)); //Top gap left when centering the resized screen - DD_FillRect(ddraw.surface.primary, SubScreenRect.left, SubScreenRect.bottom, wr.right, wr.bottom, RGB(128, 128, 128)); //Bottom gap left when centering the resized screen - } - } - } - - for(int i = 0; i < 2; i++) - { - if(i && video.layout == 2) - break; - - if (!ddraw.blt(dstRects[i], srcRects[i])) return; - } - - if (video.layout == 1) return; - if (video.layout == 2) return; - - // Gap - if(video.screengap > 0) - { - //u32 color = gapColors[win_fw_config.fav_colour]; - //u32 color_rev = (((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16)); - u32 color_rev = (u32)ScreenGapColor; - - HDC dc; - HBRUSH brush; - - dc = GetDC(MainWindow->getHWnd()); - brush = CreateSolidBrush(color_rev); - - FillRect(dc, &GapRect, brush); - - DeleteObject((HGDIOBJ)brush); - ReleaseDC(MainWindow->getHWnd(), dc); - } -} - -//triple buffering logic -struct DisplayBuffer -{ - DisplayBuffer() - : buffer(NULL) - , size(0) - { - } - u32* buffer; - size_t size; //[256*192*4]; -} displayBuffers[3]; - -volatile int currDisplayBuffer=-1; -volatile int newestDisplayBuffer=-2; -slock_t *display_mutex = NULL; -sthread_t *display_thread = NULL; -volatile bool display_die = false; -HANDLE display_wakeup_event = INVALID_HANDLE_VALUE; -HANDLE display_done_event = INVALID_HANDLE_VALUE; -DWORD display_done_timeout = 500; - -int displayPostponeType = 0; -DWORD displayPostponeUntil = ~0; -bool displayNoPostponeNext = false; - -DWORD display_invoke_argument = 0; -void (*display_invoke_function)(DWORD) = NULL; -HANDLE display_invoke_ready_event = INVALID_HANDLE_VALUE; -HANDLE display_invoke_done_event = INVALID_HANDLE_VALUE; -DWORD display_invoke_timeout = 500; -CRITICAL_SECTION display_invoke_handler_cs; - -static void InvokeOnMainThread(void (*function)(DWORD), DWORD argument); - -static void DoDisplay_DrawHud() -{ - osd->update(); - DrawHUD(); - osd->clear(); -} - -//does a single display work unit. only to be used from the display thread -static void DoDisplay() -{ - Lock lock (win_backbuffer_sync); - - bool ddhw = (GetStyle()&DWS_DDRAW_HW)!=0; - bool ddsw = (GetStyle()&DWS_DDRAW_SW)!=0; - - if(displayPostponeType && !displayNoPostponeNext && (displayPostponeType < 0 || timeGetTime() < displayPostponeUntil)) - return; - displayNoPostponeNext = false; - - //we have to do a copy here because we're about to draw the OSD onto it. bummer. - if(gpu_bpp == 15) - ColorspaceConvertBuffer555To8888Opaque((u16 *)video.srcBuffer, video.buffer, video.srcBufferSize / 2); - else - ColorspaceConvertBuffer888XTo8888Opaque((u32*)video.srcBuffer, video.buffer, video.srcBufferSize / 4); - - //some games use the backlight for fading effects - const size_t pixCount = video.prefilterWidth * video.prefilterHeight / 2; - const NDSDisplayInfo &displayInfo = GPU->GetDisplayInfo(); - ColorspaceApplyIntensityToBuffer32(video.buffer, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Main]); - ColorspaceApplyIntensityToBuffer32(video.buffer + pixCount, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Touch]); - - if(AnyLuaActive()) - { - if(sthread_isself(display_thread)) - { - InvokeOnMainThread((void(*)(DWORD)) - CallRegisteredLuaFunctions, LUACALL_AFTEREMULATIONGUI); - } - else - { - CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI); - } - } - - // draw hud - aggDraw.hud->clear(); - DoDisplay_DrawHud(); - if (ddhw || ddsw) - { - // DirectDraw doesn't support alpha blending, so we must scale and overlay the HUD ourselves. - T_AGG_RGBA target((u8*)video.buffer, video.prefilterWidth, video.prefilterHeight, video.prefilterWidth * 4); - target.transformImage(aggDraw.hud->image(), 0, 0, video.prefilterWidth, video.prefilterHeight); - } - - //apply user's filter - video.filter(); - - if(ddhw || ddsw) - { - gldisplay.kill(); - DD_DoDisplay(); - } - else - { - OGL_DoDisplay(); - } -} - -void displayProc() -{ - slock_lock(display_mutex); - - //find a buffer to display - int todo = newestDisplayBuffer; - bool alreadyDisplayed = (todo == currDisplayBuffer); - - slock_unlock(display_mutex); - - //something new to display: - if(!alreadyDisplayed) { - //start displaying a new buffer - currDisplayBuffer = todo; - video.srcBuffer = (u8*)displayBuffers[currDisplayBuffer].buffer; - video.srcBufferSize = displayBuffers[currDisplayBuffer].size; - } - DoDisplay(); -} - - -void displayThread(void *arg) -{ - do - { - if ( (MainWindow == NULL) || IsMinimized(MainWindow->getHWnd()) ) - { - WaitForSingleObject(display_wakeup_event, INFINITE); - } - else if ( (emu_paused || !execute || !romloaded) && (!HudEditorMode && !CommonSettings.hud.ShowInputDisplay && !CommonSettings.hud.ShowGraphicalInputDisplay) ) - { - WaitForSingleObject(display_wakeup_event, 250); - } - else - { - WaitForSingleObject(display_wakeup_event, 10); - } - - if (display_die) - { - break; - } - - displayProc(); - SetEvent(display_done_event); - } while (!display_die); -} - -void KillDisplay() -{ - display_die = true; - SetEvent(display_wakeup_event); - if(display_thread) - sthread_join(display_thread); -} - -void Display() -{ - const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo(); - - if(CommonSettings.single_core()) - { - video.srcBuffer = (u8*)dispInfo.masterCustomBuffer; - video.srcBufferSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2; - DoDisplay(); - } - else - { - if(display_thread == NULL) - { - display_mutex = slock_new(); - display_thread = sthread_create(&displayThread, nullptr); - } - - slock_lock(display_mutex); - - if(int diff = (currDisplayBuffer+1)%3 - newestDisplayBuffer) - newestDisplayBuffer += diff; - else newestDisplayBuffer = (currDisplayBuffer+2)%3; - - DisplayBuffer& db = displayBuffers[newestDisplayBuffer]; - size_t targetSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2; - if(db.size != targetSize) - { - free_aligned(db.buffer); - db.buffer = (u32*)malloc_alignedPage(targetSize); - db.size = targetSize; - } - memcpy(db.buffer,dispInfo.masterCustomBuffer,targetSize); - - slock_unlock(display_mutex); - - SetEvent(display_wakeup_event); - } -} - - - void CheckMessages() { MSG msg; @@ -2330,31 +1224,6 @@ void CheckMessages() readConsole(); } -static void InvokeOnMainThread(void (*function)(DWORD), DWORD argument) -{ - ResetEvent(display_invoke_ready_event); - display_invoke_argument = argument; - display_invoke_function = function; - PostMessage(MainWindow->getHWnd(), WM_CUSTINVOKE, 0, 0); // in case a modal dialog or menu is open - SignalObjectAndWait(display_invoke_ready_event, display_invoke_done_event, display_invoke_timeout, FALSE); - display_invoke_function = NULL; -} -static void _ServiceDisplayThreadInvocation() -{ - Lock lock (display_invoke_handler_cs); - DWORD res = WaitForSingleObject(display_invoke_ready_event, display_invoke_timeout); - if(res != WAIT_ABANDONED && display_invoke_function) - display_invoke_function(display_invoke_argument); - display_invoke_function = NULL; - SetEvent(display_invoke_done_event); -} -static FORCEINLINE void ServiceDisplayThreadInvocations() -{ - if(display_invoke_function) - _ServiceDisplayThreadInvocation(); -} - - static struct MainLoopData { u64 freq; @@ -2372,7 +1241,6 @@ static struct MainLoopData int toolframecount; } mainLoopData = {0}; - static void StepRunLoop_Core() { input_acquire(); @@ -2424,9 +1292,12 @@ static void StepRunLoop_User() Hud.fps = mainLoopData.fps; Hud.fps3d = GPU->GetFPSRender3D(); - // wait for the HUD to update from last frame - if(frameskiprate==0) WaitForSingleObject(display_done_event, display_done_timeout); - Display(); + if (mainLoopData.framesskipped == 0) + { + WaitForSingleObject(display_done_event, display_done_timeout); + Display(); + } + ResetEvent(display_done_event); mainLoopData.fps3d = Hud.fps3d; @@ -3083,17 +1954,10 @@ int _main() if(GetPrivateProfileBool("Display", "Show Menu In Fullscreen Mode", false, IniName)) style |= DWS_FS_MENU; if (GetPrivateProfileBool("Display", "Non-exclusive Fullscreen Mode", false, IniName)) style |= DWS_FS_WINDOW; - if(GetPrivateProfileBool("Video","Display Method Filter", false, IniName)) - style |= DWS_FILTER; + gldisplay.filter = GetPrivateProfileBool("Video","Display Method Filter", false, IniName); if(GetPrivateProfileBool("Video","VSync", false, IniName)) style |= DWS_VSYNC; - int dispMethod = GetPrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName); - if(dispMethod == DISPMETHOD_DDRAW_SW) - style |= DWS_DDRAW_SW; - if(dispMethod == DISPMETHOD_DDRAW_HW) - style |= DWS_DDRAW_HW; - if(dispMethod == DISPMETHOD_OPENGL) - style |= DWS_OPENGL; + displayMethod = GetPrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName); windowSize = GetPrivateProfileInt("Video","Window Size", 0, IniName); video.rotation = GetPrivateProfileInt("Video","Window Rotate", 0, IniName); @@ -4860,10 +3724,10 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM MainWindow->checkMenu(IDM_RENDER_5XBRZ, video.currentfilter == video._5XBRZ ); MainWindow->checkMenu(ID_DISPLAYMETHOD_VSYNC, (GetStyle()&DWS_VSYNC)!=0); - MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWHW, (GetStyle()&DWS_DDRAW_HW)!=0); - MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWSW, (GetStyle()&DWS_DDRAW_SW)!=0); - MainWindow->checkMenu(ID_DISPLAYMETHOD_OPENGL, (GetStyle()&DWS_OPENGL)!=0); - MainWindow->checkMenu(ID_DISPLAYMETHOD_FILTER, (GetStyle()&DWS_FILTER)!=0); + MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWHW, displayMethod == DISPMETHOD_DDRAW_HW); + MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWSW, displayMethod == DISPMETHOD_DDRAW_SW); + MainWindow->checkMenu(ID_DISPLAYMETHOD_OPENGL, displayMethod == DISPMETHOD_OPENGL); + MainWindow->checkMenu(ID_DISPLAYMETHOD_FILTER, gldisplay.filter); MainWindow->checkMenu(IDC_BACKGROUNDPAUSE, lostFocusPause); MainWindow->checkMenu(IDC_BACKGROUNDINPUT, allowBackgroundInput); @@ -6233,7 +5097,8 @@ DOKEYDOWN: case ID_DISPLAYMETHOD_DIRECTDRAWHW: { Lock lock (win_backbuffer_sync); - SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_DDRAW_HW); + displayMethod = DISPMETHOD_DDRAW_HW; + ddraw.systemMemory = false; WritePrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName); ddraw.createSurfaces(hwnd); } @@ -6242,7 +5107,8 @@ DOKEYDOWN: case ID_DISPLAYMETHOD_DIRECTDRAWSW: { Lock lock (win_backbuffer_sync); - SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_DDRAW_SW); + displayMethod = DISPMETHOD_DDRAW_SW; + ddraw.systemMemory = true; WritePrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_SW, IniName); ddraw.createSurfaces(hwnd); } @@ -6251,7 +5117,7 @@ DOKEYDOWN: case ID_DISPLAYMETHOD_OPENGL: { Lock lock (win_backbuffer_sync); - SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_OPENGL); + displayMethod = DISPMETHOD_OPENGL; WritePrivateProfileInt("Video","Display Method", DISPMETHOD_OPENGL, IniName); } break; @@ -6259,8 +5125,8 @@ DOKEYDOWN: case ID_DISPLAYMETHOD_FILTER: { Lock lock (win_backbuffer_sync); - SetStyle((GetStyle()^DWS_FILTER)); - WritePrivateProfileInt("Video","Display Method Filter", (GetStyle()&DWS_FILTER)?1:0, IniName); + gldisplay.filter = !gldisplay.filter; + WritePrivateProfileInt("Video","Display Method Filter", gldisplay.filter?1:0, IniName); } break; @@ -7821,161 +6687,6 @@ void WIN_InstallGBACartridge() GBACartridge_SRAMPath = Path::GetFileNameWithoutExt(win32_GBA_cfgRomPath) + "." + GBA_SRAM_FILE_EXT; } -// ================================================================= DDraw -u32 DDRAW::create(HWND hwnd) -{ - if (handle) return 0; - - if (FAILED(DirectDrawCreateEx(NULL, (LPVOID*)&handle, IID_IDirectDraw7, NULL))) - return 1; - - if (FAILED(handle->SetCooperativeLevel(hwnd, DDSCL_NORMAL))) - return 2; - - createSurfaces(hwnd); - - return 0; -} - -bool DDRAW::release() -{ - if (!handle) return true; - - if (clip != NULL) clip->Release(); - if (surface.back != NULL) surface.back->Release(); - if (surface.primary != NULL) surface.primary->Release(); - - if (FAILED(handle->Release())) return false; - return true; -} - -bool DDRAW::createBackSurface(int width, int height) -{ - if (surface.back) { surface.back->Release(); surface.back = NULL; } - - bool hw = (GetStyle()&DWS_DDRAW_HW)!=0; - bool sw = (GetStyle()&DWS_DDRAW_SW)!=0; - - if(!hw && !sw) return true; - - memset(&surfDescBack, 0, sizeof(surfDescBack)); - surfDescBack.dwSize = sizeof(surfDescBack); - surfDescBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; - surfDescBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; - - if(sw) - surfDescBack.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; - else - surfDescBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; - - - surfDescBack.dwWidth = width; - surfDescBack.dwHeight = height; - - if (FAILED(handle->CreateSurface(&surfDescBack, &surface.back, NULL))) return false; - - return true; -} - -bool DDRAW::createSurfaces(HWND hwnd) -{ - if (!handle) return true; - - if (clip) { clip->Release(); clip = NULL; } - if (surface.primary) { surface.primary->Release(); surface.primary = NULL; } - - - // primary - memset(&surfDesc, 0, sizeof(surfDesc)); - surfDesc.dwSize = sizeof(surfDesc); - surfDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - surfDesc.dwFlags = DDSD_CAPS; - if (FAILED(handle->CreateSurface(&surfDesc, &surface.primary, NULL))) - return false; - - //default doesnt matter much, itll get adjusted later - if(!createBackSurface(GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2)) - return false; - - if (FAILED(handle->CreateClipper(0, &clip, NULL))) return false; - if (FAILED(clip->SetHWnd(0, hwnd))) return false; - if (FAILED(surface.primary->SetClipper(clip))) return false; - - backbuffer_invalidate = false; - - return true; -} - -bool DDRAW::lock() -{ - if (!handle) return true; - if (!surface.back) return false; - memset(&surfDescBack, 0, sizeof(surfDescBack)); - surfDescBack.dwSize = sizeof(surfDescBack); - surfDescBack.dwFlags = DDSD_ALL; - - HRESULT res = surface.back->Lock(NULL, &surfDescBack, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - if (FAILED(res)) - { - //INFO("DDraw failed: Lock %i\n", res); - if (res == DDERR_SURFACELOST) - { - res = surface.back->Restore(); - if (FAILED(res)) return false; - } - } - return true; -} - -bool DDRAW::unlock() -{ - if (!handle) return true; - if (!surface.back) return false; - if (FAILED(surface.back->Unlock((LPRECT)surfDescBack.lpSurface))) return false; - return true; -} - -bool DDRAW::OK() -{ - if (!handle) return false; - if (!surface.primary) return false; - if (!surface.back) return false; - return true; -} - -bool DDRAW::blt(LPRECT dst, LPRECT src) -{ - if (!handle) return true; - if (!surface.primary) return false; - if (!surface.back) return false; - - if(GetStyle()&DWS_VSYNC) - { - //this seems to block the whole process. this destroys the display thread and will easily block the emulator to 30fps. - //IDirectDraw7_WaitForVerticalBlank(handle,DDWAITVB_BLOCKBEGIN,0); - - for(;;) - { - BOOL vblank; - IDirectDraw7_GetVerticalBlankStatus(handle,&vblank); - if(vblank) break; - //must be a greedy loop since vblank is small relative to 1msec minimum Sleep() resolution. - } - } - - HRESULT res = surface.primary->Blt(dst, surface.back, src, DDBLT_WAIT, 0); - if (FAILED(res)) - { - //INFO("DDraw failed: Blt %i\n", res); - if (res == DDERR_SURFACELOST) - { - res = surface.primary->Restore(); - if (FAILED(res)) return false; - } - } - return true; -} - void SetStyle(u32 dws) { //pokefan's suggestion, there are a number of ways we could do this. @@ -8004,4 +6715,5 @@ void SetStyle(u32 dws) SetWindowPos(MainWindow->getHWnd(), insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); gldisplay.setvsync(!!(GetStyle()&DWS_VSYNC)); + ddraw.vSync = GetStyle()&DWS_VSYNC; } diff --git a/desmume/src/frontend/windows/main.h b/desmume/src/frontend/windows/main.h index bf6037032..b65231234 100644 --- a/desmume/src/frontend/windows/main.h +++ b/desmume/src/frontend/windows/main.h @@ -28,6 +28,7 @@ extern WINCLASS *MainWindow; extern HINSTANCE hAppInst; extern HMENU mainMenu; //Holds handle to the main DeSmuME menu extern CToolBar* MainWindowToolbar; +extern CRITICAL_SECTION win_backbuffer_sync; extern volatile bool execute, paused; extern bool romloaded; @@ -98,4 +99,6 @@ void WIN_InstallGBACartridge(); #define IDT_VIEW_LIGHTS 50011 #define IDM_EXEC 50112 +#define WM_CUSTINVOKE WM_USER+52 + #endif diff --git a/desmume/src/frontend/windows/ogl_display.cpp b/desmume/src/frontend/windows/ogl_display.cpp new file mode 100644 index 000000000..107f8ef38 --- /dev/null +++ b/desmume/src/frontend/windows/ogl_display.cpp @@ -0,0 +1,142 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#include "ogl_display.h" + +bool GLDISPLAY::initialize(HWND hwnd) +{ + //do we need to use another HDC? + if (hwnd == this->hwnd) return true; + else if (this->hwnd) return false; + + if (initContext(hwnd, &privateContext)) + { + this->hwnd = hwnd; + return true; + } + else + return false; +} +//http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl +bool GLDISPLAY::WGLExtensionSupported(const char *extension_name) +{ + // this is pointer to function which returns pointer to string with list of all wgl extensions + PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL; + + // determine pointer to wglGetExtensionsStringEXT function + _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT"); + + if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) + { + // string was not found + return false; + } + + // extension is supported + return true; +} +void GLDISPLAY::_setvsync() +{ + //even if it doesn't work, we'll track it + haveVsync = wantVsync; + + if (!WGLExtensionSupported("WGL_EXT_swap_control")) return; + + //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl + PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; + PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL; + { + // Extension is supported, init pointers. + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + + // this is another function from WGL_EXT_swap_control extension + wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT"); + } + + wglSwapIntervalEXT(wantVsync ? 1 : 0); +} + + +GLDISPLAY::GLDISPLAY() + : active(false) + , wantVsync(false) + , haveVsync(false) +{ +} + +void GLDISPLAY::kill() +{ + if (!hwnd) return; + wglDeleteContext(privateContext); + privateContext = NULL; + hwnd = NULL; + haveVsync = false; +} + +bool GLDISPLAY::begin(HWND hwnd) +{ + DWORD myThread = GetCurrentThreadId(); + + //always use another context for display logic + //1. if this is a single threaded process (3d rendering and display in the main thread) then alternating contexts is benign + //2. if this is a multi threaded process (3d rendernig and display in other threads) then the display needs some context + + if (!this->hwnd) + { + if (!initialize(hwnd)) return false; + } + + privateDC = GetDC(hwnd); + wglMakeCurrent(privateDC, privateContext); + + //go ahead and sync the vsync setting while we have the context + if (wantVsync != haveVsync) + _setvsync(); + + if (filter) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + + return active = true; +} + +void GLDISPLAY::end() +{ + wglMakeCurrent(NULL, privateContext); + ReleaseDC(hwnd, privateDC); + privateDC = NULL; + active = false; +} + +void GLDISPLAY::setvsync(bool vsync) +{ + wantVsync = vsync; +} + +void GLDISPLAY::showPage() +{ + SwapBuffers(privateDC); +} + diff --git a/desmume/src/frontend/windows/ogl_display.h b/desmume/src/frontend/windows/ogl_display.h new file mode 100644 index 000000000..0b84c26c9 --- /dev/null +++ b/desmume/src/frontend/windows/ogl_display.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2018 DeSmuME team + +This file 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 file 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 the this software. If not, see . +*/ + +#ifndef _OGL_DISPLAY_H_ +#define _OGL_DISPLAY_H_ + +#include "types.h" +#include "ogl.h" + +#include +#include +#include + + +struct GLDISPLAY +{ +private: + HGLRC privateContext; + HDC privateDC; + HWND hwnd; + bool initialize(HWND hwnd); + bool WGLExtensionSupported(const char *extension_name); + void _setvsync(); + +public: + bool active; + bool wantVsync, haveVsync; + bool filter; + + GLDISPLAY(); + void kill(); + bool begin(HWND hwnd); + void end(); + void setvsync(bool vsync); + void showPage(); +}; + +#endif \ No newline at end of file diff --git a/desmume/src/frontend/windows/video.h b/desmume/src/frontend/windows/video.h index 66938f33b..ce40c9917 100644 --- a/desmume/src/frontend/windows/video.h +++ b/desmume/src/frontend/windows/video.h @@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with the this software. If not, see . */ +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + #include "filter/filter.h" #include "common.h" @@ -349,3 +352,5 @@ public: return screengap * height / 384; } }; + +#endif