2020-01-05 20:40:18 +00:00
|
|
|
/*
|
|
|
|
** sdlglvideo.cpp
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 2005-2016 Christoph Oelckers et.al.
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
2020-01-11 14:26:57 +00:00
|
|
|
// HEADER FILES ------------------------------------------------------------
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
#include "i_module.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "i_video.h"
|
|
|
|
#include "m_argv.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "c_dispatch.h"
|
2020-01-11 14:26:57 +00:00
|
|
|
#include "printf.h"
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
#include "hardware.h"
|
|
|
|
#include "gl_sysfb.h"
|
2020-04-23 19:18:40 +00:00
|
|
|
#include "gl_system.h"
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
#include "gl/renderer/gl_renderer.h"
|
|
|
|
#include "gl/system/gl_framebuffer.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
#include "rendering/vulkan/system/vk_framebuffer.h"
|
|
|
|
#endif
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
2020-01-05 20:40:18 +00:00
|
|
|
#include "rendering/polyrenderer/backend/poly_framebuffer.h"
|
2020-01-11 14:26:57 +00:00
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
// Requires SDL 2.0.6 or newer
|
|
|
|
//#define SDL2_STATIC_LIBRARY
|
|
|
|
|
|
|
|
#if defined SDL2_STATIC_LIBRARY && defined HAVE_VULKAN
|
|
|
|
#include <SDL_vulkan.h>
|
|
|
|
#endif // SDL2_STATIC_LIBRARY && HAVE_VULKAN
|
|
|
|
|
2020-01-05 20:40:18 +00:00
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
2020-04-23 19:18:40 +00:00
|
|
|
extern double refreshfreq;
|
2020-01-05 20:40:18 +00:00
|
|
|
extern IVideo *Video;
|
|
|
|
|
|
|
|
EXTERN_CVAR (Int, vid_adapter)
|
|
|
|
EXTERN_CVAR (Int, vid_displaybits)
|
|
|
|
EXTERN_CVAR (Int, vid_defwidth)
|
|
|
|
EXTERN_CVAR (Int, vid_defheight)
|
|
|
|
EXTERN_CVAR (Int, vid_preferbackend)
|
|
|
|
EXTERN_CVAR (Bool, cl_capfps)
|
|
|
|
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
|
|
|
{
|
|
|
|
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
|
|
|
}
|
|
|
|
CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
|
|
|
{
|
|
|
|
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
CVAR(Bool, i_soundinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
CUSTOM_CVAR(String, vid_sdl_render_driver, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
|
|
|
{
|
|
|
|
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD(vid_list_sdl_render_drivers)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < SDL_GetNumRenderDrivers(); ++i)
|
|
|
|
{
|
|
|
|
SDL_RendererInfo info;
|
|
|
|
if (SDL_GetRenderDriverInfo(i, &info) == 0)
|
|
|
|
Printf("%s\n", info.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
|
|
|
|
namespace Priv
|
|
|
|
{
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef SDL2_STATIC_LIBRARY
|
|
|
|
|
|
|
|
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
|
|
|
|
RESULT(*NAME)(__VA_ARGS__) = SDL_ ## NAME
|
|
|
|
|
|
|
|
#else // !SDL2_STATIC_LIBRARY
|
|
|
|
|
2020-01-05 20:40:18 +00:00
|
|
|
FModule library("SDL2");
|
|
|
|
|
|
|
|
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
|
|
|
|
static TOptProc<library, RESULT(*)(__VA_ARGS__)> NAME("SDL_" #NAME)
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#endif // SDL2_STATIC_LIBRARY
|
|
|
|
|
2020-01-05 20:40:18 +00:00
|
|
|
SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right);
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height);
|
|
|
|
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names);
|
|
|
|
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#undef SDL2_OPTIONAL_FUNCTION
|
|
|
|
|
|
|
|
static const uint32_t VulkanWindowFlag = 0x1000'0000;
|
|
|
|
|
|
|
|
SDL_Window *window;
|
|
|
|
bool vulkanEnabled;
|
|
|
|
bool softpolyEnabled;
|
|
|
|
bool fullscreenSwitch;
|
|
|
|
|
|
|
|
void CreateWindow(uint32_t extraFlags)
|
|
|
|
{
|
|
|
|
assert(Priv::window == nullptr);
|
|
|
|
|
|
|
|
// Set default size
|
|
|
|
SDL_Rect bounds;
|
|
|
|
SDL_GetDisplayBounds(vid_adapter, &bounds);
|
|
|
|
|
|
|
|
if (win_w <= 0 || win_h <= 0)
|
|
|
|
{
|
|
|
|
win_w = bounds.w * 8 / 10;
|
|
|
|
win_h = bounds.h * 8 / 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
FString caption;
|
2020-01-11 14:26:57 +00:00
|
|
|
caption.Format(GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
const uint32_t windowFlags = (win_maximized ? SDL_WINDOW_MAXIMIZED : 0) | SDL_WINDOW_RESIZABLE | extraFlags;
|
|
|
|
Priv::window = SDL_CreateWindow(caption,
|
|
|
|
(win_x <= 0) ? SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter) : win_x,
|
|
|
|
(win_y <= 0) ? SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter) : win_y,
|
|
|
|
win_w, win_h, windowFlags);
|
|
|
|
|
|
|
|
if (Priv::window != nullptr)
|
|
|
|
{
|
|
|
|
// Enforce minimum size limit
|
|
|
|
SDL_SetWindowMinimumSize(Priv::window, VID_MIN_WIDTH, VID_MIN_HEIGHT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DestroyWindow()
|
|
|
|
{
|
|
|
|
assert(Priv::window != nullptr);
|
|
|
|
|
|
|
|
SDL_DestroyWindow(Priv::window);
|
|
|
|
Priv::window = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupPixelFormat(int multisample, const int *glver)
|
|
|
|
{
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
if (multisample > 0) {
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisample);
|
|
|
|
}
|
|
|
|
if (gl_debug)
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
|
|
|
|
|
|
|
if (gl_es)
|
|
|
|
{
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
|
|
}
|
|
|
|
else if (glver[0] > 2)
|
|
|
|
{
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glver[0]);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glver[1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SDLVideo : public IVideo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SDLVideo ();
|
|
|
|
~SDLVideo ();
|
|
|
|
|
|
|
|
DFrameBuffer *CreateFrameBuffer ();
|
|
|
|
|
|
|
|
private:
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
VulkanDevice *device = nullptr;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
void I_GetVulkanDrawableSize(int *width, int *height)
|
|
|
|
{
|
|
|
|
assert(Priv::vulkanEnabled);
|
|
|
|
assert(Priv::window != nullptr);
|
|
|
|
assert(Priv::Vulkan_GetDrawableSize);
|
|
|
|
Priv::Vulkan_GetDrawableSize(Priv::window, width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names)
|
|
|
|
{
|
|
|
|
assert(Priv::vulkanEnabled);
|
|
|
|
assert(Priv::window != nullptr);
|
|
|
|
return Priv::Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
|
|
|
|
{
|
|
|
|
assert(Priv::vulkanEnabled);
|
|
|
|
assert(Priv::window != nullptr);
|
|
|
|
return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
2020-01-05 20:40:18 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
SDL_Renderer* polyrendertarget = nullptr;
|
|
|
|
SDL_Texture* polytexture = nullptr;
|
|
|
|
int polytexturew = 0;
|
|
|
|
int polytextureh = 0;
|
|
|
|
bool polyvsync = false;
|
|
|
|
bool polyfirstinit = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_PolyPresentInit()
|
|
|
|
{
|
|
|
|
assert(Priv::softpolyEnabled);
|
|
|
|
assert(Priv::window != nullptr);
|
|
|
|
|
|
|
|
if (strcmp(vid_sdl_render_driver, "") != 0)
|
|
|
|
{
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, vid_sdl_render_driver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
|
|
|
|
{
|
|
|
|
// When vsync changes we need to reinitialize
|
|
|
|
if (polyrendertarget && polyvsync != vsync)
|
|
|
|
{
|
|
|
|
I_PolyPresentDeinit();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!polyrendertarget)
|
|
|
|
{
|
|
|
|
polyvsync = vsync;
|
|
|
|
|
|
|
|
polyrendertarget = SDL_CreateRenderer(Priv::window, -1, vsync ? SDL_RENDERER_PRESENTVSYNC : 0);
|
|
|
|
if (!polyrendertarget)
|
|
|
|
{
|
|
|
|
I_FatalError("Could not create render target for softpoly: %s\n", SDL_GetError());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell the user which render driver is being used, but don't repeat
|
|
|
|
// outselves if we're just changing vsync.
|
|
|
|
if (polyfirstinit)
|
|
|
|
{
|
|
|
|
polyfirstinit = false;
|
|
|
|
|
|
|
|
SDL_RendererInfo rendererInfo;
|
|
|
|
if (SDL_GetRendererInfo(polyrendertarget, &rendererInfo) == 0)
|
|
|
|
{
|
|
|
|
Printf("Using render driver %s\n", rendererInfo.name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf("Failed to query render driver\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mask color
|
|
|
|
SDL_SetRenderDrawColor(polyrendertarget, 0, 0, 0, 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!polytexture || polytexturew != w || polytextureh != h)
|
|
|
|
{
|
|
|
|
if (polytexture)
|
|
|
|
{
|
|
|
|
SDL_DestroyTexture(polytexture);
|
|
|
|
polytexture = nullptr;
|
|
|
|
polytexturew = polytextureh = 0;
|
|
|
|
}
|
|
|
|
if ((polytexture = SDL_CreateTexture(polyrendertarget, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h)) == nullptr)
|
|
|
|
I_Error("Failed to create %dx%d render target texture.", w, h);
|
|
|
|
polytexturew = w;
|
|
|
|
polytextureh = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* pixels;
|
|
|
|
SDL_LockTexture(polytexture, nullptr, (void**)&pixels, &pitch);
|
|
|
|
return pixels;
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_PolyPresentUnlock(int x, int y, int width, int height)
|
|
|
|
{
|
|
|
|
SDL_UnlockTexture(polytexture);
|
|
|
|
|
|
|
|
int ClientWidth, ClientHeight;
|
|
|
|
SDL_GetRendererOutputSize(polyrendertarget, &ClientWidth, &ClientHeight);
|
|
|
|
|
|
|
|
SDL_Rect clearrects[4];
|
|
|
|
int count = 0;
|
|
|
|
if (y > 0)
|
|
|
|
{
|
|
|
|
clearrects[count].x = 0;
|
|
|
|
clearrects[count].y = 0;
|
|
|
|
clearrects[count].w = ClientWidth;
|
|
|
|
clearrects[count].h = y;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (y + height < ClientHeight)
|
|
|
|
{
|
|
|
|
clearrects[count].x = 0;
|
|
|
|
clearrects[count].y = y + height;
|
|
|
|
clearrects[count].w = ClientWidth;
|
|
|
|
clearrects[count].h = ClientHeight - clearrects[count].y;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (x > 0)
|
|
|
|
{
|
|
|
|
clearrects[count].x = 0;
|
|
|
|
clearrects[count].y = y;
|
|
|
|
clearrects[count].w = x;
|
|
|
|
clearrects[count].h = height;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (x + width < ClientWidth)
|
|
|
|
{
|
|
|
|
clearrects[count].x = x + width;
|
|
|
|
clearrects[count].y = y;
|
|
|
|
clearrects[count].w = ClientWidth - clearrects[count].x;
|
|
|
|
clearrects[count].h = height;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
SDL_RenderFillRects(polyrendertarget, clearrects, count);
|
|
|
|
|
|
|
|
SDL_Rect dstrect;
|
|
|
|
dstrect.x = x;
|
|
|
|
dstrect.y = y;
|
|
|
|
dstrect.w = width;
|
|
|
|
dstrect.h = height;
|
|
|
|
SDL_RenderCopy(polyrendertarget, polytexture, nullptr, &dstrect);
|
|
|
|
|
|
|
|
SDL_RenderPresent(polyrendertarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_PolyPresentDeinit()
|
|
|
|
{
|
|
|
|
if (polytexture)
|
|
|
|
{
|
|
|
|
SDL_DestroyTexture(polytexture);
|
|
|
|
polytexture = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (polyrendertarget)
|
|
|
|
{
|
|
|
|
SDL_DestroyRenderer(polyrendertarget);
|
|
|
|
polyrendertarget = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2020-01-11 14:26:57 +00:00
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
SDLVideo::SDLVideo ()
|
|
|
|
{
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifndef SDL2_STATIC_LIBRARY
|
2020-01-05 20:40:18 +00:00
|
|
|
// Load optional SDL functions
|
|
|
|
if (!Priv::library.IsLoaded())
|
|
|
|
{
|
|
|
|
Priv::library.Load({ "libSDL2-2.0.so.0", "libSDL2-2.0.so", "libSDL2.so" });
|
|
|
|
}
|
2020-04-23 19:18:40 +00:00
|
|
|
#endif // !SDL2_STATIC_LIBRARY
|
2020-01-05 20:40:18 +00:00
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
|
|
|
Priv::softpolyEnabled = vid_preferbackend == 2;
|
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
Priv::vulkanEnabled = vid_preferbackend == 1
|
|
|
|
&& Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface;
|
|
|
|
|
|
|
|
if (Priv::vulkanEnabled)
|
|
|
|
{
|
|
|
|
Priv::CreateWindow(Priv::VulkanWindowFlag | SDL_WINDOW_HIDDEN);
|
|
|
|
|
|
|
|
if (Priv::window == nullptr)
|
|
|
|
{
|
|
|
|
Priv::vulkanEnabled = false;
|
|
|
|
}
|
|
|
|
}
|
2020-04-23 19:18:40 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SOFTPOLY
|
|
|
|
if (Priv::softpolyEnabled)
|
2020-01-05 20:40:18 +00:00
|
|
|
{
|
|
|
|
Priv::CreateWindow(SDL_WINDOW_HIDDEN);
|
|
|
|
}
|
|
|
|
#endif
|
2020-04-15 11:51:25 +00:00
|
|
|
|
|
|
|
// Get refresh rate for current display.
|
|
|
|
SDL_DisplayMode display;
|
|
|
|
|
|
|
|
if(SDL_GetCurrentDisplayMode(vid_adapter, &display) == 0)
|
|
|
|
{
|
|
|
|
refreshfreq = display.refresh_rate;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to get refresh rate: %s\n", SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SDLVideo::~SDLVideo ()
|
|
|
|
{
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
delete device;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
DFrameBuffer *SDLVideo::CreateFrameBuffer ()
|
|
|
|
{
|
|
|
|
SystemBaseFrameBuffer *fb = nullptr;
|
|
|
|
|
|
|
|
// first try Vulkan, if that fails OpenGL
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
if (Priv::vulkanEnabled)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
assert(device == nullptr);
|
|
|
|
device = new VulkanDevice();
|
2020-01-11 14:26:57 +00:00
|
|
|
fb = new VulkanFrameBuffer(nullptr, vid_fullscreen, device);
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
|
|
|
catch (CVulkanError const&)
|
|
|
|
{
|
|
|
|
if (Priv::window != nullptr)
|
|
|
|
{
|
|
|
|
Priv::DestroyWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
Priv::vulkanEnabled = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
2020-01-05 20:40:18 +00:00
|
|
|
if (Priv::softpolyEnabled)
|
|
|
|
{
|
2020-01-11 14:26:57 +00:00
|
|
|
fb = new PolyFrameBuffer(nullptr, vid_fullscreen);
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
2020-01-22 10:54:59 +00:00
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
if (fb == nullptr)
|
|
|
|
{
|
2020-01-11 14:26:57 +00:00
|
|
|
fb = new OpenGLRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fb;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IVideo *gl_CreateVideo()
|
|
|
|
{
|
|
|
|
return new SDLVideo();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FrameBuffer Implementation -----------------------------------------------
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
SystemBaseFrameBuffer::SystemBaseFrameBuffer (void *, bool fullscreen)
|
2020-01-05 20:40:18 +00:00
|
|
|
: DFrameBuffer (vid_defwidth, vid_defheight)
|
|
|
|
{
|
|
|
|
if (Priv::window != nullptr)
|
|
|
|
{
|
2020-04-23 19:18:40 +00:00
|
|
|
SDL_SetWindowFullscreen(Priv::window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
2020-01-05 20:40:18 +00:00
|
|
|
SDL_ShowWindow(Priv::window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int SystemBaseFrameBuffer::GetClientWidth()
|
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
2020-01-05 20:40:18 +00:00
|
|
|
if (Priv::softpolyEnabled)
|
|
|
|
{
|
|
|
|
if (polyrendertarget)
|
|
|
|
SDL_GetRendererOutputSize(polyrendertarget, &width, nullptr);
|
|
|
|
else
|
|
|
|
SDL_GetWindowSize(Priv::window, &width, nullptr);
|
|
|
|
return width;
|
|
|
|
}
|
2020-01-11 14:26:57 +00:00
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
assert(Priv::vulkanEnabled);
|
|
|
|
Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SystemBaseFrameBuffer::GetClientHeight()
|
|
|
|
{
|
|
|
|
int height = 0;
|
2020-01-11 14:26:57 +00:00
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
#ifdef HAVE_SOFTPOLY
|
2020-01-05 20:40:18 +00:00
|
|
|
if (Priv::softpolyEnabled)
|
|
|
|
{
|
|
|
|
if (polyrendertarget)
|
|
|
|
SDL_GetRendererOutputSize(polyrendertarget, nullptr, &height);
|
|
|
|
else
|
|
|
|
SDL_GetWindowSize(Priv::window, nullptr, &height);
|
|
|
|
return height;
|
|
|
|
}
|
2020-01-11 14:26:57 +00:00
|
|
|
#endif
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
assert(Priv::vulkanEnabled);
|
|
|
|
Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SystemBaseFrameBuffer::IsFullscreen ()
|
|
|
|
{
|
|
|
|
return (SDL_GetWindowFlags(Priv::window) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemBaseFrameBuffer::ToggleFullscreen(bool yes)
|
|
|
|
{
|
|
|
|
SDL_SetWindowFullscreen(Priv::window, yes ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
|
|
|
if ( !yes )
|
|
|
|
{
|
|
|
|
if ( !Priv::fullscreenSwitch )
|
|
|
|
{
|
|
|
|
Priv::fullscreenSwitch = true;
|
2020-01-11 14:26:57 +00:00
|
|
|
vid_fullscreen = false;
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Priv::fullscreenSwitch = false;
|
|
|
|
SetWindowSize(win_w, win_h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
|
|
|
|
{
|
|
|
|
if (w < VID_MIN_WIDTH || h < VID_MIN_HEIGHT)
|
|
|
|
{
|
|
|
|
w = VID_MIN_WIDTH;
|
|
|
|
h = VID_MIN_HEIGHT;
|
|
|
|
}
|
|
|
|
win_w = w;
|
|
|
|
win_h = h;
|
2020-04-23 19:18:40 +00:00
|
|
|
if (vid_fullscreen)
|
2020-01-05 20:40:18 +00:00
|
|
|
{
|
2020-01-11 14:26:57 +00:00
|
|
|
vid_fullscreen = false;
|
2020-01-05 20:40:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
win_maximized = false;
|
|
|
|
SDL_SetWindowSize(Priv::window, w, h);
|
|
|
|
SDL_SetWindowPosition(Priv::window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter), SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter));
|
|
|
|
SetSize(GetClientWidth(), GetClientHeight());
|
|
|
|
int x, y;
|
|
|
|
SDL_GetWindowPosition(Priv::window, &x, &y);
|
|
|
|
win_x = x;
|
|
|
|
win_y = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-23 19:18:40 +00:00
|
|
|
SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen)
|
|
|
|
: SystemBaseFrameBuffer(hMonitor, fullscreen)
|
2020-01-05 20:40:18 +00:00
|
|
|
{
|
|
|
|
// NOTE: Core profiles were added with GL 3.2, so there's no sense trying
|
|
|
|
// to set core 3.1 or 3.0. We could try a forward-compatible context
|
|
|
|
// instead, but that would be too restrictive (w.r.t. shaders).
|
|
|
|
static const int glvers[][2] = {
|
|
|
|
{ 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
|
|
|
|
{ 3, 3 }, { 3, 2 }, { 2, 0 },
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
|
|
|
int glveridx = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
const char *version = Args->CheckValue("-glversion");
|
|
|
|
if (version != NULL)
|
|
|
|
{
|
|
|
|
double gl_version = strtod(version, NULL) + 0.01;
|
|
|
|
int vermaj = (int)gl_version;
|
|
|
|
int vermin = (int)(gl_version*10.0) % 10;
|
|
|
|
|
|
|
|
while (glvers[glveridx][0] > vermaj || (glvers[glveridx][0] == vermaj &&
|
|
|
|
glvers[glveridx][1] > vermin))
|
|
|
|
{
|
|
|
|
glveridx++;
|
|
|
|
if (glvers[glveridx][0] == 0)
|
|
|
|
{
|
|
|
|
glveridx = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( ; glvers[glveridx][0] > 0; ++glveridx)
|
|
|
|
{
|
|
|
|
Priv::SetupPixelFormat(0, glvers[glveridx]);
|
2020-04-23 19:18:40 +00:00
|
|
|
Priv::CreateWindow(SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
|
2020-01-05 20:40:18 +00:00
|
|
|
|
|
|
|
if (Priv::window == nullptr)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLContext = SDL_GL_CreateContext(Priv::window);
|
|
|
|
if (GLContext == nullptr)
|
|
|
|
{
|
|
|
|
Priv::DestroyWindow();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SystemGLFrameBuffer::~SystemGLFrameBuffer ()
|
|
|
|
{
|
|
|
|
if (Priv::window)
|
|
|
|
{
|
|
|
|
if (GLContext)
|
|
|
|
{
|
|
|
|
SDL_GL_DeleteContext(GLContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
Priv::DestroyWindow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int SystemGLFrameBuffer::GetClientWidth()
|
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
SDL_GL_GetDrawableSize(Priv::window, &width, nullptr);
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SystemGLFrameBuffer::GetClientHeight()
|
|
|
|
{
|
|
|
|
int height = 0;
|
|
|
|
SDL_GL_GetDrawableSize(Priv::window, nullptr, &height);
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemGLFrameBuffer::SetVSync( bool vsync )
|
|
|
|
{
|
|
|
|
#if defined (__APPLE__)
|
|
|
|
const GLint value = vsync ? 1 : 0;
|
|
|
|
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &value );
|
|
|
|
#else
|
|
|
|
if (vsync)
|
|
|
|
{
|
|
|
|
if (SDL_GL_SetSwapInterval(-1) == -1)
|
|
|
|
SDL_GL_SetSwapInterval(1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_GL_SetSwapInterval(0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemGLFrameBuffer::SwapBuffers()
|
|
|
|
{
|
|
|
|
SDL_GL_SwapWindow(Priv::window);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessSDLWindowEvent(const SDL_WindowEvent &event)
|
|
|
|
{
|
|
|
|
switch (event.event)
|
|
|
|
{
|
|
|
|
extern bool AppActive;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
|
|
S_SetSoundPaused(1);
|
|
|
|
AppActive = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
|
|
S_SetSoundPaused(i_soundinbackground);
|
|
|
|
AppActive = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_MOVED:
|
2020-01-11 14:26:57 +00:00
|
|
|
if (!vid_fullscreen && Priv::GetWindowBordersSize)
|
2020-01-05 20:40:18 +00:00
|
|
|
{
|
|
|
|
int top = 0, left = 0;
|
|
|
|
Priv::GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
|
|
|
|
win_x = event.data1-left;
|
|
|
|
win_y = event.data2-top;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
2020-01-11 14:26:57 +00:00
|
|
|
if (!vid_fullscreen && !Priv::fullscreenSwitch)
|
2020-01-05 20:40:18 +00:00
|
|
|
{
|
|
|
|
win_w = event.data1;
|
|
|
|
win_h = event.data2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
|
|
|
win_maximized = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
|
|
win_maximized = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// each platform has its own specific version of this function.
|
|
|
|
void I_SetWindowTitle(const char* caption)
|
|
|
|
{
|
|
|
|
if (caption)
|
|
|
|
{
|
|
|
|
SDL_SetWindowTitle(Priv::window, caption);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FString default_caption;
|
2020-01-11 14:26:57 +00:00
|
|
|
default_caption.Format(GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
2020-01-05 20:40:18 +00:00
|
|
|
SDL_SetWindowTitle(Priv::window, default_caption);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|