mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
2004 lines
56 KiB
C++
2004 lines
56 KiB
C++
#include "IGpDisplayDriver.h"
|
|
|
|
#include "GpApplicationName.h"
|
|
#include "GpComPtr.h"
|
|
#include "GpFiber_SDL.h"
|
|
#include "GpDisplayDriverProperties.h"
|
|
#include "GpSystemServices_Win32.h"
|
|
#include "GpWindows.h" // TODO: Remove
|
|
#include "GpVOSEvent.h"
|
|
#include "GpRingBuffer.h"
|
|
#include "IGpCursor.h"
|
|
#include "IGpDisplayDriverSurface.h"
|
|
#include "IGpLogDriver.h"
|
|
#include "IGpPrefsHandler.h"
|
|
#include "IGpVOSEventQueue.h"
|
|
|
|
#include "SDL_opengl.h"
|
|
#include "SDL_video.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <new>
|
|
#include <assert.h>
|
|
|
|
#include <chrono>
|
|
#include <numeric>
|
|
#include <vector>
|
|
|
|
|
|
#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 GpBinarizedShaders
|
|
{
|
|
extern const char *g_drawQuadV_GL2;
|
|
|
|
extern const char *g_drawQuadPaletteP_GL2;
|
|
extern const char *g_drawQuadRGBP_GL2;
|
|
extern const char *g_drawQuad15BitP_GL2;
|
|
|
|
extern const char *g_drawQuadPaletteICCP_GL2;
|
|
extern const char *g_drawQuadRGBICCP_GL2;
|
|
extern const char *g_drawQuad15BitICCP_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)();
|
|
|
|
PFNGLCLEARPROC Clear;
|
|
PFNGLCLEARCOLORPROC ClearColor;
|
|
|
|
PFNGLVIEWPORTPROC Viewport;
|
|
|
|
PFNGLGENFRAMEBUFFERSPROC GenFramebuffers;
|
|
PFNGLBINDFRAMEBUFFERPROC BindFramebuffer;
|
|
PFNGLFRAMEBUFFERTEXTURE2DPROC FramebufferTexture2D;
|
|
PFNGLCHECKFRAMEBUFFERSTATUSPROC CheckFramebufferStatus;
|
|
PFNGLDELETEFRAMEBUFFERSPROC DeleteFramebuffers;
|
|
|
|
PFNGLGENBUFFERSPROC GenBuffers;
|
|
PFNGLBUFFERDATAPROC BufferData;
|
|
PFNGLMAPBUFFERPROC MapBuffer;
|
|
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();
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
~GpDisplayDriverSurface_GL2();
|
|
|
|
static GpDisplayDriverSurface_GL2 *Create(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat);
|
|
|
|
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();
|
|
|
|
size_t GetImageWidth() const;
|
|
size_t GetPaddedTextureWidth() const;
|
|
size_t GetHeight() const;
|
|
GpPixelFormat_t GetPixelFormat() const;
|
|
GpGLTexture *GetTexture() const;
|
|
|
|
private:
|
|
bool Init();
|
|
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;
|
|
};
|
|
|
|
class GpCursor_SDL2 final : public IGpCursor
|
|
{
|
|
public:
|
|
void Destroy() override { delete this; }
|
|
};
|
|
|
|
class GpDisplayDriver_SDL_GL2 final : public IGpDisplayDriver, public IGpPrefsHandler
|
|
{
|
|
public:
|
|
explicit GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
|
|
~GpDisplayDriver_SDL_GL2();
|
|
|
|
bool Init();
|
|
|
|
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) override;
|
|
void DrawSurface(IGpDisplayDriverSurface *surface, int32_t x, int32_t y, size_t width, size_t height, const GpDisplayDriverSurfaceEffects *effects) override;
|
|
IGpCursor *LoadCursor(bool isColor, int cursorID) 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;
|
|
|
|
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;
|
|
|
|
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 virtualWidth, uint32_t virtualHeight);
|
|
|
|
void BecomeFullScreen();
|
|
void BecomeWindowed();
|
|
|
|
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;
|
|
|
|
GpComPtr<GpGLRenderTargetView> m_virtualScreenTextureRTV;
|
|
GpComPtr<GpGLTexture> m_virtualScreenTexture;
|
|
|
|
GpComPtr<GpGLVertexArray> m_quadVertexArray;
|
|
GpComPtr<GpGLBuffer> m_quadVertexBufferKeepalive;
|
|
GpComPtr<GpGLBuffer> m_quadIndexBuffer;
|
|
|
|
GpComPtr<GpGLTexture> m_paletteTexture;
|
|
|
|
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 ScaleQuadProgram
|
|
{
|
|
GpComPtr<GpGLProgram> m_program;
|
|
GLint m_vertexNDCOriginAndDimensionsLocation;
|
|
GLint m_vertexSurfaceDimensionsLocation;
|
|
GLint m_pixelDXDYDimensionsLocation;
|
|
GLint m_vertexPosUVLocation;
|
|
GLint m_pixelSurfaceTextureLocation;
|
|
GLint m_pixelPaletteTextureLocation;
|
|
|
|
bool Link(GpDisplayDriver_SDL_GL2 *driver, const GpGLShader<GL_VERTEX_SHADER> *vertexShader, const GpGLShader<GL_FRAGMENT_SHADER> *pixelShader);
|
|
};
|
|
|
|
ScaleQuadProgram m_scaleQuadProgram;
|
|
|
|
DrawQuadProgram m_drawQuadPaletteProgram;
|
|
DrawQuadProgram m_drawQuadRGBProgram;
|
|
DrawQuadProgram m_drawQuad15BitProgram;
|
|
DrawQuadProgram m_drawQuadPaletteICCProgram;
|
|
DrawQuadProgram m_drawQuadRGBICCProgram;
|
|
DrawQuadProgram m_drawQuad15BitICCProgram;
|
|
|
|
SDL_Window *m_window;
|
|
SDL_GLContext m_glContext;
|
|
|
|
UINT m_expectedSyncDelta;
|
|
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;
|
|
|
|
IGpCursor *m_activeCursor;
|
|
IGpCursor *m_pendingCursor;
|
|
EGpStandardCursor_t m_currentStandardCursor;
|
|
EGpStandardCursor_t m_pendingStandardCursor;
|
|
bool m_mouseIsInClientArea;
|
|
|
|
IGpFiber *m_vosFiber;
|
|
PortabilityLayer::HostThreadEvent *m_vosEvent;
|
|
GpWindowsGlobals *m_osGlobals;
|
|
|
|
HCURSOR m_arrowCursor;
|
|
HCURSOR m_waitCursor;
|
|
HCURSOR m_ibeamCursor;
|
|
|
|
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::GpDisplayDriverSurface_GL2(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpGLTexture *texture, GpPixelFormat_t pixelFormat)
|
|
: m_gl(driver->GetGLFunctions())
|
|
, m_texture(texture)
|
|
, m_pixelFormat(pixelFormat)
|
|
, m_imageWidth(width)
|
|
, m_paddedTextureWidth(0)
|
|
, m_height(height)
|
|
, m_pitch(pitch)
|
|
{
|
|
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;
|
|
}
|
|
|
|
m_paddedTextureWidth = width + paddingPixels;
|
|
}
|
|
|
|
GpDisplayDriverSurface_GL2::~GpDisplayDriverSurface_GL2()
|
|
{
|
|
}
|
|
|
|
GpDisplayDriverSurface_GL2 *GpDisplayDriverSurface_GL2::Create(GpDisplayDriver_SDL_GL2 *driver, size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat)
|
|
{
|
|
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);
|
|
if (!surface->Init())
|
|
{
|
|
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);
|
|
|
|
GLenum preError = m_gl->GetError();
|
|
GLenum preError2 = m_gl->GetError();
|
|
|
|
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);
|
|
|
|
GLenum postError = m_gl->GetError();
|
|
|
|
int n = 0;
|
|
}
|
|
|
|
void GpDisplayDriverSurface_GL2::Destroy()
|
|
{
|
|
this->~GpDisplayDriverSurface_GL2();
|
|
free(this);
|
|
}
|
|
|
|
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()
|
|
{
|
|
m_gl->BindTexture(GL_TEXTURE_2D, m_texture->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, ResolveGLInternalFormat(), m_paddedTextureWidth, m_height, 0, ResolveGLFormat(), ResolveGLType(), nullptr);
|
|
m_gl->BindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
GLenum GpDisplayDriverSurface_GL2::ResolveGLFormat() const
|
|
{
|
|
switch (m_pixelFormat)
|
|
{
|
|
case GpPixelFormats::k8BitCustom:
|
|
case GpPixelFormats::k8BitStandard:
|
|
case GpPixelFormats::kBW1:
|
|
return GL_RED;
|
|
case GpPixelFormats::kRGB24:
|
|
case GpPixelFormats::kRGB555:
|
|
return GL_RGB;
|
|
case GpPixelFormats::kRGB32:
|
|
return GL_RGBA;
|
|
default:
|
|
return GL_RGBA;
|
|
}
|
|
}
|
|
|
|
GLenum GpDisplayDriverSurface_GL2::ResolveGLInternalFormat() const
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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_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_osGlobals(static_cast<GpWindowsGlobals*>(properties.m_osGlobals))
|
|
, m_properties(properties)
|
|
, m_syncTimeBase(std::chrono::time_point<std::chrono::high_resolution_clock>::duration::zero())
|
|
{
|
|
m_bgColor[0] = 0.f;
|
|
m_bgColor[1] = 0.f;
|
|
m_bgColor[2] = 0.f;
|
|
m_bgColor[3] = 1.f;
|
|
|
|
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);
|
|
}
|
|
|
|
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 = static_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(Clear);
|
|
LOOKUP_FUNC(ClearColor);
|
|
|
|
LOOKUP_FUNC(Viewport);
|
|
|
|
LOOKUP_FUNC(GenFramebuffers);
|
|
LOOKUP_FUNC(BindFramebuffer);
|
|
LOOKUP_FUNC(FramebufferTexture2D);
|
|
LOOKUP_FUNC(CheckFramebufferStatus);
|
|
LOOKUP_FUNC(DeleteFramebuffers);
|
|
|
|
LOOKUP_FUNC(GenBuffers);
|
|
LOOKUP_FUNC(BufferData);
|
|
LOOKUP_FUNC(MapBuffer);
|
|
LOOKUP_FUNC(BindBuffer);
|
|
LOOKUP_FUNC(DeleteBuffers);
|
|
|
|
LOOKUP_FUNC(CreateProgram);
|
|
LOOKUP_FUNC(DeleteProgram);
|
|
LOOKUP_FUNC(LinkProgram);
|
|
LOOKUP_FUNC(UseProgram);
|
|
LOOKUP_FUNC(GetProgramiv);
|
|
LOOKUP_FUNC(GetProgramInfoLog);
|
|
|
|
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;
|
|
}
|
|
|
|
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 = GpSystemServices_Win32::GetInstance()->CreateThreadEvent(true, false);
|
|
m_vosFiber = new GpFiber_SDL(nullptr, m_vosEvent);
|
|
|
|
m_window = SDL_CreateWindow(GP_APPLICATION_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_windowWidthPhysical, m_windowHeightPhysical, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
|
|
|
StartOpenGLForWindow(logger);
|
|
|
|
if (!m_gl.LookUpFunctions())
|
|
return;
|
|
|
|
InitResources(m_windowWidthVirtual, m_windowHeightVirtual);
|
|
|
|
LARGE_INTEGER lastTimestamp;
|
|
memset(&lastTimestamp, 0, sizeof(lastTimestamp));
|
|
|
|
MSG msg;
|
|
for (;;)
|
|
{
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
DispatchMessage(&msg);
|
|
|
|
{
|
|
if (msg.message == WM_MOUSEMOVE)
|
|
{
|
|
if (!m_mouseIsInClientArea)
|
|
{
|
|
m_mouseIsInClientArea = true;
|
|
|
|
TRACKMOUSEEVENT tme;
|
|
ZeroMemory(&tme, sizeof(tme));
|
|
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = m_osGlobals->m_hwnd;
|
|
tme.dwHoverTime = HOVER_DEFAULT;
|
|
TrackMouseEvent(&tme);
|
|
}
|
|
}
|
|
else if (msg.message == WM_MOUSELEAVE)
|
|
m_mouseIsInClientArea = false;
|
|
|
|
m_osGlobals->m_translateWindowsMessageFunc(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_isFullScreen != m_isFullScreenDesired)
|
|
{
|
|
if (m_isFullScreenDesired)
|
|
BecomeFullScreen();
|
|
else
|
|
BecomeWindowed();
|
|
}
|
|
|
|
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)
|
|
{
|
|
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);
|
|
resizedOK = resizedOK && InitBackBuffer(virtualWidth, virtualHeight);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
GpDisplayDriverTickStatus_t tickStatus = PresentFrameAndSync();
|
|
if (tickStatus == GpDisplayDriverTickStatuses::kFatalFault || tickStatus == GpDisplayDriverTickStatuses::kApplicationTerminated)
|
|
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)
|
|
{
|
|
return GpDisplayDriverSurface_GL2::Create(this, width, height, pitch, pixelFormat);
|
|
}
|
|
|
|
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_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)
|
|
program = m_useICCProfile ? &m_drawQuadPaletteICCProgram : &m_drawQuadPaletteProgram;
|
|
else if (pixelFormat == GpPixelFormats::kRGB555)
|
|
{
|
|
return;
|
|
}
|
|
else if (pixelFormat == GpPixelFormats::kRGB32)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_gl.UseProgram(program->m_program->GetID());
|
|
|
|
{
|
|
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);
|
|
}
|
|
|
|
GLint vpos[1] = { program->m_vertexPosUVLocation };
|
|
|
|
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_paletteTexture->GetID());
|
|
m_gl.Uniform1i(program->m_pixelPaletteTextureLocation, 1);
|
|
}
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_quadIndexBuffer->GetID());
|
|
m_gl.DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
|
|
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_quadVertexArray->Deactivate(vpos);
|
|
|
|
m_gl.UseProgram(0);
|
|
}
|
|
|
|
IGpCursor *GpDisplayDriver_SDL_GL2::LoadCursor(bool isColor, int cursorID)
|
|
{
|
|
return new GpCursor_SDL2();
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetCursor(IGpCursor *cursor)
|
|
{
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::SetStandardCursor(EGpStandardCursor_t standardCursor)
|
|
{
|
|
}
|
|
|
|
void GpDisplayDriver_SDL_GL2::UpdatePalette(const void *paletteData)
|
|
{
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_paletteTexture->GetID());
|
|
m_gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 virtualWidth, uint32_t virtualHeight)
|
|
{
|
|
IGpLogDriver *logger = m_properties.m_logger;
|
|
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Information, "GpDisplayDriver_SDL_GL2::InitResources");
|
|
|
|
if (!InitBackBuffer(virtualWidth, virtualHeight))
|
|
return false;
|
|
|
|
// Quad index buffer
|
|
{
|
|
const uint16_t indexBufferData[] = { 0, 1, 2, 1, 3, 2 };
|
|
|
|
m_quadIndexBuffer = GpGLBuffer::Create(this);
|
|
if (!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_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_quadVertexBufferKeepalive = GpGLBuffer::Create(this);
|
|
if (!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_quadVertexBufferKeepalive->GetID());
|
|
m_gl.BufferData(GL_ARRAY_BUFFER, sizeof(vertexBufferData), vertexBufferData, GL_STATIC_DRAW);
|
|
m_gl.BindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
m_quadVertexArray = GpGLVertexArray::Create(this);
|
|
if (!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_quadVertexBufferKeepalive,
|
|
0, // index
|
|
2, // size
|
|
GL_FLOAT, // type
|
|
GL_FALSE, // normalized
|
|
sizeof(float) * 2, // stride
|
|
0
|
|
};
|
|
|
|
if (!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>> drawQuadPalettePixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteP_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>> drawQuadPaletteICCPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCP_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);
|
|
|
|
if (!m_drawQuadPaletteProgram.Link(this, drawQuadVertexShader, drawQuadPalettePixelShader)
|
|
//|| !m_drawQuadRGBProgram.Link(this, drawQuadVertexShader, drawQuadRGBPixelShader)
|
|
//|| !m_drawQuad15BitProgram.Link(this, drawQuadVertexShader, drawQuad15BitPixelShader)
|
|
|| !m_drawQuadPaletteICCProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCPixelShader)
|
|
//|| !m_drawQuadRGBICCProgram.Link(this, drawQuadVertexShader, drawQuadRGBICCPixelShader)
|
|
//|| !m_drawQuad15BitICCProgram.Link(this, drawQuadVertexShader, drawQuad15BitICCPixelShader)
|
|
|| !m_scaleQuadProgram.Link(this, drawQuadVertexShader, scaleQuadPixelShader))
|
|
return false;
|
|
|
|
|
|
|
|
// Palette texture
|
|
{
|
|
uint8_t initialDataBytes[256][4];
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
for (int ch = 0; ch < 4; ch++)
|
|
initialDataBytes[i][ch] = 255;
|
|
}
|
|
|
|
m_paletteTexture = GpGLTexture::Create(this);
|
|
if (!m_paletteTexture)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitResources: GpGLTexture::Create failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_paletteTexture->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, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, initialDataBytes);
|
|
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;
|
|
}
|
|
|
|
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 < 640)
|
|
desiredWidth = 640;
|
|
else if (desiredWidth > MAXDWORD)
|
|
desiredWidth = MAXDWORD;
|
|
|
|
if (desiredHeight < 480)
|
|
desiredHeight = 480;
|
|
else if (desiredHeight > MAXDWORD)
|
|
desiredHeight = MAXDWORD;
|
|
|
|
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");
|
|
|
|
{
|
|
m_virtualScreenTexture = GpGLTexture::Create(this);
|
|
if (!m_virtualScreenTexture)
|
|
{
|
|
if (logger)
|
|
logger->Printf(IGpLogDriver::Category_Error, "GpDisplayDriver_SDL_GL2::InitBackBuffer: GpGLTexture::Create for virtual screen texture failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 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, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
{
|
|
m_virtualScreenTextureRTV = GpGLRenderTargetView::Create(this);
|
|
|
|
if (!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_virtualScreenTextureRTV->GetID());
|
|
m_gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 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");
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void GpDisplayDriver_SDL_GL2::ScaleVirtualScreen()
|
|
{
|
|
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
m_gl.Viewport(0, 0, m_windowWidthPhysical, m_windowHeightPhysical);
|
|
|
|
{
|
|
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] =
|
|
{
|
|
twoDivWidth - 1.0f,
|
|
twoDivHeight - 1.0f,
|
|
fWidth * twoDivWidth,
|
|
fHeight * twoDivHeight
|
|
};
|
|
|
|
float surfaceDimensions_TextureRegion[4] =
|
|
{
|
|
static_cast<float>(m_windowWidthVirtual),
|
|
static_cast<float>(m_windowHeightVirtual),
|
|
1.f,
|
|
1.f
|
|
};
|
|
|
|
m_gl.UseProgram(m_scaleQuadProgram.m_program->GetID());
|
|
m_gl.Uniform4fv(m_scaleQuadProgram.m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(ndcOriginsAndDimensions));
|
|
m_gl.Uniform4fv(m_scaleQuadProgram.m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(surfaceDimensions_TextureRegion));
|
|
|
|
float dxdy_dimensions[4] =
|
|
{
|
|
static_cast<float>(static_cast<double>(m_windowWidthVirtual) / static_cast<double>(m_windowWidthPhysical)),
|
|
static_cast<float>(static_cast<double>(m_windowHeightVirtual) / static_cast<double>(m_windowHeightPhysical)),
|
|
m_windowWidthVirtual,
|
|
m_windowHeightVirtual
|
|
};
|
|
|
|
m_gl.Uniform4fv(m_scaleQuadProgram.m_pixelDXDYDimensionsLocation, 1, reinterpret_cast<const GLfloat*>(dxdy_dimensions));
|
|
}
|
|
|
|
GLint attribLocations[] = { m_scaleQuadProgram.m_vertexPosUVLocation };
|
|
|
|
m_quadVertexArray->Activate(attribLocations);
|
|
|
|
m_gl.ActiveTexture(GL_TEXTURE0 + 0);
|
|
m_gl.BindTexture(GL_TEXTURE_2D, m_virtualScreenTexture->GetID());
|
|
m_gl.Uniform1i(m_scaleQuadProgram.m_pixelSurfaceTextureLocation, 0);
|
|
|
|
m_gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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_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::ScaleQuadProgram::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_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();
|
|
|
|
GLenum errorCode = m_gl.GetError();
|
|
|
|
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::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")
|