Use upstream melonDS's code for doing screen control madness. This means too that both the OpenGL and software renderer will *present* frames with OpenGL (and if this is unavailable, screen control madness will be unavailable)

also fix up this code so upscaling works properly
c# changes pending
This commit is contained in:
CasualPokePlayer 2023-09-25 05:21:53 -07:00
parent 0dc4b94475
commit 3d5c2120f2
7 changed files with 225 additions and 85 deletions

Binary file not shown.

View File

@ -11,10 +11,13 @@
namespace GLPresenter namespace GLPresenter
{ {
static const char* VertexShader = R"(#version 140 constexpr u32 NDS_WIDTH = 256;
constexpr u32 NDS_HEIGHT = 384;
uniform vec2 uSize; static const char* ScreenVS = R"(#version 140
uniform mat2x3 uTransform;
uniform vec2 uScreenSize;
uniform mat2x3 uScreenTransform;
in vec2 vPosition; in vec2 vPosition;
in vec2 vTexcoord; in vec2 vTexcoord;
@ -25,10 +28,10 @@ void main()
{ {
vec4 fpos; vec4 fpos;
fpos.xy = vec3(vPosition, 1.0) * uTransform; fpos.xy = vec3(vPosition, 1.0) * uScreenTransform;
fpos.xy = ((fpos.xy * 2.0) / uSize) - 1.0; fpos.xy = ((fpos.xy * 2.0) / uScreenSize) - 1.0;
// fpos.y *= -1; fpos.y *= -1;
fpos.z = 0.0; fpos.z = 0.0;
fpos.w = 1.0; fpos.w = 1.0;
@ -37,9 +40,9 @@ void main()
} }
)"; )";
static const char* FragmentShader = R"(#version 140 static const char* ScreenFS = R"(#version 140
uniform sampler2D Tex; uniform sampler2D ScreenTex;
smooth in vec2 fTexcoord; smooth in vec2 fTexcoord;
@ -47,47 +50,47 @@ out vec4 oColor;
void main() void main()
{ {
vec4 pixel = texture(Tex, fTexcoord); vec4 pixel = texture(ScreenTex, fTexcoord);
oColor = vec4(pixel.bgr, 1.0); oColor = vec4(pixel.bgr, 1.0);
} }
)"; )";
ECL_INVISIBLE static GLuint ScreenShaderProgram[3];
ECL_INVISIBLE static GLuint ShaderProgram[3]; ECL_INVISIBLE static GLuint ScreenShaderTransformULoc, ScreenShaderSizeULoc;
ECL_INVISIBLE static GLuint ShaderTransformULoc, ShaderSizeULoc;
ECL_INVISIBLE static GLuint VertexBuffer, VertexArray; ECL_INVISIBLE static GLuint VertexBuffer, VertexArray;
ECL_INVISIBLE static float TransformMatrix[2 * 6]; ECL_INVISIBLE static float ScreenMatrix[2 * 6];
ECL_INVISIBLE static int ScreenKinds[2];
ECL_INVISIBLE static int NumScreens;
ECL_INVISIBLE static u32 Width, Height; ECL_INVISIBLE static u32 Width, Height;
ECL_INVISIBLE static u32 GLScale;
ECL_INVISIBLE static GLuint TextureID; ECL_INVISIBLE static GLuint InputTextureID;
ECL_INVISIBLE static GLuint FboID; ECL_INVISIBLE static GLuint OutputTextureID;
ECL_INVISIBLE static GLuint PboID; ECL_INVISIBLE static GLuint OutputFboID;
ECL_INVISIBLE static GLuint OutputPboID;
void Init(u32 scale) void Init(u32 scale)
{ {
Width = 256 * scale; OpenGL::BuildShaderProgram(ScreenVS, ScreenFS, ScreenShaderProgram, "GLPresenterShader");
Height = 384 * scale;
OpenGL::BuildShaderProgram(VertexShader, FragmentShader, ShaderProgram, "GLPresenterShader"); GLuint pid = ScreenShaderProgram[2];
GLuint pid = ShaderProgram[2];
glBindAttribLocation(pid, 0, "vPosition"); glBindAttribLocation(pid, 0, "vPosition");
glBindAttribLocation(pid, 1, "vTexcoord"); glBindAttribLocation(pid, 1, "vTexcoord");
glBindFragDataLocation(pid, 0, "oColor"); glBindFragDataLocation(pid, 0, "oColor");
OpenGL::LinkShaderProgram(ShaderProgram); OpenGL::LinkShaderProgram(ScreenShaderProgram);
glUseProgram(pid); glUseProgram(pid);
glUniform1i(glGetUniformLocation(pid, "Tex"), 0); glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0);
ShaderSizeULoc = glGetUniformLocation(pid, "uSize"); ScreenShaderSizeULoc = glGetUniformLocation(pid, "uScreenSize");
ShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); ScreenShaderTransformULoc = glGetUniformLocation(pid, "uScreenTransform");
constexpr int paddedHeight = 192 * 2 + 2; constexpr int paddedHeight = NDS_HEIGHT + 2;
constexpr float padPixels = 1.f / paddedHeight; constexpr float padPixels = 1.f / paddedHeight;
static const float vertices[] = static const float vertices[] =
@ -118,34 +121,25 @@ void Init(u32 scale)
glEnableVertexAttribArray(1); // texcoord glEnableVertexAttribArray(1); // texcoord
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * 4, (void*)(2 * 4)); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * 4, (void*)(2 * 4));
// TODO: Could we use this instead of all the screen transforming code in the frontend? glGenTextures(1, &InputTextureID);
// see https://github.com/TASEmulators/BizHawk/issues/3772
// OK THIS NEEDS TO BE DONE FRONTEND CODE DOESN'T LIKE AN UPSCALED IMAGE
Frontend::SetupScreenLayout(Width, Height, Frontend::screenLayout_Natural, Frontend::screenRot_0Deg, Frontend::screenSizing_Even, 0, true, false, 1, 1);
int discard[2];
Frontend::GetScreenTransforms(TransformMatrix, discard);
glGenTextures(1, &TextureID);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID); glBindTexture(GL_TEXTURE_2D, InputTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NDS_WIDTH, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, 0); static u8 zeroData[NDS_WIDTH * 4 * 4]{};
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2, NDS_WIDTH, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData);
glGenFramebuffers(1, &FboID); glGenBuffers(1, &OutputPboID);
glBindFramebuffer(GL_FRAMEBUFFER, FboID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TextureID, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glGenBuffers(1, &PboID); GLScale = scale;
} }
std::pair<u32, u32> Present(bool filter) std::pair<u32, u32> Present()
{ {
glBindFramebuffer(GL_FRAMEBUFFER, FboID); glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDepthMask(false); glDepthMask(false);
glDisable(GL_BLEND); glDisable(GL_BLEND);
@ -155,32 +149,40 @@ std::pair<u32, u32> Present(bool filter)
glViewport(0, 0, Width, Height); glViewport(0, 0, Width, Height);
glUseProgram(ShaderProgram[2]); glUseProgram(ScreenShaderProgram[2]);
glUniform2f(ShaderSizeULoc, Width, Height); glUniform2f(ScreenShaderSizeULoc, Width, Height);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
GPU::CurGLCompositor->BindOutputTexture(GPU::FrontBuffer);
GLint texFilter = filter ? GL_LINEAR : GL_NEAREST; if (GPU3D::CurrentRenderer->Accelerated)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texFilter); {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texFilter); GPU::CurGLCompositor->BindOutputTexture(GPU::FrontBuffer);
}
else
{
glBindTexture(GL_TEXTURE_2D, InputTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_BYTE, GPU::Framebuffer[GPU::FrontBuffer][0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2 + 2, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_BYTE, GPU::Framebuffer[GPU::FrontBuffer][1]);
}
glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer);
glBindVertexArray(VertexArray); glBindVertexArray(VertexArray);
glUniformMatrix2x3fv(ShaderTransformULoc, 1, GL_TRUE, &TransformMatrix[0]); for (int i = 0; i < NumScreens; i++)
glDrawArrays(GL_TRIANGLES, 0, 6); {
glUniformMatrix2x3fv(ScreenShaderTransformULoc, 1, GL_TRUE, &ScreenMatrix[i * 6]);
glUniformMatrix2x3fv(ShaderTransformULoc, 1, GL_TRUE, &TransformMatrix[6]); glDrawArrays(GL_TRIANGLES, ScreenKinds[i] == 0 ? 0 : 6, 6);
glDrawArrays(GL_TRIANGLES, 6, 6); }
glFlush(); glFlush();
GLint oldPbo; GLint oldPbo;
glGetIntegerv(GL_PIXEL_PACK_BUFFER, &oldPbo); glGetIntegerv(GL_PIXEL_PACK_BUFFER, &oldPbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, PboID);
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
glBufferData(GL_PIXEL_PACK_BUFFER, Width * Height * sizeof(u32), nullptr, GL_STREAM_READ); glBufferData(GL_PIXEL_PACK_BUFFER, Width * Height * sizeof(u32), nullptr, GL_STREAM_READ);
glReadPixels(0, 0, Width, Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, static_cast<void*>(0)); glReadPixels(0, 0, Width, Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)(0));
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
return std::make_pair(Width, Height); return std::make_pair(Width, Height);
@ -188,21 +190,139 @@ std::pair<u32, u32> Present(bool filter)
ECL_EXPORT u32 GetGLTexture() ECL_EXPORT u32 GetGLTexture()
{ {
return TextureID; return OutputTextureID;
} }
ECL_EXPORT void ReadFrameBuffer(u32* buffer) ECL_EXPORT void ReadFrameBuffer(u32* buffer)
{ {
GLint oldPbo; GLint oldPbo;
glGetIntegerv(GL_PIXEL_PACK_BUFFER, &oldPbo); glGetIntegerv(GL_PIXEL_PACK_BUFFER, &oldPbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, PboID);
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
const auto p = static_cast<const u32*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); const auto p = static_cast<const u32*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
if (p) if (p)
{ {
memcpy(buffer, p, Width * Height * sizeof(u32)); // FBOs render upside down, so flip vertically to counteract that
buffer += Width * (Height - 1);
const int w = Width;
const int h = Height;
for (int i = 0; i < h; i++)
{
std::memcpy(&buffer[-i * w], &p[i * w], Width * sizeof(u32));
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
} }
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
} }
struct ScreenSettings
{
Frontend::ScreenLayout ScreenLayout;
Frontend::ScreenRotation ScreenRotation;
Frontend::ScreenSizing ScreenSizing;
int ScreenGap;
bool ScreenSwap;
};
static std::pair<u32, u32> GetScreenSize(const ScreenSettings* screenSettings, u32 scale)
{
bool isHori = screenSettings->ScreenRotation == Frontend::screenRot_90Deg
|| screenSettings->ScreenRotation == Frontend::screenRot_270Deg;
int gap = screenSettings->ScreenGap * scale;
int w = NDS_WIDTH * scale;
int h = (NDS_HEIGHT / 2) * scale;
if (screenSettings->ScreenSizing == Frontend::screenSizing_TopOnly
|| screenSettings->ScreenSizing == Frontend::screenSizing_BotOnly)
{
return isHori
? std::make_pair(h, w)
: std::make_pair(w, h);
}
switch (screenSettings->ScreenLayout)
{
case Frontend::screenLayout_Natural:
return isHori
? std::make_pair(h * 2 + gap, w)
: std::make_pair(w, h * 2 + gap);
case Frontend::screenLayout_Vertical:
return isHori
? std::make_pair(h, w * 2 + gap)
: std::make_pair(w, h * 2 + gap);
case Frontend::screenLayout_Horizontal:
return isHori
? std::make_pair(h * 2 + gap, w)
: std::make_pair(w * 2 + gap, h);
default:
__builtin_unreachable();
}
}
ECL_EXPORT void SetScreenSettings(const ScreenSettings* screenSettings, u32* width, u32* height, u32* vwidth, u32* vheight)
{
auto [w, h] = GetScreenSize(screenSettings, GLScale);
if (w != Width || h != Height)
{
Width = w;
Height = h;
glDeleteTextures(1, &OutputTextureID);
glGenTextures(1, &OutputTextureID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, OutputTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glDeleteFramebuffers(1, &OutputFboID);
glGenFramebuffers(1, &OutputFboID);
glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, OutputTextureID, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
Frontend::SetupScreenLayout(w, h,
screenSettings->ScreenLayout,
screenSettings->ScreenRotation,
screenSettings->ScreenSizing,
screenSettings->ScreenGap,
true, // Integer Scaling
screenSettings->ScreenSwap,
1, 1); // Aspect Ratio
NumScreens = Frontend::GetScreenTransforms(ScreenMatrix, ScreenKinds);
Present();
*width = Width;
*height = Height;
if (GLScale > 1)
{
auto [vw, vh] = GetScreenSize(screenSettings, 1);
*vwidth = vw;
*vheight = vh;
}
else
{
*vwidth = Width;
*vheight = Height;
}
}
ECL_EXPORT void GetTouchCoords(int* x, int* y)
{
if (!Frontend::GetTouchCoords(*x, *y, true))
{
*x = 0;
*y = 0;
}
}
} }

