mirror of
https://github.com/izzy2lost/Super3.git
synced 2026-07-05 15:18:38 -07:00
better rendering
This commit is contained in:
@@ -112,6 +112,8 @@ add_library(super3 SHARED
|
||||
android_shim.cpp
|
||||
model3_stubs.cpp
|
||||
android_input_system.cpp
|
||||
gles_presenter.cpp
|
||||
gles_stub_render3d.cpp
|
||||
render2d_android.cpp
|
||||
${SUPER3_SOURCES}
|
||||
${M68K_GENERATED_SOURCES}
|
||||
@@ -129,10 +131,14 @@ target_include_directories(super3 PRIVATE
|
||||
find_library(log-lib log)
|
||||
find_library(android-lib android)
|
||||
find_library(z-lib z)
|
||||
find_library(egl-lib EGL)
|
||||
find_library(glesv3-lib GLESv3)
|
||||
|
||||
target_link_libraries(super3 PRIVATE
|
||||
${log-lib}
|
||||
${android-lib}
|
||||
${z-lib}
|
||||
${egl-lib}
|
||||
${glesv3-lib}
|
||||
SDL2::SDL2 # present when SUPER3_FETCH_SDL2 is ON
|
||||
)
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
#include "gles_presenter.h"
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
static GLuint Compile(GLenum type, const char* src)
|
||||
{
|
||||
GLuint sh = glCreateShader(type);
|
||||
glShaderSource(sh, 1, &src, nullptr);
|
||||
glCompileShader(sh);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
char log[2048];
|
||||
GLsizei n = 0;
|
||||
glGetShaderInfoLog(sh, (GLsizei)sizeof(log), &n, log);
|
||||
glDeleteShader(sh);
|
||||
return 0;
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool GlesPresenter::Init()
|
||||
{
|
||||
if (!CreateProgram())
|
||||
return false;
|
||||
if (!CreateGeometry())
|
||||
return false;
|
||||
|
||||
glGenTextures(1, &m_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, m_tex);
|
||||
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_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlesPresenter::Shutdown()
|
||||
{
|
||||
if (m_tex) { glDeleteTextures(1, &m_tex); m_tex = 0; }
|
||||
if (m_vbo) { glDeleteBuffers(1, &m_vbo); m_vbo = 0; }
|
||||
if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; }
|
||||
if (m_program) { glDeleteProgram(m_program); m_program = 0; }
|
||||
m_uTex = -1;
|
||||
m_uploadRGBA.clear();
|
||||
m_outputW = m_outputH = m_srcW = m_srcH = 0;
|
||||
}
|
||||
|
||||
bool GlesPresenter::CreateProgram()
|
||||
{
|
||||
static const char* vs = R"glsl(
|
||||
#version 300 es
|
||||
layout(location=0) in vec2 aPos;
|
||||
layout(location=1) in vec2 aUv;
|
||||
out vec2 vUv;
|
||||
void main() {
|
||||
vUv = aUv;
|
||||
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
static const char* fs = R"glsl(
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 vUv;
|
||||
uniform sampler2D uTex;
|
||||
out vec4 oColor;
|
||||
void main() {
|
||||
oColor = texture(uTex, vUv);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
GLuint v = Compile(GL_VERTEX_SHADER, vs);
|
||||
GLuint f = Compile(GL_FRAGMENT_SHADER, fs);
|
||||
if (!v || !f)
|
||||
{
|
||||
if (v) glDeleteShader(v);
|
||||
if (f) glDeleteShader(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_program = glCreateProgram();
|
||||
glAttachShader(m_program, v);
|
||||
glAttachShader(m_program, f);
|
||||
glLinkProgram(m_program);
|
||||
glDeleteShader(v);
|
||||
glDeleteShader(f);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetProgramiv(m_program, GL_LINK_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
glDeleteProgram(m_program);
|
||||
m_program = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_uTex = glGetUniformLocation(m_program, "uTex");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GlesPresenter::CreateGeometry()
|
||||
{
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
glGenBuffers(1, &m_vbo);
|
||||
glBindVertexArray(m_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(m_verts), m_verts, GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlesPresenter::Resize(int outputW, int outputH)
|
||||
{
|
||||
m_outputW = std::max(1, outputW);
|
||||
m_outputH = std::max(1, outputH);
|
||||
UpdateQuadVerts();
|
||||
}
|
||||
|
||||
void GlesPresenter::UpdateFrameARGB(const uint32_t* pixelsARGB, int width, int height)
|
||||
{
|
||||
if (!pixelsARGB || width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
if (m_srcW != width || m_srcH != height)
|
||||
{
|
||||
m_srcW = width;
|
||||
m_srcH = height;
|
||||
glBindTexture(GL_TEXTURE_2D, m_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_srcW, m_srcH, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
UpdateQuadVerts();
|
||||
}
|
||||
|
||||
const size_t count = static_cast<size_t>(width) * static_cast<size_t>(height);
|
||||
m_uploadRGBA.resize(count * 4);
|
||||
|
||||
// Convert ARGB (0xAARRGGBB) -> RGBA8 bytes.
|
||||
// Note: input is packed in CPU endian; treat as integer and extract.
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const uint32_t p = pixelsARGB[i];
|
||||
const uint8_t a = static_cast<uint8_t>(p >> 24);
|
||||
const uint8_t r = static_cast<uint8_t>(p >> 16);
|
||||
const uint8_t g = static_cast<uint8_t>(p >> 8);
|
||||
const uint8_t b = static_cast<uint8_t>(p >> 0);
|
||||
m_uploadRGBA[(i * 4) + 0] = r;
|
||||
m_uploadRGBA[(i * 4) + 1] = g;
|
||||
m_uploadRGBA[(i * 4) + 2] = b;
|
||||
m_uploadRGBA[(i * 4) + 3] = a;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, m_tex);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_srcW, m_srcH, GL_RGBA, GL_UNSIGNED_BYTE, m_uploadRGBA.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void GlesPresenter::UpdateQuadVerts()
|
||||
{
|
||||
if (m_outputW <= 0 || m_outputH <= 0 || m_srcW <= 0 || m_srcH <= 0)
|
||||
return;
|
||||
|
||||
const float outW = (float)m_outputW;
|
||||
const float outH = (float)m_outputH;
|
||||
const float srcW = (float)m_srcW;
|
||||
const float srcH = (float)m_srcH;
|
||||
|
||||
const float scale = std::min(outW / srcW, outH / srcH);
|
||||
const float drawW = srcW * scale;
|
||||
const float drawH = srcH * scale;
|
||||
|
||||
const float ndcW = (drawW / outW) * 2.0f;
|
||||
const float ndcH = (drawH / outH) * 2.0f;
|
||||
|
||||
const float l = -ndcW * 0.5f;
|
||||
const float r = +ndcW * 0.5f;
|
||||
const float b = -ndcH * 0.5f;
|
||||
const float t = +ndcH * 0.5f;
|
||||
|
||||
// Triangle strip: (l,b)->(r,b)->(l,t)->(r,t)
|
||||
// pos.xy, uv.xy
|
||||
const float v[] = {
|
||||
l, b, 0.0f, 1.0f,
|
||||
r, b, 1.0f, 1.0f,
|
||||
l, t, 0.0f, 0.0f,
|
||||
r, t, 1.0f, 0.0f,
|
||||
};
|
||||
std::memcpy(m_verts, v, sizeof(v));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(m_verts), m_verts);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GlesPresenter::Render()
|
||||
{
|
||||
if (!m_program || !m_vao || !m_tex || m_outputW <= 0 || m_outputH <= 0)
|
||||
return;
|
||||
|
||||
glViewport(0, 0, m_outputW, m_outputH);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glUseProgram(m_program);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, m_tex);
|
||||
glUniform1i(m_uTex, 0);
|
||||
|
||||
glBindVertexArray(m_vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
// Minimal OpenGL ES presenter:
|
||||
// - Uploads an ARGB8888 (0xAARRGGBB) framebuffer as a texture
|
||||
// - Renders it with aspect-correct letterboxing
|
||||
class GlesPresenter
|
||||
{
|
||||
public:
|
||||
bool Init();
|
||||
void Shutdown();
|
||||
|
||||
void Resize(int outputW, int outputH);
|
||||
|
||||
// pixelsARGB: 0xAARRGGBB packed.
|
||||
void UpdateFrameARGB(const uint32_t* pixelsARGB, int width, int height);
|
||||
void Render();
|
||||
|
||||
private:
|
||||
bool CreateProgram();
|
||||
bool CreateGeometry();
|
||||
void UpdateQuadVerts();
|
||||
|
||||
int m_outputW = 0;
|
||||
int m_outputH = 0;
|
||||
int m_srcW = 0;
|
||||
int m_srcH = 0;
|
||||
|
||||
unsigned m_program = 0;
|
||||
unsigned m_vao = 0;
|
||||
unsigned m_vbo = 0;
|
||||
unsigned m_tex = 0;
|
||||
int m_uTex = -1;
|
||||
|
||||
// Interleaved: pos.xy, uv.xy (4 verts)
|
||||
float m_verts[16]{};
|
||||
|
||||
// Converted upload buffer in RGBA8.
|
||||
std::vector<uint8_t> m_uploadRGBA;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "gles_stub_render3d.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
static GLuint Compile(GLenum type, const char* src)
|
||||
{
|
||||
GLuint sh = glCreateShader(type);
|
||||
glShaderSource(sh, 1, &src, nullptr);
|
||||
glCompileShader(sh);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
glDeleteShader(sh);
|
||||
return 0;
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GlesStubRender3D::BeginFrame()
|
||||
{
|
||||
if (m_inited)
|
||||
return;
|
||||
|
||||
static const char* vs = R"glsl(
|
||||
#version 300 es
|
||||
layout(location=0) in vec2 aPos;
|
||||
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
|
||||
)glsl";
|
||||
|
||||
static const char* fs = R"glsl(
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
out vec4 oColor;
|
||||
void main() { oColor = vec4(0.15, 0.2, 0.45, 1.0); }
|
||||
)glsl";
|
||||
|
||||
GLuint v = Compile(GL_VERTEX_SHADER, vs);
|
||||
GLuint f = Compile(GL_FRAGMENT_SHADER, fs);
|
||||
if (!v || !f)
|
||||
return;
|
||||
|
||||
m_program = glCreateProgram();
|
||||
glAttachShader(m_program, v);
|
||||
glAttachShader(m_program, f);
|
||||
glLinkProgram(m_program);
|
||||
glDeleteShader(v);
|
||||
glDeleteShader(f);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetProgramiv(m_program, GL_LINK_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
glDeleteProgram(m_program);
|
||||
m_program = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
glGenBuffers(1, &m_vbo);
|
||||
|
||||
// Small triangle in the top-left corner (NDC).
|
||||
const float verts[] = {
|
||||
-0.98f, 0.98f,
|
||||
-0.70f, 0.98f,
|
||||
-0.98f, 0.70f,
|
||||
};
|
||||
|
||||
glBindVertexArray(m_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
m_inited = true;
|
||||
}
|
||||
|
||||
void GlesStubRender3D::RenderFrame()
|
||||
{
|
||||
if (!m_inited)
|
||||
BeginFrame();
|
||||
if (!m_inited)
|
||||
return;
|
||||
|
||||
glUseProgram(m_program);
|
||||
glBindVertexArray(m_vao);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include "Graphics/IRender3D.h"
|
||||
|
||||
// Tiny GLES-backed IRender3D implementation used as a stepping stone:
|
||||
// - Verifies the 3D renderer hook is being called from the Real3D/GPU path.
|
||||
// - Does NOT implement Model 3 Real3D yet.
|
||||
class GlesStubRender3D final : public IRender3D
|
||||
{
|
||||
public:
|
||||
bool Init(unsigned, unsigned, unsigned, unsigned, unsigned, unsigned) override { return true; }
|
||||
void AttachMemory(const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*, const uint16_t*) override {}
|
||||
void UploadTextures(unsigned, unsigned, unsigned, unsigned, unsigned) override {}
|
||||
void SetStepping(int) override {}
|
||||
void SetSunClamp(bool) override {}
|
||||
void SetSignedShade(bool) override {}
|
||||
float GetLosValue(int) override { return 0.f; }
|
||||
|
||||
void BeginFrame() override;
|
||||
void RenderFrame() override;
|
||||
void EndFrame() override {}
|
||||
|
||||
private:
|
||||
bool m_inited = false;
|
||||
GLuint m_program = 0;
|
||||
GLuint m_vao = 0;
|
||||
GLuint m_vbo = 0;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include "GameLoader.h"
|
||||
#include "Model3/Model3.h"
|
||||
#include "ROMSet.h"
|
||||
@@ -24,6 +26,8 @@
|
||||
#include "BlockFile.h"
|
||||
|
||||
#include "android_input_system.h"
|
||||
#include "gles_presenter.h"
|
||||
#include "gles_stub_render3d.h"
|
||||
|
||||
// Minimal OSD glue -----------------------------------------------------------
|
||||
|
||||
@@ -58,7 +62,7 @@ struct Super3Host {
|
||||
AndroidInputSystem inputSystem;
|
||||
CInputs inputs{&inputSystem};
|
||||
StubOutputs outputs;
|
||||
NullRender3D render3d;
|
||||
GlesStubRender3D render3d;
|
||||
CRender2D render2d{config};
|
||||
|
||||
std::unique_ptr<GameLoader> loader;
|
||||
@@ -264,7 +268,6 @@ struct Super3Host {
|
||||
// it mainly keeps the input system in a sane state.
|
||||
inputs.Poll(&game, 0, 0, 496, 384);
|
||||
model3->RunFrame();
|
||||
model3->RenderFrame();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -303,13 +306,20 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
SDL_Window* window = SDL_CreateWindow(
|
||||
"Super3 (SDL bootstrap)",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
1280,
|
||||
720,
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL
|
||||
);
|
||||
|
||||
if (!window) {
|
||||
@@ -318,24 +328,21 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!renderer) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateRenderer failed: %s", SDL_GetError());
|
||||
SDL_GLContext gl = SDL_GL_CreateContext(window);
|
||||
if (!gl) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Texture for tile-generator output (496x384 ARGB). This renders the built-in test screen.
|
||||
SDL_Texture* tgTexture = SDL_CreateTexture(
|
||||
renderer,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
496,
|
||||
384);
|
||||
if (!tgTexture) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateTexture failed: %s", SDL_GetError());
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_GL_MakeCurrent(window, gl);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
GlesPresenter presenter;
|
||||
if (!presenter.Init()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "GlesPresenter.Init failed");
|
||||
SDL_GL_DeleteContext(gl);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
@@ -466,45 +473,27 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
host.RunFrame();
|
||||
}
|
||||
|
||||
// Present the tile generator framebuffer if available; otherwise clear to black.
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
|
||||
SDL_RenderClear(renderer);
|
||||
int winW = 0, winH = 0;
|
||||
SDL_GL_GetDrawableSize(window, &winW, &winH);
|
||||
presenter.Resize(winW, winH);
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (state == 1 && host.render2d.HasFrame()) {
|
||||
const uint32_t* pixels = host.render2d.GetFrameBufferRGBA();
|
||||
const int pitch = (int)(host.render2d.GetFrameWidth() * sizeof(uint32_t));
|
||||
if (SDL_UpdateTexture(tgTexture, nullptr, pixels, pitch) != 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_UpdateTexture failed: %s", SDL_GetError());
|
||||
} else {
|
||||
int winW = 0, winH = 0;
|
||||
SDL_GetRendererOutputSize(renderer, &winW, &winH);
|
||||
const int srcW = (int)host.render2d.GetFrameWidth();
|
||||
const int srcH = (int)host.render2d.GetFrameHeight();
|
||||
presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight());
|
||||
presenter.Render();
|
||||
|
||||
// Preserve aspect ratio (letterbox/pillarbox).
|
||||
float scale = std::min((float)winW / (float)srcW, (float)winH / (float)srcH);
|
||||
SDL_Rect dst{};
|
||||
dst.w = (int)(srcW * scale);
|
||||
dst.h = (int)(srcH * scale);
|
||||
dst.x = (winW - dst.w) / 2;
|
||||
dst.y = (winH - dst.h) / 2;
|
||||
SDL_RenderCopy(renderer, tgTexture, nullptr, &dst);
|
||||
}
|
||||
// Debug: draw a small triangle on top so we can verify GLES is working
|
||||
// and we have a valid 3D hook. The real integration will happen inside
|
||||
// the GPU/TileGen render path once New3D is ported.
|
||||
host.render3d.BeginFrame();
|
||||
host.render3d.RenderFrame();
|
||||
host.render3d.EndFrame();
|
||||
}
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
if (SDL_GetError()[0] != '\0') {
|
||||
// SDL doesn't always surface present errors via return codes; log and clear.
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL error after present: %s", SDL_GetError());
|
||||
// If the EGL surface has gone away (background/minimize), stop rendering
|
||||
// until we get a foreground/surface recreation event.
|
||||
if (strstr(SDL_GetError(), "EGL_BAD_SURFACE") != nullptr ||
|
||||
strstr(SDL_GetError(), "unable to show color buffer") != nullptr) {
|
||||
backgrounded = true;
|
||||
SDL_Log("Detected bad surface; pausing rendering until foreground");
|
||||
}
|
||||
SDL_ClearError();
|
||||
}
|
||||
SDL_GL_SwapWindow(window);
|
||||
|
||||
uint32_t t = SDL_GetTicks();
|
||||
if (t - lastStatusLog > 2000) {
|
||||
@@ -513,12 +502,12 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DestroyTexture(tgTexture);
|
||||
presenter.Shutdown();
|
||||
if (loadState.load(std::memory_order_acquire) == 1) {
|
||||
host.SaveNVRAM();
|
||||
}
|
||||
CloseAudio();
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_GL_DeleteContext(gl);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user