mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 14:53:52 +00:00
3181 lines
90 KiB
C++
3181 lines
90 KiB
C++
#include "IGpDisplayDriver.h"
|
|
|
|
#include "GpApplicationName.h"
|
|
#include "GpComPtr.h"
|
|
#include "GpFiber_SDL.h"
|
|
#include "GpDisplayDriverProperties.h"
|
|
#include "GpVOSEvent.h"
|
|
#include "GpRingBuffer.h"
|
|
#include "HostSystemServices.h"
|
|
#include "IGpCursor.h"
|
|
#include "IGpDisplayDriverSurface.h"
|
|
#include "IGpLogDriver.h"
|
|
#include "IGpPrefsHandler.h"
|
|
#include "IGpVOSEventQueue.h"
|
|
|
|
#include "SDL_events.h"
|
|
#include "SDL_mouse.h"
|
|
#include "SDL_opengl.h"
|
|
#include "SDL_video.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <new>
|
|
#include <assert.h>
|
|
|
|
#include <chrono>
|
|
#include <numeric>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
|
|
#pragma push_macro("LoadCursor")
|
|
#ifdef LoadCursor
|
|
#undef LoadCursor
|
|
#endif
|
|
|
|
#define GP_GL_IS_OPENGL_4_CONTEXT 0
|
|
|
|
class GpDisplayDriver_SDL_GL2;
|
|
|
|
static GpDisplayDriverSurfaceEffects gs_defaultEffects;
|
|
|
|
|
|
namespace DeleteMe
|
|
{
|
|
bool DecodeCodePoint(const uint8_t *characters, size_t availableCharacters, size_t &outCharactersDigested, uint32_t &outCodePoint)
|
|
{
|
|
if (availableCharacters <= 0)
|
|
return false;
|
|
|
|
if ((characters[0] & 0x80) == 0x00)
|
|
{
|
|
outCharactersDigested = 1;
|
|
outCodePoint = characters[0];
|
|
return true;
|
|
}
|
|
|
|
size_t sz = 0;
|
|
uint32_t codePoint = 0;
|
|
uint32_t minCodePoint = 0;
|
|
if ((characters[0] & 0xe0) == 0xc0)
|
|
{
|
|
sz = 2;
|
|
minCodePoint = 0x80;
|
|
codePoint = (characters[0] & 0x1f);
|
|
}
|
|
else if ((characters[0] & 0xf0) == 0xe0)
|
|
{
|
|
sz = 3;
|
|
minCodePoint = 0x800;
|
|
codePoint = (characters[0] & 0x0f);
|
|
}
|
|
else if ((characters[0] & 0xf8) == 0xf0)
|
|
{
|
|
sz = 4;
|
|
minCodePoint = 0x10000;
|
|
codePoint = (characters[0] & 0x07);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
if (availableCharacters < sz)
|
|
return false;
|
|
|
|
for (size_t auxByte = 1; auxByte < sz; auxByte++)
|
|
{
|
|
if ((characters[auxByte] & 0xc0) != 0x80)
|
|
return false;
|
|
|
|
codePoint = (codePoint << 6) | (characters[auxByte] & 0x3f);
|
|
}
|
|
|
|
if (codePoint < minCodePoint || codePoint > 0x10ffff)
|
|
return false;
|
|
|
|
if (codePoint >= 0xd800 && codePoint <= 0xdfff)
|
|
return false;
|
|
|
|
outCodePoint = codePoint;
|
|
outCharactersDigested = sz;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace GpBinarizedShaders
|
|
{
|
|
extern const char *g_drawQuadV_GL2;
|
|
|
|
extern const char *g_drawQuadPalettePF_GL2;
|
|
extern const char *g_drawQuadPalettePNF_GL2;
|
|
extern const char *g_drawQuadRGBPF_GL2;
|
|
extern const char *g_drawQuadRGBPNF_GL2;
|
|
extern const char *g_drawQuad15BitPF_GL2;
|
|
extern const char *g_drawQuad15BitPNF_GL2;
|
|
|
|
extern const char *g_drawQuadPaletteICCPF_GL2;
|
|
extern const char *g_drawQuadPaletteICCPNF_GL2;
|
|
extern const char *g_drawQuadRGBICCPF_GL2;
|
|
extern const char *g_drawQuadRGBICCPNF_GL2;
|
|
extern const char *g_drawQuad15BitICCPF_GL2;
|
|
extern const char *g_drawQuad15BitICCPNF_GL2;
|
|
|
|
extern const char *g_copyQuadP_GL2;
|
|
extern const char *g_scaleQuadP_GL2;
|
|
}
|
|
|
|
struct GpGLFunctions
|
|
{
|
|
typedef void (GLAPIENTRYP PFNGLCLEARPROC)(GLbitfield mask);
|
|
typedef void (GLAPIENTRYP PFNGLCLEARCOLORPROC)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
|
|
typedef void (GLAPIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
|
|
typedef void (GLAPIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
|
|
typedef void (GLAPIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);
|
|
typedef void (GLAPIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);
|
|
typedef void (GLAPIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, GLuint *textures);
|
|
typedef void (GLAPIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
|
|
typedef void (GLAPIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
|
|
typedef void (GLAPIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);
|
|
typedef void (GLAPIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);
|
|
typedef GLenum (GLAPIENTRYP PFNGLGETERRORPROC)();
|
|
typedef void (GLAPIENTRYP PFNGLENABLEPROC)(GLenum cap);
|
|
typedef void (GLAPIENTRYP PFNGLDISABLEPROC)(GLenum cap);
|
|
|
|
PFNGLENABLEPROC Enable;
|
|
PFNGLDISABLEPROC Disable;
|
|
|
|
PFNGLCLEARPROC Clear;
|
|
PFNGLCLEARCOLORPROC ClearColor;
|
|
|
|
PFNGLVIEWPORTPROC Viewport;
|
|
|
|
PFNGLGENFRAMEBUFFERSPROC GenFramebuffers;
|
|
PFNGLBINDFRAMEBUFFERPROC BindFramebuffer;
|
|
PFNGLFRAMEBUFFERTEXTURE2DPROC FramebufferTexture2D;
|
|
PFNGLCHECKFRAMEBUFFERSTATUSPROC CheckFramebufferStatus;
|
|
PFNGLDELETEFRAMEBUFFERSPROC DeleteFramebuffers;
|
|
|
|
PFNGLGENBUFFERSPROC GenBuffers;
|
|
PFNGLBUFFERDATAPROC BufferData;
|
|
PFNGLBINDBUFFERPROC BindBuffer;
|
|
PFNGLDELETEBUFFERSPROC DeleteBuffers;
|
|
|
|
PFNGLCREATEPROGRAMPROC CreateProgram;
|
|
PFNGLDELETEPROGRAMPROC DeleteProgram;
|
|
PFNGLLINKPROGRAMPROC LinkProgram;
|
|
PFNGLUSEPROGRAMPROC UseProgram;
|
|
PFNGLGETPROGRAMIVPROC GetProgramiv;
|
|
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
|
|
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
|
|
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
|
|
PFNGLUNIFORM4FVPROC Uniform4fv;
|
|
PFNGLUNIFORM2FVPROC Uniform2fv;
|
|
PFNGLUNIFORM1FVPROC Uniform1fv;
|
|
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
|
|
|
|
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
|
|
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
|
|
|
|
#if GP_GL_IS_OPENGL_4_CONTEXT
|
|
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
|
|
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
|
|
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
|
|
#endif
|
|
|
|
PFNGLCREATESHADERPROC CreateShader;
|
|
PFNGLCOMPILESHADERPROC CompileShader;
|
|
PFNGLGETSHADERIVPROC GetShaderiv;
|
|
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
|
|
PFNGLATTACHSHADERPROC AttachShader;
|
|
PFNGLSHADERSOURCEPROC ShaderSource;
|
|
PFNGLDELETESHADERPROC DeleteShader;
|
|
|
|
PFNGLDRAWELEMENTSPROC DrawElements;
|
|
|
|
PFNGLACTIVETEXTUREPROC ActiveTexture;
|
|
PFNGLBINDTEXTUREPROC BindTexture;
|
|
PFNGLTEXPARAMETERIPROC TexParameteri;
|
|
PFNGLTEXIMAGE2DPROC TexImage2D;
|
|
PFNGLTEXSUBIMAGE2DPROC TexSubImage2D;
|
|
PFNGLPIXELSTOREIPROC PixelStorei;
|
|
PFNGLUNIFORM1IPROC Uniform1i;
|
|
|
|
PFNGLGENTEXTURESPROC GenTextures;
|
|
PFNGLDELETETEXTURESPROC DeleteTextures;
|
|
|
|
PFNGLGETERRORPROC GetError;
|
|
|
|
bool LookUpFunctions();
|
|
};
|
|
|
|
static void CheckGLError(const GpGLFunctions &gl, IGpLogDriver *logger)
|
|
{
|
|
GLenum errorCode = gl.GetError();
|
|
if (errorCode != 0)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GL error reported: %x", static_cast<int>(errorCode));
|
|
}
|
|
|
|
assert(errorCode == 0);
|
|
}
|
|
|
|
class GpGLObject
|
|
{
|
|
public:
|
|
explicit GpGLObject();
|
|
|
|
void AddRef();
|
|
void Release();
|
|
|
|
protected:
|
|
void InitDriver(GpDisplayDriver_SDL_GL2 *driver, const GpGLFunctions *gl);
|
|
virtual void Destroy() = 0;
|
|
|
|
GpDisplayDriver_SDL_GL2 *m_driver;
|
|
const GpGLFunctions *m_gl;
|
|
unsigned int m_count;
|
|
};
|
|
|
|
|
|
GpGLObject::GpGLObject()
|
|
: m_count(0)
|
|
, m_driver(nullptr)
|
|
{
|
|
}
|
|
|
|
void GpGLObject::AddRef()
|
|
{
|
|
++m_count;
|
|
}
|
|
|
|
void GpGLObject::Release()
|
|
{
|
|
unsigned int count = m_count;
|
|
if (count == 1)
|
|
{
|
|
this->Destroy();
|
|
return;
|
|
}
|
|
else
|
|
m_count = count - 1;
|
|
}
|
|
|
|
void GpGLObject::InitDriver(GpDisplayDriver_SDL_GL2 *driver, const GpGLFunctions *gl)
|
|
{
|
|
m_driver = driver;
|
|
m_gl = gl;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
class GpGLObjectImpl : public GpGLObject
|
|
{
|
|
public:
|
|
static T *Create(GpDisplayDriver_SDL_GL2 *driver);
|
|
|
|
protected:
|
|
virtual bool Init() = 0;
|
|
void Destroy() final override;
|
|
};
|
|
|
|
template<class T>
|
|
void GpGLObjectImpl<T>::Destroy()
|
|
{
|
|
T *self = static_cast<T*>(this);
|
|
self->~T();
|
|
free(self);
|
|
}
|
|
|
|
class GpGLRenderTargetView final : public GpGLObjectImpl<GpGLRenderTargetView>
|
|
{
|
|
public:
|
|
GpGLRenderTargetView();
|
|
~GpGLRenderTargetView();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
|
|
GpGLRenderTargetView::GpGLRenderTargetView()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
GpGLRenderTargetView::~GpGLRenderTargetView()
|
|
{
|
|
if (m_id)
|
|
m_gl->DeleteFramebuffers(1, &m_id);
|
|
}
|
|
|
|
bool GpGLRenderTargetView::Init()
|
|
{
|
|
m_gl->GenFramebuffers(1, &m_id);
|
|
|
|
return m_id != 0;
|
|
}
|
|
|
|
GLuint GpGLRenderTargetView::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
class GpGLTexture final : public GpGLObjectImpl<GpGLTexture>
|
|
{
|
|
public:
|
|
GpGLTexture();
|
|
~GpGLTexture();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
|
|
GpGLTexture::GpGLTexture()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
GpGLTexture::~GpGLTexture()
|
|
{
|
|
if (m_gl)
|
|
m_gl->DeleteTextures(1, &m_id);
|
|
}
|
|
|
|
bool GpGLTexture::Init()
|
|
{
|
|
m_gl->GenTextures(1, &m_id);
|
|
|
|
return m_id != 0;
|
|
}
|
|
|
|
GLuint GpGLTexture::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
|
|
class GpGLBuffer final : public GpGLObjectImpl<GpGLBuffer>
|
|
{
|
|
public:
|
|
GpGLBuffer();
|
|
~GpGLBuffer();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
GpGLBuffer::GpGLBuffer()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
GpGLBuffer::~GpGLBuffer()
|
|
{
|
|
if (m_gl)
|
|
m_gl->DeleteBuffers(1, &m_id);
|
|
}
|
|
|
|
bool GpGLBuffer::Init()
|
|
{
|
|
m_gl->GenBuffers(1, &m_id);
|
|
|
|
return m_id != 0;
|
|
}
|
|
|
|
GLuint GpGLBuffer::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
|
|
struct GpGLVertexArraySpec
|
|
{
|
|
const GpGLBuffer *m_buffer;
|
|
GLuint m_index;
|
|
GLint m_size;
|
|
GLenum m_type;
|
|
GLboolean m_normalized;
|
|
GLsizei m_stride;
|
|
GLsizei m_offset;
|
|
};
|
|
|
|
#if GP_GL_IS_OPENGL_4_CONTEXT
|
|
|
|
class GpGLVertexArray final : public GpGLObjectImpl<GpGLVertexArray>
|
|
{
|
|
public:
|
|
GpGLVertexArray();
|
|
~GpGLVertexArray();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
bool InitWithSpecs(const GpGLVertexArraySpec *specs, size_t numSpecs);
|
|
void Activate(const GLint *locations);
|
|
void Deactivate(const GLint *locations);
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
|
|
GpGLVertexArray::GpGLVertexArray()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
GpGLVertexArray::~GpGLVertexArray()
|
|
{
|
|
if (m_id)
|
|
m_gl->DeleteVertexArrays(1, &m_id);
|
|
}
|
|
|
|
bool GpGLVertexArray::Init()
|
|
{
|
|
m_gl->GenVertexArrays(1, &m_id);
|
|
return m_id != 0;
|
|
}
|
|
|
|
GLuint GpGLVertexArray::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
|
|
bool GpGLVertexArray::InitWithSpecs(const GpGLVertexArraySpec *specs, size_t numSpecs)
|
|
{
|
|
m_gl->BindVertexArray(m_id);
|
|
for (size_t i = 0; i < numSpecs; i++)
|
|
{
|
|
const GpGLVertexArraySpec &spec = specs[i];
|
|
m_gl->BindBuffer(GL_ARRAY_BUFFER, spec.m_buffer->GetID());
|
|
m_gl->VertexAttribPointer(spec.m_index, spec.m_size, spec.m_type, spec.m_normalized, spec.m_stride, static_cast<const char*>(nullptr) + spec.m_offset);
|
|
m_gl->EnableVertexAttribArray(spec.m_index);
|
|
m_gl->BindBuffer(GL_ARRAY_BUFFER, 0);
|
|
}
|
|
m_gl->BindVertexArray(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
void GpGLVertexArray::Activate(const GLint *locations)
|
|
{
|
|
m_gl->BindVertexArray(m_id);
|
|
}
|
|
|
|
void GpGLVertexArray::Deactivate(const GLint *locations)
|
|
{
|
|
m_gl->BindVertexArray(0);
|
|
}
|
|
#else
|
|
class GpGLVertexArray final : public GpGLObjectImpl<GpGLVertexArray>
|
|
{
|
|
public:
|
|
GpGLVertexArray();
|
|
~GpGLVertexArray();
|
|
|
|
bool Init() override;
|
|
|
|
bool InitWithSpecs(const GpGLVertexArraySpec *specs, size_t numSpecs);
|
|
void Activate(const GLint *locations);
|
|
void Deactivate(const GLint *locations);
|
|
|
|
private:
|
|
GpGLVertexArraySpec *m_specs;
|
|
size_t m_numSpecs;
|
|
};
|
|
|
|
|
|
GpGLVertexArray::GpGLVertexArray()
|
|
: m_specs(nullptr)
|
|
, m_numSpecs(0)
|
|
{
|
|
}
|
|
|
|
GpGLVertexArray::~GpGLVertexArray()
|
|
{
|
|
if (m_specs)
|
|
free(m_specs);
|
|
}
|
|
|
|
bool GpGLVertexArray::Init()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool GpGLVertexArray::InitWithSpecs(const GpGLVertexArraySpec *specs, size_t numSpecs)
|
|
{
|
|
m_specs = static_cast<GpGLVertexArraySpec*>(malloc(sizeof(GpGLVertexArraySpec) * numSpecs));
|
|
if (!m_specs)
|
|
return false;
|
|
|
|
for (size_t i = 0; i < numSpecs; i++)
|
|
m_specs[i] = specs[i];
|
|
|
|
m_numSpecs = numSpecs;
|
|
|
|
return true;
|
|
}
|
|
|
|
void GpGLVertexArray::Activate(const GLint *locations)
|
|
{
|
|
size_t numSpecs = m_numSpecs;
|
|
for (size_t i = 0; i < numSpecs; i++)
|
|
{
|
|
if (locations[i] < 0)
|
|
continue;
|
|
|
|
const GpGLVertexArraySpec &spec = m_specs[i];
|
|
m_gl->BindBuffer(GL_ARRAY_BUFFER, spec.m_buffer->GetID());
|
|
m_gl->VertexAttribPointer(locations[i], spec.m_size, spec.m_type, spec.m_normalized, spec.m_stride, static_cast<const char*>(nullptr) + spec.m_offset);
|
|
m_gl->EnableVertexAttribArray(locations[i]);
|
|
m_gl->BindBuffer(GL_ARRAY_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
void GpGLVertexArray::Deactivate(const GLint *locations)
|
|
{
|
|
size_t numSpecs = m_numSpecs;
|
|
for (size_t i = 0; i < numSpecs; i++)
|
|
{
|
|
if (locations[i] < 0)
|
|
continue;
|
|
|
|
m_gl->DisableVertexAttribArray(locations[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
class GpGLProgram final : public GpGLObjectImpl<GpGLProgram>
|
|
{
|
|
public:
|
|
GpGLProgram();
|
|
~GpGLProgram();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
GpGLProgram::GpGLProgram()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
GpGLProgram::~GpGLProgram()
|
|
{
|
|
if (m_id)
|
|
m_gl->DeleteProgram(m_id);
|
|
}
|
|
|
|
bool GpGLProgram::Init()
|
|
{
|
|
m_id = m_gl->CreateProgram();
|
|
|
|
return m_id != 0;
|
|
}
|
|
|
|
GLuint GpGLProgram::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
template<GLuint TShaderType>
|
|
class GpGLShader final : public GpGLObjectImpl<GpGLShader<TShaderType> >
|
|
{
|
|
public:
|
|
GpGLShader();
|
|
~GpGLShader();
|
|
|
|
bool Init() override;
|
|
GLuint GetID() const;
|
|
|
|
private:
|
|
GLuint m_id;
|
|
};
|
|
|
|
template<GLuint TShaderType>
|
|
GpGLShader<TShaderType>::GpGLShader()
|
|
: m_id(0)
|
|
{
|
|
}
|
|
|
|
template<GLuint TShaderType>
|
|
GpGLShader<TShaderType>::~GpGLShader()
|
|
{
|
|
if (m_id != 0)
|
|
this->m_gl->DeleteShader(m_id);
|
|
}
|
|
|
|
template<GLuint TShaderType>
|
|
bool GpGLShader<TShaderType>::Init()
|
|
{
|
|
m_id = this->m_gl->CreateShader(TShaderType);
|
|
return m_id != 0;
|
|
}
|
|
|
|
template<GLuint TShaderType>
|
|
GLuint GpGLShader<TShaderType>::GetID() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
class GpDisplayDriverSurface_GL2 : public IGpDisplayDriverSurface
|
|
{
|
|
public:
|
|
GpDisplayDriverSurface_GL2(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpGLTexture *texture, GpPixelFormat_t pixelFormat, IGpDisplayDriver::SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext);
|
|
~GpDisplayDriverSurface_GL2();
|
|
|
|
static GpDisplayDriverSurface_GL2 *Create(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat, GpDisplayDriverSurface_GL2 *prevSurface, IGpDisplayDriver::SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext);
|
|
|
|
void Upload(const void *data, size_t x, size_t y, size_t width, size_t height, size_t pitch);
|
|
void UploadEntire(const void *data, size_t pitch);
|
|
void Destroy();
|
|
|
|
void DestroyAll();
|
|
bool RecreateAll();
|
|
|
|
size_t GetImageWidth() const;
|
|
size_t GetPaddedTextureWidth() const;
|
|
size_t GetHeight() const;
|
|
GpPixelFormat_t GetPixelFormat() const;
|
|
GpGLTexture *GetTexture() const;
|
|
|
|
private:
|
|
bool Init(GpDisplayDriverSurface_GL2 *prevSurface);
|
|
bool RecreateSingle();
|
|
GLenum ResolveGLFormat() const;
|
|
GLenum ResolveGLInternalFormat() const;
|
|
GLenum ResolveGLType() const;
|
|
|
|
GpComPtr<GpGLTexture> m_texture;
|
|
const GpGLFunctions *m_gl;
|
|
GpPixelFormat_t m_pixelFormat;
|
|
size_t m_imageWidth;
|
|
size_t m_paddedTextureWidth;
|
|
size_t m_pitch;
|
|
size_t m_height;
|
|
|
|
GpDisplayDriver_SDL_GL2 *m_driver;
|
|
GpDisplayDriverSurface_GL2 *m_next;
|
|
GpDisplayDriverSurface_GL2 *m_prev;
|
|
|
|
IGpDisplayDriver::SurfaceInvalidateCallback_t m_invalidateCallback;
|
|
void *m_invalidateContext;
|
|
};
|
|
|
|
class GpCursor_SDL2 final : public IGpCursor
|
|
{
|
|
public:
|
|
explicit GpCursor_SDL2(SDL_Cursor *cursor);
|
|
|
|
SDL_Cursor* GetCursor() const;
|
|
|
|
void IncRef();
|
|
void DecRef();
|
|
|
|
void Destroy() override { this->DecRef(); }
|
|
|
|
private:
|
|
SDL_Cursor *m_cursor;
|
|
unsigned int m_count;
|
|
};
|
|
|
|
GpCursor_SDL2::GpCursor_SDL2(SDL_Cursor *cursor)
|
|
: m_cursor(cursor)
|
|
, m_count(1)
|
|
{
|
|
}
|
|
|
|
SDL_Cursor* GpCursor_SDL2::GetCursor() const
|
|
{
|
|
return m_cursor;
|
|
}
|
|
|
|
void GpCursor_SDL2::IncRef()
|
|
{
|
|
++m_count;
|
|
}
|
|
|
|
void GpCursor_SDL2::DecRef()
|
|
{
|
|
if (m_count == 1)
|
|
delete this;
|
|
else
|
|
--m_count;
|
|
}
|
|
|
|
|
|
class GpDisplayDriver_SDL_GL2 final : public IGpDisplayDriver, public IGpPrefsHandler
|
|
{
|
|
public:
|
|
explicit GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
|
|
~GpDisplayDriver_SDL_GL2();
|
|
|
|
bool Init();
|
|
|
|
void TranslateSDLMessage(const SDL_Event *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY, bool obstructiveTextInput);
|
|
|
|
void Run() override;
|
|
void Shutdown() override;
|
|
void GetDisplayResolution(unsigned int *width, unsigned int *height) override;
|
|
IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat, SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext) override;
|
|
void DrawSurface(IGpDisplayDriverSurface *surface, int32_t x, int32_t y, size_t width, size_t height, const GpDisplayDriverSurfaceEffects *effects) override;
|
|
IGpCursor *CreateBWCursor(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY) override;
|
|
IGpCursor *CreateColorCursor(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY) override;
|
|
void SetCursor(IGpCursor *cursor) override;
|
|
void SetStandardCursor(EGpStandardCursor_t standardCursor) override;
|
|
void UpdatePalette(const void *paletteData) override;
|
|
void SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
|
void SetBackgroundDarkenEffect(bool isDark) override;
|
|
void SetUseICCProfile(bool useICCProfile) override;
|
|
void RequestToggleFullScreen(uint32_t timestamp) override;
|
|
void RequestResetVirtualResolution() override;
|
|
bool IsFullScreen() const override;
|
|
const GpDisplayDriverProperties &GetProperties() const override;
|
|
IGpPrefsHandler *GetPrefsHandler() const override;
|
|
|
|
bool SupportsSizedFormats() const;
|
|
|
|
void ApplyPrefs(const void *identifier, size_t identifierSize, const void *contents, size_t contentsSize, uint32_t version) override;
|
|
bool SavePrefs(void *context, WritePrefsFunc_t writeFunc) override;
|
|
|
|
void UnlinkSurface(GpDisplayDriverSurface_GL2 *surface, GpDisplayDriverSurface_GL2 *prev, GpDisplayDriverSurface_GL2 *next);
|
|
|
|
const GpGLFunctions *GetGLFunctions() const;
|
|
|
|
template<GLuint TShaderType> GpComPtr<GpGLShader<TShaderType> > CreateShader(const char *shaderSrc);
|
|
|
|
private:
|
|
struct DrawQuadPixelFloatConstants
|
|
{
|
|
float m_modulation[4];
|
|
|
|
float m_flickerAxis[2];
|
|
float m_flickerStart;
|
|
float m_flickerEnd;
|
|
|
|
float m_desaturation;
|
|
float m_unused[3];
|
|
};
|
|
|
|
struct CompactedPresentHistoryItem
|
|
{
|
|
std::chrono::time_point<std::chrono::high_resolution_clock>::duration m_timestamp;
|
|
unsigned int m_numFrames;
|
|
};
|
|
|
|
void StartOpenGLForWindow(IGpLogDriver *logger);
|
|
bool InitResources(uint32_t physicalWidth, uint32_t physicalHeight, uint32_t virtualWidth, uint32_t virtualHeight);
|
|
|
|
void BecomeFullScreen();
|
|
void BecomeWindowed();
|
|
|
|
void SynchronizeCursors();
|
|
void ChangeToCursor(SDL_Cursor *cursor);
|
|
void ChangeToStandardCursor(EGpStandardCursor_t cursor);
|
|
|
|
bool ResizeOpenGLWindow(uint32_t &windowWidth, uint32_t &windowHeight, uint32_t desiredWidth, uint32_t desiredHeight, IGpLogDriver *logger);
|
|
bool InitBackBuffer(uint32_t width, uint32_t height);
|
|
|
|
void ScaleVirtualScreen();
|
|
|
|
GpDisplayDriverTickStatus_t PresentFrameAndSync();
|
|
|
|
GpGLFunctions m_gl;
|
|
GpDisplayDriverProperties m_properties;
|
|
|
|
struct DrawQuadProgram
|
|
{
|
|
GpComPtr<GpGLProgram> m_program;
|
|
GLint m_vertexNDCOriginAndDimensionsLocation;
|
|
GLint m_vertexSurfaceDimensionsLocation;
|
|
GLint m_vertexPosUVLocation;
|
|
GLint m_pixelModulationLocation;
|
|
GLint m_pixelFlickerAxisLocation;
|
|
GLint m_pixelFlickerStartThresholdLocation;
|
|
GLint m_pixelFlickerEndThresholdLocation;
|
|
GLint m_pixelDesaturationLocation;
|
|
GLint m_pixelSurfaceTextureLocation;
|
|
GLint m_pixelPaletteTextureLocation;
|
|
|
|
bool Link(GpDisplayDriver_SDL_GL2 *driver, const GpGLShader<GL_VERTEX_SHADER> *vertexShader, const GpGLShader<GL_FRAGMENT_SHADER> *pixelShader);
|
|
};
|
|
|
|
struct BlitQuadProgram
|
|
{
|
|
GpComPtr<GpGLProgram> m_program;
|
|
GLint m_vertexNDCOriginAndDimensionsLocation;
|
|
GLint m_vertexSurfaceDimensionsLocation;
|
|
GLint m_pixelDXDYDimensionsLocation;
|
|
GLint m_vertexPosUVLocation;
|
|
GLint m_pixelSurfaceTextureLocation;
|
|
|
|
bool Link(GpDisplayDriver_SDL_GL2 *driver, const GpGLShader<GL_VERTEX_SHADER> *vertexShader, const GpGLShader<GL_FRAGMENT_SHADER> *pixelShader);
|
|
};
|
|
|
|
struct InstancedResources
|
|
{
|
|
GpComPtr<GpGLRenderTargetView> m_virtualScreenTextureRTV;
|
|
GpComPtr<GpGLTexture> m_virtualScreenTexture;
|
|
|
|
GpComPtr<GpGLRenderTargetView> m_upscaleTextureRTV;
|
|
GpComPtr<GpGLTexture> m_upscaleTexture;
|
|
|
|
uint32_t m_upscaleTextureWidth;
|
|
uint32_t m_upscaleTextureHeight;
|
|
|
|
GpComPtr<GpGLVertexArray> m_quadVertexArray;
|
|
GpComPtr<GpGLBuffer> m_quadVertexBufferKeepalive;
|
|
GpComPtr<GpGLBuffer> m_quadIndexBuffer;
|
|
|
|
GpComPtr<GpGLTexture> m_paletteTexture;
|
|
|
|
BlitQuadProgram m_scaleQuadProgram;
|
|
BlitQuadProgram m_copyQuadProgram;
|
|
|
|
DrawQuadProgram m_drawQuadPaletteNoFlickerProgram;
|
|
DrawQuadProgram m_drawQuadPaletteFlickerProgram;
|
|
DrawQuadProgram m_drawQuadRGBProgram;
|
|
DrawQuadProgram m_drawQuad15BitProgram;
|
|
DrawQuadProgram m_drawQuadPaletteICCNoFlickerProgram;
|
|
DrawQuadProgram m_drawQuadPaletteICCFlickerProgram;
|
|
DrawQuadProgram m_drawQuadRGBICCProgram;
|
|
DrawQuadProgram m_drawQuad15BitICCProgram;
|
|
};
|
|
|
|
InstancedResources m_res;
|
|
|
|
SDL_Window *m_window;
|
|
SDL_GLContext m_glContext;
|
|
|
|
SDL_Cursor *m_waitCursor;
|
|
SDL_Cursor *m_iBeamCursor;
|
|
SDL_Cursor *m_arrowCursor;
|
|
bool m_cursorIsHidden;
|
|
bool m_contextLost;
|
|
|
|
bool m_isResettingSwapChain;
|
|
|
|
bool m_isFullScreen;
|
|
bool m_isFullScreenDesired;
|
|
bool m_isResolutionResetDesired;
|
|
int m_windowModeRevertX;
|
|
int m_windowModeRevertY;
|
|
int m_windowModeRevertWidth;
|
|
int m_windowModeRevertHeight;
|
|
uint32_t m_lastFullScreenToggleTimeStamp;
|
|
|
|
std::chrono::high_resolution_clock::duration m_frameTimeAccumulated;
|
|
std::chrono::high_resolution_clock::duration m_frameTimeSliceSize;
|
|
|
|
uint32_t m_windowWidthPhysical; // Physical resolution is the resolution of the actual window
|
|
uint32_t m_windowHeightPhysical;
|
|
uint32_t m_windowWidthVirtual; // Virtual resolution is the resolution reported to the game
|
|
uint32_t m_windowHeightVirtual;
|
|
float m_pixelScaleX;
|
|
float m_pixelScaleY;
|
|
bool m_useUpscaleFilter;
|
|
|
|
GpCursor_SDL2 *m_activeCursor;
|
|
GpCursor_SDL2 *m_pendingCursor;
|
|
EGpStandardCursor_t m_currentStandardCursor;
|
|
EGpStandardCursor_t m_pendingStandardCursor;
|
|
bool m_mouseIsInClientArea;
|
|
|
|
IGpFiber *m_vosFiber;
|
|
PortabilityLayer::HostThreadEvent *m_vosEvent;
|
|
|
|
float m_bgColor[4];
|
|
bool m_bgIsDark;
|
|
|
|
bool m_useICCProfile;
|
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock>::duration m_syncTimeBase;
|
|
GpRingBuffer<CompactedPresentHistoryItem, 60> m_presentHistory;
|
|
|
|
GpDisplayDriverSurface_GL2 *m_firstSurface;
|
|
GpDisplayDriverSurface_GL2 *m_lastSurface;
|
|
|
|
uint8_t m_paletteStorage[256 * 4 + GP_SYSTEM_MEMORY_ALIGNMENT];
|
|
uint8_t *m_paletteData;
|
|
|
|
bool m_textInputEnabled;
|
|
};
|
|
|
|
|
|
GpDisplayDriverSurface_GL2::GpDisplayDriverSurface_GL2(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpGLTexture *texture, GpPixelFormat_t pixelFormat, IGpDisplayDriver::SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext)
|
|
: m_gl(driver->GetGLFunctions())
|
|
, m_texture(texture)
|
|
, m_pixelFormat(pixelFormat)
|
|
, m_imageWidth(width)
|
|
, m_paddedTextureWidth(0)
|
|
, m_height(height)
|
|
, m_pitch(pitch)
|
|
, m_driver(driver)
|
|
, m_prev(nullptr)
|
|
, m_next(nullptr)
|
|
, m_invalidateCallback(invalidateCallback)
|
|
, m_invalidateContext(invalidateContext)
|
|
{
|
|
size_t paddingPixels = 0;
|
|
|
|
switch (pixelFormat)
|
|
{
|
|
case GpPixelFormats::kBW1:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::k8BitCustom:
|
|
paddingPixels = pitch - width;
|
|
break;
|
|
case GpPixelFormats::kRGB555:
|
|
assert(pitch % 2 == 0);
|
|
paddingPixels = pitch / 2 - width;
|
|
break;
|
|
case GpPixelFormats::kRGB24:
|
|
assert(pitch % 3 == 0);
|
|
paddingPixels = pitch / 3 - width;
|
|
break;
|
|
case GpPixelFormats::kRGB32:
|
|
assert(pitch % 4 == 0);
|
|
paddingPixels = pitch / 4 - width;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
paddingPixels = 0;
|
|
}
|
|
|
|
m_paddedTextureWidth = width + paddingPixels;
|
|
}
|
|
|
|
GpDisplayDriverSurface_GL2::~GpDisplayDriverSurface_GL2()
|
|
{
|
|
if (m_prev)
|
|
m_prev->m_next = m_next;
|
|
if (m_next)
|
|
m_next->m_prev = m_prev;
|
|
|
|
m_driver->UnlinkSurface(this, m_prev, m_next);
|
|
}
|
|
|
|
GpDisplayDriverSurface_GL2 *GpDisplayDriverSurface_GL2::Create(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat, GpDisplayDriverSurface_GL2 *prevSurface, IGpDisplayDriver::SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext)
|
|
{
|
|
GpComPtr<GpGLTexture> texture = GpComPtr<GpGLTexture>(GpGLTexture::Create(driver));
|
|
if (!texture)
|
|
return nullptr;
|
|
|
|
GpDisplayDriverSurface_GL2 *surface = static_cast<GpDisplayDriverSurface_GL2*>(malloc(sizeof(GpDisplayDriverSurface_GL2)));
|
|
if (!surface)
|
|
return nullptr;
|
|
|
|
new (surface) GpDisplayDriverSurface_GL2(driver, width, height, pitch, texture, pixelFormat, invalidateCallback, invalidateContext);
|
|
if (!surface->Init(prevSurface))
|
|
{
|
|
surface->Destroy();
|
|
return nullptr;
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
void GpDisplayDriverSurface_GL2::Upload(const void *data, size_t x, size_t y, size_t width, size_t height, size_t pitch)
|
|
{
|
|
m_gl->BindTexture(GL_TEXTURE_2D, m_texture->GetID());
|
|
m_gl->TexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, ResolveGLFormat(), ResolveGLType(), data);
|
|
m_gl->BindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void GpDisplayDriverSurface_GL2::UploadEntire(const void *data, size_t pitch)
|
|
{
|
|
assert(pitch == m_pitch);
|
|
|
|
CheckGLError(*m_gl, m_driver->GetProperties().m_logger);
|
|
|
|
const GLint internalFormat = ResolveGLInternalFormat();
|
|
const GLenum glFormat = ResolveGLFormat();
|
|
const GLenum glType = ResolveGLType();
|
|
|
|
m_gl->BindTexture(GL_TEXTURE_2D, m_texture->GetID());
|
|
m_gl->TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_paddedTextureWidth, m_height, 0, glFormat, glType, data);
|
|
m_gl->BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
CheckGLError(*m_gl, m_driver->GetProperties().m_logger);
|
|
}
|
|
|
|
void GpDisplayDriverSurface_GL2::Destroy()
|
|
{
|
|
this->~GpDisplayDriverSurface_GL2();
|
|
free(this);
|
|
}
|
|
|
|
void GpDisplayDriverSurface_GL2::DestroyAll()
|
|
{
|
|
for (GpDisplayDriverSurface_GL2 *scan = this; scan; scan = scan->m_next)
|
|
{
|
|
scan->m_invalidateCallback(scan->m_invalidateContext);
|
|
scan->m_texture = nullptr;
|
|
}
|
|
}
|
|
|
|
bool GpDisplayDriverSurface_GL2::RecreateAll()
|
|
{
|
|
for (GpDisplayDriverSurface_GL2 *scan = this; scan; scan = scan->m_next)
|
|
{
|
|
if (!scan->RecreateSingle())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t GpDisplayDriverSurface_GL2::GetImageWidth() const
|
|
{
|
|
return m_imageWidth;
|
|
}
|
|
|
|
size_t GpDisplayDriverSurface_GL2::GetPaddedTextureWidth() const
|
|
{
|
|
return m_paddedTextureWidth;
|
|
}
|
|
|
|
size_t GpDisplayDriverSurface_GL2::GetHeight() const
|
|
{
|
|
return m_height;
|
|
}
|
|
|
|
GpPixelFormat_t GpDisplayDriverSurface_GL2::GetPixelFormat() const
|
|
{
|
|
return m_pixelFormat;
|
|
}
|
|
|
|
GpGLTexture *GpDisplayDriverSurface_GL2::GetTexture() const
|
|
{
|
|
return m_texture;
|
|
}
|
|
|
|
|
|
bool GpDisplayDriverSurface_GL2::Init(GpDisplayDriverSurface_GL2 *prevSurface)
|
|
{
|
|
CheckGLError(*m_gl, m_driver->GetProperties().m_logger);
|
|
|
|
m_gl->BindTexture(GL_TEXTURE_2D, m_texture->GetID());
|
|
m_gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
m_gl->TexImage2D(GL_TEXTURE_2D, 0, ResolveGLInternalFormat(), m_paddedTextureWidth, m_height, 0, ResolveGLFormat(), ResolveGLType(), nullptr);
|
|
m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
m_gl->BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
CheckGLError(*m_gl, m_driver->GetProperties().m_logger);
|
|
|
|
m_prev = prevSurface;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GpDisplayDriverSurface_GL2::RecreateSingle()
|
|
{
|
|
m_texture = GpGLTexture::Create(m_driver);
|
|
return m_texture != nullptr && this->Init(m_prev);
|
|
}
|
|
|
|
GLenum GpDisplayDriverSurface_GL2::ResolveGLFormat() const
|
|
{
|
|
switch (m_pixelFormat)
|
|
{
|
|
case GpPixelFormats::k8BitCustom:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::kBW1:
|
|
return m_driver->SupportsSizedFormats() ? GL_RED : GL_LUMINANCE;
|
|
case GpPixelFormats::kRGB24:
|
|
case GpPixelFormats::kRGB555:
|
|
return GL_RGB;
|
|
case GpPixelFormats::kRGB32:
|
|
return GL_RGBA;
|
|
default:
|
|
return GL_RGBA;
|
|
}
|
|
}
|
|
|
|
GLenum GpDisplayDriverSurface_GL2::ResolveGLInternalFormat() const
|
|
{
|
|
if (m_driver->SupportsSizedFormats())
|
|
{
|
|
switch (m_pixelFormat)
|
|
{
|
|
case GpPixelFormats::k8BitCustom:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::kBW1:
|
|
return GL_R8;
|
|
case GpPixelFormats::kRGB24:
|
|
return GL_RGB8;
|
|
case GpPixelFormats::kRGB555:
|
|
return GL_RGB5;
|
|
case GpPixelFormats::kRGB32:
|
|
return GL_RGBA8;
|
|
default:
|
|
return GL_RGBA8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (m_pixelFormat)
|
|
{
|
|
case GpPixelFormats::k8BitCustom:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::kBW1:
|
|
return GL_LUMINANCE;
|
|
case GpPixelFormats::kRGB24:
|
|
return GL_RGB;
|
|
case GpPixelFormats::kRGB555:
|
|
return GL_RGBA;
|
|
case GpPixelFormats::kRGB32:
|
|
return GL_RGBA;
|
|
default:
|
|
return GL_RGBA;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLenum GpDisplayDriverSurface_GL2::ResolveGLType() const
|
|
{
|
|
switch (m_pixelFormat)
|
|
{
|
|
case GpPixelFormats::k8BitCustom:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::kBW1:
|
|
return GL_UNSIGNED_BYTE;
|
|
case GpPixelFormats::kRGB24:
|
|
return GL_UNSIGNED_BYTE;
|
|
case GpPixelFormats::kRGB555:
|
|
return GL_UNSIGNED_SHORT_5_5_5_1;
|
|
case GpPixelFormats::kRGB32:
|
|
return GL_UNSIGNED_BYTE;
|
|
default:
|
|
return GL_UNSIGNED_BYTE;
|
|
}
|
|
}
|
|
|
|
|
|
GpDisplayDriver_SDL_GL2::GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties)
|
|
: m_window(nullptr)
|
|
, m_frameTimeAccumulated(std::chrono::high_resolution_clock::duration::zero())
|
|
, m_frameTimeSliceSize(std::chrono::high_resolution_clock::duration::zero())
|
|
, m_windowWidthPhysical(640)
|
|
, m_windowHeightPhysical(480)
|
|
, m_windowWidthVirtual(640)
|
|
, m_windowHeightVirtual(480)
|
|
, m_pixelScaleX(1.0f)
|
|
, m_pixelScaleY(1.0f)
|
|
, m_useUpscaleFilter(false)
|
|
, m_vosFiber(nullptr)
|
|
, m_vosEvent(nullptr)
|
|
, m_pendingCursor(nullptr)
|
|
, m_activeCursor(nullptr)
|
|
, m_currentStandardCursor(EGpStandardCursors::kArrow)
|
|
, m_pendingStandardCursor(EGpStandardCursors::kArrow)
|
|
, m_mouseIsInClientArea(false)
|
|
, m_isFullScreen(false)
|
|
, m_isFullScreenDesired(false)
|
|
, m_isResolutionResetDesired(false)
|
|
, m_windowModeRevertX(200)
|
|
, m_windowModeRevertY(200)
|
|
, m_windowModeRevertWidth(640)
|
|
, m_windowModeRevertHeight(480)
|
|
, m_lastFullScreenToggleTimeStamp(0)
|
|
, m_bgIsDark(false)
|
|
, m_useICCProfile(false)
|
|
, m_properties(properties)
|
|
, m_syncTimeBase(std::chrono::time_point<std::chrono::high_resolution_clock>::duration::zero())
|
|
, m_waitCursor(nullptr)
|
|
, m_iBeamCursor(nullptr)
|
|
, m_arrowCursor(nullptr)
|
|
, m_cursorIsHidden(false)
|
|
, m_contextLost(true)
|
|
, m_lastSurface(nullptr)
|
|
, m_firstSurface(nullptr)
|
|
, m_textInputEnabled(false)
|
|
{
|
|
m_bgColor[0] = 0.f;
|
|
m_bgColor[1] = 0.f;
|
|
m_bgColor[2] = 0.f;
|
|
m_bgColor[3] = 1.f;
|
|
|
|
// Stupid hack to detect mobile...
|
|
m_isFullScreenDesired = m_properties.m_systemServices->IsTouchscreen() && !m_properties.m_systemServices->IsUsingMouseAsTouch();
|
|
|
|
const intmax_t periodNum = std::chrono::high_resolution_clock::period::num;
|
|
const intmax_t periodDen = std::chrono::high_resolution_clock::period::den;
|
|
|
|
m_frameTimeSliceSize = std::chrono::high_resolution_clock::duration(periodDen * static_cast<intmax_t>(properties.m_frameTimeLockNumerator) / static_cast<intmax_t>(properties.m_frameTimeLockDenominator) / periodNum);
|
|
|
|
m_waitCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
|
|
m_iBeamCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
|
m_arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
|
|
|
m_paletteData = m_paletteStorage;
|
|
while (reinterpret_cast<uintptr_t>(m_paletteData) % GP_SYSTEM_MEMORY_ALIGNMENT != 0)
|
|
m_paletteData++;
|
|
|
|
memset(m_paletteData, 255, 256 * 4);
|
|
}
|
|
|
|
template<class T>
|
|
static bool LookupOpenGLFunction(T &target, const char *name)
|
|
{
|
|
target = static_cast<T>(nullptr);
|
|
void *proc = SDL_GL_GetProcAddress(name);
|
|
if (proc)
|
|
{
|
|
target = reinterpret_cast<T>(proc);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
#define LOOKUP_FUNC(func) do { if (!LookupOpenGLFunction(this->func, "gl" #func)) return false; } while(false)
|
|
|
|
|
|
bool GpGLFunctions::LookUpFunctions()
|
|
{
|
|
LOOKUP_FUNC(Enable);
|
|
LOOKUP_FUNC(Disable);
|
|
|
|
LOOKUP_FUNC(Clear);
|
|
LOOKUP_FUNC(ClearColor);
|
|
|
|
LOOKUP_FUNC(Viewport);
|
|
|
|
LOOKUP_FUNC(GenFramebuffers);
|
|
LOOKUP_FUNC(BindFramebuffer);
|
|
LOOKUP_FUNC(FramebufferTexture2D);
|
|
LOOKUP_FUNC(CheckFramebufferStatus);
|
|
LOOKUP_FUNC(DeleteFramebuffers);
|
|
|
|
LOOKUP_FUNC(CreateProgram);
|
|
LOOKUP_FUNC(DeleteProgram);
|
|
LOOKUP_FUNC(LinkProgram);
|
|
LOOKUP_FUNC(UseProgram);
|
|
LOOKUP_FUNC(GetProgramiv);
|
|
LOOKUP_FUNC(GetProgramInfoLog);
|
|
|
|
LOOKUP_FUNC(GenBuffers);
|
|
LOOKUP_FUNC(BufferData);
|
|
LOOKUP_FUNC(BindBuffer);
|
|
LOOKUP_FUNC(DeleteBuffers);
|
|
|
|
LOOKUP_FUNC(GetUniformLocation);
|
|
LOOKUP_FUNC(GetAttribLocation);
|
|
LOOKUP_FUNC(Uniform4fv);
|
|
LOOKUP_FUNC(Uniform2fv);
|
|
LOOKUP_FUNC(Uniform1fv);
|
|
LOOKUP_FUNC(VertexAttribPointer);
|
|
|
|
LOOKUP_FUNC(EnableVertexAttribArray);
|
|
LOOKUP_FUNC(DisableVertexAttribArray);
|
|
|
|
#if GP_GL_IS_OPENGL_4_CONTEXT
|
|
LOOKUP_FUNC(GenVertexArrays);
|
|
LOOKUP_FUNC(DeleteVertexArrays);
|
|
LOOKUP_FUNC(BindVertexArray);
|
|
#endif
|
|
|
|
LOOKUP_FUNC(CreateShader);
|
|
LOOKUP_FUNC(CompileShader);
|
|
LOOKUP_FUNC(GetShaderiv);
|
|
LOOKUP_FUNC(GetShaderInfoLog);
|
|
LOOKUP_FUNC(AttachShader);
|
|
LOOKUP_FUNC(ShaderSource);
|
|
LOOKUP_FUNC(DeleteShader);
|
|
|
|
LOOKUP_FUNC(DrawElements);
|
|
|
|
LOOKUP_FUNC(ActiveTexture);
|
|
LOOKUP_FUNC(BindTexture);
|
|
LOOKUP_FUNC(TexParameteri);
|
|
LOOKUP_FUNC(TexImage2D);
|
|
LOOKUP_FUNC(TexSubImage2D);
|
|
LOOKUP_FUNC(PixelStorei);
|
|
LOOKUP_FUNC(Uniform1i);
|
|
|
|
LOOKUP_FUNC(GenTextures);
|
|
LOOKUP_FUNC(DeleteTextures);
|
|
|
|
LOOKUP_FUNC(GetError);
|
|
|
|
return true;
|
|
}
|
|
|
|
GpDisplayDriver_SDL_GL2::~GpDisplayDriver_SDL_GL2()
|
|
{
|
|
SDL_DestroyWindow(m_window);
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::Init()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY)
|
|
{
|
|
if (GpVOSEvent *evt = eventQueue->QueueEvent())
|
|
{
|
|
evt->m_eventType = GpVOSEventTypes::kMouseInput;
|
|
|
|
GpMouseInputEvent &mEvent = evt->m_event.m_mouseInputEvent;
|
|
mEvent.m_button = button;
|
|
mEvent.m_x = x;
|
|
mEvent.m_y = y;
|
|
mEvent.m_eventType = eventType;
|
|
|
|
if (pixelScaleX != 1.0f)
|
|
mEvent.m_x = static_cast<int32_t>(static_cast<float>(x) / pixelScaleX);
|
|
|
|
if (pixelScaleY != 1.0f)
|
|
mEvent.m_y = static_cast<int32_t>(static_cast<float>(y) / pixelScaleX);
|
|
}
|
|
}
|
|
|
|
static void PostTouchEvent(IGpVOSEventQueue *eventQueue, GpTouchEventType_t eventType, int32_t x, int32_t y, int64_t deviceID, int64_t fingerID)
|
|
{
|
|
if (GpVOSEvent *evt = eventQueue->QueueEvent())
|
|
{
|
|
evt->m_eventType = GpVOSEventTypes::kTouchInput;
|
|
|
|
GpTouchInputEvent &tEvent = evt->m_event.m_touchInputEvent;
|
|
tEvent.m_deviceID = deviceID;
|
|
tEvent.m_fingerID = fingerID;
|
|
tEvent.m_x = x;
|
|
tEvent.m_y = y;
|
|
tEvent.m_eventType = eventType;
|
|
}
|
|
}
|
|
|
|
static bool IdentifyVKey(const SDL_KeyboardEvent *keyEvt, GpKeyIDSubset_t &outSubset, GpKeyboardInputEvent::KeyUnion &outKey)
|
|
{
|
|
SDL_KeyCode keyCode = static_cast<SDL_KeyCode>(keyEvt->keysym.sym);
|
|
|
|
switch (keyCode)
|
|
{
|
|
case SDLK_ESCAPE:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kEscape;
|
|
break;
|
|
case SDLK_PRINTSCREEN:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kPrintScreen;
|
|
break;
|
|
case SDLK_SCROLLLOCK:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kScrollLock;
|
|
break;
|
|
case SDLK_PAUSE:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kPause;
|
|
break;
|
|
case SDLK_INSERT:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kInsert;
|
|
break;
|
|
case SDLK_HOME:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kHome;
|
|
break;
|
|
case SDLK_PAGEUP:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kPageUp;
|
|
break;
|
|
case SDLK_PAGEDOWN:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kPageDown;
|
|
break;
|
|
case SDLK_DELETE:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kDelete;
|
|
break;
|
|
case SDLK_TAB:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kTab;
|
|
break;
|
|
case SDLK_END:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kEnd;
|
|
break;
|
|
case SDLK_BACKSPACE:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kBackspace;
|
|
break;
|
|
case SDLK_CAPSLOCK:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kCapsLock;
|
|
break;
|
|
case SDLK_RETURN:
|
|
case SDLK_KP_ENTER:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kEnter;
|
|
break;
|
|
case SDLK_LSHIFT:
|
|
case SDLK_RSHIFT:
|
|
{
|
|
if (keyCode == SDLK_LSHIFT)
|
|
{
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kLeftShift;
|
|
}
|
|
else if (keyCode == SDLK_RSHIFT)
|
|
{
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kRightShift;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
break;
|
|
case SDLK_LCTRL:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kLeftCtrl;
|
|
break;
|
|
case SDLK_RCTRL:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kRightCtrl;
|
|
break;
|
|
case SDLK_LALT:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kLeftAlt;
|
|
break;
|
|
case SDLK_RALT:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kRightAlt;
|
|
break;
|
|
case SDLK_NUMLOCKCLEAR:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kNumLock;
|
|
break;
|
|
case SDLK_KP_0:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 0;
|
|
break;
|
|
case SDLK_KP_1:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 1;
|
|
break;
|
|
case SDLK_KP_2:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 2;
|
|
break;
|
|
case SDLK_KP_3:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 3;
|
|
break;
|
|
case SDLK_KP_4:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 4;
|
|
break;
|
|
case SDLK_KP_5:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 5;
|
|
break;
|
|
case SDLK_KP_6:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 6;
|
|
break;
|
|
case SDLK_KP_7:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 7;
|
|
break;
|
|
case SDLK_KP_8:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 8;
|
|
break;
|
|
case SDLK_KP_9:
|
|
outSubset = GpKeyIDSubsets::kNumPadNumber;
|
|
outKey.m_numPadNumber = 9;
|
|
break;
|
|
|
|
case SDLK_F1:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 1;
|
|
break;
|
|
case SDLK_F2:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 2;
|
|
break;
|
|
case SDLK_F3:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 3;
|
|
break;
|
|
case SDLK_F4:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 4;
|
|
break;
|
|
case SDLK_F5:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 5;
|
|
break;
|
|
case SDLK_F6:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 6;
|
|
break;
|
|
case SDLK_F7:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 7;
|
|
break;
|
|
case SDLK_F8:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 8;
|
|
break;
|
|
case SDLK_F9:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 9;
|
|
break;
|
|
case SDLK_F10:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 10;
|
|
break;
|
|
case SDLK_F11:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 11;
|
|
break;
|
|
case SDLK_F12:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 12;
|
|
break;
|
|
case SDLK_F13:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 13;
|
|
break;
|
|
case SDLK_F14:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 14;
|
|
break;
|
|
case SDLK_F15:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 15;
|
|
break;
|
|
case SDLK_F16:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 16;
|
|
break;
|
|
case SDLK_F17:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 17;
|
|
break;
|
|
case SDLK_F18:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 18;
|
|
break;
|
|
case SDLK_F19:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 19;
|
|
break;
|
|
case SDLK_F20:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 20;
|
|
break;
|
|
case SDLK_F21:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 21;
|
|
break;
|
|
case SDLK_F22:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 22;
|
|
break;
|
|
case SDLK_F23:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 23;
|
|
break;
|
|
case SDLK_F24:
|
|
outSubset = GpKeyIDSubsets::kFKey;
|
|
outKey.m_fKey = 24;
|
|
break;
|
|
|
|
case SDLK_COMMA:
|
|
outSubset = GpKeyIDSubsets::kASCII;
|
|
outKey.m_asciiChar = ',';
|
|
break;
|
|
|
|
case SDLK_MINUS:
|
|
outSubset = GpKeyIDSubsets::kASCII;
|
|
outKey.m_asciiChar = '-';
|
|
break;
|
|
|
|
case SDLK_UP:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kUpArrow;
|
|
break;
|
|
case SDLK_DOWN:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kDownArrow;
|
|
break;
|
|
case SDLK_LEFT:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kLeftArrow;
|
|
break;
|
|
case SDLK_RIGHT:
|
|
outSubset = GpKeyIDSubsets::kSpecial;
|
|
outKey.m_specialKey = GpKeySpecials::kRightArrow;
|
|
break;
|
|
|
|
case SDLK_KP_COMMA:
|
|
outSubset = GpKeyIDSubsets::kNumPadSpecial;
|
|
outKey.m_numPadSpecialKey = GpNumPadSpecials::kComma;
|
|
break;
|
|
|
|
case SDLK_KP_MULTIPLY:
|
|
outSubset = GpKeyIDSubsets::kNumPadSpecial;
|
|
outKey.m_numPadSpecialKey = GpNumPadSpecials::kAsterisk;
|
|
break;
|
|
|
|
case SDLK_KP_PERIOD:
|
|
outSubset = GpKeyIDSubsets::kNumPadSpecial;
|
|
outKey.m_numPadSpecialKey = GpNumPadSpecials::kPeriod;
|
|
break;
|
|
|
|
case SDLK_KP_DIVIDE:
|
|
outSubset = GpKeyIDSubsets::kNumPadSpecial;
|
|
outKey.m_numPadSpecialKey = GpNumPadSpecials::kSlash;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (keyCode < 128)
|
|
{
|
|
outSubset = GpKeyIDSubsets::kASCII;
|
|
if (keyCode >= 'a' && keyCode <= 'z')
|
|
outKey.m_asciiChar = static_cast<char>(keyCode + 'A' - 'a');
|
|
else
|
|
outKey.m_asciiChar = static_cast<char>(keyCode);
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void PostKeyboardEvent(IGpVOSEventQueue *eventQueue, GpKeyboardInputEventType_t eventType, GpKeyIDSubset_t subset, const GpKeyboardInputEvent::KeyUnion &key, uint32_t repeatCount)
|
|
{
|
|
if (GpVOSEvent *evt = eventQueue->QueueEvent())
|
|
{
|
|
evt->m_eventType = GpVOSEventTypes::kKeyboardInput;
|
|
|
|
GpKeyboardInputEvent &mEvent = evt->m_event.m_keyboardInputEvent;
|
|
mEvent.m_key = key;
|
|
mEvent.m_eventType = eventType;
|
|
mEvent.m_keyIDSubset = subset;
|
|
mEvent.m_repeatCount = repeatCount;
|
|
}
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::TranslateSDLMessage(const SDL_Event *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY, bool obstructiveTextInput)
|
|
{
|
|
switch (msg->type)
|
|
{
|
|
case SDL_MOUSEMOTION:
|
|
{
|
|
const SDL_MouseMotionEvent *mouseEvt = reinterpret_cast<const SDL_MouseMotionEvent *>(msg);
|
|
PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, mouseEvt->x, mouseEvt->y, pixelScaleX, pixelScaleY);
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
{
|
|
const SDL_MouseButtonEvent *mouseEvt = reinterpret_cast<const SDL_MouseButtonEvent *>(msg);
|
|
GpMouseEventType_t evtType = GpMouseEventTypes::kDown;
|
|
GpMouseButton_t mouseButton = GpMouseButtons::kLeft;
|
|
|
|
if (mouseEvt->type == SDL_MOUSEBUTTONDOWN)
|
|
evtType = GpMouseEventTypes::kDown;
|
|
else if (mouseEvt->type == SDL_MOUSEBUTTONUP)
|
|
evtType = GpMouseEventTypes::kUp;
|
|
else
|
|
break;
|
|
|
|
if (mouseEvt->button == SDL_BUTTON_LEFT)
|
|
mouseButton = GpMouseButtons::kLeft;
|
|
else if (mouseEvt->button == SDL_BUTTON_RIGHT)
|
|
mouseButton = GpMouseButtons::kRight;
|
|
else if (mouseEvt->button == SDL_BUTTON_MIDDLE)
|
|
mouseButton = GpMouseButtons::kMiddle;
|
|
else if (mouseEvt->button == SDL_BUTTON_X1)
|
|
mouseButton = GpMouseButtons::kX1;
|
|
else if (mouseEvt->button == SDL_BUTTON_X2)
|
|
mouseButton = GpMouseButtons::kX2;
|
|
else
|
|
break;
|
|
|
|
PostMouseEvent(eventQueue, evtType, mouseButton, mouseEvt->x, mouseEvt->y, pixelScaleX, pixelScaleY);
|
|
}
|
|
break;
|
|
case SDL_FINGERUP:
|
|
case SDL_FINGERDOWN:
|
|
case SDL_FINGERMOTION:
|
|
{
|
|
const SDL_TouchFingerEvent *fingerEvt = reinterpret_cast<const SDL_TouchFingerEvent *>(msg);
|
|
GpTouchEventType_t evtType = GpTouchEventTypes::kDown;
|
|
|
|
if (fingerEvt->type == SDL_FINGERUP)
|
|
evtType = GpTouchEventTypes::kUp;
|
|
else if (fingerEvt->type == SDL_FINGERDOWN)
|
|
evtType = GpTouchEventTypes::kDown;
|
|
else if (fingerEvt->type == SDL_FINGERMOTION)
|
|
evtType = GpTouchEventTypes::kMove;
|
|
else
|
|
break;
|
|
|
|
float unnormalizedX = static_cast<float>(m_windowWidthVirtual) * fingerEvt->x;
|
|
float unnormalizedY = static_cast<float>(m_windowHeightVirtual) * fingerEvt->y;
|
|
|
|
PostTouchEvent(eventQueue, evtType, static_cast<int32_t>(unnormalizedX), static_cast<int32_t>(unnormalizedY), fingerEvt->touchId, fingerEvt->fingerId);
|
|
}
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
{
|
|
const SDL_KeyboardEvent *keyEvt = reinterpret_cast<const SDL_KeyboardEvent *>(msg);
|
|
|
|
GpKeyIDSubset_t subset;
|
|
GpKeyboardInputEvent::KeyUnion key;
|
|
bool isRepeat = (keyEvt->repeat != 0);
|
|
const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAuto : GpKeyboardInputEventTypes::kDown;
|
|
if (IdentifyVKey(keyEvt, subset, key))
|
|
{
|
|
PostKeyboardEvent(eventQueue, keyEventType, subset, key, keyEvt->repeat + 1);
|
|
if (subset == GpKeyIDSubsets::kSpecial && key.m_specialKey == GpKeySpecials::kEnter)
|
|
{
|
|
const GpKeyboardInputEventType_t charEventType = isRepeat ? GpKeyboardInputEventTypes::kAutoChar : GpKeyboardInputEventTypes::kDownChar;
|
|
|
|
GpKeyboardInputEvent::KeyUnion crKey;
|
|
crKey.m_asciiChar = '\n';
|
|
PostKeyboardEvent(eventQueue, charEventType, GpKeyIDSubsets::kASCII, crKey, keyEvt->repeat + 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SDL_KEYUP:
|
|
{
|
|
const SDL_KeyboardEvent *keyEvt = reinterpret_cast<const SDL_KeyboardEvent *>(msg);
|
|
|
|
GpKeyIDSubset_t subset;
|
|
GpKeyboardInputEvent::KeyUnion key;
|
|
if (IdentifyVKey(keyEvt, subset, key))
|
|
PostKeyboardEvent(eventQueue, GpKeyboardInputEventTypes::kUp, subset, key, keyEvt->repeat + 1);
|
|
}
|
|
break;
|
|
case SDL_TEXTINPUT:
|
|
{
|
|
// SDL doesn't report if the text input event is a repeat, which sucks...
|
|
const SDL_TextInputEvent *teEvt = reinterpret_cast<const SDL_TextInputEvent *>(msg);
|
|
|
|
size_t lenUTF8 = strlen(teEvt->text);
|
|
|
|
size_t parseOffset = 0;
|
|
while (parseOffset < lenUTF8)
|
|
{
|
|
uint32_t codePoint;
|
|
size_t numDigested;
|
|
DeleteMe::DecodeCodePoint(reinterpret_cast<const uint8_t*>(teEvt->text) + parseOffset, lenUTF8 - parseOffset, numDigested, codePoint);
|
|
|
|
parseOffset += numDigested;
|
|
|
|
const GpKeyboardInputEventType_t keyEventType = GpKeyboardInputEventTypes::kDownChar;
|
|
GpKeyboardInputEvent::KeyUnion key;
|
|
GpKeyIDSubset_t subset = GpKeyIDSubsets::kASCII;
|
|
if (codePoint <= 128)
|
|
key.m_asciiChar = static_cast<char>(codePoint);
|
|
else
|
|
{
|
|
subset = GpKeyIDSubsets::kUnicode;
|
|
key.m_unicodeChar = static_cast<uint32_t>(codePoint);
|
|
}
|
|
PostKeyboardEvent(eventQueue, keyEventType, subset, key, 1);
|
|
}
|
|
|
|
if (!obstructiveTextInput)
|
|
{
|
|
SDL_StopTextInput();
|
|
SDL_StartTextInput();
|
|
}
|
|
}
|
|
break;
|
|
case SDL_QUIT:
|
|
{
|
|
if (GpVOSEvent *evt = eventQueue->QueueEvent())
|
|
evt->m_eventType = GpVOSEventTypes::kQuit;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::Run()
|
|
{
|
|
#if GP_GL_IS_OPENGL_4_CONTEXT
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
|
#else
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
#endif
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
|
|
IGpLogDriver *logger = m_properties.m_logger;
|
|
|
|
m_vosEvent = m_properties.m_systemServices->CreateThreadEvent(true, false);
|
|
m_vosFiber = new GpFiber_SDL(nullptr, m_vosEvent);
|
|
|
|
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
|
if (m_isFullScreenDesired)
|
|
{
|
|
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
m_isFullScreen = true;
|
|
}
|
|
else
|
|
windowFlags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
m_window = SDL_CreateWindow(GP_APPLICATION_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_windowWidthPhysical, m_windowHeightPhysical, windowFlags);
|
|
|
|
if (m_isFullScreen)
|
|
{
|
|
m_windowModeRevertWidth = m_windowWidthPhysical;
|
|
m_windowModeRevertHeight = m_windowHeightPhysical;
|
|
|
|
int windowWidth = 0;
|
|
int windowHeight = 0;
|
|
SDL_GetWindowSize(m_window, &windowWidth, &windowHeight);
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Initialized fullscreen SDL window %i x %i", windowWidth, windowHeight);
|
|
|
|
m_windowWidthPhysical = windowWidth;
|
|
m_windowHeightPhysical = windowHeight;
|
|
|
|
uint32_t desiredWidth = windowWidth;
|
|
uint32_t desiredHeight = windowHeight;
|
|
uint32_t virtualWidth = m_windowWidthVirtual;
|
|
uint32_t virtualHeight = m_windowHeightVirtual;
|
|
float pixelScaleX = m_pixelScaleX;
|
|
float pixelScaleY = m_pixelScaleY;
|
|
|
|
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
|
{
|
|
m_windowWidthVirtual = virtualWidth;
|
|
m_windowHeightVirtual = virtualHeight;
|
|
m_pixelScaleX = pixelScaleX;
|
|
m_pixelScaleY = pixelScaleY;
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "AdjustedRequestedResolution succeeded. Virtual dimensions %i x %i Pixel scale %f x %f", static_cast<int>(virtualWidth), static_cast<int>(virtualHeight), static_cast<float>(pixelScaleX), static_cast<float>(pixelScaleY));
|
|
}
|
|
else
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "AdjustedRequestedResolution failed!");
|
|
}
|
|
}
|
|
|
|
const bool obstructiveTextInput = m_properties.m_systemServices->IsTextInputObstructive();
|
|
|
|
if (!obstructiveTextInput)
|
|
SDL_StartTextInput();
|
|
|
|
StartOpenGLForWindow(logger);
|
|
|
|
if (!m_gl.LookUpFunctions())
|
|
return;
|
|
|
|
for (;;)
|
|
{
|
|
SDL_Event msg;
|
|
if (SDL_PollEvent(&msg) != 0)
|
|
{
|
|
if (msg.type == SDL_MOUSEMOTION)
|
|
{
|
|
if (!m_mouseIsInClientArea)
|
|
m_mouseIsInClientArea = true;
|
|
}
|
|
//else if (msg.type == SDL_MOUSELEAVE) // Does SDL support this??
|
|
// m_mouseIsInClientArea = false;
|
|
else if (msg.type == SDL_RENDER_DEVICE_RESET || msg.type == SDL_RENDER_TARGETS_RESET)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to device loss (Type: %i)", static_cast<int>(msg.type));
|
|
|
|
m_contextLost = true;
|
|
}
|
|
|
|
TranslateSDLMessage(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY, obstructiveTextInput);
|
|
}
|
|
else
|
|
{
|
|
if (m_isFullScreen != m_isFullScreenDesired)
|
|
{
|
|
if (m_isFullScreenDesired)
|
|
BecomeFullScreen();
|
|
else
|
|
BecomeWindowed();
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to fullscreen state change");
|
|
|
|
m_contextLost = true;
|
|
continue;
|
|
}
|
|
|
|
int clientWidth = 0;
|
|
int clientHeight = 0;
|
|
SDL_GetWindowSize(m_window, &clientWidth, &clientHeight);
|
|
|
|
unsigned int desiredWidth = clientWidth;
|
|
unsigned int desiredHeight = clientHeight;
|
|
if (desiredWidth != m_windowWidthPhysical || desiredHeight != m_windowHeightPhysical || m_isResolutionResetDesired)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Detected window size change");
|
|
|
|
uint32_t prevWidthPhysical = m_windowWidthPhysical;
|
|
uint32_t prevHeightPhysical = m_windowHeightPhysical;
|
|
uint32_t prevWidthVirtual = m_windowWidthVirtual;
|
|
uint32_t prevHeightVirtual = m_windowHeightVirtual;
|
|
uint32_t virtualWidth = m_windowWidthVirtual;
|
|
uint32_t virtualHeight = m_windowHeightVirtual;
|
|
float pixelScaleX = 1.0f;
|
|
float pixelScaleY = 1.0f;
|
|
|
|
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
|
|
{
|
|
bool resizedOK = ResizeOpenGLWindow(m_windowWidthPhysical, m_windowHeightPhysical, desiredWidth, desiredHeight, logger);
|
|
|
|
if (!resizedOK)
|
|
break; // Critical video driver error, exit
|
|
|
|
m_windowWidthVirtual = virtualWidth;
|
|
m_windowHeightVirtual = virtualHeight;
|
|
m_pixelScaleX = pixelScaleX;
|
|
m_pixelScaleY = pixelScaleY;
|
|
m_isResolutionResetDesired = false;
|
|
|
|
if (GpVOSEvent *resizeEvent = m_properties.m_eventQueue->QueueEvent())
|
|
{
|
|
resizeEvent->m_eventType = GpVOSEventTypes::kVideoResolutionChanged;
|
|
resizeEvent->m_event.m_resolutionChangedEvent.m_prevWidth = prevWidthVirtual;
|
|
resizeEvent->m_event.m_resolutionChangedEvent.m_prevHeight = prevHeightVirtual;
|
|
resizeEvent->m_event.m_resolutionChangedEvent.m_newWidth = m_windowWidthVirtual;
|
|
resizeEvent->m_event.m_resolutionChangedEvent.m_newHeight = m_windowHeightVirtual;
|
|
}
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to window size change");
|
|
|
|
m_contextLost = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (m_contextLost)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Resetting OpenGL context. Physical: %i x %i Virtual %i x %i", static_cast<int>(m_windowWidthPhysical), static_cast<int>(m_windowHeightPhysical), static_cast<int>(m_windowWidthVirtual), static_cast<int>(m_windowHeightVirtual));
|
|
|
|
// Drop everything and reset
|
|
m_res.~InstancedResources();
|
|
new (&m_res) InstancedResources();
|
|
|
|
if (m_firstSurface)
|
|
m_firstSurface->DestroyAll();
|
|
|
|
if (!InitResources(m_windowWidthPhysical, m_windowHeightPhysical, m_windowWidthVirtual, m_windowHeightVirtual))
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to InitResources failing");
|
|
|
|
break;
|
|
}
|
|
|
|
if (m_firstSurface)
|
|
m_firstSurface->RecreateAll();
|
|
|
|
m_contextLost = false;
|
|
continue;
|
|
}
|
|
|
|
bool wantTextInput = m_properties.m_systemServices->IsTextInputEnabled();
|
|
if (wantTextInput != m_textInputEnabled)
|
|
{
|
|
m_textInputEnabled = wantTextInput;
|
|
if (m_textInputEnabled)
|
|
SDL_StartTextInput();
|
|
else
|
|
SDL_StopTextInput();
|
|
}
|
|
|
|
// Handle dismissal of on-screen keyboard
|
|
const bool isTextInputActuallyActive = SDL_IsTextInputActive();
|
|
m_textInputEnabled = isTextInputActuallyActive;
|
|
m_properties.m_systemServices->SetTextInputEnabled(isTextInputActuallyActive);
|
|
|
|
GpDisplayDriverTickStatus_t tickStatus = PresentFrameAndSync();
|
|
if (tickStatus == GpDisplayDriverTickStatuses::kFatalFault || tickStatus == GpDisplayDriverTickStatuses::kApplicationTerminated)
|
|
{
|
|
if (logger)
|
|
{
|
|
if (tickStatus == GpDisplayDriverTickStatuses::kFatalFault)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to fatal fault");
|
|
if (tickStatus == GpDisplayDriverTickStatuses::kApplicationTerminated)
|
|
logger->Printf(IGpLogDriver::Category_Information, "Terminating display driver due to application termination");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exit
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::Shutdown()
|
|
{
|
|
this->~GpDisplayDriver_SDL_GL2();
|
|
free(this);
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::GetDisplayResolution(unsigned int *width, unsigned int *height)
|
|
{
|
|
if (width)
|
|
*width = m_windowWidthVirtual;
|
|
|
|
if (height)
|
|
*height = m_windowHeightVirtual;
|
|
}
|
|
|
|
IGpDisplayDriverSurface *GpDisplayDriver_SDL_GL2::CreateSurface(size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat, SurfaceInvalidateCallback_t invalidateCallback, void *invalidateContext)
|
|
{
|
|
GpDisplayDriverSurface_GL2 *surface = GpDisplayDriverSurface_GL2::Create(this, width, height, pitch, pixelFormat, m_lastSurface, invalidateCallback, invalidateContext);
|
|
if (surface)
|
|
{
|
|
m_lastSurface = surface;
|
|
if (m_firstSurface == nullptr)
|
|
m_firstSurface = surface;
|
|
}
|
|
return surface;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::DrawSurface(IGpDisplayDriverSurface *surface, int32_t x, int32_t y, size_t width, size_t height, const GpDisplayDriverSurfaceEffects *effects)
|
|
{
|
|
if (!effects)
|
|
effects = &gs_defaultEffects;
|
|
|
|
GpGLVertexArray *vaPtr = m_res.m_quadVertexArray;
|
|
size_t vbStride = sizeof(float) * 2;
|
|
size_t zero = 0;
|
|
|
|
GpDisplayDriverSurface_GL2 *glSurface = static_cast<GpDisplayDriverSurface_GL2*>(surface);
|
|
GpPixelFormat_t pixelFormat = glSurface->GetPixelFormat();
|
|
|
|
DrawQuadProgram *program = nullptr;
|
|
|
|
if (pixelFormat == GpPixelFormats::k8BitStandard || pixelFormat == GpPixelFormats::k8BitCustom)
|
|
{
|
|
if (m_useICCProfile)
|
|
{
|
|
if (effects->m_flicker)
|
|
program = &m_res.m_drawQuadPaletteICCFlickerProgram;
|
|
else
|
|
program = &m_res.m_drawQuadPaletteICCNoFlickerProgram;
|
|
}
|
|
else
|
|
{
|
|
if (effects->m_flicker)
|
|
program = &m_res.m_drawQuadPaletteFlickerProgram;
|
|
else
|
|
program = &m_res.m_drawQuadPaletteNoFlickerProgram;
|
|
}
|
|
}
|
|
else if (pixelFormat == GpPixelFormats::kRGB555)
|
|
{
|
|
return;
|
|
}
|
|
else if (pixelFormat == GpPixelFormats::kRGB32)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
CheckGLError(m_gl, m_properties.m_logger);
|
|
|
|
m_gl.UseProgram(program->m_program->GetID());
|
|
|
|
CheckGLError(m_gl, m_properties.m_logger);
|
|
|
|
{
|
|
const float twoDivWidth = 2.0f / static_cast<float>(m_windowWidthVirtual);
|
|
const float negativeTwoDivHeight = -2.0f / static_cast<float>(m_windowHeightVirtual);
|
|
|
|
GLfloat ndcOriginAndDimensions[4] =
|
|
{
|
|
|
|
static_cast<GLfloat>(x) * twoDivWidth - 1.0f,
|
|
static_cast<GLfloat>(y) * negativeTwoDivHeight + 1.0f,
|
|
static_cast<GLfloat>(width) * twoDivWidth,
|
|
static_cast<GLfloat>(height) * negativeTwoDivHeight,
|
|
};
|
|
|
|
GLfloat surfaceDimensions_TextureRegion[4] =
|
|
{
|
|
static_cast<GLfloat>(glSurface->GetImageWidth()),
|
|
static_cast<GLfloat>(glSurface->GetHeight()),
|
|
static_cast<GLfloat>(static_cast<float>(glSurface->GetImageWidth()) / static_cast<float>(glSurface->GetPaddedTextureWidth())),
|
|
1.f
|
|
};
|
|
|
|
m_gl.Uniform4fv(program->m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(ndcOriginAndDimensions));
|
|
m_gl.Uniform4fv(program->m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(surfaceDimensions_TextureRegion));
|
|
|
|
GLfloat modulation[4] = { 1.f, 1.f, 1.f, 1.f };
|
|
GLfloat flickerAxis[2] = { 0.f, 0.f };
|
|
GLfloat flickerStart = -1.f;
|
|
GLfloat flickerEnd = -2.f;
|
|
|
|
if (effects->m_flicker)
|
|
{
|
|
flickerAxis[0] = effects->m_flickerAxisX;
|
|
flickerAxis[1] = effects->m_flickerAxisY;
|
|
flickerStart = effects->m_flickerStartThreshold;
|
|
flickerEnd = effects->m_flickerEndThreshold;
|
|
}
|
|
|
|
float desaturation = effects->m_desaturation;
|
|
|
|
if (effects->m_darken)
|
|
for (int i = 0; i < 3; i++)
|
|
modulation[i] = 0.5f;
|
|
|
|
m_gl.Uniform4fv(program->m_pixelModulationLocation, 1, modulation);
|
|
m_gl.Uniform2fv(program->m_pixelFlickerAxisLocation, 1, flickerAxis);
|
|
m_gl.Uniform1fv(program->m_pixelFlickerStartThresholdLocation, 1, &flickerStart);
|
|
m_gl.Uniform1fv(program->m_pixelFlickerEndThresholdLocation, 1, &flickerEnd);
|
|
m_gl.Uniform1fv(program->m_pixelDesaturationLocation, 1, &desaturation);
|
|
}
|
|
|
|
GLint vpos[1] = { program->m_vertexPosUVLocation };
|
|
|
|
m_res.m_quadVertexArray->Activate(vpos);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, glSurface->GetTexture()->GetID());
|
|
m_gl.Uniform1i(program->m_pixelSurfaceTextureLocation, 0);
|
|
|
|
if (pixelFormat == GpPixelFormats::k8BitStandard || pixelFormat == GpPixelFormats::k8BitCustom)
|
|
{
|
|
m_gl.ActiveTexture(GL_TEXTURE1);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_paletteTexture->GetID());
|
|
m_gl.Uniform1i(program->m_pixelPaletteTextureLocation, 1);
|
|
}
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_res.m_quadIndexBuffer->GetID());
|
|
m_gl.DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
CheckGLError(m_gl, m_properties.m_logger);
|
|
|
|
if (pixelFormat == GpPixelFormats::k8BitStandard || pixelFormat == GpPixelFormats::k8BitCustom)
|
|
{
|
|
m_gl.ActiveTexture(GL_TEXTURE1);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
m_res.m_quadVertexArray->Deactivate(vpos);
|
|
|
|
m_gl.UseProgram(0);
|
|
|
|
CheckGLError(m_gl, m_properties.m_logger);
|
|
}
|
|
|
|
|
|
IGpCursor *GpDisplayDriver_SDL_GL2::CreateBWCursor(size_t width, size_t height, const void *pixelData, const void *maskData, size_t hotSpotX, size_t hotSpotY)
|
|
{
|
|
SDL_Cursor *cursor = SDL_CreateCursor(static_cast<const Uint8*>(pixelData), static_cast<const Uint8*>(maskData), width, height, hotSpotX, hotSpotY);
|
|
return new GpCursor_SDL2(cursor);
|
|
}
|
|
|
|
IGpCursor *GpDisplayDriver_SDL_GL2::CreateColorCursor(size_t width, size_t height, const void *pixelDataRGBA, size_t hotSpotX, size_t hotSpotY)
|
|
{
|
|
uint32_t channelMasks[4];
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
channelMasks[i] = 0;
|
|
reinterpret_cast<uint8_t*>(&channelMasks[i])[i] = 0xff;
|
|
}
|
|
|
|
SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, 32, channelMasks[0], channelMasks[1], channelMasks[2], channelMasks[3]);
|
|
if (!surface)
|
|
return nullptr;
|
|
|
|
size_t surfacePitch = surface->pitch;
|
|
uint8_t *destPixels = reinterpret_cast<uint8_t*>(surface->pixels);
|
|
for (size_t y = 0; y < height; y++)
|
|
memcpy(destPixels + y * surfacePitch, static_cast<const uint8_t*>(pixelDataRGBA) + y * width * 4, width * 4);
|
|
|
|
SDL_Cursor *cursor = SDL_CreateColorCursor(surface, hotSpotX, hotSpotY);
|
|
SDL_FreeSurface(surface);
|
|
|
|
if (!cursor)
|
|
return nullptr;
|
|
|
|
return new GpCursor_SDL2(cursor);
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetCursor(IGpCursor *cursor)
|
|
{
|
|
GpCursor_SDL2 *sdlCursor = static_cast<GpCursor_SDL2*>(cursor);
|
|
|
|
sdlCursor->IncRef();
|
|
|
|
if (m_pendingCursor)
|
|
m_pendingCursor->DecRef();
|
|
|
|
m_pendingCursor = sdlCursor;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetStandardCursor(EGpStandardCursor_t standardCursor)
|
|
{
|
|
if (m_pendingCursor)
|
|
{
|
|
m_pendingCursor->DecRef();
|
|
m_pendingCursor = nullptr;
|
|
}
|
|
|
|
m_pendingStandardCursor = standardCursor;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::UpdatePalette(const void *paletteData)
|
|
{
|
|
memcpy(m_paletteData, paletteData, 256 * 4);
|
|
|
|
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_paletteTexture->GetID());
|
|
m_gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_paletteData);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
|
{
|
|
uint8_t rgba[4] = { r, g, b, a };
|
|
for (int i = 0; i < 4; i++)
|
|
m_bgColor[i] = static_cast<float>(rgba[i]) / 255.0f;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetBackgroundDarkenEffect(bool isDark)
|
|
{
|
|
m_bgIsDark = isDark;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetUseICCProfile(bool useICCProfile)
|
|
{
|
|
m_useICCProfile = useICCProfile;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::RequestToggleFullScreen(uint32_t timestamp)
|
|
{
|
|
// Alt-Enter gets re-sent after a full-screen toggle, so we ignore toggle requests until half a second has elapsed
|
|
if (timestamp == 0 || timestamp > m_lastFullScreenToggleTimeStamp + 30)
|
|
{
|
|
m_isFullScreenDesired = !m_isFullScreenDesired;
|
|
m_lastFullScreenToggleTimeStamp = timestamp;
|
|
}
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::RequestResetVirtualResolution()
|
|
{
|
|
m_isResolutionResetDesired = true;
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::IsFullScreen() const
|
|
{
|
|
return m_isFullScreenDesired;
|
|
}
|
|
|
|
const GpDisplayDriverProperties &GpDisplayDriver_SDL_GL2::GetProperties() const
|
|
{
|
|
return m_properties;
|
|
}
|
|
|
|
IGpPrefsHandler *GpDisplayDriver_SDL_GL2::GetPrefsHandler() const
|
|
{
|
|
return const_cast<GpDisplayDriver_SDL_GL2*>(this);
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::SupportsSizedFormats() const
|
|
{
|
|
#if GP_GL_IS_OPENGL_4_CONTEXT
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::ApplyPrefs(const void *identifier, size_t identifierSize, const void *contents, size_t contentsSize, uint32_t version)
|
|
{
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::SavePrefs(void *context, WritePrefsFunc_t writeFunc)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::UnlinkSurface(GpDisplayDriverSurface_GL2 *surface, GpDisplayDriverSurface_GL2 *prev, GpDisplayDriverSurface_GL2 *next)
|
|
{
|
|
if (m_lastSurface == surface)
|
|
m_lastSurface = prev;
|
|
|
|
if (m_firstSurface == surface)
|
|
m_firstSurface = next;
|
|
}
|
|
|
|
const GpGLFunctions *GpDisplayDriver_SDL_GL2::GetGLFunctions() const
|
|
{
|
|
return &m_gl;
|
|
}
|
|
|
|
|
|
|
|
template<GLuint TShaderType>
|
|
GpComPtr<GpGLShader<TShaderType>> GpDisplayDriver_SDL_GL2::CreateShader(const char *shaderSrc)
|
|
{
|
|
size_t shaderCodeLength = strlen(shaderSrc);
|
|
GpComPtr<GpGLShader<TShaderType>> shader(GpGLShader<TShaderType>::Create(this));
|
|
if (shader == nullptr)
|
|
return shader;
|
|
|
|
const GLchar *shaderSrcGL = reinterpret_cast<const GLchar*>(shaderSrc);
|
|
GLint shaderLenGL = static_cast<GLint>(shaderCodeLength);
|
|
m_gl.ShaderSource(shader->GetID(), 1, &shaderSrcGL, &shaderLenGL);
|
|
m_gl.CompileShader(shader->GetID());
|
|
|
|
GLint compiled = 0;
|
|
m_gl.GetShaderiv(shader->GetID(), GL_COMPILE_STATUS, &compiled);
|
|
if (!compiled)
|
|
{
|
|
GLint infoLength = 0;
|
|
m_gl.GetShaderiv(shader->GetID(), GL_INFO_LOG_LENGTH, &infoLength);
|
|
|
|
std::vector<char> log;
|
|
log.resize(infoLength + 1);
|
|
log[infoLength] = '\0';
|
|
|
|
m_gl.GetShaderInfoLog(shader->GetID(), static_cast<GLsizei>(log.size() - 1), nullptr, reinterpret_cast<GLchar*>(&log[0]));
|
|
|
|
const char *errorMsg = &log[0];
|
|
|
|
return GpComPtr<GpGLShader<TShaderType>>();
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::StartOpenGLForWindow(IGpLogDriver *logger)
|
|
{
|
|
SDL_GLContext context = SDL_GL_CreateContext(m_window);
|
|
SDL_GL_SetSwapInterval(1);
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t physicalHeight, uint32_t virtualWidth, uint32_t virtualHeight)
|
|
{
|
|
IGpLogDriver *logger = m_properties.m_logger;
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "GpDisplayDriver_SDL_GL2::InitResources");
|
|
|
|
if ((m_pixelScaleX < 2.0f && m_pixelScaleX > 1.0f) || (m_pixelScaleY < 2.0f && m_pixelScaleY > 1.0f))
|
|
m_useUpscaleFilter = true;
|
|
|
|
CheckGLError(m_gl, logger);
|
|
|
|
if (!InitBackBuffer(virtualWidth, virtualHeight))
|
|
return false;
|
|
|
|
// Quad index buffer
|
|
{
|
|
const uint16_t indexBufferData[] = { 0, 1, 2, 1, 3, 2 };
|
|
|
|
m_res.m_quadIndexBuffer = GpGLBuffer::Create(this);
|
|
if (!m_res.m_quadIndexBuffer)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: CreateBuffer for draw quad index buffer failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_res.m_quadIndexBuffer->GetID());
|
|
m_gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexBufferData), indexBufferData, GL_STATIC_DRAW);
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
// Quad vertex buffer
|
|
{
|
|
const float vertexBufferData[] =
|
|
{
|
|
0.f, 0.f,
|
|
1.f, 0.f,
|
|
0.f, 1.f,
|
|
1.f, 1.f,
|
|
};
|
|
|
|
m_res.m_quadVertexBufferKeepalive = GpGLBuffer::Create(this);
|
|
if (!m_res.m_quadVertexBufferKeepalive)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: GpGLBuffer::Create for draw quad vertex buffer failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindBuffer(GL_ARRAY_BUFFER, m_res.m_quadVertexBufferKeepalive->GetID());
|
|
m_gl.BufferData(GL_ARRAY_BUFFER, sizeof(vertexBufferData), vertexBufferData, GL_STATIC_DRAW);
|
|
m_gl.BindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
m_res.m_quadVertexArray = GpGLVertexArray::Create(this);
|
|
if (!m_res.m_quadVertexBufferKeepalive)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: GpGLVertexArray::Create for draw quad vertex buffer failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
GpGLVertexArraySpec specs[] =
|
|
{
|
|
m_res.m_quadVertexBufferKeepalive,
|
|
0, // index
|
|
2, // size
|
|
GL_FLOAT, // type
|
|
GL_FALSE, // normalized
|
|
sizeof(float) * 2, // stride
|
|
0
|
|
};
|
|
|
|
if (!m_res.m_quadVertexArray->InitWithSpecs(specs, sizeof(specs) / sizeof(specs[0])))
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: InitWithSpecs for draw quad vertex buffer failed");
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
GpComPtr<GpGLShader<GL_VERTEX_SHADER>> drawQuadVertexShader = CreateShader<GL_VERTEX_SHADER>(GpBinarizedShaders::g_drawQuadV_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePF_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteNoFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePNF_GL2);
|
|
//m_drawQuadRGBPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadRGBP_GL2);
|
|
//m_drawQuad15BitPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad15BitP_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPF_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCNFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPNF_GL2);
|
|
//m_drawQuadRGBICCPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadRGBICCP_GL2);
|
|
//m_drawQuad15BitICCPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad15BitICCP_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> scaleQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_scaleQuadP_GL2);
|
|
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> copyQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_copyQuadP_GL2);
|
|
|
|
if (!m_res.m_drawQuadPaletteFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader)
|
|
|| !m_res.m_drawQuadPaletteNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader)
|
|
//|| !m_drawQuadRGBProgram.Link(this, drawQuadVertexShader, drawQuadRGBPixelShader)
|
|
//|| !m_drawQuad15BitProgram.Link(this, drawQuadVertexShader, drawQuad15BitPixelShader)
|
|
|| !m_res.m_drawQuadPaletteICCFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCFPixelShader)
|
|
|| !m_res.m_drawQuadPaletteICCNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCNFPixelShader)
|
|
//|| !m_drawQuadRGBICCProgram.Link(this, drawQuadVertexShader, drawQuadRGBICCPixelShader)
|
|
//|| !m_drawQuad15BitICCProgram.Link(this, drawQuadVertexShader, drawQuad15BitICCPixelShader)
|
|
|| !m_res.m_scaleQuadProgram.Link(this, drawQuadVertexShader, scaleQuadPixelShader)
|
|
|| !m_res.m_copyQuadProgram.Link(this, drawQuadVertexShader, copyQuadPixelShader))
|
|
return false;
|
|
|
|
// Palette texture
|
|
{
|
|
m_res.m_paletteTexture = GpGLTexture::Create(this);
|
|
if (!m_res.m_paletteTexture)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: GpGLTexture::Create failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_paletteTexture->GetID());
|
|
m_gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_paletteData);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::BecomeFullScreen()
|
|
{
|
|
SDL_GetWindowPosition(m_window, &m_windowModeRevertX, &m_windowModeRevertY);
|
|
SDL_GetWindowSize(m_window, &m_windowModeRevertWidth, &m_windowModeRevertHeight);
|
|
SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
|
|
m_isFullScreen = true;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::BecomeWindowed()
|
|
{
|
|
SDL_SetWindowFullscreen(m_window, 0);
|
|
SDL_SetWindowPosition(m_window, m_windowModeRevertX, m_windowModeRevertY);
|
|
SDL_SetWindowSize(m_window, m_windowModeRevertWidth, m_windowModeRevertHeight);
|
|
|
|
m_isFullScreen = false;
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SynchronizeCursors()
|
|
{
|
|
if (m_activeCursor)
|
|
{
|
|
if (m_pendingCursor != m_activeCursor)
|
|
{
|
|
if (m_pendingCursor == nullptr)
|
|
{
|
|
m_currentStandardCursor = m_pendingStandardCursor;
|
|
ChangeToStandardCursor(m_currentStandardCursor);
|
|
|
|
m_activeCursor->DecRef();
|
|
m_activeCursor = nullptr;
|
|
}
|
|
else
|
|
{
|
|
ChangeToCursor(m_pendingCursor->GetCursor());
|
|
|
|
m_pendingCursor->IncRef();
|
|
m_activeCursor->DecRef();
|
|
m_activeCursor = m_pendingCursor;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_pendingCursor)
|
|
{
|
|
m_pendingCursor->IncRef();
|
|
m_activeCursor = m_pendingCursor;
|
|
|
|
ChangeToCursor(m_activeCursor->GetCursor());
|
|
}
|
|
else
|
|
{
|
|
if (m_pendingStandardCursor != m_currentStandardCursor)
|
|
{
|
|
ChangeToStandardCursor(m_pendingStandardCursor);
|
|
m_currentStandardCursor = m_pendingStandardCursor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::ChangeToCursor(SDL_Cursor *cursor)
|
|
{
|
|
if (cursor == nullptr)
|
|
{
|
|
if (!m_cursorIsHidden)
|
|
{
|
|
m_cursorIsHidden = true;
|
|
SDL_ShowCursor(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_cursorIsHidden)
|
|
{
|
|
m_cursorIsHidden = false;
|
|
SDL_ShowCursor(1);
|
|
}
|
|
SDL_SetCursor(cursor);
|
|
}
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::ChangeToStandardCursor(EGpStandardCursor_t cursor)
|
|
{
|
|
switch (cursor)
|
|
{
|
|
case EGpStandardCursors::kArrow:
|
|
SDL_SetCursor(m_arrowCursor);
|
|
break;
|
|
case EGpStandardCursors::kHidden:
|
|
SDL_SetCursor(nullptr);
|
|
break;
|
|
case EGpStandardCursors::kIBeam:
|
|
SDL_SetCursor(m_iBeamCursor);
|
|
break;
|
|
case EGpStandardCursors::kWait:
|
|
SDL_SetCursor(m_waitCursor);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool GpDisplayDriver_SDL_GL2::ResizeOpenGLWindow(uint32_t &windowWidth, uint32_t &windowHeight, uint32_t desiredWidth, uint32_t desiredHeight, IGpLogDriver *logger)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "ResizeOpenGLWindow: %i x %i", static_cast<int>(desiredWidth), static_cast<int>(desiredHeight));
|
|
|
|
if (desiredWidth > 32768)
|
|
desiredWidth = 32768;
|
|
|
|
if (desiredHeight > 32768)
|
|
desiredHeight = 32768;
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "ResizeOpenGLWindow: Adjusted dimensions: %i x %i", static_cast<int>(desiredWidth), static_cast<int>(desiredHeight));
|
|
|
|
SDL_SetWindowSize(m_window, desiredWidth, desiredHeight);
|
|
|
|
windowWidth = desiredWidth;
|
|
windowHeight = desiredHeight;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
|
{
|
|
IGpLogDriver *logger = m_properties.m_logger;
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "GpDisplayDriver_SDL_GL2::InitBackBuffer: %i x %i", static_cast<int>(width), static_cast<int>(height));
|
|
|
|
{
|
|
m_res.m_virtualScreenTexture = GpGLTexture::Create(this);
|
|
if (!m_res.m_virtualScreenTexture)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: GpGLTexture::Create for virtual screen texture failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_virtualScreenTexture->GetID());
|
|
m_gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
CheckGLError(m_gl, logger);
|
|
}
|
|
|
|
{
|
|
m_res.m_virtualScreenTextureRTV = GpGLRenderTargetView::Create(this);
|
|
|
|
if (!m_res.m_virtualScreenTextureRTV)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: GpGLRenderTargetView::Create for virtual screen texture failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, m_res.m_virtualScreenTextureRTV->GetID());
|
|
m_gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_res.m_virtualScreenTexture->GetID(), 0);
|
|
GLenum status = m_gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: Framebuffer complete check failed, status was %i VST ID is %i", static_cast<int>(status), static_cast<int>(m_res.m_virtualScreenTextureRTV->GetID()));
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
|
|
if (m_pixelScaleX != floor(m_pixelScaleX) || m_pixelScaleY != floor(m_pixelScaleY))
|
|
{
|
|
uint32_t upscaleX = ceil(m_pixelScaleX);
|
|
uint32_t upscaleY = ceil(m_pixelScaleY);
|
|
|
|
{
|
|
m_res.m_upscaleTexture = GpGLTexture::Create(this);
|
|
if (!m_res.m_upscaleTexture)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: GpGLTexture::Create for upscale texture failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_res.m_upscaleTextureWidth = width * upscaleX;
|
|
m_res.m_upscaleTextureHeight = height * upscaleY;
|
|
|
|
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_upscaleTexture->GetID());
|
|
m_gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
CheckGLError(m_gl, logger);
|
|
}
|
|
|
|
{
|
|
m_res.m_upscaleTextureRTV = GpGLRenderTargetView::Create(this);
|
|
|
|
if (!m_res.m_upscaleTextureRTV)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: GpGLRenderTargetView::Create for upscale texture failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, m_res.m_upscaleTextureRTV->GetID());
|
|
m_gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_res.m_upscaleTexture->GetID(), 0);
|
|
GLenum status = m_gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: Framebuffer complete check failed for upscale texture, status was %i VST ID is %i", static_cast<int>(status), static_cast<int>(m_res.m_virtualScreenTextureRTV->GetID()));
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
}
|
|
|
|
CheckGLError(m_gl, logger);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void GpDisplayDriver_SDL_GL2::ScaleVirtualScreen()
|
|
{
|
|
if (m_useUpscaleFilter)
|
|
{
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, m_res.m_upscaleTextureRTV->GetID());
|
|
|
|
m_gl.Viewport(0, 0, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight);
|
|
|
|
const BlitQuadProgram &program = m_res.m_scaleQuadProgram;
|
|
|
|
float ndcOriginsAndDimensions[4] =
|
|
{
|
|
-1.0f,
|
|
-1.0f,
|
|
2.0f,
|
|
2.0f,
|
|
};
|
|
|
|
float surfaceDimensions_TextureRegion[4] =
|
|
{
|
|
static_cast<float>(m_windowWidthVirtual),
|
|
static_cast<float>(m_windowHeightVirtual),
|
|
1.f,
|
|
1.f
|
|
};
|
|
|
|
m_gl.UseProgram(program.m_program->GetID());
|
|
m_gl.Uniform4fv(program.m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(ndcOriginsAndDimensions));
|
|
m_gl.Uniform4fv(program.m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(surfaceDimensions_TextureRegion));
|
|
|
|
float dxdy_dimensions[4] =
|
|
{
|
|
static_cast<float>(1.0 / m_windowWidthVirtual),
|
|
static_cast<float>(1.0 / m_windowHeightVirtual),
|
|
static_cast<float>(m_windowWidthVirtual),
|
|
static_cast<float>(m_windowHeightVirtual)
|
|
};
|
|
|
|
m_gl.Uniform4fv(program.m_pixelDXDYDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(dxdy_dimensions));
|
|
|
|
GLint attribLocations[] = { program.m_vertexPosUVLocation };
|
|
|
|
m_res.m_quadVertexArray->Activate(attribLocations);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0 + 0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_res.m_virtualScreenTexture->GetID());
|
|
m_gl.Uniform1i(program.m_pixelSurfaceTextureLocation, 0);
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_res.m_quadIndexBuffer->GetID());
|
|
m_gl.DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
|
|
|
|
m_gl.UseProgram(0);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0 + 0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
m_res.m_quadVertexArray->Deactivate(attribLocations);
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
m_gl.Viewport(0, 0, m_windowWidthPhysical, m_windowHeightPhysical);
|
|
|
|
const BlitQuadProgram &program = m_res.m_copyQuadProgram;
|
|
|
|
{
|
|
const float twoDivWidth = 2.0f / static_cast<float>(m_windowWidthPhysical);
|
|
const float twoDivHeight = 2.0f / static_cast<float>(m_windowHeightPhysical);
|
|
|
|
// Use the scaled virtual width instead of the physical width to correctly handle cases where the window boundary is in the middle of a pixel
|
|
float fWidth = static_cast<float>(m_windowWidthVirtual) * m_pixelScaleX;
|
|
float fHeight = static_cast<float>(m_windowHeightVirtual) * m_pixelScaleY;
|
|
|
|
float ndcOriginsAndDimensions[4] =
|
|
{
|
|
-1.0f,
|
|
-1.0f,
|
|
2.0f,
|
|
2.0f,
|
|
};
|
|
|
|
float surfaceDimensions_TextureRegion[4] =
|
|
{
|
|
static_cast<float>(m_windowWidthVirtual),
|
|
static_cast<float>(m_windowHeightVirtual),
|
|
1.f,
|
|
1.f
|
|
};
|
|
|
|
m_gl.UseProgram(program.m_program->GetID());
|
|
m_gl.Uniform4fv(program.m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(ndcOriginsAndDimensions));
|
|
m_gl.Uniform4fv(program.m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(surfaceDimensions_TextureRegion));
|
|
|
|
float dxdy_dimensions[4] =
|
|
{
|
|
static_cast<float>(1.0 / m_windowWidthVirtual),
|
|
static_cast<float>(1.0 / m_windowHeightVirtual),
|
|
static_cast<float>(m_windowWidthVirtual),
|
|
static_cast<float>(m_windowHeightVirtual)
|
|
};
|
|
|
|
m_gl.Uniform4fv(program.m_pixelDXDYDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(dxdy_dimensions));
|
|
}
|
|
|
|
GLint attribLocations[] = { program.m_vertexPosUVLocation };
|
|
|
|
m_res.m_quadVertexArray->Activate(attribLocations);
|
|
|
|
|
|
GpGLTexture *inputTexture = m_useUpscaleFilter ? static_cast<GpGLTexture*>(m_res.m_upscaleTexture) : static_cast<GpGLTexture*>(m_res.m_virtualScreenTexture);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0 + 0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, inputTexture->GetID());
|
|
m_gl.Uniform1i(program.m_pixelSurfaceTextureLocation, 0);
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_res.m_quadIndexBuffer->GetID());
|
|
m_gl.DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
|
|
|
|
m_gl.UseProgram(0);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0 + 0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
m_res.m_quadVertexArray->Deactivate(attribLocations);
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
|
|
bool GpDisplayDriver_SDL_GL2::DrawQuadProgram::Link(GpDisplayDriver_SDL_GL2 *driver, const GpGLShader<GL_VERTEX_SHADER> *vertexShader, const GpGLShader<GL_FRAGMENT_SHADER> *pixelShader)
|
|
{
|
|
m_program = GpGLProgram::Create(driver);
|
|
if (!m_program)
|
|
return false;
|
|
|
|
const GpGLFunctions *gl = driver->GetGLFunctions();
|
|
gl->AttachShader(m_program->GetID(), vertexShader->GetID());
|
|
gl->AttachShader(m_program->GetID(), pixelShader->GetID());
|
|
gl->LinkProgram(m_program->GetID());
|
|
|
|
GLint linked = 0;
|
|
gl->GetProgramiv(m_program->GetID(), GL_LINK_STATUS, &linked);
|
|
if (!linked)
|
|
{
|
|
GLint logLength = 0;
|
|
gl->GetProgramiv(m_program->GetID(), GL_INFO_LOG_LENGTH, &logLength);
|
|
|
|
std::vector<char> errorMsgBuffer;
|
|
errorMsgBuffer.resize(static_cast<size_t>(logLength) + 1);
|
|
errorMsgBuffer[logLength] = '\0';
|
|
|
|
gl->GetProgramInfoLog(m_program->GetID(), static_cast<size_t>(logLength), nullptr, reinterpret_cast<GLchar*>(&errorMsgBuffer[0]));
|
|
const char *errorMsg = &errorMsgBuffer[0];
|
|
|
|
return false;
|
|
}
|
|
|
|
m_vertexNDCOriginAndDimensionsLocation = gl->GetUniformLocation(m_program->GetID(), "ndcOriginAndDimensions");
|
|
m_vertexSurfaceDimensionsLocation = gl->GetUniformLocation(m_program->GetID(), "surfaceDimensions_TextureRegion");
|
|
m_vertexPosUVLocation = gl->GetAttribLocation(m_program->GetID(), "posUV");
|
|
|
|
m_pixelModulationLocation = gl->GetUniformLocation(m_program->GetID(), "constants_Modulation");
|
|
m_pixelFlickerAxisLocation = gl->GetUniformLocation(m_program->GetID(), "constants_FlickerAxis");
|
|
m_pixelFlickerStartThresholdLocation = gl->GetUniformLocation(m_program->GetID(), "constants_FlickerStartThreshold");
|
|
m_pixelFlickerEndThresholdLocation = gl->GetUniformLocation(m_program->GetID(), "constants_FlickerEndThreshold");
|
|
m_pixelDesaturationLocation = gl->GetUniformLocation(m_program->GetID(), "constants_Desaturation");
|
|
|
|
m_pixelSurfaceTextureLocation = gl->GetUniformLocation(m_program->GetID(), "surfaceTexture");
|
|
m_pixelPaletteTextureLocation = gl->GetUniformLocation(m_program->GetID(), "paletteTexture");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GpDisplayDriver_SDL_GL2::BlitQuadProgram::Link(GpDisplayDriver_SDL_GL2 *driver, const GpGLShader<GL_VERTEX_SHADER> *vertexShader, const GpGLShader<GL_FRAGMENT_SHADER> *pixelShader)
|
|
{
|
|
m_program = GpGLProgram::Create(driver);
|
|
if (!m_program)
|
|
return false;
|
|
|
|
const GpGLFunctions *gl = driver->GetGLFunctions();
|
|
gl->AttachShader(m_program->GetID(), vertexShader->GetID());
|
|
gl->AttachShader(m_program->GetID(), pixelShader->GetID());
|
|
gl->LinkProgram(m_program->GetID());
|
|
|
|
GLint linked = 0;
|
|
gl->GetProgramiv(m_program->GetID(), GL_LINK_STATUS, &linked);
|
|
if (!linked)
|
|
{
|
|
GLint logLength = 0;
|
|
gl->GetProgramiv(m_program->GetID(), GL_INFO_LOG_LENGTH, &logLength);
|
|
|
|
std::vector<char> errorMsgBuffer;
|
|
errorMsgBuffer.resize(static_cast<size_t>(logLength) + 1);
|
|
errorMsgBuffer[logLength] = '\0';
|
|
|
|
gl->GetProgramInfoLog(m_program->GetID(), static_cast<size_t>(logLength), nullptr, reinterpret_cast<GLchar*>(&errorMsgBuffer[0]));
|
|
const char *errorMsg = &errorMsgBuffer[0];
|
|
|
|
return false;
|
|
}
|
|
|
|
m_vertexNDCOriginAndDimensionsLocation = gl->GetUniformLocation(m_program->GetID(), "ndcOriginAndDimensions");
|
|
m_vertexSurfaceDimensionsLocation = gl->GetUniformLocation(m_program->GetID(), "surfaceDimensions_TextureRegion");
|
|
m_pixelDXDYDimensionsLocation = gl->GetUniformLocation(m_program->GetID(), "dxdy_dimensions");
|
|
m_vertexPosUVLocation = gl->GetAttribLocation(m_program->GetID(), "posUV");
|
|
m_pixelSurfaceTextureLocation = gl->GetUniformLocation(m_program->GetID(), "surfaceTexture");
|
|
|
|
return true;
|
|
}
|
|
|
|
GpDisplayDriverTickStatus_t GpDisplayDriver_SDL_GL2::PresentFrameAndSync()
|
|
{
|
|
SynchronizeCursors();
|
|
|
|
float bgColor[4];
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
bgColor[i] = m_bgColor[i];
|
|
|
|
if (m_bgIsDark)
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
bgColor[i] *= 0.25f;
|
|
}
|
|
|
|
//ID3D11RenderTargetView *const rtv = m_backBufferRTV;
|
|
GpGLRenderTargetView *const vsRTV = m_res.m_virtualScreenTextureRTV;
|
|
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, vsRTV->GetID());
|
|
|
|
m_gl.Viewport(0, 0, m_windowWidthVirtual, m_windowHeightVirtual);
|
|
|
|
m_gl.ClearColor(m_bgColor[0], m_bgColor[1], m_bgColor[2], m_bgColor[3]);
|
|
m_gl.Clear(GL_COLOR_BUFFER_BIT);
|
|
|
|
m_properties.m_renderFunc(m_properties.m_renderFuncContext);
|
|
|
|
ScaleVirtualScreen();
|
|
|
|
CheckGLError(m_gl, m_properties.m_logger);
|
|
|
|
SDL_GL_SwapWindow(m_window);
|
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock>::duration syncTime = std::chrono::high_resolution_clock::now().time_since_epoch();
|
|
const intmax_t periodNum = std::chrono::high_resolution_clock::period::num;
|
|
const intmax_t periodDen = std::chrono::high_resolution_clock::period::den;
|
|
|
|
if (syncTime.count() != 0)
|
|
{
|
|
if (m_syncTimeBase.count() == 0)
|
|
m_syncTimeBase = syncTime;
|
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock>::duration timestamp;
|
|
timestamp = syncTime - m_syncTimeBase;
|
|
|
|
bool compacted = false;
|
|
if (m_presentHistory.Size() > 0)
|
|
{
|
|
CompactedPresentHistoryItem &lastItem = m_presentHistory[m_presentHistory.Size() - 1];
|
|
std::chrono::time_point<std::chrono::high_resolution_clock>::duration timeDelta = timestamp - lastItem.m_timestamp;
|
|
|
|
if (timeDelta.count() < 0)
|
|
timeDelta = std::chrono::time_point<std::chrono::high_resolution_clock>::duration::zero(); // This should never happen
|
|
|
|
if (timeDelta.count() * static_cast<intmax_t>(m_properties.m_frameTimeLockDenominator) * periodNum < periodDen * static_cast<intmax_t>(m_properties.m_frameTimeLockNumerator))
|
|
{
|
|
lastItem.m_numFrames++;
|
|
compacted = true;
|
|
}
|
|
}
|
|
|
|
if (!compacted)
|
|
{
|
|
if (m_presentHistory.Size() == m_presentHistory.CAPACITY)
|
|
m_presentHistory.RemoveFromStart();
|
|
|
|
CompactedPresentHistoryItem *newItem = m_presentHistory.Append();
|
|
newItem->m_timestamp = timestamp;
|
|
newItem->m_numFrames = 1;
|
|
}
|
|
}
|
|
|
|
if (m_presentHistory.Size() >= 2)
|
|
{
|
|
const size_t presentHistorySizeMinusOne = m_presentHistory.Size() - 1;
|
|
unsigned int numFrames = 0;
|
|
for (size_t i = 0; i < presentHistorySizeMinusOne; i++)
|
|
numFrames += m_presentHistory[i].m_numFrames;
|
|
|
|
std::chrono::high_resolution_clock::duration timeFrame = m_presentHistory[presentHistorySizeMinusOne].m_timestamp - m_presentHistory[0].m_timestamp;
|
|
|
|
unsigned int cancelledFrames = 0;
|
|
std::chrono::high_resolution_clock::duration cancelledTime = std::chrono::high_resolution_clock::duration::zero();
|
|
|
|
const int overshootTolerance = 2;
|
|
|
|
for (size_t i = 0; i < presentHistorySizeMinusOne; i++)
|
|
{
|
|
std::chrono::high_resolution_clock::duration blockTimeframe = m_presentHistory[i + 1].m_timestamp - m_presentHistory[i].m_timestamp;
|
|
unsigned int blockNumFrames = m_presentHistory[i].m_numFrames;
|
|
|
|
if (blockTimeframe.count() * static_cast<intmax_t>(numFrames) >= timeFrame.count() * static_cast<intmax_t>(blockNumFrames) * overshootTolerance)
|
|
{
|
|
cancelledTime += blockTimeframe;
|
|
cancelledFrames += blockNumFrames;
|
|
}
|
|
}
|
|
|
|
numFrames -= cancelledFrames;
|
|
timeFrame -= cancelledTime;
|
|
|
|
// timeFrame / numFrames = Frame timestep
|
|
// Unless Frame timestep is within the frame lock range, a.k.a.
|
|
// timeFrame / numFrames / qpFreq >= minFrameTimeNum / minFrameTimeDenom
|
|
|
|
bool isInFrameTimeLock = false;
|
|
if (timeFrame.count() * static_cast<intmax_t>(m_properties.m_frameTimeLockMinDenominator) * periodNum >= static_cast<intmax_t>(numFrames) * static_cast<intmax_t>(m_properties.m_frameTimeLockMinNumerator) * periodDen
|
|
&& timeFrame.count() * static_cast<intmax_t>(m_properties.m_frameTimeLockMaxDenominator) * periodNum <= static_cast<intmax_t>(numFrames) * static_cast<intmax_t>(m_properties.m_frameTimeLockMaxNumerator) * periodDen)
|
|
{
|
|
isInFrameTimeLock = true;
|
|
}
|
|
|
|
std::chrono::high_resolution_clock::duration frameTimeStep = m_frameTimeSliceSize;
|
|
if (!isInFrameTimeLock)
|
|
{
|
|
const int MAX_FRAMES_PER_STEP = 4;
|
|
|
|
frameTimeStep = std::chrono::high_resolution_clock::duration(timeFrame.count() / numFrames);
|
|
if (frameTimeStep > m_frameTimeSliceSize * MAX_FRAMES_PER_STEP)
|
|
frameTimeStep = m_frameTimeSliceSize * MAX_FRAMES_PER_STEP;
|
|
}
|
|
|
|
m_frameTimeAccumulated += frameTimeStep;
|
|
while (m_frameTimeAccumulated >= m_frameTimeSliceSize)
|
|
{
|
|
GpDisplayDriverTickStatus_t tickStatus = m_properties.m_tickFunc(m_properties.m_tickFuncContext, m_vosFiber);
|
|
m_frameTimeAccumulated -= m_frameTimeSliceSize;
|
|
|
|
if (tickStatus == GpDisplayDriverTickStatuses::kSynchronizing)
|
|
{
|
|
m_frameTimeAccumulated = std::chrono::high_resolution_clock::duration::zero();
|
|
break;
|
|
}
|
|
else if (tickStatus != GpDisplayDriverTickStatuses::kOK)
|
|
return tickStatus;
|
|
}
|
|
}
|
|
|
|
return GpDisplayDriverTickStatuses::kOK;
|
|
}
|
|
|
|
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties)
|
|
{
|
|
GpDisplayDriver_SDL_GL2 *driver = static_cast<GpDisplayDriver_SDL_GL2*>(malloc(sizeof(GpDisplayDriver_SDL_GL2)));
|
|
if (!driver)
|
|
return nullptr;
|
|
|
|
new (driver) GpDisplayDriver_SDL_GL2(properties);
|
|
|
|
if (!driver->Init())
|
|
{
|
|
driver->Shutdown();
|
|
return nullptr;
|
|
}
|
|
|
|
return driver;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
T *GpGLObjectImpl<T>::Create(GpDisplayDriver_SDL_GL2 *driver)
|
|
{
|
|
T *obj = static_cast<T*>(malloc(sizeof(T)));
|
|
if (!obj)
|
|
return nullptr;
|
|
|
|
new (obj) T();
|
|
|
|
obj->InitDriver(driver, driver->GetGLFunctions());
|
|
|
|
if (!obj->Init())
|
|
{
|
|
obj->Destroy();
|
|
return nullptr;
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
#pragma pop_macro("LoadCursor")
|