View File

@ -7,7 +7,7 @@ namespace GLPresenter
{ {
void Init(u32 scale); void Init(u32 scale);
std::pair<u32, u32> Present(bool filter); std::pair<u32, u32> Present();
} }

View File

@ -18,6 +18,7 @@
static bool SkipFW; static bool SkipFW;
static time_t CurTime; static time_t CurTime;
static bool GLPresentation;
struct InitConfig struct InitConfig
{ {
@ -60,25 +61,31 @@ ECL_EXPORT const char* Init(InitConfig* initConfig,
return "Failed to init core!"; return "Failed to init core!";
} }
switch (initConfig->ThreeDeeRenderer) if (loadGLProc)
{ {
case 0: switch (initConfig->ThreeDeeRenderer)
break; {
case 1: case 0:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_2, initConfig->IsWinApi); BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_1, initConfig->IsWinApi);
break; break;
#if false case 1:
case 2: BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_2, initConfig->IsWinApi);
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V4_3, initConfig->IsWinApi); break;
break; #if false // OpenGL Compute Renderer isn't released yet
case 2:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V4_3, initConfig->IsWinApi);
break;
#endif #endif
default: default:
return "Unknown 3DRenderer!"; return "Unknown 3DRenderer!";
} }
if (initConfig->ThreeDeeRenderer) GLPresenter::Init(initConfig->ThreeDeeRenderer ? initConfig->RenderSettings.GL_ScaleFactor : 1);
GLPresentation = true;
}
else
{ {
GLPresenter::Init(initConfig->RenderSettings.GL_ScaleFactor); GLPresentation = false;
} }
GPU::InitRenderer(initConfig->ThreeDeeRenderer); GPU::InitRenderer(initConfig->ThreeDeeRenderer);
@ -192,15 +199,18 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo* f)
NDS::RunFrame(); NDS::RunFrame();
if (GPU3D::CurrentRenderer->Accelerated) if (!GPU3D::CurrentRenderer->Accelerated)
{
std::tie(f->Width, f->Height) = GLPresenter::Present(false);
}
else
{ {
auto softRenderer = reinterpret_cast<GPU3D::SoftRenderer*>(GPU3D::CurrentRenderer.get()); auto softRenderer = reinterpret_cast<GPU3D::SoftRenderer*>(GPU3D::CurrentRenderer.get());
softRenderer->StopRenderThread(); softRenderer->StopRenderThread();
}
if (GLPresentation)
{
std::tie(f->Width, f->Height) = GLPresenter::Present();
}
else
{
constexpr u32 SingleScreenSize = 256 * 192; constexpr u32 SingleScreenSize = 256 * 192;
memcpy(f->VideoBuffer, GPU::Framebuffer[GPU::FrontBuffer][0], SingleScreenSize * sizeof(u32)); memcpy(f->VideoBuffer, GPU::Framebuffer[GPU::FrontBuffer][0], SingleScreenSize * sizeof(u32));
memcpy(f->VideoBuffer + SingleScreenSize, GPU::Framebuffer[GPU::FrontBuffer][1], SingleScreenSize * sizeof(u32)); memcpy(f->VideoBuffer + SingleScreenSize, GPU::Framebuffer[GPU::FrontBuffer][1], SingleScreenSize * sizeof(u32));
@ -210,9 +220,9 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo* f)
} }
f->Samples = SPU::ReadOutput(f->SoundBuffer); f->Samples = SPU::ReadOutput(f->SoundBuffer);
if (f->Samples == 0) // hack if (f->Samples == 0) // hack when core decides to stop outputting audio altogether (lid closed or power off)
{ {
memset(f->SoundBuffer, 0, 737 * 2 * sizeof (u16)); memset(f->SoundBuffer, 0, 737 * 2 * sizeof(u16));
f->Samples = 737; f->Samples = 737;
} }

