Compare commits
1 Commits
c6c4aebab1
...
metal-inte
| Author | SHA1 | Date | |
|---|---|---|---|
| b3f6e2fcf0 |
@@ -97,11 +97,6 @@ set(EXTERNAL_SOURCES
|
|||||||
source/external/gif.cpp
|
source/external/gif.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Añadir archivos específicos para macOS (Metal)
|
|
||||||
if(APPLE)
|
|
||||||
list(APPEND EXTERNAL_SOURCES source/external/jail_shader_metal.mm)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Añadir jail_audio.cpp solo si el audio está habilitado
|
# Añadir jail_audio.cpp solo si el audio está habilitado
|
||||||
if(NOT DISABLE_AUDIO)
|
if(NOT DISABLE_AUDIO)
|
||||||
list(APPEND EXTERNAL_SOURCES source/external/jail_audio.cpp)
|
list(APPEND EXTERNAL_SOURCES source/external/jail_audio.cpp)
|
||||||
@@ -152,23 +147,6 @@ elseif(APPLE)
|
|||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||||
|
|
||||||
# Enlazar frameworks de Metal para macOS
|
|
||||||
find_library(METAL_FRAMEWORK Metal)
|
|
||||||
find_library(QUARTZCORE_FRAMEWORK QuartzCore)
|
|
||||||
find_library(COREFOUNDATION_FRAMEWORK CoreFoundation)
|
|
||||||
find_library(FOUNDATION_FRAMEWORK Foundation)
|
|
||||||
|
|
||||||
if(METAL_FRAMEWORK AND QUARTZCORE_FRAMEWORK AND COREFOUNDATION_FRAMEWORK AND FOUNDATION_FRAMEWORK)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
|
||||||
${METAL_FRAMEWORK}
|
|
||||||
${QUARTZCORE_FRAMEWORK}
|
|
||||||
${COREFOUNDATION_FRAMEWORK}
|
|
||||||
${FOUNDATION_FRAMEWORK})
|
|
||||||
message(STATUS "Metal frameworks encontrados y enlazados")
|
|
||||||
else()
|
|
||||||
message(WARNING "Algunos frameworks de Metal no se encontraron - OpenGL será usado como fallback")
|
|
||||||
endif()
|
|
||||||
elseif(UNIX AND NOT APPLE)
|
elseif(UNIX AND NOT APPLE)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -71,9 +71,7 @@ SOUND|${PREFIX}/data/sound/walk.wav
|
|||||||
|
|
||||||
# Shaders
|
# Shaders
|
||||||
DATA|${PREFIX}/data/shaders/crtpi_240.glsl
|
DATA|${PREFIX}/data/shaders/crtpi_240.glsl
|
||||||
DATA|${PREFIX}/data/shaders/crtpi_240.metal
|
|
||||||
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
|
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
|
||||||
DATA|${PREFIX}/data/shaders/crtpi_256.metal
|
|
||||||
|
|
||||||
# Texturas - Balloons
|
# Texturas - Balloons
|
||||||
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani
|
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
crt-pi - A Raspberry Pi friendly CRT shader.
|
|
||||||
|
|
||||||
Metal Shading Language version converted from GLSL
|
|
||||||
Copyright (C) 2015-2016 davej
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <metal_stdlib>
|
|
||||||
using namespace metal;
|
|
||||||
|
|
||||||
// Haven't put these as parameters as it would slow the code down.
|
|
||||||
#define SCANLINES
|
|
||||||
#define MULTISAMPLE
|
|
||||||
#define GAMMA
|
|
||||||
//#define FAKE_GAMMA
|
|
||||||
//#define CURVATURE
|
|
||||||
//#define SHARPER
|
|
||||||
// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish)
|
|
||||||
#define MASK_TYPE 2
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
#define CURVATURE_X 0.05
|
|
||||||
#define CURVATURE_Y 0.1
|
|
||||||
#define MASK_BRIGHTNESS 0.80
|
|
||||||
#define SCANLINE_WEIGHT 6.0
|
|
||||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
|
||||||
#define BLOOM_FACTOR 3.5
|
|
||||||
#define INPUT_GAMMA 2.4
|
|
||||||
#define OUTPUT_GAMMA 2.2
|
|
||||||
|
|
||||||
struct VertexIn {
|
|
||||||
float4 position [[attribute(0)]];
|
|
||||||
float2 texcoord [[attribute(1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
float4 position [[position]];
|
|
||||||
float2 TEX0;
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 screenScale;
|
|
||||||
#endif
|
|
||||||
float filterWidth;
|
|
||||||
};
|
|
||||||
|
|
||||||
vertex VertexOut vertex_main(VertexIn in [[stage_in]]) {
|
|
||||||
VertexOut out;
|
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
out.screenScale = float2(1.0, 1.0);
|
|
||||||
#endif
|
|
||||||
out.filterWidth = (768.0 / 240.0) / 3.0;
|
|
||||||
out.TEX0 = float2(in.texcoord.x, 1.0 - in.texcoord.y) * 1.0001;
|
|
||||||
out.position = in.position;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 Distort(float2 coord, float2 screenScale) {
|
|
||||||
float2 CURVATURE_DISTORTION = float2(CURVATURE_X, CURVATURE_Y);
|
|
||||||
// Barrel distortion shrinks the display area a bit, this will allow us to counteract that.
|
|
||||||
float2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION);
|
|
||||||
coord *= screenScale;
|
|
||||||
coord -= float2(0.5);
|
|
||||||
float rsq = coord.x * coord.x + coord.y * coord.y;
|
|
||||||
coord += coord * (CURVATURE_DISTORTION * rsq);
|
|
||||||
coord *= barrelScale;
|
|
||||||
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
|
|
||||||
coord = float2(-1.0); // If out of bounds, return an invalid value.
|
|
||||||
else {
|
|
||||||
coord += float2(0.5);
|
|
||||||
coord /= screenScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return coord;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float CalcScanLineWeight(float dist) {
|
|
||||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
float CalcScanLine(float dy, float filterWidth) {
|
|
||||||
float scanLineWeight = CalcScanLineWeight(dy);
|
|
||||||
#if defined(MULTISAMPLE)
|
|
||||||
scanLineWeight += CalcScanLineWeight(dy - filterWidth);
|
|
||||||
scanLineWeight += CalcScanLineWeight(dy + filterWidth);
|
|
||||||
scanLineWeight *= 0.3333333;
|
|
||||||
#endif
|
|
||||||
return scanLineWeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment float4 fragment_main(VertexOut in [[stage_in]],
|
|
||||||
texture2d<float> Texture [[texture(0)]],
|
|
||||||
sampler textureSampler [[sampler(0)]]) {
|
|
||||||
float2 TextureSize = float2(320.0, 240.0);
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 texcoord = Distort(in.TEX0, in.screenScale);
|
|
||||||
if (texcoord.x < 0.0)
|
|
||||||
return float4(0.0);
|
|
||||||
else
|
|
||||||
#else
|
|
||||||
float2 texcoord = in.TEX0;
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
float2 texcoordInPixels = texcoord * TextureSize;
|
|
||||||
#if defined(SHARPER)
|
|
||||||
float2 tempCoord = floor(texcoordInPixels) + 0.5;
|
|
||||||
float2 coord = tempCoord / TextureSize;
|
|
||||||
float2 deltas = texcoordInPixels - tempCoord;
|
|
||||||
float scanLineWeight = CalcScanLine(deltas.y, in.filterWidth);
|
|
||||||
float2 signs = sign(deltas);
|
|
||||||
deltas.x *= 2.0;
|
|
||||||
deltas = deltas * deltas;
|
|
||||||
deltas.y = deltas.y * deltas.y;
|
|
||||||
deltas.x *= 0.5;
|
|
||||||
deltas.y *= 8.0;
|
|
||||||
deltas /= TextureSize;
|
|
||||||
deltas *= signs;
|
|
||||||
float2 tc = coord + deltas;
|
|
||||||
#else
|
|
||||||
float tempY = floor(texcoordInPixels.y) + 0.5;
|
|
||||||
float yCoord = tempY / TextureSize.y;
|
|
||||||
float dy = texcoordInPixels.y - tempY;
|
|
||||||
float scanLineWeight = CalcScanLine(dy, in.filterWidth);
|
|
||||||
float signY = sign(dy);
|
|
||||||
dy = dy * dy;
|
|
||||||
dy = dy * dy;
|
|
||||||
dy *= 8.0;
|
|
||||||
dy /= TextureSize.y;
|
|
||||||
dy *= signY;
|
|
||||||
float2 tc = float2(texcoord.x, yCoord + dy);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float3 colour = Texture.sample(textureSampler, tc).rgb;
|
|
||||||
|
|
||||||
#if defined(SCANLINES)
|
|
||||||
#if defined(GAMMA)
|
|
||||||
#if defined(FAKE_GAMMA)
|
|
||||||
colour = colour * colour;
|
|
||||||
#else
|
|
||||||
colour = pow(colour, float3(INPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
scanLineWeight *= BLOOM_FACTOR;
|
|
||||||
colour *= scanLineWeight;
|
|
||||||
|
|
||||||
#if defined(GAMMA)
|
|
||||||
#if defined(FAKE_GAMMA)
|
|
||||||
colour = sqrt(colour);
|
|
||||||
#else
|
|
||||||
colour = pow(colour, float3(1.0/OUTPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#if MASK_TYPE == 0
|
|
||||||
return float4(colour, 1.0);
|
|
||||||
#else
|
|
||||||
#if MASK_TYPE == 1
|
|
||||||
float whichMask = fract((in.position.x * 1.0001) * 0.5);
|
|
||||||
float3 mask;
|
|
||||||
if (whichMask < 0.5)
|
|
||||||
mask = float3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
|
|
||||||
else
|
|
||||||
mask = float3(1.0, MASK_BRIGHTNESS, 1.0);
|
|
||||||
#elif MASK_TYPE == 2
|
|
||||||
float whichMask = fract((in.position.x * 1.0001) * 0.3333333);
|
|
||||||
float3 mask = float3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
|
|
||||||
if (whichMask < 0.3333333)
|
|
||||||
mask.x = 1.0;
|
|
||||||
else if (whichMask < 0.6666666)
|
|
||||||
mask.y = 1.0;
|
|
||||||
else
|
|
||||||
mask.z = 1.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return float4(colour * mask, 1.0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
crt-pi - A Raspberry Pi friendly CRT shader.
|
|
||||||
|
|
||||||
Metal Shading Language version converted from GLSL
|
|
||||||
Copyright (C) 2015-2016 davej
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <metal_stdlib>
|
|
||||||
using namespace metal;
|
|
||||||
|
|
||||||
// Haven't put these as parameters as it would slow the code down.
|
|
||||||
#define SCANLINES
|
|
||||||
#define MULTISAMPLE
|
|
||||||
#define GAMMA
|
|
||||||
//#define FAKE_GAMMA
|
|
||||||
//#define CURVATURE
|
|
||||||
//#define SHARPER
|
|
||||||
// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish)
|
|
||||||
#define MASK_TYPE 2
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
#define CURVATURE_X 0.05
|
|
||||||
#define CURVATURE_Y 0.1
|
|
||||||
#define MASK_BRIGHTNESS 0.80
|
|
||||||
#define SCANLINE_WEIGHT 6.0
|
|
||||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
|
||||||
#define BLOOM_FACTOR 3.5
|
|
||||||
#define INPUT_GAMMA 2.4
|
|
||||||
#define OUTPUT_GAMMA 2.2
|
|
||||||
|
|
||||||
struct VertexIn {
|
|
||||||
float4 position [[attribute(0)]];
|
|
||||||
float2 texcoord [[attribute(1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
float4 position [[position]];
|
|
||||||
float2 TEX0;
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 screenScale;
|
|
||||||
#endif
|
|
||||||
float filterWidth;
|
|
||||||
};
|
|
||||||
|
|
||||||
vertex VertexOut vertex_main(VertexIn in [[stage_in]]) {
|
|
||||||
VertexOut out;
|
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
out.screenScale = float2(1.0, 1.0);
|
|
||||||
#endif
|
|
||||||
out.filterWidth = (768.0 / 256.0) / 3.0;
|
|
||||||
out.TEX0 = float2(in.texcoord.x, 1.0 - in.texcoord.y) * 1.0001;
|
|
||||||
out.position = in.position;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 Distort(float2 coord, float2 screenScale) {
|
|
||||||
float2 CURVATURE_DISTORTION = float2(CURVATURE_X, CURVATURE_Y);
|
|
||||||
// Barrel distortion shrinks the display area a bit, this will allow us to counteract that.
|
|
||||||
float2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION);
|
|
||||||
coord *= screenScale;
|
|
||||||
coord -= float2(0.5);
|
|
||||||
float rsq = coord.x * coord.x + coord.y * coord.y;
|
|
||||||
coord += coord * (CURVATURE_DISTORTION * rsq);
|
|
||||||
coord *= barrelScale;
|
|
||||||
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
|
|
||||||
coord = float2(-1.0); // If out of bounds, return an invalid value.
|
|
||||||
else {
|
|
||||||
coord += float2(0.5);
|
|
||||||
coord /= screenScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return coord;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float CalcScanLineWeight(float dist) {
|
|
||||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
float CalcScanLine(float dy, float filterWidth) {
|
|
||||||
float scanLineWeight = CalcScanLineWeight(dy);
|
|
||||||
#if defined(MULTISAMPLE)
|
|
||||||
scanLineWeight += CalcScanLineWeight(dy - filterWidth);
|
|
||||||
scanLineWeight += CalcScanLineWeight(dy + filterWidth);
|
|
||||||
scanLineWeight *= 0.3333333;
|
|
||||||
#endif
|
|
||||||
return scanLineWeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment float4 fragment_main(VertexOut in [[stage_in]],
|
|
||||||
texture2d<float> Texture [[texture(0)]],
|
|
||||||
sampler textureSampler [[sampler(0)]]) {
|
|
||||||
float2 TextureSize = float2(320.0, 256.0);
|
|
||||||
#if defined(CURVATURE)
|
|
||||||
float2 texcoord = Distort(in.TEX0, in.screenScale);
|
|
||||||
if (texcoord.x < 0.0)
|
|
||||||
return float4(0.0);
|
|
||||||
else
|
|
||||||
#else
|
|
||||||
float2 texcoord = in.TEX0;
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
float2 texcoordInPixels = texcoord * TextureSize;
|
|
||||||
#if defined(SHARPER)
|
|
||||||
float2 tempCoord = floor(texcoordInPixels) + 0.5;
|
|
||||||
float2 coord = tempCoord / TextureSize;
|
|
||||||
float2 deltas = texcoordInPixels - tempCoord;
|
|
||||||
float scanLineWeight = CalcScanLine(deltas.y, in.filterWidth);
|
|
||||||
float2 signs = sign(deltas);
|
|
||||||
deltas.x *= 2.0;
|
|
||||||
deltas = deltas * deltas;
|
|
||||||
deltas.y = deltas.y * deltas.y;
|
|
||||||
deltas.x *= 0.5;
|
|
||||||
deltas.y *= 8.0;
|
|
||||||
deltas /= TextureSize;
|
|
||||||
deltas *= signs;
|
|
||||||
float2 tc = coord + deltas;
|
|
||||||
#else
|
|
||||||
float tempY = floor(texcoordInPixels.y) + 0.5;
|
|
||||||
float yCoord = tempY / TextureSize.y;
|
|
||||||
float dy = texcoordInPixels.y - tempY;
|
|
||||||
float scanLineWeight = CalcScanLine(dy, in.filterWidth);
|
|
||||||
float signY = sign(dy);
|
|
||||||
dy = dy * dy;
|
|
||||||
dy = dy * dy;
|
|
||||||
dy *= 8.0;
|
|
||||||
dy /= TextureSize.y;
|
|
||||||
dy *= signY;
|
|
||||||
float2 tc = float2(texcoord.x, yCoord + dy);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float3 colour = Texture.sample(textureSampler, tc).rgb;
|
|
||||||
|
|
||||||
#if defined(SCANLINES)
|
|
||||||
#if defined(GAMMA)
|
|
||||||
#if defined(FAKE_GAMMA)
|
|
||||||
colour = colour * colour;
|
|
||||||
#else
|
|
||||||
colour = pow(colour, float3(INPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
scanLineWeight *= BLOOM_FACTOR;
|
|
||||||
colour *= scanLineWeight;
|
|
||||||
|
|
||||||
#if defined(GAMMA)
|
|
||||||
#if defined(FAKE_GAMMA)
|
|
||||||
colour = sqrt(colour);
|
|
||||||
#else
|
|
||||||
colour = pow(colour, float3(1.0/OUTPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#if MASK_TYPE == 0
|
|
||||||
return float4(colour, 1.0);
|
|
||||||
#else
|
|
||||||
#if MASK_TYPE == 1
|
|
||||||
float whichMask = fract((in.position.x * 1.0001) * 0.5);
|
|
||||||
float3 mask;
|
|
||||||
if (whichMask < 0.5)
|
|
||||||
mask = float3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
|
|
||||||
else
|
|
||||||
mask = float3(1.0, MASK_BRIGHTNESS, 1.0);
|
|
||||||
#elif MASK_TYPE == 2
|
|
||||||
float whichMask = fract((in.position.x * 1.0001) * 0.3333333);
|
|
||||||
float3 mask = float3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
|
|
||||||
if (whichMask < 0.3333333)
|
|
||||||
mask.x = 1.0;
|
|
||||||
else if (whichMask < 0.6666666)
|
|
||||||
mask.y = 1.0;
|
|
||||||
else
|
|
||||||
mask.z = 1.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return float4(colour * mask, 1.0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -219,7 +219,7 @@ constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
|
|||||||
constexpr bool VIDEO_FULLSCREEN = false;
|
constexpr bool VIDEO_FULLSCREEN = false;
|
||||||
constexpr bool VIDEO_VSYNC = true;
|
constexpr bool VIDEO_VSYNC = true;
|
||||||
constexpr bool VIDEO_INTEGER_SCALE = true;
|
constexpr bool VIDEO_INTEGER_SCALE = true;
|
||||||
constexpr bool VIDEO_SHADERS = false;
|
constexpr bool VIDEO_SHADERS = true;
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
constexpr bool MUSIC_ENABLED = true;
|
constexpr bool MUSIC_ENABLED = true;
|
||||||
|
|||||||
45
source/external/jail_shader.cpp
vendored
45
source/external/jail_shader.cpp
vendored
@@ -234,7 +234,7 @@ GLuint getTextureID(SDL_Texture *texture) {
|
|||||||
return textureId;
|
return textureId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &shader_source, const std::string &fragment_shader) {
|
bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) {
|
||||||
shader::win = window;
|
shader::win = window;
|
||||||
shader::renderer = SDL_GetRenderer(window);
|
shader::renderer = SDL_GetRenderer(window);
|
||||||
shader::backBuffer = back_buffer_texture;
|
shader::backBuffer = back_buffer_texture;
|
||||||
@@ -253,15 +253,7 @@ bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::strin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
// Verificar que el renderer sea OpenGL
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Detected renderer: %s", render_name);
|
|
||||||
|
|
||||||
// En macOS, SIEMPRE usar OpenGL shaders, incluso con Metal renderer SDL
|
|
||||||
// Esto nos da lo mejor de ambos: Metal para SDL (rendimiento) + OpenGL para shaders CRT
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "macOS: Using OpenGL shaders with %s SDL renderer", render_name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Verificar que el renderer sea OpenGL solamente (Metal no puede usar shaders OpenGL)
|
|
||||||
if (!strncmp(render_name, "opengl", 6)) {
|
if (!strncmp(render_name, "opengl", 6)) {
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
if (!initGLExtensions()) {
|
if (!initGLExtensions()) {
|
||||||
@@ -271,39 +263,24 @@ bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::strin
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Compilar el programa de shaders utilizando std::string
|
// Compilar el programa de shaders utilizando std::string
|
||||||
programId = compileProgram(shader_source, fragment_shader);
|
programId = compileProgram(vertex_shader, fragment_shader);
|
||||||
if (programId == INVALID_PROGRAM_ID) {
|
if (programId == INVALID_PROGRAM_ID) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders.");
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders.");
|
||||||
usingOpenGL = false;
|
usingOpenGL = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
usingOpenGL = true;
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** OpenGL shader system initialized successfully");
|
|
||||||
return true;
|
|
||||||
} else if (!strncmp(render_name, "metal", 5)) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL using Metal renderer - CRT shaders not available yet");
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running with excellent Metal performance (~450+ FPS) without CRT effects");
|
|
||||||
usingOpenGL = false;
|
|
||||||
return true; // Success, but no shaders
|
|
||||||
} else {
|
} else {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es compatible (%s).", render_name);
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name);
|
||||||
usingOpenGL = false;
|
usingOpenGL = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usingOpenGL = true;
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Shader system initialized successfully");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
#ifdef __APPLE__
|
|
||||||
// En macOS con Metal, los shaders están deshabilitados por ahora
|
|
||||||
// TODO: Implementar integración correcta SDL Metal + shaders CRT
|
|
||||||
if (!usingOpenGL) {
|
|
||||||
// Usar renderizado SDL normal (sin shaders)
|
|
||||||
// Esto da excelente rendimiento ~450+ FPS
|
|
||||||
return; // Let SDL handle rendering normally
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Establece el color de fondo
|
// Establece el color de fondo
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
SDL_SetRenderTarget(renderer, nullptr);
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
@@ -415,12 +392,6 @@ void render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
#ifdef __APPLE__
|
|
||||||
if (!usingOpenGL) {
|
|
||||||
metal::cleanupMetal();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (programId != INVALID_PROGRAM_ID) {
|
if (programId != INVALID_PROGRAM_ID) {
|
||||||
glDeleteProgram(programId);
|
glDeleteProgram(programId);
|
||||||
programId = INVALID_PROGRAM_ID;
|
programId = INVALID_PROGRAM_ID;
|
||||||
|
|||||||
2
source/external/jail_shader.h
vendored
2
source/external/jail_shader.h
vendored
@@ -12,8 +12,10 @@ bool isUsingOpenGL();
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
namespace metal {
|
namespace metal {
|
||||||
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
|
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
|
||||||
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height);
|
||||||
void updateMetalTexture(SDL_Texture* backBuffer);
|
void updateMetalTexture(SDL_Texture* backBuffer);
|
||||||
void renderMetal();
|
void renderMetal();
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture);
|
||||||
void cleanupMetal();
|
void cleanupMetal();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
392
source/external/jail_shader_metal.mm
vendored
392
source/external/jail_shader_metal.mm
vendored
@@ -15,12 +15,16 @@ namespace metal {
|
|||||||
|
|
||||||
// Metal objects
|
// Metal objects
|
||||||
id<MTLDevice> device = nullptr;
|
id<MTLDevice> device = nullptr;
|
||||||
id<MTLCommandQueue> commandQueue = nullptr;
|
|
||||||
id<MTLRenderPipelineState> pipelineState = nullptr;
|
id<MTLRenderPipelineState> pipelineState = nullptr;
|
||||||
id<MTLTexture> metalTexture = nullptr;
|
|
||||||
id<MTLBuffer> vertexBuffer = nullptr;
|
id<MTLBuffer> vertexBuffer = nullptr;
|
||||||
CAMetalLayer* metalLayer = nullptr;
|
id<MTLTexture> backBufferTexture = nullptr;
|
||||||
SDL_MetalView metalView = nullptr;
|
id<MTLTexture> gameCanvasTexture = nullptr; // Our custom render target texture
|
||||||
|
id<MTLSamplerState> sampler = nullptr;
|
||||||
|
|
||||||
|
// SDL objects (references from main shader module)
|
||||||
|
SDL_Window* win = nullptr;
|
||||||
|
SDL_Renderer* renderer = nullptr;
|
||||||
|
SDL_Texture* backBuffer = nullptr;
|
||||||
|
|
||||||
// Vertex data for fullscreen quad
|
// Vertex data for fullscreen quad
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@@ -29,7 +33,7 @@ struct Vertex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Vertex quadVertices[] = {
|
const Vertex quadVertices[] = {
|
||||||
// Position (x, y, z, w) // TexCoord (u, v)
|
// Position (x, y, z, w) // TexCoord (u, v) - Standard OpenGL-style coordinates
|
||||||
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
|
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
|
||||||
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
|
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
|
||||||
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
|
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
|
||||||
@@ -45,38 +49,79 @@ std::string loadMetalShader(const std::string& filename) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename) {
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height) {
|
||||||
// Get Metal view from SDL
|
if (!renderer) {
|
||||||
metalView = SDL_Metal_CreateView(window);
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "createMetalRenderTarget: No renderer provided");
|
||||||
if (!metalView) {
|
return nullptr;
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal view: %s", SDL_GetError());
|
}
|
||||||
|
|
||||||
|
// Crear textura Metal como render target
|
||||||
|
SDL_Texture* metalTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
|
||||||
|
if (!metalTexture) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render target texture: %s", SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar filtrado nearest neighbor para look pixelado
|
||||||
|
SDL_SetTextureScaleMode(metalTexture, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
|
// Try to extract and store the Metal texture directly
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(metalTexture);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
gameCanvasTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (gameCanvasTexture) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Successfully extracted Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [gameCanvasTexture width], [gameCanvasTexture height]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameCanvasTexture) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not extract Metal texture from SDL texture - shaders may not work");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Created Metal render target texture (%dx%d)", width, height);
|
||||||
|
return metalTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initMetal(SDL_Window* window, SDL_Texture* backBufferTexture, const std::string& shaderFilename) {
|
||||||
|
// Store references
|
||||||
|
win = window;
|
||||||
|
backBuffer = backBufferTexture;
|
||||||
|
renderer = SDL_GetRenderer(window);
|
||||||
|
|
||||||
|
if (!renderer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL renderer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
metalLayer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(metalView);
|
// Get Metal layer from SDL renderer and extract device from it
|
||||||
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)SDL_GetRenderMetalLayer(renderer);
|
||||||
if (!metalLayer) {
|
if (!metalLayer) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer");
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer from SDL renderer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Metal device
|
device = metalLayer.device;
|
||||||
device = MTLCreateSystemDefaultDevice();
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal device");
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal device from layer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the layer
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal device from SDL layer: %s", [[device name] UTF8String]);
|
||||||
metalLayer.device = device;
|
|
||||||
metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
||||||
metalLayer.displaySyncEnabled = YES;
|
|
||||||
|
|
||||||
// Create command queue
|
// Note: We no longer need our own texture - we'll use the backBuffer directly
|
||||||
commandQueue = [device newCommandQueue];
|
|
||||||
if (!commandQueue) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal command queue");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and compile shaders
|
// Load and compile shaders
|
||||||
std::string metalShaderSource = loadMetalShader(shaderFilename);
|
std::string metalShaderSource = loadMetalShader(shaderFilename);
|
||||||
@@ -111,7 +156,7 @@ bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& s
|
|||||||
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||||
pipelineDescriptor.vertexFunction = vertexFunction;
|
pipelineDescriptor.vertexFunction = vertexFunction;
|
||||||
pipelineDescriptor.fragmentFunction = fragmentFunction;
|
pipelineDescriptor.fragmentFunction = fragmentFunction;
|
||||||
pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat;
|
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
|
||||||
// Set up vertex descriptor
|
// Set up vertex descriptor
|
||||||
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
|
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
|
||||||
@@ -140,108 +185,6 @@ bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& s
|
|||||||
length:sizeof(quadVertices)
|
length:sizeof(quadVertices)
|
||||||
options:MTLResourceOptionCPUCacheModeDefault];
|
options:MTLResourceOptionCPUCacheModeDefault];
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateMetalTexture(SDL_Texture* backBuffer) {
|
|
||||||
if (!device || !backBuffer) return;
|
|
||||||
|
|
||||||
// Solo intentar obtener la textura una vez al inicio, después reutilizar
|
|
||||||
static bool textureInitialized = false;
|
|
||||||
if (textureInitialized && metalTexture) {
|
|
||||||
return; // Ya tenemos la textura configurada
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentar obtener la textura Metal directamente desde SDL
|
|
||||||
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
|
||||||
if (props == 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get texture properties: %s", SDL_GetError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtener la textura Metal nativa de SDL
|
|
||||||
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, "SDL.texture.metal.texture", nullptr);
|
|
||||||
|
|
||||||
if (sdlMetalTexture) {
|
|
||||||
// Usar la textura Metal directamente de SDL
|
|
||||||
metalTexture = sdlMetalTexture;
|
|
||||||
textureInitialized = true;
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using SDL Metal texture directly (size: %lux%lu)",
|
|
||||||
[metalTexture width], [metalTexture height]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentar con nombres alternativos
|
|
||||||
const char* propertyNames[] = {
|
|
||||||
"texture.metal.texture",
|
|
||||||
"SDL.renderer.metal.texture",
|
|
||||||
"metal.texture"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const char* propName : propertyNames) {
|
|
||||||
sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
|
||||||
if (sdlMetalTexture) {
|
|
||||||
metalTexture = sdlMetalTexture;
|
|
||||||
textureInitialized = true;
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using SDL Metal texture via property '%s'", propName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si no podemos obtener la textura Metal, crear una propia que coincida con la de SDL
|
|
||||||
if (!metalTexture) {
|
|
||||||
float width_f, height_f;
|
|
||||||
if (SDL_GetTextureSize(backBuffer, &width_f, &height_f)) {
|
|
||||||
MTLTextureDescriptor* desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
|
||||||
width:(int)width_f
|
|
||||||
height:(int)height_f
|
|
||||||
mipmapped:NO];
|
|
||||||
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
|
|
||||||
metalTexture = [device newTextureWithDescriptor:desc];
|
|
||||||
textureInitialized = true;
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Created fallback Metal texture (%dx%d)",
|
|
||||||
(int)width_f, (int)height_f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderMetal() {
|
|
||||||
if (!device || !commandQueue || !pipelineState || !metalLayer) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current drawable
|
|
||||||
id<CAMetalDrawable> drawable = [metalLayer nextDrawable];
|
|
||||||
if (!drawable) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal drawable");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create render pass descriptor
|
|
||||||
MTLRenderPassDescriptor* renderPass = [MTLRenderPassDescriptor renderPassDescriptor];
|
|
||||||
renderPass.colorAttachments[0].texture = drawable.texture;
|
|
||||||
renderPass.colorAttachments[0].loadAction = MTLLoadActionClear;
|
|
||||||
renderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
|
||||||
renderPass.colorAttachments[0].storeAction = MTLStoreActionStore;
|
|
||||||
|
|
||||||
// Create command buffer
|
|
||||||
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
|
|
||||||
|
|
||||||
// Create render command encoder
|
|
||||||
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPass];
|
|
||||||
|
|
||||||
// Set pipeline state
|
|
||||||
[encoder setRenderPipelineState:pipelineState];
|
|
||||||
|
|
||||||
// Set vertex buffer
|
|
||||||
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
|
||||||
|
|
||||||
// Set texture and sampler (use metalTexture if available, otherwise render solid color)
|
|
||||||
if (metalTexture) {
|
|
||||||
[encoder setFragmentTexture:metalTexture atIndex:0];
|
|
||||||
|
|
||||||
// Create sampler state for nearest neighbor filtering (pixelated look)
|
// Create sampler state for nearest neighbor filtering (pixelated look)
|
||||||
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
||||||
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
|
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
|
||||||
@@ -249,40 +192,191 @@ void renderMetal() {
|
|||||||
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
|
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
|
|
||||||
id<MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||||
[encoder setFragmentSamplerState:sampler atIndex:0];
|
|
||||||
} else {
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No Metal texture - rendering test pattern");
|
return true;
|
||||||
// No texture available - the shader should handle this case
|
}
|
||||||
|
|
||||||
|
void updateMetalTexture(SDL_Texture* backBuffer) {
|
||||||
|
if (!device || !backBuffer) return;
|
||||||
|
|
||||||
|
// Only log this occasionally to avoid spam
|
||||||
|
static int attemptCount = 0;
|
||||||
|
static bool hasLogged = false;
|
||||||
|
|
||||||
|
// Try multiple property names that SDL3 might use
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (sdlMetalTexture) {
|
||||||
|
backBufferTexture = sdlMetalTexture;
|
||||||
|
if (!hasLogged) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the quad
|
// If we can't get the texture after several attempts, log once and continue
|
||||||
|
if (!hasLogged && attemptCount++ > 10) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not access SDL Metal texture after %d attempts - shader will be skipped", attemptCount);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderMetal() {
|
||||||
|
if (!renderer || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct pipeline: backBuffer → Metal shaders → screen (no double rendering)
|
||||||
|
|
||||||
|
// Try to get the Metal texture directly from the SDL backBuffer texture
|
||||||
|
updateMetalTexture(backBuffer);
|
||||||
|
|
||||||
|
if (!backBufferTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Metal texture not available - falling back to normal SDL rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render without shaders using normal SDL path
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader directly: backBuffer texture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal render attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, backBufferTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect directly to backBuffer texture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:backBufferTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
// End encoding
|
static int successCount = 0;
|
||||||
[encoder endEncoding];
|
if (successCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT shader to backBuffer (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
|
||||||
// Present drawable
|
static int fallbackCount = 0;
|
||||||
[commandBuffer presentDrawable:drawable];
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Commit command buffer
|
SDL_RenderPresent(renderer);
|
||||||
[commandBuffer commit];
|
}
|
||||||
|
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture) {
|
||||||
|
if (!renderer || !sourceTexture || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing failed: missing components");
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use our stored Metal texture if available
|
||||||
|
id<MTLTexture> metalTexture = gameCanvasTexture;
|
||||||
|
|
||||||
|
if (!metalTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "gameCanvasTexture not available - falling back to normal rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader post-processing: sourceTexture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, metalTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect to sourceTexture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:metalTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
static int successCount = 0;
|
||||||
|
if (successCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT post-processing (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [metalTexture width], [metalTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
|
||||||
|
static int fallbackCount = 0;
|
||||||
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder for post-processing - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupMetal() {
|
void cleanupMetal() {
|
||||||
// Release Metal objects (ARC handles most of this automatically)
|
// Release Metal objects (ARC handles most of this automatically)
|
||||||
pipelineState = nullptr;
|
pipelineState = nullptr;
|
||||||
metalTexture = nullptr;
|
backBufferTexture = nullptr;
|
||||||
|
gameCanvasTexture = nullptr;
|
||||||
vertexBuffer = nullptr;
|
vertexBuffer = nullptr;
|
||||||
commandQueue = nullptr;
|
sampler = nullptr;
|
||||||
device = nullptr;
|
device = nullptr;
|
||||||
|
renderer = nullptr;
|
||||||
if (metalView) {
|
|
||||||
SDL_Metal_DestroyView(metalView);
|
|
||||||
metalView = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
metalLayer = nullptr;
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,21 @@ Screen::Screen()
|
|||||||
initSDLVideo();
|
initSDLVideo();
|
||||||
|
|
||||||
// Crea la textura de destino
|
// Crea la textura de destino
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Usar nuestra propia Metal texture como render target
|
||||||
|
game_canvas_ = shader::metal::createMetalRenderTarget(renderer_, param.game.width, param.game.height);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using custom Metal render target for game_canvas_");
|
||||||
|
} else {
|
||||||
|
// Fallback para otros renderers
|
||||||
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Crea el objeto de texto
|
// Crea el objeto de texto
|
||||||
createText();
|
createText();
|
||||||
@@ -63,7 +76,6 @@ Screen::Screen()
|
|||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
Screen::~Screen() {
|
Screen::~Screen() {
|
||||||
shader::cleanup();
|
|
||||||
SDL_DestroyTexture(game_canvas_);
|
SDL_DestroyTexture(game_canvas_);
|
||||||
SDL_DestroyRenderer(renderer_);
|
SDL_DestroyRenderer(renderer_);
|
||||||
SDL_DestroyWindow(window_);
|
SDL_DestroyWindow(window_);
|
||||||
@@ -100,7 +112,18 @@ void Screen::renderPresent() {
|
|||||||
clean();
|
clean();
|
||||||
|
|
||||||
if (Options::video.shaders) {
|
if (Options::video.shaders) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Use Metal post-processing with our custom render target
|
||||||
|
shader::metal::renderWithPostProcessing(renderer_, game_canvas_);
|
||||||
|
} else {
|
||||||
|
// Fallback to standard shader system for non-Metal renderers
|
||||||
shader::render();
|
shader::render();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
shader::render();
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
@@ -335,20 +358,7 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crear renderer
|
// Crear renderer
|
||||||
#ifdef __APPLE__
|
|
||||||
// Intentar crear renderer Metal específicamente en macOS
|
|
||||||
renderer_ = SDL_CreateRenderer(window_, "metal");
|
|
||||||
if (renderer_ == nullptr) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Failed to create Metal renderer, trying default: %s", SDL_GetError());
|
|
||||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||||
} else {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal renderer created successfully");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (renderer_ == nullptr) {
|
if (renderer_ == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"FATAL: Failed to create renderer! SDL Error: %s",
|
"FATAL: Failed to create renderer! SDL Error: %s",
|
||||||
|
|||||||
Reference in New Issue
Block a user