From 45aa5b4cbab8b52706ee71ab42ff7d3ac1f90b62 Mon Sep 17 00:00:00 2001 From: elasota Date: Sat, 26 Sep 2020 16:45:52 -0400 Subject: [PATCH] OpenGL display driver --- .gitignore | 1 + {GpCommon => Aerofoil}/GpCursor_Win32.h | 0 Aerofoil/GpFiberStarter_Win32.cpp | 2 +- Aerofoil/GpFiber_Win32.cpp | 34 +- Aerofoil/GpFiber_Win32.h | 9 +- AerofoilSDL/AerofoilSDL.vcxproj | 46 + AerofoilSDL/AerofoilSDL.vcxproj.filters | 53 + AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp | 2003 +++++++++++++++++ AerofoilSDL/GpFiberStarter_SDL.cpp | 51 + AerofoilSDL/GpFiber_SDL.cpp | 27 + AerofoilSDL/GpFiber_SDL.h | 27 + AerofoilSDL/GpMain_SDL_Win32.cpp | 476 +++- AerofoilSDL/ShaderCode/DrawQuadPaletteP.cpp | 28 + .../ShaderCode/DrawQuadPixelConstants.h | 5 + AerofoilSDL/ShaderCode/DrawQuadV.cpp | 17 + AerofoilSDL/ShaderCode/Functions.h | 100 + AerofoilSDL/ShaderCode/ScaleQuadP.cpp | 50 + GpCommon/EGpDisplayDriverType.h | 3 +- GpCommon/IGpDisplayDriver.h | 4 +- GpCommon/IGpFiber.h | 2 +- .../GpDisplayDriverD3D11.cpp | 2 +- GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h | 2 +- GpMainApp.props | 4 +- GpShell/GpAppEnvironment.cpp | 6 +- PortabilityLayer/QDGraf.cpp | 2 +- Release.props | 1 + 26 files changed, 2922 insertions(+), 33 deletions(-) rename {GpCommon => Aerofoil}/GpCursor_Win32.h (100%) create mode 100644 AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp create mode 100644 AerofoilSDL/GpFiberStarter_SDL.cpp create mode 100644 AerofoilSDL/GpFiber_SDL.cpp create mode 100644 AerofoilSDL/GpFiber_SDL.h create mode 100644 AerofoilSDL/ShaderCode/DrawQuadPaletteP.cpp create mode 100644 AerofoilSDL/ShaderCode/DrawQuadPixelConstants.h create mode 100644 AerofoilSDL/ShaderCode/DrawQuadV.cpp create mode 100644 AerofoilSDL/ShaderCode/Functions.h create mode 100644 AerofoilSDL/ShaderCode/ScaleQuadP.cpp diff --git a/.gitignore b/.gitignore index 850834c4..7a6f99e2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ InstallerPackages/* *.wixpdb *.wixobj *.CopyComplete +*.lnk ReleasePackageInstaller/obj/* ReleasePackageInstaller/bin/* ReleasePackageInstaller/AerofoilPackageDefs.wxi diff --git a/GpCommon/GpCursor_Win32.h b/Aerofoil/GpCursor_Win32.h similarity index 100% rename from GpCommon/GpCursor_Win32.h rename to Aerofoil/GpCursor_Win32.h diff --git a/Aerofoil/GpFiberStarter_Win32.cpp b/Aerofoil/GpFiberStarter_Win32.cpp index ef679698..37e9ce41 100644 --- a/Aerofoil/GpFiberStarter_Win32.cpp +++ b/Aerofoil/GpFiberStarter_Win32.cpp @@ -20,7 +20,7 @@ namespace GpFiberStarter_Win32 GpFiberStarter::ThreadFunc_t threadFunc = tss->m_threadFunc; IGpFiber *creatingFiber = tss->m_creatingFiber; void *context = tss->m_context; - creatingFiber->YieldTo(); + SwitchToFiber(static_cast(creatingFiber)->GetFiber()); threadFunc(context); diff --git a/Aerofoil/GpFiber_Win32.cpp b/Aerofoil/GpFiber_Win32.cpp index 8c5e6c8e..bd675f83 100644 --- a/Aerofoil/GpFiber_Win32.cpp +++ b/Aerofoil/GpFiber_Win32.cpp @@ -1,32 +1,32 @@ #include "GpFiber_Win32.h" -#include - -GpFiber_Win32::GpFiber_Win32(LPVOID fiber) - : m_fiber(fiber) -{ -} - -void GpFiber_Win32::YieldTo() -{ - SwitchToFiber(m_fiber); -} - -void GpFiber_Win32::Destroy() +#include + +GpFiber_Win32::GpFiber_Win32(LPVOID fiber) + : m_fiber(fiber) +{ +} + +void GpFiber_Win32::YieldTo(IGpFiber *toFiber) +{ + SwitchToFiber(static_cast(toFiber)->m_fiber); +} + +void GpFiber_Win32::Destroy() { this->~GpFiber_Win32(); free(this); } GpFiber_Win32::~GpFiber_Win32() -{ - DeleteFiber(m_fiber); +{ + DeleteFiber(m_fiber); } - + IGpFiber *GpFiber_Win32::Create(LPVOID fiber) { void *storage = malloc(sizeof(GpFiber_Win32)); if (!storage) return nullptr; - return new (storage) GpFiber_Win32(fiber); + return new (storage) GpFiber_Win32(fiber); } diff --git a/Aerofoil/GpFiber_Win32.h b/Aerofoil/GpFiber_Win32.h index 936f4ada..2c1b30c8 100644 --- a/Aerofoil/GpFiber_Win32.h +++ b/Aerofoil/GpFiber_Win32.h @@ -5,14 +5,21 @@ class GpFiber_Win32 final : public IGpFiber { public: - void YieldTo() override; + void YieldTo(IGpFiber *toFiber) override; void Destroy() override; static IGpFiber *Create(LPVOID fiber); + LPVOID GetFiber() const; + private: explicit GpFiber_Win32(LPVOID fiber); ~GpFiber_Win32(); LPVOID m_fiber; }; + +inline LPVOID GpFiber_Win32::GetFiber() const +{ + return m_fiber; +} diff --git a/AerofoilSDL/AerofoilSDL.vcxproj b/AerofoilSDL/AerofoilSDL.vcxproj index cf5e3559..23ce25a1 100644 --- a/AerofoilSDL/AerofoilSDL.vcxproj +++ b/AerofoilSDL/AerofoilSDL.vcxproj @@ -40,6 +40,10 @@ + + + + @@ -47,6 +51,9 @@ + + + @@ -62,6 +69,7 @@ true true + shlwapi.lib;%(AdditionalDependencies) @@ -71,9 +79,47 @@ true true + + shlwapi.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + {6233c3f2-5781-488e-b190-4fa8836f5a77} + + + {e3bdc783-8646-433e-adf0-8b6390d36669} + + + {4b564030-8985-4975-91e1-e1b2c16ae2a1} + + + {17b96f07-ef92-47cd-95a5-8e6ee38ab564} + + + {10cf9b5f-61d0-4b5b-89f4-810b58fc053d} + + + + + + diff --git a/AerofoilSDL/AerofoilSDL.vcxproj.filters b/AerofoilSDL/AerofoilSDL.vcxproj.filters index 794b618f..ebadabc0 100644 --- a/AerofoilSDL/AerofoilSDL.vcxproj.filters +++ b/AerofoilSDL/AerofoilSDL.vcxproj.filters @@ -13,10 +13,63 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {85279826-1cd2-4894-a780-3f74af9c1260} + Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\ShaderCode + + + Source Files\ShaderCode + + + Source Files\ShaderCode + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp b/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp new file mode 100644 index 00000000..3106b3a4 --- /dev/null +++ b/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp @@ -0,0 +1,2003 @@ +#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 +#include +#include + +#include +#include +#include + + +#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 GpGLObjectImpl : public GpGLObject +{ +public: + static T *Create(GpDisplayDriver_SDL_GL2 *driver); + +protected: + virtual bool Init() = 0; + void Destroy() final override; +}; + +template +void GpGLObjectImpl::Destroy() +{ + T *self = static_cast(this); + self->~T(); + free(self); +} + +class GpGLRenderTargetView final : public GpGLObjectImpl +{ +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 +{ +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 +{ +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 +{ +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(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 +{ +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(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(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 +{ +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 +class GpGLShader final : public GpGLObjectImpl > +{ +public: + GpGLShader(); + ~GpGLShader(); + + bool Init() override; + GLuint GetID() const; + +private: + GLuint m_id; +}; + +template +GpGLShader::GpGLShader() + : m_id(0) +{ +} + +template +GpGLShader::~GpGLShader() +{ + if (m_id != 0) + this->m_gl->DeleteShader(m_id); +} + +template +bool GpGLShader::Init() +{ + m_id = this->m_gl->CreateShader(TShaderType); + return m_id != 0; +} + +template +GLuint GpGLShader::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 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 GpComPtr > 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::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 m_virtualScreenTextureRTV; + GpComPtr m_virtualScreenTexture; + + GpComPtr m_quadVertexArray; + GpComPtr m_quadVertexBufferKeepalive; + GpComPtr m_quadIndexBuffer; + + GpComPtr m_paletteTexture; + + struct DrawQuadProgram + { + GpComPtr 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 *vertexShader, const GpGLShader *pixelShader); + }; + + struct ScaleQuadProgram + { + GpComPtr 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 *vertexShader, const GpGLShader *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::duration m_syncTimeBase; + GpRingBuffer 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 texture = GpComPtr(GpGLTexture::Create(driver)); + if (!texture) + return nullptr; + + GpDisplayDriverSurface_GL2 *surface = static_cast(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(properties.m_osGlobals)) + , m_properties(properties) + , m_syncTimeBase(std::chrono::time_point::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(properties.m_frameTimeLockNumerator) / static_cast(properties.m_frameTimeLockDenominator) / periodNum); +} + +template +static bool LookupOpenGLFunction(T &target, const char *name) +{ + target = static_cast(nullptr); + void *proc = SDL_GL_GetProcAddress(name); + if (proc) + { + target = static_cast(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(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(m_windowWidthVirtual); + const float negativeTwoDivHeight = -2.0f / static_cast(m_windowHeightVirtual); + + GLfloat ndcOriginAndDimensions[4] = + { + + static_cast(x) * twoDivWidth - 1.0f, + static_cast(y) * negativeTwoDivHeight + 1.0f, + static_cast(width) * twoDivWidth, + static_cast(height) * negativeTwoDivHeight, + }; + + GLfloat surfaceDimensions_TextureRegion[4] = + { + static_cast(glSurface->GetImageWidth()), + static_cast(glSurface->GetHeight()), + static_cast(static_cast(glSurface->GetImageWidth()) / static_cast(glSurface->GetPaddedTextureWidth())), + 1.f + }; + + m_gl.Uniform4fv(program->m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast(ndcOriginAndDimensions)); + m_gl.Uniform4fv(program->m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast(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(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(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 +GpComPtr> GpDisplayDriver_SDL_GL2::CreateShader(const char *shaderSrc) +{ + size_t shaderCodeLength = strlen(shaderSrc); + GpComPtr> shader(GpGLShader::Create(this)); + if (shader == nullptr) + return shader; + + const GLchar *shaderSrcGL = reinterpret_cast(shaderSrc); + GLint shaderLenGL = static_cast(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 log; + log.resize(infoLength + 1); + log[infoLength] = '\0'; + + m_gl.GetShaderInfoLog(shader->GetID(), static_cast(log.size() - 1), nullptr, reinterpret_cast(&log[0])); + + const char *errorMsg = &log[0]; + + return GpComPtr>(); + } + + 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> drawQuadVertexShader = CreateShader(GpBinarizedShaders::g_drawQuadV_GL2); + GpComPtr> drawQuadPalettePixelShader = CreateShader(GpBinarizedShaders::g_drawQuadPaletteP_GL2); + //m_drawQuadRGBPixelShader = CreateShader(GpBinarizedShaders::g_drawQuadRGBP_GL2); + //m_drawQuad15BitPixelShader = CreateShader(GpBinarizedShaders::g_drawQuad15BitP_GL2); + GpComPtr> drawQuadPaletteICCPixelShader = CreateShader(GpBinarizedShaders::g_drawQuadPaletteICCP_GL2); + //m_drawQuadRGBICCPixelShader = CreateShader(GpBinarizedShaders::g_drawQuadRGBICCP_GL2); + //m_drawQuad15BitICCPixelShader = CreateShader(GpBinarizedShaders::g_drawQuad15BitICCP_GL2); + GpComPtr> scaleQuadPixelShader = CreateShader(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(desiredWidth), static_cast(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(desiredWidth), static_cast(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(m_windowWidthPhysical); + const float twoDivHeight = 2.0f / static_cast(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(m_windowWidthVirtual) * m_pixelScaleX; + float fHeight = static_cast(m_windowHeightVirtual) * m_pixelScaleY; + + float ndcOriginsAndDimensions[4] = + { + twoDivWidth - 1.0f, + twoDivHeight - 1.0f, + fWidth * twoDivWidth, + fHeight * twoDivHeight + }; + + float surfaceDimensions_TextureRegion[4] = + { + static_cast(m_windowWidthVirtual), + static_cast(m_windowHeightVirtual), + 1.f, + 1.f + }; + + m_gl.UseProgram(m_scaleQuadProgram.m_program->GetID()); + m_gl.Uniform4fv(m_scaleQuadProgram.m_vertexNDCOriginAndDimensionsLocation, 1, reinterpret_cast(ndcOriginsAndDimensions)); + m_gl.Uniform4fv(m_scaleQuadProgram.m_vertexSurfaceDimensionsLocation, 1, reinterpret_cast(surfaceDimensions_TextureRegion)); + + float dxdy_dimensions[4] = + { + static_cast(static_cast(m_windowWidthVirtual) / static_cast(m_windowWidthPhysical)), + static_cast(static_cast(m_windowHeightVirtual) / static_cast(m_windowHeightPhysical)), + m_windowWidthVirtual, + m_windowHeightVirtual + }; + + m_gl.Uniform4fv(m_scaleQuadProgram.m_pixelDXDYDimensionsLocation, 1, reinterpret_cast(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 *vertexShader, const GpGLShader *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 errorMsgBuffer; + errorMsgBuffer.resize(static_cast(logLength) + 1); + errorMsgBuffer[logLength] = '\0'; + + gl->GetProgramInfoLog(m_program->GetID(), static_cast(logLength), nullptr, reinterpret_cast(&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 *vertexShader, const GpGLShader *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 errorMsgBuffer; + errorMsgBuffer.resize(static_cast(logLength) + 1); + errorMsgBuffer[logLength] = '\0'; + + gl->GetProgramInfoLog(m_program->GetID(), static_cast(logLength), nullptr, reinterpret_cast(&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::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::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::duration timeDelta = timestamp - lastItem.m_timestamp; + + if (timeDelta.count() < 0) + timeDelta = std::chrono::time_point::duration::zero(); // This should never happen + + if (timeDelta.count() * static_cast(m_properties.m_frameTimeLockDenominator) * periodNum < periodDen * static_cast(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(numFrames) >= timeFrame.count() * static_cast(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(m_properties.m_frameTimeLockMinDenominator) * periodNum >= static_cast(numFrames) * static_cast(m_properties.m_frameTimeLockMinNumerator) * periodDen + && timeFrame.count() * static_cast(m_properties.m_frameTimeLockMaxDenominator) * periodNum <= static_cast(numFrames) * static_cast(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(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 +T *GpGLObjectImpl::Create(GpDisplayDriver_SDL_GL2 *driver) +{ + T *obj = static_cast(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") diff --git a/AerofoilSDL/GpFiberStarter_SDL.cpp b/AerofoilSDL/GpFiberStarter_SDL.cpp new file mode 100644 index 00000000..6b756eee --- /dev/null +++ b/AerofoilSDL/GpFiberStarter_SDL.cpp @@ -0,0 +1,51 @@ +#include "GpFiberStarter.h" +#include "GpFiber_SDL.h" +#include "GpSystemServices_Win32.h" + +#include "HostThreadEvent.h" + +#include "SDL_thread.h" + +#include + +namespace GpFiberStarter_SDL +{ + struct FiberStartState + { + GpFiberStarter::ThreadFunc_t m_threadFunc; + PortabilityLayer::HostThreadEvent *m_creatingReturnEvent; + void *m_context; + }; + + static int SDLCALL FiberStartRoutine(void *lpThreadParameter) + { + const FiberStartState *tss = static_cast(lpThreadParameter); + + GpFiberStarter::ThreadFunc_t threadFunc = tss->m_threadFunc; + PortabilityLayer::HostThreadEvent *creatingReturnEvent = tss->m_creatingReturnEvent; + void *context = tss->m_context; + creatingReturnEvent->Signal(); + + threadFunc(context); + + return 0; + } +} + +IGpFiber *GpFiberStarter::StartFiber(ThreadFunc_t threadFunc, void *context, IGpFiber *creatingFiber) +{ + PortabilityLayer::HostThreadEvent *returnEvent = GpSystemServices_Win32::GetInstance()->CreateThreadEvent(true, false); + + GpFiberStarter_SDL::FiberStartState startState; + startState.m_context = context; + startState.m_creatingReturnEvent = returnEvent; + startState.m_threadFunc = threadFunc; + + SDL_Thread *thread = SDL_CreateThread(GpFiberStarter_SDL::FiberStartRoutine, "Fiber", &startState); + if (!thread) + return nullptr; + + returnEvent->Wait(); + + return new GpFiber_SDL(thread, returnEvent); +} diff --git a/AerofoilSDL/GpFiber_SDL.cpp b/AerofoilSDL/GpFiber_SDL.cpp new file mode 100644 index 00000000..39f34f84 --- /dev/null +++ b/AerofoilSDL/GpFiber_SDL.cpp @@ -0,0 +1,27 @@ +#include "GpFiber_SDL.h" +#include "HostSystemServices.h" +#include "HostThreadEvent.h" + +#include "GpSystemServices_Win32.h" + +GpFiber_SDL::GpFiber_SDL(SDL_Thread *thread, PortabilityLayer::HostThreadEvent *threadEvent) + : m_event(threadEvent) + , m_thread(thread) +{ +} + +GpFiber_SDL::~GpFiber_SDL() +{ + m_event->Destroy(); +} + +void GpFiber_SDL::YieldTo(IGpFiber *toFiber) +{ + static_cast(toFiber)->m_event->Signal(); + m_event->Wait(); +} + +void GpFiber_SDL::Destroy() +{ + delete this; +} diff --git a/AerofoilSDL/GpFiber_SDL.h b/AerofoilSDL/GpFiber_SDL.h new file mode 100644 index 00000000..d019fa1a --- /dev/null +++ b/AerofoilSDL/GpFiber_SDL.h @@ -0,0 +1,27 @@ +#pragma once + +#include "IGpFiber.h" +#include "SDL_thread.h" + +namespace PortabilityLayer +{ + class HostSystemServices; + class HostThreadEvent; +} + +class GpFiber_SDL final : public IGpFiber +{ +public: + explicit GpFiber_SDL(SDL_Thread *thread, PortabilityLayer::HostThreadEvent *threadEvent); + ~GpFiber_SDL(); + + void YieldTo(IGpFiber *fromFiber) override; + void Destroy() override; + +private: + static int SDLCALL InternalThreadFunction(void *data); + + bool m_isDestroying; + PortabilityLayer::HostThreadEvent *m_event; + SDL_Thread *m_thread; +}; diff --git a/AerofoilSDL/GpMain_SDL_Win32.cpp b/AerofoilSDL/GpMain_SDL_Win32.cpp index cbc8e368..f4474781 100644 --- a/AerofoilSDL/GpMain_SDL_Win32.cpp +++ b/AerofoilSDL/GpMain_SDL_Win32.cpp @@ -1,6 +1,474 @@ -#include "SDL_main.h" +#include "SDL.h" -SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[]) -{ - return 0; +#include "GpMain.h" +#include "GpAudioDriverFactory.h" +#include "GpCursor_Win32.h" +#include "GpDisplayDriverFactory.h" +#include "GpGlobalConfig.h" +#include "GpFiber_Win32.h" +#include "GpFiber_SDL.h" +#include "GpFileSystem_Win32.h" +#include "GpLogDriver_Win32.h" +#include "GpFontHandlerFactory.h" +#include "GpInputDriverFactory.h" +#include "GpAppInterface.h" +#include "GpSystemServices_Win32.h" +#include "GpVOSEvent.h" +#include "IGpVOSEventQueue.h" + +#include "HostFileSystem.h" +#include "HostThreadEvent.h" + +#include "GpWindows.h" + +#include "resource.h" + +#include +#include +#include + + +GpWindowsGlobals g_gpWindowsGlobals; + +extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties); +extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties); +extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties); + +IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties); + +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(static_cast(x) / pixelScaleX); + + if (pixelScaleY != 1.0f) + mEvent.m_y = static_cast(static_cast(y) / pixelScaleX); + } +} + +static bool IdentifyVKey(const WPARAM &wparam, const LPARAM &lparam, GpKeyIDSubset_t &outSubset, GpKeyboardInputEvent::KeyUnion &outKey) +{ + switch (wparam) + { + case VK_ESCAPE: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kEscape; + break; + case VK_PRINT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kPrintScreen; + break; + case VK_SCROLL: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kScrollLock; + break; + case VK_PAUSE: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kPause; + break; + case VK_INSERT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kInsert; + break; + case VK_HOME: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kHome; + break; + case VK_PRIOR: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kPageUp; + break; + case VK_NEXT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kPageDown; + break; + case VK_DELETE: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kDelete; + break; + case VK_TAB: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kTab; + break; + case VK_END: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kEnd; + break; + case VK_BACK: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kBackspace; + break; + case VK_CAPITAL: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kCapsLock; + break; + case VK_RETURN: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kEnter; + break; + case VK_SHIFT: + { + UINT vkey = MapVirtualKeyW((lparam >> 16) & 0xff, MAPVK_VSC_TO_VK_EX); + + if (vkey == VK_LSHIFT || vkey == VK_SHIFT) + { + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kLeftShift; + } + else if (vkey == VK_RSHIFT) + { + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kRightShift; + } + else + return false; + } + break; + case VK_RSHIFT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kRightShift; + break; + case VK_CONTROL: + outSubset = GpKeyIDSubsets::kSpecial; + if (lparam & 0x01000000) + outKey.m_specialKey = GpKeySpecials::kRightCtrl; + else + outKey.m_specialKey = GpKeySpecials::kLeftCtrl; + break; + case VK_MENU: + outSubset = GpKeyIDSubsets::kSpecial; + if (lparam & 0x01000000) + outKey.m_specialKey = GpKeySpecials::kRightAlt; + else + outKey.m_specialKey = GpKeySpecials::kLeftAlt; + break; + case VK_NUMLOCK: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kNumLock; + break; + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + outSubset = GpKeyIDSubsets::kNumPadNumber; + outKey.m_numPadNumber = static_cast(wparam - VK_NUMPAD0); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = static_cast(wparam); + break; + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + outSubset = GpKeyIDSubsets::kFKey; + outKey.m_fKey = static_cast(wparam - VK_F1 + 1); + break; + case VK_OEM_COMMA: + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = ','; + break; + case VK_OEM_MINUS: + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = '-'; + break; + case VK_OEM_PERIOD: + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = '.'; + break; + case VK_OEM_PLUS: + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = '+'; + break; + case VK_UP: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kUpArrow; + break; + case VK_DOWN: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kDownArrow; + break; + case VK_LEFT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kLeftArrow; + break; + case VK_RIGHT: + outSubset = GpKeyIDSubsets::kSpecial; + outKey.m_specialKey = GpKeySpecials::kRightArrow; + break; + default: + { + if (wparam >= VK_OEM_1 && wparam <= VK_OEM_102) + { + UINT charCode = MapVirtualKeyW(static_cast(wparam), MAPVK_VK_TO_CHAR); + if (charCode == 0) + return false; + + if (charCode < 128) + { + outSubset = GpKeyIDSubsets::kASCII; + outKey.m_asciiChar = static_cast(charCode); + break; + } + else + { + outSubset = GpKeyIDSubsets::kUnicode; + outKey.m_unicodeChar = charCode;; + 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; + } +} + +static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY) +{ + WPARAM wParam = msg->wParam; + LPARAM lParam = msg->lParam; + + switch (msg->message) + { + case WM_LBUTTONDOWN: + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_LBUTTONUP: + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_MBUTTONDOWN: + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_MBUTTONUP: + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_RBUTTONDOWN: + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_RBUTTONUP: + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_XBUTTONDOWN: + if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_XBUTTONUP: + if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_MOUSEMOVE: + PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); + break; + case WM_MOUSELEAVE: + PostMouseEvent(eventQueue, GpMouseEventTypes::kLeave, GpMouseButtons::kNone, 0, 0, pixelScaleX, pixelScaleY); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + GpKeyIDSubset_t subset; + GpKeyboardInputEvent::KeyUnion key; + bool isRepeat = ((lParam & 0x40000000) != 0); + const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAuto : GpKeyboardInputEventTypes::kDown; + if (IdentifyVKey(wParam, lParam, subset, key)) + PostKeyboardEvent(eventQueue, keyEventType, subset, key, static_cast(lParam & 0xffff)); + + (void)TranslateMessage(msg); + } + break; + case WM_KEYUP: + case WM_SYSKEYUP: + { + GpKeyIDSubset_t subset; + GpKeyboardInputEvent::KeyUnion key; + if (IdentifyVKey(wParam, lParam, subset, key)) + PostKeyboardEvent(eventQueue, GpKeyboardInputEventTypes::kUp, subset, key, (lParam & 0xffff)); + } + break; + case WM_CHAR: + case WM_UNICHAR: + { + bool isRepeat = ((lParam & 0x4000000) != 0); + const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAutoChar : GpKeyboardInputEventTypes::kDownChar; + GpKeyboardInputEvent::KeyUnion key; + GpKeyIDSubset_t subset = GpKeyIDSubsets::kASCII; + if (wParam <= 128) + key.m_asciiChar = static_cast(wParam); + else + { + subset = GpKeyIDSubsets::kUnicode; + key.m_unicodeChar = static_cast(wParam); + } + PostKeyboardEvent(eventQueue, keyEventType, subset, key, (lParam & 0xffff)); + } + break; + case WM_QUIT: + { + if (GpVOSEvent *evt = eventQueue->QueueEvent()) + evt->m_eventType = GpVOSEventTypes::kQuit; + } + break; + default: + break; + } +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + return -1; + + LPWSTR cmdLine = GetCommandLineW(); + + int nArgs; + LPWSTR *cmdLineArgs = CommandLineToArgvW(cmdLine, &nArgs); + + for (int i = 1; i < nArgs; i++) + { + if (!wcscmp(cmdLineArgs[i], L"-diagnostics")) + GpLogDriver_Win32::Init(); + } + + IGpLogDriver *logger = GpLogDriver_Win32::GetInstance(); + + GpAppInterface_Get()->PL_HostFileSystem_SetInstance(GpFileSystem_Win32::GetInstance()); + GpAppInterface_Get()->PL_HostSystemServices_SetInstance(GpSystemServices_Win32::GetInstance()); + GpAppInterface_Get()->PL_HostLogDriver_SetInstance(GpLogDriver_Win32::GetInstance()); + + g_gpWindowsGlobals.m_hInstance = hInstance; + g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance; + g_gpWindowsGlobals.m_cmdLine = cmdLine; + g_gpWindowsGlobals.m_cmdLineArgc = nArgs; + g_gpWindowsGlobals.m_cmdLineArgv = cmdLineArgs; + g_gpWindowsGlobals.m_nCmdShow = nCmdShow; + g_gpWindowsGlobals.m_baseDir = GpFileSystem_Win32::GetInstance()->GetBasePath(); + g_gpWindowsGlobals.m_hwnd = nullptr; + //g_gpWindowsGlobals.m_hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ICON1)); + //g_gpWindowsGlobals.m_hIconSm = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ICON2)); + + g_gpWindowsGlobals.m_loadCursorFunc = GpCursor_Win32::Load; + g_gpWindowsGlobals.m_translateWindowsMessageFunc = TranslateWindowsMessage; + + g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2; + + g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_XAudio2; + + g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_FreeType2; + + EGpInputDriverType inputDrivers[] = + { + EGpInputDriverType_XInput + }; + + g_gpGlobalConfig.m_inputDriverTypes = inputDrivers; + g_gpGlobalConfig.m_numInputDrivers = sizeof(inputDrivers) / sizeof(inputDrivers[0]); + + g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals; + g_gpGlobalConfig.m_logger = logger; + + GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2); + GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2); + GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_XInput, GpDriver_CreateInputDriver_XInput); + GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2); + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "SDL environment configured, starting up"); + + int returnCode = GpMain::Run(); + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "SDL environment exited with code %i, cleaning up", returnCode); + + LocalFree(cmdLineArgs); + + return returnCode; } diff --git a/AerofoilSDL/ShaderCode/DrawQuadPaletteP.cpp b/AerofoilSDL/ShaderCode/DrawQuadPaletteP.cpp new file mode 100644 index 00000000..e28020c7 --- /dev/null +++ b/AerofoilSDL/ShaderCode/DrawQuadPaletteP.cpp @@ -0,0 +1,28 @@ +#include "Functions.h" +#include "DrawQuadPixelConstants.h" + +#define GP_GL_SHADER_CODE_DRAWQUADPALETTEP_GLSL "varying vec4 texCoord;\n"\ +"uniform sampler2D surfaceTexture;\n"\ +"uniform sampler2D paletteTexture;\n"\ +"\n"\ +"vec3 SamplePixel(vec2 tc)\n"\ +"{\n"\ +" float surfaceColor = texture2D(surfaceTexture, tc).r;\n"\ +" return texture2D(paletteTexture, vec2(surfaceColor * (255.0 / 256.0) + (0.5 / 256.0), 0.5)).rgb;\n"\ +"}\n"\ +"\n"\ +"void main()\n"\ +"{\n"\ +" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\ +" resultColor *= constants_Modulation;\n"\ +" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.xy, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\ +" resultColor = ApplyDesaturation(constants_Desaturation, resultColor);\n"\ +"\n"\ +" gl_FragColor = vec4(ApplyColorSpaceTransform(resultColor.rgb), resultColor.a);\n"\ +"}\n" + +namespace GpBinarizedShaders +{ + const char *g_drawQuadPaletteP_GL2 = GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUADPALETTEP_GLSL; + const char *g_drawQuadPaletteICCP_GL2 = "#define USE_ICC_PROFILE\n" GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUADPALETTEP_GLSL; +} diff --git a/AerofoilSDL/ShaderCode/DrawQuadPixelConstants.h b/AerofoilSDL/ShaderCode/DrawQuadPixelConstants.h new file mode 100644 index 00000000..ebc98bdc --- /dev/null +++ b/AerofoilSDL/ShaderCode/DrawQuadPixelConstants.h @@ -0,0 +1,5 @@ +#define GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H "uniform vec4 constants_Modulation;\n"\ +"uniform vec2 constants_FlickerAxis;\n"\ +"uniform float constants_FlickerStartThreshold;\n"\ +"uniform float constants_FlickerEndThreshold;\n"\ +"uniform float constants_Desaturation;\n" diff --git a/AerofoilSDL/ShaderCode/DrawQuadV.cpp b/AerofoilSDL/ShaderCode/DrawQuadV.cpp new file mode 100644 index 00000000..b8864328 --- /dev/null +++ b/AerofoilSDL/ShaderCode/DrawQuadV.cpp @@ -0,0 +1,17 @@ +#define GP_GL_SHADER_CODE_DRAWQUADV_GLSL "uniform vec4 ndcOriginAndDimensions;\n"\ +"uniform vec4 surfaceDimensions_TextureRegion;\n"\ +"attribute vec2 posUV;\n"\ +"varying vec4 texCoord;\n"\ +"\n"\ +"void main()\n"\ +"{\n"\ +" vec2 ndcPos = ndcOriginAndDimensions.xy + posUV.xy * ndcOriginAndDimensions.zw;\n"\ +"\n"\ +" gl_Position = vec4(ndcPos.x, ndcPos.y, 0.0, 1.0);\n"\ +" texCoord = vec4(posUV.xyxy * surfaceDimensions_TextureRegion.zwxy);\n"\ +"}\n" + +namespace GpBinarizedShaders +{ + const char *g_drawQuadV_GL2 = GP_GL_SHADER_CODE_DRAWQUADV_GLSL; +} diff --git a/AerofoilSDL/ShaderCode/Functions.h b/AerofoilSDL/ShaderCode/Functions.h new file mode 100644 index 00000000..24b1ff6e --- /dev/null +++ b/AerofoilSDL/ShaderCode/Functions.h @@ -0,0 +1,100 @@ +#define GP_GL_SHADER_CODE_FUNCTIONS_H "vec3 pow3(vec3 v, float ex)\n"\ +"{\n"\ +" return vec3(pow(v.x, ex), pow(v.y, ex), pow(v.z, ex));\n"\ +"}\n"\ +"\n"\ +"float SRGBToLinear(float v)\n"\ +"{\n"\ +" if (v <= 0.04045)\n"\ +" return v * (1.0 / 12.92);\n"\ +" else\n"\ +" return pow(((v + 0.055) * (1.0 / 1.055)), 2.4);\n"\ +"}\n"\ +"\n"\ +"vec2 SRGBToLinear(vec2 v)\n"\ +"{\n"\ +" return vec2(SRGBToLinear(v.x), SRGBToLinear(v.y));\n"\ +"}\n"\ +"\n"\ +"vec3 SRGBToLinear(vec3 v)\n"\ +"{\n"\ +" return vec3(SRGBToLinear(v.x), SRGBToLinear(v.y), SRGBToLinear(v.z));\n"\ +"}\n"\ +"\n"\ +"float LinearToSRGB(float v)\n"\ +"{\n"\ +" if (v <= 0.0031308)\n"\ +" return 12.92 * v;\n"\ +" else\n"\ +" return 1.055 * pow(v, 1.0 / 2.4) - 0.055;\n"\ +"}\n"\ +"\n"\ +"vec2 LinearToSRGB(vec2 v)\n"\ +"{\n"\ +" return vec2(LinearToSRGB(v.x), LinearToSRGB(v.y));\n"\ +"}\n"\ +"\n"\ +"vec3 LinearToSRGB(vec3 v)\n"\ +"{\n"\ +" return vec3(LinearToSRGB(v.x), LinearToSRGB(v.y), LinearToSRGB(v.z));\n"\ +"}\n"\ +"\n"\ +"vec4 ApplyFlicker(vec2 flickerAxis, vec2 coordinate, float startThreshold, float endThreshold, vec4 color)\n"\ +"{\n"\ +" float flickerTotal = dot(flickerAxis, coordinate);\n"\ +" if (flickerTotal < startThreshold)\n"\ +" return vec4(0, 0, 0, 0);\n"\ +" else if (flickerTotal >= endThreshold)\n"\ +" return color;\n"\ +"\n"\ +" return vec4(1, 1, 1, 1);\n"\ +"}\n"\ +"\n"\ +"vec4 ApplyDesaturation(float desaturation, vec4 color)\n"\ +"{\n"\ +" // This is intentionally done in gamma space\n"\ +" if (desaturation == 0.0)\n"\ +" return color;\n"\ +"\n"\ +" float grayLevel = dot(color.rgb, vec3(3.0, 6.0, 1.0) / 10.0);\n"\ +"\n"\ +" color.rgb = color.rgb * (1.0 - desaturation) + vec3(grayLevel, grayLevel, grayLevel) * desaturation;\n"\ +" return color;\n"\ +"}\n"\ +"\n"\ +"float saturate(float v)\n"\ +"{\n"\ +" return min(1.0, max(0.0, v));\n"\ +"}\n"\ +"\n"\ +"vec2 saturate(vec2 v)\n"\ +"{\n"\ +" return vec2(saturate(v.x), saturate(v.y));\n"\ +"}\n"\ +"\n"\ +"vec3 saturate(vec3 v)\n"\ +"{\n"\ +" return vec3(saturate(v.x), saturate(v.y), saturate(v.z));\n"\ +"}\n"\ +"\n"\ +"vec3 AppleRGBToSRGBLinear(vec3 color)\n"\ +"{\n"\ +" color = pow3(saturate(color), 1.8);\n"\ +"\n"\ +" vec3 result;\n"\ +" result = color.r * vec3(1.06870538834699, 0.024110476735, 0.00173499822713);\n"\ +" result += color.g * vec3(-0.07859532843279, 0.96007030899244, 0.02974755969275);\n"\ +" result += color.b * vec3(0.00988984558395, 0.01581936633364, 0.96851741859153);\n"\ +"\n"\ +" return result;\n"\ +"}\n"\ +"\n"\ +"vec3 ApplyColorSpaceTransform(vec3 color)\n"\ +"{\n"\ +"#ifdef USE_ICC_PROFILE\n"\ +" return saturate(AppleRGBToSRGBLinear(color));\n"\ +"#else\n"\ +" return SRGBToLinear(color);\n"\ +"#endif\n"\ +"}\n"\ +"\n" diff --git a/AerofoilSDL/ShaderCode/ScaleQuadP.cpp b/AerofoilSDL/ShaderCode/ScaleQuadP.cpp new file mode 100644 index 00000000..1cf3ffc6 --- /dev/null +++ b/AerofoilSDL/ShaderCode/ScaleQuadP.cpp @@ -0,0 +1,50 @@ +#include "Functions.h" + +#define GP_GL_SHADER_CODE_SCALEQUADP_GLSL "uniform sampler2D surfaceTexture;\n"\ +"varying vec4 texCoord;\n"\ +"\n"\ +"uniform vec4 dxdy_dimensions;\n"\ +"\n"\ +"vec3 SamplePixel(vec2 coord)\n"\ +"{\n"\ +" return texture2D(surfaceTexture, (coord + vec2(0.5, 0.5)) / dxdy_dimensions.zw).rgb;\n"\ +"}\n"\ +"\n"\ +"void main()\n"\ +"{\n"\ +" float dx = dxdy_dimensions.x;\n"\ +" float dy = dxdy_dimensions.y;\n"\ +" vec2 dimensions = dxdy_dimensions.zw;\n"\ +" vec2 texCoordInteger = dxdy_dimensions.zw;\n"\ +"\n"\ +" vec2 pixelTopLeftCoord = max(vec2(0.0, 0.0), texCoord.zw - vec2(dx, dy) * 0.5);\n"\ +" vec2 pixelBottomRightCoord = pixelTopLeftCoord + min(vec2(1.0, 1.0), vec2(dx, dy));\n"\ +"\n"\ +" vec2 topLeftCoordInteger = floor(pixelTopLeftCoord);\n"\ +" vec2 bottomRightCoordInteger = floor(pixelBottomRightCoord);\n"\ +"\n"\ +" vec2 interpolators = saturate((pixelBottomRightCoord - bottomRightCoordInteger) / vec2(dx, dy));\n"\ +"\n"\ +" vec3 topLeftColor = SamplePixel(topLeftCoordInteger);\n"\ +" vec3 topRightColor = SamplePixel(vec2(bottomRightCoordInteger.x, topLeftCoordInteger.y));\n"\ +" vec3 bottomLeftColor = SamplePixel(vec2(topLeftCoordInteger.x, bottomRightCoordInteger.y));\n"\ +" vec3 bottomRightColor = SamplePixel(bottomRightCoordInteger);\n"\ +"\n"\ +" vec3 topColor = (1.0 - interpolators.x) * topLeftColor + interpolators.x * topRightColor;\n"\ +" vec3 bottomColor = (1.0 - interpolators.x) * bottomLeftColor + interpolators.x * bottomRightColor;\n"\ +" vec3 interpolatedColor = (1.0 - interpolators.y) * topColor + interpolators.y * bottomColor;\n"\ +"\n"\ +" gl_FragColor = vec4(LinearToSRGB(interpolatedColor), 1.0);\n"\ +"}\n" + +namespace GpBinarizedShaders +{ + const char *g_scaleQuadP_GL2 = GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_SCALEQUADP_GLSL; + + extern const char *g_drawQuadRGBP_GL2; + extern const char *g_drawQuad15BitP_GL2; + + extern const char *g_drawQuadRGBICCP_GL2; + extern const char *g_drawQuad15BitICCP_GL2; + +} diff --git a/GpCommon/EGpDisplayDriverType.h b/GpCommon/EGpDisplayDriverType.h index e0207819..146d1016 100644 --- a/GpCommon/EGpDisplayDriverType.h +++ b/GpCommon/EGpDisplayDriverType.h @@ -2,7 +2,8 @@ enum EGpDisplayDriverType { - EGpDisplayDriverType_D3D11, + EGpDisplayDriverType_D3D11, + EGpDisplayDriverType_SDL_GL2, EGpDisplayDriverType_Count, }; diff --git a/GpCommon/IGpDisplayDriver.h b/GpCommon/IGpDisplayDriver.h index 452addd7..156fbc40 100644 --- a/GpCommon/IGpDisplayDriver.h +++ b/GpCommon/IGpDisplayDriver.h @@ -3,6 +3,8 @@ #include "GpPixelFormat.h" #include "EGpStandardCursor.h" +#include + struct IGpDisplayDriverSurface; struct IGpCursor; struct IGpPrefsHandler; @@ -30,7 +32,7 @@ struct IGpDisplayDriver virtual void GetDisplayResolution(unsigned int *width, unsigned int *height) = 0; - virtual IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, GpPixelFormat_t pixelFormat) = 0; + virtual IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat) = 0; virtual void DrawSurface(IGpDisplayDriverSurface *surface, int32_t x, int32_t y, size_t width, size_t height, const GpDisplayDriverSurfaceEffects *effects) = 0; virtual IGpCursor *LoadCursor(bool isColor, int cursorID) = 0; diff --git a/GpCommon/IGpFiber.h b/GpCommon/IGpFiber.h index 6b3a2243..c9646a44 100644 --- a/GpCommon/IGpFiber.h +++ b/GpCommon/IGpFiber.h @@ -4,6 +4,6 @@ struct IGpFiber { - virtual void YieldTo() = 0; + virtual void YieldTo(IGpFiber *toFiber) = 0; virtual void Destroy() = 0; }; diff --git a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp index f9544792..55b7fb7f 100644 --- a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp +++ b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp @@ -1245,7 +1245,7 @@ void GpDisplayDriverD3D11::GetDisplayResolution(unsigned int *width, unsigned in *height = m_windowHeightVirtual; } -IGpDisplayDriverSurface *GpDisplayDriverD3D11::CreateSurface(size_t width, size_t height, GpPixelFormat_t pixelFormat) +IGpDisplayDriverSurface *GpDisplayDriverD3D11::CreateSurface(size_t width, size_t height, size_t pitch, GpPixelFormat_t pixelFormat) { return GpDisplayDriverSurfaceD3D11::Create(m_device, m_deviceContext, width, height, pixelFormat); } diff --git a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h index 086fdab8..26ca06cd 100644 --- a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h +++ b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h @@ -41,7 +41,7 @@ class GpDisplayDriverD3D11 : public IGpDisplayDriver, public IGpPrefsHandler void GetDisplayResolution(unsigned int *width, unsigned int *height) override; - IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, GpPixelFormat_t pixelFormat) 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; diff --git a/GpMainApp.props b/GpMainApp.props index cd7f92f4..ac1c0bab 100644 --- a/GpMainApp.props +++ b/GpMainApp.props @@ -2,7 +2,9 @@ - + + $(SolutionDir)Aerofoil;$(IncludePath) + GP_APP_DLL;%(PreprocessorDefinitions) diff --git a/GpShell/GpAppEnvironment.cpp b/GpShell/GpAppEnvironment.cpp index 286c6b92..4199eafe 100644 --- a/GpShell/GpAppEnvironment.cpp +++ b/GpShell/GpAppEnvironment.cpp @@ -57,7 +57,7 @@ GpDisplayDriverTickStatus_t GpAppEnvironment::Tick(IGpFiber *vosFiber) return GpDisplayDriverTickStatuses::kOK; case ApplicationState_Running: SynchronizeState(); - m_applicationFiber->YieldTo(); + m_vosFiber->YieldTo(m_applicationFiber); break; case ApplicationState_SystemCall: { @@ -135,7 +135,7 @@ void GpAppEnvironment::AppThreadFunc() GpAppInterface_Get()->ApplicationMain(); m_applicationState = ApplicationState_Terminated; - m_vosFiber->YieldTo(); + m_applicationFiber->YieldTo(m_vosFiber); } void GpAppEnvironment::InitializeApplicationState() @@ -165,7 +165,7 @@ void GpAppEnvironment::StaticSuspendHookFunc(void *context, PortabilityLayer::Ho appEnv->m_suspendReturnValue = returnValue; appEnv->m_applicationState = ApplicationState_SystemCall; - appEnv->m_vosFiber->YieldTo(); + appEnv->m_applicationFiber->YieldTo(appEnv->m_vosFiber); } void GpAppEnvironment::DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) diff --git a/PortabilityLayer/QDGraf.cpp b/PortabilityLayer/QDGraf.cpp index 34c51b33..8c48f0f9 100644 --- a/PortabilityLayer/QDGraf.cpp +++ b/PortabilityLayer/QDGraf.cpp @@ -28,7 +28,7 @@ void DrawSurface::PushToDDSurface(IGpDisplayDriver *displayDriver) } if (m_ddSurface == nullptr) - m_ddSurface = displayDriver->CreateSurface(pixMap->m_rect.right - pixMap->m_rect.left, pixMap->m_rect.bottom - pixMap->m_rect.top, pixMap->m_pixelFormat); + m_ddSurface = displayDriver->CreateSurface(pixMap->m_rect.right - pixMap->m_rect.left, pixMap->m_rect.bottom - pixMap->m_rect.top, pixMap->m_pitch, pixMap->m_pixelFormat); if (m_port.IsDirty(PortabilityLayer::QDPortDirtyFlag_Contents) && m_ddSurface != nullptr) { diff --git a/Release.props b/Release.props index 0bfa4ebd..a482c214 100644 --- a/Release.props +++ b/Release.props @@ -6,6 +6,7 @@ GP_DEBUG_CONFIG=0;NDEBUG;%(PreprocessorDefinitions) + true