View File

@ -574,7 +574,7 @@ PFNGLWAITSYNCPROC biz_glWaitSync;
namespace BizOGL namespace BizOGL
{ {
static void LoadGL3_2(LoadGLProc load) static void LoadGL3_1(LoadGLProc load)
{ {
// 1.0 // 1.0
biz_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); biz_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
@ -892,6 +892,12 @@ static void LoadGL3_2(LoadGLProc load)
biz_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv"); biz_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv");
biz_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName"); biz_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName");
biz_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding"); biz_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding");
}
static void LoadGL3_2(LoadGLProc load)
{
// 1.0 - 3.1
LoadGL3_1(load);
// 3.2 // 3.2
biz_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex"); biz_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex");
@ -1183,6 +1189,9 @@ void LoadGL(LoadGLProc load, LoadGLVersion version, bool isWinApi)
{ {
switch (version) switch (version)
{ {
case LoadGLVersion::V3_1:
LoadGL3_1(load);
break;
case LoadGLVersion::V3_2: case LoadGLVersion::V3_2:
LoadGL3_2(load); LoadGL3_2(load);
break; break;

View File

@ -20,6 +20,7 @@ using LoadGLProc = void* (*)(const char* proc);
enum class LoadGLVersion enum class LoadGLVersion
{ {
V3_1,
V3_2, V3_2,
V4_3, V4_3,
}; };

@ -1 +1 @@
Subproject commit b200c5476bce4a0c127c304105b3cb7d54f0cee4 Subproject commit ee581ad6461c78ab08e440bb1ca5afd05fa96f66